├── demo
├── app.wxss
├── pages
│ ├── index
│ │ ├── index.json
│ │ ├── a.js
│ │ ├── footer.wxml
│ │ ├── index.wxml
│ │ ├── index.wxss
│ │ ├── index.js
│ │ └── dayjs.js
│ ├── kid
│ │ ├── index.wxss
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.js
│ ├── child
│ │ ├── index.json
│ │ ├── index.wxss
│ │ ├── index.wxml
│ │ └── index.js
│ └── item
│ │ ├── index.json
│ │ ├── slot.wxml
│ │ ├── index.wxml
│ │ ├── index.wxss
│ │ └── index.js
├── 2.wxss
├── README.md
├── 1.wxss
├── public
│ ├── 0.png
│ └── 1.png
├── app.js
├── sitemap.json
├── app.json
└── project.config.json
├── smallappandroid
├── app
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── values
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ ├── themes.xml
│ │ │ │ │ └── colors.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ ├── ic_launcher.webp
│ │ │ │ │ └── ic_launcher_round.webp
│ │ │ │ ├── mipmap-anydpi-v26
│ │ │ │ │ ├── ic_launcher.xml
│ │ │ │ │ └── ic_launcher_round.xml
│ │ │ │ ├── layout
│ │ │ │ │ └── activity_main.xml
│ │ │ │ ├── xml
│ │ │ │ │ ├── backup_rules.xml
│ │ │ │ │ └── data_extraction_rules.xml
│ │ │ │ └── drawable
│ │ │ │ │ ├── ic_launcher_foreground.xml
│ │ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── smallapp
│ │ │ │ │ ├── ui
│ │ │ │ │ └── theme
│ │ │ │ │ │ ├── Color.kt
│ │ │ │ │ │ ├── Type.kt
│ │ │ │ │ │ └── Theme.kt
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── AndroidManifest.xml
│ │ ├── test
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── smallapp
│ │ │ │ └── ExampleUnitTest.kt
│ │ └── androidTest
│ │ │ └── java
│ │ │ └── com
│ │ │ └── smallapp
│ │ │ └── ExampleInstrumentedTest.kt
│ ├── proguard-rules.pro
│ └── build.gradle.kts
├── .idea
│ ├── .name
│ ├── .gitignore
│ ├── compiler.xml
│ ├── kotlinc.xml
│ ├── vcs.xml
│ ├── misc.xml
│ └── gradle.xml
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── build.gradle.kts
├── .gitignore
├── settings.gradle.kts
├── gradle.properties
├── gradlew.bat
└── gradlew
├── runtime
├── README.md
├── .gitignore
├── master
│ ├── components
│ │ ├── image.js
│ │ ├── text.js
│ │ ├── button.js
│ │ ├── block.js
│ │ ├── label.js
│ │ ├── radio-group.js
│ │ ├── input.js
│ │ ├── icon.js
│ │ ├── checkbox-group.js
│ │ ├── switch.js
│ │ ├── view.js
│ │ ├── index.js
│ │ ├── radio.js
│ │ └── checkbox.js
│ ├── app.js
│ ├── helper.js
│ ├── page.js
│ ├── context.js
│ ├── global.js
│ ├── init.js
│ ├── component.js
│ ├── safe-obj.js
│ ├── wxapi.js
│ ├── exec-script.js
│ ├── index.js
│ ├── dom
│ │ ├── proxy-dom.js
│ │ └── fake-dom.js
│ ├── expr.js
│ └── fre-esm.js
├── build.js
├── package.json
├── LICENSE
├── slave
│ ├── proxy.js
│ └── index.js
└── dist
│ └── slave.js
├── compiler
├── .gitignore
├── bin
│ └── index.js
├── README.md
├── wxml
│ ├── index.js
│ ├── parse.js
│ ├── lex.js
│ └── generate.js
├── .vscode
│ └── launch.json
├── core
│ ├── plugins
│ │ ├── postcss-tag-replacer.js
│ │ ├── esbuild-component-tag.js
│ │ ├── postcss-rpx2rem.js
│ │ └── postcss-scoped-css.js
│ ├── packagers
│ │ ├── wxss.js
│ │ ├── js.js
│ │ ├── berial.js
│ │ ├── wxml.js
│ │ └── util.js
│ ├── assets
│ │ ├── wxml.js
│ │ ├── asset.js
│ │ ├── js.js
│ │ ├── wxss.js
│ │ └── json.js
│ ├── commander.js
│ ├── serve.js
│ ├── index.ejs
│ ├── cli.js
│ ├── bundle.js
│ └── package.js
└── package.json
├── .gitignore
├── install.sh
├── pnpm-workspace.yaml
├── Dockerfile
├── debug.sh
├── antlr4
└── Wxml.g4
├── package.json
├── README.md
└── worker.js
/demo/app.wxss:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demo/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/smallappandroid/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/runtime/README.md:
--------------------------------------------------------------------------------
1 | # @fre-miniapp/runtime
--------------------------------------------------------------------------------
/smallappandroid/.idea/.name:
--------------------------------------------------------------------------------
1 | smallapp-android
--------------------------------------------------------------------------------
/demo/2.wxss:
--------------------------------------------------------------------------------
1 | .b{
2 | color: aliceblue;
3 | }
--------------------------------------------------------------------------------
/runtime/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 |
3 | dist
--------------------------------------------------------------------------------
/demo/pages/kid/index.wxss:
--------------------------------------------------------------------------------
1 | /* pages/kid/index.wxss */
--------------------------------------------------------------------------------
/demo/README.md:
--------------------------------------------------------------------------------
1 | # minniprogam-todomvc
2 | 微信小程序 todomvc
3 |
--------------------------------------------------------------------------------
/demo/pages/child/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
--------------------------------------------------------------------------------
/demo/1.wxss:
--------------------------------------------------------------------------------
1 | @import "./2.wxss";
2 |
3 | .a{
4 | color:#fff
5 | }
--------------------------------------------------------------------------------
/compiler/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules/
2 | /dist/
3 | *.log
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/compiler/bin/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | require('../core/cli');
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/**/node_modules/
2 | demo/dist/*
3 | runtime/dist
4 | .vscode
5 | .DS_Store
--------------------------------------------------------------------------------
/demo/pages/child/index.wxss:
--------------------------------------------------------------------------------
1 | .switch{
2 | margin: 30px;
3 | display: block;
4 | }
--------------------------------------------------------------------------------
/demo/pages/kid/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true,
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/demo/public/0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yisar/smallapp/HEAD/demo/public/0.png
--------------------------------------------------------------------------------
/demo/public/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yisar/smallapp/HEAD/demo/public/1.png
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | pnpm install
2 |
3 | cnpm i chokidar ejs esbuild express md5 postcss --save
--------------------------------------------------------------------------------
/smallappandroid/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | # all packages in subdirs of packages/ and components/
3 | - '/**'
--------------------------------------------------------------------------------
/demo/pages/index/a.js:
--------------------------------------------------------------------------------
1 | export function a() {
2 | console.log(123)
3 | }
4 |
5 | export default {}
6 |
7 |
--------------------------------------------------------------------------------
/demo/pages/kid/index.wxml:
--------------------------------------------------------------------------------
1 |
2 | pages/kid/index.wxml
3 |
--------------------------------------------------------------------------------
/demo/app.js:
--------------------------------------------------------------------------------
1 | App({
2 | onLaunch: () => {
3 | wx.navigateTo({
4 | url: "/pages/index/index",
5 | })
6 | },
7 | })
8 |
--------------------------------------------------------------------------------
/compiler/README.md:
--------------------------------------------------------------------------------
1 | # @fre-miniapp/runtime
2 |
3 | > fre 小程序基础包
4 |
5 | ### 启动
6 |
7 | ```console
8 | yarn start
9 | ```
10 |
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | smallapp-android
3 |
--------------------------------------------------------------------------------
/demo/pages/item/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true,
3 | "usingComponents": {
4 | "child-child": "/pages/kid/index"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/smallappandroid/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yisar/smallapp/HEAD/smallappandroid/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/runtime/master/components/image.js:
--------------------------------------------------------------------------------
1 | import { h } from '../fre-esm'
2 |
3 | export default function Image(props) {
4 |
5 | return
6 | }
--------------------------------------------------------------------------------
/runtime/master/components/text.js:
--------------------------------------------------------------------------------
1 |
2 | import {h} from '../fre-esm'
3 |
4 | export default function Text(props){
5 | return
6 | }
--------------------------------------------------------------------------------
/runtime/master/components/button.js:
--------------------------------------------------------------------------------
1 | import {h} from '../fre-esm'
2 |
3 | export default function Button(props) {
4 |
5 | return
6 | }
--------------------------------------------------------------------------------
/demo/pages/item/slot.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/runtime/master/components/block.js:
--------------------------------------------------------------------------------
1 | import {h,Fragment} from '../fre-esm'
2 |
3 | export default function Block(props) {
4 | return
{props.children}
5 | }
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yisar/smallapp/HEAD/smallappandroid/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yisar/smallapp/HEAD/smallappandroid/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yisar/smallapp/HEAD/smallappandroid/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yisar/smallapp/HEAD/smallappandroid/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yisar/smallapp/HEAD/smallappandroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yisar/smallapp/HEAD/smallappandroid/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yisar/smallapp/HEAD/smallappandroid/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yisar/smallapp/HEAD/smallappandroid/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yisar/smallapp/HEAD/smallappandroid/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yisar/smallapp/HEAD/smallappandroid/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/demo/sitemap.json:
--------------------------------------------------------------------------------
1 | {
2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
3 | "rules": [{
4 | "action": "allow",
5 | "page": "*"
6 | }]
7 | }
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx
2 |
3 | USER root
4 |
5 | COPY /packages/react/dist/ /usr/share/nginx/html/react/
6 | COPY /packages/fre/dist/ /usr/share/nginx/html/fre/
7 |
8 | RUN chmod -R 775 /usr/share/nginx/html
--------------------------------------------------------------------------------
/compiler/wxml/index.js:
--------------------------------------------------------------------------------
1 | const parse = require("./parse")
2 | const lex = require("./lex")
3 | const generate = require("./generate")
4 |
5 | module.exports = {
6 | lex,
7 | parse,
8 | generate,
9 | }
10 |
--------------------------------------------------------------------------------
/smallappandroid/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/runtime/master/components/label.js:
--------------------------------------------------------------------------------
1 | import { h } from '../fre-esm'
2 |
3 | function Label(props) {
4 | const { children, ...rest } = props
5 | return
6 | }
7 |
8 | export default Label
--------------------------------------------------------------------------------
/smallappandroid/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/smallappandroid/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/smallappandroid/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | id("com.android.application") version "8.1.2" apply false
4 | id("org.jetbrains.kotlin.android") version "1.8.10" apply false
5 | }
--------------------------------------------------------------------------------
/demo/pages/child/index.wxml:
--------------------------------------------------------------------------------
1 | Switch
2 |
3 |
7 |
8 |
--------------------------------------------------------------------------------
/smallappandroid/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jan 29 09:11:04 CST 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/demo/pages/index/footer.wxml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demo/pages/item/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/smallappandroid/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 | app/src/main/assets/
--------------------------------------------------------------------------------
/demo/pages/kid/index.js:
--------------------------------------------------------------------------------
1 | // pages/kid/index.js
2 | Component({
3 | /**
4 | * 组件的属性列表
5 | */
6 | properties: {
7 |
8 | },
9 |
10 | /**
11 | * 组件的初始数据
12 | */
13 | data: {
14 |
15 | },
16 |
17 | /**
18 | * 组件的方法列表
19 | */
20 | methods: {
21 | emmm(){
22 | this.triggerEvent('eee')
23 | }
24 | }
25 | })
26 |
--------------------------------------------------------------------------------
/runtime/master/app.js:
--------------------------------------------------------------------------------
1 | let app = null
2 |
3 | class _App{
4 | constructor(){
5 | this.graph = {}
6 | }
7 | }
8 |
9 | export function App(option){
10 | app = new _App()
11 | }
12 |
13 | export function getApp(){
14 | return app
15 | }
16 |
17 | export function getInsById(id){
18 | return app.graph[id]
19 | }
20 |
21 |
22 | App() // 暂时默认执行
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/java/com/smallapp/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.smallapp.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple80 = Color(0xFFD0BCFF)
6 | val PurpleGrey80 = Color(0xFFCCC2DC)
7 | val Pink80 = Color(0xFFEFB8C8)
8 |
9 | val Purple40 = Color(0xFF6650a4)
10 | val PurpleGrey40 = Color(0xFF625b71)
11 | val Pink40 = Color(0xFF7D5260)
--------------------------------------------------------------------------------
/debug.sh:
--------------------------------------------------------------------------------
1 | clear
2 |
3 | cd runtime
4 |
5 | npm run build
6 |
7 | cd ../demo
8 |
9 | rm -rf dist
10 |
11 | mkdir dist
12 |
13 | node ../compiler/bin/index.js -e app.json -o dist
14 |
15 | cd ../
16 |
17 | rm -rf smallappandroid/app/src/main/assets*.*
18 |
19 | cp -r runtime/dist/* smallappandroid/app/src/main/assets
20 |
21 | cp -r demo/dist/* smallappandroid/app/src/main/assets
--------------------------------------------------------------------------------
/demo/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{title}}
6 | Made with 💔 & Fre miniapp
7 |
--------------------------------------------------------------------------------
/runtime/master/components/radio-group.js:
--------------------------------------------------------------------------------
1 | import { createContext } from '../context'
2 | import {h} from '../fre-esm'
3 |
4 | export const RadioContext = createContext()
5 |
6 | function RadioGroup(props) {
7 | const { onChange } = props
8 |
9 | return {props.children}
10 | }
11 |
12 | export default RadioGroup
--------------------------------------------------------------------------------
/demo/pages/item/index.wxss:
--------------------------------------------------------------------------------
1 | .list-items {
2 | display: flex;
3 | padding: 0 30rpx;
4 | justify-content: space-around;
5 | align-items: center;
6 | height: 100rpx;
7 | font-size: 40rpx;
8 | border-bottom: 1px solid #e6e6e6;
9 | }
10 | .selectAll {
11 | width: 50rpx;
12 | height: 50rpx;
13 | }
14 |
15 | .completed {
16 | text-decoration: line-through;
17 | color: #d9d9d9;
18 | }
19 |
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/compiler/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.1.0",
3 | "configurations": [
4 | {
5 | "type": "node",
6 | "request": "launch",
7 | "name": "debug demo",
8 | "program": "${workspaceFolder}/core/cli.js",
9 | "args": [
10 | "-e",
11 | "./demo/app.json",
12 | "-o",
13 | "./dist/"
14 | ]
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/demo/pages/child/index.js:
--------------------------------------------------------------------------------
1 | Page({
2 | data: {
3 | items: [
4 | {name: 'USA', value: '美国'},
5 | {name: 'CHN', value: '中国', checked: 'true'},
6 | {name: 'BRA', value: '巴西'},
7 | {name: 'JPN', value: '日本'},
8 | {name: 'ENG', value: '英国'},
9 | {name: 'TUR', value: '法国'},
10 | ]
11 | },
12 | radioChange(e) {
13 | console.log('radio发生change事件,携带value值为:', e.detail.value)
14 | }
15 | })
--------------------------------------------------------------------------------
/smallappandroid/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 |
16 | rootProject.name = "smallapp-android"
17 | include(":app")
18 |
--------------------------------------------------------------------------------
/runtime/master/components/input.js:
--------------------------------------------------------------------------------
1 | import { h } from '../fre-esm'
2 |
3 | export default function Input(props) {
4 | return
16 | }
--------------------------------------------------------------------------------
/smallappandroid/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/smallappandroid/app/src/test/java/com/smallapp/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.smallapp
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/runtime/master/components/icon.js:
--------------------------------------------------------------------------------
1 | import {h} from '../fre-esm'
2 |
3 | function Icon(props) {
4 | const { size=24, color='var(--primary-color)', type, ...rest } = props
5 | return (
6 |
15 | )
16 | }
17 | export default Icon
18 |
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
10 |
--------------------------------------------------------------------------------
/runtime/master/components/checkbox-group.js:
--------------------------------------------------------------------------------
1 | import { createContext } from '../context'
2 | import {h} from '../fre-esm'
3 |
4 | export const CheckboxContext = createContext()
5 |
6 | function CheckboxGroup(props) {
7 | const { onChange } = props
8 |
9 | return (
10 |
11 | {props.children}
12 |
13 | )
14 | }
15 |
16 | export default CheckboxGroup
17 |
--------------------------------------------------------------------------------
/runtime/master/components/switch.js:
--------------------------------------------------------------------------------
1 | import {h, useState} from '../fre-esm'
2 |
3 | export default function Switch(props) {
4 | const [checked, setChecked] = useState(props.checked)
5 | const onChange = e => {
6 | setChecked(e.detail.checked)
7 | props.onChange && props.onChange({ detail: { value: e.detail.checked } })
8 | }
9 | return (
10 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/runtime/build.js:
--------------------------------------------------------------------------------
1 | const esbuild = require('esbuild')
2 |
3 | esbuild.build({
4 | entryPoints: {
5 | master: './master/index.js',
6 | slave: './slave/index.js',
7 | },
8 | outdir: './dist/',
9 | loader: {
10 | '.js': 'jsx'
11 | },
12 | format: 'esm',
13 | jsxFactory: "h",
14 | jsxFragment: "Fragment",
15 | bundle: true,
16 | sourcemap: false,
17 | treeShaking: true,
18 | }).then(() => {
19 | console.log('build success')
20 | })
--------------------------------------------------------------------------------
/compiler/core/plugins/postcss-tag-replacer.js:
--------------------------------------------------------------------------------
1 | module.exports = (opts) => {
2 | return {
3 | postcssPlugin: "postcss-tag-replacer",
4 | Once(root) {
5 | // Plugin code
6 | root.walkRules((rule) => {
7 | if (opts.replaceMap[rule.selector]) {
8 | rule.replaceWith({
9 | selector: opts.replaceMap[rule.selector],
10 | nodes: rule.nodes,
11 | })
12 | }
13 | })
14 | },
15 | }
16 | }
17 | module.exports.postcss = true
18 |
--------------------------------------------------------------------------------
/runtime/master/components/view.js:
--------------------------------------------------------------------------------
1 | import { h } from '../fre-esm'
2 |
3 | function computedStype(str) {
4 | let out = {}
5 | let arr = str.split(';')
6 | arr.forEach(s => {
7 | const [name, value] = s.split(':')
8 | out[name] = value
9 | })
10 | return out
11 | }
12 |
13 | export default function View(props) {
14 | if (props.style) {
15 | var style = computedStype(props.style)
16 | delete props.style
17 | }
18 |
19 | return
20 | }
--------------------------------------------------------------------------------
/compiler/core/packagers/wxss.js:
--------------------------------------------------------------------------------
1 | module.exports = async function packWxss(asset, options) {
2 | let cache = []
3 | asset.output = asset.code
4 | const walk = async (child) => {
5 | for (const dep of child.childAssets.values()) {
6 | if (cache.indexOf(dep.path) < 0) {
7 | asset.output = dep.code + "\n" + asset.output
8 | cache.push(dep.path)
9 | }
10 | if (dep.childAssets.size) {
11 | await walk(dep)
12 | }
13 | }
14 | }
15 | await walk(asset)
16 |
17 | return asset.output
18 | }
19 |
--------------------------------------------------------------------------------
/compiler/core/assets/wxml.js:
--------------------------------------------------------------------------------
1 | const Asset = require("./asset")
2 | const { lex, parse, generate } = require("../../wxml/index.js")
3 |
4 | module.exports = class Wxml extends Asset {
5 | constructor(path, type, name) {
6 | super(path, type, name)
7 | }
8 | async transform(input) {
9 | const tokens = lex(input)
10 | const ast = parse(tokens)
11 | this.ast = ast
12 | let { imports, blocks } = generate(this)
13 | this.blocks = blocks
14 | imports.forEach((i) => this.dependencies.add({ path: i, ext: ".wxml" }))
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/demo/pages/item/index.js:
--------------------------------------------------------------------------------
1 | const app = getApp()
2 |
3 | Component({
4 | properties: {
5 | iitem: {
6 | type: Object,
7 | value: {},
8 | },
9 | },
10 | methods: {
11 | clickIco(e) {
12 | console.log(123)
13 | this.triggerEvent("myevent", e)
14 | },
15 | clear(e) {
16 | this.triggerEvent("clear", e)
17 | },
18 | },
19 | lifetimes: {
20 | attached: function () {
21 | console.log(233)
22 | },
23 | detached: function () {
24 | // 在组件实例被从页面节点树移除时执行
25 | },
26 | },
27 | })
28 |
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/compiler/core/commander.js:
--------------------------------------------------------------------------------
1 | const { program } = require("commander")
2 |
3 | program
4 | .arguments("[buildType]")
5 | .option("-v, --version", "get version")
6 | .option("-w, --watch", "wean watch", false)
7 | .option("-m, --minify", "wean minify", false)
8 | .option("-e, --entry ", "wean entry", "./app.json")
9 | .option("-o, --output ", "wean output", "./dist/")
10 | .option("-p, --public-url ", "wean public-url")
11 | .action((buildType, command) => (command.t = buildType))
12 | .parse()
13 |
14 | const options = program.opts()
15 |
16 | module.exports = options
17 |
--------------------------------------------------------------------------------
/compiler/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smallapp",
3 | "version": "0.0.4",
4 | "description": "",
5 | "main": "index.js",
6 | "bin": {
7 | "smallapp": "bin/index.js",
8 | "sm": "bin/index.js"
9 | },
10 | "scripts": {
11 | "dev": "smallapp -e ../demo/app.json -o ./dist/",
12 | "build": "node build.js",
13 | "debug": "node --inspect ./core/cli.js -e ../demo/app.json -o ../demo/dist/"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git@github.com/yisar/fre-miniapp.git"
18 | },
19 | "keywords": [],
20 | "author": "",
21 | "license": "MIT",
22 | "devDependencies": {
23 | }
24 | }
--------------------------------------------------------------------------------
/runtime/master/components/index.js:
--------------------------------------------------------------------------------
1 | import Button from "./button"
2 | import Text from "./text"
3 | import View from "./view"
4 | import Switch from "./switch"
5 | import Icon from './icon'
6 | import Input from "./input"
7 | import Block from './block'
8 | import Label from "./label"
9 | import RadioGroup from "./radio-group"
10 | import Radio from "./radio"
11 | import Checkbox from "./checkbox"
12 | import CheckboxGroup from "./checkbox-group"
13 | import Image from "./image"
14 |
15 | const comp = { Button, Text, View, Switch, Icon, Input, Block, Label, Radio, RadioGroup, Checkbox, CheckboxGroup, Image }
16 |
17 | export default comp
--------------------------------------------------------------------------------
/compiler/core/assets/asset.js:
--------------------------------------------------------------------------------
1 | const md5 = require('md5')
2 | let clock = 0
3 |
4 | const exts = {
5 | '.js': '.js',
6 | '.wxml': '.jsx',
7 | '.wxss': '.css',
8 | }
9 |
10 | module.exports = class Asset {
11 | constructor(path, type, name,tag) {
12 | this.path = path
13 | this.tag = tag
14 | this.id = clock++
15 | this.hash = md5(this.id)
16 | this.name = name
17 | this.ext = exts[type]
18 | this.type = type.slice(1)
19 | this.dependencies = new Set()
20 | this.childAssets = new Map()
21 | this.siblingAssets = new Map() // 0 wxml 1 js 2 wxss
22 | this.output = {}
23 | this.symbols = new Map()
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/runtime/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@fre-miniapp/runtime",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "worker.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "serve ./",
9 | "build": "node build.js"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/yisar/fre-worker.git"
14 | },
15 | "keywords": [],
16 | "author": "",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/yisar/fre-worker/issues"
20 | },
21 | "homepage": "https://github.com/yisar/fre-worker#readme",
22 | "dependencies": {
23 | "rollup": "^2.67.2"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/compiler/core/plugins/esbuild-component-tag.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 |
3 | module.exports = options => {
4 | return {
5 | name: 'componentTag',
6 | setup(build) {
7 | build.onLoad({ filter: /.+/ }, async ({ path }) => {
8 | const source = await fs.promises.readFile(path, "utf8")
9 | const pagecode = `\nPage.id="${options.id}"\n` + source
10 | const componentcode = `\nComponent.id="${options.id}"\nComponent.pid="${options.pid}"\nComponent.tag="${options.tag || ''}"\n` + source
11 | return {
12 | contents: options.tag ? componentcode : pagecode,
13 | }
14 | })
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/compiler/core/packagers/js.js:
--------------------------------------------------------------------------------
1 | module.exports = async function packJs(asset, options) {
2 | const defer = []
3 | const cache = []
4 | asset.out = asset.code
5 | const walk = async (child) => {
6 | for (const dep of child.childAssets.values()) {
7 | if (dep.tag) {
8 | defer.push(dep.code)
9 | } else {
10 | if (cache.indexOf(dep.path) < 0) {
11 | asset.out = dep.code + "\n" + asset.out
12 | cache.push(dep.path)
13 | }
14 | }
15 | if (dep.childAssets.size) {
16 | await walk(dep)
17 | }
18 | }
19 |
20 | for (const code of defer) {
21 | asset.out += "\n" + code
22 | }
23 | }
24 | walk(asset)
25 | return asset.out + "\n\n"
26 | }
27 |
--------------------------------------------------------------------------------
/runtime/master/helper.js:
--------------------------------------------------------------------------------
1 | import { getInsById } from "./app";
2 |
3 |
4 | export function $handleEvent(name, id, custom) {
5 | const ins = getInsById(id)
6 | const method = ins[name] || (ins.methods || {})[name] || function () { }
7 | ins.eventMap[custom] = name
8 | return (e) => {
9 | if (e.type === 'keydown' && e.keyCode !== 13) {
10 | return
11 | }
12 | if (e.target) {
13 | e.target.dataset = e.dataset // 兼容微信
14 | }
15 | method.call(ins, e)
16 | }
17 | }
18 |
19 | export const $for = (arr, fn, key) => {
20 | arr = arr || []
21 | return arr.map((item, index) => {
22 | const vdom = fn(item)
23 | vdom.key = key || index
24 | return vdom
25 | })
26 | }
27 |
--------------------------------------------------------------------------------
/demo/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [
3 | "pages/index/index"
4 | ],
5 | "tabBar": {
6 | "custom": true,
7 | "color": "#000000",
8 | "selectedColor": "#000000",
9 | "backgroundColor": "#000000",
10 | "list": [
11 | {
12 | "pagePath": "page/component/index",
13 | "text": "组件",
14 | "iconPath": "./public/0.png",
15 | "selectedIconPath": "./public/1.png"
16 | }
17 | ]
18 | },
19 | "window": {
20 | "navigationBarBackgroundColor": "#fff",
21 | "navigationBarTextStyle": "black",
22 | "navigationBarTitleText": "v3-demo-todomvc",
23 | "backgroundColor": "#eeeeee",
24 | "backgroundTextStyle": "light",
25 | "enablePullDownRefresh": true
26 | },
27 | "sitemapLocation": "sitemap.json"
28 | }
--------------------------------------------------------------------------------
/smallappandroid/app/src/androidTest/java/com/smallapp/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.smallapp
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.smallapp", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/smallappandroid/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
20 |
--------------------------------------------------------------------------------
/antlr4/Wxml.g4:
--------------------------------------------------------------------------------
1 | grammar Wxml;
2 |
3 | wxml
4 | : element*
5 | ;
6 |
7 | element
8 | : OPEN_TAG attribute* CLOSE_TAG
9 | | OPEN_TAG attribute* '>' content* '' IDENTIFIER '>'
10 | ;
11 |
12 | attribute
13 | : IDENTIFIER '=' STRING_LITERAL
14 | | IDENTIFIER '=' DYNAMIC_EXPRESSION
15 | ;
16 |
17 | content
18 | : TEXT
19 | | element
20 | ;
21 |
22 | DYNAMIC_EXPRESSION
23 | : '{{' IDENTIFIER ('.' IDENTIFIER)* '}}'
24 | ;
25 |
26 | OPEN_TAG
27 | : '<' IDENTIFIER
28 | ;
29 |
30 | CLOSE_TAG
31 | : '/>'
32 | | '>' // Assuming self-closing tags are handled by the CLOSE_TAG rule
33 | ;
34 |
35 | IDENTIFIER
36 | : [a-zA-Z_][a-zA-Z_0-9-]*
37 | ;
38 |
39 | STRING_LITERAL
40 | : '"' (~["\\]|'\\'.)* '"'
41 | | '\'' (~['\\]|'\\'.)* '\''
42 | ;
43 |
44 | TEXT
45 | : ~[<]+
46 | ;
47 |
48 | WS
49 | : [ \t\r\n]+ -> skip
50 | ;
51 |
--------------------------------------------------------------------------------
/smallappandroid/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/compiler/core/assets/js.js:
--------------------------------------------------------------------------------
1 | const Asset = require("./asset")
2 | const esbuild = require('esbuild')
3 | const componentTag = require('../plugins/esbuild-component-tag')
4 | const { getId } = require('../packagers/util')
5 |
6 | module.exports = class JS extends Asset {
7 | constructor(path, type, name) {
8 | super(path, type, name)
9 | }
10 |
11 | async transform() {
12 | const out = await esbuild.build({
13 | entryPoints: [this.path],
14 | bundle: true,
15 | format: 'esm',
16 | sourcemap: false,
17 | write: false,
18 | outdir: 'out',
19 | treeShaking: true,
20 | plugins: [componentTag({
21 | id: this.parent.id + '',
22 | tag: this.parent.tag,
23 | pid: getId(this.parent)
24 | })]
25 | })
26 |
27 | this.code = String.fromCharCode.apply(null, out.outputFiles[0].contents)
28 | }
29 | }
--------------------------------------------------------------------------------
/demo/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | view {
2 | text-align: center;
3 | }
4 |
5 | button {
6 | margin: 15px auto;
7 | }
8 |
9 | image {
10 | height: 100px;
11 | width: 100px;
12 | border-radius: 100px;
13 | margin: 15px;
14 | border: 4px solid var(--primary-border-color);
15 | }
16 |
17 | /* text {
18 | display: block;
19 | } */
20 |
21 |
22 | .input {
23 | background: #eee;
24 | padding: 10px !important;
25 | display: block;
26 | border-radius: 5px !important;
27 | width: 90%;
28 | margin: 10px auto;
29 | }
30 |
31 | .b {
32 | color: #bbb;
33 | margin: 10px;
34 | }
35 |
36 | .info {
37 | margin-top: 20px;
38 | display: block;
39 | }
40 |
41 | .rank {
42 | padding: 10px;
43 | color: royalblue;
44 |
45 | }
46 |
47 | .rank .r {
48 | padding: 2px;
49 | }
50 |
51 | .title {
52 | font-size: 50px;
53 | color: orange;
54 | font-weight: bold;
55 | margin: 30px;
56 | }
--------------------------------------------------------------------------------
/runtime/master/page.js:
--------------------------------------------------------------------------------
1 | import { getApp } from './app'
2 | import { setter } from './safe-obj'
3 | import { global } from './global'
4 |
5 | let currentPage = null
6 | const app = getApp()
7 | export function Page(option) {
8 | const pageid = Page.id
9 | const page = new _Page(pageid, option)
10 | app.graph[pageid] = page
11 | currentPage = page
12 | }
13 |
14 | export function getCurrentPage() {
15 | return currentPage
16 | }
17 |
18 | class _Page {
19 | constructor(id, option) {
20 | this.id = id
21 | this.children = new Map()
22 | this.parent = null
23 | this.eventMap = {}
24 | for (const key in option) {
25 | this[key] = option[key]
26 | }
27 | }
28 |
29 | setData(data, extra) {
30 | this.data = { ...this.data, ...data }
31 | const setState = global.setStates[this.id]
32 | if (typeof data === 'string') {
33 | setter(data)(this.data, extra)
34 | }
35 | setState(this.data)
36 | }
37 | }
--------------------------------------------------------------------------------
/compiler/core/packagers/berial.js:
--------------------------------------------------------------------------------
1 | const { manifest } = require('../package.js')
2 | const Path = require('path')
3 | const esbuild = require('esbuild')
4 |
5 | module.exports = async function packBerial(asset, options) {
6 | const edir = Path.resolve(Path.dirname(options.e))
7 |
8 | try {
9 | var { code } = await esbuild.transform(asset.output.jsx, {
10 | jsxFactory: 'fre.h',
11 | jsxFragment: 'fre.Fragment',
12 | loader: 'jsx',
13 | format: 'cjs',
14 | })
15 | } catch (e) {
16 | console.log(e)
17 | }
18 | asset.output.jsx = code
19 |
20 | const path = asset.path.replace(edir, '').replace(/\\/g, '/').replace('.json', '')
21 |
22 | const prefix = options.p ? options.p : '/'
23 | const basename = options.p ? `${'/' + Path.basename(options.p)}` : ''
24 | const hash = prefix + asset.hash
25 | manifest.push({
26 | id: asset.id,
27 | info: asset.ast,
28 | scripts: [asset.output.js, asset.output.jsx],
29 | styles: [hash + '.css'],
30 | path: `${basename + path}`,
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/runtime/master/components/radio.js:
--------------------------------------------------------------------------------
1 | import { h } from '../fre-esm'
2 | import { useContext } from '../context'
3 | import { RadioContext } from './radio-group'
4 |
5 | function Radio(props) {
6 | const change = useContext(RadioContext)
7 | let { color, id, style, value } = props
8 |
9 | const onClick = e => {
10 | if (props.onClick) {
11 | props.onClick(e)
12 | }
13 | }
14 |
15 | const onChange = e => {
16 | e.stopPropagation && e.stopPropagation()
17 | change && change({ detail: { value } })
18 | if (props.onChange) {
19 | props.onChange({
20 | detail: {
21 | value: value,
22 | checked: e.target.checked,
23 | },
24 | })
25 | }
26 | }
27 |
28 | if (props.checked === 'undefined'){ // why??
29 | props.checked = false
30 | }
31 |
32 | return (
33 |
40 | )
41 | }
42 |
43 | export default Radio
--------------------------------------------------------------------------------
/runtime/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 伊撒尔
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/java/com/smallapp/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.smallapp.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/runtime/master/context.js:
--------------------------------------------------------------------------------
1 | import {
2 | useReducer,
3 | useRef,
4 | useEffect,
5 | } from "./fre-esm"
6 |
7 | export const createContext = (defaultValue) => {
8 | const context = {
9 | value: defaultValue,
10 | subs: new Set(),
11 | Provider: ({ value, children = "" }) => {
12 | useEffect(() => {
13 | context.subs.forEach((fn) => fn(value))
14 | context.value = value
15 | })
16 | return children
17 | },
18 | }
19 | return context
20 | }
21 |
22 | export const useContext = (context, selector) => {
23 | const subs = context.subs
24 | const [, forceUpdate] = useReducer((c) => c + 1, 0)
25 | const selected = selector ? selector(context.value) : context.value
26 | const ref = useRef(null)
27 | useEffect(() => {
28 | ref.current = selected
29 | })
30 | useEffect(() => {
31 | const fn = (nextValue) => {
32 | if (selector && ref.current === selector(nextValue)) return
33 | forceUpdate(nextValue)
34 | }
35 | subs.add(fn)
36 | return () => subs.delete(fn)
37 | }, [subs])
38 | return selected
39 | }
--------------------------------------------------------------------------------
/runtime/master/global.js:
--------------------------------------------------------------------------------
1 | import { Fragment, h, render, useCallback, useEffect, useLayout, useMemo, useReducer, useRef, useState } from './fre-esm.js'
2 | import comp from './components/index'
3 | import { Page } from './page'
4 | import { getApp } from './app.js'
5 | import { Component } from './component.js'
6 | import { $handleEvent, $for } from './helper.js'
7 | import { wx } from './wxapi'
8 | import { expr } from './expr.js'
9 | import { getter, setter } from './safe-obj.js'
10 |
11 | export const global = {
12 | modules: {},
13 | Page,
14 | getApp,
15 | Component,
16 | fre: { Fragment, h, render, useCallback, useEffect, useLayout, useMemo, useReducer, useRef, useState },
17 | comp,
18 | $handleEvent,
19 | $for,
20 | setStates: {},
21 | wx,
22 | expr,
23 | getter,
24 | setter,
25 | native: {
26 | readFileSync(path) {
27 | var request = new XMLHttpRequest()
28 | request.open('GET', '/' + path, false)
29 | request.send(null)
30 | if (request.status === 200) {
31 | return request.responseText
32 | }
33 | },
34 | log(msg) {
35 | console.log(msg)
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/runtime/master/init.js:
--------------------------------------------------------------------------------
1 | import { execScript } from './exec-script.js'
2 | import { getCurrentPage } from './page.js'
3 | import { global as ref } from './global'
4 | import { render, h, useEffect } from './fre-esm'
5 |
6 | export function init(manifest) {
7 | let path = '/'
8 | let p = ''
9 | const pages = manifest.pages
10 | if (path === '/') {
11 | p = pages[0]
12 | } else {
13 | p = pages.find(i => i.path === path)
14 | }
15 |
16 | const { scripts, styles } = p
17 | let link = document.createElement('link')
18 | link.setAttribute("href", "." + styles[0]);
19 | link.setAttribute("rel", "stylesheet");
20 | document.body.appendChild(link);
21 | execScript(scripts[1], ref);
22 | execScript(scripts[0], ref);
23 |
24 | const page = getCurrentPage()
25 |
26 | const c = ref.modules[scripts[1]].default
27 |
28 | const wrapComp = () => {
29 | useEffect(() => {
30 | page.onLoad && page.onLoad()
31 | return () => {
32 | page.unLoad && page.unLoad()
33 | }
34 | }, [])
35 |
36 | return h(c, { data: page.data })
37 | }
38 |
39 |
40 | render(h(wrapComp, {}), globalThis.document.body)
41 | }
--------------------------------------------------------------------------------
/runtime/master/component.js:
--------------------------------------------------------------------------------
1 | import { getInsById, getApp } from "./app"
2 | let currentComponent = null
3 |
4 | const app = getApp()
5 |
6 | export function Component(option) {
7 | const { pid, tag, id } = Component
8 | const parent = getInsById(pid)
9 | const c = new _Component(id, tag, pid, option)
10 | parent.children.set(id, c)
11 | currentComponent = c
12 | app.graph[id] = c
13 | }
14 |
15 | export function getCurrentPage() {
16 | return currentPage
17 | }
18 |
19 | class _Component {
20 | constructor(id, tag, pid, option) {
21 | this.id = id
22 | this.tag = tag
23 | this.pid = pid
24 | this.children = new Map()
25 | this.parent = null
26 | this.eventMap = {}
27 | for (const key in option) {
28 | this[key] = option[key]
29 | }
30 | }
31 |
32 | setData(data) {
33 | this.data = { ...this.data, ...data }
34 | const setState = global.setStates[this.id]
35 | setState(this.data)
36 | }
37 |
38 | triggerEvent(name, e) {
39 | const parent = getInsById(this.pid)
40 | const realname = parent.eventMap['bind' + name]
41 | e.target.dataset = e.dataset
42 | console.log(e)
43 | parent[realname].call(parent, e)
44 | }
45 | }
--------------------------------------------------------------------------------
/runtime/master/components/checkbox.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from '../fre-esm'
2 | import { useContext } from '../context'
3 | import { CheckboxContext } from './checkbox-group.js'
4 |
5 | function Checkbox(props) {
6 | let { change, value: newValue } = useContext(CheckboxContext)
7 | const [checked, setChecked] = useState(props.checked)
8 | let { color, id, style, value } = props
9 |
10 | useEffect(() => {
11 | if (checked && newValue.indexOf(value) === -1) {
12 | newValue.push(value)
13 | } else if (!checked) {
14 | const index = newValue.indexOf(value)
15 | newValue.splice(index, 1)
16 | }
17 | change && change({ detail: { value: newValue } })
18 | }, [checked])
19 |
20 | const onChange = e => {
21 | e.stopPropagation && e.stopPropagation()
22 | setChecked(e.target.checked)
23 | if (props.onChange) {
24 | props.onChange({
25 | detail: {
26 | value,
27 | checked,
28 | },
29 | })
30 | }
31 | }
32 |
33 | return (
34 |
43 | )
44 | }
45 |
46 | export default Checkbox
--------------------------------------------------------------------------------
/runtime/master/safe-obj.js:
--------------------------------------------------------------------------------
1 | export function getter(obj, keys, def, p, undef) {
2 | keys = keyArr(keys)
3 | for (p = 0; p < keys.length; p++) {
4 | const k = keys[p]
5 | obj = obj ? obj[isNaN(+k) ? k : +k] : undef
6 | }
7 | return obj === undef ? def : obj
8 | }
9 |
10 | function keyArr(key) {
11 | return key.match(/[^.^\]^[]+|(?=\[\]|\.\.)/g,) || ['']
12 | }
13 |
14 | export function setter(source, keys, update) {
15 | keys = keyArr(keys)
16 |
17 | let next = copy(source),
18 | last = next,
19 | i = 0,
20 | l = keys.length
21 |
22 | for (; i < l; i++) {
23 | last = last[keys[i]] =
24 | i === l - 1
25 | ? update && !!update.call
26 | ? update(last[keys[i]])
27 | : update
28 | : copy(last[keys[i]])
29 | }
30 | return next
31 | }
32 |
33 | function copy(source) {
34 | let to = source && !!source.pop ? [] : {}
35 | for (let i in source) to[i] = source[i]
36 | return to
37 | }
38 |
39 | // let obj = {
40 | // foo: {
41 | // bar: ['hi', { buz: { baz: 'hello' } }]
42 | // }
43 | // }
44 |
45 | // let next = setter(obj, 'foo.bar[1].buz.baz', 'world')
46 | // console.log(JSON.stringify(next))
47 | // console.log(getter(next, 'foo.bar[1].buz.baz'))
--------------------------------------------------------------------------------
/compiler/core/packagers/wxml.js:
--------------------------------------------------------------------------------
1 | const { titleCase } = require('./util')
2 |
3 | module.exports = async function packWxml(asset) {
4 | const walk = async (child) => {
5 | for (const dep of child.childAssets.values()) {
6 | wiredBlock(dep.blocks, asset)
7 | if (dep.childAssets.size) {
8 | await walk(dep)
9 | }
10 | }
11 | }
12 | asset.out = ''
13 | wiredBlock(asset.blocks, asset)
14 | walk(asset)
15 | const code =
16 | asset.parent.type === 'page'
17 | ? `export default (props) => {
18 | const [state, setState] = fre.useState(props.data)
19 | useEffect(()=>{
20 | setStates[${asset.parent.id}] = setState
21 | },[]);
22 | return ${asset.out}
23 | };\n`
24 | : `comp.${titleCase(asset.parent.tag)} = (props) =>{
25 | const [state, setState] = fre.useState({})
26 | useEffect(()=>{
27 | setStates[${asset.parent.id}] = setState
28 | },[]);
29 | return <>${asset.out}>
30 | };\n`
31 |
32 | return code
33 | }
34 |
35 | function wiredBlock(blocks, asset) {
36 | for (let key in blocks) {
37 | let value = blocks[key]
38 | if (isNaN(+key)) {
39 | asset.out = asset.out.replace(`$template$${key}$`, value).replace(`$slot$${key}$`, value) || ''
40 | } else {
41 | asset.out += value || ''
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
15 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smallapp",
3 | "version": "0.0.5",
4 | "description": "",
5 | "main": "index.js",
6 | "bin": {
7 | "smallapp": "./compiler/bin/index.js",
8 | "sm": "./compiler/bin/index.js"
9 | },
10 | "devDependencies": {
11 | "@babel/cli": "^7.23.9",
12 | "@babel/core": "^7.23.9",
13 | "@babel/preset-env": "^7.23.9",
14 | "commander": "^7.1.0",
15 | "cross-fetch": "^3.0.6",
16 | "nanoid": "^3.1.21",
17 | "nodemon": "^2.0.6",
18 | "pnpm": "^6.32.9",
19 | "postcss-selector-parser": "^6.0.4",
20 | "prettier": "^2.2.1",
21 | "rollup": "^4.9.6",
22 | "rollup-plugin-babel": "^4.4.0",
23 | "serve": "^13.0.2"
24 | },
25 | "scripts": {
26 | "watch": "bash debug.sh",
27 | "test": "echo \"Error: no test specified\" && exit 1",
28 | "debug": "cd compiler && pnpm run debug",
29 | "start": "npm run build && npm run debug",
30 | "build": "cd runtime && pnpm run build",
31 | "build:wxml": "cd ./packages/wxml && wasm-pack build --target nodejs "
32 | },
33 | "keywords": [],
34 | "author": "",
35 | "license": "ISC",
36 | "dependencies": {
37 | "chalk": "^5.2.0",
38 | "chokidar": "^3.5.3",
39 | "ejs": "^3.1.9",
40 | "esbuild": "^0.20.0",
41 | "express": "^4.18.2",
42 | "md5": "^2.3.0",
43 | "postcss": "^8.4.33"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/compiler/core/serve.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const express = require('express')
3 | const { getIndexHtmlCode } = require('./packagers/util')
4 | const { PORT = 1122 } = process.env
5 |
6 | module.exports = function serve(options) {
7 | const basetdir = path.join(__dirname, '../../demo/dist')
8 | const distdir = path.join(options.i, options.o)
9 | const runtimedir = path.join(__dirname,'../../runtime/dist')
10 |
11 | const app = express()
12 | .use(express.static(basetdir))
13 | .use(express.static(distdir))
14 | .use(express.static(runtimedir))
15 | .get('/', (req, res) => {
16 | getIndexHtmlCode().then((data) => {
17 | res.end(data)
18 | })
19 | })
20 | .use(redirect)
21 | .get("/hello", (req, res) => {
22 | res.end('hello world')
23 | })
24 | .get("*", (req, res) => {
25 | res.end('404')
26 | })
27 | .listen(PORT, (err) => {
28 | if (err) throw err
29 | console.log(`serve on http://localhost:${PORT}`)
30 | })
31 |
32 | return app.server
33 | }
34 |
35 | const redirect = function (req, res, next) {
36 | res.redirect = (location) => {
37 | let str = `Redirecting to ${location}`
38 | res.writeHead(302, {
39 | Location: location,
40 | "Content-Type": "text/plain",
41 | "Content-Length": str.length,
42 | })
43 | res.end(str)
44 | }
45 | next()
46 | }
47 |
--------------------------------------------------------------------------------
/smallappandroid/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/compiler/core/assets/wxss.js:
--------------------------------------------------------------------------------
1 | const Asset = require("./asset")
2 | const postcss = require("postcss")
3 | const postcssTagReplacer = require("../plugins/postcss-tag-replacer")
4 | const postcssRpx2rem = require("../plugins/postcss-rpx2rem")
5 |
6 |
7 | module.exports = class Wxss extends Asset {
8 | constructor(path, type, name) {
9 | super(path, type, name)
10 | }
11 |
12 | addDep() {
13 | let that = this
14 | return {
15 | postcssPlugin: "postcss-add-dep",
16 | AtRule(node) {
17 | if (node.name === "import") {
18 | const dep = { path: node.params.replace(/"/g, ""), ext: ".wxss" }
19 | that.dependencies.add(dep)
20 | node.type = "comment"
21 | node.text = JSON.stringify(dep)
22 | }
23 | },
24 | }
25 | }
26 |
27 | async transform(input) {
28 | // const wxml = this.parent.siblingAssets.get(".wxml")
29 | // const id = wxml ? `data-w-${wxml.hash.slice(0, 6)}` : null
30 | // const scoped = false
31 |
32 | this.code = postcss([
33 | postcssTagReplacer({
34 | // css 需要替换的标签
35 | replaceMap: {
36 | view: "div",
37 | icon: "i",
38 | text: "span",
39 | navigator: "a",
40 | image: "img",
41 | page: "#root",
42 | },
43 | }),
44 | this.addDep(),
45 | // postcssSopedCss({
46 | // id,
47 | // }),
48 | postcssRpx2rem(),
49 | ]).process(input).css
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/runtime/master/wxapi.js:
--------------------------------------------------------------------------------
1 | let callbacks = {}
2 |
3 | let index = 0;
4 |
5 | export const wx = {
6 | navigateTo(options) {
7 | sendMessage('navigateTo', options)
8 | },
9 | showToast(options) {
10 | sendMessage('showToast', options)
11 | },
12 | showPicker(options) {
13 | sendMessage('showPicker', options)
14 | },
15 | request(url, data) {
16 | if (typeof fetch !== 'undefined') {
17 | return new Promise(resolve => {
18 | fetch(url, data).then(res => res.json()).then(data => {
19 | resolve(data)
20 | })
21 | })
22 | }
23 | }
24 | }
25 |
26 | function serOptions(options) {
27 | let out = {}
28 | for (const key in options) {
29 | let val = options[key]
30 | if (typeof val === 'function') {
31 | let id = '.' + index
32 | out[key] = id
33 | callbacks[id] = val
34 | index++
35 | } else {
36 | out[key] = val
37 | }
38 | }
39 | return out
40 | }
41 |
42 | function sendMessage(name, options) {
43 | const ser = serOptions(options)
44 | const args = {
45 | type: 'wxapi',
46 | name: name,
47 | options: ser
48 | }
49 |
50 | send(args)
51 | }
52 |
53 | export function handleWxEvent(data) {
54 | console.log(data.id)
55 | let callback = callbacks[data.id]
56 | callback(data.res)
57 | callbacks[data.id] = undefined
58 | }
--------------------------------------------------------------------------------
/compiler/core/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | wean-demo
7 |
8 |
29 |
30 |
31 |
32 |
33 |
34 | <%-
35 | manifest.map(function(arg){
36 | return `<${arg.name}>${arg.name}>`
37 | }).join("\n")
38 | _%>
39 |
40 |
44 |
45 |
--------------------------------------------------------------------------------
/compiler/core/cli.js:
--------------------------------------------------------------------------------
1 | const chokidar = require("chokidar")
2 | const Path = require("path")
3 | const build = require("./bundle")
4 | const pack = require("./package")
5 | const serve = require("./serve")
6 | const argv = require("./commander")
7 | // const { spawn, execFile, execFileSync } = require("child_process")
8 | const BUILD_TYPE = {
9 | BUILD: 'build'
10 | }
11 |
12 | async function run(argv) {
13 | const options = {
14 | e: argv.entry,
15 | o: argv.output,
16 | i: "/",
17 | w: argv.watch,
18 | m: argv.minify,
19 | p: argv.publicUrl,
20 | t: argv.t,
21 | }
22 | start(options)
23 | if (options.w) {
24 | chokidar
25 | .watch(Path.dirname(options.e), {
26 | ignored: /(dist|.git)/,
27 | persistent: true,
28 | awaitWriteFinish: {
29 | stabilityThreshold: 500,
30 | pollInterval: 500,
31 | },
32 | })
33 | .on("change", (path) => {
34 | console.log(`rebuild ${path}`)
35 | start(options)
36 | })
37 | }
38 | }
39 |
40 | async function start(options) {
41 | options.old && options.old.close()
42 | console.log('start compiling...')
43 | const start = Date.now()
44 | const adt = await build(options.e, options)
45 | await pack(adt, options)
46 | const end = Date.now()
47 | console.log(`compile total time ${end - start}ms`)
48 | serve(options)
49 | // execFile(Path.join(__dirname, '../../container/fre_miniapp.exe'))
50 | }
51 |
52 | if (argv.version) {
53 | console.log('version:', require('../package.json').version)
54 | } else {
55 | run(argv)
56 | }
57 |
--------------------------------------------------------------------------------
/runtime/master/exec-script.js:
--------------------------------------------------------------------------------
1 | export function execScript(path, ref) {
2 | const { modules, native, fre, comp, getApp, Page, Component, App, $handleEvent, setStates, $for, wx } = ref
3 | const str = path
4 | const fn = new Function('module', 'require', 'fre', 'comp', 'getApp', 'Page', 'Component', 'App', '$handleEvent', '$for', 'setStates', 'wx', str)
5 |
6 | const relative = function (parent) {
7 | const resolve = function (path) {
8 | var orig = path;
9 | var reg = path + '.js';
10 | var index = path + '/index.js';
11 | return modules[reg] && reg
12 | || modules[index] && index
13 | || orig;
14 | };
15 | function require(p) {
16 | return modules[resolve(p)];
17 | }
18 | return function (p) {
19 | if ('.' != p.charAt(0)) return require(p);
20 | var path = parent.split('/');
21 | var segs = p.split('/');
22 | path.pop();
23 |
24 | for (var i = 0; i < segs.length; i++) {
25 | var seg = segs[i];
26 | if ('..' == seg) path.pop();
27 | else if ('.' != seg) path.push(seg);
28 | }
29 |
30 | return require(path.join('/'));
31 | };
32 | };
33 |
34 | var module = {
35 | exports: {}
36 | };
37 | try {
38 | fn.call(module.exports, module, relative(path), fre, comp, getApp, Page, Component, App, $handleEvent, $for, setStates, wx);
39 | } catch (e) {
40 | console.log(e)
41 | }
42 | modules[path] = module.exports
43 | }
--------------------------------------------------------------------------------
/compiler/core/packagers/util.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 |
4 | function toHump(name) {
5 | return name.replace(/\-(\w)/g, (all, letter) => letter.toUpperCase())
6 | }
7 |
8 | const titleCase = (str) => str.slice(0, 1).toUpperCase() + toHump(str).slice(1)
9 |
10 | const random = function randomString(len) {
11 | len = len || 6
12 | var $chars = "abcdefhijkmnprstwxyz"
13 | var letter = ""
14 | for (i = 0; i < len; i++) {
15 | letter += $chars.charAt(Math.floor(Math.random() * $chars.length))
16 | }
17 | return letter
18 | }
19 |
20 | function getId(asset) {
21 | let p = asset.parent
22 | while (p && p.type === 'wxml') {
23 | p = p.parent
24 | }
25 | return p ? p.id : null
26 | }
27 |
28 | async function getIndexHtmlCode() {
29 | const defaultCss = fs.readFileSync(path.join(__dirname, '../css', 'default.css'), 'utf-8')
30 | return `
31 |
32 |
33 |
34 |
35 |
36 | Fre miniapp
37 |
38 |
39 |
40 |
41 |
42 |
52 |
53 |
54 | `
55 | }
56 |
57 | module.exports = {
58 | titleCase: titleCase,
59 | random: random,
60 | getId: getId,
61 | getIndexHtmlCode: getIndexHtmlCode
62 | }
63 |
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/demo/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "项目配置文件",
3 | "packOptions": {
4 | "ignore": []
5 | },
6 | "setting": {
7 | "urlCheck": true,
8 | "es6": true,
9 | "enhance": false,
10 | "postcss": true,
11 | "preloadBackgroundData": false,
12 | "minified": true,
13 | "newFeature": false,
14 | "coverView": true,
15 | "nodeModules": false,
16 | "autoAudits": false,
17 | "showShadowRootInWxmlPanel": true,
18 | "scopeDataCheck": false,
19 | "uglifyFileName": false,
20 | "checkInvalidKey": true,
21 | "checkSiteMap": true,
22 | "uploadWithSourceMap": true,
23 | "compileHotReLoad": false,
24 | "useMultiFrameRuntime": true,
25 | "useApiHook": true,
26 | "useApiHostProcess": true,
27 | "babelSetting": {
28 | "ignore": [],
29 | "disablePlugins": [],
30 | "outputPath": ""
31 | },
32 | "enableEngineNative": false,
33 | "bundle": false,
34 | "useIsolateContext": true,
35 | "useCompilerModule": true,
36 | "userConfirmedUseCompilerModuleSwitch": false,
37 | "userConfirmedBundleSwitch": false,
38 | "packNpmManually": false,
39 | "packNpmRelationList": [],
40 | "minifyWXSS": true
41 | },
42 | "compileType": "miniprogram",
43 | "libVersion": "2.17.3",
44 | "appid": "wxf90ca3e12cfc42ab",
45 | "projectname": "miniprogram-5",
46 | "debugOptions": {
47 | "hidedInDevtools": []
48 | },
49 | "scripts": {},
50 | "staticServerOptions": {
51 | "baseURL": "",
52 | "servePath": ""
53 | },
54 | "isGameTourist": false,
55 | "condition": {
56 | "search": {
57 | "list": []
58 | },
59 | "conversation": {
60 | "list": []
61 | },
62 | "game": {
63 | "list": []
64 | },
65 | "plugin": {
66 | "list": []
67 | },
68 | "gamePlugin": {
69 | "list": []
70 | },
71 | "miniprogram": {
72 | "list": []
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # smallapp
4 |
5 | `smallapp` is a [Chinese miniapp architecture](https://www.w3.org/TR/mini-app-white-paper) implementation.
6 |
7 | As of now, there are over 7 million miniapps in China, Chinese people do not like to use browsers or search engines.
8 |
9 | Musk envied `WeChat` and he really wanted this miniapp architecture, so I opened it up.
10 |
11 | ### Docs
12 |
13 | smallapp follows WeChat's miniapp standard, you should refer to WeChat's documentation.
14 |
15 | [WeChat miniapp docs](https://developers.weixin.qq.com/miniprogram/en/dev/framework/)
16 |
17 | ### Syntax
18 |
19 | ```html
20 |
21 | {{count}}
22 |
23 |
24 | ```
25 | ```js
26 | Page({
27 | data: {
28 | count: 0
29 | },
30 | add: () {
31 | this.setData({
32 | count: this.data.count + 1
33 | })
34 |
35 | wx.showToast({
36 | title: 'count is added!'
37 | })
38 | }
39 | })
40 | ```
41 |
42 | ### Demos
43 |
44 | [https://v.douyin.com/Ug9bwvq/](https://v.douyin.com/Ug9bwvq/)
45 |
46 |
47 |
48 | ### Principle
49 |
50 | 1. compiler
51 |
52 | ```
53 | smallapp build -e app.json -o /dist
54 | ```
55 |
56 | This step will package the miniapp project into js files, which is double threaded. The jsx file is used for rendering threads, and the js file is used for logical threads.
57 |
58 | 2. worker
59 |
60 | The logical thread is responsible for running JavaScript logic, and you need to find a JavaScript runtime, such as worker.
61 |
62 | - Web worker
63 | - Cloudflare worker
64 | - quickjs/v8/hermes
65 |
66 | As long as it has the standard API and communication mechanism of the worker, it can serve as the logical layer of the miniapp.
67 |
68 | 3. Native container
69 |
70 | Miniapps runs on a super app, such as Wechat/Alipay/Baidu and its API is provided by the native container.
71 |
--------------------------------------------------------------------------------
/compiler/wxml/parse.js:
--------------------------------------------------------------------------------
1 |
2 | function parse(tokens) {
3 | let ast = {
4 | children: [],
5 | }
6 |
7 | let state = {
8 | current: 0,
9 | tokens,
10 | }
11 |
12 | while (state.current < tokens.length) {
13 | let child = parseWalk(state)
14 | if (child) {
15 | ast.children.push(child)
16 | }
17 |
18 | }
19 | return ast
20 | }
21 |
22 | function parseWalk(state) {
23 | let token = state.tokens[state.current]
24 | let prevToken = state.tokens[state.current - 1]
25 |
26 | function move(num) {
27 | state.current += num != null ? num : 1
28 | token = state.tokens[state.current]
29 | prevToken = state.tokens[state.current - 1]
30 | }
31 |
32 | if (token.type === "text") {
33 | move()
34 | return prevToken.value
35 | }
36 |
37 | if (token.type === "comment") {
38 | move()
39 | return null
40 | }
41 |
42 | if (token.type === "tag") {
43 | let type = token.value
44 | let closeStart = token.closeStart
45 | let closeEnd = token.closeEnd
46 |
47 | let node = parseNode(type, token.attributes, [])
48 | move()
49 | if (closeEnd === true) {
50 | return node
51 | } else if (closeStart) {
52 | return null
53 | } else if (token) {
54 | while (
55 | token.type !== "tag" ||
56 | (token.type === "tag" &&
57 | ((!token.closeStart && !token.closeEnd) || token.value !== type))
58 | ) {
59 | let child = parseWalk(state)
60 | if (child) {
61 | node.children.push(child)
62 | }
63 | move(0)
64 | if (!token) {
65 | break
66 | }
67 | }
68 | move()
69 | }
70 | return node
71 | }
72 | move()
73 | return
74 | }
75 |
76 | function parseNode(name, attributes, children) {
77 | let type = "node"
78 | if (
79 | name.indexOf("-") > -1 ||
80 | (name[0] === name[0].toUpperCase() && name[0] !== name[0].toLowerCase())
81 | ) {
82 | type = "component"
83 | }
84 |
85 | return {
86 | type,
87 | name,
88 | attributes,
89 | children
90 | }
91 | }
92 | module.exports = parse
93 |
--------------------------------------------------------------------------------
/compiler/core/assets/json.js:
--------------------------------------------------------------------------------
1 | const Asset = require("./asset")
2 | const fs = require('fs')
3 | const path = require('path')
4 |
5 | class App extends Asset {
6 | // app.json
7 | constructor(path, type, name) {
8 | super(path, type, name)
9 | }
10 | async transform(input) {
11 | this.ast = JSON.parse(input)
12 | for (const key in this.ast) {
13 | const value = this.ast[key]
14 | if (key === "pages") {
15 | for (let i = 0; i < value.length; i++) {
16 | const path = value[i]
17 | this.dependencies.add({ path, ext: ".page" })
18 | }
19 | }
20 | if (key === "usingComponents") {
21 | // toto 公共组件
22 | }
23 | if (key === 'tabBar') {
24 | // tabbar 只需要处理附件,不需要实现
25 | value.list.forEach(item => {
26 | item.iconPath = path.join(path.dirname(this.path), item.iconPath)
27 | item.selectedIconPath = path.join(path.dirname(this.path), item.selectedIconPath)
28 | })
29 | }
30 | }
31 | }
32 | }
33 |
34 | class Page extends Asset {
35 | constructor(path, type, name) {
36 | super(path, type, name)
37 | }
38 | async transform(input) {
39 | this.ast = JSON.parse(input)
40 | for (const key in this.ast) {
41 | const value = this.ast[key]
42 | if (key === "usingComponents") {
43 | for (const k in value) {
44 | const v = value[k]
45 | this.dependencies.add({ path: v, tag: k, ext: ".component" })
46 | }
47 | }
48 | }
49 | }
50 | }
51 |
52 | class Component extends Asset {
53 | constructor(path, type, name) {
54 | super(path, type, name)
55 | }
56 | async transform(input) {
57 | this.ast = JSON.parse(input)
58 | for (const key in this.ast) {
59 | const value = this.ast[key]
60 | if (key === "usingComponents") {
61 | for (const k in value) {
62 | const v = value[k]
63 | this.dependencies.add({ path: v, tag: k, type: ".component", ext: ".component" })
64 | }
65 | }
66 | }
67 | }
68 | }
69 |
70 | module.exports = {
71 | App: App,
72 | Page: Page,
73 | Component: Component,
74 | }
75 |
--------------------------------------------------------------------------------
/compiler/core/plugins/postcss-rpx2rem.js:
--------------------------------------------------------------------------------
1 | const postcss = require("postcss")
2 |
3 | const defaults = {
4 | targetUnit: "rpx",
5 | outputUnit: "rem",
6 | proportion: 0.01, // 1rpx = 0.01rem
7 | unitPrecision: 5,
8 | replace: true,
9 | mediaQuery: false,
10 | }
11 |
12 | function toFixed(number, precision) {
13 | const multiplier = Math.pow(10, precision + 1)
14 | const wholeNumber = Math.floor(number * multiplier)
15 | const fixedWholeNumber = Math.round(wholeNumber / 10) * 10
16 | return fixedWholeNumber / multiplier
17 | }
18 |
19 | function declarationExists(decls, prop, value) {
20 | return decls.some((decl) => decl.prop === prop && decl.value === value)
21 | }
22 |
23 | module.exports = (options) => {
24 | const {
25 | targetUnit,
26 | outputUnit,
27 | proportion,
28 | unitPrecision,
29 | replace,
30 | mediaQuery,
31 | } = Object.assign({}, defaults, options)
32 |
33 | const replaceFn = (m, $1) => {
34 | if (!$1) {
35 | return m
36 | }
37 | const value = toFixed(parseFloat($1) * proportion, unitPrecision)
38 | return value === 0 ? "0" : `${value}${outputUnit}`
39 | }
40 |
41 | const replaceRegex = new RegExp(
42 | `"[^"]+"|'[^']+'|url\\([^)]+\\)|(\\d*\\.?\\d+)${targetUnit}`,
43 | "g"
44 | )
45 |
46 | return {
47 | postcssPlugin: 'postcss-rpx2rem',
48 | Once(root) {
49 | root.walkRules((rule, result) => {
50 | rule.walkDecls((decl) => {
51 | if (decl.value.indexOf(targetUnit) === -1) return
52 | const value = decl.value.replace(replaceRegex, replaceFn)
53 | if (declarationExists(decl.parent, decl.prop, value)) return
54 | if (replace) {
55 | decl.value = value
56 | } else {
57 | decl.parent.insertAfter(result, decl.clone({ value }))
58 | }
59 | })
60 | })
61 |
62 | if (mediaQuery) {
63 | root.walkAtRules("media", (rule) => {
64 | if (rule.params.indexOf(targetUnit) === -1) return
65 | rule.params = rule.params.replace(replaceRegex, replaceFn)
66 | })
67 | }
68 | },
69 | }
70 | }
71 | module.exports.postcss = true
--------------------------------------------------------------------------------
/compiler/core/plugins/postcss-scoped-css.js:
--------------------------------------------------------------------------------
1 | const selectorParser = require("postcss-selector-parser")
2 |
3 | module.exports = ({ id = "" }) => {
4 | return {
5 | postcssPlugin: "postcss-scoped-css",
6 | Rule(rule) {
7 | processRule(id, rule)
8 | },
9 | AtRule(node) {
10 | processKeyframes(id, node)
11 | },
12 | OnceExit(root) {
13 | root.walkDecls((node) => {
14 | if (exist(node)) return
15 | const prefix = `${id}-`
16 | const { prop, value } = node
17 |
18 | if (animationNameRE.test(prop)) {
19 | node.value = value
20 | .split(",")
21 | .map((v) => (v === "none" ? v.trim() : prefix + v.trim()))
22 | .join(",")
23 | } else if (animationRE.test(prop)) {
24 | node.value = value
25 | .split(" ")
26 | .map((v) => (keyframes.indexOf(v) > -1 ? prefix + v : v))
27 | .join(" ")
28 | }
29 | })
30 | },
31 | }
32 | }
33 |
34 | const processedRules = new WeakSet()
35 | const exist = (n) => {
36 | if (!processedRules.has(n)) {
37 | processedRules.add(n)
38 | return false
39 | }
40 | return true
41 | }
42 |
43 | let keyframes = []
44 |
45 | function processRule(id, rule) {
46 | if (exist(rule)) return
47 |
48 | rule.selector = selectorParser((selectorRoot) => {
49 | selectorRoot.each((selector) => {
50 | selector.each((n) => {
51 | if (n.type === "class") {
52 | selector.insertAfter(
53 | n,
54 | selectorParser.attribute({
55 | attribute: id,
56 | value: id,
57 | raws: {},
58 | quoteMark: `"`,
59 | })
60 | )
61 | }
62 | })
63 | })
64 | }).processSync(rule.selector)
65 | }
66 |
67 | const animationRE = /^(-\w+-)?animation$/
68 | const animationNameRE = /^(-\w+-)?animation-name$/
69 |
70 | function processKeyframes(id, node) {
71 | if (exist(node)) return
72 | if (/-?keyframes$/.test(node.name)) {
73 | keyframes.push(node.params)
74 | node.params = `${id}-${node.params}`
75 | }
76 | }
77 |
78 | module.exports.postcss = true
79 |
--------------------------------------------------------------------------------
/smallappandroid/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("org.jetbrains.kotlin.android")
4 | }
5 |
6 | android {
7 | namespace = "com.smallapp"
8 | compileSdk = 34
9 |
10 | defaultConfig {
11 | applicationId = "com.smallapp"
12 | minSdk = 24
13 | targetSdk = 34
14 | versionCode = 1
15 | versionName = "1.0"
16 |
17 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
18 | vectorDrawables {
19 | useSupportLibrary = true
20 | }
21 | }
22 |
23 | buildTypes {
24 | release {
25 | isMinifyEnabled = false
26 | proguardFiles(
27 | getDefaultProguardFile("proguard-android-optimize.txt"),
28 | "proguard-rules.pro"
29 | )
30 | }
31 | }
32 | compileOptions {
33 | sourceCompatibility = JavaVersion.VERSION_1_8
34 | targetCompatibility = JavaVersion.VERSION_1_8
35 | }
36 | kotlinOptions {
37 | jvmTarget = "1.8"
38 | }
39 | buildFeatures {
40 | compose = true
41 | }
42 | composeOptions {
43 | kotlinCompilerExtensionVersion = "1.4.3"
44 | }
45 | packaging {
46 | resources {
47 | excludes += "/META-INF/{AL2.0,LGPL2.1}"
48 | }
49 | }
50 | }
51 |
52 | dependencies {
53 | implementation ("com.eclipsesource.j2v8:j2v8:6.2.1@aar")
54 | implementation("androidx.core:core-ktx:1.9.0")
55 | implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
56 | implementation("androidx.activity:activity-compose:1.8.2")
57 | implementation(platform("androidx.compose:compose-bom:2023.03.00"))
58 | implementation("androidx.compose.ui:ui")
59 | implementation("androidx.compose.ui:ui-graphics")
60 | implementation("androidx.compose.ui:ui-tooling-preview")
61 | implementation("androidx.compose.material3:material3")
62 | testImplementation("junit:junit:4.13.2")
63 | androidTestImplementation("androidx.test.ext:junit:1.1.5")
64 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
65 | androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
66 | androidTestImplementation("androidx.compose.ui:ui-test-junit4")
67 | debugImplementation("androidx.compose.ui:ui-tooling")
68 | debugImplementation("androidx.compose.ui:ui-test-manifest")
69 | }
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/java/com/smallapp/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.smallapp.ui.theme
2 |
3 | import android.app.Activity
4 | import android.os.Build
5 | import androidx.compose.foundation.isSystemInDarkTheme
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.darkColorScheme
8 | import androidx.compose.material3.dynamicDarkColorScheme
9 | import androidx.compose.material3.dynamicLightColorScheme
10 | import androidx.compose.material3.lightColorScheme
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.runtime.SideEffect
13 | import androidx.compose.ui.graphics.toArgb
14 | import androidx.compose.ui.platform.LocalContext
15 | import androidx.compose.ui.platform.LocalView
16 | import androidx.core.view.WindowCompat
17 |
18 | private val DarkColorScheme = darkColorScheme(
19 | primary = Purple80,
20 | secondary = PurpleGrey80,
21 | tertiary = Pink80
22 | )
23 |
24 | private val LightColorScheme = lightColorScheme(
25 | primary = Purple40,
26 | secondary = PurpleGrey40,
27 | tertiary = Pink40
28 |
29 | /* Other default colors to override
30 | background = Color(0xFFFFFBFE),
31 | surface = Color(0xFFFFFBFE),
32 | onPrimary = Color.White,
33 | onSecondary = Color.White,
34 | onTertiary = Color.White,
35 | onBackground = Color(0xFF1C1B1F),
36 | onSurface = Color(0xFF1C1B1F),
37 | */
38 | )
39 |
40 | @Composable
41 | fun SmallappandroidTheme(
42 | darkTheme: Boolean = isSystemInDarkTheme(),
43 | // Dynamic color is available on Android 12+
44 | dynamicColor: Boolean = true,
45 | content: @Composable () -> Unit
46 | ) {
47 | val colorScheme = when {
48 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
49 | val context = LocalContext.current
50 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
51 | }
52 |
53 | darkTheme -> DarkColorScheme
54 | else -> LightColorScheme
55 | }
56 | val view = LocalView.current
57 | if (!view.isInEditMode) {
58 | SideEffect {
59 | val window = (view.context as Activity).window
60 | window.statusBarColor = colorScheme.primary.toArgb()
61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
62 | }
63 | }
64 |
65 | MaterialTheme(
66 | colorScheme = colorScheme,
67 | typography = Typography,
68 | content = content
69 | )
70 | }
--------------------------------------------------------------------------------
/compiler/core/bundle.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs")
2 | const { promises } = fs
3 | const Path = require("path")
4 | let ref = {}
5 |
6 | module.exports = async function build(main, options) {
7 | ref.options = options
8 | const rootJson = await resolveAsset(Path.resolve(main || ""))
9 | ref.options.i = Path.dirname(rootJson.path)
10 | await loadAsset(rootJson)
11 | return rootJson
12 | }
13 |
14 | async function loadAsset(asset) {
15 | if (asset.path) {
16 | const input = await promises.readFile(asset.path)
17 | await asset.transform(input.toString())
18 | }
19 |
20 | if (asset.type === "page") {
21 | asset.outputPath = Path.resolve(ref.options.o, asset.hash)
22 | }
23 |
24 | var siblings = []
25 |
26 | if (asset.type === "page" || asset.type === "component") {
27 | siblings = [".wxml", ".js", ".wxss"].map(async (type) => {
28 | if (asset.parent) {
29 | const depAsset = await resolveAsset(asset.path.replace(".json", type))
30 | asset.siblingAssets.set(type, depAsset)
31 | depAsset.parent = asset
32 | await loadAsset(depAsset)
33 | }
34 | })
35 | }
36 |
37 | if (asset.type === "app") {
38 | siblings = [".js"].map(async (type) => {
39 | const depAsset = await resolveAsset(asset.path.replace(".json", type))
40 | asset.siblingAssets.set(type, depAsset)
41 | depAsset.parent = asset
42 | await loadAsset(depAsset)
43 | })
44 | }
45 |
46 | const dependencies = Array.from(asset.dependencies)
47 |
48 | const childs = dependencies.map(async (dep) => {
49 | const depAsset = await resolveAsset(
50 | dep.path.replace(dep.ext, "") + dep.ext,
51 | asset.path
52 | )
53 | depAsset.tag = dep.tag
54 | asset.childAssets.set(dep.path, depAsset)
55 | depAsset.parent = asset
56 | await loadAsset(depAsset)
57 | })
58 | await Promise.all(siblings.concat(childs))
59 | }
60 |
61 | async function resolveAsset(path = "", parent) {
62 | let type = Path.extname(path)
63 |
64 | switch (type) {
65 | case ".js":
66 | Asset = require("./assets/js")
67 | break
68 | case ".json":
69 | Asset = require("./assets/json").App
70 | break
71 | case ".page":
72 | Asset = require("./assets/json").Page
73 | break
74 | case ".component":
75 | Asset = require("./assets/json").Component
76 | break
77 | case ".wxml":
78 | Asset = require("./assets/wxml")
79 | break
80 | case ".wxss":
81 | Asset = require("./assets/wxss")
82 | break
83 | default:
84 | break
85 | }
86 |
87 | path = path.replace(".component", ".json").replace(".page", ".json")
88 | type = type === ".json" ? ".app" : type
89 |
90 | let resolvePath = parent ? Path.join(Path.dirname(parent), path) : path
91 | if (!fs.existsSync(resolvePath)) {
92 | resolvePath = Path.join(ref.options.i, path)
93 | }
94 | return new Asset(resolvePath, type, path)
95 | }
96 |
--------------------------------------------------------------------------------
/worker.js:
--------------------------------------------------------------------------------
1 | import { serve } from "https://deno.land/std@0.140.0/http/server.ts"
2 |
3 | async function handleXingtie(req) {
4 | const { search } = new URL(req.url)
5 |
6 | const api = 'https://api-takumi.mihoyo.com/common/gacha_record/api/getGachaLog' + search
7 |
8 | const data = await fetch(api, {
9 | headers: {
10 | "Host": "api-takumi.mihoyo.com",
11 | "User-Agent":
12 | "Mozilla/ 5.0(Windows NT 10.0; Win64; x64) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 114.0.0.0 Safari / 537.36"
13 | }
14 |
15 | }).then(res => res.json())
16 |
17 |
18 | return new Response(JSON.stringify(data), {
19 | headers: {
20 | "Content-Type": "application/json",
21 | "Access-Control-Allow-Origin": "*"
22 | }
23 | })
24 | }
25 |
26 |
27 | async function getJs(url) {
28 | const data = await fetch(url, {
29 |
30 | }).then(res => res.text())
31 |
32 |
33 | return new Response(data, {
34 | headers: {
35 | "Content-Type": "application/json",
36 | "Access-Control-Allow-Origin": "*"
37 | }
38 | })
39 | }
40 |
41 | async function handler(req) {
42 | const { pathname } = new URL(req.url)
43 |
44 | if (pathname.includes('xingtie_chouka')) {
45 | return handleXingtie(req)
46 | }
47 |
48 | const entries = []
49 |
50 | if (pathname === '/slave.js' || pathname === '/master.js') {
51 | return getJs("https://npm.elemecdn.com/smallapp@latest/runtime/dist/" + pathname)
52 | }
53 |
54 | for await (const entry of Deno.readDir(`./dist`)) {
55 | entries.push(entry)
56 | }
57 |
58 | const file2 = entries.find(i => {
59 | console.log(pathname, i.name)
60 | return pathname.slice(1) === i.name
61 | })
62 |
63 | if (file2 && file2.name) {
64 | const str = await Deno.readFile(`./dist/${file2.name}`)
65 | return new Response(str, {
66 | headers: {
67 | "content-type": file2.name.includes('json') ? 'application/json' : file2.name.includes('css') ? "text/css" : "application/javascript"
68 | }
69 | })
70 | }
71 |
72 | const str = `
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | Fre miniapp
81 |
82 |
83 |
84 |
85 |
89 |
90 |
91 | `
92 | return new Response(str, {
93 | headers: {
94 | "content-type": "text/html",
95 | }
96 | })
97 |
98 | }
99 |
100 | serve(handler)
--------------------------------------------------------------------------------
/smallappandroid/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/java/com/smallapp/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.smallapp
2 |
3 | import android.annotation.SuppressLint
4 | import android.os.Bundle
5 | import android.webkit.JavascriptInterface
6 | import android.webkit.WebView
7 | import android.webkit.WebViewClient
8 | import androidx.activity.ComponentActivity
9 |
10 |
11 | class MainActivity : ComponentActivity() {
12 |
13 | private lateinit var webView: WebView
14 | @SuppressLint("JavascriptInterface")
15 | override fun onCreate(savedInstanceState: Bundle?) {
16 | super.onCreate(savedInstanceState)
17 | setContentView(R.layout.activity_main)
18 | webView = findViewById(R.id.webview)
19 | val webViewClient = object : WebViewClient() {
20 | override fun onPageFinished(view: WebView, url: String) {
21 | // 当页面加载完成后,在主线程中执行JavaScript代码
22 | view.post {
23 | // 使用WebView的evaluateJavascript方法执行JavaScript代码
24 | view.evaluateJavascript("javascript:window.isAndroid = true;", null);
25 | view.evaluateJavascript(getCode("master.js"), null)
26 | }
27 | }
28 | }
29 | webView.webViewClient = webViewClient
30 | // 启用JavaScript支持
31 | webView.settings.javaScriptEnabled = true
32 | WebView.setWebContentsDebuggingEnabled(true);
33 | webView.addJavascriptInterface(JSViewBridgeImpl(webView), "AndroidJSViewBridge")
34 | webView.addJavascriptInterface(JSServiceBridgeImpl(webView), "AndroidJSServiceBridge");
35 | webView.loadUrl("file:///android_asset/index.html")
36 | }
37 |
38 | // 重写回退按钮的事件,当用户点击回退按钮时,如果WebView可以回退,则回退到上一个页面
39 | override fun onBackPressed() {
40 | if (webView.canGoBack()) {
41 | webView.goBack()
42 | } else {
43 | super.onBackPressed()
44 | }
45 | }
46 |
47 |
48 | fun getCode(name: String): String {
49 | val inputStream = resources.assets.open(name);
50 | val size = inputStream.available()
51 | val buffer = ByteArray(size)
52 | inputStream.read(buffer)
53 | inputStream.close()
54 | return String(buffer);
55 | }
56 |
57 | // 创建一个Java接口,该接口将在WebView的JavaScript代码中暴露
58 | interface JSViewBridge {
59 | fun postMessage(param: String)
60 | }
61 |
62 | class JSViewBridgeImpl(private val webView: WebView) : JSViewBridge {
63 |
64 | @JavascriptInterface
65 | override fun postMessage(param: String) {
66 | val callbackJs = "javascript: window.AndroidJSServiceBridge.onmessage('$param');"
67 | webView.post { webView.evaluateJavascript(callbackJs, null) }
68 | }
69 | }
70 |
71 | // 创建一个Java接口,该接口将在WebView的JavaScript代码中暴露
72 | interface JSServiceBridge {
73 | fun postMessage(param: String)
74 |
75 | }
76 |
77 | class JSServiceBridgeImpl(private val webView: WebView) : JSServiceBridge {
78 |
79 | @JavascriptInterface
80 | override fun postMessage(param: String) {
81 | val callbackJs = "javascript: window.AndroidJSViewBridge.onmessage('$param');"
82 | webView.post { webView.evaluateJavascript(callbackJs, null) }
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/runtime/master/index.js:
--------------------------------------------------------------------------------
1 | import fakedom from './dom/fake-dom.js'
2 | // import './dom/proxy-dom.js'
3 | import { handleWxEvent } from './wxapi'
4 | import { init } from './init'
5 |
6 | try {
7 | console.log(window)
8 | } catch (error) {
9 | var window = { isAndroid: false }
10 | }
11 |
12 | self.send = function send(message) {
13 | try {
14 | if (window.isAndroid) {
15 | AndroidJSViewBridge.postMessage(JSON.stringify(message))
16 | }
17 | } catch (error) {
18 | postMessage(JSON.stringify(message))
19 | }
20 | }
21 |
22 | self.document = fakedom()
23 | globalThis.document = self.document
24 |
25 | for (let i in document.defaultView) if (document.defaultView.hasOwnProperty(i)) {
26 | self[i] = document.defaultView[i]
27 | }
28 |
29 | let COUNTER = 0
30 |
31 | const TO_SANITIZE = ['addedNodes', 'removedNodes', 'nextSibling', 'previousSibling', 'target']
32 |
33 | const PROP_BLACKLIST = ['children', 'parentNode', '__handlers', '_component', '_componentConstructor']
34 |
35 | const NODES = new Map()
36 |
37 | function getNode(node) {
38 | let id
39 | if (node && typeof node === 'object') id = node.__id
40 | if (typeof node === 'string') id = node
41 | if (!id) return null
42 | if (node.nodeName === 'BODY') return document.body
43 | return NODES.get(id)
44 | }
45 |
46 | function handleEvent(event) {
47 | let target = getNode(event.target)
48 | if (target) {
49 | event.target = target
50 | event.bubbles = true
51 | target.dispatchEvent && target.dispatchEvent(event)
52 | }
53 | }
54 |
55 | function sanitize(obj) {
56 | if (!obj || typeof obj !== 'object') return obj
57 |
58 | if (Array.isArray(obj)) return obj.map(sanitize)
59 |
60 | if (obj instanceof document.defaultView.Node) {
61 | let id = obj.__id
62 | if (!id) {
63 | id = obj.__id = String(++COUNTER)
64 | }
65 | NODES.set(id, obj)
66 | }
67 |
68 | let out = {}
69 | for (let i in obj) {
70 | if (obj.hasOwnProperty(i) && PROP_BLACKLIST.indexOf(i) < 0) {
71 | out[i] = obj[i]
72 | }
73 | }
74 | if (out.childNodes && out.childNodes.length) {
75 | out.childNodes = sanitize(out.childNodes)
76 | }
77 | return out
78 | }
79 |
80 | (new MutationObserver(mutations => {
81 | for (let i = mutations.length; i--;) {
82 | let mutation = mutations[i]
83 | for (let j = TO_SANITIZE.length; j--;) {
84 | let prop = TO_SANITIZE[j]
85 | mutation[prop] = sanitize(mutation[prop])
86 | }
87 | }
88 | send({ type: 'MutationRecord', mutations })
89 | })).observe(document, { subtree: true, childList: true })
90 |
91 | function _message(data) {
92 | if (typeof data === "string") {
93 | data = JSON.parse(data)
94 | }
95 | switch (data.type) {
96 | case "init":
97 | init(data.manifest ? data.manifest : window.manifest)
98 | break
99 | case "event":
100 | handleEvent(data.event)
101 | break
102 | case "wxcallback":
103 | handleWxEvent(data.payload)
104 | break
105 | }
106 | }
107 |
108 | try {
109 | if (window.isAndroid) {
110 | AndroidJSViewBridge.onmessage = function (data) {
111 | _message(data)
112 | }
113 | }
114 | } catch (error) {
115 | addEventListener("message", ({ data }) => {
116 | _message(data)
117 | })
118 | }
119 |
--------------------------------------------------------------------------------
/runtime/slave/proxy.js:
--------------------------------------------------------------------------------
1 | if (!self.slave) self.slave = {}
2 | const idMap = new Map([[0, self]])
3 | let nextid = -1
4 |
5 | slave.wrap = (arg) => {
6 | if (canClone(arg)) {
7 | return [0, arg]
8 | } else {
9 | return [1, obj2id(arg)]
10 | }
11 | }
12 |
13 | slave.unwrap = (arr) => {
14 | switch (arr[0]) {
15 | case 0: // primitive
16 | return arr[1]
17 | case 1: // object
18 | return id2obj(arr[1])
19 | case 2: // callback
20 | return getCb(arr[1])
21 | case 3: // property
22 | return id2prop(arr[1], arr[2])
23 | default:
24 | throw new Error('invalid arg type')
25 | }
26 | }
27 |
28 | slave.connect = function connect(worker) {
29 | worker.onmessage = (e) => slave.onmessage(e.data)
30 | slave.postMessage = (data) => worker.postMessage(data)
31 | worker.postMessage('start')
32 | }
33 |
34 | function id2obj(id) {
35 | const ret = idMap.get(id)
36 | if (!ret) throw new Error('missing object id: ' + id)
37 | return ret
38 | }
39 |
40 | function obj2id(object) {
41 | const id = nextid--
42 | idMap.set(id, object)
43 | return id
44 | }
45 |
46 | function id2prop(id, path) {
47 | const ret = idMap.get(id)
48 | if (!ret) throw new Error('missing object id: ' + id)
49 | let base = ret
50 | for (let i = 0, len = path.length; i < len; ++i) base = base[path[i]]
51 | return base
52 | }
53 |
54 | function getCb(id) {
55 | return (...args) =>
56 | slave.postMessage({
57 | type: 'cb',
58 | id,
59 | args: args.map(slave.wrap),
60 | })
61 | }
62 |
63 | slave.onmessage = function (data) {
64 | switch (data.type) {
65 | case 'cmds':
66 | command(data)
67 | break
68 | case 'cleanup':
69 | cleanup(data)
70 | break
71 | default:
72 | console.error('Unknown message type: ' + data.type)
73 | break
74 | }
75 | }
76 |
77 | function command(data) {
78 | const res = []
79 | for (const cmd of data.cmds) process(cmd, res)
80 |
81 | slave.postMessage({
82 | type: 'done',
83 | flushId: data.flushId,
84 | res,
85 | })
86 | }
87 |
88 | function process(arr) {
89 | const type = arr[0]
90 | switch (type) {
91 | case 0:
92 | call(arr[1], arr[2], arr[3], arr[4], type)
93 | break
94 | case 1:
95 | set(arr[1], arr[2], arr[3])
96 | break
97 | case 2:
98 | call(arr[1], arr[2], arr[3], arr[4], type)
99 | break
100 | default:
101 | throw new Error('invalid cmd type: ' + type)
102 | }
103 | }
104 |
105 | function call(id, path, arg, returnid, isNew) {
106 | const obj = id2obj(id)
107 | const args = arg.map(slave.unwrap)
108 | const name = path[path.length - 1]
109 | let base = obj
110 | for (let i = 0; i < path.length - 1; i++) {
111 | base = base[path[i]]
112 | }
113 | let ret = isNew ? new base[name](...args) : base[name](...args)
114 | idMap.set(returnid, ret)
115 | }
116 |
117 | function set(id, path, arg) {
118 | const obj = id2obj(id)
119 | const value = slave.unwrap(arg)
120 | const name = path[path.length - 1]
121 | let base = obj
122 | for (let i = 0; i < path.length - 1; i++) {
123 | base = base[path[i]]
124 | }
125 | base[name] = value
126 | }
127 |
128 | function cleanup(data) {
129 | for (const id of data.ids) idMap.delete(id)
130 | }
131 |
132 | function canClone(o) {
133 | const t = typeof o
134 | return (
135 | t === 'undefined' ||
136 | o === null ||
137 | t === 'boolean' ||
138 | t === 'number' ||
139 | t === 'string' ||
140 | o instanceof Blob ||
141 | o instanceof ArrayBuffer ||
142 | o instanceof ImageData
143 | )
144 | }
--------------------------------------------------------------------------------
/compiler/core/package.js:
--------------------------------------------------------------------------------
1 | const { promises } = require('fs')
2 | const ejs = require('ejs')
3 | const Path = require('path')
4 | const esbuild = require('esbuild')
5 | const { getIndexHtmlCode } = require('./packagers/util')
6 |
7 | const manifest = []
8 |
9 | module.exports.manifest = manifest
10 |
11 | const packJs = require('./packagers/js.js')
12 | const packWxss = require('./packagers/wxss.js')
13 | const packWxml = require('./packagers/wxml.js')
14 | const packBerial = require('./packagers/berial.js')
15 |
16 | module.exports = async function pack(asset, options) {
17 | await packageAsset(asset, options)
18 | await writeAsset(asset, options)
19 | await generateEntry(asset, options)
20 | }
21 |
22 | async function writeAsset(asset, options) {
23 | asset.outputPath = Path.resolve(options.o, asset.hash)
24 | asset.output.js = asset.siblingAssets.get('.js').code
25 | await write(asset, options)
26 |
27 | const childs = Array.from(asset.childAssets.values()).map(async (page) => {
28 | await packBerial(page, options)
29 | await write(page, options)
30 | })
31 | await Promise.all(childs)
32 | }
33 |
34 | async function packageAsset(asset, options) {
35 | await packageJson(asset, options)
36 | const page = getPage(asset)
37 | if (asset.type === 'component' && page) {
38 | page.output.jsx += asset.output.jsx || ''
39 | page.output.js += asset.output.js || ''
40 | page.output.css += asset.output.css || ''
41 | }
42 | const all = Array.from(asset.childAssets.values()).map(async (child) => {
43 | await packageAsset(child, options)
44 | })
45 |
46 | await Promise.all(all)
47 | }
48 |
49 | function getPage(asset) {
50 | let p = asset
51 | while (p && (p = p.parent)) {
52 | if (p.type === 'page') {
53 | return p
54 | }
55 | }
56 | }
57 |
58 | async function write(asset, options) {
59 | await promises.mkdir(Path.resolve(options.o), { recursive: true })
60 | for (const key in asset.output) {
61 | let path = `${asset.outputPath}.${key}`
62 | let code = asset.output[key]
63 |
64 | if (options.m) {
65 | code = await esbuild.transform(code, {
66 | loader: key,
67 | minify: true,
68 | })
69 | }
70 |
71 | await promises.writeFile(path, code)
72 | }
73 | }
74 |
75 | async function packageJson(asset, options) {
76 | const siblings = asset.siblingAssets
77 | if (siblings) {
78 | const all = Array.from(siblings.keys()).map(async (key) => {
79 | const value = siblings.get(key)
80 | switch (key) {
81 | case '.js':
82 | asset.output.js = await packJs(value, options)
83 | break
84 | case '.wxml':
85 | asset.output.jsx = await packWxml(value, options)
86 | break
87 | case '.wxss':
88 | asset.output.css = await packWxss(value)
89 | break
90 | }
91 | })
92 | await Promise.all(all)
93 | }
94 | }
95 |
96 | async function generateEntry(asset, options) {
97 | const o = Path.resolve(options.o)
98 | await promises.mkdir(Path.join(o, 'public'), { recursive: true })
99 |
100 | const tabbars = asset.ast.tabBar.list
101 | const all = tabbars.map(async (item) => {
102 | let { iconPath, selectedIconPath } = item
103 | const $1 = Path.join(o, 'public', Path.basename(iconPath))
104 | const $2 = Path.join(o, 'public', Path.basename(selectedIconPath))
105 | await promises.copyFile(iconPath, $1)
106 | item.iconPath = $1
107 | await promises.copyFile(selectedIconPath, $2)
108 | item.selectedIconPath = $2
109 | })
110 | await Promise.all(all)
111 |
112 | const pages = asset.ast.pages.map(path => manifest.find(i => i.path=== '/' + path))
113 | const json = {
114 | origin: asset.ast,
115 | pages,
116 | }
117 | const out = JSON.stringify(json)
118 | const code = `window.manifest = ${out}`
119 | await promises.writeFile(Path.join(Path.resolve(options.o), 'app.js'), code)
120 | await promises.writeFile(Path.join(Path.resolve(options.o), 'index.html'), await getIndexHtmlCode())
121 | }
122 |
--------------------------------------------------------------------------------
/demo/pages/index/index.js:
--------------------------------------------------------------------------------
1 | import { a } from './a.js'
2 | import d from './a.js'
3 |
4 | function b() {
5 | a()
6 | }
7 | b()
8 |
9 | const app = getApp()
10 |
11 | Page({
12 | data: {
13 | url: "",
14 | type: 0, // 1 常驻 2 新手池 11 限定卡池
15 | count: 0,
16 | page: 1,
17 | end_id: 0,
18 | five: [],
19 | role: "未出",
20 | map: {
21 | 0: '限定卡池',
22 | 1: '常驻卡池',
23 | 2: '新手卡池'
24 | },
25 | title: "未知"
26 | },
27 | onload(){
28 | console.log('onLoad')
29 | },
30 | changeUrl(e) {
31 | this.setData({
32 | url: e.detail.value
33 | })
34 | },
35 | radioChange(e) {
36 |
37 | },
38 |
39 |
40 | async analyse() {
41 | console.log(this.data.url)
42 | return
43 | // https://api-takumi.mihoyo.com/common/gacha_record/api/getGachaLog?gacha_type=11&authkey_ver=1&default_gacha_type=1&lang=zh-cn&authkey=EGWjU%2BK2jFTOmgkC9%2FcS4y1b1X5FJkFOxNJXOd1CiQ%2BU7V1hLp8mzX6qsWYWoUvFiPRiREclt5LcMIkWo4Ugvvt574HIzlfWnEK72620SOzO130Zkm%2BO1d9IOv9PtiVR7NBQYw93PsOf9Kqp6PAmWI1MGlgKlkrcMcWGS8LfDck3ctWD96EbfZzZjrHQoeJFlqGJCoEvCsjYzq5deg56Z1AtOn2ZsuQjeSOoL%2BbzEFuNu14Av0kUdY%2FOIyvvAEoaWJ4fa6sWzs1YAjHngkWxcl3L3lfPiqDHz%2B%2BFxU4TzWc9v5v%2BOzg1rD775xLSG%2F73o%2F%2BGfOHODFlDoXTiiE0wz4P0xzpnbel6n2Tb8LyOV7GP6lCu4AdnDnTO%2BRqJfFlBHWRNmTWx1oEXzckxPcIZZnpGo5g9GvO4IDbdVwxLj5hc74P0YQFK2MiVdOUgkMF1LT0HnFO4mWlY0Sbn6kfOHIeP%2FQ8DG6hCRcMRqIpDFgw%2Bl8sES7VCLuQAIaoDMSMmdAh1j8k5Wrju21%2FfyIlp8lsQPWBscSFD3wEAsoTGvKru96OuKmLQlMYq%2BQ7qJd34RUqYaoBs2Hzp5Cv%2BMHrk4VdOsUcFOgk7AwZC6c%2BPTVip1n2Est06s7drOr0At8BTyMdxNRNQzAOhP5BaRVZxta9MX%2FfEb1jYeROHxXaoeHA%3D&game_biz=hkrpg_cn&page=1&size=20
44 | const search = new URL(this.data.url).search
45 | let type = this.data.type
46 | if (type === 0) {
47 | type = 11
48 | }
49 | const api = 'https://miniapp.deno.dev/xingtie_chouka' + search + `&gacha_type=${type}&size=20&page=${this.data.page}&end_id=${this.data.end_id}`
50 | const data = await wx.request(api)
51 | const list = data.data.list
52 | for (const v of list) {
53 | if (v.rank_type === '5') {
54 | const role = this.data.role
55 | this.setData({
56 | role: v.name,
57 | five: this.data.five.concat([[role, this.data.count + 1]]),
58 | count: 0,
59 | })
60 | } else {
61 | this.setData({
62 | count: this.data.count + 1
63 | })
64 | }
65 | }
66 |
67 | if (list.length < 5) {
68 | // console.log(`常驻池分析完毕,开始分析${map[this.data.type]}`)
69 | if (this.data.type >= 2) {
70 | console.log('分析结束')
71 | } else {
72 | const role = this.data.role
73 | this.setData({
74 | role: '未出',
75 | five: this.data.five.concat([[role, this.data.count + 1]]),
76 | count: 0,
77 | })
78 |
79 | this.medal()
80 | }
81 | } else {
82 | this.setData({
83 | page: this.data.page + 1,
84 | end_id: list[list.length - 1].id
85 | })
86 |
87 | await new Promise(r => setTimeout(() => {
88 | r()
89 | }, 1000))
90 |
91 | await this.analyse()
92 | }
93 | },
94 |
95 | medal() {
96 | const nums = this.data.five.map(i => i[1])
97 | const realNums = nums.slice(1)
98 | const average = realNums.reduce((a, b) => a + b) / realNums.length;
99 |
100 | let title = ''
101 |
102 | if (average > 80) {
103 | title = '终极无敌至尊非酋王'
104 | } else if (average > 70) {
105 | title = '非酋王'
106 | } else if (average > 60) {
107 | title = '大非酋'
108 | } else if (average > 50) {
109 | title = '小非酋'
110 | } else if (average > 40) {
111 | title = '小欧皇'
112 | } else if (average > 30) {
113 | title = '大欧皇'
114 | } else if (average > 20) {
115 | title = '欧皇王'
116 | } else {
117 | title = '终极无敌至尊欧皇王'
118 | }
119 | this.setData({
120 | title,
121 | })
122 |
123 | },
124 | toast() {
125 | wx.showToast({
126 | title: 'qq群975551446',
127 | success: () => {
128 | console.log('success')
129 | }
130 | })
131 | }
132 | })
--------------------------------------------------------------------------------
/compiler/wxml/lex.js:
--------------------------------------------------------------------------------
1 | const tagOrCommentStartRE = /<\/?(?:[A-Za-z]+\w*)|', current)
60 | if (endOfComment === -1) {
61 | state.tokens.push({
62 | type: 'comment',
63 | value: input.slice(current)
64 | })
65 | state.current = len
66 | } else {
67 | state.tokens.push({
68 | type: 'comment',
69 | value: input.slice(current, endOfComment)
70 | })
71 | state.current = endOfComment + 3
72 | }
73 | }
74 |
75 | function lexTag(state) {
76 | let input = state.input
77 | let isCloseStart = input.charAt(state.current + 1) === '/'
78 | state.current += isCloseStart ? 2 : 1
79 | let tagToken = lexType(state)
80 | lexAttributes(tagToken, state)
81 | let isCloseEnd = input.charAt(state.current) === '/'
82 | state.current += isCloseEnd ? 2 : 1
83 |
84 | if (isCloseEnd) {
85 | tagToken.closeEnd = true
86 | }
87 | if (isCloseStart) {
88 | tagToken.closeStart = true
89 | }
90 |
91 | }
92 | function lexType(state) {
93 | let input = state.input
94 | let current = state.current
95 | let len = input.length
96 | let type = ''
97 |
98 | while (current < len) {
99 | let char = input.charAt(current)
100 | if (char === '/' || char === '>' || char === ' ') {
101 | break
102 | } else {
103 | type += char
104 | }
105 | current++
106 | }
107 | let token = {
108 | type: 'tag',
109 | value: type
110 | }
111 | state.tokens.push(token)
112 | state.current = current
113 | return token
114 | }
115 |
116 | function lexAttributes(token, state) {
117 | let input = state.input
118 | let current = state.current
119 | let len = input.length
120 | let char = input.charAt(current)
121 | let nextChar = input.charAt(current + 1)
122 |
123 | function next() {
124 | current++
125 | char = input.charAt(current)
126 | nextChar = input.charAt(current + 1)
127 | }
128 |
129 | let attributes = {}
130 |
131 | while (current < len) {
132 | if (char === '>' || (char === '/' && nextChar === '>')) {
133 | break
134 | }
135 | if (char === ' ') {
136 | next()
137 | continue
138 | }
139 | let name = ''
140 | let noValue = false
141 | while (current < len && char !== '=') {
142 | if (char === ' ' || char === '>' || (char === '/' && nextChar === '>')) {
143 | noValue = true
144 | break
145 | } else {
146 | name += char
147 | }
148 | next()
149 | }
150 |
151 | let value = ''
152 | if (noValue) {
153 | attributes[name] = value
154 | continue
155 | }
156 | next()
157 | let quoteType = ' '
158 | if (char === "'" || char === "\"") {
159 | quoteType = char
160 | next()
161 | }
162 | while (current < len && char !== quoteType) {
163 | value += char
164 | next()
165 | }
166 | next()
167 | attributes[name] = value
168 | }
169 | state.current = current
170 | token.attributes = attributes
171 | }
172 |
173 | module.exports = lex
174 |
--------------------------------------------------------------------------------
/compiler/wxml/generate.js:
--------------------------------------------------------------------------------
1 | const { getId } = require('../core/packagers/util')
2 |
3 | const eventMap = {
4 | tap: 'onClick',
5 | confirm: 'onKeyDown',
6 | }
7 |
8 | let clock = 0
9 |
10 | function generate(asset) {
11 | let tree = asset.ast
12 | let children = tree.children
13 |
14 | let state = {
15 | imports: [],
16 | methods: [],
17 | blocks: {},
18 | }
19 |
20 | for (let i = 0; i < children.length; i++) {
21 | const kid = children[i]
22 | const next = children[i + 1]
23 | const block = generateNode(kid, state, asset, next)
24 | if (block) {
25 | state.blocks[clock++] = block
26 | }
27 | }
28 |
29 | return { imports: state.imports, blocks: state.blocks }
30 | }
31 |
32 | function generateNode(node, state, asset, nextNode) {
33 | if (typeof node === 'string') {
34 | let compiled = compileExpression(node)
35 | return `${compiled}`
36 | } else if (node.name === 'template') {
37 | const { is, name } = node.attributes
38 | if (is) {
39 | return `$template$${is}$`
40 | } else {
41 | let code = node.children
42 | .map(item => generateNode(item, state, asset))
43 | .join('\n')
44 | state.blocks[name] = code
45 | return null
46 | }
47 | } else if (node.name === 'slot') {
48 | const { name } = node.attributes
49 | if (name) {
50 | return `$slot$${name}$`
51 | }
52 | } else {
53 | let code = `<${titleCase(node.name)} `
54 | code += generateProps(node, state, asset)
55 | if (node.children) {
56 | code += `${node.children
57 | .map((item, index) =>
58 | generateNode(item, state, asset, node.children[index + 1])
59 | )
60 | .join('\n')}`
61 | }
62 | code += `${titleCase(node.name)}>`
63 |
64 | if (Object.keys(node.attributes).indexOf('slot') > -1) {
65 | // slot
66 | state.blocks[node.attributes.slot] = code
67 | return null
68 | }
69 |
70 | if (node.name === 'import') {
71 | return ''
72 | }
73 | if (node.directives) {
74 | code = generateDirect(node, code, nextNode)
75 | }
76 | return code
77 | }
78 | }
79 |
80 | let ifcode = ''
81 |
82 | function generateDirect(node, code, next) {
83 | for (let i = 0; i < node.directives.length; i++) {
84 | const [name, value] = node.directives[i]
85 | const compiled = compileExpression(value)
86 | if (code[0] === '{') {
87 | code = `${code}
`
88 | }
89 | if (name === 'wx:for') {
90 | const item = findItem(node)
91 | code = `{$for(${compiled},(${item}) => (${code}))}`
92 | }
93 | if (name === 'wx:if') {
94 | ifcode += `{${compiled}?${code}:`
95 | if (isElse(next)) {
96 | continue
97 | } else {
98 | code = ifcode + 'null}'
99 | ifcode = ''
100 | }
101 | }
102 | if (name === 'wx:elseif') {
103 | ifcode += `${compiled}?${code}:`
104 | if (isElse(next)) {
105 | continue
106 | } else {
107 | code = ifcode + 'null}'
108 | ifcode = ''
109 | }
110 | }
111 | if (name === 'wx:else') {
112 | if (ifcode === '') {
113 | ifcode += `{!${compiled}?${code}:null}`
114 | } else {
115 | ifcode += `${code}}`
116 | }
117 | code = ifcode
118 | ifcode = ''
119 | }
120 | return code
121 | }
122 | }
123 |
124 | function isElse(node) {
125 | if (node) {
126 | for (const name in node.attributes) {
127 | if (name.indexOf('else') > -1) return true
128 | }
129 | }
130 | return false
131 | }
132 |
133 | function findItem(node) {
134 | const item = node.directives.find(item => item[0] === 'wx:for-item')
135 | return item ? item[1] : 'item'
136 | }
137 |
138 | function generateProps(node, state, asset) {
139 | let code = ''
140 | for (let name in node.attributes) {
141 | const value = node.attributes[name]
142 | if (name.startsWith('wx:')) {
143 | node.directives = node.directives || []
144 | node.directives.push([name, value])
145 | } else if (name.startsWith('bind')) {
146 | if (state.methods.indexOf(value) < 0) {
147 | state.methods.push(value)
148 | }
149 | const key = wriedName(name)
150 | code += ` ${key}={$handleEvent("${value}", "${getId(asset)}", "${name}")} `
151 | } else if (node.name === 'import') {
152 | state.imports.push(value)
153 | } else {
154 | let compiled = compileExpression(value)
155 | code += `${name}=${compiled || 'true'}`
156 | }
157 | }
158 | return code + '>'
159 | }
160 |
161 | function wriedName(key) {
162 | key = key.replace(/(bind|catch)\:?/g, '')
163 | return key in eventMap
164 | ? eventMap[key]
165 | : 'on' + key[0].toUpperCase() + key.substr(1)
166 | }
167 |
168 | function compileExpression(expression) {
169 | var expr = expression, dynamic = false
170 | if (expression.startsWith('{{')) {
171 | dynamic = true
172 | expr = expression.substring(2, expression.length - 2)
173 | }
174 |
175 | if (dynamic) {
176 | return `{expr("${expr}", state)}`
177 | } else {
178 | return `{"${expr}"}`
179 | }
180 | }
181 |
182 | const titleCase = str =>
183 | 'comp.' + str.slice(0, 1).toUpperCase() +
184 | str.replace(/\-(\w)/g, (_, letter) => letter.toUpperCase()).slice(1)
185 |
186 | module.exports = generate
187 |
--------------------------------------------------------------------------------
/runtime/master/dom/proxy-dom.js:
--------------------------------------------------------------------------------
1 | if (!self.master) self.master = {}
2 |
3 | const getRes = new Map()
4 | const flushRes = new Map()
5 | const queue = []
6 | let dirty = false
7 | let flushid = 0
8 |
9 | let callbackid = 0
10 | let returnid = 1
11 | const callbackToId = new Map()
12 | const idToCallback = new Map()
13 |
14 | master.targetSymbol = Symbol('target')
15 | master.objectSymbol = Symbol('object')
16 |
17 | master.getReturnId = () => {
18 | return returnid++
19 | }
20 |
21 | function canClone(o) {
22 | const t = typeof o
23 | return (
24 | t === 'undefined' ||
25 | o === null ||
26 | t === 'boolean' ||
27 | t === 'number' ||
28 | t === 'string' ||
29 | o instanceof Blob ||
30 | o instanceof ArrayBuffer ||
31 | o instanceof ImageData
32 | )
33 | }
34 |
35 | master.enqueue = function (d) {
36 | queue.push(d)
37 | if (!dirty) {
38 | dirty = true
39 | Promise.resolve().then(master.flush)
40 | }
41 | }
42 |
43 | master.onmessage = function (data) {
44 | switch (data.type) {
45 | case 'done':
46 | done(data)
47 | break
48 | case 'cb':
49 | callback(data)
50 | break
51 | default:
52 | throw new Error('invalid message type: ' + data.type)
53 | }
54 | }
55 |
56 | function done(data) {
57 | for (const [getId, valueData] of data.res) {
58 | const resolve = getRes.get(getId)
59 | if (!resolve) throw new Error('invalid get id')
60 | getRes.delete(getId)
61 | resolve(master.unwrap(valueData))
62 | }
63 | const flushId = data.flushId
64 | const flushResolve = flushRes.get(flushId)
65 | if (!flushResolve) throw new Error('invalid flush id')
66 |
67 | flushRes.delete(flushId)
68 | flushResolve()
69 | }
70 |
71 | function callback(data) {
72 | const fn = idToCallback.get(data.id)
73 | if (!fn) throw new Error('invalid callback id')
74 | const args = data.args.map(master.unwrap)
75 | fn(...args)
76 | }
77 |
78 | master.unwrap = function (arr) {
79 | switch (arr[0]) {
80 | case 0:
81 | return arr[1]
82 | case 1:
83 | return master.makeObj(arr[1])
84 | default:
85 | throw new Error('invalid arg type')
86 | }
87 | }
88 |
89 | master.flush = function () {
90 | dirty = false
91 | if (!queue.length) return Promise.resolve()
92 | const flushId = flushid++
93 |
94 | master.postMessage({
95 | type: 'cmds',
96 | cmds: queue,
97 | flushId: flushId,
98 | })
99 |
100 | queue.length = 0
101 | return new Promise((resolve) => {
102 | flushRes.set(flushId, resolve)
103 | })
104 | }
105 |
106 | master.wrap = function (arg) {
107 | if (typeof arg === 'function') {
108 | const objectId = arg[master.objectSymbol]
109 | if (typeof objectId === 'number') return [1, objectId]
110 | const target = arg[master.targetSymbol]
111 | if (target) {
112 | return [3, target.id, target.path]
113 | }
114 | return [2, getCid(arg)]
115 | } else if (canClone(arg)) {
116 | return [0, arg]
117 | } else throw new Error('invalid argument')
118 | }
119 |
120 | function getCid(fn) {
121 | let id = callbackToId.get(fn)
122 | if (typeof id === 'undefined') {
123 | id = callbackid++
124 | callbackToId.set(fn, id)
125 | idToCallback.set(id, fn)
126 | }
127 | return id
128 | }
129 |
130 | const objHandler = {
131 | get(target, property) {
132 | if (property === master.objectSymbol) return target.id
133 | return master.makeProp(target.id, [property])
134 | },
135 | set(target, property, value) {
136 | master.enqueue([1, target.id, [property], master.wrap(value)])
137 | return true
138 | },
139 | }
140 |
141 | const propHandler = {
142 | get(target, property) {
143 | if (property === master.targetSymbol) return target
144 | const cache = target.cache
145 | const c = cache.get(property)
146 | if (c) return c
147 | const path = target.path.slice(0)
148 | path.push(property)
149 | const ret = master.makeProp(target.id, path)
150 | cache.set(property, ret)
151 | return ret
152 | },
153 |
154 | set(target, property, value) {
155 | const path = target.path.slice(0)
156 | path.push(property)
157 | master.enqueue([1, target.id, path, master.wrap(value)])
158 | return true
159 | },
160 |
161 | apply(target, thisArg, args) {
162 | const returnid = master.getReturnId()
163 | master.enqueue([0, target.id, target.path, args.map(master.wrap), returnid])
164 | return master.makeObj(returnid)
165 | },
166 |
167 | construct(target, args) {
168 | const returnid = master.getReturnId()
169 | master.enqueue([2, target.id, target.path, args.map(master.wrap), returnid])
170 | return master.makeObj(returnid)
171 | },
172 | }
173 |
174 | master.makeObj = function (id) {
175 | const fn = function () {}
176 | fn.id = id
177 | return new Proxy(fn, objHandler)
178 | }
179 |
180 | master.makeProp = function (id, path) {
181 | const fn = function () {}
182 | fn.id = id
183 | fn.path = path
184 | fn.cache = new Map()
185 | return new Proxy(fn, propHandler)
186 | }
187 |
188 | self.hijack = (cb) => {
189 | self.addEventListener('message', (e) => {
190 | if (e.data === 'start') {
191 | master.postMessage = (data) => self.postMessage(data)
192 | cb(master.makeObj(0))
193 | } else {
194 | master.onmessage(e.data)
195 | }
196 | })
197 | }
198 |
--------------------------------------------------------------------------------
/runtime/master/expr.js:
--------------------------------------------------------------------------------
1 | //copy from vue1.x https://github.com/vuejs/vue/blob/1.1/src/parsers/expression.js
2 |
3 | var defaultAllowedKeywords = "Math,Date,this,true,false,null,undefined,Infinity,NaN,isNaN,isFinite,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,parseInt,parseFloat"
4 | var improperKeywordsRE = new RegExp(
5 | "^(" + "break,case,class,catch,const,continue,debugger,default,delete,do,else,export,extends,finally,for,function,if,import,in,instanceof,let,return,super,switch,throw,try,var,while,with,yield,enum,await,implements,package,protected,static,interface,private,public".replace(/,/g, "\\b|") + "\\b)"
6 | )
7 | var wsRE = /\s/g
8 | var newlineRE = /\n/g
9 | var saveRE = /[\{,]\s*[\w\$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`)|new |typeof |void /g
10 | var restoreRE = /"(\d+)"/g
11 | var pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/
12 | var identRE = /[^\w$\.](?:[A-Za-z_$][\w$]*)/g
13 | var booleanLiteralRE = /^(?:true|false)$/
14 | var saved = []
15 |
16 | function save(str, isString) {
17 | var i = saved.length
18 | saved[i] = isString ? str.replace(newlineRE, "\\n") : str
19 | return '"' + i + '"'
20 | }
21 | function restore(str, i) {
22 | return saved[i]
23 | }
24 | function isSimplePath(expression) {
25 | return pathTestRE.test(expression) && !booleanLiteralRE.test(expression)
26 | }
27 | function parseKeywordsToRE(keywords) {
28 | return new RegExp(
29 | "^(?:" + keywords.replace(wsRE, "").replace(/\$/g, "\\$").replace(/,/g, "\\b|") + "\\b)"
30 | )
31 | }
32 |
33 | class Expr {
34 | constructor(options = {}) {
35 | let { scope = "$", scopes, params } = options
36 | this._cache = new Map()
37 | this._funcParams = (params || (scopes ? [scope, ...Object.keys(scopes)] : [scope])).join(",").replace(wsRE, "")
38 | this._funcBefore = `function(${this._funcParams}){return `
39 | this._funcAfter = "}"
40 | this.scope = scope
41 | if (scopes) {
42 | const keys = Object.keys(scopes)
43 | for (let i = 0, l = keys.length; i < l; i++) {
44 | const key = keys[i]
45 | const keywords = scopes[key]
46 | scopes[key] = parseKeywordsToRE(
47 | Object.prototype.toString.call(keywords) === "[object Array]" ? keywords.join(",") : keywords
48 | )
49 | }
50 | this._scopeREs = scopes
51 | }
52 | let paramsPrefix
53 | if (params && params.length > 1) {
54 | params = params.slice(1)
55 | paramsPrefix = params.join(",")
56 | this._paramsPrefixRE = parseKeywordsToRE(paramsPrefix)
57 | }
58 | const allowedKeywords = paramsPrefix ? `${paramsPrefix},${defaultAllowedKeywords}` : defaultAllowedKeywords
59 | this._allowedKeywordsRE = parseKeywordsToRE(allowedKeywords)
60 | }
61 |
62 | _addScope(expression) {
63 | if (this._paramsPrefixRE && this._paramsPrefixRE.test(expression)) {
64 | return expression
65 | }
66 | if (this._scopeREs) {
67 | const keys = Object.keys(this._scopeREs)
68 | for (let i = 0, l = keys.length; i < l; i++) {
69 | const re = this._scopeREs[keys[i]]
70 | if (re.test(expression)) {
71 | return `${keys[i]}.${expression}`
72 | }
73 | }
74 | }
75 | return `${this.scope}.${expression}`
76 | }
77 |
78 | compile(expression) {
79 | if (process.env.NODE_ENV === "development" && improperKeywordsRE.test(expression)) {
80 | console.warn(`Avoid using reserved keywords in expression: ${expression}`)
81 | }
82 | saved.length = 0
83 | let body = expression.replace(saveRE, save).replace(wsRE, "")
84 | const self = this
85 | let c, path
86 | return (" " + body).replace(identRE, (raw) => {
87 | c = raw.charAt(0)
88 | path = raw.slice(1)
89 | if (self._allowedKeywordsRE.test(path)) {
90 | return raw
91 | } else {
92 | path = path.indexOf('"') > -1 ? path.replace(restoreRE, restore) : path
93 | return c + self._addScope(path)
94 | }
95 | }).replace(restoreRE, restore).slice(1)
96 | }
97 |
98 | parse(source) {
99 | if (!(source && (source = source.trim()))) {
100 | return ""
101 | }
102 | let hit = this._cache.get(source)
103 | if (hit) {
104 | return hit
105 | }
106 | const result = isSimplePath(source) && source.indexOf("[") < 0 ? this._addScope(source) : this.compile(source)
107 | this._cache.set(source, result)
108 | return result
109 | }
110 |
111 | build(expression) {
112 | try {
113 | return new Function(this._funcParams, `return ${expression}`)
114 | } catch (e) {
115 | if (process.env.NODE_ENV === "development") {
116 | console.warn(`Invalid expression. Generated function body: ${expression}`)
117 | }
118 | }
119 | }
120 |
121 | buildToString(expression) {
122 | return `${this._funcBefore}${expression}${this._funcAfter}`
123 | }
124 |
125 | make(source) {
126 | const expression = this.parse(source)
127 | return this.build(expression)
128 | }
129 |
130 | makeToString(source) {
131 | const expression = this.parse(source)
132 | return this.buildToString(expression)
133 | }
134 | }
135 |
136 | export function expr(expr, obj) {
137 | const jep = new Expr()
138 | const fun = jep.make(expr)
139 | const res = fun(obj)
140 | return res
141 | }
--------------------------------------------------------------------------------
/smallappandroid/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/smallappandroid/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/runtime/dist/slave.js:
--------------------------------------------------------------------------------
1 | // slave/index.js
2 | var EVENT_BLACKLIST = "mousewheel wheel animationstart animationiteration animationend devicemotion deviceorientation deviceorientationabsolute".split(" ");
3 | var EVENT_OPTS = {
4 | capture: true,
5 | passive: true
6 | };
7 | function fakedom({ worker: worker2 }) {
8 | const NODES = /* @__PURE__ */ new Map();
9 | function getNode(node) {
10 | if (!node)
11 | return null;
12 | if (node.nodeName === "BODY")
13 | return document.body;
14 | return NODES.get(node.__id);
15 | }
16 | let supportsPassive = false;
17 | try {
18 | addEventListener("test", null, {
19 | get passive() {
20 | supportsPassive = true;
21 | }
22 | });
23 | } catch (e) {
24 | }
25 | for (let i in window) {
26 | let m = i.substring(2);
27 | if (i.substring(0, 2) === "on" && i === i.toLowerCase() && EVENT_BLACKLIST.indexOf(m) < 0 && (window[i] === null || typeof window[i] === "function")) {
28 | addEventListener(m, proxyEvent, supportsPassive ? EVENT_OPTS : true);
29 | }
30 | }
31 | let touch;
32 | function getTouch(e) {
33 | let t = e.changedTouches && e.changedTouches[0] || e.touches && e.touches[0] || e;
34 | return t && { pageX: t.pageX, pageY: t.pageY };
35 | }
36 | function proxyEvent(e) {
37 | if (e.type === "click" && touch)
38 | return false;
39 | let event = { type: e.type };
40 | if (e.target) {
41 | event.target = e.target.__id;
42 | }
43 | event.detail = {
44 | value: e.target.value,
45 | checked: e.target.checked
46 | };
47 | event.dataset = {};
48 | for (let d in e.target.dataset) {
49 | event.dataset[d] = e.target.dataset[d];
50 | }
51 | for (let i in e) {
52 | let v = e[i];
53 | if (typeof v !== "object" && typeof v !== "function" && i !== i.toUpperCase() && !event.hasOwnProperty(i)) {
54 | event[i] = v;
55 | }
56 | }
57 | worker2.postMessage(JSON.stringify({
58 | type: "event",
59 | event
60 | }));
61 | if (e.type === "touchstart") {
62 | touch = getTouch(e);
63 | } else if (e.type === "touchend" && touch) {
64 | let t = getTouch(e);
65 | if (t) {
66 | let delta = Math.sqrt(Math.pow(t.pageX - touch.pageX, 2) + Math.pow(t.pageY - touch.pageY, 2));
67 | if (delta < 10) {
68 | event.type = "click";
69 | worker2.postMessage(JSON.stringify({ type: "event", event }));
70 | }
71 | }
72 | }
73 | }
74 | function createNode(skel) {
75 | let node;
76 | if (skel.nodeType === 3) {
77 | node = document.createTextNode(skel.data);
78 | }
79 | if (skel.nodeType === 11) {
80 | node = document.createDocumentFragment();
81 | if (skel.childNodes) {
82 | for (let i = 0; i < skel.childNodes.length; i++) {
83 | node.appendChild(createNode(skel.childNodes[i]));
84 | }
85 | }
86 | } else if (skel.nodeType === 1) {
87 | node = document.createElement(skel.nodeName);
88 | if (skel.className) {
89 | node.className = skel.className;
90 | }
91 | if (skel.style) {
92 | for (let i in skel.style)
93 | if (skel.style.hasOwnProperty(i)) {
94 | node.style[i] = skel.style[i];
95 | }
96 | }
97 | if (skel.attributes) {
98 | for (let i = 0; i < skel.attributes.length; i++) {
99 | let a = skel.attributes[i];
100 | node.setAttribute(a.name, a.value);
101 | }
102 | }
103 | if (skel.childNodes) {
104 | for (let i = 0; i < skel.childNodes.length; i++) {
105 | node.appendChild(createNode(skel.childNodes[i]));
106 | }
107 | }
108 | }
109 | node.__id = skel.__id;
110 | NODES.set(skel.__id, node);
111 | return node;
112 | }
113 | const MUTATIONS = {
114 | childList({ target, removedNodes, addedNodes, previousSibling, nextSibling }) {
115 | let parent = getNode(target);
116 | if (removedNodes) {
117 | for (let i = removedNodes.length; i--; ) {
118 | parent.removeChild(getNode(removedNodes[i]));
119 | }
120 | }
121 | if (addedNodes) {
122 | for (let i = 0; i < addedNodes.length; i++) {
123 | let newNode = getNode(addedNodes[i]);
124 | if (!newNode) {
125 | newNode = createNode(addedNodes[i]);
126 | }
127 | parent.insertBefore(newNode, nextSibling && getNode(nextSibling) || null);
128 | }
129 | }
130 | },
131 | attributes({ target, attributeName }) {
132 | let val;
133 | for (let i = target.attributes.length; i--; ) {
134 | let p = target.attributes[i];
135 | if (p.name === attributeName) {
136 | val = p.value;
137 | break;
138 | }
139 | }
140 | getNode(target).setAttribute(attributeName, val);
141 | },
142 | characterData({ target, oldValue }) {
143 | getNode(target).nodeValue = target.data;
144 | }
145 | };
146 | function applyMutation(mutation) {
147 | MUTATIONS[mutation.type](mutation);
148 | }
149 | let timer;
150 | let MUTATION_QUEUE = [];
151 | function isElementInViewport(el, cache) {
152 | if (el.nodeType === 3)
153 | el = el.parentNode;
154 | let bbox = el.getBoundingClientRect();
155 | return bbox.bottom >= 0 && bbox.right >= 0 && bbox.top <= (cache.height || (cache.height = window.innerHeight)) && bbox.left <= (cache.width || (cache.width = window.innerWidth));
156 | }
157 | if (!self.requestIdleCallback) {
158 | const IDLE_TIMEOUT = 10;
159 | self.requestIdleCallback = (cb) => {
160 | let start = Date.now();
161 | setTimeout(() => cb({
162 | timeRemaining: () => Math.max(0, IDLE_TIMEOUT - (Date.now() - start))
163 | }), 1);
164 | };
165 | }
166 | function processMutationQueue(deadline) {
167 | clearTimeout(timer);
168 | let q = MUTATION_QUEUE, start = Date.now(), isDeadline = deadline && deadline.timeRemaining, cache = {}, useVis = (document.getElementById("#use-vis") || cache).checked, i;
169 | for (i = 0; i < q.length; i++) {
170 | if (isDeadline ? deadline.timeRemaining() <= 0 : Date.now() - start > 1)
171 | break;
172 | let m = q[i];
173 | if (useVis && (m.type === "characterData" || m.type === "attributes")) {
174 | let target = getNode(m.target);
175 | if (target && !isElementInViewport(target, cache))
176 | continue;
177 | }
178 | applyMutation(q.splice(i--, 1)[0]);
179 | }
180 | if (q.length)
181 | doProcessMutationQueue();
182 | }
183 | function doProcessMutationQueue() {
184 | clearTimeout(timer);
185 | timer = setTimeout(processMutationQueue, 100);
186 | requestIdleCallback(processMutationQueue);
187 | }
188 | function queueMutation(mutation) {
189 | if (mutation.type === "characterData" || mutation.type === "attributes") {
190 | for (let i = MUTATION_QUEUE.length; i--; ) {
191 | let m = MUTATION_QUEUE[i];
192 | if (m.type == mutation.type && m.target.__id == mutation.target.__id) {
193 | if (m.type === "attributes") {
194 | MUTATION_QUEUE.splice(i + 1, 0, mutation);
195 | } else {
196 | MUTATION_QUEUE[i] = mutation;
197 | }
198 | return;
199 | }
200 | }
201 | }
202 | if (MUTATION_QUEUE.push(mutation) === 1) {
203 | doProcessMutationQueue();
204 | }
205 | }
206 | worker2.onmessage = (res) => {
207 | let data = "";
208 | if (typeof res === "string") {
209 | data = JSON.parse(res);
210 | } else {
211 | data = JSON.parse(res.data);
212 | }
213 | if (data.type === "MutationRecord") {
214 | for (let i = 0; i < data.mutations.length; i++) {
215 | queueMutation(data.mutations[i]);
216 | }
217 | } else if (data.type === "wxapi") {
218 | if (typeof window["nativeChannel"] !== "undefined") {
219 | window["nativeChannel"].postMessage(JSON.stringify(data));
220 | }
221 | }
222 | };
223 | if (window.isAndroid) {
224 | worker2.postMessage(JSON.stringify({
225 | type: "init"
226 | }));
227 | } else {
228 | console.log(worker2);
229 | worker2.postMessage(JSON.stringify({
230 | type: "init",
231 | manifest: window.manifest
232 | }));
233 | }
234 | }
235 | window["javascriptChannel"] = function(json) {
236 | worker.postMessage(JSON.stringify({ type: "wxcallback", payload: json }));
237 | };
238 | fakedom.umd = true;
239 |
--------------------------------------------------------------------------------
/runtime/master/dom/fake-dom.js:
--------------------------------------------------------------------------------
1 | export default function fakedom() {
2 | let observers = [],
3 | pendingMutations = false
4 |
5 | class Node {
6 | constructor(nodeType, nodeName) {
7 | this.nodeType = nodeType
8 | this.nodeName = nodeName
9 | this.childNodes = []
10 | }
11 | appendChild(child) {
12 | child.remove()
13 | child.parentNode = this
14 | this.childNodes.push(child)
15 | if (this.children && child.nodeType === 1) this.children.push(child)
16 | mutation(this, 'childList', { addedNodes: [child], previousSibling: this.childNodes[this.childNodes.length - 2] })
17 | }
18 | insertBefore(child, ref) {
19 | child.remove()
20 | let i = splice(this.childNodes, ref, child), ref2
21 | if (!ref) {
22 | this.appendChild(child)
23 | }
24 | else {
25 | if (~i && child.nodeType === 1) {
26 | while (i < this.childNodes.length && (ref2 = this.childNodes[i]).nodeType !== 1 || ref === child) i++
27 | if (ref2) splice(this.children, ref, child)
28 | }
29 | mutation(this, 'childList', { addedNodes: [child], nextSibling: ref })
30 | }
31 | }
32 | replaceChild(child, ref) {
33 | if (ref.parentNode === this) {
34 | this.insertBefore(child, ref)
35 | ref.remove()
36 | }
37 | }
38 | removeChild(child) {
39 | let i = splice(this.childNodes, child)
40 | if (child.nodeType === 1) splice(this.children, child)
41 | mutation(this, 'childList', { removedNodes: [child], previousSibling: this.childNodes[i - 1], nextSibling: this.childNodes[i] })
42 | }
43 | remove() {
44 | if (this.parentNode) this.parentNode.removeChild(this)
45 | }
46 | }
47 |
48 |
49 | class Text extends Node {
50 | constructor(text) {
51 | super(3, '#text') // TEXT_NODE
52 | // this.textContent = this.nodeValue = text;
53 | this.data = text
54 | }
55 | get textContent() {
56 | return this.data
57 | }
58 | set textContent(value) {
59 | let oldValue = this.data
60 | this.data = value
61 | mutation(this, 'characterData', { oldValue })
62 | }
63 | get nodeValue() {
64 | return this.data
65 | }
66 | set nodeValue(value) {
67 | this.textContent = value
68 | }
69 | }
70 |
71 |
72 | class Element extends Node {
73 | constructor(nodeType, nodeName) {
74 | super(nodeType || 1, nodeName) // ELEMENT_NODE
75 | this.attributes = []
76 | this.children = []
77 | this.__handlers = {}
78 | this.style = {}
79 | Object.defineProperty(this, 'className', {
80 | set: val => { this.setAttribute('class', val) },
81 | get: () => this.getAttribute('style')
82 | })
83 | Object.defineProperty(this.style, 'cssText', {
84 | set: val => { this.setAttribute('style', val) },
85 | get: () => this.getAttribute('style')
86 | })
87 | }
88 |
89 | setAttribute(key, value) {
90 | this.setAttributeNS(null, key, value)
91 | }
92 | getAttribute(key) {
93 | return this.getAttributeNS(null, key)
94 | }
95 | removeAttribute(key) {
96 | this.removeAttributeNS(null, key)
97 | }
98 |
99 | setAttributeNS(ns, name, value) {
100 | let attr = findWhere(this.attributes, createAttributeFilter(ns, name)),
101 | oldValue = attr && attr.value
102 | if (!attr) this.attributes.push(attr = { ns, name })
103 | attr.value = String(value)
104 | mutation(this, 'attributes', { attributeName: name, attributeNamespace: ns, oldValue })
105 | }
106 | getAttributeNS(ns, name) {
107 | let attr = findWhere(this.attributes, createAttributeFilter(ns, name))
108 | return attr && attr.value
109 | }
110 | removeAttributeNS(ns, name) {
111 | splice(this.attributes, createAttributeFilter(ns, name))
112 | mutation(this, 'attributes', { attributeName: name, attributeNamespace: ns, oldValue: this.getAttributeNS(ns, name) })
113 | }
114 |
115 | addEventListener(type, handler) {
116 | (this.__handlers[toLower(type)] || (this.__handlers[toLower(type)] = [])).push(handler)
117 | }
118 | removeEventListener(type, handler) {
119 | splice(this.__handlers[toLower(type)], handler, 0, true)
120 | }
121 | dispatchEvent(event) {
122 | let t = event.currentTarget = this,
123 | c = event.cancelable,
124 | l, i
125 | do {
126 | l = t.__handlers && t.__handlers[toLower(event.type)]
127 | if (l) for (i = l.length; i--;) {
128 | if ((l[i].call(t, event) === false || event._end) && c) break
129 | }
130 | } while (event.bubbles && !(c && event._stop) && (event.target = t = t.parentNode))
131 | return !event.defaultPrevented
132 | }
133 | }
134 |
135 |
136 | class SVGElement extends Element { }
137 |
138 |
139 | class Document extends Element {
140 | constructor() {
141 | super(9, '#document') // DOCUMENT_NODE
142 | }
143 | }
144 |
145 |
146 | class Event {
147 | constructor(type, opts) {
148 | this.type = type
149 | this.bubbles = !!opts.bubbles
150 | this.cancelable = !!opts.cancelable
151 | }
152 | stopPropagation() {
153 | this._stop = true
154 | }
155 | stopImmediatePropagation() {
156 | this._end = this._stop = true
157 | }
158 | preventDefault() {
159 | this.defaultPrevented = true
160 | }
161 | }
162 |
163 |
164 | function mutation(target, type, record) {
165 | record.target = target
166 | record.type = type
167 |
168 | for (let i = observers.length; i--;) {
169 | let ob = observers[i],
170 | match = target === ob._target
171 | if (!match && ob._options.subtree) {
172 | do {
173 | if ((match = target === ob._target)) break
174 | } while ((target = target.parentNode))
175 | }
176 | if (match) {
177 | ob._records.push(record)
178 | if (!pendingMutations) {
179 | pendingMutations = true
180 | setImmediate(flushMutations)
181 | }
182 | }
183 | }
184 | }
185 |
186 | function flushMutations() {
187 | pendingMutations = false
188 | for (let i = observers.length; i--;) {
189 | let ob = observers[i]
190 | if (ob._records.length) {
191 | ob.callback(ob.takeRecords())
192 | }
193 | }
194 | }
195 |
196 | class MutationObserver {
197 | constructor(callback) {
198 | this.callback = callback
199 | this._records = []
200 | }
201 | observe(target, options) {
202 | this.disconnect()
203 | this._target = target
204 | this._options = options || {}
205 | observers.push(this)
206 | }
207 | disconnect() {
208 | this._target = null
209 | splice(observers, this)
210 | }
211 | takeRecords() {
212 | return this._records.splice(0, this._records.length)
213 | }
214 | }
215 |
216 |
217 | function createElement(type) {
218 | return new Element(null, String(type).toUpperCase())
219 | }
220 |
221 | function createDocumentFragment(type) {
222 | return new Element(11, '#document-fragment')
223 | }
224 |
225 |
226 | function createElementNS(ns, type) {
227 | let element = createElement(type)
228 | element.namespace = ns
229 | return element
230 | }
231 |
232 |
233 | function createTextNode(text) {
234 | return new Text(text)
235 | }
236 |
237 |
238 | function createDocument() {
239 | let document = new Document()
240 | assign(document, document.defaultView = { document, MutationObserver, Document, Node, Text, Element, SVGElement, Event })
241 | assign(document, { documentElement: document, createElement, createElementNS, createTextNode, createDocumentFragment })
242 | document.appendChild(document.body = createElement('body'))
243 | return document
244 | }
245 |
246 |
247 | return createDocument()
248 | }
249 |
250 | export function assign(obj, props) {
251 | for (let i in props) obj[i] = props[i] // eslint-disable-line guard-for-in
252 | }
253 |
254 | export function toLower(str) {
255 | return String(str).toLowerCase()
256 | }
257 |
258 | export function createAttributeFilter(ns, name) {
259 | return o => o.ns === ns && toLower(o.name) === toLower(name)
260 | }
261 |
262 | export function splice(arr, item, add, byValueOnly) {
263 | let i = arr ? findWhere(arr, item, true, byValueOnly) : -1
264 | if (~i) add ? arr.splice(i, 0, add) : arr.splice(i, 1)
265 | return i
266 | }
267 |
268 | export function findWhere(arr, fn, returnIndex, byValueOnly) {
269 | let i = arr.length
270 | while (i--) if (typeof fn === 'function' && !byValueOnly ? fn(arr[i]) : arr[i] === fn) break
271 | return returnIndex ? i : arr[i]
272 | }
273 |
274 | let resolved = typeof Promise !== 'undefined' && Promise.resolve()
275 | export const setImmediate = resolved ? (f => { resolved.then(f) }) : setTimeout
--------------------------------------------------------------------------------
/runtime/slave/index.js:
--------------------------------------------------------------------------------
1 | const EVENT_BLACKLIST = 'mousewheel wheel animationstart animationiteration animationend devicemotion deviceorientation deviceorientationabsolute'.split(' ');
2 |
3 |
4 | const EVENT_OPTS = {
5 | capture: true,
6 | passive: true
7 | };
8 |
9 |
10 | function fakedom({worker}) {
11 | const NODES = new Map();
12 |
13 | function getNode(node) {
14 | if (!node) return null;
15 | if (node.nodeName === 'BODY') return document.body;
16 | return NODES.get(node.__id);
17 | }
18 |
19 | let supportsPassive = false;
20 | try {
21 | addEventListener('test', null, {
22 | get passive() { supportsPassive = true; }
23 | });
24 | } catch (e) { }
25 |
26 |
27 |
28 | for (let i in window) {
29 | let m = i.substring(2);
30 | if (i.substring(0, 2) === 'on' && i === i.toLowerCase() && EVENT_BLACKLIST.indexOf(m) < 0 && (window[i] === null || typeof window[i] === 'function')) {
31 | addEventListener(m, proxyEvent, supportsPassive ? EVENT_OPTS : true);
32 | }
33 | }
34 |
35 |
36 | let touch;
37 |
38 | function getTouch(e) {
39 | let t = (e.changedTouches && e.changedTouches[0]) || (e.touches && e.touches[0]) || e;
40 | return t && { pageX: t.pageX, pageY: t.pageY };
41 | }
42 |
43 | function proxyEvent(e) {
44 | if (e.type === 'click' && touch) return false;
45 |
46 | let event = { type: e.type };
47 | if (e.target) {
48 | event.target = e.target.__id;
49 | }
50 | event.detail = {
51 | value: e.target.value,
52 | checked: e.target.checked,
53 | }
54 | event.dataset = {}
55 | for (let d in e.target.dataset) {
56 | event.dataset[d] = e.target.dataset[d]
57 | }
58 | for (let i in e) {
59 | let v = e[i];
60 | if (typeof v !== 'object' && typeof v !== 'function' && i !== i.toUpperCase() && !event.hasOwnProperty(i)) {
61 | event[i] = v;
62 | }
63 | }
64 | worker.postMessage(JSON.stringify({
65 | type: 'event',
66 | event
67 | }));
68 |
69 | if (e.type === 'touchstart') {
70 | touch = getTouch(e);
71 | }
72 | else if (e.type === 'touchend' && touch) {
73 | let t = getTouch(e);
74 | if (t) {
75 | let delta = Math.sqrt(Math.pow(t.pageX - touch.pageX, 2) + Math.pow(t.pageY - touch.pageY, 2));
76 | if (delta < 10) {
77 | event.type = 'click';
78 | worker.postMessage(JSON.stringify({ type: 'event', event }));
79 | }
80 | }
81 | }
82 | }
83 |
84 |
85 | function createNode(skel) {
86 | let node;
87 | if (skel.nodeType === 3) {
88 | node = document.createTextNode(skel.data);
89 | }
90 | if (skel.nodeType === 11) {
91 | node = document.createDocumentFragment();
92 | if (skel.childNodes) {
93 | for (let i = 0; i < skel.childNodes.length; i++) {
94 | node.appendChild(createNode(skel.childNodes[i]));
95 | }
96 | }
97 | }
98 | else if (skel.nodeType === 1) {
99 | node = document.createElement(skel.nodeName);
100 | if (skel.className) {
101 | node.className = skel.className;
102 | }
103 | if (skel.style) {
104 | for (let i in skel.style) if (skel.style.hasOwnProperty(i)) {
105 | node.style[i] = skel.style[i];
106 | }
107 | }
108 | if (skel.attributes) {
109 | for (let i = 0; i < skel.attributes.length; i++) {
110 | let a = skel.attributes[i];
111 | // @TODO .ns
112 | node.setAttribute(a.name, a.value);
113 | }
114 | }
115 | if (skel.childNodes) {
116 | for (let i = 0; i < skel.childNodes.length; i++) {
117 | node.appendChild(createNode(skel.childNodes[i]));
118 | }
119 | }
120 | }
121 | node.__id = skel.__id;
122 | NODES.set(skel.__id, node);
123 | return node;
124 | }
125 |
126 |
127 | const MUTATIONS = {
128 | childList({ target, removedNodes, addedNodes, previousSibling, nextSibling }) {
129 | let parent = getNode(target);
130 | if (removedNodes) {
131 | for (let i = removedNodes.length; i--;) {
132 | parent.removeChild(getNode(removedNodes[i]));
133 | }
134 | }
135 | if (addedNodes) {
136 | for (let i = 0; i < addedNodes.length; i++) {
137 | let newNode = getNode(addedNodes[i]);
138 | if (!newNode) {
139 | newNode = createNode(addedNodes[i]);
140 | }
141 | parent.insertBefore(newNode, nextSibling && getNode(nextSibling) || null);
142 | }
143 | }
144 | },
145 | attributes({ target, attributeName }) {
146 | let val;
147 | for (let i = target.attributes.length; i--;) {
148 | let p = target.attributes[i];
149 | if (p.name === attributeName) {
150 | val = p.value;
151 | break;
152 | }
153 | }
154 | getNode(target).setAttribute(attributeName, val);
155 | },
156 | characterData({ target, oldValue }) {
157 | getNode(target).nodeValue = target.data;
158 | }
159 | };
160 |
161 |
162 | function applyMutation(mutation) {
163 | MUTATIONS[mutation.type](mutation);
164 | }
165 |
166 | let timer;
167 |
168 | let MUTATION_QUEUE = [];
169 |
170 | function isElementInViewport(el, cache) {
171 | if (el.nodeType === 3) el = el.parentNode;
172 | let bbox = el.getBoundingClientRect();
173 | return (
174 | bbox.bottom >= 0 &&
175 | bbox.right >= 0 &&
176 | bbox.top <= (cache.height || (cache.height = window.innerHeight)) &&
177 | bbox.left <= (cache.width || (cache.width = window.innerWidth))
178 | );
179 | }
180 |
181 |
182 | if (!self.requestIdleCallback) {
183 | const IDLE_TIMEOUT = 10;
184 | self.requestIdleCallback = cb => {
185 | let start = Date.now();
186 | setTimeout(() => cb({
187 | timeRemaining: () => Math.max(0, IDLE_TIMEOUT - (Date.now() - start))
188 | }), 1);
189 | };
190 | }
191 |
192 |
193 | function processMutationQueue(deadline) {
194 | clearTimeout(timer);
195 | let q = MUTATION_QUEUE,
196 | start = Date.now(),
197 | isDeadline = deadline && deadline.timeRemaining,
198 | cache = {},
199 | useVis = (document.getElementById('#use-vis') || cache).checked,
200 | i;
201 | for (i = 0; i < q.length; i++) {
202 | if (isDeadline ? deadline.timeRemaining() <= 0 : (Date.now() - start) > 1) break;
203 |
204 | let m = q[i];
205 |
206 | if (useVis && (m.type === 'characterData' || m.type === 'attributes')) {
207 | let target = getNode(m.target);
208 | if (target && !isElementInViewport(target, cache)) continue;
209 | }
210 |
211 | applyMutation(q.splice(i--, 1)[0]);
212 | }
213 |
214 | if (q.length) doProcessMutationQueue();
215 | }
216 |
217 |
218 | function doProcessMutationQueue() {
219 | clearTimeout(timer);
220 | timer = setTimeout(processMutationQueue, 100);
221 | requestIdleCallback(processMutationQueue);
222 | }
223 |
224 |
225 | function queueMutation(mutation) {
226 | if (mutation.type === 'characterData' || mutation.type === 'attributes') {
227 | for (let i = MUTATION_QUEUE.length; i--;) {
228 | let m = MUTATION_QUEUE[i];
229 | if (m.type == mutation.type && m.target.__id == mutation.target.__id) {
230 | if (m.type === 'attributes') {
231 | MUTATION_QUEUE.splice(i + 1, 0, mutation);
232 | }
233 | else {
234 | MUTATION_QUEUE[i] = mutation;
235 | }
236 | return;
237 | }
238 | }
239 | }
240 | if (MUTATION_QUEUE.push(mutation) === 1) {
241 | doProcessMutationQueue();
242 | }
243 | }
244 |
245 |
246 | worker.onmessage = (res) => {
247 | let data = ''
248 | if (typeof res === 'string') {
249 | data = JSON.parse(res)
250 | } else {
251 | data = JSON.parse(res.data)
252 | }
253 |
254 | if (data.type === 'MutationRecord') {
255 | for (let i = 0; i < data.mutations.length; i++) {
256 | queueMutation(data.mutations[i]);
257 | }
258 | } else if (data.type === 'wxapi') {
259 | if (typeof window['nativeChannel'] !== 'undefined') {
260 | window['nativeChannel'].postMessage(JSON.stringify(data));
261 | }
262 | }
263 | };
264 | if (window.isAndroid) {
265 | worker.postMessage(JSON.stringify({
266 | type: 'init'
267 | }))
268 | } else {
269 | console.log(worker)
270 | worker.postMessage(JSON.stringify({
271 | type: 'init',
272 | manifest: window.manifest
273 | }))
274 | }
275 | };
276 |
277 | window['javascriptChannel'] = function (json) { // native 调用 webview,只有这一处
278 | worker.postMessage(JSON.stringify({ type: 'wxcallback', payload: json }))
279 | }
280 |
281 | fakedom.umd = true
282 |
--------------------------------------------------------------------------------
/demo/pages/index/dayjs.js:
--------------------------------------------------------------------------------
1 | var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
2 | function createCommonjsModule(fn, basedir, module) {
3 | return module = {
4 | path: basedir,
5 | exports: {},
6 | require: function(path, base) {
7 | return commonjsRequire(path, base === void 0 || base === null ? module.path : base);
8 | }
9 | }, fn(module, module.exports), module.exports;
10 | }
11 | function commonjsRequire() {
12 | throw new Error("Dynamic requires are not currently supported by @rollup/plugin-commonjs");
13 | }
14 | var dayjs_min = createCommonjsModule(function(module, exports) {
15 | !function(t, e) {
16 | module.exports = e();
17 | }(commonjsGlobal, function() {
18 | var t = "millisecond", e = "second", n = "minute", r = "hour", i = "day", s = "week", u = "month", a = "quarter", o = "year", f = "date", h = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, c = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, d = {name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_")}, $ = function(t2, e2, n2) {
19 | var r2 = String(t2);
20 | return !r2 || r2.length >= e2 ? t2 : "" + Array(e2 + 1 - r2.length).join(n2) + t2;
21 | }, l = {s: $, z: function(t2) {
22 | var e2 = -t2.utcOffset(), n2 = Math.abs(e2), r2 = Math.floor(n2 / 60), i2 = n2 % 60;
23 | return (e2 <= 0 ? "+" : "-") + $(r2, 2, "0") + ":" + $(i2, 2, "0");
24 | }, m: function t2(e2, n2) {
25 | if (e2.date() < n2.date())
26 | return -t2(n2, e2);
27 | var r2 = 12 * (n2.year() - e2.year()) + (n2.month() - e2.month()), i2 = e2.clone().add(r2, u), s2 = n2 - i2 < 0, a2 = e2.clone().add(r2 + (s2 ? -1 : 1), u);
28 | return +(-(r2 + (n2 - i2) / (s2 ? i2 - a2 : a2 - i2)) || 0);
29 | }, a: function(t2) {
30 | return t2 < 0 ? Math.ceil(t2) || 0 : Math.floor(t2);
31 | }, p: function(h2) {
32 | return {M: u, y: o, w: s, d: i, D: f, h: r, m: n, s: e, ms: t, Q: a}[h2] || String(h2 || "").toLowerCase().replace(/s$/, "");
33 | }, u: function(t2) {
34 | return t2 === void 0;
35 | }}, y = "en", M = {};
36 | M[y] = d;
37 | var m = function(t2) {
38 | return t2 instanceof S;
39 | }, D = function(t2, e2, n2) {
40 | var r2;
41 | if (!t2)
42 | return y;
43 | if (typeof t2 == "string")
44 | M[t2] && (r2 = t2), e2 && (M[t2] = e2, r2 = t2);
45 | else {
46 | var i2 = t2.name;
47 | M[i2] = t2, r2 = i2;
48 | }
49 | return !n2 && r2 && (y = r2), r2 || !n2 && y;
50 | }, v = function(t2, e2) {
51 | if (m(t2))
52 | return t2.clone();
53 | var n2 = typeof e2 == "object" ? e2 : {};
54 | return n2.date = t2, n2.args = arguments, new S(n2);
55 | }, g = l;
56 | g.l = D, g.i = m, g.w = function(t2, e2) {
57 | return v(t2, {locale: e2.$L, utc: e2.$u, x: e2.$x, $offset: e2.$offset});
58 | };
59 | var S = function() {
60 | function d2(t2) {
61 | this.$L = D(t2.locale, null, true), this.parse(t2);
62 | }
63 | var $2 = d2.prototype;
64 | return $2.parse = function(t2) {
65 | this.$d = function(t3) {
66 | var e2 = t3.date, n2 = t3.utc;
67 | if (e2 === null)
68 | return new Date(NaN);
69 | if (g.u(e2))
70 | return new Date();
71 | if (e2 instanceof Date)
72 | return new Date(e2);
73 | if (typeof e2 == "string" && !/Z$/i.test(e2)) {
74 | var r2 = e2.match(h);
75 | if (r2) {
76 | var i2 = r2[2] - 1 || 0, s2 = (r2[7] || "0").substring(0, 3);
77 | return n2 ? new Date(Date.UTC(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2)) : new Date(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2);
78 | }
79 | }
80 | return new Date(e2);
81 | }(t2), this.$x = t2.x || {}, this.init();
82 | }, $2.init = function() {
83 | var t2 = this.$d;
84 | this.$y = t2.getFullYear(), this.$M = t2.getMonth(), this.$D = t2.getDate(), this.$W = t2.getDay(), this.$H = t2.getHours(), this.$m = t2.getMinutes(), this.$s = t2.getSeconds(), this.$ms = t2.getMilliseconds();
85 | }, $2.$utils = function() {
86 | return g;
87 | }, $2.isValid = function() {
88 | return !(this.$d.toString() === "Invalid Date");
89 | }, $2.isSame = function(t2, e2) {
90 | var n2 = v(t2);
91 | return this.startOf(e2) <= n2 && n2 <= this.endOf(e2);
92 | }, $2.isAfter = function(t2, e2) {
93 | return v(t2) < this.startOf(e2);
94 | }, $2.isBefore = function(t2, e2) {
95 | return this.endOf(e2) < v(t2);
96 | }, $2.$g = function(t2, e2, n2) {
97 | return g.u(t2) ? this[e2] : this.set(n2, t2);
98 | }, $2.unix = function() {
99 | return Math.floor(this.valueOf() / 1e3);
100 | }, $2.valueOf = function() {
101 | return this.$d.getTime();
102 | }, $2.startOf = function(t2, a2) {
103 | var h2 = this, c2 = !!g.u(a2) || a2, d3 = g.p(t2), $3 = function(t3, e2) {
104 | var n2 = g.w(h2.$u ? Date.UTC(h2.$y, e2, t3) : new Date(h2.$y, e2, t3), h2);
105 | return c2 ? n2 : n2.endOf(i);
106 | }, l2 = function(t3, e2) {
107 | return g.w(h2.toDate()[t3].apply(h2.toDate("s"), (c2 ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e2)), h2);
108 | }, y2 = this.$W, M2 = this.$M, m2 = this.$D, D2 = "set" + (this.$u ? "UTC" : "");
109 | switch (d3) {
110 | case o:
111 | return c2 ? $3(1, 0) : $3(31, 11);
112 | case u:
113 | return c2 ? $3(1, M2) : $3(0, M2 + 1);
114 | case s:
115 | var v2 = this.$locale().weekStart || 0, S2 = (y2 < v2 ? y2 + 7 : y2) - v2;
116 | return $3(c2 ? m2 - S2 : m2 + (6 - S2), M2);
117 | case i:
118 | case f:
119 | return l2(D2 + "Hours", 0);
120 | case r:
121 | return l2(D2 + "Minutes", 1);
122 | case n:
123 | return l2(D2 + "Seconds", 2);
124 | case e:
125 | return l2(D2 + "Milliseconds", 3);
126 | default:
127 | return this.clone();
128 | }
129 | }, $2.endOf = function(t2) {
130 | return this.startOf(t2, false);
131 | }, $2.$set = function(s2, a2) {
132 | var h2, c2 = g.p(s2), d3 = "set" + (this.$u ? "UTC" : ""), $3 = (h2 = {}, h2[i] = d3 + "Date", h2[f] = d3 + "Date", h2[u] = d3 + "Month", h2[o] = d3 + "FullYear", h2[r] = d3 + "Hours", h2[n] = d3 + "Minutes", h2[e] = d3 + "Seconds", h2[t] = d3 + "Milliseconds", h2)[c2], l2 = c2 === i ? this.$D + (a2 - this.$W) : a2;
133 | if (c2 === u || c2 === o) {
134 | var y2 = this.clone().set(f, 1);
135 | y2.$d[$3](l2), y2.init(), this.$d = y2.set(f, Math.min(this.$D, y2.daysInMonth())).$d;
136 | } else
137 | $3 && this.$d[$3](l2);
138 | return this.init(), this;
139 | }, $2.set = function(t2, e2) {
140 | return this.clone().$set(t2, e2);
141 | }, $2.get = function(t2) {
142 | return this[g.p(t2)]();
143 | }, $2.add = function(t2, a2) {
144 | var f2, h2 = this;
145 | t2 = Number(t2);
146 | var c2 = g.p(a2), d3 = function(e2) {
147 | var n2 = v(h2);
148 | return g.w(n2.date(n2.date() + Math.round(e2 * t2)), h2);
149 | };
150 | if (c2 === u)
151 | return this.set(u, this.$M + t2);
152 | if (c2 === o)
153 | return this.set(o, this.$y + t2);
154 | if (c2 === i)
155 | return d3(1);
156 | if (c2 === s)
157 | return d3(7);
158 | var $3 = (f2 = {}, f2[n] = 6e4, f2[r] = 36e5, f2[e] = 1e3, f2)[c2] || 1, l2 = this.$d.getTime() + t2 * $3;
159 | return g.w(l2, this);
160 | }, $2.subtract = function(t2, e2) {
161 | return this.add(-1 * t2, e2);
162 | }, $2.format = function(t2) {
163 | var e2 = this;
164 | if (!this.isValid())
165 | return "Invalid Date";
166 | var n2 = t2 || "YYYY-MM-DDTHH:mm:ssZ", r2 = g.z(this), i2 = this.$locale(), s2 = this.$H, u2 = this.$m, a2 = this.$M, o2 = i2.weekdays, f2 = i2.months, h2 = function(t3, r3, i3, s3) {
167 | return t3 && (t3[r3] || t3(e2, n2)) || i3[r3].substr(0, s3);
168 | }, d3 = function(t3) {
169 | return g.s(s2 % 12 || 12, t3, "0");
170 | }, $3 = i2.meridiem || function(t3, e3, n3) {
171 | var r3 = t3 < 12 ? "AM" : "PM";
172 | return n3 ? r3.toLowerCase() : r3;
173 | }, l2 = {YY: String(this.$y).slice(-2), YYYY: this.$y, M: a2 + 1, MM: g.s(a2 + 1, 2, "0"), MMM: h2(i2.monthsShort, a2, f2, 3), MMMM: h2(f2, a2), D: this.$D, DD: g.s(this.$D, 2, "0"), d: String(this.$W), dd: h2(i2.weekdaysMin, this.$W, o2, 2), ddd: h2(i2.weekdaysShort, this.$W, o2, 3), dddd: o2[this.$W], H: String(s2), HH: g.s(s2, 2, "0"), h: d3(1), hh: d3(2), a: $3(s2, u2, true), A: $3(s2, u2, false), m: String(u2), mm: g.s(u2, 2, "0"), s: String(this.$s), ss: g.s(this.$s, 2, "0"), SSS: g.s(this.$ms, 3, "0"), Z: r2};
174 | return n2.replace(c, function(t3, e3) {
175 | return e3 || l2[t3] || r2.replace(":", "");
176 | });
177 | }, $2.utcOffset = function() {
178 | return 15 * -Math.round(this.$d.getTimezoneOffset() / 15);
179 | }, $2.diff = function(t2, f2, h2) {
180 | var c2, d3 = g.p(f2), $3 = v(t2), l2 = 6e4 * ($3.utcOffset() - this.utcOffset()), y2 = this - $3, M2 = g.m(this, $3);
181 | return M2 = (c2 = {}, c2[o] = M2 / 12, c2[u] = M2, c2[a] = M2 / 3, c2[s] = (y2 - l2) / 6048e5, c2[i] = (y2 - l2) / 864e5, c2[r] = y2 / 36e5, c2[n] = y2 / 6e4, c2[e] = y2 / 1e3, c2)[d3] || y2, h2 ? M2 : g.a(M2);
182 | }, $2.daysInMonth = function() {
183 | return this.endOf(u).$D;
184 | }, $2.$locale = function() {
185 | return M[this.$L];
186 | }, $2.locale = function(t2, e2) {
187 | if (!t2)
188 | return this.$L;
189 | var n2 = this.clone(), r2 = D(t2, e2, true);
190 | return r2 && (n2.$L = r2), n2;
191 | }, $2.clone = function() {
192 | return g.w(this.$d, this);
193 | }, $2.toDate = function() {
194 | return new Date(this.valueOf());
195 | }, $2.toJSON = function() {
196 | return this.isValid() ? this.toISOString() : null;
197 | }, $2.toISOString = function() {
198 | return this.$d.toISOString();
199 | }, $2.toString = function() {
200 | return this.$d.toUTCString();
201 | }, d2;
202 | }(), p2 = S.prototype;
203 | return v.prototype = p2, [["$ms", t], ["$s", e], ["$m", n], ["$H", r], ["$W", i], ["$M", u], ["$y", o], ["$D", f]].forEach(function(t2) {
204 | p2[t2[1]] = function(e2) {
205 | return this.$g(e2, t2[0], t2[1]);
206 | };
207 | }), v.extend = function(t2, e2) {
208 | return t2.$i || (t2(e2, S, v), t2.$i = true), v;
209 | }, v.locale = D, v.isDayjs = m, v.unix = function(t2) {
210 | return v(1e3 * t2);
211 | }, v.en = M[y], v.Ls = M, v.p = {}, v;
212 | });
213 | });
214 | var Ls = dayjs_min.Ls;
215 | export default dayjs_min;
216 | var en = dayjs_min.en;
217 | var extend = dayjs_min.extend;
218 | var isDayjs = dayjs_min.isDayjs;
219 | var locale = dayjs_min.locale;
220 | var p = dayjs_min.p;
221 | var unix = dayjs_min.unix;
222 | export {Ls, dayjs_min as __moduleExports, en, extend, isDayjs, locale, p, unix};
223 |
--------------------------------------------------------------------------------
/runtime/master/fre-esm.js:
--------------------------------------------------------------------------------
1 | const defaultObj = {};
2 | const jointIter = (aProps, bProps, callback) => {
3 | aProps = aProps || defaultObj;
4 | bProps = bProps || defaultObj;
5 | Object.keys(aProps).forEach((k) => callback(k, aProps[k], bProps[k]));
6 | Object.keys(bProps).forEach((k) => !aProps.hasOwnProperty(k) && callback(k, undefined, bProps[k]));
7 | };
8 | const updateElement = (dom, aProps, bProps) => {
9 | jointIter(aProps, bProps, (name, a, b) => {
10 | if (a === b || name === 'children') ;
11 | else if (name === 'style' && !isStr(b)) {
12 | jointIter(a, b, (styleKey, aStyle, bStyle) => {
13 | if (aStyle !== bStyle) {
14 | dom[name][styleKey] = bStyle || '';
15 | }
16 | });
17 | }
18 | else if (name[0] === 'o' && name[1] === 'n') {
19 | name = name.slice(2).toLowerCase();
20 | if (a)
21 | dom.removeEventListener(name, a);
22 | dom.addEventListener(name, b);
23 | }
24 | else if (name in dom && !(dom instanceof SVGElement)) {
25 | dom[name] = b || '';
26 | }
27 | else if (b == null || b === false) {
28 | dom.removeAttribute(name);
29 | }
30 | else {
31 | dom.setAttribute && (dom === null || dom === void 0 ? void 0 : dom.setAttribute(name, b));
32 | }
33 | });
34 | };
35 | const createElement = (fiber) => {
36 | const dom = fiber.type === '#text'
37 | ? document.createTextNode('')
38 | : fiber.lane & 16
39 | ? document.createElementNS('http://www.w3.org/2000/svg', fiber.type)
40 | : document.createElement(fiber.type);
41 | updateElement(dom, {}, fiber.props);
42 | return dom;
43 | };
44 |
45 | const EMPTY_ARR = [];
46 | let cursor = 0;
47 | const resetCursor = () => {
48 | cursor = 0;
49 | };
50 | const useState = (initState) => {
51 | return useReducer(null, initState);
52 | };
53 | const useReducer = (reducer, initState) => {
54 | const [hook, current] = getSlot(cursor++);
55 | if (hook.length === 0) {
56 | hook[0] = initState;
57 | }
58 | hook[1] = (value) => {
59 | let v = reducer
60 | ? reducer(hook[0], value)
61 | : isFn(value)
62 | ? value(hook[0])
63 | : value;
64 | if (hook[0] !== v) {
65 | hook[0] = v;
66 | update(current);
67 | }
68 | };
69 | return hook;
70 | };
71 | const useEffect = (cb, deps) => {
72 | return effectImpl(cb, deps, 'effect');
73 | };
74 | const useLayout = (cb, deps) => {
75 | return effectImpl(cb, deps, 'layout');
76 | };
77 | const effectImpl = (cb, deps, key) => {
78 | const [hook, current] = getSlot(cursor++);
79 | if (isChanged(hook[1], deps)) {
80 | hook[0] = cb;
81 | hook[1] = deps;
82 | current.hooks[key].push(hook);
83 | }
84 | };
85 | const useMemo = (cb, deps) => {
86 | const hook = getSlot(cursor++)[0];
87 | if (isChanged(hook[1], deps)) {
88 | hook[1] = deps;
89 | return (hook[0] = cb());
90 | }
91 | return hook[0];
92 | };
93 | const useCallback = (cb, deps) => {
94 | return useMemo(() => cb, deps);
95 | };
96 | const useRef = (current) => {
97 | return useMemo(() => ({ current }), []);
98 | };
99 | const getSlot = (cursor) => {
100 | const current = useFiber();
101 | const hooks = current.hooks || (current.hooks = { list: [], effect: [], layout: [] });
102 | if (cursor >= hooks.list.length) {
103 | hooks.list.push([]);
104 | }
105 | return [hooks.list[cursor], current];
106 | };
107 | const createContext = (initialValue) => {
108 | const contextComponent = ({ value, children }) => {
109 | const valueRef = useRef(value);
110 | const subscribers = useMemo(() => new Set(), EMPTY_ARR);
111 | if (valueRef.current !== value) {
112 | valueRef.current = value;
113 | subscribers.forEach((subscriber) => subscriber());
114 | }
115 | return children;
116 | };
117 | contextComponent.initialValue = initialValue;
118 | return contextComponent;
119 | };
120 | const useContext = (contextType) => {
121 | let subscribersSet;
122 | const triggerUpdate = useReducer(null, null)[1];
123 | useEffect(() => {
124 | return () => subscribersSet && subscribersSet.delete(triggerUpdate);
125 | }, EMPTY_ARR);
126 | let contextFiber = useFiber().parent;
127 | while (contextFiber && contextFiber.type !== contextType) {
128 | contextFiber = contextFiber.parent;
129 | }
130 | if (contextFiber) {
131 | const hooks = contextFiber.hooks.list;
132 | const [[value], [subscribers]] = hooks;
133 | subscribersSet = subscribers.add(triggerUpdate);
134 | return value.current;
135 | }
136 | else {
137 | return contextType.initialValue;
138 | }
139 | };
140 | const isChanged = (a, b) => {
141 | return (!a ||
142 | a.length !== b.length ||
143 | b.some((arg, index) => !Object.is(arg, a[index])));
144 | };
145 |
146 | const queue = [];
147 | const threshold = 5;
148 | const transitions = [];
149 | let deadline = 0;
150 | const startTransition = (cb) => {
151 | transitions.push(cb) && translate();
152 | };
153 | const schedule = (callback) => {
154 | queue.push({ callback });
155 | startTransition(flush);
156 | };
157 | const task = (pending) => {
158 | const cb = () => transitions.splice(0, 1).forEach((c) => c());
159 | if (!pending && typeof queueMicrotask !== 'undefined') {
160 | return () => queueMicrotask(cb);
161 | }
162 | if (typeof MessageChannel !== 'undefined') {
163 | const { port1, port2 } = new MessageChannel();
164 | port1.onmessage = cb;
165 | return () => port2.postMessage(null);
166 | }
167 | return () => setTimeout(cb);
168 | };
169 | let translate = task(false);
170 | const flush = () => {
171 | deadline = getTime() + threshold;
172 | let job = peek(queue);
173 | while (job && !shouldYield()) {
174 | const { callback } = job;
175 | job.callback = null;
176 | const next = callback();
177 | if (next) {
178 | job.callback = next;
179 | }
180 | else {
181 | queue.shift();
182 | }
183 | job = peek(queue);
184 | }
185 | job && (translate = task(shouldYield())) && startTransition(flush);
186 | };
187 | const shouldYield = () => {
188 | return getTime() >= deadline;
189 | };
190 | const getTime = () => performance.now();
191 | const peek = (queue) => queue[0];
192 |
193 | const commit = (fiber) => {
194 | var _a;
195 | if (!fiber) {
196 | return;
197 | }
198 | refer(fiber.ref, fiber.node);
199 | commitSibling(fiber.child);
200 | const { op, ref, cur } = fiber.action || {};
201 | const parent = (_a = fiber === null || fiber === void 0 ? void 0 : fiber.parent) === null || _a === void 0 ? void 0 : _a.node;
202 | if (op & 4 || op & 64) {
203 | parent.insertBefore(cur.node, ref === null || ref === void 0 ? void 0 : ref.node);
204 | }
205 | if (op & 2) {
206 | const node = fiber.node;
207 | updateElement(node, fiber.alternate.props || {}, fiber.props);
208 | }
209 | fiber.action = null;
210 | commitSibling(fiber.sibling);
211 | };
212 | function commitSibling(fiber) {
213 | if (fiber === null || fiber === void 0 ? void 0 : fiber.memo) {
214 | commitSibling(fiber.sibling);
215 | }
216 | else {
217 | commit(fiber);
218 | }
219 | }
220 | const refer = (ref, dom) => {
221 | if (ref)
222 | isFn(ref) ? ref(dom) : (ref.current = dom);
223 | };
224 | const kidsRefer = (kids) => {
225 | kids.forEach((kid) => {
226 | kid.kids && kidsRefer(kid.kids);
227 | refer(kid.ref, null);
228 | });
229 | };
230 | const removeElement = (fiber, flag = true) => {
231 | if (fiber.isComp) {
232 | fiber.hooks && fiber.hooks.list.forEach((e) => e[2] && e[2]());
233 | }
234 | else {
235 | if (flag) {
236 | fiber.node.parentNode.removeChild(fiber.node);
237 | flag = false;
238 | }
239 | kidsRefer(fiber.kids);
240 | refer(fiber.ref, null);
241 | }
242 | fiber.kids.forEach(v => removeElement(v, flag));
243 | };
244 |
245 | let currentFiber = null;
246 | let rootFiber = null;
247 | const render = (vnode, node) => {
248 | rootFiber = {
249 | node,
250 | props: { children: vnode },
251 | };
252 | update(rootFiber);
253 | };
254 | const update = (fiber) => {
255 | if (!fiber.dirty) {
256 | fiber.dirty = true;
257 | schedule(() => reconcile(fiber));
258 | }
259 | };
260 | const reconcile = (fiber) => {
261 | while (fiber && !shouldYield())
262 | fiber = capture(fiber);
263 | if (fiber)
264 | return reconcile.bind(null, fiber);
265 | return null;
266 | };
267 | const capture = (fiber) => {
268 | fiber.isComp = isFn(fiber.type);
269 | if (fiber.isComp) {
270 | if (isMemo(fiber)) {
271 | fiber.memo = true;
272 | return getSibling(fiber);
273 | }
274 | else if (fiber.memo) {
275 | fiber.memo = false;
276 | }
277 | updateHook(fiber);
278 | }
279 | else {
280 | updateHost(fiber);
281 | }
282 | if (fiber.child)
283 | return fiber.child;
284 | const sibling = getSibling(fiber);
285 | return sibling;
286 | };
287 | const isMemo = (fiber) => {
288 | var _a, _b;
289 | if (fiber.type.memo &&
290 | fiber.type === ((_a = fiber.alternate) === null || _a === void 0 ? void 0 : _a.type) &&
291 | ((_b = fiber.alternate) === null || _b === void 0 ? void 0 : _b.props)) {
292 | let scu = fiber.type.shouldUpdate || shouldUpdate;
293 | if (!scu(fiber.props, fiber.alternate.props)) {
294 | return true;
295 | }
296 | }
297 | return false;
298 | };
299 | const getSibling = (fiber) => {
300 | while (fiber) {
301 | bubble(fiber);
302 | if (fiber.dirty) {
303 | fiber.dirty = false;
304 | commit(fiber);
305 | return null;
306 | }
307 | if (fiber.sibling)
308 | return fiber.sibling;
309 | fiber = fiber.parent;
310 | }
311 | return null;
312 | };
313 | const bubble = (fiber) => {
314 | if (fiber.isComp) {
315 | if (fiber.hooks) {
316 | side(fiber.hooks.layout);
317 | schedule(() => side(fiber.hooks.effect));
318 | }
319 | }
320 | };
321 | const shouldUpdate = (a, b) => {
322 | for (let i in a)
323 | if (!(i in b))
324 | return true;
325 | for (let i in b)
326 | if (a[i] !== b[i])
327 | return true;
328 | };
329 | const fragment = (fiber) => {
330 | const f = document.createDocumentFragment(fiber.type);
331 | return f;
332 | };
333 | const updateHook = (fiber) => {
334 | resetCursor();
335 | currentFiber = fiber;
336 | fiber.node = fragment(fiber);
337 | let children = fiber.type(fiber.props);
338 | reconcileChidren(fiber, simpleVnode(children));
339 | };
340 | const updateHost = (fiber) => {
341 | if (!fiber.node) {
342 | if (fiber.type === 'svg')
343 | fiber.lane |= 16;
344 | fiber.node = createElement(fiber);
345 | }
346 | reconcileChidren(fiber, fiber.props.children);
347 | };
348 | const simpleVnode = (type) => isStr(type) ? createText(type) : type;
349 | const reconcileChidren = (fiber, children) => {
350 | let aCh = fiber.kids || [], bCh = (fiber.kids = arrayfy$1(children));
351 | const actions = diff(aCh, bCh);
352 | for (let i = 0, prev = null, len = bCh.length; i < len; i++) {
353 | const child = bCh[i];
354 | child.action = actions[i];
355 | if (fiber.lane & 16) {
356 | child.lane |= 16;
357 | }
358 | child.parent = fiber;
359 | if (i > 0) {
360 | prev.sibling = child;
361 | }
362 | else {
363 | fiber.child = child;
364 | }
365 | prev = child;
366 | }
367 | };
368 | function clone(a, b) {
369 | b.hooks = a.hooks;
370 | b.ref = a.ref;
371 | b.node = a.node;
372 | b.kids = a.kids;
373 | b.alternate = a;
374 | }
375 | const arrayfy$1 = (arr) => !arr ? [] : isArr(arr) ? arr : [arr];
376 | const side = (effects) => {
377 | effects.forEach((e) => e[2] && e[2]());
378 | effects.forEach((e) => (e[2] = e[0]()));
379 | effects.length = 0;
380 | };
381 | const diff = (aCh, bCh) => {
382 | let aHead = 0, bHead = 0, aTail = aCh.length - 1, bTail = bCh.length - 1, aMap = {}, bMap = {}, same = (a, b) => {
383 | return a.type === b.type && a.key === b.key;
384 | }, temp = [], actions = [];
385 | while (aHead <= aTail && bHead <= bTail) {
386 | if (!same(aCh[aTail], bCh[bTail]))
387 | break;
388 | clone(aCh[aTail], bCh[bTail]);
389 | temp.push({ op: 2 });
390 | aTail--;
391 | bTail--;
392 | }
393 | while (aHead <= aTail && bHead <= bTail) {
394 | if (!same(aCh[aHead], bCh[bHead]))
395 | break;
396 | clone(aCh[aHead], bCh[bHead]);
397 | actions.push({ op: 2 });
398 | aHead++;
399 | bHead++;
400 | }
401 | for (let i = aHead; i <= aTail; i++) {
402 | if (aCh[i].key)
403 | aMap[aCh[i].key] = i;
404 | }
405 | for (let i = bHead; i <= bTail; i++) {
406 | if (bCh[i].key)
407 | bMap[bCh[i].key] = i;
408 | }
409 | while (aHead <= aTail || bHead <= bTail) {
410 | var aElm = aCh[aHead], bElm = bCh[bHead];
411 | if (aElm === null) {
412 | aHead++;
413 | }
414 | else if (bTail + 1 <= bHead) {
415 | removeElement(aElm);
416 | aHead++;
417 | }
418 | else if (aTail + 1 <= aHead) {
419 | actions.push({ op: 4, cur: bElm, ref: aElm });
420 | bHead++;
421 | }
422 | else if (same(aElm, bElm)) {
423 | clone(aElm, bElm);
424 | actions.push({ op: 2 });
425 | aHead++;
426 | bHead++;
427 | }
428 | else {
429 | var foundB = bMap[aElm.key];
430 | var foundA = aMap[bElm.key];
431 | if (foundB == null) {
432 | removeElement(aElm);
433 | aHead++;
434 | }
435 | else if (foundA == null) {
436 | actions.push({ op: 4, cur: bElm, ref: aElm });
437 | bHead++;
438 | }
439 | else {
440 | clone(aCh[foundA], bElm);
441 | actions.push({ op: 64, cur: aCh[foundA], ref: aElm });
442 | aCh[foundA] = null;
443 | bHead++;
444 | }
445 | }
446 | }
447 | for (let i = temp.length - 1; i >= 0; i--) {
448 | actions.push(temp[i]);
449 | }
450 | return actions;
451 | };
452 | const useFiber = () => currentFiber || null;
453 | const isFn = (x) => typeof x === 'function';
454 | const isStr = (s) => typeof s === 'number' || typeof s === 'string';
455 |
456 | const h = (type, props, ...kids) => {
457 | props = props || {};
458 | kids = flat(arrayfy(props.children || kids));
459 | if (kids.length)
460 | props.children = kids.length === 1 ? kids[0] : kids;
461 | const key = props.key || null;
462 | const ref = props.ref || null;
463 | if (key)
464 | props.key = undefined;
465 | if (ref)
466 | props.ref = undefined;
467 | return createVnode(type, props, key, ref);
468 | };
469 | const arrayfy = (arr) => !arr ? [] : isArr(arr) ? arr : [arr];
470 | const some = (x) => x != null && x !== true && x !== false;
471 | const flat = (arr, target = []) => {
472 | arr.forEach((v) => {
473 | isArr(v)
474 | ? flat(v, target)
475 | : some(v) && target.push(isStr(v) ? createText(v) : v);
476 | });
477 | return target;
478 | };
479 | const createVnode = (type, props, key, ref) => ({
480 | type,
481 | props,
482 | key,
483 | ref,
484 | });
485 | const createText = (vnode) => ({ type: '#text', props: { nodeValue: vnode + '' } });
486 | function Fragment(props) {
487 | return props.children;
488 | }
489 | function memo(fn, compare) {
490 | fn.memo = true;
491 | fn.shouldUpdate = compare;
492 | return fn;
493 | }
494 | const isArr = Array.isArray;
495 |
496 | export { Fragment, createContext, h as createElement, h, memo, render, shouldYield, schedule as startTranstion, useCallback, useContext, useEffect, useLayout, useLayout as useLayoutEffect, useMemo, useReducer, useRef, useState };
497 | //# sourceMappingURL=fre.esm.js.map
498 |
--------------------------------------------------------------------------------