├── 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 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 += `` 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 | --------------------------------------------------------------------------------