diff --git a/.gitignore b/.gitignore index 8636e699..d8b9e23e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ -/node_modules +node_modules .idea .vscode package-lock.json pnpm-lock.yaml -/packages/**/node_modules /packages/inula-cli/lib build /packages/inula-router/connectRouter /packages/inula-router/router +dist diff --git a/demos/v2/index.html b/demos/v2/index.html new file mode 100644 index 00000000..28fb9622 --- /dev/null +++ b/demos/v2/index.html @@ -0,0 +1,12 @@ + + + + + Dlight.JS + + + +
+ + + diff --git a/demos/v2/package.json b/demos/v2/package.json new file mode 100644 index 00000000..63425f3a --- /dev/null +++ b/demos/v2/package.json @@ -0,0 +1,25 @@ +{ + "name": "dev", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@babel/standalone": "^7.22.4", + "@inula/next": "workspace:*", + "@iandx/easy-css": "^0.10.14", + "babel-preset-inula-next": "workspace:*" + }, + "devDependencies": { + "typescript": "^5.2.2", + "vite": "^4.4.9", + "vite-plugin-inula-next": "workspace:*" + }, + "keywords": [ + "dlight.js" + ] +} diff --git a/demos/v2/src/App.css b/demos/v2/src/App.css new file mode 100644 index 00000000..73c673f8 --- /dev/null +++ b/demos/v2/src/App.css @@ -0,0 +1,3 @@ +.ok { + color: var(--color-ok); +} \ No newline at end of file diff --git a/demos/v2/src/App.view.tsx b/demos/v2/src/App.view.tsx new file mode 100644 index 00000000..0e47914b --- /dev/null +++ b/demos/v2/src/App.view.tsx @@ -0,0 +1,77 @@ +// @ts-nocheck +import { + Children, + Content, + Main, + Model, + Prop, + View, + Watch, + button, + div, + input, + insertChildren, + use, + render, +} from '@inula/next'; + +// @ts-ignore +function Button({ children, onClick }) { + return ( + + ); +} + +function ArrayModification() { + let arr = []; + willMount(() => {}); + return ( +
+

ArrayModification

+ {arr.join(',')} + +
+ ); +} + +function MyComp() { + let count = 0; + let db = count * 2; + return ( + <> +

Hello dlight fn comp

+
+ count: {count}, double is: {db} + +
+ + + + + ); +} + +function ConditionalRendering({ count }) { + return ( +
+

Condition

+ 1}>{count} is bigger than is 1 + {count} is equal to 1 + {count} is smaller than 1 +
+ ); +} + +render('main', MyComp); diff --git a/demos/v2/tsconfig.json b/demos/v2/tsconfig.json new file mode 100644 index 00000000..0f30d84c --- /dev/null +++ b/demos/v2/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "jsx": "preserve", + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "isolatedModules": true, + "esModuleInterop": true, + "noEmit": true, + "noUnusedParameters": true, + "skipLibCheck": true, + "experimentalDecorators": true + }, + "ts-node": { + "esm": true + } +} diff --git a/demos/v2/vite.config.ts b/demos/v2/vite.config.ts new file mode 100644 index 00000000..7029d548 --- /dev/null +++ b/demos/v2/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite'; +import inula from 'vite-plugin-inula-next'; + +export default defineConfig({ + server: { + port: 4320, + }, + base: '', + optimizeDeps: { + disabled: true, + }, + plugins: [inula({ files: '**/*.{view,model}.{ts,js,tsx,jsx}', enableDevTools: true })], +}); diff --git a/package.json b/package.json index d5f9497e..4eaba18d 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "build:inula-intl": "pnpm -F inula-intl build", "build:inula-request": "pnpm -F inula-request build", "build:inula-router": "pnpm -F inula-router build", + "build:transpiler": "pnpm --filter './packages/transpiler/*' run build", "commitlint": "commitlint --config commitlint.config.js -e", "postinstall": "husky install" }, diff --git a/packages/inula-cli/README.md b/packages/inula-cli/README.md index bebd176e..e60a1cd2 100644 --- a/packages/inula-cli/README.md +++ b/packages/inula-cli/README.md @@ -54,7 +54,7 @@ inula-cli的推荐目录结构如下: │ └── inula-cli │ ├── lib ├── mock // mock目录 -│ └── mock.ts +│ └── transform.ts ├── src // 项目源码目录 │ ├── pages │ │ ├── index.less diff --git a/packages/inula-cli/externals.d.ts b/packages/inula-cli/externals.d.ts index 2fe4981b..ab03cf87 100644 --- a/packages/inula-cli/externals.d.ts +++ b/packages/inula-cli/externals.d.ts @@ -14,4 +14,3 @@ */ declare module 'crequire'; - diff --git a/packages/inula-cli/src/builtInPlugins/command/dev/buildDev.ts b/packages/inula-cli/src/builtInPlugins/command/dev/buildDev.ts index 400fbe53..9ffccf76 100644 --- a/packages/inula-cli/src/builtInPlugins/command/dev/buildDev.ts +++ b/packages/inula-cli/src/builtInPlugins/command/dev/buildDev.ts @@ -57,7 +57,7 @@ export default (api: API) => { api.applyHook({ name: 'afterStartDevServer' }); }); } else { - api.logger.error('Can\'t find config'); + api.logger.error("Can't find config"); } break; case 'vite': @@ -70,7 +70,7 @@ export default (api: API) => { server.printUrls(); }); } else { - api.logger.error('Can\'t find config'); + api.logger.error("Can't find config"); } break; default: diff --git a/packages/inula-cli/src/builtInPlugins/command/generate/generate.ts b/packages/inula-cli/src/builtInPlugins/command/generate/generate.ts index 777aefe8..e71f4f96 100644 --- a/packages/inula-cli/src/builtInPlugins/command/generate/generate.ts +++ b/packages/inula-cli/src/builtInPlugins/command/generate/generate.ts @@ -33,7 +33,7 @@ export default (api: API) => { args._.shift(); } if (args._.length === 0) { - api.logger.warn('Can\'t find any generate options.'); + api.logger.warn("Can't find any generate options."); return; } diff --git a/packages/inula-intl/babel.config.js b/packages/inula-intl/babel.config.js index 7f94d715..90df52a7 100644 --- a/packages/inula-intl/babel.config.js +++ b/packages/inula-intl/babel.config.js @@ -16,10 +16,7 @@ const { preset } = require('./jest.config'); module.exports = { presets: [ - [ - '@babel/preset-env', - { targets: { node: 'current' } }, - ], + ['@babel/preset-env', { targets: { node: 'current' } }], ['@babel/preset-typescript'], [ '@babel/preset-react', diff --git a/packages/inula-intl/rollup.config.js b/packages/inula-intl/rollup.config.js index 86cd6fb2..e0ed9912 100644 --- a/packages/inula-intl/rollup.config.js +++ b/packages/inula-intl/rollup.config.js @@ -40,7 +40,7 @@ export default { { file: path.resolve(output, 'intl.esm-browser.js'), format: 'esm', - } + }, ], plugins: [ nodeResolve({ diff --git a/packages/inula-intl/src/parser/mappingRule.ts b/packages/inula-intl/src/parser/mappingRule.ts index 1dff32ec..54bee482 100644 --- a/packages/inula-intl/src/parser/mappingRule.ts +++ b/packages/inula-intl/src/parser/mappingRule.ts @@ -14,11 +14,11 @@ */ const body: Record = { - doubleapos: { match: '\'\'', value: () => '\'' }, + doubleapos: { match: "''", value: () => "'" }, quoted: { lineBreaks: true, match: /'[{}#](?:[^]*?[^'])?'(?!')/u, - value: src => src.slice(1, -1).replace(/''/g, '\''), + value: src => src.slice(1, -1).replace(/''/g, "'"), }, argument: { lineBreaks: true, diff --git a/packages/inula-intl/tests/core/I18n.test.ts b/packages/inula-intl/tests/core/I18n.test.ts index 77079664..e33c7e82 100644 --- a/packages/inula-intl/tests/core/I18n.test.ts +++ b/packages/inula-intl/tests/core/I18n.test.ts @@ -90,19 +90,19 @@ describe('I18n', () => { }); it('._ allow escaping syntax characters', () => { const messages = { - 'My \'\'name\'\' is \'{name}\'': 'Mi \'\'nombre\'\' es \'{name}\'', + "My ''name'' is '{name}'": "Mi ''nombre'' es '{name}'", }; const i18n = new I18n({ locale: 'es', messages: { es: messages }, }); - expect(i18n.formatMessage('My \'\'name\'\' is \'{name}\'')).toEqual('Mi \'nombre\' es {name}'); + expect(i18n.formatMessage("My ''name'' is '{name}'")).toEqual("Mi 'nombre' es {name}"); }); it('._ should format message from catalog', function () { const messages = { Hello: 'Salut', - id: 'Je m\'appelle {name}', + id: "Je m'appelle {name}", }; const i18n = new I18n({ locale: 'fr', @@ -110,7 +110,7 @@ describe('I18n', () => { }); expect(i18n.locale).toEqual('fr'); expect(i18n.formatMessage('Hello')).toEqual('Salut'); - expect(i18n.formatMessage('id', { name: 'Fred' })).toEqual('Je m\'appelle Fred'); + expect(i18n.formatMessage('id', { name: 'Fred' })).toEqual("Je m'appelle Fred"); }); it('should return the formatted date and time', () => { diff --git a/packages/inula-intl/tests/utils/eventListener.test.ts b/packages/inula-intl/tests/utils/eventListener.test.ts index 17e0f43b..c72af6cf 100644 --- a/packages/inula-intl/tests/utils/eventListener.test.ts +++ b/packages/inula-intl/tests/utils/eventListener.test.ts @@ -43,7 +43,7 @@ describe('eventEmitter', () => { expect(listener).not.toBeCalled(); }); - it('should do nothing when even doesn\'t exist', () => { + it("should do nothing when even doesn't exist", () => { const unknown = jest.fn(); const emitter = new EventEmitter(); diff --git a/packages/inula-request/babel.config.js b/packages/inula-request/babel.config.js index 383563ed..f373490c 100644 --- a/packages/inula-request/babel.config.js +++ b/packages/inula-request/babel.config.js @@ -14,11 +14,5 @@ */ module.exports = { - presets: [ - [ - '@babel/preset-env', - { targets: { node: 'current' }}, - ], - ['@babel/preset-typescript'], - ], + presets: [['@babel/preset-env', { targets: { node: 'current' } }], ['@babel/preset-typescript']], }; diff --git a/packages/inula-request/rollup.config.js b/packages/inula-request/rollup.config.js index e7e32556..b1ad23b7 100644 --- a/packages/inula-request/rollup.config.js +++ b/packages/inula-request/rollup.config.js @@ -21,16 +21,19 @@ import { babel } from '@rollup/plugin-babel'; export default { input: './index.ts', - output: [{ - file: 'dist/inulaRequest.js', - format: 'umd', - exports: 'named', - name: 'inulaRequest', - sourcemap: false, - }, { - file: 'dist/inulaRequest.esm-browser.js', - format: 'esm', - }], + output: [ + { + file: 'dist/inulaRequest.js', + format: 'umd', + exports: 'named', + name: 'inulaRequest', + sourcemap: false, + }, + { + file: 'dist/inulaRequest.esm-browser.js', + format: 'esm', + }, + ], plugins: [ resolve(), commonjs(), diff --git a/packages/inula-router/src/router/matcher/__tests__/lexer.test.ts b/packages/inula-router/src/router/matcher/__tests__/lexer.test.ts index f6741216..f5a3725e 100644 --- a/packages/inula-router/src/router/matcher/__tests__/lexer.test.ts +++ b/packages/inula-router/src/router/matcher/__tests__/lexer.test.ts @@ -38,7 +38,7 @@ describe('path lexer Test', () => { expect(tokens).toStrictEqual([{ type: 'delimiter', value: '/' }]); }); - it('don\'t start with a slash', () => { + it("don't start with a slash", () => { const func = () => lexer('abc.com'); expect(func).toThrow(Error('Url must start with "/".')); }); diff --git a/packages/inula-v2-store/package.json b/packages/inula-v2-store/package.json new file mode 100644 index 00000000..cf40cc8e --- /dev/null +++ b/packages/inula-v2-store/package.json @@ -0,0 +1,38 @@ +{ + "name": "@inula/store", + "version": "0.0.0", + "description": "DLight shared store", + "author": { + "name": "IanDx", + "email": "iandxssxx@gmail.com" + }, + "keywords": [ + "dlight.js" + ], + "license": "MIT", + "files": [ + "dist" + ], + "type": "module", + "main": "dist/index.js", + "module": "dist/index.js", + "typings": "dist/index.d.ts", + "scripts": { + "build": "tsup --sourcemap" + }, + "devDependencies": { + "tsup": "^6.5.0", + "typescript": "^5.3.2" + }, + "tsup": { + "entry": [ + "src/index.ts" + ], + "format": [ + "esm" + ], + "clean": true, + "dts": true, + "minify": true + } +} diff --git a/packages/inula-v2-store/src/index.ts b/packages/inula-v2-store/src/index.ts new file mode 100644 index 00000000..7dc7fbfd --- /dev/null +++ b/packages/inula-v2-store/src/index.ts @@ -0,0 +1 @@ +export const Store = {}; diff --git a/packages/inula-v2-store/tsconfig.json b/packages/inula-v2-store/tsconfig.json new file mode 100644 index 00000000..ad175791 --- /dev/null +++ b/packages/inula-v2-store/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "esModuleInterop": true + }, + "ts-node": { + "esm": true + } +} + diff --git a/packages/inula-v2/README.md b/packages/inula-v2/README.md new file mode 100644 index 00000000..6f89397b --- /dev/null +++ b/packages/inula-v2/README.md @@ -0,0 +1,2 @@ +# DLight Main Package +See the website's documentations for usage. diff --git a/packages/inula-v2/package.json b/packages/inula-v2/package.json new file mode 100644 index 00000000..9136e106 --- /dev/null +++ b/packages/inula-v2/package.json @@ -0,0 +1,41 @@ +{ + "name": "@inula/next", + "version": "1.0.0-next.9", + "author": { + "name": "IanDx", + "email": "iandxssxx@gmail.com" + }, + "keywords": [ + "dlight.js" + ], + "license": "MIT", + "files": [ + "dist", + "README.md" + ], + "type": "module", + "main": "dist/index.cjs", + "module": "dist/index.js", + "typings": "dist/index.d.ts", + "scripts": { + "build": "tsup --sourcemap && cp src/index.d.ts dist/ && cp -r src/types dist/" + }, + "dependencies": { + "csstype": "^3.1.3", + "@inula/store": "workspace:*" + }, + "devDependencies": { + "tsup": "^6.5.0" + }, + "tsup": { + "entry": [ + "src/index.js" + ], + "format": [ + "cjs", + "esm" + ], + "clean": true, + "minify": true + } +} diff --git a/packages/inula-v2/src/CompNode.js b/packages/inula-v2/src/CompNode.js new file mode 100644 index 00000000..a56e56bc --- /dev/null +++ b/packages/inula-v2/src/CompNode.js @@ -0,0 +1,367 @@ +import { DLNode, DLNodeType } from './DLNode'; +import { forwardHTMLProp } from './HTMLNode'; +import { DLStore, cached } from './store'; + +export class CompNode extends DLNode { + /** + * @brief Constructor, Comp type + * @internal + * * key - private property key + * * $$key - dependency number, e.g. 0b1, 0b10, 0b100 + * * $s$key - set of properties that depend on this property + * * $p$key - exist if this property is a prop + * * $e$key - exist if this property is an env + * * $en$key - exist if this property is an env, and it's the innermost env that contains this env + * * $w$key - exist if this property is a watcher + * * $f$key - a function that returns the value of this property, called when the property's dependencies change + * * _$children - children nodes of type PropView + * * _$contentKey - the key key of the content prop + * * _$forwardProps - exist if this node is forwarding props + * * _$forwardPropsId - the keys of the props that this node is forwarding, collected in _$setForwardProp + * * _$forwardPropsSet - contain all the nodes that are forwarding props to this node, collected with _$addForwardProps + */ + constructor() { + super(DLNodeType.Comp); + } + + /** + * @brief Init function, called explicitly in the subclass's constructor + * @param props - Object containing properties + * @param content - Content to be used + * @param children - Child nodes + * @param forwardPropsScope - Scope for forwarding properties + */ + _$init(props, content, children, forwardPropsScope) { + this._$notInitd = true; + + // ---- Forward props first to allow internal props to override forwarded props + if (forwardPropsScope) forwardPropsScope._$addForwardProps(this); + if (content) this._$setContent(() => content[0], content[1]); + if (props) + props.forEach(([key, value, deps]) => { + if (key === 'props') return this._$setProps(() => value, deps); + this._$setProp(key, () => value, deps); + }); + if (children) this._$children = children; + + // ---- Add envs + DLStore.global.DLEnvStore && + Object.entries(DLStore.global.DLEnvStore.envs).forEach(([key, [value, envNode]]) => { + if (key === '_$catchable') { + this._$catchable = value; + return; + } + if (!(`$e$${key}` in this)) return; + envNode.addNode(this); + this._$initEnv(key, value, envNode); + }); + + const willCall = () => { + this._$callUpdatesBeforeInit(); + this.didMount && DLNode.addDidMount(this, this.didMount.bind(this)); + this.willUnmount && DLNode.addWillUnmount(this, this.willUnmount.bind(this)); + DLNode.addDidUnmount(this, this._$setUnmounted.bind(this)); + this.didUnmount && DLNode.addDidUnmount(this, this.didUnmount.bind(this)); + this.willMount?.(); + this._$nodes = this.Body?.() ?? []; + }; + + if (this._$catchable) { + this._$catchable(willCall)(); + if (this._$update) this._$update = this._$catchable(this._$update.bind(this)); + this._$updateDerived = this._$catchable(this._$updateDerived.bind(this)); + delete this._$catchable; + } else { + willCall(); + } + } + + _$setUnmounted() { + this._$unmounted = true; + } + + /** + * @brief Call updates manually before the node is mounted + */ + _$callUpdatesBeforeInit() { + const protoProps = Object.getOwnPropertyNames(Object.getPrototypeOf(this)); + const ownProps = Object.getOwnPropertyNames(this); + const allProps = [...protoProps, ...ownProps]; + allProps.forEach(key => { + // ---- Run watcher + if (key.startsWith('$w$')) return this[key.slice(3)](); + // ---- Run model update + if (key.startsWith('$md$')) { + const realKey = key.slice(4); + this[realKey] = this[realKey](); + return; + } + // ---- Run derived value + if (key.startsWith('$f$')) { + const realKey = key.slice(3); + this[realKey] = this[key]; + this._$updateDerived(realKey); + } + }); + delete this._$notInitd; + } + + /** + * @brief Set all the props to forward + * @param key + * @param value + * @param deps + */ + _$setPropToForward(key, value, deps) { + this._$forwardPropsSet.forEach(node => { + const isContent = key === '_$content'; + if (node._$dlNodeType === DLNodeType.Comp) { + if (isContent) node._$setContent(() => value, deps); + else node._$setProp(key, () => value, deps); + return; + } + if (node instanceof HTMLElement) { + if (isContent) key = 'textContent'; + forwardHTMLProp(node, key, () => value, deps); + } + }); + } + + /** + * @brief Define forward props + * @param key + * @param value + */ + _$setForwardProp(key, valueFunc, deps) { + const notInitd = '_$notInitd' in this; + if (!notInitd && this._$cache(key, deps)) return; + const value = valueFunc(); + if (key === '_$content' && this._$contentKey) { + this[this._$contentKey] = value; + this._$updateDerived(this._$contentKey); + } + this[key] = value; + this._$updateDerived(key); + if (notInitd) this._$forwardPropsId.push(key); + else this._$setPropToForward(key, value, deps); + } + + /** + * @brief Add a node to the set of nodes that are forwarding props to this node and init these props + * @param node + */ + _$addForwardProps(node) { + this._$forwardPropsSet.add(node); + this._$forwardPropsId.forEach(key => { + this._$setPropToForward(key, this[key], []); + }); + DLNode.addWillUnmount(node, this._$forwardPropsSet.delete.bind(this._$forwardPropsSet, node)); + } + + /** + * @brief Cache the deps and return true if the deps are the same as the previous deps + * @param key + * @param deps + * @returns + */ + _$cache(key, deps) { + if (!deps || !deps.length) return false; + const cacheKey = `$cc$${key}`; + if (cached(deps, this[cacheKey])) return true; + this[cacheKey] = deps; + return false; + } + + /** + * @brief Set the content prop, the key is stored in _$contentKey + * @param value + */ + _$setContent(valueFunc, deps) { + if ('_$forwardProps' in this) return this._$setForwardProp('_$content', valueFunc, deps); + const contentKey = this._$contentKey; + if (!contentKey) return; + if (this._$cache(contentKey, deps)) return; + this[contentKey] = valueFunc(); + this._$updateDerived(contentKey); + } + + /** + * @brief Set a prop directly, if this is a forwarded prop, go and init forwarded props + * @param key + * @param value + * @param deps + */ + _$setProp(key, valueFunc, deps) { + if ('_$forwardProps' in this) return this._$setForwardProp(key, valueFunc, deps); + if (!(`$p$${key}` in this)) { + console.warn(`[${key}] is not a prop in ${this.constructor.name}`); + return; + } + if (this._$cache(key, deps)) return; + this[key] = valueFunc(); + this._$updateDerived(key); + } + + _$setProps(valueFunc, deps) { + if (this._$cache('props', deps)) return; + const props = valueFunc(); + if (!props) return; + Object.entries(props).forEach(([key, value]) => { + this._$setProp(key, () => value, []); + }); + } + + /** + * @brief Init an env, put the corresponding innermost envNode in $en$key + * @param key + * @param value + * @param envNode + */ + _$initEnv(key, value, envNode) { + this[key] = value; + this[`$en$${key}`] = envNode; + } + + // ---- Update functions + /** + * @brief Update an env, called in EnvNode._$update + * @param key + * @param value + * @param envNode + */ + _$updateEnv(key, value, envNode) { + if (!(`$e$${key}` in this)) return; + if (envNode !== this[`$en$${key}`]) return; + this[key] = value; + this._$updateDerived(key); + } + + /** + * @brief Update a prop + */ + _$ud(exp, key) { + this._$updateDerived(key); + return exp; + } + + /** + * @brief Update properties that depend on this property + * @param key + */ + _$updateDerived(key) { + if ('_$notInitd' in this) return; + + this[`$s$${key}`]?.forEach(k => { + if (`$w$${k}` in this) { + // ---- Watcher + this[k](key); + } else if (`$md$${k}` in this) { + this[k]._$update(); + } else { + // ---- Regular derived value + this[k] = this[`$f$${k}`]; + } + }); + + // ---- "trigger-view" + this._$updateView(key); + } + + _$updateView(key) { + if (this._$modelCallee) return this._$updateModelCallee(); + if (!('_$update' in this)) return; + const depNum = this[`$$${key}`]; + if (!depNum) return; + // ---- Collect all depNums that need to be updated + if ('_$depNumsToUpdate' in this) { + this._$depNumsToUpdate.push(depNum); + } else { + this._$depNumsToUpdate = [depNum]; + // ---- Update in the next microtask + Promise.resolve().then(() => { + // ---- Abort if unmounted + if (this._$unmounted) return; + const depNums = this._$depNumsToUpdate; + if (depNums.length > 0) { + const depNum = depNums.reduce((acc, cur) => acc | cur, 0); + this._$update(depNum); + } + delete this._$depNumsToUpdate; + }); + } + } + + _$updateModelCallee() { + if ('_$depNumsToUpdate' in this) return; + this._$depNumsToUpdate = true; + // ---- Update in the next microtask + Promise.resolve().then(() => { + // ---- Abort if unmounted + if (this._$unmounted) return; + this._$modelCallee._$updateDerived(this._$modelKey); + delete this._$depNumsToUpdate; + }); + } + /** + * @brief Update all props and content of the model + */ + static _$updateModel(model, propsFunc, contentFunc) { + // ---- Suppress update because top level update will be performed + // directly by the state variable in the model callee, which will + // trigger the update of the model + const props = propsFunc() ?? {}; + const collectedProps = props.s ?? []; + props.m?.forEach(([props, deps]) => { + Object.entries(props).forEach(([key, value]) => { + collectedProps.push([key, value, deps]); + }); + }); + collectedProps.forEach(([key, value, deps]) => { + model._$setProp(key, () => value, deps); + }); + const content = contentFunc(); + if (content) model._$setContent(() => content[0], content[1]); + } + + static _$releaseModel() { + delete this._$modelCallee; + } + + /** + * @brief Inject Dlight model in to a property + * @param ModelCls + * @param props { m: [props, deps], s: [key, value, deps] } + * @param content + * @param key + * @returns + */ + _$injectModel(ModelCls, propsFunc, contentFunc, key) { + const props = propsFunc() ?? {}; + const collectedProps = props.s ?? []; + props.m?.forEach(([props, deps]) => { + Object.entries(props).forEach(([key, value]) => { + collectedProps.push([key, value, deps]); + }); + }); + const model = new ModelCls(); + model._$init(collectedProps, contentFunc(), null, null); + model._$modelCallee = this; + model._$modelKey = key; + model._$update = CompNode._$updateModel.bind(null, model, propsFunc, contentFunc); + + return model; + } +} + +// ---- @View -> class Comp extends View +export const View = CompNode; +export const Model = CompNode; + +/** + * @brief Run all update functions given the key + * @param dlNode + * @param key + */ +export function update(dlNode, key) { + dlNode._$updateDerived(key); +} diff --git a/packages/inula-v2/src/DLNode.js b/packages/inula-v2/src/DLNode.js new file mode 100644 index 00000000..9fafd54b --- /dev/null +++ b/packages/inula-v2/src/DLNode.js @@ -0,0 +1,209 @@ +import { DLStore } from './store'; + +export const DLNodeType = { + Comp: 0, + For: 1, + Cond: 2, + Env: 3, + Exp: 4, + Snippet: 5, + Try: 6, +}; + +export class DLNode { + /** + * @brief Node type: HTML, Text, Custom, For, If, Env, Expression + */ + _$dlNodeType; + + /** + * @brief Constructor + * @param nodeType + */ + constructor(nodeType) { + this._$dlNodeType = nodeType; + } + + /** + * @brief Node element + * Either one real element for HTMLNode and TextNode + * Or an array of DLNode for CustomNode, ForNode, IfNode, EnvNode, ExpNode + */ + get _$el() { + return DLNode.toEls(this._$nodes); + } + + /** + * @brief Loop all child DLNodes to get all the child elements + * @param nodes + * @returns HTMLElement[] + */ + static toEls(nodes) { + const els = []; + this.loopShallowEls(nodes, el => { + els.push(el); + }); + return els; + } + + // ---- Loop nodes ---- + /** + * @brief Loop all elements shallowly, + * i.e., don't loop the child nodes of dom elements and only call runFunc on dom elements + * @param nodes + * @param runFunc + */ + static loopShallowEls(nodes, runFunc) { + const stack = [...nodes].reverse(); + while (stack.length > 0) { + const node = stack.pop(); + if (!('_$dlNodeType' in node)) runFunc(node); + else node._$nodes && stack.push(...[...node._$nodes].reverse()); + } + } + + /** + * @brief Add parentEl to all nodes until the first element + * @param nodes + * @param parentEl + */ + static addParentEl(nodes, parentEl) { + nodes.forEach(node => { + if ('_$dlNodeType' in node) { + node._$parentEl = parentEl; + node._$nodes && DLNode.addParentEl(node._$nodes, parentEl); + } + }); + } + + // ---- Flow index and add child elements ---- + /** + * @brief Get the total count of dom elements before the stop node + * @param nodes + * @param stopNode + * @returns total count of dom elements + */ + static getFlowIndexFromNodes(nodes, stopNode) { + let index = 0; + const stack = [...nodes].reverse(); + while (stack.length > 0) { + const node = stack.pop(); + if (node === stopNode) break; + if ('_$dlNodeType' in node) { + node._$nodes && stack.push(...[...node._$nodes].reverse()); + } else { + index++; + } + } + return index; + } + + /** + * @brief Given an array of nodes, append them to the parentEl + * 1. If nextSibling is provided, insert the nodes before the nextSibling + * 2. If nextSibling is not provided, append the nodes to the parentEl + * @param nodes + * @param parentEl + * @param nextSibling + * @returns Added element count + */ + static appendNodesWithSibling(nodes, parentEl, nextSibling) { + if (nextSibling) return this.insertNodesBefore(nodes, parentEl, nextSibling); + return this.appendNodes(nodes, parentEl); + } + + /** + * @brief Given an array of nodes, append them to the parentEl using the index + * 1. If the index is the same as the length of the parentEl.childNodes, append the nodes to the parentEl + * 2. If the index is not the same as the length of the parentEl.childNodes, insert the nodes before the node at the index + * @param nodes + * @param parentEl + * @param index + * @param length + * @returns Added element count + */ + static appendNodesWithIndex(nodes, parentEl, index, length) { + length = length ?? parentEl.childNodes.length; + if (length !== index) return this.insertNodesBefore(nodes, parentEl, parentEl.childNodes[index]); + return this.appendNodes(nodes, parentEl); + } + + /** + * @brief Insert nodes before the nextSibling + * @param nodes + * @param parentEl + * @param nextSibling + * @returns Added element count + */ + static insertNodesBefore(nodes, parentEl, nextSibling) { + let count = 0; + this.loopShallowEls(nodes, el => { + parentEl.insertBefore(el, nextSibling); + count++; + }); + return count; + } + + /** + * @brief Append nodes to the parentEl + * @param nodes + * @param parentEl + * @returns Added element count + */ + static appendNodes(nodes, parentEl) { + let count = 0; + this.loopShallowEls(nodes, el => { + parentEl.appendChild(el); + count++; + }); + return count; + } + + // ---- Lifecycle ---- + /** + * @brief Add willUnmount function to node + * @param node + * @param func + */ + static addWillUnmount(node, func) { + const willUnmountStore = DLStore.global.WillUnmountStore; + const currentStore = willUnmountStore[willUnmountStore.length - 1]; + // ---- If the current store is empty, it means this node is not mutable + if (!currentStore) return; + currentStore.push(func.bind(null, node)); + } + + /** + * @brief Add didUnmount function to node + * @param node + * @param func + */ + static addDidUnmount(node, func) { + const didUnmountStore = DLStore.global.DidUnmountStore; + const currentStore = didUnmountStore[didUnmountStore.length - 1]; + // ---- If the current store is empty, it means this node is not mutable + if (!currentStore) return; + currentStore.push(func.bind(null, node)); + } + + /** + * @brief Add didUnmount function to global store + * @param func + */ + static addDidMount(node, func) { + if (!DLStore.global.DidMountStore) DLStore.global.DidMountStore = []; + DLStore.global.DidMountStore.push(func.bind(null, node)); + } + + /** + * @brief Run all didMount functions and reset the global store + */ + static runDidMount() { + const didMountStore = DLStore.global.DidMountStore; + if (!didMountStore || didMountStore.length === 0) return; + for (let i = didMountStore.length - 1; i >= 0; i--) { + didMountStore[i](); + } + DLStore.global.DidMountStore = []; + } +} diff --git a/packages/inula-v2/src/EnvNode.js b/packages/inula-v2/src/EnvNode.js new file mode 100644 index 00000000..22b1b4c0 --- /dev/null +++ b/packages/inula-v2/src/EnvNode.js @@ -0,0 +1,103 @@ +import { DLNode, DLNodeType } from './DLNode'; +import { DLStore, cached } from './store'; + +export class EnvStoreClass { + constructor() { + this.envs = {}; + this.currentEnvNodes = []; + } + + /** + * @brief Add a node to the current env and merge envs + * @param node - The node to add + */ + addEnvNode(node) { + this.currentEnvNodes.push(node); + this.mergeEnvs(); + } + + /** + * @brief Replace the current env with the given nodes and merge envs + * @param nodes - The nodes to replace the current environment with + */ + replaceEnvNodes(nodes) { + this.currentEnvNodes = nodes; + this.mergeEnvs(); + } + + /** + * @brief Remove the last node from the current env and merge envs + */ + removeEnvNode() { + this.currentEnvNodes.pop(); + this.mergeEnvs(); + } + + /** + * @brief Merge all the envs in currentEnvNodes, inner envs override outer envs + */ + mergeEnvs() { + this.envs = {}; + this.currentEnvNodes.forEach(envNode => { + Object.entries(envNode.envs).forEach(([key, value]) => { + this.envs[key] = [value, envNode]; + }); + }); + } +} + +export class EnvNode extends DLNode { + constructor(envs, depsArr) { + super(DLNodeType.Env); + // Declare a global variable to store the environment variables + if (!('DLEnvStore' in DLStore.global)) DLStore.global.DLEnvStore = new EnvStoreClass(); + + this.envs = envs; + this.depsArr = depsArr; + this.updateNodes = new Set(); + + DLStore.global.DLEnvStore.addEnvNode(this); + } + + cached(deps, name) { + if (!deps || !deps.length) return false; + if (cached(deps, this.depsArr[name])) return true; + this.depsArr[name] = deps; + return false; + } + + /** + * @brief Update a specific env, and update all the comp nodes that depend on this env + * @param name - The name of the environment variable to update + * @param value - The new value of the environment variable + */ + updateEnv(name, valueFunc, deps) { + if (this.cached(deps, name)) return; + const value = valueFunc(); + this.envs[name] = value; + if (DLStore.global.DLEnvStore.currentEnvNodes.includes(this)) { + DLStore.global.DLEnvStore.mergeEnvs(); + } + this.updateNodes.forEach(node => { + node._$updateEnv(name, value, this); + }); + } + + /** + * @brief Add a node to this.updateNodes, delete the node from this.updateNodes when it unmounts + * @param node - The node to add + */ + addNode(node) { + this.updateNodes.add(node); + DLNode.addWillUnmount(node, this.updateNodes.delete.bind(this.updateNodes, node)); + } + + /** + * @brief Set this._$nodes, and exit the current env + * @param nodes - The nodes to set + */ + initNodes(nodes) { + this._$nodes = nodes; + DLStore.global.DLEnvStore.removeEnvNode(); + } +} diff --git a/packages/inula-v2/src/HTMLNode.js b/packages/inula-v2/src/HTMLNode.js new file mode 100644 index 00000000..f8f8ab4a --- /dev/null +++ b/packages/inula-v2/src/HTMLNode.js @@ -0,0 +1,163 @@ +import { DLNode } from './DLNode'; +import { DLStore, cached } from './store'; + +function cache(el, key, deps) { + if (deps.length === 0) return false; + const cacheKey = `$${key}`; + if (cached(deps, el[cacheKey])) return true; + el[cacheKey] = deps; + return false; +} + +/** + * @brief Plainly set style + * @param el + * @param value + */ +export function setStyle(el, value) { + Object.entries(value).forEach(([key, value]) => { + if (key.startsWith('--')) { + el.style.setProperty(key, value); + } else { + el.style[key] = value; + } + }); +} + +/** + * @brief Plainly set dataset + * @param el + * @param value + */ +export function setDataset(el, value) { + Object.assign(el.dataset, value); +} + +/** + * @brief Set HTML property with checking value equality first + * @param el + * @param key + * @param value + */ +export function setHTMLProp(el, key, valueFunc, deps) { + // ---- Comparing deps, same value won't trigger + // will lead to a bug if the value is set outside of the DLNode + // e.g. setHTMLProp(el, "textContent", "value", []) + // => el.textContent = "other" + // => setHTMLProp(el, "textContent", "value", []) + // The value will be set to "other" instead of "value" + if (cache(el, key, deps)) return; + el[key] = valueFunc(); +} + +/** + * @brief Plainly set HTML properties + * @param el + * @param value + */ +export function setHTMLProps(el, value) { + Object.entries(value).forEach(([key, v]) => { + if (key === 'style') return setStyle(el, v); + if (key === 'dataset') return setDataset(el, v); + setHTMLProp(el, key, () => v, []); + }); +} + +/** + * @brief Set HTML attribute with checking value equality first + * @param el + * @param key + * @param value + */ +export function setHTMLAttr(el, key, valueFunc, deps) { + if (cache(el, key, deps)) return; + el.setAttribute(key, valueFunc()); +} + +/** + * @brief Plainly set HTML attributes + * @param el + * @param value + */ +export function setHTMLAttrs(el, value) { + Object.entries(value).forEach(([key, v]) => { + setHTMLAttr(el, key, () => v, []); + }); +} + +/** + * @brief Set memorized event, store the previous event in el[`$on${key}`], if it exists, remove it first + * @param el + * @param key + * @param value + */ +export function setEvent(el, key, value) { + const prevEvent = el[`$on${key}`]; + if (prevEvent) el.removeEventListener(key, prevEvent); + el.addEventListener(key, value); + el[`$on${key}`] = value; +} + +function eventHandler(e) { + const key = `$$${e.type}`; + for (const node of e.composedPath()) { + if (node[key]) node[key](e); + if (e.cancelBubble) return; + } +} + +export function delegateEvent(el, key, value) { + if (el[`$$${key}`] === value) return; + el[`$$${key}`] = value; + if (!DLStore.delegatedEvents.has(key)) { + DLStore.delegatedEvents.add(key); + DLStore.document.addEventListener(key, eventHandler); + } +} +/** + * @brief Shortcut for document.createElement + * @param tag + * @returns HTMLElement + */ +export function createElement(tag) { + return DLStore.document.createElement(tag); +} + +/** + * @brief Insert any DLNode into an element, set the _$nodes and append the element to the element's children + * @param el + * @param node + * @param position + */ +export function insertNode(el, node, position) { + // ---- Set _$nodes + if (!el._$nodes) el._$nodes = Array.from(el.childNodes); + el._$nodes.splice(position, 0, node); + + // ---- Insert nodes' elements + const flowIdx = DLNode.getFlowIndexFromNodes(el._$nodes, node); + DLNode.appendNodesWithIndex([node], el, flowIdx); + // ---- Set parentEl + DLNode.addParentEl([node], el); +} + +/** + * @brief An inclusive assign prop function that accepts any type of prop + * @param el + * @param key + * @param value + */ +export function forwardHTMLProp(el, key, valueFunc, deps) { + if (key === 'style') return setStyle(el, valueFunc()); + if (key === 'dataset') return setDataset(el, valueFunc()); + if (key === 'element') return; + if (key === 'prop') return setHTMLProps(el, valueFunc()); + if (key === 'attr') return setHTMLAttrs(el, valueFunc()); + if (key === 'innerHTML') return setHTMLProp(el, 'innerHTML', valueFunc, deps); + if (key === 'textContent') return setHTMLProp(el, 'textContent', valueFunc, deps); + if (key === 'forwardProp') return; + if (key.startsWith('on')) { + return setEvent(el, key.slice(2).toLowerCase(), valueFunc()); + } + setHTMLAttr(el, key, valueFunc, deps); +} diff --git a/packages/inula-v2/src/MutableNode/CondNode.js b/packages/inula-v2/src/MutableNode/CondNode.js new file mode 100644 index 00000000..dbf75f48 --- /dev/null +++ b/packages/inula-v2/src/MutableNode/CondNode.js @@ -0,0 +1,69 @@ +import { DLNodeType } from '../DLNode'; +import { FlatNode } from './FlatNode'; + +export class CondNode extends FlatNode { + /** + * @brief Constructor, If type, accept a function that returns a list of nodes + * @param caseFunc + */ + constructor(depNum, condFunc) { + super(DLNodeType.Cond); + this.depNum = depNum; + this.cond = -1; + this.condFunc = condFunc; + this.initUnmountStore(); + this._$nodes = this.condFunc(this); + this.setUnmountFuncs(); + + // ---- Add to the global UnmountStore + CondNode.addWillUnmount(this, this.runWillUnmount.bind(this)); + CondNode.addDidUnmount(this, this.runDidUnmount.bind(this)); + } + + /** + * @brief Update the nodes in the environment + */ + updateCond(key) { + // ---- Need to save prev unmount funcs because we can't put removeNodes before geneNewNodesInEnv + // The reason is that if it didn't change, we don't need to unmount or remove the nodes + const prevFuncs = [this.willUnmountFuncs, this.didUnmountFuncs]; + const newNodes = this.geneNewNodesInEnv(() => this.condFunc(this)); + + // ---- If the new nodes are the same as the old nodes, we only need to update children + if (this.didntChange) { + [this.willUnmountFuncs, this.didUnmountFuncs] = prevFuncs; + this.didntChange = false; + this.updateFunc?.(this.depNum, key); + return; + } + // ---- Remove old nodes + const newFuncs = [this.willUnmountFuncs, this.didUnmountFuncs]; + [this.willUnmountFuncs, this.didUnmountFuncs] = prevFuncs; + this._$nodes && this._$nodes.length > 0 && this.removeNodes(this._$nodes); + [this.willUnmountFuncs, this.didUnmountFuncs] = newFuncs; + + if (newNodes.length === 0) { + // ---- No branch has been taken + this._$nodes = []; + return; + } + // ---- Add new nodes + const parentEl = this._$parentEl; + // ---- Faster append with nextSibling rather than flowIndex + const flowIndex = CondNode.getFlowIndexFromNodes(parentEl._$nodes, this); + + const nextSibling = parentEl.childNodes[flowIndex]; + CondNode.appendNodesWithSibling(newNodes, parentEl, nextSibling); + CondNode.runDidMount(); + this._$nodes = newNodes; + } + + /** + * @brief The update function of IfNode's childNodes is stored in the first child node + * @param changed + */ + update(changed) { + if (!(~this.depNum & changed)) return; + this.updateFunc?.(changed); + } +} diff --git a/packages/inula-v2/src/MutableNode/ExpNode.js b/packages/inula-v2/src/MutableNode/ExpNode.js new file mode 100644 index 00000000..0373dc1f --- /dev/null +++ b/packages/inula-v2/src/MutableNode/ExpNode.js @@ -0,0 +1,86 @@ +import { DLNodeType } from '../DLNode'; +import { FlatNode } from './FlatNode'; +import { DLStore, cached } from '../store'; + +export class ExpNode extends FlatNode { + /** + * @brief Constructor, Exp type, accept a function that returns a list of nodes + * @param nodesFunc + */ + constructor(value, deps) { + super(DLNodeType.Exp); + this.initUnmountStore(); + this._$nodes = ExpNode.formatNodes(value); + this.setUnmountFuncs(); + this.deps = this.parseDeps(deps); + // ---- Add to the global UnmountStore + ExpNode.addWillUnmount(this, this.runWillUnmount.bind(this)); + ExpNode.addDidUnmount(this, this.runDidUnmount.bind(this)); + } + + parseDeps(deps) { + return deps.map(dep => { + // ---- CompNode + if (dep?.prototype?._$init) return dep.toString(); + // ---- SnippetNode + if (dep?.propViewFunc) return dep.propViewFunc.toString(); + return dep; + }); + } + + cache(deps) { + if (!deps || !deps.length) return false; + deps = this.parseDeps(deps); + if (cached(deps, this.deps)) return true; + this.deps = deps; + return false; + } + /** + * @brief Generate new nodes and replace the old nodes + */ + update(valueFunc, deps) { + if (this.cache(deps)) return; + this.removeNodes(this._$nodes); + const newNodes = this.geneNewNodesInEnv(() => ExpNode.formatNodes(valueFunc())); + if (newNodes.length === 0) { + this._$nodes = []; + return; + } + + // ---- Add new nodes + const parentEl = this._$parentEl; + const flowIndex = ExpNode.getFlowIndexFromNodes(parentEl._$nodes, this); + const nextSibling = parentEl.childNodes[flowIndex]; + ExpNode.appendNodesWithSibling(newNodes, parentEl, nextSibling); + ExpNode.runDidMount(); + + this._$nodes = newNodes; + } + + /** + * @brief Format the nodes + * @param nodes + * @returns New nodes + */ + static formatNodes(nodes) { + if (!Array.isArray(nodes)) nodes = [nodes]; + return ( + nodes + // ---- Flatten the nodes + .flat(1) + // ---- Filter out empty nodes + .filter(node => node !== undefined && node !== null && typeof node !== 'boolean') + .map(node => { + // ---- If the node is a string, number or bigint, convert it to a text node + if (typeof node === 'string' || typeof node === 'number' || typeof node === 'bigint') { + return DLStore.document.createTextNode(`${node}`); + } + // ---- If the node has PropView, call it to get the view + if ('propViewFunc' in node) return node.build(); + return node; + }) + // ---- Flatten the nodes again + .flat(1) + ); + } +} diff --git a/packages/inula-v2/src/MutableNode/FlatNode.js b/packages/inula-v2/src/MutableNode/FlatNode.js new file mode 100644 index 00000000..9f1519e8 --- /dev/null +++ b/packages/inula-v2/src/MutableNode/FlatNode.js @@ -0,0 +1,33 @@ +import { DLStore } from '../store'; +import { MutableNode } from './MutableNode'; + +export class FlatNode extends MutableNode { + willUnmountFuncs = []; + didUnmountFuncs = []; + + setUnmountFuncs() { + this.willUnmountFuncs = DLStore.global.WillUnmountStore.pop(); + this.didUnmountFuncs = DLStore.global.DidUnmountStore.pop(); + } + + runWillUnmount() { + for (let i = 0; i < this.willUnmountFuncs.length; i++) this.willUnmountFuncs[i](); + } + + runDidUnmount() { + for (let i = this.didUnmountFuncs.length - 1; i >= 0; i--) this.didUnmountFuncs[i](); + } + + removeNodes(nodes) { + this.runWillUnmount(); + super.removeNodes(nodes); + this.runDidUnmount(); + } + + geneNewNodesInEnv(newNodesFunc) { + this.initUnmountStore(); + const nodes = super.geneNewNodesInEnv(newNodesFunc); + this.setUnmountFuncs(); + return nodes; + } +} diff --git a/packages/inula-v2/src/MutableNode/ForNode.js b/packages/inula-v2/src/MutableNode/ForNode.js new file mode 100644 index 00000000..0cc14972 --- /dev/null +++ b/packages/inula-v2/src/MutableNode/ForNode.js @@ -0,0 +1,406 @@ +import { DLNodeType } from '../DLNode'; +import { DLStore } from '../store'; +import { MutableNode } from './MutableNode'; + +export class ForNode extends MutableNode { + array; + nodeFunc; + depNum; + + nodesMap = new Map(); + updateArr = []; + + /** + * @brief Getter for nodes + */ + get _$nodes() { + const nodes = []; + for (let idx = 0; idx < this.array.length; idx++) { + nodes.push(...this.nodesMap.get(this.keys?.[idx] ?? idx)); + } + return nodes; + } + + /** + * @brief Constructor, For type + * @param array + * @param nodeFunc + * @param keys + */ + constructor(array, depNum, keys, nodeFunc) { + super(DLNodeType.For); + this.array = [...array]; + this.keys = keys; + this.depNum = depNum; + this.addNodeFunc(nodeFunc); + } + + /** + * @brief To be called immediately after the constructor + * @param nodeFunc + */ + addNodeFunc(nodeFunc) { + this.nodeFunc = nodeFunc; + this.array.forEach((item, idx) => { + this.initUnmountStore(); + const key = this.keys?.[idx] ?? idx; + const nodes = nodeFunc(item, this.updateArr, idx); + this.nodesMap.set(key, nodes); + this.setUnmountMap(key); + }); + // ---- For nested ForNode, the whole strategy is just like EnvStore + // we use array of function array to create "environment", popping and pushing + ForNode.addWillUnmount(this, this.runAllWillUnmount.bind(this)); + ForNode.addDidUnmount(this, this.runAllDidUnmount.bind(this)); + } + + /** + * @brief Update the view related to one item in the array + * @param nodes + * @param item + */ + updateItem(idx, array, changed) { + // ---- The update function of ForNode's childNodes is stored in the first child node + this.updateArr[idx]?.(changed ?? this.depNum, array[idx]); + } + + updateItems(changed) { + for (let idx = 0; idx < this.array.length; idx++) { + this.updateItem(idx, this.array, changed); + } + } + + /** + * @brief Non-array update function + * @param changed + */ + update(changed) { + // ---- e.g. this.depNum -> 1110 changed-> 1010 + // ~this.depNum & changed -> ~1110 & 1010 -> 0000 + // no update because depNum contains all the changed + // ---- e.g. this.depNum -> 1110 changed-> 1101 + // ~this.depNum & changed -> ~1110 & 1101 -> 0001 + // update because depNum doesn't contain all the changed + if (!(~this.depNum & changed)) return; + this.updateItems(changed); + } + + /** + * @brief Array-related update function + * @param newArray + * @param newKeys + */ + updateArray(newArray, newKeys) { + if (newKeys) { + this.updateWithKey(newArray, newKeys); + return; + } + this.updateWithOutKey(newArray); + } + + /** + * @brief Shortcut to generate new nodes with idx and key + */ + getNewNodes(idx, key, array, updateArr) { + this.initUnmountStore(); + const nodes = this.geneNewNodesInEnv(() => this.nodeFunc(array[idx], updateArr ?? this.updateArr, idx)); + this.setUnmountMap(key); + this.nodesMap.set(key, nodes); + return nodes; + } + + /** + * @brief Set the unmount map by getting the last unmount map from the global store + * @param key + */ + setUnmountMap(key) { + const willUnmountMap = DLStore.global.WillUnmountStore.pop(); + if (willUnmountMap && willUnmountMap.length > 0) { + if (!this.willUnmountMap) this.willUnmountMap = new Map(); + this.willUnmountMap.set(key, willUnmountMap); + } + const didUnmountMap = DLStore.global.DidUnmountStore.pop(); + if (didUnmountMap && didUnmountMap.length > 0) { + if (!this.didUnmountMap) this.didUnmountMap = new Map(); + this.didUnmountMap.set(key, didUnmountMap); + } + } + + /** + * @brief Run all the unmount functions and clear the unmount map + */ + runAllWillUnmount() { + if (!this.willUnmountMap || this.willUnmountMap.size === 0) return; + this.willUnmountMap.forEach(funcs => { + for (let i = 0; i < funcs.length; i++) funcs[i]?.(); + }); + this.willUnmountMap.clear(); + } + + /** + * @brief Run all the unmount functions and clear the unmount map + */ + runAllDidUnmount() { + if (!this.didUnmountMap || this.didUnmountMap.size === 0) return; + this.didUnmountMap.forEach(funcs => { + for (let i = funcs.length - 1; i >= 0; i--) funcs[i]?.(); + }); + this.didUnmountMap.clear(); + } + + /** + * @brief Run the unmount functions of the given key + * @param key + */ + runWillUnmount(key) { + if (!this.willUnmountMap || this.willUnmountMap.size === 0) return; + const funcs = this.willUnmountMap.get(key); + if (!funcs) return; + for (let i = 0; i < funcs.length; i++) funcs[i]?.(); + this.willUnmountMap.delete(key); + } + + /** + * @brief Run the unmount functions of the given key + */ + runDidUnmount(key) { + if (!this.didUnmountMap || this.didUnmountMap.size === 0) return; + const funcs = this.didUnmountMap.get(key); + if (!funcs) return; + for (let i = funcs.length - 1; i >= 0; i--) funcs[i]?.(); + this.didUnmountMap.delete(key); + } + + /** + * @brief Remove nodes from parentEl and run willUnmount and didUnmount + * @param nodes + * @param key + */ + removeNodes(nodes, key) { + this.runWillUnmount(key); + super.removeNodes(nodes); + this.runDidUnmount(key); + this.nodesMap.delete(key); + } + + /** + * @brief Update the nodes without keys + * @param newArray + */ + updateWithOutKey(newArray) { + const preLength = this.array.length; + const currLength = newArray.length; + + if (preLength === currLength) { + // ---- If the length is the same, we only need to update the nodes + for (let idx = 0; idx < this.array.length; idx++) { + this.updateItem(idx, newArray); + } + this.array = [...newArray]; + return; + } + const parentEl = this._$parentEl; + // ---- If the new array is longer, add new nodes directly + if (preLength < currLength) { + let flowIndex = ForNode.getFlowIndexFromNodes(parentEl._$nodes, this); + // ---- Calling parentEl.childNodes.length is time-consuming, + // so we use a length variable to store the length + const length = parentEl.childNodes.length; + for (let idx = 0; idx < currLength; idx++) { + if (idx < preLength) { + flowIndex += ForNode.getFlowIndexFromNodes(this.nodesMap.get(idx)); + this.updateItem(idx, newArray); + continue; + } + const newNodes = this.getNewNodes(idx, idx, newArray); + ForNode.appendNodesWithIndex(newNodes, parentEl, flowIndex, length); + } + ForNode.runDidMount(); + this.array = [...newArray]; + return; + } + + // ---- Update the nodes first + for (let idx = 0; idx < currLength; idx++) { + this.updateItem(idx, newArray); + } + // ---- If the new array is shorter, remove the extra nodes + for (let idx = currLength; idx < preLength; idx++) { + const nodes = this.nodesMap.get(idx); + this.removeNodes(nodes, idx); + } + this.updateArr.splice(currLength, preLength - currLength); + this.array = [...newArray]; + } + + /** + * @brief Update the nodes with keys + * @param newArray + * @param newKeys + */ + updateWithKey(newArray, newKeys) { + if (newKeys.length !== new Set(newKeys).size) { + throw new Error('DLight: Duplicate keys in for loop are not allowed'); + } + const prevKeys = this.keys; + this.keys = newKeys; + + if (ForNode.arrayEqual(prevKeys, this.keys)) { + // ---- If the keys are the same, we only need to update the nodes + for (let idx = 0; idx < newArray.length; idx++) { + this.updateItem(idx, newArray); + } + this.array = [...newArray]; + return; + } + + const parentEl = this._$parentEl; + + // ---- No nodes after, delete all nodes + if (this.keys.length === 0) { + const parentNodes = parentEl._$nodes ?? []; + if (parentNodes.length === 1 && parentNodes[0] === this) { + // ---- ForNode is the only node in the parent node + // Frequently used in real life scenarios because we tend to always wrap for with a div element, + // so we optimize it here + this.runAllWillUnmount(); + parentEl.innerHTML = ''; + this.runAllDidUnmount(); + } else { + for (let prevIdx = 0; prevIdx < prevKeys.length; prevIdx++) { + const prevKey = prevKeys[prevIdx]; + this.removeNodes(this.nodesMap.get(prevKey), prevKey); + } + } + this.nodesMap.clear(); + this.updateArr = []; + this.array = []; + return; + } + + // ---- Record how many nodes are before this ForNode with the same parentNode + const flowIndex = ForNode.getFlowIndexFromNodes(parentEl._$nodes, this); + + // ---- No nodes before, append all nodes + if (prevKeys.length === 0) { + const nextSibling = parentEl.childNodes[flowIndex]; + for (let idx = 0; idx < this.keys.length; idx++) { + const newNodes = this.getNewNodes(idx, this.keys[idx], newArray); + ForNode.appendNodesWithSibling(newNodes, parentEl, nextSibling); + } + ForNode.runDidMount(); + this.array = [...newArray]; + return; + } + + const shuffleKeys = []; + const newUpdateArr = []; + + // ---- 1. Delete the nodes that are no longer in the array + for (let prevIdx = 0; prevIdx < prevKeys.length; prevIdx++) { + const prevKey = prevKeys[prevIdx]; + if (this.keys.includes(prevKey)) { + shuffleKeys.push(prevKey); + newUpdateArr.push(this.updateArr[prevIdx]); + continue; + } + this.removeNodes(this.nodesMap.get(prevKey), prevKey); + } + + // ---- 2. Add the nodes that are not in the array but in the new array + // ---- Calling parentEl.childNodes.length is time-consuming, + // so we use a length variable to store the length + let length = parentEl.childNodes.length; + let newFlowIndex = flowIndex; + for (let idx = 0; idx < this.keys.length; idx++) { + const key = this.keys[idx]; + const prevIdx = shuffleKeys.indexOf(key); + if (prevIdx !== -1) { + // ---- These nodes are already in the parentEl, + // and we need to keep track of their flowIndex + newFlowIndex += ForNode.getFlowIndexFromNodes(this.nodesMap.get(key)); + newUpdateArr[prevIdx]?.(this.depNum, newArray[idx]); + continue; + } + // ---- Insert updateArr first because in getNewNode the updateFunc will replace this null + newUpdateArr.splice(idx, 0, null); + const newNodes = this.getNewNodes(idx, key, newArray, newUpdateArr); + // ---- Add the new nodes + shuffleKeys.splice(idx, 0, key); + + const count = ForNode.appendNodesWithIndex(newNodes, parentEl, newFlowIndex, length); + newFlowIndex += count; + length += count; + } + ForNode.runDidMount(); + + // ---- After adding and deleting, the only thing left is to reorder the nodes, + // but if the keys are the same, we don't need to reorder + if (ForNode.arrayEqual(this.keys, shuffleKeys)) { + this.array = [...newArray]; + this.updateArr = newUpdateArr; + return; + } + + newFlowIndex = flowIndex; + const bufferNodes = new Map(); + // ---- 3. Replace the nodes in the same position using Fisher-Yates shuffle algorithm + for (let idx = 0; idx < this.keys.length; idx++) { + const key = this.keys[idx]; + const prevIdx = shuffleKeys.indexOf(key); + + const bufferedNode = bufferNodes.get(key); + if (bufferedNode) { + // ---- We need to add the flowIndex of the bufferedNode, + // because the bufferedNode is in the parentEl and the new position is ahead of the previous position + const bufferedFlowIndex = ForNode.getFlowIndexFromNodes(bufferedNode); + const lastEl = ForNode.toEls(bufferedNode).pop(); + const nextSibling = parentEl.childNodes[newFlowIndex + bufferedFlowIndex]; + if (lastEl !== nextSibling && lastEl.nextSibling !== nextSibling) { + // ---- If the node is buffered, we need to add it to the parentEl + ForNode.insertNodesBefore(bufferedNode, parentEl, nextSibling); + } + // ---- So the added length is the length of the bufferedNode + newFlowIndex += bufferedFlowIndex; + delete bufferNodes[idx]; + } else if (prevIdx === idx) { + // ---- If the node is in the same position, we don't need to do anything + newFlowIndex += ForNode.getFlowIndexFromNodes(this.nodesMap.get(key)); + continue; + } else { + // ---- If the node is not in the same position, we need to buffer it + // We buffer the node of the previous position, and then replace it with the node of the current position + const prevKey = shuffleKeys[idx]; + bufferNodes.set(prevKey, this.nodesMap.get(prevKey)); + // ---- Length would never change, and the last will always be in the same position, + // so it'll always be insertBefore instead of appendChild + const childNodes = this.nodesMap.get(key); + const lastEl = ForNode.toEls(childNodes).pop(); + const nextSibling = parentEl.childNodes[newFlowIndex]; + if (lastEl !== nextSibling && lastEl.nextSibling !== nextSibling) { + newFlowIndex += ForNode.insertNodesBefore(childNodes, parentEl, nextSibling); + } + } + // ---- Swap the keys + const tempKey = shuffleKeys[idx]; + shuffleKeys[idx] = shuffleKeys[prevIdx]; + shuffleKeys[prevIdx] = tempKey; + const tempUpdateFunc = newUpdateArr[idx]; + newUpdateArr[idx] = newUpdateArr[prevIdx]; + newUpdateArr[prevIdx] = tempUpdateFunc; + } + this.array = [...newArray]; + this.updateArr = newUpdateArr; + } + + /** + * @brief Compare two arrays + * @param arr1 + * @param arr2 + * @returns + */ + static arrayEqual(arr1, arr2) { + if (arr1.length !== arr2.length) return false; + return arr1.every((item, idx) => item === arr2[idx]); + } +} diff --git a/packages/inula-v2/src/MutableNode/MutableNode.js b/packages/inula-v2/src/MutableNode/MutableNode.js new file mode 100644 index 00000000..b557bdbc --- /dev/null +++ b/packages/inula-v2/src/MutableNode/MutableNode.js @@ -0,0 +1,71 @@ +import { DLNode } from '../DLNode'; +import { DLStore } from '../store'; + +export class MutableNode extends DLNode { + /** + * @brief Mutable node is a node that this._$nodes can be changed, things need to pay attention: + * 1. The environment of the new nodes should be the same as the old nodes + * 2. The new nodes should be added to the parentEl + * 3. The old nodes should be removed from the parentEl + * @param type + */ + constructor(type) { + super(type); + // ---- Save the current environment nodes, must be a new reference + if (DLStore.global.DLEnvStore && DLStore.global.DLEnvStore.currentEnvNodes.length > 0) { + this.savedEnvNodes = [...DLStore.global.DLEnvStore.currentEnvNodes]; + } + } + + /** + * @brief Initialize the new nodes, add parentEl to all nodes + * @param nodes + */ + initNewNodes(nodes) { + // ---- Add parentEl to all nodes + DLNode.addParentEl(nodes, this._$parentEl); + } + + /** + * @brief Generate new nodes in the saved environment + * @param newNodesFunc + * @returns + */ + geneNewNodesInEnv(newNodesFunc) { + if (!this.savedEnvNodes) { + // ---- No saved environment, just generate new nodes + const newNodes = newNodesFunc(); + // ---- Only for IfNode's same condition return + // ---- Initialize the new nodes + this.initNewNodes(newNodes); + return newNodes; + } + // ---- Save the current environment nodes + const currentEnvNodes = DLStore.global.DLEnvStore.currentEnvNodes; + // ---- Replace the saved environment nodes + DLStore.global.DLEnvStore.replaceEnvNodes(this.savedEnvNodes); + const newNodes = newNodesFunc(); + // ---- Retrieve the current environment nodes + DLStore.global.DLEnvStore.replaceEnvNodes(currentEnvNodes); + // ---- Only for IfNode's same condition return + // ---- Initialize the new nodes + this.initNewNodes(newNodes); + return newNodes; + } + + initUnmountStore() { + DLStore.global.WillUnmountStore.push([]); + DLStore.global.DidUnmountStore.push([]); + } + + /** + * @brief Remove nodes from parentEl and run willUnmount and didUnmount + * @param nodes + * @param removeEl Only remove outermost element + */ + removeNodes(nodes) { + DLNode.loopShallowEls(nodes, node => { + this._$parentEl.removeChild(node); + }); + } +} diff --git a/packages/inula-v2/src/MutableNode/TryNode.js b/packages/inula-v2/src/MutableNode/TryNode.js new file mode 100644 index 00000000..12f1bb18 --- /dev/null +++ b/packages/inula-v2/src/MutableNode/TryNode.js @@ -0,0 +1,45 @@ +import { DLNodeType } from '../DLNode'; +import { FlatNode } from './FlatNode'; +import { EnvNode } from '../EnvNode'; + +export class TryNode extends FlatNode { + constructor(tryFunc, catchFunc) { + super(DLNodeType.Try); + this.tryFunc = tryFunc; + const catchable = this.getCatchable(catchFunc); + this.envNode = new EnvNode({ _$catchable: catchable }); + const nodes = tryFunc(this.setUpdateFunc.bind(this), catchable) ?? []; + this.envNode.initNodes(nodes); + this._$nodes = nodes; + } + + update(changed) { + this.updateFunc?.(changed); + } + + setUpdateFunc(updateFunc) { + this.updateFunc = updateFunc; + } + + getCatchable(catchFunc) { + return callback => + (...args) => { + try { + return callback(...args); + } catch (e) { + // ---- Run it in next tick to make sure when error occurs before + // didMount, this._$parentEl is not null + Promise.resolve().then(() => { + const nodes = this.geneNewNodesInEnv(() => catchFunc(this.setUpdateFunc.bind(this), e)); + this._$nodes && this.removeNodes(this._$nodes); + const parentEl = this._$parentEl; + const flowIndex = FlatNode.getFlowIndexFromNodes(parentEl._$nodes, this); + const nextSibling = parentEl.childNodes[flowIndex]; + FlatNode.appendNodesWithSibling(nodes, parentEl, nextSibling); + FlatNode.runDidMount(); + this._$nodes = nodes; + }); + } + }; + } +} diff --git a/packages/inula-v2/src/PropView.js b/packages/inula-v2/src/PropView.js new file mode 100644 index 00000000..2cb1701f --- /dev/null +++ b/packages/inula-v2/src/PropView.js @@ -0,0 +1,48 @@ +import { DLNode } from './DLNode'; +import { insertNode } from './HTMLNode'; +export class PropView { + propViewFunc; + dlUpdateFunc = new Set(); + + /** + * @brief PropView constructor, accept a function that returns a list of DLNode + * @param propViewFunc - A function that when called, collects and returns an array of DLNode instances + */ + constructor(propViewFunc) { + this.propViewFunc = propViewFunc; + } + + /** + * @brief Build the prop view by calling the propViewFunc and add every single instance of the returned DLNode to dlUpdateNodes + * @returns An array of DLNode instances returned by propViewFunc + */ + build() { + let update; + const addUpdate = updateFunc => { + update = updateFunc; + this.dlUpdateFunc.add(updateFunc); + }; + const newNodes = this.propViewFunc(addUpdate); + if (newNodes.length === 0) return []; + if (update) { + // Remove the updateNode from dlUpdateNodes when it unmounts + DLNode.addWillUnmount(newNodes[0], this.dlUpdateFunc.delete.bind(this.dlUpdateFunc, update)); + } + + return newNodes; + } + + /** + * @brief Update every node in dlUpdateNodes + * @param changed - A parameter indicating what changed to trigger the update + */ + update(...args) { + this.dlUpdateFunc.forEach(update => { + update(...args); + }); + } +} + +export function insertChildren(el, propView) { + insertNode(el, { _$nodes: propView.build(), _$dlNodeType: 7 }, 0); +} diff --git a/packages/inula-v2/src/SnippetNode.js b/packages/inula-v2/src/SnippetNode.js new file mode 100644 index 00000000..cb1522cf --- /dev/null +++ b/packages/inula-v2/src/SnippetNode.js @@ -0,0 +1,18 @@ +import { DLNode, DLNodeType } from './DLNode'; +import { cached } from './store'; + +export class SnippetNode extends DLNode { + constructor(depsArr) { + super(DLNodeType.Snippet); + this.depsArr = depsArr; + } + + cached(deps, changed) { + if (!deps || !deps.length) return false; + const idx = Math.log2(changed); + const prevDeps = this.depsArr[idx]; + if (cached(deps, prevDeps)) return true; + this.depsArr[idx] = deps; + return false; + } +} diff --git a/packages/inula-v2/src/TextNode.js b/packages/inula-v2/src/TextNode.js new file mode 100644 index 00000000..5a55bee9 --- /dev/null +++ b/packages/inula-v2/src/TextNode.js @@ -0,0 +1,24 @@ +import { DLStore, cached } from './store'; + +/** + * @brief Shorten document.createTextNode + * @param value + * @returns Text + */ +export function createTextNode(value, deps) { + const node = DLStore.document.createTextNode(value); + node.$$deps = deps; + return node; +} + +/** + * @brief Update text node and check if the value is changed + * @param node + * @param value + */ +export function updateText(node, valueFunc, deps) { + if (cached(deps, node.$$deps)) return; + const value = valueFunc(); + node.textContent = value; + node.$$deps = deps; +} diff --git a/packages/inula-v2/src/index.d.ts b/packages/inula-v2/src/index.d.ts new file mode 100644 index 00000000..a3d3b4a0 --- /dev/null +++ b/packages/inula-v2/src/index.d.ts @@ -0,0 +1 @@ +export * from './types/index'; diff --git a/packages/inula-v2/src/index.js b/packages/inula-v2/src/index.js new file mode 100644 index 00000000..449b9e63 --- /dev/null +++ b/packages/inula-v2/src/index.js @@ -0,0 +1,56 @@ +import { DLNode } from './DLNode'; +import { insertNode } from './HTMLNode'; + +export * from './HTMLNode'; +export * from './CompNode'; +export * from './EnvNode'; +export * from './TextNode'; +export * from './PropView'; +export * from './SnippetNode'; +export * from './MutableNode/ForNode'; +export * from './MutableNode/ExpNode'; +export * from './MutableNode/CondNode'; +export * from './MutableNode/TryNode'; + +import { DLStore } from './store'; +export { setGlobal, setDocument } from './store'; + +function initStore() { + // Declare a global variable to store willUnmount functions + DLStore.global.WillUnmountStore = []; + // Declare a global variable to store didUnmount functions + DLStore.global.DidUnmountStore = []; +} + +export function render(idOrEl, DL) { + let el = idOrEl; + if (typeof idOrEl === 'string') { + const elFound = DLStore.document.getElementById(idOrEl); + if (elFound) el = elFound; + else { + throw new Error(`DLight: Element with id ${idOrEl} not found`); + } + } + initStore(); + el.innerHTML = ''; + const dlNode = new DL(); + dlNode._$init(); + insertNode(el, dlNode, 0); + DLNode.runDidMount(); +} + +export function manual(callback, _deps) { + return callback(); +} +export function escape(arg) { + return arg; +} + +export const $ = escape; +export const required = null; + +export function use() { + console.error( + 'DLight: use() is not supported be called directly. You can only assign `use(model)` to a dlight class property. Any other expressions are not allowed.' + ); +} diff --git a/packages/inula-v2/src/store.js b/packages/inula-v2/src/store.js new file mode 100644 index 00000000..76aead7b --- /dev/null +++ b/packages/inula-v2/src/store.js @@ -0,0 +1,42 @@ +import { Store } from '@inula/store'; + +// ---- Using external Store to store global and document +// Because Store is a singleton, it is safe to use it as a global variable +// If created in DLight package, different package versions will introduce +// multiple Store instances. + +if (!('global' in Store)) { + if (typeof window !== 'undefined') { + Store.global = window; + } else if (typeof global !== 'undefined') { + Store.global = global; + } else { + Store.global = {}; + } +} +if (!('document' in Store)) { + if (typeof document !== 'undefined') { + Store.document = document; + } +} + +export const DLStore = { ...Store, delegatedEvents: new Set() }; + +export function setGlobal(globalObj) { + DLStore.global = globalObj; +} + +export function setDocument(customDocument) { + DLStore.document = customDocument; +} + +/** + * @brief Compare the deps with the previous deps + * @param deps + * @param prevDeps + * @returns + */ +export function cached(deps, prevDeps) { + if (!prevDeps || deps.length !== prevDeps.length) return false; + return deps.every((dep, i) => !(dep instanceof Object) && prevDeps[i] === dep); +} diff --git a/packages/inula-v2/src/types/compTag.d.ts b/packages/inula-v2/src/types/compTag.d.ts new file mode 100644 index 00000000..f2ea568c --- /dev/null +++ b/packages/inula-v2/src/types/compTag.d.ts @@ -0,0 +1,74 @@ +import { type DLightHTMLAttributes } from './htmlTag'; + +// a very magical solution +// when vscode parse ts, if it is type A = B>, it will show the detailed type, +// but if type A = B> & xxx, it will only show alias (here is A) +// because I don't want to expose the detailed type, so type A = B> & Useless +// but if type Useless = { useless: never } will cause this type to have an additional property userless +// so just don't add key! +type Useless = { [key in '']: never }; + +export type DLightObject = { + [K in keyof T]-?: undefined extends T[K] + ? (value?: T[K]) => DLightObject> + : (value: T[K]) => DLightObject>; +}; +interface CustomNodeProps { + willMount: (node: any) => void; + didMount: (node: any) => void; + willUnmount: (node: any) => void; + didUnmount: (node: any) => void; + didUpdate: (node: any, key: string, prevValue: any, currValue: any) => void; + ref: (node: any) => void; + elements: HTMLElement[] | ((holder: HTMLElement[]) => void) | undefined; + forwardProps: true | undefined; +} + +export type ContentProp = T & { _$idContent: true }; + +export type RemoveOptional = { + [K in keyof T]-?: T[K]; +}; + +type IsAny = { _$isAny: true } extends T ? true : false; + +export type ContentKeyName = { + [K in keyof T]: IsAny extends true + ? never + : // eslint-disable-next-line @typescript-eslint/no-unused-vars + T[K] extends ContentProp + ? K + : never; +}[keyof T]; + +export type CheckContent = RemoveOptional[ContentKeyName>]; + +type CustomClassTag = + ContentKeyName> extends undefined + ? () => DLightObject + : undefined extends O[ContentKeyName>] + ? CheckContent extends ContentProp + ? (content?: U extends unknown ? any : unknown) => DLightObject>>> + : never + : CheckContent extends ContentProp + ? (content: U extends unknown ? any : unknown) => DLightObject>>> + : never; + +type CustomSnippetTag = T extends { content: infer U } + ? (content: U) => DLightObject> + : T extends { content?: infer U } + ? (content?: U) => DLightObject> + : () => DLightObject; + +type CustomTagType = CustomClassTag< + T & CustomNodeProps & (keyof G extends never ? object : DLightHTMLAttributes), + T +> & + Useless; +export type Typed = CustomTagType & Useless; +export type SnippetTyped = CustomSnippetTag & Useless; + +export type Pretty = any; + +// ---- reverse +export type UnTyped = T extends Typed ? U : never; diff --git a/packages/inula-v2/src/types/envTag.d.ts b/packages/inula-v2/src/types/envTag.d.ts new file mode 100644 index 00000000..c85e0030 --- /dev/null +++ b/packages/inula-v2/src/types/envTag.d.ts @@ -0,0 +1,6 @@ +// ---- env +import { DLightObject } from './compTag'; + +type AnyEnv = { _$anyEnv: true }; + +export const env: () => T extends AnyEnv ? any : DLightObject; diff --git a/packages/inula-v2/src/types/expressionTag.d.ts b/packages/inula-v2/src/types/expressionTag.d.ts new file mode 100644 index 00000000..7546c296 --- /dev/null +++ b/packages/inula-v2/src/types/expressionTag.d.ts @@ -0,0 +1,13 @@ +interface ExpressionTag { + willMount: (node: any) => void; + didMount: (node: any) => void; + willUnmount: (node: any) => void; + didUnmount: (node: any) => void; + didUpdate: (node: any, key: string, prevValue: T, currValue: T) => void; + elements: HTMLElement[] | ((holder: HTMLElement[]) => void) | undefined; + ref: (node: any) => void; +} + +type ExpressionTagFunc = (nodes: any) => ExpressionTag; + +export const _: ExpressionTagFunc; diff --git a/packages/inula-v2/src/types/htmlTag/event.d.ts b/packages/inula-v2/src/types/htmlTag/event.d.ts new file mode 100644 index 00000000..e6a3625e --- /dev/null +++ b/packages/inula-v2/src/types/htmlTag/event.d.ts @@ -0,0 +1,516 @@ +export interface DLightGlobalEventHandlers { + /** + * Fires when the user aborts the download. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/abort_event) + */ + onAbort: ((this: GlobalEventHandlers, ev: UIEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/animationcancel_event) */ + onAnimationCancel: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/animationend_event) */ + onAnimationEnd: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/animationiteration_event) */ + onAnimationIteration: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/animationstart_event) */ + onAnimationStart: ((this: GlobalEventHandlers, ev: AnimationEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/auxclick_event) */ + onAuxClick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/beforeinput_event) */ + onBeforeInput: ((this: GlobalEventHandlers, ev: InputEvent) => any) | null; + + /** + * Fires when the object loses the input focus. + * @param ev The focus event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/blur_event) + */ + onBlur: ((this: GlobalEventHandlers, ev: FocusEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLDialogElement/cancel_event) */ + onCancel: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Occurs when playback is possible, but would require further buffering. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/canplay_event) + */ + onCanPlay: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/canplaythrough_event) */ + onCanPlayThrough: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Fires when the contents of the object or selection have changed. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/change_event) + */ + onChange: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Fires when the user clicks the left mouse button on the object + * @param ev The mouse event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/click_event) + */ + onClick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLDialogElement/close_event) */ + onClose: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Fires when the user clicks the right mouse button in the client area, opening the context menu. + * @param ev The mouse event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/contextmenu_event) + */ + onContextMenu: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/copy_event) */ + onCopy: ((this: GlobalEventHandlers, ev: ClipboardEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLTrackElement/cuechange_event) */ + onCueChange: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/cut_event) */ + onCut: ((this: GlobalEventHandlers, ev: ClipboardEvent) => any) | null; + + /** + * Fires when the user double-clicks the object. + * @param ev The mouse event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/dblclick_event) + */ + onDblClick: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; + + /** + * Fires on the source object continuously during a drag operation. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/drag_event) + */ + onDrag: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; + + /** + * Fires on the source object when the user releases the mouse at the close of a drag operation. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/dragend_event) + */ + onDragEnd: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; + + /** + * Fires on the target element when the user drags the object to a valid drop target. + * @param ev The drag event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/dragenter_event) + */ + onDragEnter: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; + + /** + * Fires on the target object when the user moves the mouse out of a valid drop target during a drag operation. + * @param ev The drag event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/dragleave_event) + */ + onDragLeave: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; + + /** + * Fires on the target element continuously while the user drags the object over a valid drop target. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/dragover_event) + */ + onDragOver: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; + + /** + * Fires on the source object when the user starts to drag a text selection or selected object. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/dragstart_event) + */ + onDragStart: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/drop_event) */ + onDrop: ((this: GlobalEventHandlers, ev: DragEvent) => any) | null; + + /** + * Occurs when the duration attribute is updated. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/durationchange_event) + */ + onDurationChange: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Occurs when the media element is reset to its initial state. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/emptied_event) + */ + onEmptied: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Occurs when the end of playback is reached. + * @param ev The event + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/ended_event) + */ + onEnded: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Fires when an error occurs during object loading. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/error_event) + */ + onError: OnErrorEventHandler; + + /** + * Fires when the object receives focus. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/focus_event) + */ + onFocus: ((this: GlobalEventHandlers, ev: FocusEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLFormElement/formdata_event) */ + onFormData: ((this: GlobalEventHandlers, ev: FormDataEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/gotpointercapture_event) */ + onGotPointerCapture: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/input_event) */ + onInput: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLInputElement/invalid_event) */ + onInvalid: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Fires when the user presses a key. + * @param ev The keyboard event + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/keydown_event) + */ + onKeyDown: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null; + + /** + * Fires when the user presses an alphanumeric key. + * @param ev The event. + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/keypress_event) + */ + onKeyPress: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null; + + /** + * Fires when the user releases a key. + * @param ev The keyboard event + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/keyup_event) + */ + onKeyUp: ((this: GlobalEventHandlers, ev: KeyboardEvent) => any) | null; + + /** + * Fires immediately after the browser loads the object. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/SVGElement/load_event) + */ + onLoad: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Occurs when media data is loaded at the current playback position. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/loadeddata_event) + */ + onLoadedData: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Occurs when the duration and dimensions of the media have been determined. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/loadedmetadata_event) + */ + onLoadedMetadata: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Occurs when Internet Explorer begins looking for media data. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/loadstart_event) + */ + onLoadStart: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/lostpointercapture_event) */ + onLostPointerCapture: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; + + /** + * Fires when the user clicks the object with either mouse button. + * @param ev The mouse event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/mousedown_event) + */ + onMouseDown: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/mouseenter_event) */ + onMouseEnter: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/mouseleave_event) */ + onMouseLeave: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; + + /** + * Fires when the user moves the mouse over the object. + * @param ev The mouse event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/mousemove_event) + */ + onMouseMove: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; + + /** + * Fires when the user moves the mouse pointer outside the boundaries of the object. + * @param ev The mouse event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/mouseout_event) + */ + onMouseOut: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; + + /** + * Fires when the user moves the mouse pointer into the object. + * @param ev The mouse event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/mouseover_event) + */ + onMouseOver: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; + + /** + * Fires when the user releases a mouse button while the mouse is over the object. + * @param ev The mouse event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/mouseup_event) + */ + onMouseUp: ((this: GlobalEventHandlers, ev: MouseEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/paste_event) */ + onPaste: ((this: GlobalEventHandlers, ev: ClipboardEvent) => any) | null; + + /** + * Occurs when playback is paused. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/pause_event) + */ + onPause: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Occurs when the play method is requested. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/play_event) + */ + onPlay: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Occurs when the audio or video has started playing. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/playing_event) + */ + onPlaying: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/pointercancel_event) */ + onPointerCancel: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/pointerdown_event) */ + onPointerDown: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/pointerenter_event) */ + onPointerEnter: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/pointerleave_event) */ + onPointerLeave: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/pointermove_event) */ + onPointerMove: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/pointerout_event) */ + onPointerOut: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/pointerover_event) */ + onPointerOver: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/pointerup_event) */ + onPointerUp: ((this: GlobalEventHandlers, ev: PointerEvent) => any) | null; + + /** + * Occurs to indicate progress while downloading media data. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/progress_event) + */ + onProgress: ((this: GlobalEventHandlers, ev: ProgressEvent) => any) | null; + + /** + * Occurs when the playback rate is increased or decreased. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/ratechange_event) + */ + onRateChange: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Fires when the user resets a form. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLFormElement/reset_event) + */ + onReset: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement/resize_event) */ + onResize: ((this: GlobalEventHandlers, ev: UIEvent) => any) | null; + + /** + * Fires when the user repositions the scroll box in the scroll bar on the object. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/scroll_event) + */ + onScroll: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/scrollend_event) */ + onScrollEnd: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/securitypolicyviolation_event) */ + onSecurityPolicyViolation: ((this: GlobalEventHandlers, ev: SecurityPolicyViolationEvent) => any) | null; + + /** + * Occurs when the seek operation ends. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/seeked_event) + */ + onSeeked: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Occurs when the current playback position is moved. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/seeking_event) + */ + onSeeking: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Fires when the current selection changes. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLInputElement/select_event) + */ + onSelect: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/selectionchange_event) */ + onSelectionChange: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Node/selectstart_event) */ + onSelectStart: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLSlotElement/slotchange_event) */ + onSlotChange: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Occurs when the download has stopped. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/stalled_event) + */ + onStalled: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLFormElement/submit_event) */ + onSubmit: ((this: GlobalEventHandlers, ev: SubmitEvent) => any) | null; + + /** + * Occurs if the load operation has been intentionally halted. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/suspend_event) + */ + onSuspend: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Occurs to indicate the current playback position. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/timeupdate_event) + */ + onTimeUpdate: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLDetailsElement/toggle_event) */ + onToggle: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/transitioncancel_event) */ + onTransitionCancel: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/transitionend_event) */ + onTransitionEnd: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/transitionrun_event) */ + onTransitionRun: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/transitionstart_event) */ + onTransitionStart: ((this: GlobalEventHandlers, ev: TransitionEvent) => any) | null; + + /** + * Occurs when the volume is changed, or playback is muted or unmuted. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/volumechange_event) + */ + onVolumeChange: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * Occurs when playback stops because the next frame of a video resource is not available. + * @param ev The event. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLMediaElement/waiting_event) + */ + onWaiting: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * @deprecated This is a legacy alias of `onAnimationEnd`. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/animationend_event) + */ + onWebkitAnimationEnd: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * @deprecated This is a legacy alias of `onAnimationIteration`. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/animationiteration_event) + */ + onWebkitAnimationIteration: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * @deprecated This is a legacy alias of `onAnimationStart`. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/animationstart_event) + */ + onWebkitAnimationStart: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** + * @deprecated This is a legacy alias of `onTransitionEnd`. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/transitionend_event) + */ + onWebkitTransitionEnd: ((this: GlobalEventHandlers, ev: Event) => any) | null; + + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/wheel_event) */ + onWheel: ((this: GlobalEventHandlers, ev: WheelEvent) => any) | null; +} diff --git a/packages/inula-v2/src/types/htmlTag/htmlElement.d.ts b/packages/inula-v2/src/types/htmlTag/htmlElement.d.ts new file mode 100644 index 00000000..80381a4c --- /dev/null +++ b/packages/inula-v2/src/types/htmlTag/htmlElement.d.ts @@ -0,0 +1,33 @@ +import { type Properties } from 'csstype'; + +// ---- Used to determine whether X and Y are equal, return A if equal, otherwise B +type IfEquals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? A : B; + +export type OmitIndexSignature = { + // eslint-disable-next-line @typescript-eslint/ban-types + [KeyType in keyof ObjectType as {} extends Record ? never : KeyType]: ObjectType[KeyType]; +}; + +// ---- For each key, check whether there is readonly, if there is, return never, and then Pick out is not never +type WritableKeysOf = { + [P in keyof T]: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P, never>; +}[keyof T]; +type RemoveReadOnly = Pick>; + +// ---- Delete all functions +type OmitFunction = Omit any ? K : never }[keyof T]>; + +type OmitFuncAndReadOnly = RemoveReadOnly>>; + +// ---- properties +type OmitFuncAndReadOnlyProperty = Omit, 'className' | 'htmlFor' | 'style' | 'innerText'>; + +type CustomCSSProperties = { + [Key in `--${string}`]: string | number; +}; + +export type HTMLAttributes = OmitFuncAndReadOnlyProperty & { + style: Properties & CustomCSSProperties; + class: string; + for: string; +}; diff --git a/packages/inula-v2/src/types/htmlTag/index.d.ts b/packages/inula-v2/src/types/htmlTag/index.d.ts new file mode 100644 index 00000000..2f75cd64 --- /dev/null +++ b/packages/inula-v2/src/types/htmlTag/index.d.ts @@ -0,0 +1,236 @@ +import type { DLightGlobalEventHandlers } from './event'; +import type { OmitIndexSignature, HTMLAttributes } from './htmlElement'; + +// ---- If there is an event(start with on), remove it +export type PropertyWithEvent = Omit< + G, + { + [K in keyof G]: K extends `on${string}` ? K : never; + }[keyof G] +> & + DLightGlobalEventHandlers; + +interface DLightHtmlProps { + ref: El | ((holder: El) => void) | undefined; + prop: Record; + attr: Record; + dataset: Record; + forwardProps: true | undefined; + willMount: (el: El) => void; + didMount: (el: El) => void; + willUnmount: (el: El) => void; + didUnmount: (el: El) => void; + didUpdate: (el: El, key: string, prevValue: T, currValue: T) => void; +} + +export type DLightHTMLAttributes = DLightHtmlProps & HTMLAttributes & G; + +export type DLightHTMLAttributesFunc = { + [K in keyof DLightHTMLAttributes]: ( + value?: DLightHTMLAttributes[K] + ) => Omit, K>; +}; + +export type DLightHtmlTagFunc = ( + innerText?: string | number | ((View: never) => void) +) => DLightHTMLAttributesFunc>, G, T>; + +export const a: DLightHtmlTagFunc; +export const abbr: DLightHtmlTagFunc; +export const address: DLightHtmlTagFunc; +export const area: DLightHtmlTagFunc; +export const article: DLightHtmlTagFunc; +export const aside: DLightHtmlTagFunc; +export const audio: DLightHtmlTagFunc; +export const b: DLightHtmlTagFunc; +export const base: DLightHtmlTagFunc; +export const bdi: DLightHtmlTagFunc; +export const bdo: DLightHtmlTagFunc; +export const blockquote: DLightHtmlTagFunc; +export const body: DLightHtmlTagFunc; +export const br: DLightHtmlTagFunc; +export const button: DLightHtmlTagFunc; +export const canvas: DLightHtmlTagFunc; +export const caption: DLightHtmlTagFunc; +export const cite: DLightHtmlTagFunc; +export const code: DLightHtmlTagFunc; +export const col: DLightHtmlTagFunc; +export const colgroup: DLightHtmlTagFunc; +export const data: DLightHtmlTagFunc; +export const datalist: DLightHtmlTagFunc; +export const dd: DLightHtmlTagFunc; +export const del: DLightHtmlTagFunc; +export const details: DLightHtmlTagFunc; +export const dfn: DLightHtmlTagFunc; +export const dialog: DLightHtmlTagFunc; +export const div: DLightHtmlTagFunc; +export const dl: DLightHtmlTagFunc; +export const dt: DLightHtmlTagFunc; +export const em: DLightHtmlTagFunc; +export const embed: DLightHtmlTagFunc; +export const fieldset: DLightHtmlTagFunc; +export const figcaption: DLightHtmlTagFunc; +export const figure: DLightHtmlTagFunc; +export const footer: DLightHtmlTagFunc; +export const form: DLightHtmlTagFunc; +export const h1: DLightHtmlTagFunc; +export const h2: DLightHtmlTagFunc; +export const h3: DLightHtmlTagFunc; +export const h4: DLightHtmlTagFunc; +export const h5: DLightHtmlTagFunc; +export const h6: DLightHtmlTagFunc; +export const head: DLightHtmlTagFunc; +export const header: DLightHtmlTagFunc; +export const hgroup: DLightHtmlTagFunc; +export const hr: DLightHtmlTagFunc; +export const html: DLightHtmlTagFunc; +export const i: DLightHtmlTagFunc; +export const iframe: DLightHtmlTagFunc; +export const img: DLightHtmlTagFunc; +export const input: DLightHtmlTagFunc; +export const ins: DLightHtmlTagFunc; +export const kbd: DLightHtmlTagFunc; +export const label: DLightHtmlTagFunc; +export const legend: DLightHtmlTagFunc; +export const li: DLightHtmlTagFunc; +export const link: DLightHtmlTagFunc; +export const main: DLightHtmlTagFunc; +export const map: DLightHtmlTagFunc; +export const mark: DLightHtmlTagFunc; +export const menu: DLightHtmlTagFunc; +export const meta: DLightHtmlTagFunc; +export const meter: DLightHtmlTagFunc; +export const nav: DLightHtmlTagFunc; +export const noscript: DLightHtmlTagFunc; +export const object: DLightHtmlTagFunc; +export const ol: DLightHtmlTagFunc; +export const optgroup: DLightHtmlTagFunc; +export const option: DLightHtmlTagFunc; +export const output: DLightHtmlTagFunc; +export const p: DLightHtmlTagFunc; +export const picture: DLightHtmlTagFunc; +export const pre: DLightHtmlTagFunc; +export const progress: DLightHtmlTagFunc; +export const q: DLightHtmlTagFunc; +export const rp: DLightHtmlTagFunc; +export const rt: DLightHtmlTagFunc; +export const ruby: DLightHtmlTagFunc; +export const s: DLightHtmlTagFunc; +export const samp: DLightHtmlTagFunc; +export const script: DLightHtmlTagFunc; +export const section: DLightHtmlTagFunc; +export const select: DLightHtmlTagFunc; +export const slot: DLightHtmlTagFunc; +export const small: DLightHtmlTagFunc; +export const source: DLightHtmlTagFunc; +export const span: DLightHtmlTagFunc; +export const strong: DLightHtmlTagFunc; +export const style: DLightHtmlTagFunc; +export const sub: DLightHtmlTagFunc; +export const summary: DLightHtmlTagFunc; +export const sup: DLightHtmlTagFunc; +export const table: DLightHtmlTagFunc; +export const tbody: DLightHtmlTagFunc; +export const td: DLightHtmlTagFunc; +export const template: DLightHtmlTagFunc; +export const textarea: DLightHtmlTagFunc; +export const tfoot: DLightHtmlTagFunc; +export const th: DLightHtmlTagFunc; +export const thead: DLightHtmlTagFunc; +export const time: DLightHtmlTagFunc; +export const title: DLightHtmlTagFunc; +export const tr: DLightHtmlTagFunc; +export const track: DLightHtmlTagFunc; +export const u: DLightHtmlTagFunc; +export const ul: DLightHtmlTagFunc; +export const var_: DLightHtmlTagFunc; +export const video: DLightHtmlTagFunc; +export const wbr: DLightHtmlTagFunc; +export const acronym: DLightHtmlTagFunc; +export const applet: DLightHtmlTagFunc; +export const basefont: DLightHtmlTagFunc; +export const bgsound: DLightHtmlTagFunc; +export const big: DLightHtmlTagFunc; +export const blink: DLightHtmlTagFunc; +export const center: DLightHtmlTagFunc; +export const dir: DLightHtmlTagFunc; +export const font: DLightHtmlTagFunc; +export const frame: DLightHtmlTagFunc; +export const frameset: DLightHtmlTagFunc; +export const isindex: DLightHtmlTagFunc; +export const keygen: DLightHtmlTagFunc; +export const listing: DLightHtmlTagFunc; +export const marquee: DLightHtmlTagFunc; +export const menuitem: DLightHtmlTagFunc; +export const multicol: DLightHtmlTagFunc; +export const nextid: DLightHtmlTagFunc; +export const nobr: DLightHtmlTagFunc; +export const noembed: DLightHtmlTagFunc; +export const noframes: DLightHtmlTagFunc; +export const param: DLightHtmlTagFunc; +export const plaintext: DLightHtmlTagFunc; +export const rb: DLightHtmlTagFunc; +export const rtc: DLightHtmlTagFunc; +export const spacer: DLightHtmlTagFunc; +export const strike: DLightHtmlTagFunc; +export const tt: DLightHtmlTagFunc; +export const xmp: DLightHtmlTagFunc; +export const animate: DLightHtmlTagFunc; +export const animateMotion: DLightHtmlTagFunc; +export const animateTransform: DLightHtmlTagFunc; +export const circle: DLightHtmlTagFunc; +export const clipPath: DLightHtmlTagFunc; +export const defs: DLightHtmlTagFunc; +export const desc: DLightHtmlTagFunc; +export const ellipse: DLightHtmlTagFunc; +export const feBlend: DLightHtmlTagFunc; +export const feColorMatrix: DLightHtmlTagFunc; +export const feComponentTransfer: DLightHtmlTagFunc; +export const feComposite: DLightHtmlTagFunc; +export const feConvolveMatrix: DLightHtmlTagFunc; +export const feDiffuseLighting: DLightHtmlTagFunc; +export const feDisplacementMap: DLightHtmlTagFunc; +export const feDistantLight: DLightHtmlTagFunc; +export const feDropShadow: DLightHtmlTagFunc; +export const feFlood: DLightHtmlTagFunc; +export const feFuncA: DLightHtmlTagFunc; +export const feFuncB: DLightHtmlTagFunc; +export const feFuncG: DLightHtmlTagFunc; +export const feFuncR: DLightHtmlTagFunc; +export const feGaussianBlur: DLightHtmlTagFunc; +export const feImage: DLightHtmlTagFunc; +export const feMerge: DLightHtmlTagFunc; +export const feMergeNode: DLightHtmlTagFunc; +export const feMorphology: DLightHtmlTagFunc; +export const feOffset: DLightHtmlTagFunc; +export const fePointLight: DLightHtmlTagFunc; +export const feSpecularLighting: DLightHtmlTagFunc; +export const feSpotLight: DLightHtmlTagFunc; +export const feTile: DLightHtmlTagFunc; +export const feTurbulence: DLightHtmlTagFunc; +export const filter: DLightHtmlTagFunc; +export const foreignObject: DLightHtmlTagFunc; +export const g: DLightHtmlTagFunc; +export const image: DLightHtmlTagFunc; +export const line: DLightHtmlTagFunc; +export const linearGradient: DLightHtmlTagFunc; +export const marker: DLightHtmlTagFunc; +export const mask: DLightHtmlTagFunc; +export const metadata: DLightHtmlTagFunc; +export const mpath: DLightHtmlTagFunc; +export const path: DLightHtmlTagFunc; +export const pattern: DLightHtmlTagFunc; +export const polygon: DLightHtmlTagFunc; +export const polyline: DLightHtmlTagFunc; +export const radialGradient: DLightHtmlTagFunc; +export const rect: DLightHtmlTagFunc; +export const set: DLightHtmlTagFunc; +export const stop: DLightHtmlTagFunc; +export const svg: DLightHtmlTagFunc; +export const switch_: DLightHtmlTagFunc; +export const symbol: DLightHtmlTagFunc; +export const text: DLightHtmlTagFunc; +export const textPath: DLightHtmlTagFunc; +export const tspan: DLightHtmlTagFunc; +// export const use: DLightHtmlTagFunc +export const view: DLightHtmlTagFunc; diff --git a/packages/inula-v2/src/types/index.d.ts b/packages/inula-v2/src/types/index.d.ts new file mode 100644 index 00000000..9bc1e12c --- /dev/null +++ b/packages/inula-v2/src/types/index.d.ts @@ -0,0 +1,41 @@ +import { type Typed } from './compTag'; +import { type DLightHtmlTagFunc } from './htmlTag'; +export { type Properties as CSSProperties } from 'csstype'; + +export const comp: (tag: T) => object extends T ? any : Typed; +export const tag: (tag: any) => DLightHtmlTagFunc; + +export { _ } from './expressionTag'; +export * from './htmlTag'; +export * from './compTag'; +export * from './envTag'; +export * from './model'; +export const Static: any; +export const Children: any; +export const Content: any; +export const Prop: any; +export const Env: any; +export const Watch: any; +export const ForwardProps: any; +export const Main: any; +export const App: any; +export const Mount: (idOrEl: string | HTMLElement) => any; + +// ---- With actual value +export function render(idOrEl: string | HTMLElement, DL: any): void; +export function manual(callback: () => T, _deps?: any[]): T; +export function escape(arg: T): T; +export function setGlobal(globalObj: any): void; +export function setDocument(customDocument: any): void; +export const $: typeof escape; +export const View: any; +export const Snippet: any; +export const Model: any; +export const update: any; +export const required: any; +export function insertChildren(parent: T, children: DLightViewProp): void; + +// ---- View types +export type DLightViewComp = Typed; +export type DLightViewProp = (View: any) => void; +export type DLightViewLazy = () => Promise<{ default: T }>; diff --git a/packages/inula-v2/src/types/model.d.ts b/packages/inula-v2/src/types/model.d.ts new file mode 100644 index 00000000..ac4a4c0b --- /dev/null +++ b/packages/inula-v2/src/types/model.d.ts @@ -0,0 +1,22 @@ +import { ContentKeyName, ContentProp } from './compTag'; + +type RemoveDLightInternal = Omit; + +export type Modeling = (props: Props) => Model; + +type GetProps = keyof T extends never ? never : ContentKeyName extends undefined ? T : Omit>; + +type GetContent = + ContentKeyName extends undefined ? never : T[ContentKeyName] extends ContentProp ? U : never; + +export const use: ( + model: M, + // @ts-expect-error Model should be a function + props?: GetProps[0]>, + // @ts-expect-error Model should be a function + content?: GetContent[0]> + // @ts-expect-error Model should be a function +) => RemoveDLightInternal, Parameters[0]>; + +// @ts-expect-error Model should be a function +export type ModelType = RemoveDLightInternal, Parameters[0]>; diff --git a/packages/inula-v2/tsconfig.json b/packages/inula-v2/tsconfig.json new file mode 100644 index 00000000..e0932d78 --- /dev/null +++ b/packages/inula-v2/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "esModuleInterop": true + }, + "ts-node": { + "esm": true + } +} \ No newline at end of file diff --git a/packages/inula/scripts/rollup/rollup.config.js b/packages/inula/scripts/rollup/rollup.config.js index 9228bb45..4e28e96d 100644 --- a/packages/inula/scripts/rollup/rollup.config.js +++ b/packages/inula/scripts/rollup/rollup.config.js @@ -109,13 +109,16 @@ function genConfig(mode) { function genJSXRuntimeConfig(mode) { return { input: path.resolve(libDir, 'src', 'jsx-runtime.ts'), - output: [{ - file: outputResolve('jsx-runtime.js'), - format: 'cjs', - }, { - file: outputResolve('jsx-runtime.esm-browser.js'), - format: 'esm', - }], + output: [ + { + file: outputResolve('jsx-runtime.js'), + format: 'cjs', + }, + { + file: outputResolve('jsx-runtime.esm-browser.js'), + format: 'esm', + }, + ], plugins: [...getBasicPlugins(mode)], }; } diff --git a/packages/inula/src/inulax/types.ts b/packages/inula/src/inulax/types.ts index 2814218c..044d7abb 100644 --- a/packages/inula/src/inulax/types.ts +++ b/packages/inula/src/inulax/types.ts @@ -31,7 +31,11 @@ export interface IObserver { clearByVNode: (vNode: any) => void; } -export type StoreConfig, A extends UserActions, C extends UserComputedValues> = { +export type StoreConfig< + S extends Record, + A extends UserActions, + C extends UserComputedValues, +> = { id?: string; state?: S; actions?: A; @@ -45,7 +49,11 @@ export type UserActions> = { [K: string]: ActionFunction; }; -export type ActionFunction> = (this: StoreObj, state: S, ...args: any[]) => any; +export type ActionFunction> = ( + this: StoreObj, + state: S, + ...args: any[] +) => any; export type StoreActions, A extends UserActions> = { [K in keyof A]: Action; diff --git a/packages/transpiler/class-transformer/package.json b/packages/transpiler/class-transformer/package.json new file mode 100644 index 00000000..7d6c5690 --- /dev/null +++ b/packages/transpiler/class-transformer/package.json @@ -0,0 +1,43 @@ +{ + "name": "@inula/class-transformer", + "version": "0.0.0", + "description": "Inula view generator", + "keywords": [ + "inula" + ], + "files": [ + "dist" + ], + "type": "module", + "main": "dist/index.cjs", + "module": "dist/index.js", + "typings": "dist/index.d.ts", + "scripts": { + "build": "tsup --sourcemap", + "test": "vitest --ui" + }, + "devDependencies": { + "@babel/core": "^7.20.12", + "@babel/generator": "^7.23.6", + "@babel/traverse": "^7.24.1", + "@babel/plugin-syntax-jsx": "7.16.7", + "@types/babel__core": "^7.20.5", + "@types/babel__generator": "^7.6.8", + "@types/babel__traverse": "^7.6.8", + "@vitest/ui": "^0.34.5", + "tsup": "^6.7.0", + "typescript": "^5.3.2", + "vitest": "^0.34.5" + }, + "tsup": { + "entry": [ + "src/index.ts" + ], + "format": [ + "cjs", + "esm" + ], + "clean": true, + "dts": true + } +} diff --git a/packages/transpiler/class-transformer/src/index.ts b/packages/transpiler/class-transformer/src/index.ts new file mode 100644 index 00000000..4f8bb0ab --- /dev/null +++ b/packages/transpiler/class-transformer/src/index.ts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openInula is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { Option } from './types'; +import type { ConfigAPI, TransformOptions } from '@babel/core'; +import transformer from './plugin'; + +export default function (_: ConfigAPI, options: Option): TransformOptions { + return { + plugins: [ + ['@babel/plugin-syntax-jsx'], + ['@babel/plugin-syntax-typescript', { isTSX: true }], + [transformer, options], + ], + }; +} +export const plugin = transformer; + +export type { Option }; diff --git a/packages/transpiler/class-transformer/src/plugin.ts b/packages/transpiler/class-transformer/src/plugin.ts new file mode 100644 index 00000000..3c035355 --- /dev/null +++ b/packages/transpiler/class-transformer/src/plugin.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openInula is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { PluginObj } from '@babel/core'; +import { Option } from './types'; +import * as babel from '@babel/core'; +import { PluginProvider } from './pluginProvider'; + +export default function (api: typeof babel, options: Option): PluginObj { + const pluginProvider = new PluginProvider(api, options); + + return { + name: 'zouyu-2', + visitor: { + FunctionDeclaration(path) { + pluginProvider.functionDeclarationVisitor(path); + }, + }, + }; +} diff --git a/packages/transpiler/class-transformer/src/pluginProvider.ts b/packages/transpiler/class-transformer/src/pluginProvider.ts new file mode 100644 index 00000000..a0b54c70 --- /dev/null +++ b/packages/transpiler/class-transformer/src/pluginProvider.ts @@ -0,0 +1,318 @@ +import { type types as t, NodePath } from '@babel/core'; +import * as babel from '@babel/core'; +import { Option } from './types'; +import type { Scope } from '@babel/traverse'; + +function replaceFnWithClass(path: NodePath, classTransformer: ClassComponentTransformer) { + const originalName = path.node.id.name; + const tempName = path.node.id.name + 'Temp'; + const classComp = classTransformer.genClassComponent(tempName); + path.replaceWith(classComp); + path.scope.rename(tempName, originalName); +} + +export class PluginProvider { + // ---- Plugin Level ---- + private readonly babelApi: typeof babel; + private readonly t: typeof t; + + private programNode: t.Program | undefined; + + constructor(babelApi: typeof babel, options: Option) { + this.babelApi = babelApi; + this.t = babelApi.types; + } + + functionDeclarationVisitor(path: NodePath): void { + // find Component function by: + // 1. has JSXElement as return value + // 2. name is capitalized + if (path.node.id?.name[0] !== path.node.id?.name[0].toUpperCase()) return; + const returnStatement = path.node.body.body.find(n => this.t.isReturnStatement(n)) as t.ReturnStatement; + if (!returnStatement) return; + if (!(this.t.isJSXElement(returnStatement.argument) || this.t.isJSXFragment(returnStatement.argument))) return; + const classTransformer = new ClassComponentTransformer(this.babelApi, path); + // transform the parameters to props + const params = path.node.params; + const props = params[0]; + classTransformer.transformProps(props); + // iterate the function body orderly + const body = path.node.body.body; + body.forEach((node, idx) => { + if (this.t.isVariableDeclaration(node)) { + classTransformer.transformStateDeclaration(node); + return; + } + // handle method + if (this.t.isFunctionDeclaration(node)) { + classTransformer.transformMethods(node); + return; + } + + // handle watch + if (classTransformer.shouldTransformWatch(node)) { + // transform the watch statement to watch method + classTransformer.transformWatch(node); + return; + } + + // handle return statement + if (this.t.isReturnStatement(node)) { + // handle early return + if (idx !== body.length - 1) { + // transform the return statement to render method + // TODO: handle early return + throw new Error('Early return is not supported yet.'); + } + // transform the return statement to render method + classTransformer.transformRenderMethod(node); + return; + } + }); + + // replace the function declaration with class declaration + replaceFnWithClass(path, classTransformer); + } +} + +type ToWatchNode = + | t.ExpressionStatement + | t.ForStatement + | t.WhileStatement + | t.IfStatement + | t.SwitchStatement + | t.TryStatement; + +class ClassComponentTransformer { + properties: (t.ClassProperty | t.ClassMethod)[] = []; + private readonly babelApi: typeof babel; + private readonly t: typeof t; + private readonly functionScope: Scope; + + valueWrapper(node) { + return this.t.file(this.t.program([this.t.isStatement(node) ? node : this.t.expressionStatement(node)])); + } + + addProperty(prop: t.ClassProperty | t.ClassMethod, name?: string) { + this.properties.push(prop); + if (name) { + // replace the variable in scope to process the variable in class scope + // e.g. replace () => count++ to () => this.count++ + // TODO: search for better solution + this.functionScope.rename(name, `this.${name}`); + this.functionScope.path.traverse({ + Identifier: path => { + if (path.node.name === `this.${name}`) { + path.replaceWith(this.t.memberExpression(this.t.thisExpression(), this.t.identifier(name))); + } + }, + }); + } + } + + constructor(babelApi: typeof babel, fnNode: NodePath) { + this.babelApi = babelApi; + this.t = babelApi.types; + // get the function body scope + this.functionScope = fnNode.scope; + } + + // transform function component to class component extends View + genClassComponent(name: string) { + return this.t.classDeclaration( + this.t.identifier(name), + this.t.identifier('View'), + this.t.classBody(this.properties), + [] + ); + } + + /** + * Transform state declaration to class property + * if the variable is declared with `let` or `const`, it should be transformed to class property + * @param node + */ + transformStateDeclaration(node: t.VariableDeclaration) { + // iterate the declarations + node.declarations.forEach(declaration => { + const id = declaration.id; + // handle destructuring + if (this.t.isObjectPattern(id)) { + return this.transformPropsDestructuring(id); + } else if (this.t.isArrayPattern(id)) { + // TODO: handle array destructuring + } else if (this.t.isIdentifier(id)) { + // clone the id + const cloneId = this.t.cloneNode(id); + this.addProperty(this.t.classProperty(cloneId, declaration.init), id.name); + } + }); + } + + /** + * Transform render method to Body method + * The Body method should return the original return statement + * @param node + */ + transformRenderMethod(node: t.ReturnStatement) { + const body = this.t.classMethod( + 'method', + this.t.identifier('Body'), + [], + this.t.blockStatement([node]), + false, + false + ); + this.addProperty(body, 'Body'); + } + + transformLifeCycle() {} + + transformComputed() {} + + transformMethods(node: t.FunctionDeclaration) { + // transform the function declaration to class method + const methodName = node.id?.name; + if (!methodName) return; + const method = this.t.classMethod( + 'method', + this.t.identifier(methodName), + node.params, + node.body, + node.generator, + node.async + ); + this.addProperty(method, methodName); + } + + transformProps(param: t.Identifier | t.RestElement | t.Pattern) { + if (!param) return; + // handle destructuring + if (this.isObjDestructuring(param)) { + this.transformPropsDestructuring(param); + return; + } + if (this.t.isIdentifier(param)) { + // TODO: handle props identifier + return; + } + throw new Error('Unsupported props type, please use object destructuring or identifier.'); + } + + // transform node to method with watch decorator + transformWatch(node: ToWatchNode) { + const id = this.functionScope.generateUidIdentifier('watch'); + const method = this.t.classMethod('method', id, [], this.t.blockStatement([node]), false, false); + method.decorators = [this.t.decorator(this.t.identifier('Watch'))]; + this.addProperty(method); + } + + private isObjDestructuring(param: t.Identifier | t.RestElement | t.Pattern): param is t.ObjectPattern { + return this.t.isObjectPattern(param); + } + + private transformPropsDestructuring(param: t.ObjectPattern) { + const propNames: t.Identifier[] = []; + param.properties.forEach(prop => { + if (this.t.isObjectProperty(prop)) { + const key = prop.key; + if (this.t.isIdentifier(key)) { + if (this.t.isAssignmentPattern(prop.value)) { + // handle default value + const defaultValue = prop.value.right; + this.addProp(key, defaultValue); + propNames.push(key); + return; + } else if (this.t.isIdentifier(prop.value)) { + // handle simple destructuring + this.addProp(key, undefined, prop.value.name === 'children'); + propNames.push(key); + return; + } else if (this.t.isObjectPattern(prop.value)) { + // TODO: handle nested destructuring + this.transformPropsDestructuring(prop.value); + return; + } + return; + } + // handle default value + if (this.t.isAssignmentPattern(prop.value)) { + const defaultValue = prop.value.right; + const propName = prop.value.left; + if (this.t.isIdentifier(propName)) { + this.addProp(propName, defaultValue); + propNames.push(propName); + } + // TODO: handle nested destructuring + return; + } + throw new Error('Unsupported props destructuring, please use simple object destructuring.'); + } else { + // TODO: handle rest element + } + }); + + return propNames; + } + + // add prop to class, like @prop name = ''; + private addProp(key: t.Identifier, defaultValue?: t.Expression, isChildren = false) { + // clone the key to avoid reference issue + const id = this.t.cloneNode(key); + this.addProperty( + this.t.classProperty( + id, + defaultValue ?? undefined, + undefined, + // use prop decorator + [this.t.decorator(this.t.identifier(isChildren ? 'Children' : 'Prop'))], + undefined, + false + ), + key.name + ); + } + + /** + * Check if the node should be transformed to watch method, including: + * 1. call expression. + * 2. for loop + * 3. while loop + * 4. if statement + * 5. switch statement + * 6. assignment expression + * 7. try statement + * 8. ++/-- expression + * @param node + */ + shouldTransformWatch(node: t.Node): node is ToWatchNode { + if (this.t.isExpressionStatement(node)) { + if (this.t.isCallExpression(node.expression)) { + return true; + } + if (this.t.isAssignmentExpression(node.expression)) { + return true; + } + if (this.t.isUpdateExpression(node.expression)) { + return true; + } + } + if (this.t.isForStatement(node)) { + return true; + } + if (this.t.isWhileStatement(node)) { + return true; + } + if (this.t.isIfStatement(node)) { + return true; + } + if (this.t.isSwitchStatement(node)) { + return true; + } + if (this.t.isTryStatement(node)) { + return true; + } + + return false; + } +} diff --git a/packages/transpiler/class-transformer/src/test/component-composition.test.ts b/packages/transpiler/class-transformer/src/test/component-composition.test.ts new file mode 100644 index 00000000..b40380ab --- /dev/null +++ b/packages/transpiler/class-transformer/src/test/component-composition.test.ts @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openInula is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { describe, expect, it } from 'vitest'; +import { transform } from './transform'; + +describe('component-composition', () => { + describe('props destructuring', () => { + it('should support default values', () => { + //language=JSX + expect( + transform(` + function UserProfile({ + name = '', + age = null, + favouriteColors = [], + isAvailable = false, + }) { + return ( + <> +

My name is {name}!

+

My age is {age}!

+

My favourite colors are {favouriteColors.join(', ')}!

+

I am {isAvailable ? 'available' : 'not available'}

+ + ); + }`), + ` + class UserProfile { + @Prop name = '' + @Prop age = null + @Prop favouriteColors = [] + @Prop isAvailable = false + + Body() { + p(\`My name is \${this.name}!\`) + p(\`My age is \${this.age}!\`) + p(\`My favourite colors are \${this.favouriteColors.join(', ')}!\`) + p(\`I am \${this.isAvailable ? 'available' : 'not available'}\`) + } + } + ` + ); + }); + + it('should support nested destruing', () => { + //language=JSX + expect( + transform(` + function UserProfile({ + name = '', + age = null, + favouriteColors : [{r,g,b}, color2], + isAvailable = false, + }) { + return ( + <> +

My name is {name}!

+

My age is {age}!

+

My favourite colors are {favouriteColors.join(', ')}!

+

I am {isAvailable ? 'available' : 'not available'}

+ + ); + }`), + ` + class UserProfile { + @Prop name = ''; + @Prop age = null; + @Prop favouriteColors = []; + @Prop isAvailable = false; + color1; + color2; + r; + g; + b; + xx = (() => { + const [{r, g, b},color2] = this.favouriteColors; + this.r = r + this.g = g + this.b = b + this.color2 = color2 + }); + + Body() { + p(\`My name is \${this.name}!\`); + p(\`My age is \${this.age}!\`); + p(\`My favourite colors are \${this.favouriteColors.join(', ')}!\`); + p(\`I am \${this.isAvailable ? 'available' : 'not available'}\`); + } + } + ` + ); + }); + + it('should support children prop', () => { + //language=JSX + expect( + transform(` + function Card({ children }) { + return ( +
+ {children} +
+ ); + }`), + ` + class Card { + @Children children + + Body() { + div(\`card\`, this.children) + } + } + ` + ); + }); + }); + + it('should support children prop with alias', () => { + //language=JSX + expect( + transform(` + function Card({ children: content }) { + return ( +
+ {children} +
+ ); + }`), + ` + class Card { + @Children content + + Body() { + div(\`card\`, this.children) + } + } + ` + ); + }); +}); diff --git a/packages/transpiler/class-transformer/src/test/conditional.test.ts b/packages/transpiler/class-transformer/src/test/conditional.test.ts new file mode 100644 index 00000000..f2d816fb --- /dev/null +++ b/packages/transpiler/class-transformer/src/test/conditional.test.ts @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 Huawei Technologies Co.,Ltd. + * + * openInula is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +import { test, it, expect } from 'vitest'; +import { transform } from './transform'; + +test('conditional', () => {}); diff --git a/packages/transpiler/class-transformer/src/test/fn2Class.test.ts b/packages/transpiler/class-transformer/src/test/fn2Class.test.ts new file mode 100644 index 00000000..55ea9d87 --- /dev/null +++ b/packages/transpiler/class-transformer/src/test/fn2Class.test.ts @@ -0,0 +1,299 @@ +import { it, describe, expect } from 'vitest'; +import { transform } from './transform'; + +describe('fn2Class', () => { + it('should transform state assignment', () => { + expect( + //language=JSX + transform(` + export default function Name() { + let name = 'John'; + + return

{name}

; + } + `) + ).toMatchInlineSnapshot(` + "class Name extends View { + name = 'John'; + Body() { + return

{this.name}

; + } + } + export { Name as default };" + `); + }); + + it('should transform state modification ', () => { + expect( + transform(` + function MyApp() { + let count = 0; + return
count++}>{count}
+ } + `) + ).toMatchInlineSnapshot(` + "class MyApp extends View { + count = 0; + Body() { + return
this.count++}>{this.count}
; + } + }" + `); + }); + + it('should not transform variable out of scope', () => { + expect( + //language=JSX + transform(` + const name = "John"; + export default function Name() { + return

{name}

; + } + `) + ).toMatchInlineSnapshot(` + "const name = \\"John\\"; + class Name extends View { + Body() { + return

{name}

; + } + } + export { Name as default };" + `); + }); + + it('should transform function declaration', () => { + expect( + //language=JSX + transform(` + const name = "John"; + + function Name() { + function getName() { + return name; + } + + const onClick = () => { + console.log(getName()); + } + return

{name}

; + } + `) + ).toMatchInlineSnapshot(` + "const name = \\"John\\"; + class Name extends View { + getName() { + return name; + } + onClick = () => { + console.log(this.getName()); + }; + Body() { + return

{name}

; + } + }" + `); + }); + + it('should not transform function parameter to this', () => { + expect( + //language=JSX + transform(` + function Name() { + let name = 'Doe' + + function getName(name) { + return name + '!' + } + + const onClick = () => { + console.log(getName('John')); + } + return

{name}

; + } + `) + ).toMatchInlineSnapshot(` + "class Name extends View { + name = 'Doe'; + getName(name) { + return name + '!'; + } + onClick = () => { + console.log(this.getName('John')); + }; + Body() { + return

{this.name}

; + } + }" + `); + }); + + it('should not transform constant data', () => { + expect( + //language=JSX + transform(` + const name = "John"; + export default function Name() { + return

{name}

; + } + `) + ).toMatchInlineSnapshot(` + "const name = \\"John\\"; + class Name extends View { + Body() { + return

{name}

; + } + } + export { Name as default };" + `); + }); + + it('should transform derived assignment', () => { + expect( + //language=JSX + transform(` + export default function NameComp() { + let firstName = "John"; + let lastName = "Doe"; + let fullName = \`\${firstName} \${lastName}\` + + return

{fullName}

; + } + `) + ).toMatchInlineSnapshot(` + "class NameComp extends View { + firstName = \\"John\\"; + lastName = \\"Doe\\"; + fullName = \`\${this.firstName} \${this.lastName}\`; + Body() { + return

{this.fullName}

; + } + } + export { NameComp as default };" + `); + }); + + it('should transform watch from call expression', () => { + expect( + //language=JSX + transform(` + export default function CountComp() { + let count = 0; + console.log(count); + + return
{count}
; + } + `) + ).toMatchInlineSnapshot(` + "class CountComp extends View { + count = 0; + @Watch + _watch() { + console.log(this.count); + } + Body() { + return
{this.count}
; + } + } + export { CountComp as default };" + `); + }); + + it('should transform watch from block statement', () => { + expect( + //language=JSX + transform(` + export default function CountComp() { + let count = 0; + let countDown; + let color + for (let i = 0; i < count; i++) { + console.log(\`The count change to: \${i}\`); + } + for (let i = 0; i < dbCount; i++) { + console.log('color changed:', getColor()); + } + function () + return <> + +
{count}
+ ; + }; + `) + ).toMatchInlineSnapshot( + ` + "class CountComp extends View { + count = 0; + @Watch + _watch() { + for (let i = 0; i < this.count; i++) { + console.log(\`The count change to: \${i}\`); + } + } + Body() { + return <> + +
{this.count}
+ ; + } + } + export { CountComp as default }; + ;" + ` + ); + }); + + it('should transform watch from if statement', () => { + expect( + //language=JSX + transform(` + export default function CountComp() { + let count = 0; + if (count > 0) { + console.log(\`The count is greater than 0\`); + } + + return
{count}
; + } + `) + ).toMatchInlineSnapshot(` + "class CountComp extends View { + count = 0; + @Watch + _watch() { + if (this.count > 0) { + console.log(\`The count is greater than 0\`); + } + } + Body() { + return
{this.count}
; + } + } + export { CountComp as default };" + `); + }); + + it('should transform function component reactively', () => { + expect( + transform(` + function MyComp() { + let count = 0 + return <> +

Hello dlight fn, {count}

+ + + + + +

Condition

+ 1}>{count} is bigger than is 1 + {count} is smaller than 1 + + + `) + ); + }); +}); diff --git a/packages/transpiler/jsx-parser/src/test/TemplateUnit.test.ts b/packages/transpiler/jsx-parser/src/test/TemplateUnit.test.ts new file mode 100644 index 00000000..671def94 --- /dev/null +++ b/packages/transpiler/jsx-parser/src/test/TemplateUnit.test.ts @@ -0,0 +1,82 @@ +import { describe, expect, it } from 'vitest'; +import { parse } from './mock'; +import { types as t } from '@babel/core'; +import type { HTMLUnit, TemplateUnit } from '../index'; + +describe('TemplateUnit', () => { + // ---- Type + it('should not parse a single HTMLUnit to a TemplateUnit', () => { + const viewUnits = parse('
'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('html'); + }); + + it('should parse a nested HTMLUnit to a TemplateUnit', () => { + const viewUnits = parse('
'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('template'); + }); + + it("should correctly parse a nested HTMLUnit's structure into a template", () => { + const viewUnits = parse('
'); + const template = (viewUnits[0] as TemplateUnit).template; + + expect(t.isStringLiteral(template.tag, { value: 'div' })).toBeTruthy(); + expect(template.children).toHaveLength(1); + const firstChild = template.children![0] as HTMLUnit; + expect(t.isStringLiteral(firstChild.tag, { value: 'div' })).toBeTruthy(); + }); + + // ---- Props + it("should correctly parse the path of TemplateUnit's dynamic props in root element", () => { + const viewUnits = parse('
'); + const dynamicProps = (viewUnits[0] as TemplateUnit).props; + + expect(dynamicProps).toHaveLength(1); + const prop = dynamicProps[0]; + expect(prop.path).toHaveLength(0); + }); + + it("should correctly parse the path of TemplateUnit's dynamic props in nested element", () => { + const viewUnits = parse('
'); + const dynamicProps = (viewUnits[0] as TemplateUnit).props!; + + expect(dynamicProps).toHaveLength(1); + const prop = dynamicProps[0]!; + expect(prop.path).toHaveLength(1); + expect(prop.path[0]).toBe(0); + }); + + it("should correctly parse the path of TemplateUnit's dynamic props with mutable particles ahead", () => { + const viewUnits = parse('
'); + const dynamicProps = (viewUnits[0] as TemplateUnit).props!; + + expect(dynamicProps).toHaveLength(1); + const prop = dynamicProps[0]!; + expect(prop.path).toHaveLength(1); + expect(prop.path[0]).toBe(1); + }); + + it("should correctly parse the path of TemplateUnit's mutableUnits", () => { + const viewUnits = parse('
'); + const mutableParticles = (viewUnits[0] as TemplateUnit).mutableUnits!; + + expect(mutableParticles).toHaveLength(1); + const particle = mutableParticles[0]!; + expect(particle.path).toHaveLength(1); + expect(particle.path[0]).toBe(0); + }); + + it("should correctly parse the path of multiple TemplateUnit's mutableUnits", () => { + const viewUnits = parse('
'); + const mutableParticles = (viewUnits[0] as TemplateUnit).mutableUnits!; + + expect(mutableParticles).toHaveLength(2); + const firstParticle = mutableParticles[0]!; + expect(firstParticle.path).toHaveLength(1); + expect(firstParticle.path[0]).toBe(0); + const secondParticle = mutableParticles[1]!; + expect(secondParticle.path).toHaveLength(1); + expect(secondParticle.path[0]).toBe(2); + }); +}); diff --git a/packages/transpiler/jsx-parser/src/test/TextUnit.test.ts b/packages/transpiler/jsx-parser/src/test/TextUnit.test.ts new file mode 100644 index 00000000..bcd435de --- /dev/null +++ b/packages/transpiler/jsx-parser/src/test/TextUnit.test.ts @@ -0,0 +1,73 @@ +import { describe, expect, it } from 'vitest'; +import { parse } from './mock'; +import { types as t } from '@babel/core'; +import type { TextUnit } from '../index'; + +describe('TextUnit', () => { + // ---- Type + it('should identify text unit', () => { + const viewUnits = parse('<>hello world'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('text'); + }); + + it('should identify text unit with boolean expression', () => { + const viewUnits = parse('<>{true}'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('text'); + }); + + it('should identify text unit with number expression', () => { + const viewUnits = parse('<>{1}'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('text'); + }); + + it('should identify text unit with null expression', () => { + const viewUnits = parse('<>{null}'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('text'); + }); + + it('should identify text unit with string literal expression', () => { + const viewUnits = parse('<>{"hello world"}'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('text'); + }); + + // ---- Content + it('should correctly parse content for text unit', () => { + const viewUnits = parse('<>hello world'); + const content = (viewUnits[0] as TextUnit).content; + + expect(t.isStringLiteral(content, { value: 'hello world' })).toBeTruthy(); + }); + + it('should correctly parse content for boolean text unit', () => { + const viewUnits = parse('<>{true}'); + const content = (viewUnits[0] as TextUnit).content; + + expect(t.isBooleanLiteral(content, { value: true })).toBeTruthy(); + }); + + it('should correctly parse content for number text unit', () => { + const viewUnits = parse('<>{1}'); + const content = (viewUnits[0] as TextUnit).content; + + expect(t.isNumericLiteral(content, { value: 1 })).toBeTruthy(); + }); + + it('should correctly parse content for null text unit', () => { + const viewUnits = parse('<>{null}'); + const content = (viewUnits[0] as TextUnit).content; + + expect(t.isNullLiteral(content)).toBeTruthy(); + }); + + it('should correctly parse content for string literal text unit', () => { + const viewUnits = parse('<>{"hello world"}'); + const content = (viewUnits[0] as TextUnit).content; + + expect(t.isStringLiteral(content)).toBeTruthy(); + }); +}); diff --git a/packages/transpiler/jsx-parser/src/test/global.d.ts b/packages/transpiler/jsx-parser/src/test/global.d.ts new file mode 100644 index 00000000..a500f9b5 --- /dev/null +++ b/packages/transpiler/jsx-parser/src/test/global.d.ts @@ -0,0 +1 @@ +declare module '@babel/plugin-syntax-jsx'; diff --git a/packages/transpiler/jsx-parser/src/test/mock.ts b/packages/transpiler/jsx-parser/src/test/mock.ts new file mode 100644 index 00000000..89f8682f --- /dev/null +++ b/packages/transpiler/jsx-parser/src/test/mock.ts @@ -0,0 +1,228 @@ +import babel, { parseSync, types as t } from '@babel/core'; +import { AllowedJSXNode, ViewParserConfig } from '../types'; +import { parseView as pV } from '..'; +import babelJSX from '@babel/plugin-syntax-jsx'; + +const htmlTags = [ + 'a', + 'abbr', + 'address', + 'area', + 'article', + 'aside', + 'audio', + 'b', + 'base', + 'bdi', + 'bdo', + 'blockquote', + 'body', + 'br', + 'button', + 'canvas', + 'caption', + 'cite', + 'code', + 'col', + 'colgroup', + 'data', + 'datalist', + 'dd', + 'del', + 'details', + 'dfn', + 'dialog', + 'div', + 'dl', + 'dt', + 'em', + 'embed', + 'fieldset', + 'figcaption', + 'figure', + 'footer', + 'form', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'head', + 'header', + 'hgroup', + 'hr', + 'html', + 'i', + 'iframe', + 'img', + 'input', + 'ins', + 'kbd', + 'label', + 'legend', + 'li', + 'link', + 'main', + 'map', + 'mark', + 'menu', + 'meta', + 'meter', + 'nav', + 'noscript', + 'object', + 'ol', + 'optgroup', + 'option', + 'output', + 'p', + 'picture', + 'pre', + 'progress', + 'q', + 'rp', + 'rt', + 'ruby', + 's', + 'samp', + 'script', + 'section', + 'select', + 'slot', + 'small', + 'source', + 'span', + 'strong', + 'style', + 'sub', + 'summary', + 'sup', + 'table', + 'tbody', + 'td', + 'template', + 'textarea', + 'tfoot', + 'th', + 'thead', + 'time', + 'title', + 'tr', + 'track', + 'u', + 'ul', + 'var', + 'video', + 'wbr', + 'acronym', + 'applet', + 'basefont', + 'bgsound', + 'big', + 'blink', + 'center', + 'dir', + 'font', + 'frame', + 'frameset', + 'isindex', + 'keygen', + 'listing', + 'marquee', + 'menuitem', + 'multicol', + 'nextid', + 'nobr', + 'noembed', + 'noframes', + 'param', + 'plaintext', + 'rb', + 'rtc', + 'spacer', + 'strike', + 'tt', + 'xmp', + 'animate', + 'animateMotion', + 'animateTransform', + 'circle', + 'clipPath', + 'defs', + 'desc', + 'ellipse', + 'feBlend', + 'feColorMatrix', + 'feComponentTransfer', + 'feComposite', + 'feConvolveMatrix', + 'feDiffuseLighting', + 'feDisplacementMap', + 'feDistantLight', + 'feDropShadow', + 'feFlood', + 'feFuncA', + 'feFuncB', + 'feFuncG', + 'feFuncR', + 'feGaussianBlur', + 'feImage', + 'feMerge', + 'feMergeNode', + 'feMorphology', + 'feOffset', + 'fePointLight', + 'feSpecularLighting', + 'feSpotLight', + 'feTile', + 'feTurbulence', + 'filter', + 'foreignObject', + 'g', + 'image', + 'line', + 'linearGradient', + 'marker', + 'mask', + 'metadata', + 'mpath', + 'path', + 'pattern', + 'polygon', + 'polyline', + 'radialGradient', + 'rect', + 'set', + 'stop', + 'svg', + 'switch', + 'symbol', + 'text', + 'textPath', + 'tspan', + 'use', + 'view', +]; + +export const config: ViewParserConfig = { + babelApi: babel, + htmlTags, +}; + +export function parseCode(code: string) { + return (parseSync(code, { plugins: [babelJSX] })!.program.body[0] as t.ExpressionStatement) + .expression as AllowedJSXNode; +} + +export function parseView(node: AllowedJSXNode) { + return pV(node, config); +} + +export function parse(code: string) { + return parseView(parseCode(code)); +} + +export function wrapWithFile(node: t.Expression): t.File { + return t.file(t.program([t.expressionStatement(node)])); +} diff --git a/packages/transpiler/jsx-parser/src/types.ts b/packages/transpiler/jsx-parser/src/types.ts new file mode 100644 index 00000000..e860ad92 --- /dev/null +++ b/packages/transpiler/jsx-parser/src/types.ts @@ -0,0 +1,85 @@ +import type Babel from '@babel/core'; +import type { types as t } from '@babel/core'; + +export interface Context { + ifElseStack: IfUnit[]; +} +export interface UnitProp { + value: t.Expression; + viewPropMap: Record; + specifier?: string; +} + +export interface TextUnit { + type: 'text'; + content: t.Literal; +} + +export type MutableUnit = ViewUnit & { path: number[] }; + +export interface TemplateProp { + tag: t.Expression; + name: string; + key: string; + path: number[]; + value: t.Expression; +} + +export interface TemplateUnit { + type: 'template'; + template: HTMLUnit; + mutableUnits: MutableUnit[]; + props: TemplateProp[]; +} + +export interface HTMLUnit { + type: 'html'; + tag: t.Expression; + props: Record; + children: ViewUnit[]; +} + +export interface CompUnit { + type: 'comp'; + tag: t.Expression; + props: Record; + children: ViewUnit[]; +} + +export interface IfBranch { + condition: t.Expression; + children: ViewUnit[]; +} + +export interface IfUnit { + type: 'if'; + branches: IfBranch[]; +} + +export interface ExpUnit { + type: 'exp'; + content: UnitProp; +} + +export interface EnvUnit { + type: 'env'; + props: Record; + children: ViewUnit[]; +} + +export interface ForUnit { + type: 'for'; + item: t.LVal; + array: t.Expression; + key: t.Expression; + children: ViewUnit[]; +} +export type ViewUnit = TextUnit | HTMLUnit | CompUnit | IfUnit | ExpUnit | EnvUnit | TemplateUnit | ForUnit; + +export interface ViewParserConfig { + babelApi: typeof Babel; + htmlTags: string[]; + parseTemplate?: boolean; +} + +export type AllowedJSXNode = t.JSXElement | t.JSXFragment | t.JSXText | t.JSXExpressionContainer | t.JSXSpreadChild; diff --git a/packages/transpiler/jsx-parser/tsconfig.json b/packages/transpiler/jsx-parser/tsconfig.json new file mode 100644 index 00000000..e0932d78 --- /dev/null +++ b/packages/transpiler/jsx-parser/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "esModuleInterop": true + }, + "ts-node": { + "esm": true + } +} \ No newline at end of file diff --git a/packages/transpiler/reactivity-parser/package.json b/packages/transpiler/reactivity-parser/package.json new file mode 100644 index 00000000..3fc66fa5 --- /dev/null +++ b/packages/transpiler/reactivity-parser/package.json @@ -0,0 +1,47 @@ +{ + "name": "@inula/reactivity-parser", + "version": "0.0.1", + "author": { + "name": "IanDx", + "email": "iandxssxx@gmail.com" + }, + "keywords": [ + "dlight.js" + ], + "license": "MIT", + "files": [ + "dist" + ], + "type": "module", + "main": "dist/index.cjs", + "module": "dist/index.js", + "typings": "dist/index.d.ts", + "scripts": { + "build": "tsup --sourcemap", + "test": "vitest --ui" + }, + "devDependencies": { + "@babel/core": "^7.20.12", + "@types/babel__core": "^7.20.5", + "@vitest/ui": "^0.34.5", + "tsup": "^6.7.0", + "typescript": "^5.3.2", + "vitest": "^0.34.5" + }, + "dependencies": { + "@inula/error-handler": "workspace:*", + "@inula/view-parser": "workspace:*" + }, + "tsup": { + "entry": [ + "src/index.ts" + ], + "format": [ + "cjs", + "esm" + ], + "clean": true, + "dts": true, + "minify": true + } +} diff --git a/packages/transpiler/reactivity-parser/src/error.ts b/packages/transpiler/reactivity-parser/src/error.ts new file mode 100644 index 00000000..987b7efd --- /dev/null +++ b/packages/transpiler/reactivity-parser/src/error.ts @@ -0,0 +1,5 @@ +import { createErrorHandler } from '@inula/error-handler'; + +export const DLError = createErrorHandler('ReactivityParser', { + 1: 'Invalid ViewUnit type', +}); diff --git a/packages/transpiler/reactivity-parser/src/index.ts b/packages/transpiler/reactivity-parser/src/index.ts new file mode 100644 index 00000000..20cddd44 --- /dev/null +++ b/packages/transpiler/reactivity-parser/src/index.ts @@ -0,0 +1,25 @@ +import { type ViewUnit } from '@inula/view-parser'; +import { ReactivityParser } from './parser'; +import { type ViewParticle, type ReactivityParserConfig } from './types'; + +/** + * @brief Parse view units to get used properties and view particles with reactivity + * @param viewUnits + * @param config + * @param options + * @returns [viewParticles, usedProperties] + */ +export function parseReactivity(viewUnits: ViewUnit[], config: ReactivityParserConfig): [ViewParticle[], Set] { + // ---- ReactivityParser only accepts one view unit at a time, + // so we loop through the view units and get all the used properties + const usedProperties = new Set(); + const dlParticles = viewUnits.map(viewUnit => { + const parser = new ReactivityParser(config); + const dlParticle = parser.parse(viewUnit); + parser.usedProperties.forEach(usedProperties.add.bind(usedProperties)); + return dlParticle; + }); + return [dlParticles, usedProperties]; +} + +export type * from './types'; diff --git a/packages/transpiler/reactivity-parser/src/parser.ts b/packages/transpiler/reactivity-parser/src/parser.ts new file mode 100644 index 00000000..336f9454 --- /dev/null +++ b/packages/transpiler/reactivity-parser/src/parser.ts @@ -0,0 +1,1023 @@ +import { + type TemplateProp, + type ReactivityParserConfig, + type MutableParticle, + type ViewParticle, + type TemplateParticle, + type TextParticle, + type HTMLParticle, + type DependencyProp, + type ExpParticle, + type CompParticle, + type ForParticle, + type IfParticle, + type EnvParticle, + type SnippetParticle, + SwitchParticle, + TryParticle, +} from './types'; +import { type NodePath, type types as t, type traverse } from '@babel/core'; +import { + type TextUnit, + type HTMLUnit, + type ViewUnit, + type CompUnit, + type ViewProp, + type ForUnit, + type IfUnit, + type EnvUnit, + type ExpUnit, + type SnippetUnit, + SwitchUnit, + TryUnit, +} from '@inula/view-parser'; +import { DLError } from './error'; + +export class ReactivityParser { + private readonly config: ReactivityParserConfig; + + private readonly t: typeof t; + private readonly traverse: typeof traverse; + private readonly availableProperties: string[]; + private readonly availableIdentifiers?: string[]; + private readonly dependencyMap: Record; + private readonly identifierDepMap: Record; + private readonly dependencyParseType; + private readonly reactivityFuncNames; + + private readonly escapeNamings = ['escape', '$']; + private static readonly customHTMLProps = [ + 'didUpdate', + 'willMount', + 'didMount', + 'willUnmount', + 'didUnmount', + 'element', + 'innerHTML', + 'props', + 'attrs', + 'dataset', + 'forwardProps', + ]; + + readonly usedProperties = new Set(); + + /** + * @brief Constructor + * @param viewUnit + * @param config + * @param options + */ + constructor(config: ReactivityParserConfig) { + this.config = config; + this.t = config.babelApi.types; + this.traverse = config.babelApi.traverse; + this.availableProperties = config.availableProperties; + this.availableIdentifiers = config.availableIdentifiers; + this.dependencyMap = config.dependencyMap; + this.identifierDepMap = config.identifierDepMap ?? {}; + this.dependencyParseType = config.dependencyParseType ?? 'property'; + this.reactivityFuncNames = config.reactivityFuncNames ?? []; + } + + /** + * @brief Parse the ViewUnit into a ViewParticle + * @returns + */ + parse(viewUnit: ViewUnit): ViewParticle { + return this.parseViewUnit(viewUnit); + } + + /** + * @brief Parse a ViewUnit into a ViewParticle + * @param viewUnit + * @returns ViewParticle + */ + private parseViewUnit(viewUnit: ViewUnit): ViewParticle { + if (this.isHTMLTemplate(viewUnit)) return this.parseTemplate(viewUnit as HTMLUnit); + if (viewUnit.type === 'text') return this.parseText(viewUnit); + if (viewUnit.type === 'html') return this.parseHTML(viewUnit); + if (viewUnit.type === 'comp') return this.parseComp(viewUnit); + if (viewUnit.type === 'for') return this.parseFor(viewUnit); + if (viewUnit.type === 'try') return this.parseTry(viewUnit); + if (viewUnit.type === 'if') return this.parseIf(viewUnit); + if (viewUnit.type === 'env') return this.parseEnv(viewUnit); + if (viewUnit.type === 'exp') return this.parseExp(viewUnit); + if (viewUnit.type === 'switch') return this.parseSwitch(viewUnit); + if (viewUnit.type === 'snippet') return this.parseSnippet(viewUnit); + return DLError.throw1(); + } + + // ---- Parsers ---- + // ---- @Template ---- + /** + * @brief Collect static HTMLUnit into a template particle and generate a template string + * MutableParticle means whatever unit that is not a static HTMLUnit or a TextUnit + * Props means all the non-static props of the nested HTMLUnit or TextUnit, e.g. div().className(this.name) + * @param htmlUnit + * @returns TemplateParticle + */ + private parseTemplate(htmlUnit: HTMLUnit): TemplateParticle { + return { + type: 'template', + template: this.generateTemplate(htmlUnit), + props: this.parseTemplateProps(htmlUnit), + mutableParticles: this.generateMutableParticles(htmlUnit), + }; + } + + /** + * @brief Generate a template + * There'll be a situation where the tag is dynamic, e.g. tag(this.htmlTag), + * which we can't generate a template string for it, so we'll wrap it in an ExpParticle in parseHTML() section + * @param htmlUnit + * @returns template string + */ + private generateTemplate(unit: HTMLUnit): HTMLParticle { + const staticProps = this.filterTemplateProps( + // ---- Get all the static props + Object.entries(unit.props).filter( + ([, prop]) => + this.isStaticProp(prop) && + // ---- Filter out props with false values + !(this.t.isBooleanLiteral(prop.value) && !prop.value.value) + ) + ).map(([key, prop]) => [ + key, + { + ...prop, + dependencyIndexArr: [], + dependenciesNode: this.t.arrayExpression([]), + dynamic: false, + }, + ]); + + let children: ViewParticle[] = []; + if (!unit.props.textContent) { + children = unit.children + .map(unit => { + if (unit.type === 'html' && this.t.isStringLiteral(unit.tag)) { + return this.generateTemplate(unit); + } + if (unit.type === 'text' && this.t.isStringLiteral(unit.content)) { + return this.parseText(unit); + } + }) + .filter(Boolean) as HTMLParticle[]; + } + return { + type: 'html', + tag: unit.tag, + props: Object.fromEntries(staticProps), + children, + }; + } + + /** + * @brief Collect all the mutable nodes in a static HTMLUnit + * We use this function to collect mutable nodes' path and props, + * so that in the generator, we know which position to insert the mutable nodes + * @param htmlUnit + * @returns mutable particles + */ + private generateMutableParticles(htmlUnit: HTMLUnit): MutableParticle[] { + const mutableParticles: MutableParticle[] = []; + const generateMutableUnit = (unit: HTMLUnit, path: number[] = []) => { + // ---- Generate mutable particles for current HTMLUnit + unit.children?.forEach((child, idx) => { + if ( + !(child.type === 'html' && this.t.isStringLiteral(child.tag)) && + !(child.type === 'text' && this.t.isStringLiteral(child.content)) + ) { + mutableParticles.push({ + path: [...path, idx], + ...this.parseViewParticle(child), + }); + } + }); + // ---- Recursively generate mutable particles for static HTMLUnit children + unit.children + ?.filter(child => child.type === 'html' && this.t.isStringLiteral(child.tag)) + .forEach((child, idx) => { + generateMutableUnit(child as HTMLUnit, [...path, idx]); + }); + }; + generateMutableUnit(htmlUnit); + + return mutableParticles; + } + + /** + * @brief Collect all the props in a static HTMLUnit or its nested HTMLUnit or TextUnit children + * Just like the mutable nodes, props are also equipped with path, + * so that we know which HTML ChildNode to insert the props + * @param htmlUnit + * @returns props + */ + private parseTemplateProps(htmlUnit: HTMLUnit): TemplateProp[] { + const templateProps: TemplateProp[] = []; + const generateVariableProp = (unit: HTMLUnit, path: number[]) => { + // ---- Generate all non-static(string/number/boolean) props for current HTMLUnit + // to be inserted further in the generator + Object.entries(unit.props) + .filter(([, prop]) => !this.isStaticProp(prop)) + .forEach(([key, prop]) => { + templateProps.push({ + tag: (unit.tag as t.StringLiteral).value, + key, + path, + value: prop.value, + ...this.getDependencies(prop.value), + }); + }); + // ---- Recursively generate props for static HTMLUnit children + unit.children + .filter( + child => + (child.type === 'html' && this.t.isStringLiteral(child.tag)) || + (child.type === 'text' && this.t.isStringLiteral(child.content)) + ) + .forEach((child, idx) => { + if (child.type === 'html') { + generateVariableProp(child, [...path, idx]); + } else if (child.type === 'text') { + // ---- if the child is a TextUnit, we just insert the text content + templateProps.push({ + tag: 'text', + key: 'value', + path: [...path, idx], + value: child.content, + dependencyIndexArr: [], + dependenciesNode: this.t.arrayExpression([]), + dynamic: false, + }); + } + }); + }; + generateVariableProp(htmlUnit, []); + + return templateProps; + } + + // ---- @Text ---- + /** + * @brief Parse a TextUnit into a TextParticle. + * This is only for a top level TextUnit, because if nested in HTMLUnit, it'll be parsed in the template string + * @param textUnit + * @returns TextParticle + */ + private parseText(textUnit: TextUnit): TextParticle { + return { + type: 'text', + content: { + value: textUnit.content, + ...this.getDependencies(textUnit.content), + }, + }; + } + + // ---- @HTML ---- + /** + * @brief Parse an HTMLUnit with a dynamic tag into an ExpParticle or an HTMLParticle + * We detect dependencies in the tag, if there's no dependency, + * we parse it as an HTMLParticle and dynamically append it to the parent node; + * if there's dependency, we parse it as an ExpParticle and wrap it in an ExpParticle + * so that we can make the tag reactive + * @param htmlUnit + * @returns ExpParticle | HTMLParticle + */ + private parseHTML(htmlUnit: HTMLUnit): ExpParticle | HTMLParticle { + const { dependencyIndexArr, dependenciesNode, dynamic } = this.getDependencies(htmlUnit.tag); + + const innerHTMLParticle: HTMLParticle = { + type: 'html', + tag: htmlUnit.tag, + props: {}, + children: [], + }; + + innerHTMLParticle.props = Object.fromEntries( + Object.entries(htmlUnit.props).map(([key, prop]) => [key, this.generateDependencyProp(prop)]) + ); + + innerHTMLParticle.children = htmlUnit.children.map(this.parseViewParticle.bind(this)); + + // ---- Not a dynamic tag + if (!dynamic) return innerHTMLParticle; + + // ---- Dynamic tag, wrap it in an ExpParticle to make the tag reactive + const id = this.uid(); + return { + type: 'exp', + content: { + value: this.t.stringLiteral(id), + viewPropMap: { + [id]: [innerHTMLParticle], + }, + dependencyIndexArr, + dependenciesNode, + dynamic, + }, + props: {}, + }; + } + + // ---- @Comp ---- + /** + * @brief Parse a CompUnit into a CompParticle or an ExpParticle + * Similar to parseHTML(), we detect dependencies in the tag, if there's no dependency, + * we parse it as a regular CompParticle, otherwise we wrap it with an ExpParticle. + * @param compUnit + * @returns CompParticle | ExpParticle + */ + private parseComp(compUnit: CompUnit): CompParticle | ExpParticle { + const { dependencyIndexArr, dependenciesNode, dynamic } = this.getDependencies(compUnit.tag); + + const compParticle: CompParticle = { + type: 'comp', + tag: compUnit.tag, + props: {}, + children: [], + }; + + compParticle.props = Object.fromEntries( + Object.entries(compUnit.props).map(([key, prop]) => [key, this.generateDependencyProp(prop)]) + ); + compParticle.children = compUnit.children.map(this.parseViewParticle.bind(this)); + + if (!dynamic) return compParticle; + + const id = this.uid(); + return { + type: 'exp', + content: { + value: this.t.stringLiteral(id), + viewPropMap: { + [id]: [compParticle], + }, + dependencyIndexArr, + dependenciesNode, + dynamic, + }, + props: {}, + }; + } + + // ---- @For ---- + /** + * @brief Parse a ForUnit into a ForParticle with dependencies + * Key and item doesn't need to be reactive, so here we don't collect dependencies for it + * @param forUnit + * @returns ForParticle + */ + private parseFor(forUnit: ForUnit): ForParticle { + const { dependencyIndexArr, dependenciesNode, dynamic } = this.getDependencies(forUnit.array); + const prevIdentifierDepMap = this.config.identifierDepMap; + // ---- Find all the identifiers in the key and remove them from the identifierDepMap + // because once the key is changed, that identifier related dependencies will be changed too, + // so no need to update them + const keyDep = this.t.isIdentifier(forUnit.key) && forUnit.key.name; + // ---- Generate an identifierDepMap to track identifiers in item and make them reactive + // based on the dependencies from the array + this.config.identifierDepMap = Object.fromEntries( + this.getIdentifiers(this.t.assignmentExpression('=', forUnit.item, this.t.objectExpression([]))) + .filter(id => !keyDep || id !== keyDep) + .map(id => [id, dependencyIndexArr.map(n => this.availableProperties[n])]) + ); + + const forParticle: ForParticle = { + type: 'for', + item: forUnit.item, + array: { + value: forUnit.array, + dynamic, + dependencyIndexArr, + dependenciesNode, + }, + children: forUnit.children.map(this.parseViewParticle.bind(this)), + key: forUnit.key, + }; + this.config.identifierDepMap = prevIdentifierDepMap; + return forParticle; + } + + // ---- @If ---- + /** + * @brief Parse an IfUnit into an IfParticle with dependencies + * @param ifUnit + * @returns IfParticle + */ + private parseIf(ifUnit: IfUnit): IfParticle { + return { + type: 'if', + branches: ifUnit.branches.map(branch => ({ + condition: { + value: branch.condition, + ...this.getDependencies(branch.condition), + }, + children: branch.children.map(this.parseViewParticle.bind(this)), + })), + }; + } + + // ---- @Switch ---- + /** + * @brief Parse a SwitchUnit into an SwitchParticle with dependencies + * @param switchUnit + * @returns SwitchParticle + */ + private parseSwitch(switchUnit: SwitchUnit): SwitchParticle { + return { + type: 'switch', + discriminant: { + value: switchUnit.discriminant, + ...this.getDependencies(switchUnit.discriminant), + }, + branches: switchUnit.branches.map(branch => ({ + case: { + value: branch.case, + ...this.getDependencies(branch.case), + }, + children: branch.children.map(this.parseViewParticle.bind(this)), + break: branch.break, + })), + }; + } + + // ---- @Try ---- + /** + * @brief Parse a TryUnit into an TryParticle with dependencies + * @param tryUnit + * @returns TryParticle + */ + private parseTry(tryUnit: TryUnit): TryParticle { + return { + type: 'try', + children: tryUnit.children.map(this.parseViewParticle.bind(this)), + exception: tryUnit.exception, + catchChildren: tryUnit.catchChildren.map(this.parseViewParticle.bind(this)), + }; + } + + // ---- @Env ---- + /** + * @brief Parse an EnvUnit into an EnvParticle with dependencies + * @param envUnit + * @returns EnvParticle + */ + private parseEnv(envUnit: EnvUnit): EnvParticle { + return { + type: 'env', + props: Object.fromEntries( + Object.entries(envUnit.props).map(([key, prop]) => [key, this.generateDependencyProp(prop)]) + ), + children: envUnit.children.map(this.parseViewParticle.bind(this)), + }; + } + + // ---- @Exp ---- + /** + * @brief Parse an ExpUnit into an ExpParticle with dependencies + * @param expUnit + * @returns ExpParticle + */ + private parseExp(expUnit: ExpUnit): ExpParticle { + const expParticle: ExpParticle = { + type: 'exp', + content: this.generateDependencyProp(expUnit.content), + props: Object.fromEntries( + Object.entries(expUnit.props).map(([key, prop]) => [key, this.generateDependencyProp(prop)]) + ), + }; + return expParticle; + } + + // ---- @Snippet ---- + /** + * @brief Parse a SnippetUnit into a SnippetParticle with dependencies + * @param snippetUnit + * @returns SnippetParticle + */ + private parseSnippet(snippetUnit: SnippetUnit): SnippetParticle { + const snippetParticle: SnippetParticle = { + type: 'snippet', + tag: snippetUnit.tag, + props: {}, + children: [], + }; + if (snippetUnit.props) { + snippetParticle.props = Object.fromEntries( + Object.entries(snippetUnit.props).map(([key, prop]) => [key, this.generateDependencyProp(prop)]) + ); + } + if (snippetUnit.children) { + snippetParticle.children = snippetUnit.children.map(this.parseViewParticle.bind(this)); + } + + return snippetParticle; + } + + // ---- Dependencies ---- + /** + * @brief Generate a dependency prop with dependencies + * @param prop + * @returns DependencyProp + */ + private generateDependencyProp(prop: ViewProp): DependencyProp { + const dependencyProp: DependencyProp = { + value: prop.value, + ...this.getDependencies(prop.value), + viewPropMap: Object.fromEntries( + Object.entries(prop.viewPropMap).map(([key, units]) => [key, units.map(this.parseViewParticle.bind(this))]) + ), + }; + return dependencyProp; + } + + /** + * @brief Get all the dependencies of a node + * this.dependencyParseType controls how we parse the dependencies + * 1. property: parse the dependencies of a node as a property, e.g. this.name + * 2. identifier: parse the dependencies of a node as an identifier, e.g. name + * The availableProperties is the list of all the properties that can be used in the template, + * no matter it's a property or an identifier + * @param node + * @returns dependency index array + */ + private getDependencies(node: t.Expression | t.Statement): { + dynamic: boolean; + dependencyIndexArr: number[]; + dependenciesNode: t.ArrayExpression; + } { + if (this.t.isFunctionExpression(node) || this.t.isArrowFunctionExpression(node)) { + return { + dynamic: false, + dependencyIndexArr: [], + dependenciesNode: this.t.arrayExpression([]), + }; + } + // ---- Both id and prop deps need to be calculated because + // id is for snippet update, prop is normal update + // in a snippet, the depsNode should be both id and prop + const [directIdentifierDeps, identifierDepNodes] = this.getIdentifierDependencies(node); + const [directPropertyDeps, propertyDepNodes] = this.getPropertyDependencies(node); + const directDependencies = this.dependencyParseType === 'identifier' ? directIdentifierDeps : directPropertyDeps; + const identifierMapDependencies = this.getIdentifierMapDependencies(node); + const deps = [...new Set([...directDependencies, ...identifierMapDependencies])]; + + const depNodes = [...identifierDepNodes, ...propertyDepNodes] as t.Expression[]; + + return { + dynamic: depNodes.length > 0 || deps.length > 0, + dependencyIndexArr: deps, + dependenciesNode: this.t.arrayExpression(depNodes), + }; + } + + /** + * @brief Get all the dependencies of a node if a property is a valid dependency as + * 1. the identifier is in the availableProperties + * 2. the identifier is a stand alone identifier + * 3. the identifier is not in an escape function + * 4. the identifier is not in a manual function + * 5. the identifier is not the left side of an assignment expression, which is an assignment expression + * 6. the identifier is not the right side of an assignment expression, which is an update expression + * @param node + * @returns dependency index array + */ + private getIdentifierDependencies(node: t.Expression | t.Statement): [number[], t.Node[]] { + const availableIdentifiers = this.availableIdentifiers ?? this.availableProperties; + + const deps = new Set(); + const assignDeps = new Set(); + const depNodes: Record = {}; + + const wrappedNode = this.valueWrapper(node); + this.traverse(wrappedNode, { + Identifier: innerPath => { + const identifier = innerPath.node; + const idName = identifier.name; + if (!availableIdentifiers.includes(idName)) return; + if (this.isAssignmentExpressionLeft(innerPath) || this.isAssignmentFunction(innerPath)) { + assignDeps.add(idName); + } else if ( + this.isStandAloneIdentifier(innerPath) && + !this.isMemberInEscapeFunction(innerPath) && + !this.isMemberInManualFunction(innerPath) + ) { + deps.add(idName); + this.dependencyMap[idName]?.forEach(deps.add.bind(deps)); + if (!depNodes[idName]) depNodes[idName] = []; + depNodes[idName].push(this.geneDependencyNode(innerPath)); + } + }, + }); + + assignDeps.forEach(dep => { + deps.delete(dep); + delete depNodes[dep]; + }); + let dependencyNodes = Object.values(depNodes).flat(); + // ---- deduplicate the dependency nodes + dependencyNodes = dependencyNodes.filter((n, i) => { + const idx = dependencyNodes.findIndex(m => this.t.isNodesEquivalent(m, n)); + return idx === i; + }); + + deps.forEach(this.usedProperties.add.bind(this.usedProperties)); + return [[...deps].map(dep => this.availableProperties.indexOf(dep)), dependencyNodes]; + } + + /** + * @brief Get all the dependencies of a node if a member expression is a valid dependency as + * 1. the property is in the availableProperties + * 2. the object is this + * 3. the member expression is not in an escape function + * 4. the member expression is not in a manual function + * 5. the member expression is not the left side of an assignment expression, which is an assignment expression + * 6. the member is not a pure function declaration + * @param node + * @returns dependency index array + */ + private getPropertyDependencies(node: t.Expression | t.Statement): [number[], t.Node[]] { + const deps = new Set(); + const assignDeps = new Set(); + const depNodes: Record = {}; + + const wrappedNode = this.valueWrapper(node); + this.traverse(wrappedNode, { + MemberExpression: innerPath => { + if (!this.t.isIdentifier(innerPath.node.property) || !this.t.isThisExpression(innerPath.node.object)) return; + const propertyKey = innerPath.node.property.name; + if (this.isAssignmentExpressionLeft(innerPath) || this.isAssignmentFunction(innerPath)) { + assignDeps.add(propertyKey); + } else if ( + this.availableProperties.includes(propertyKey) && + !this.isMemberInEscapeFunction(innerPath) && + !this.isMemberInManualFunction(innerPath) + ) { + deps.add(propertyKey); + this.dependencyMap[propertyKey]?.forEach(deps.add.bind(deps)); + if (!depNodes[propertyKey]) depNodes[propertyKey] = []; + depNodes[propertyKey].push(this.geneDependencyNode(innerPath)); + } + }, + }); + + assignDeps.forEach(dep => { + deps.delete(dep); + delete depNodes[dep]; + }); + let dependencyNodes = Object.values(depNodes).flat(); + // ---- deduplicate the dependency nodes + dependencyNodes = dependencyNodes.filter((n, i) => { + const idx = dependencyNodes.findIndex(m => this.t.isNodesEquivalent(m, n)); + return idx === i; + }); + + deps.forEach(this.usedProperties.add.bind(this.usedProperties)); + return [[...deps].map(dep => this.availableProperties.indexOf(dep)), dependencyNodes]; + } + + /** + * @brief Generate a dependency node from a dependency identifier, + * loop until the parent node is not a binary expression or a member expression + * @param path + * @returns + */ + private geneDependencyNode(path: NodePath): t.Node { + let parentPath = path; + while (parentPath?.parentPath) { + const pParentPath = parentPath.parentPath; + if ( + !( + this.t.isMemberExpression(pParentPath.node, { computed: false }) || + this.t.isOptionalMemberExpression(pParentPath.node) + ) + ) { + break; + } + parentPath = pParentPath; + } + const depNode = this.t.cloneNode(parentPath.node); + // ---- Turn memberExpression to optionalMemberExpression + this.traverse(this.valueWrapper(depNode as t.Expression), { + MemberExpression: innerPath => { + if (this.t.isThisExpression(innerPath.node.object)) return; + innerPath.node.optional = true; + innerPath.node.type = 'OptionalMemberExpression' as any; + }, + }); + return depNode; + } + + /** + * @brief Get dependencies from the identifierDepMap + * e.g. + * map: { "a": ["dep1", "dep2"] } + * expression: const b = a + * deps for b: ["dep1", "dep2"] + * @param node + * @returns dependency index array + */ + private getIdentifierMapDependencies(node: t.Expression | t.Statement): number[] { + const deps = new Set(); + + const wrappedNode = this.valueWrapper(node); + this.traverse(wrappedNode, { + Identifier: innerPath => { + const identifier = innerPath.node; + const idName = identifier.name; + if (this.isAttrFromFunction(innerPath, idName)) return; + const depsArray = this.identifierDepMap[idName]; + + if (!depsArray || !Array.isArray(depsArray)) return; + if (this.isMemberInEscapeFunction(innerPath) || this.isMemberInManualFunction(innerPath)) return; + depsArray.forEach(deps.add.bind(deps)); + }, + }); + + deps.forEach(this.usedProperties.add.bind(this.usedProperties)); + return [...deps].map(dep => this.availableProperties.indexOf(dep)); + } + + // ---- Utils ---- + /** + * @brief Parse a ViewUnit into a ViewParticle by new-ing a ReactivityParser + * @param viewUnit + * @returns ViewParticle + */ + private parseViewParticle(viewUnit: ViewUnit): ViewParticle { + const parser = new ReactivityParser(this.config); + const parsedUnit = parser.parse(viewUnit); + // ---- Collect used properties + parser.usedProperties.forEach(this.usedProperties.add.bind(this.usedProperties)); + return parsedUnit; + } + + /** + * @brief Check if a ViewUnit is a static HTMLUnit that can be parsed into a template + * Must satisfy: + * 1. type is html + * 2. tag is a string literal, i.e., non-dynamic tag + * 3. has at least one child that is a static HTMLUnit, + * or else just call a createElement function, no need for template clone + * @param viewUnit + * @returns is a static HTMLUnit + */ + private isHTMLTemplate(viewUnit: ViewUnit): boolean { + return ( + viewUnit.type === 'html' && + this.t.isStringLiteral(viewUnit.tag) && + !!viewUnit.children?.some(child => child.type === 'html' && this.t.isStringLiteral(child.tag)) + ); + } + + /** + * @brief Check if a prop is a static prop + * i.e. + * 1. no viewPropMap + * 2. value is a string/number/boolean literal + * @param prop + * @returns is a static prop + */ + private isStaticProp(prop: ViewProp): boolean { + const { value, viewPropMap } = prop; + return ( + (!viewPropMap || Object.keys(viewPropMap).length === 0) && + (this.t.isStringLiteral(value) || this.t.isNumericLiteral(value) || this.t.isBooleanLiteral(value)) + ); + } + + /** + * @brief Filter out some props that are not needed in the template, + * these are all special props to be parsed differently in the generator + * @param props + * @returns filtered props + */ + private filterTemplateProps(props: Array<[string, T]>): Array<[string, T]> { + return ( + props + // ---- Filter out event listeners + .filter(([key]) => !key.startsWith('on')) + // ---- Filter out specific props + .filter(([key]) => !ReactivityParser.customHTMLProps.includes(key)) + ); + } + + /** + * @brief Wrap the value in a file + * @param node + * @returns wrapped value + */ + private valueWrapper(node: t.Expression | t.Statement): t.File { + return this.t.file(this.t.program([this.t.isStatement(node) ? node : this.t.expressionStatement(node)])); + } + + /** + * @brief Check if an identifier is a simple stand alone identifier, + * i.e., not a member expression, nor a function param + * @param path + * 1. not a member expression + * 2. not a function param + * 3. not in a declaration + * 4. not as object property's not computed key + * @returns is a stand alone identifier + */ + private isStandAloneIdentifier(path: NodePath): boolean { + const node = path.node; + const parentNode = path.parentPath?.node; + const isMemberExpression = this.t.isMemberExpression(parentNode) && parentNode.property === node; + if (isMemberExpression) return false; + const isFunctionParam = this.isAttrFromFunction(path, node.name); + if (isFunctionParam) return false; + while (path.parentPath) { + if (this.t.isVariableDeclarator(path.parentPath.node)) return false; + if ( + this.t.isObjectProperty(path.parentPath.node) && + path.parentPath.node.key === path.node && + !path.parentPath.node.computed + ) + return false; + path = path.parentPath as NodePath; + } + return true; + } + + /** + * @brief Get all identifiers as strings in a node + * @param node + * @returns identifiers + */ + private getIdentifiers(node: t.Node): string[] { + if (this.t.isIdentifier(node)) return [node.name]; + const identifierKeys = new Set(); + this.traverse(this.valueWrapper(node as t.Expression), { + Identifier: innerPath => { + if (!this.isStandAloneIdentifier(innerPath)) return; + identifierKeys.add(innerPath.node.name); + }, + }); + return [...identifierKeys]; + } + + /** + * @brief check if the identifier is from a function param till the stopNode + * e.g: + * function myFunc1(ok) { // stopNode = functionBody + * const myFunc2 = ok => ok // from function param + * console.log(ok) // not from function param + * } + */ + private isAttrFromFunction(path: NodePath, idName: string) { + let reversePath = path.parentPath; + + const checkParam: (param: t.Node) => boolean = (param: t.Node) => { + // ---- 3 general types: + // * represent allow nesting + // ---0 Identifier: (a) + // ---1 RestElement: (...a) * + // ---1 Pattern: 3 sub Pattern + // -----0 AssignmentPattern: (a=1) * + // -----1 ArrayPattern: ([a, b]) * + // -----2 ObjectPattern: ({a, b}) + if (this.t.isIdentifier(param)) return param.name === idName; + if (this.t.isAssignmentPattern(param)) return checkParam(param.left); + if (this.t.isArrayPattern(param)) { + return param.elements + .filter(Boolean) + .map(el => checkParam(el!)) + .includes(true); + } + if (this.t.isObjectPattern(param)) { + return ( + param.properties.filter( + prop => this.t.isObjectProperty(prop) && this.t.isIdentifier(prop.key) + ) as t.ObjectProperty[] + ) + .map(prop => (prop.key as t.Identifier).name) + .includes(idName); + } + if (this.t.isRestElement(param)) return checkParam(param.argument); + + return false; + }; + + while (reversePath) { + const node = reversePath.node; + if (this.t.isArrowFunctionExpression(node) || this.t.isFunctionDeclaration(node)) { + for (const param of node.params) { + if (checkParam(param)) return true; + } + } + reversePath = reversePath.parentPath; + } + + return false; + } + + /** + * @brief Check if it's the left side of an assignment expression, e.g. this.count = 1 + * @param innerPath + * @returns is left side of an assignment expression + */ + private isAssignmentExpressionLeft(innerPath: NodePath): boolean { + let parentPath = innerPath.parentPath; + while (parentPath && !this.t.isStatement(parentPath.node)) { + if (this.t.isAssignmentExpression(parentPath.node)) { + if (parentPath.node.left === innerPath.node) return true; + const leftPath = parentPath.get('left') as NodePath; + if (innerPath.isDescendant(leftPath)) return true; + } else if (this.t.isUpdateExpression(parentPath.node)) { + return true; + } + parentPath = parentPath.parentPath; + } + + return false; + } + + /** + * @brief Check if it's a reactivity function, e.g. arr.push + * @param innerPath + * @returns + */ + private isAssignmentFunction(innerPath: NodePath): boolean { + let parentPath = innerPath.parentPath; + + while (parentPath && this.t.isMemberExpression(parentPath.node)) { + parentPath = parentPath.parentPath; + } + if (!parentPath) return false; + return ( + this.t.isCallExpression(parentPath.node) && + this.t.isMemberExpression(parentPath.node.callee) && + this.t.isIdentifier(parentPath.node.callee.property) && + this.reactivityFuncNames.includes(parentPath.node.callee.property.name) + ); + } + + /** + * @brief Check if it's in an "escape" function, + * e.g. escape(() => { console.log(this.count) }) + * deps will be empty instead of ["count"] + * @param innerPath + * @param classDeclarationNode + * @returns is in escape function + */ + private isMemberInEscapeFunction(innerPath: NodePath): boolean { + let isInFunction = false; + let reversePath = innerPath.parentPath; + while (reversePath) { + const node = reversePath.node; + if ( + this.t.isCallExpression(node) && + this.t.isIdentifier(node.callee) && + this.escapeNamings.includes(node.callee.name) + ) { + isInFunction = true; + break; + } + reversePath = reversePath.parentPath; + } + return isInFunction; + } + + /** + * @brief Check if it's in a "manual" function, + * e.g. manual(() => { console.log(this.count) }, ["flag"]) + * deps will be ["flag"] instead of ["count"] + * @param innerPath + * @param classDeclarationNode + * @returns is in manual function + */ + private isMemberInManualFunction(innerPath: NodePath): boolean { + let isInFunction = false; + let reversePath = innerPath.parentPath; + + while (reversePath) { + const node = reversePath.node; + const parentNode = reversePath.parentPath?.node; + const isManual = + this.t.isCallExpression(parentNode) && + this.t.isIdentifier(parentNode.callee) && + parentNode.callee.name === 'manual'; + const isFirstParam = this.t.isCallExpression(parentNode) && parentNode.arguments[0] === node; + if (isManual && isFirstParam) { + isInFunction = true; + break; + } + reversePath = reversePath.parentPath; + } + + return isInFunction; + } + + /** + * @brief Generate a random string + * @returns + */ + private uid(): string { + return Math.random().toString(36).slice(2); + } +} diff --git a/packages/transpiler/reactivity-parser/src/test/Dependency.test.ts b/packages/transpiler/reactivity-parser/src/test/Dependency.test.ts new file mode 100644 index 00000000..d7b86ca6 --- /dev/null +++ b/packages/transpiler/reactivity-parser/src/test/Dependency.test.ts @@ -0,0 +1,77 @@ +import { expect, describe, it } from 'vitest'; +import { availableProperties, parse, reactivityConfig } from './mock'; +import { type CompParticle } from '../types'; + +describe('Dependency', () => { + it('should parse the correct dependency', () => { + const viewParticles = parse('Comp(this.flag)'); + const content = (viewParticles[0] as CompParticle).props._$content; + expect(content?.dependencyIndexArr).toContain(0); + }); + + it('should parse the correct dependency when interfacing the dependency chain', () => { + const viewParticles = parse('Comp(this.doubleCount)'); + const content = (viewParticles[0] as CompParticle).props._$content; + const dependency = content?.dependencyIndexArr; + // ---- doubleCount depends on count, count depends on flag + // so doubleCount depends on flag, count and doubleCount + expect(dependency).toContain(availableProperties.indexOf('flag')); + expect(dependency).toContain(availableProperties.indexOf('count')); + expect(dependency).toContain(availableProperties.indexOf('doubleCount')); + }); + + it('should not parse the dependency if the property is not in the availableProperties', () => { + const viewParticles = parse('Comp(this.notExist)'); + const content = (viewParticles[0] as CompParticle).props._$content; + expect(content?.dependencyIndexArr).toHaveLength(0); + }); + + it('should not parse the dependency if the identifier is not an property of a ThisExpression', () => { + const viewParticles = parse('Comp(count)'); + const content = (viewParticles[0] as CompParticle).props._$content; + expect(content?.dependencyIndexArr).toHaveLength(0); + }); + + it('should not parse the dependency if the member expression is in an escaped function', () => { + let viewParticles = parse('Comp(escape(this.flag))'); + let content = (viewParticles[0] as CompParticle).props._$content; + expect(content?.dependencyIndexArr).toHaveLength(0); + + viewParticles = parse('Comp($(this.flag))'); + content = (viewParticles[0] as CompParticle).props._$content; + expect(content?.dependencyIndexArr).toHaveLength(0); + }); + + it('should not parse the dependency if the member expression is in a manual function', () => { + const viewParticles = parse('Comp(manual(() => this.count, []))'); + const content = (viewParticles[0] as CompParticle).props._$content; + expect(content?.dependencyIndexArr).toHaveLength(0); + }); + + it("should parse the dependencies in manual function's second parameter", () => { + const viewParticles = parse('Comp(manual(() => {let a = this.count}, [this.flag]))'); + const content = (viewParticles[0] as CompParticle).props._$content; + expect(content?.dependencyIndexArr).toHaveLength(1); + }); + + it('should not parse the dependency if the member expression is the left side of an assignment expression', () => { + const viewParticles = parse('Comp(this.flag = 1)'); + const content = (viewParticles[0] as CompParticle).props._$content; + expect(content?.dependencyIndexArr).toHaveLength(0); + }); + + it('should not parse the dependency if the member expression is right side of an assignment expression', () => { + const viewParticles = parse('Comp(this.flag = this.flag + 1)'); + const content = (viewParticles[0] as CompParticle).props._$content; + expect(content?.dependencyIndexArr).toHaveLength(0); + }); + + it('should parse the dependency as identifiers', () => { + reactivityConfig.dependencyParseType = 'identifier'; + const viewParticles = parse('Comp(flag + count)'); + const content = (viewParticles[0] as CompParticle).props._$content; + expect(content?.dependencyIndexArr).toContain(availableProperties.indexOf('flag')); + expect(content?.dependencyIndexArr).toContain(availableProperties.indexOf('count')); + reactivityConfig.dependencyParseType = 'property'; + }); +}); diff --git a/packages/transpiler/reactivity-parser/src/test/MutableTagParticle.test.ts b/packages/transpiler/reactivity-parser/src/test/MutableTagParticle.test.ts new file mode 100644 index 00000000..4037b8a7 --- /dev/null +++ b/packages/transpiler/reactivity-parser/src/test/MutableTagParticle.test.ts @@ -0,0 +1,68 @@ +import { expect, describe, it } from 'vitest'; +import { parse } from './mock'; +import { type HTMLParticle, type ExpParticle, type CompParticle } from '../types'; +import { type types as t } from '@babel/core'; + +describe('MutableTagParticle', () => { + // ---- HTML + it('should parse an HTMLUnit with dynamic tag as an HTMLParticle', () => { + const viewParticles = parse('tag(this.div)()'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('html'); + }); + + it('should parse an HTMLUnit with no children as an HTMLParticle', () => { + const viewParticles = parse('div()'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('html'); + }); + + it('should parse an HTMLUnit with non-static-html children as an HTMLParticle', () => { + const viewParticles = parse('div(); { Comp(); tag(this.div)(); }'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('html'); + }); + + it('should not parse an HTMLUnit with potential TemplateUnit as an HTMLParticle', () => { + const viewParticles = parse('div(); { div() }'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).not.toBe('html'); + }); + + it('should parse an HTMLUnit with dynamic tag with dependencies as an ExpParticle', () => { + const viewParticles = parse('tag(this.flag)()'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('exp'); + const content = (viewParticles[0] as ExpParticle).content; + + expect((content.value as t.StringLiteral).value).toBe(Object.keys(content.viewPropMap!)[0]); + const htmlParticle = content.viewPropMap![Object.keys(content.viewPropMap!)[0]][0] as HTMLParticle; + expect(htmlParticle.type).toBe('html'); + }); + + // ---- Comp + it('should parse a CompUnit with dynamic tag as an HTMLParticle', () => { + const viewParticles = parse('Comp()'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('comp'); + }); + + it('should parse a CompUnit with dynamic tag with dependencies as an ExpParticle', () => { + const viewParticles = parse('comp(CompList[this.flag])()'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('exp'); + const content = (viewParticles[0] as ExpParticle).content; + + expect((content.value as t.StringLiteral).value).toBe(Object.keys(content.viewPropMap!)[0]); + const compParticle = content.viewPropMap![Object.keys(content.viewPropMap!)[0]][0] as CompParticle; + + expect(compParticle.type).toBe('comp'); + }); + + // ---- Snippet + it('should parse a SnippetUnit as an HTMLParticle', () => { + const viewParticles = parse('this.MySnippet()'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('snippet'); + }); +}); diff --git a/packages/transpiler/reactivity-parser/src/test/OtherParticle.test.ts b/packages/transpiler/reactivity-parser/src/test/OtherParticle.test.ts new file mode 100644 index 00000000..c5dcc222 --- /dev/null +++ b/packages/transpiler/reactivity-parser/src/test/OtherParticle.test.ts @@ -0,0 +1,73 @@ +import { expect, describe, it } from 'vitest'; +import { parse } from './mock'; +import { type ForParticle } from '../../dist'; +import { type HTMLParticle } from '../types'; + +describe('OtherParticle', () => { + it('should parse a TextUnit as a TextParticle', () => { + const viewParticles = parse('"Hello World"'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('text'); + }); + + it('should parse an IfUnit as an IfParticle', () => { + const viewParticles = parse('if(this.flag) { div() }'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('if'); + }); + + it('should parse an IfUnit as an SwitchParticle', () => { + const viewParticles = parse('switch(this.flag) { }'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('switch'); + }); + + it('should parse a ForUnit as a ForParticle', () => { + const viewParticles = parse('for(const item of this.items) { div() }'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('for'); + }); + + it("should correctly parse ForUnit's item dependencies from array", () => { + console.log('this'); + const viewParticles = parse('for(const item of this.array[this.count]) { div(item) }'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('for'); + + const divParticle = (viewParticles[0] as ForParticle).children[0] as HTMLParticle; + const divDependency = divParticle.props?.textContent?.dependencyIndexArr; + expect(divDependency).toContain(0); + expect(divDependency).toContain(1); + expect(divDependency).toContain(3); + }); + + it("should correctly parse ForUnit's deconstruct item dependencies from array", () => { + const viewParticles = parse('for(const { idx, item } of this.array[this.count]) { div(item) }'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('for'); + + const divParticle = (viewParticles[0] as ForParticle).children[0] as HTMLParticle; + const divDependency = divParticle.props?.textContent?.dependencyIndexArr; + expect(divDependency).toContain(0); + expect(divDependency).toContain(1); + expect(divDependency).toContain(3); + }); + + it('should parse a EnvUnit as a EnvParticle', () => { + const viewParticles = parse('env().count(2); { div() }'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('env'); + }); + + it('should parse a ExpUnit as a ExpParticle', () => { + const viewParticles = parse('this.flag'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('exp'); + }); + + it('should parse a TryUnit as a TryParticle', () => { + const viewParticles = parse('try { div() } catch(e) { div() }'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('try'); + }); +}); diff --git a/packages/transpiler/reactivity-parser/src/test/Template.test.ts b/packages/transpiler/reactivity-parser/src/test/Template.test.ts new file mode 100644 index 00000000..9ecf0ba6 --- /dev/null +++ b/packages/transpiler/reactivity-parser/src/test/Template.test.ts @@ -0,0 +1,68 @@ +import { expect, describe, it } from 'vitest'; +import { parse } from './mock'; +import { type TemplateParticle } from '../types'; +import { types as t } from '@babel/core'; + +describe('TemplateUnit', () => { + it('should not parse a single HTMLUnit to a TemplateUnit', () => { + const viewParticles = parse('div()'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).not.toBe('template'); + }); + + it('should parse a nested HTMLUnit to a TemplateUnit', () => { + const viewParticles = parse('div(); {div()}'); + expect(viewParticles.length).toBe(1); + expect(viewParticles[0].type).toBe('template'); + }); + + it("should correctly parse a nested HTMLUnit's structure into a template", () => { + const viewParticles = parse('div(); {div()}'); + const template = (viewParticles[0] as any).template; + expect(t.isStringLiteral(template.tag, { value: 'div' })).toBe(true); + expect(template.children).toHaveLength(1); + expect(t.isStringLiteral(template.children[0].tag, { value: 'div' })).toBe(true); + }); + + it("should correctly parse the path of TemplateParticle's dynamic props in root element", () => { + const viewParticles = parse('div().class(this.flag); {div("ok")}'); + + const dynamicProps = (viewParticles[0] as TemplateParticle).props; + expect(dynamicProps).toHaveLength(1); + const prop = dynamicProps[0]; + // ---- Path will be [] because it's the root element + expect(prop.path).toHaveLength(0); + }); + + it("should correctly parse the path of TemplateParticle's dynamic props in nested element", () => { + const viewParticles = parse('div(); {div().class(this.flag)}'); + + const dynamicProps = (viewParticles[0] as TemplateParticle).props; + expect(dynamicProps).toHaveLength(1); + const prop = dynamicProps[0]; + // ---- Path will be [0] because it's the first child of the root element + expect(prop.path).toHaveLength(1); + expect(prop.path[0]).toBe(0); + }); + + it("should correctly parse the path of TemplateParticle's dynamic props with mutable particles ahead", () => { + const viewParticles = parse('div(); { Comp(); div().class(this.flag) }'); + + const dynamicProps = (viewParticles[0] as TemplateParticle).props; + expect(dynamicProps).toHaveLength(1); + const prop = dynamicProps[0]; + // ---- Path will be [0] because it's the first child of the root element + expect(prop.path).toHaveLength(1); + expect(prop.path[0]).toBe(0); + }); + + it("should correctly parse the path of TemplateParticle's mutableParticles", () => { + const viewParticles = parse('div(); {div(); Comp(); div();}'); + const mutableParticles = (viewParticles[0] as TemplateParticle).mutableParticles; + expect(mutableParticles).toHaveLength(1); + + const mutableParticle = mutableParticles[0]; + expect(mutableParticle.path).toHaveLength(1); + expect(mutableParticle.path[0]).toBe(1); + }); +}); diff --git a/packages/transpiler/reactivity-parser/src/test/mock.ts b/packages/transpiler/reactivity-parser/src/test/mock.ts new file mode 100644 index 00000000..73a54b24 --- /dev/null +++ b/packages/transpiler/reactivity-parser/src/test/mock.ts @@ -0,0 +1,250 @@ +import babelApi, { parseSync, type types as t } from '@babel/core'; +import { type ReactivityParserConfig } from '../types'; +import { parseView as pV, type ViewParserConfig } from '@inula/view-parser'; +import { parseReactivity as pR } from '../index'; + +const htmlTags = [ + 'a', + 'abbr', + 'address', + 'area', + 'article', + 'aside', + 'audio', + 'b', + 'base', + 'bdi', + 'bdo', + 'blockquote', + 'body', + 'br', + 'button', + 'canvas', + 'caption', + 'cite', + 'code', + 'col', + 'colgroup', + 'data', + 'datalist', + 'dd', + 'del', + 'details', + 'dfn', + 'dialog', + 'div', + 'dl', + 'dt', + 'em', + 'embed', + 'fieldset', + 'figcaption', + 'figure', + 'footer', + 'form', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'head', + 'header', + 'hgroup', + 'hr', + 'html', + 'i', + 'iframe', + 'img', + 'input', + 'ins', + 'kbd', + 'label', + 'legend', + 'li', + 'link', + 'main', + 'map', + 'mark', + 'menu', + 'meta', + 'meter', + 'nav', + 'noscript', + 'object', + 'ol', + 'optgroup', + 'option', + 'output', + 'p', + 'picture', + 'pre', + 'progress', + 'q', + 'rp', + 'rt', + 'ruby', + 's', + 'samp', + 'script', + 'section', + 'select', + 'slot', + 'small', + 'source', + 'span', + 'strong', + 'style', + 'sub', + 'summary', + 'sup', + 'table', + 'tbody', + 'td', + 'template', + 'textarea', + 'tfoot', + 'th', + 'thead', + 'time', + 'title', + 'tr', + 'track', + 'u', + 'ul', + 'var', + 'video', + 'wbr', + 'acronym', + 'applet', + 'basefont', + 'bgsound', + 'big', + 'blink', + 'center', + 'dir', + 'font', + 'frame', + 'frameset', + 'isindex', + 'keygen', + 'listing', + 'marquee', + 'menuitem', + 'multicol', + 'nextid', + 'nobr', + 'noembed', + 'noframes', + 'param', + 'plaintext', + 'rb', + 'rtc', + 'spacer', + 'strike', + 'tt', + 'xmp', + 'animate', + 'animateMotion', + 'animateTransform', + 'circle', + 'clipPath', + 'defs', + 'desc', + 'ellipse', + 'feBlend', + 'feColorMatrix', + 'feComponentTransfer', + 'feComposite', + 'feConvolveMatrix', + 'feDiffuseLighting', + 'feDisplacementMap', + 'feDistantLight', + 'feDropShadow', + 'feFlood', + 'feFuncA', + 'feFuncB', + 'feFuncG', + 'feFuncR', + 'feGaussianBlur', + 'feImage', + 'feMerge', + 'feMergeNode', + 'feMorphology', + 'feOffset', + 'fePointLight', + 'feSpecularLighting', + 'feSpotLight', + 'feTile', + 'feTurbulence', + 'filter', + 'foreignObject', + 'g', + 'image', + 'line', + 'linearGradient', + 'marker', + 'mask', + 'metadata', + 'mpath', + 'path', + 'pattern', + 'polygon', + 'polyline', + 'radialGradient', + 'rect', + 'set', + 'stop', + 'svg', + 'switch', + 'symbol', + 'text', + 'textPath', + 'tspan', + 'use', + 'view', +]; +const snippetNames = ['MySnippet', 'InnerButton']; + +export const availableProperties = ['flag', 'count', 'doubleCount', 'array', 'state1', 'state2', 'state3', 'state4']; +const dependencyMap = { + count: ['flag'], + doubleCount: ['count', 'flag'], + array: ['count', 'flag'], + state1: ['count', 'flag'], + state2: ['count', 'flag', 'state1'], + state3: ['count', 'flag', 'state1', 'state2'], + state4: ['count', 'flag', 'state1', 'state2', 'state3'], +}; + +const viewConfig: ViewParserConfig = { + babelApi, + htmlTags, + snippetNames, +}; + +export const reactivityConfig: ReactivityParserConfig = { + babelApi, + availableProperties, + dependencyMap, +}; + +export function parseCode(code: string) { + return (parseSync(`function code() {${code}}`)!.program.body[0] as t.FunctionDeclaration).body; +} + +function parseViewFromStatement(statement: t.BlockStatement) { + return pV(statement, viewConfig); +} + +export function parseView(code: string) { + return parseViewFromStatement(parseCode(code)); +} + +export function parseReactivity(statement: t.BlockStatement) { + return pR(parseViewFromStatement(statement), reactivityConfig)[0]; +} + +export function parse(code: string) { + return pR(parseView(code), reactivityConfig)[0]; +} diff --git a/packages/transpiler/reactivity-parser/src/types.ts b/packages/transpiler/reactivity-parser/src/types.ts new file mode 100644 index 00000000..590e017d --- /dev/null +++ b/packages/transpiler/reactivity-parser/src/types.ts @@ -0,0 +1,135 @@ +import { type types as t } from '@babel/core'; +import type Babel from '@babel/core'; + +export interface DependencyValue { + value: T; + dynamic: boolean; + dependencyIndexArr: number[]; + dependenciesNode: t.ArrayExpression; +} + +export interface DependencyProp { + value: t.Expression; + viewPropMap: Record; + dynamic: boolean; + dependencyIndexArr: number[]; + dependenciesNode: t.ArrayExpression; +} + +export interface TemplateProp { + tag: string; + key: string; + path: number[]; + value: t.Expression; + dynamic: boolean; + dependencyIndexArr: number[]; + dependenciesNode: t.ArrayExpression; +} + +export type MutableParticle = ViewParticle & { path: number[] }; + +export interface TemplateParticle { + type: 'template'; + template: HTMLParticle; + mutableParticles: MutableParticle[]; + props: TemplateProp[]; +} + +export interface TextParticle { + type: 'text'; + content: DependencyValue; +} + +export interface HTMLParticle { + type: 'html'; + tag: t.Expression; + props: Record>; + children: ViewParticle[]; +} + +export interface CompParticle { + type: 'comp'; + tag: t.Expression; + props: Record; + children: ViewParticle[]; +} + +export interface ForParticle { + type: 'for'; + item: t.LVal; + array: DependencyValue; + key: t.Expression; + children: ViewParticle[]; +} + +export interface IfBranch { + condition: DependencyValue; + children: ViewParticle[]; +} + +export interface IfParticle { + type: 'if'; + branches: IfBranch[]; +} + +export interface SwitchBranch { + case: DependencyValue; + children: ViewParticle[]; + break: boolean; +} + +export interface SwitchParticle { + type: 'switch'; + discriminant: DependencyValue; + branches: SwitchBranch[]; +} + +export interface TryParticle { + type: 'try'; + children: ViewParticle[]; + exception: t.Identifier | t.ArrayPattern | t.ObjectPattern | null; + catchChildren: ViewParticle[]; +} + +export interface EnvParticle { + type: 'env'; + props: Record; + children: ViewParticle[]; +} + +export interface ExpParticle { + type: 'exp'; + content: DependencyProp; + props: Record; +} + +export interface SnippetParticle { + type: 'snippet'; + tag: string; + props: Record; + children: ViewParticle[]; +} + +export type ViewParticle = + | TemplateParticle + | TextParticle + | HTMLParticle + | CompParticle + | ForParticle + | IfParticle + | EnvParticle + | ExpParticle + | SwitchParticle + | SnippetParticle + | TryParticle; + +export interface ReactivityParserConfig { + babelApi: typeof Babel; + availableProperties: string[]; + availableIdentifiers?: string[]; + dependencyMap: Record; + identifierDepMap?: Record; + dependencyParseType?: 'property' | 'identifier'; + parseTemplate?: boolean; + reactivityFuncNames?: string[]; +} diff --git a/packages/transpiler/reactivity-parser/tsconfig.json b/packages/transpiler/reactivity-parser/tsconfig.json new file mode 100644 index 00000000..e0932d78 --- /dev/null +++ b/packages/transpiler/reactivity-parser/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "esModuleInterop": true + }, + "ts-node": { + "esm": true + } +} \ No newline at end of file diff --git a/packages/transpiler/view-generator/package.json b/packages/transpiler/view-generator/package.json new file mode 100644 index 00000000..66817afb --- /dev/null +++ b/packages/transpiler/view-generator/package.json @@ -0,0 +1,44 @@ +{ + "name": "@inula/view-generator", + "version": "0.0.1", + "author": { + "name": "IanDx", + "email": "iandxssxx@gmail.com" + }, + "keywords": [ + "dlight.js" + ], + "license": "MIT", + "files": [ + "dist" + ], + "type": "module", + "main": "dist/index.cjs", + "module": "dist/index.js", + "typings": "dist/index.d.ts", + "scripts": { + "build": "tsup --sourcemap" + }, + "devDependencies": { + "@babel/core": "^7.20.12", + "@types/babel__core": "^7.20.5", + "@types/node": "^20.10.5", + "tsup": "^6.7.0", + "typescript": "^5.3.2", + "@inula/reactivity-parser": "workspace:*" + }, + "dependencies": { + "@inula/error-handler": "workspace:*" + }, + "tsup": { + "entry": [ + "src/index.ts" + ], + "format": [ + "cjs", + "esm" + ], + "clean": true, + "dts": true + } +} diff --git a/packages/transpiler/view-generator/src/HelperGenerators/BaseGenerator.ts b/packages/transpiler/view-generator/src/HelperGenerators/BaseGenerator.ts new file mode 100644 index 00000000..f66c62d7 --- /dev/null +++ b/packages/transpiler/view-generator/src/HelperGenerators/BaseGenerator.ts @@ -0,0 +1,284 @@ +import { type types as t, type traverse } from '@babel/core'; +import { type ViewParticle } from '@inula/reactivity-parser'; +import { type SnippetPropMap, type ViewGeneratorConfig } from '../types'; +import ViewGenerator from '../ViewGenerator'; + +export const prefixMap = { template: '$t', node: '$node' }; + +export default class BaseGenerator { + readonly viewParticle: ViewParticle; + readonly config: ViewGeneratorConfig; + + readonly t: typeof t; + readonly traverse: typeof traverse; + readonly className: string; + readonly importMap: Record; + readonly snippetPropMap: SnippetPropMap; + readonly elementAttributeMap; + readonly alterAttributeMap; + + readonly viewGenerator; + + /** + * @brief Constructor + * @param viewUnit + * @param config + */ + constructor(viewParticle: ViewParticle, config: ViewGeneratorConfig) { + this.viewParticle = viewParticle; + this.config = config; + this.t = config.babelApi.types; + this.traverse = config.babelApi.traverse; + this.className = config.className; + this.importMap = config.importMap; + this.snippetPropMap = config.snippetPropMap; + this.viewGenerator = new ViewGenerator(config); + this.elementAttributeMap = config.attributeMap + ? Object.entries(config.attributeMap).reduce>((acc, [key, elements]) => { + elements.forEach(element => { + if (!acc[element]) acc[element] = []; + acc[element].push(key); + }); + return acc; + }, {}) + : {}; + this.alterAttributeMap = config.alterAttributeMap; + } + + // ---- Init Statements + private readonly initStatements: t.Statement[] = []; + addInitStatement(...statements: (t.Statement | null)[]) { + this.initStatements.push(...(statements.filter(Boolean) as t.Statement[])); + } + + // ---- Added Class Properties, typically used in for Template + private readonly classProperties: t.ClassProperty[] = []; + addStaticClassProperty(key: string, value: t.Expression) { + this.classProperties.push( + this.t.classProperty(this.t.identifier(key), value, undefined, undefined, undefined, true) + ); + } + + // ---- Update Statements + private readonly updateStatements: Record = {}; + addUpdateStatements(dependencies: number[] | undefined, statement: t.Statement | undefined | null) { + if (!dependencies || dependencies.length === 0) return; + const depNum = BaseGenerator.calcDependencyNum(dependencies); + if (!this.updateStatements[depNum]) this.updateStatements[depNum] = []; + if (statement) this.updateStatements[depNum].push(statement); + } + + addUpdateStatementsWithoutDep(statement: t.Statement) { + if (!this.updateStatements[0]) this.updateStatements[0] = []; + this.updateStatements[0].push(statement); + } + + /** + * @returns [initStatements, updateStatements, classProperties, nodeName] + */ + generate(): [t.Statement[], Record, t.ClassProperty[], string] { + const nodeName = this.run(); + return [this.initStatements, this.updateStatements, this.classProperties, nodeName]; + } + + /** + * @brief Generate the view given the view particles, mainly used for child particles parsing + * @param viewParticles + * @param mergeStatements + * @returns [initStatements, topLevelNodes, updateStatements] + */ + generateChildren( + viewParticles: ViewParticle[], + mergeStatements = true, + newIdx = false + ): [t.Statement[], string[], Record, number] { + this.viewGenerator.nodeIdx = newIdx ? -1 : this.nodeIdx; + this.viewGenerator.templateIdx = this.templateIdx; + const [initStatements, updateStatements, classProperties, topLevelNodes] = + this.viewGenerator.generateChildren(viewParticles); + if (!newIdx) this.nodeIdx = this.viewGenerator.nodeIdx; + this.templateIdx = this.viewGenerator.templateIdx; + this.classProperties.push(...classProperties); + if (mergeStatements) this.mergeStatements(updateStatements); + + return [initStatements, topLevelNodes, updateStatements, this.viewGenerator.nodeIdx]; + } + + /** + * @brief Merge the update statements + * @param statements + */ + private mergeStatements(statements: Record): void { + Object.entries(statements).forEach(([depNum, statements]) => { + if (!this.updateStatements[Number(depNum)]) { + this.updateStatements[Number(depNum)] = []; + } + this.updateStatements[Number(depNum)].push(...statements); + }); + } + + /** + * @brief Generate the view given the view particle + * @param viewParticle + * @param mergeStatements + * @returns [initStatements, nodeName, updateStatements] + */ + generateChild( + viewParticle: ViewParticle, + mergeStatements = true, + newIdx = false + ): [t.Statement[], string, Record, number] { + this.viewGenerator.nodeIdx = newIdx ? -1 : this.nodeIdx; + this.viewGenerator.templateIdx = this.templateIdx; + const [initStatements, updateStatements, classProperties, nodeName] = + this.viewGenerator.generateChild(viewParticle); + if (!newIdx) this.nodeIdx = this.viewGenerator.nodeIdx; + this.templateIdx = this.viewGenerator.templateIdx; + this.classProperties.push(...classProperties); + if (mergeStatements) this.mergeStatements(updateStatements); + + return [initStatements, nodeName, updateStatements, this.viewGenerator.nodeIdx]; + } + + /** + * @View + * const $update = (changed) => { ${updateStatements} } + */ + geneUpdateFunc(updateStatements: Record): t.Statement { + return this.t.variableDeclaration('const', [ + this.t.variableDeclarator( + this.t.identifier('$update'), + this.t.arrowFunctionExpression([this.t.identifier('$changed')], this.geneUpdateBody(updateStatements)) + ), + ]); + } + + get updateParams() { + return [this.t.identifier('$changed')]; + } + + /** + * @View + * (changed) => { + * if (changed & 1) { + * ... + * } + * ... + * } + */ + geneUpdateBody(updateStatements: Record): t.BlockStatement { + return this.t.blockStatement([ + ...Object.entries(updateStatements) + .filter(([depNum]) => depNum !== '0') + .map(([depNum, statements]) => { + return this.t.ifStatement( + this.t.binaryExpression('&', this.t.identifier('$changed'), this.t.numericLiteral(Number(depNum))), + this.t.blockStatement(statements) + ); + }), + ...(updateStatements[0] ?? []), + ]); + } + + /** + * @View + * let node1, node2, ... + */ + declareNodes(nodeIdx: number): t.VariableDeclaration[] { + if (nodeIdx === -1) return []; + return [ + this.t.variableDeclaration( + 'let', + Array.from({ length: nodeIdx + 1 }, (_, i) => + this.t.variableDeclarator(this.t.identifier(`${prefixMap.node}${i}`)) + ) + ), + ]; + } + + /** + * @View + * return [${topLevelNodes}] + */ + generateReturnStatement(topLevelNodes: string[]): t.ReturnStatement { + return this.t.returnStatement(this.t.arrayExpression(topLevelNodes.map(name => this.t.identifier(name)))); + } + + /** + * @brief To be implemented by the subclass as the main node generation function + * @returns dlNodeName + */ + run(): string { + return ''; + } + + // ---- Name ---- + // ---- Used as dlNodeName for any node declaration + nodeIdx = -1; + generateNodeName(idx?: number): string { + return `${prefixMap.node}${idx ?? ++this.nodeIdx}`; + } + + // ---- Used as template generation as class property + templateIdx = -1; + generateTemplateName(): string { + return `${prefixMap.template}${++this.templateIdx}`; + } + + // ---- @Utils ----- + /** + * + * @param updateStatements + * @returns + */ + + /** + * @brief Calculate the dependency number from an array of dependency index + * e.g. + * [0, 1, 2] => 0b111 => 7 + * [1, 3] => 0b1010 => 10 + * @param dependencies + * @returns dependency number + */ + static calcDependencyNum(dependencies: number[] | undefined): number { + if (!dependencies || dependencies.length === 0) return 0; + dependencies = [...new Set(dependencies)]; + return dependencies.reduce((acc, dep) => acc + (1 << dep), 0); + } + + /** + * @brief Wrap the value in a file + * @param node + * @returns wrapped value + */ + valueWrapper(node: t.Expression | t.Statement): t.File { + return this.t.file(this.t.program([this.t.isStatement(node) ? node : this.t.expressionStatement(node)])); + } + + /** + * @View + * ${dlNodeName} && ${expression} + */ + optionalExpression(dlNodeName: string, expression: t.Expression): t.Statement { + return this.t.expressionStatement(this.t.logicalExpression('&&', this.t.identifier(dlNodeName), expression)); + } + + /** + * @brief Shorthand function for collecting statements in batch + * @returns [statements, collect] + */ + static statementsCollector(): [t.Statement[], (...statements: t.Statement[] | t.Statement[][]) => void] { + const statements: t.Statement[] = []; + const collect = (...newStatements: t.Statement[] | t.Statement[][]) => { + newStatements.forEach(s => { + if (Array.isArray(s)) { + statements.push(...s); + } else { + statements.push(s); + } + }); + }; + + return [statements, collect]; + } +} diff --git a/packages/transpiler/view-generator/src/HelperGenerators/CondGenerator.ts b/packages/transpiler/view-generator/src/HelperGenerators/CondGenerator.ts new file mode 100644 index 00000000..f1816b09 --- /dev/null +++ b/packages/transpiler/view-generator/src/HelperGenerators/CondGenerator.ts @@ -0,0 +1,112 @@ +import { type types as t } from '@babel/core'; +import BaseGenerator from './BaseGenerator'; + +export default class CondGenerator extends BaseGenerator { + /** + * @View + * $thisCond.cond = ${idx} + */ + geneCondIdx(idx: number): t.ExpressionStatement { + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('cond')), + this.t.numericLiteral(idx) + ) + ); + } + + /** + * @View + * if ($thisCond.cond === ${idx}) { + * $thisCond.didntChange = true + * return [] + * } + */ + geneCondCheck(idx: number): t.IfStatement { + return this.t.ifStatement( + this.t.binaryExpression( + '===', + this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('cond')), + this.t.numericLiteral(idx) + ), + this.t.blockStatement([ + this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('didntChange')), + this.t.booleanLiteral(true) + ) + ), + this.t.returnStatement(this.t.arrayExpression([])), + ]) + ); + } + + /** + * @View + * ${dlNodeName}?.updateCond(key) + */ + updateCondNodeCond(dlNodeName: string): t.Statement { + return this.optionalExpression( + dlNodeName, + this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('updateCond')), [ + ...this.updateParams.slice(1), + ]) + ); + } + + /** + * @View + * ${dlNodeName}?.update(changed) + */ + updateCondNode(dlNodeName: string): t.Statement { + return this.optionalExpression( + dlNodeName, + this.t.callExpression( + this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('update')), + this.updateParams + ) + ); + } + + /** + * @View + * ${dlNodeName} = new CondNode(${depNum}, ($thisCond) => {}) + */ + declareCondNode(dlNodeName: string, condFunc: t.BlockStatement, deps: number[]): t.Statement { + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.identifier(dlNodeName), + this.t.newExpression(this.t.identifier(this.importMap.CondNode), [ + this.t.numericLiteral(CondGenerator.calcDependencyNum(deps)), + this.t.arrowFunctionExpression([this.t.identifier('$thisCond')], condFunc), + ]) + ) + ); + } + + /** + * return $thisCond.cond === ${branchIdx} ? [${nodeNames}] : $thisCond.updateCond() + */ + geneCondReturnStatement(nodeNames: string[], branchIdx: number): t.Statement { + // ---- If the returned cond is not the last one, + // it means it's been altered in the childrenNodes, + // so we update the cond again to get the right one + return this.t.returnStatement( + this.t.conditionalExpression( + this.t.binaryExpression( + '===', + this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('cond')), + this.t.numericLiteral(branchIdx) + ), + this.t.arrayExpression(nodeNames.map(name => this.t.identifier(name))), + this.t.callExpression( + this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('updateCond')), + this.updateParams.slice(1) + ) + ) + ); + } +} diff --git a/packages/transpiler/view-generator/src/HelperGenerators/ElementGenerator.ts b/packages/transpiler/view-generator/src/HelperGenerators/ElementGenerator.ts new file mode 100644 index 00000000..b503994b --- /dev/null +++ b/packages/transpiler/view-generator/src/HelperGenerators/ElementGenerator.ts @@ -0,0 +1,52 @@ +import { type types as t } from '@babel/core'; +import PropViewGenerator from './PropViewGenerator'; + +export default class ElementGenerator extends PropViewGenerator { + /** + * @View + * el: + * View.addDidMount(${dlNodeName}, () => ( + * typeof ${value} === "function" ? ${value}($nodeEl) : ${value} = $nodeEl + * )) + * not el: + * typeof ${value} === "function" ? ${value}($nodeEl) : ${value} = $nodeEl + * @param el true: dlNodeName._$el, false: dlNodeName + */ + initElement(dlNodeName: string, value: t.Expression, el = false): t.Statement { + const elNode = el + ? this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('_$el')) + : this.t.identifier(dlNodeName); + let elementNode; + if (this.isOnlyMemberExpression(value)) { + elementNode = this.t.conditionalExpression( + this.t.binaryExpression('===', this.t.unaryExpression('typeof', value, true), this.t.stringLiteral('function')), + this.t.callExpression(value, [elNode]), + this.t.assignmentExpression('=', value as t.LVal, elNode) + ); + } else { + elementNode = this.t.callExpression(value, [elNode]); + } + + return el + ? this.t.expressionStatement( + this.t.callExpression(this.t.memberExpression(this.t.identifier('View'), this.t.identifier('addDidMount')), [ + this.t.identifier(dlNodeName), + this.t.arrowFunctionExpression([], elementNode), + ]) + ) + : this.t.expressionStatement(elementNode); + } + + // --- Utils + private isOnlyMemberExpression(value: t.Expression): boolean { + if (!this.t.isMemberExpression(value)) return false; + while (value.property) { + if (this.t.isMemberExpression(value.property)) { + value = value.property; + continue; + } else if (this.t.isIdentifier(value.property)) break; + else return false; + } + return true; + } +} diff --git a/packages/transpiler/view-generator/src/HelperGenerators/ForwardPropGenerator.ts b/packages/transpiler/view-generator/src/HelperGenerators/ForwardPropGenerator.ts new file mode 100644 index 00000000..0dfcd599 --- /dev/null +++ b/packages/transpiler/view-generator/src/HelperGenerators/ForwardPropGenerator.ts @@ -0,0 +1,16 @@ +import { type types as t } from '@babel/core'; +import ElementGenerator from './ElementGenerator'; + +export default class ForwardPropsGenerator extends ElementGenerator { + /** + * @View + * this._$forwardProp(${dlNodeName}) + */ + forwardProps(dlNodeName: string): t.ExpressionStatement { + return this.t.expressionStatement( + this.t.callExpression(this.t.memberExpression(this.t.thisExpression(), this.t.identifier('_$addForwardProps')), [ + this.t.identifier(dlNodeName), + ]) + ); + } +} diff --git a/packages/transpiler/view-generator/src/HelperGenerators/HTMLPropGenerator.ts b/packages/transpiler/view-generator/src/HelperGenerators/HTMLPropGenerator.ts new file mode 100644 index 00000000..d703bf36 --- /dev/null +++ b/packages/transpiler/view-generator/src/HelperGenerators/HTMLPropGenerator.ts @@ -0,0 +1,363 @@ +import { type types as t } from '@babel/core'; +import { DLError } from '../error'; +import ForwardPropGenerator from './ForwardPropGenerator'; + +export default class HTMLPropGenerator extends ForwardPropGenerator { + static DelegatedEvents = new Set([ + 'beforeinput', + 'click', + 'dblclick', + 'contextmenu', + 'focusin', + 'focusout', + 'input', + 'keydown', + 'keyup', + 'mousedown', + 'mousemove', + 'mouseout', + 'mouseover', + 'mouseup', + 'pointerdown', + 'pointermove', + 'pointerout', + 'pointerover', + 'pointerup', + 'touchend', + 'touchmove', + 'touchstart', + ]); + + /** + * @brief Add any HTML props according to the key + * @param name + * @param tag + * @param key + * @param value + * @param dependencyIndexArr + * @returns t.Statement + */ + addHTMLProp( + name: string, + tag: string, + key: string, + value: t.Expression, + dynamic: boolean, + dependencyIndexArr: number[], + dependenciesNode: t.ArrayExpression + ): t.Statement | null { + // ---- Dynamic HTML prop with init and update + if (dynamic) { + this.addUpdateStatements( + dependencyIndexArr, + this.setDynamicHTMLProp(name, tag, key, value, dependenciesNode, true) + ); + return this.setDynamicHTMLProp(name, tag, key, value, dependenciesNode, false); + } + // ---- Static HTML prop with init only + return this.setStaticHTMLProp(name, tag, key, value); + } + + /** + * @View + * insertNode(${dlNodeName}, ${childNodeName}, ${position}) + */ + insertNode(dlNodeName: string, childNodeName: string, position: number): t.ExpressionStatement { + return this.t.expressionStatement( + this.t.callExpression(this.t.identifier(this.importMap.insertNode), [ + this.t.identifier(dlNodeName), + this.t.identifier(childNodeName), + this.t.numericLiteral(position), + ]) + ); + } + + /** + * @View + * ${dlNodeName} && ${expression} + */ + private setPropWithCheck(dlNodeName: string, expression: t.Expression, check: boolean): t.Statement { + if (check) { + return this.optionalExpression(dlNodeName, expression); + } + return this.t.expressionStatement(expression); + } + + /** + * @View + * setStyle(${dlNodeName}, ${value}) + */ + private setHTMLStyle(dlNodeName: string, value: t.Expression, check: boolean): t.Statement { + return this.setPropWithCheck( + dlNodeName, + this.t.callExpression(this.t.identifier(this.importMap.setStyle), [this.t.identifier(dlNodeName), value]), + check + ); + } + + /** + * @View + * setStyle(${dlNodeName}, ${value}) + */ + private setHTMLDataset(dlNodeName: string, value: t.Expression, check: boolean): t.Statement { + return this.setPropWithCheck( + dlNodeName, + this.t.callExpression(this.t.identifier(this.importMap.setDataset), [this.t.identifier(dlNodeName), value]), + check + ); + } + + /** + * @View + * ${dlNodeName}.${key} = ${value} + */ + private setHTMLProp(dlNodeName: string, key: string, value: t.Expression): t.Statement { + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier(key)), + value + ) + ); + } + + /** + * @View + * ${dlNodeName}.setAttribute(${key}, ${value}) + */ + private setHTMLAttr(dlNodeName: string, key: string, value: t.Expression): t.Statement { + return this.t.expressionStatement( + this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('setAttribute')), [ + this.t.stringLiteral(key), + value, + ]) + ); + } + + /** + * @View + * ${dlNodeName}.addEventListener(${key}, ${value}) + */ + private setHTMLEvent(dlNodeName: string, key: string, value: t.Expression): t.Statement { + return this.t.expressionStatement( + this.t.callExpression( + this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('addEventListener')), + [this.t.stringLiteral(key), value] + ) + ); + } + + /** + * @View + * setEvent(${dlNodeName}, ${key}, ${value}) + */ + private setEvent(dlNodeName: string, key: string, value: t.Expression, check: boolean): t.Statement { + return this.setPropWithCheck( + dlNodeName, + this.t.callExpression(this.t.identifier(this.importMap.setEvent), [ + this.t.identifier(dlNodeName), + this.t.stringLiteral(key), + value, + ]), + check + ); + } + + /** + * @View + * delegateEvent(${dlNodeName}, ${key}, ${value}) + */ + private delegateEvent(dlNodeName: string, key: string, value: t.Expression, check: boolean): t.Statement { + return this.setPropWithCheck( + dlNodeName, + this.t.callExpression(this.t.identifier(this.importMap.delegateEvent), [ + this.t.identifier(dlNodeName), + this.t.stringLiteral(key), + value, + ]), + check + ); + } + + /** + * @View + * setHTMLProp(${dlNodeName}, ${key}, ${valueFunc}, ${dependenciesNode}) + */ + private setCachedProp( + dlNodeName: string, + key: string, + value: t.Expression, + dependenciesNode: t.ArrayExpression, + check: boolean + ): t.Statement { + return this.setPropWithCheck( + dlNodeName, + this.t.callExpression(this.t.identifier(this.importMap.setHTMLProp), [ + this.t.identifier(dlNodeName), + this.t.stringLiteral(key), + this.t.arrowFunctionExpression([], value), + dependenciesNode, + ]), + check + ); + } + + /** + * @View + * setHTMLAttr(${dlNodeName}, ${key}, ${valueFunc}, ${dependenciesNode}, ${check}) + */ + private setCachedAttr( + dlNodeName: string, + key: string, + value: t.Expression, + dependenciesNode: t.ArrayExpression, + check: boolean + ): t.Statement { + return this.setPropWithCheck( + dlNodeName, + this.t.callExpression(this.t.identifier(this.importMap.setHTMLAttr), [ + this.t.identifier(dlNodeName), + this.t.stringLiteral(key), + this.t.arrowFunctionExpression([], value), + dependenciesNode, + ]), + check + ); + } + + /** + * @View + * setHTMLProps(${dlNodeName}, ${value}) + */ + private setHTMLPropObject(dlNodeName: string, value: t.Expression, check: boolean): t.Statement { + return this.setPropWithCheck( + dlNodeName, + this.t.callExpression(this.t.identifier(this.importMap.setHTMLProps), [this.t.identifier(dlNodeName), value]), + check + ); + } + + /** + * @View + * setHTMLAttrs(${dlNodeName}, ${value}) + */ + private setHTMLAttrObject(dlNodeName: string, value: t.Expression, check: boolean): t.Statement { + return this.setPropWithCheck( + dlNodeName, + this.t.callExpression(this.t.identifier(this.importMap.setHTMLAttrs), [this.t.identifier(dlNodeName), value]), + check + ); + } + + private static commonHTMLPropKeys = [ + 'style', + 'dataset', + 'props', + 'ref', + 'attrs', + 'forwardProps', + ...HTMLPropGenerator.lifecycle, + ]; + + /** + * For style/dataset/ref/attr/prop + */ + private addCommonHTMLProp( + dlNodeName: string, + attrName: string, + value: t.Expression, + check: boolean + ): t.Statement | null { + if (HTMLPropGenerator.lifecycle.includes(attrName as (typeof HTMLPropGenerator.lifecycle)[number])) { + if (!check) return this.addLifecycle(dlNodeName, attrName as (typeof HTMLPropGenerator.lifecycle)[number], value); + return null; + } + if (attrName === 'ref') { + if (!check) return this.initElement(dlNodeName, value); + return null; + } + if (attrName === 'style') return this.setHTMLStyle(dlNodeName, value, check); + if (attrName === 'dataset') return this.setHTMLDataset(dlNodeName, value, check); + if (attrName === 'props') return this.setHTMLPropObject(dlNodeName, value, check); + if (attrName === 'attrs') return this.setHTMLAttrObject(dlNodeName, value, check); + if (attrName === 'forwardProps') return this.forwardProps(dlNodeName); + return DLError.throw2(); + } + + /** + * @View + * 1. Event listener + * - ${dlNodeName}.addEventListener(${key}, ${value}) + * 2. HTML internal attribute -> DOM property + * - ${dlNodeName}.${key} = ${value} + * 3. HTML custom attribute + * - ${dlNodeName}.setAttribute(${key}, ${value}) + */ + private setStaticHTMLProp( + dlNodeName: string, + tag: string, + attrName: string, + value: t.Expression + ): t.Statement | null { + if (HTMLPropGenerator.commonHTMLPropKeys.includes(attrName)) + return this.addCommonHTMLProp(dlNodeName, attrName, value, false); + if (attrName.startsWith('on')) { + const eventName = attrName.slice(2).toLowerCase(); + if (HTMLPropGenerator.DelegatedEvents.has(eventName)) { + return this.delegateEvent(dlNodeName, eventName, value, false); + } + return this.setHTMLEvent(dlNodeName, eventName, value); + } + if (this.isInternalAttribute(tag, attrName)) { + if (attrName === 'class') attrName = 'className'; + else if (attrName === 'for') attrName = 'htmlFor'; + return this.setHTMLProp(dlNodeName, attrName, value); + } + return this.setHTMLAttr(dlNodeName, attrName, value); + } + + /** + * @View + * 1. Event listener + * - ${setEvent}(${dlNodeName}, ${key}, ${value}) + * 2. HTML internal attribute -> DOM property + * - ${setHTMLProp}(${dlNodeName}, ${key}, ${value}) + * 3. HTML custom attribute + * - ${setHTMLAttr}(${dlNodeName}, ${key}, ${value}) + */ + private setDynamicHTMLProp( + dlNodeName: string, + tag: string, + attrName: string, + value: t.Expression, + dependenciesNode: t.ArrayExpression, + check: boolean + ): t.Statement | null { + if (HTMLPropGenerator.commonHTMLPropKeys.includes(attrName)) + return this.addCommonHTMLProp(dlNodeName, attrName, value, check); + if (attrName.startsWith('on')) { + const eventName = attrName.slice(2).toLowerCase(); + if (HTMLPropGenerator.DelegatedEvents.has(eventName)) { + return this.delegateEvent(dlNodeName, eventName, value, check); + } + return this.setEvent(dlNodeName, eventName, value, check); + } + if (this.alterAttributeMap[attrName]) { + attrName = this.alterAttributeMap[attrName]; + } + if (this.isInternalAttribute(tag, attrName)) { + return this.setCachedProp(dlNodeName, attrName, value, dependenciesNode, check); + } + return this.setCachedAttr(dlNodeName, attrName, value, dependenciesNode, check); + } + + /** + * @brief Check if the attribute is internal, i.e., can be accessed as js property + * @param tag + * @param attribute + * @returns true if the attribute is internal + */ + isInternalAttribute(tag: string, attribute: string): boolean { + return this.elementAttributeMap['*']?.includes(attribute) || this.elementAttributeMap[tag]?.includes(attribute); + } +} diff --git a/packages/transpiler/view-generator/src/HelperGenerators/LifecycleGenerator.ts b/packages/transpiler/view-generator/src/HelperGenerators/LifecycleGenerator.ts new file mode 100644 index 00000000..fe03727a --- /dev/null +++ b/packages/transpiler/view-generator/src/HelperGenerators/LifecycleGenerator.ts @@ -0,0 +1,66 @@ +import { type types as t } from '@babel/core'; +import BaseGenerator from './BaseGenerator'; + +export default class LifecycleGenerator extends BaseGenerator { + static lifecycle = ['willMount', 'didMount', 'willUnmount', 'didUnmount'] as const; + + /** + * @View + * ${dlNodeName} && ${value}(${dlNodeName}, changed) + */ + addOnUpdate(dlNodeName: string, value: t.Expression): t.Statement { + return this.t.expressionStatement( + this.t.logicalExpression( + '&&', + this.t.identifier(dlNodeName), + this.t.callExpression(value, [this.t.identifier(dlNodeName), ...this.updateParams.slice(1)]) + ) + ); + } + + /** + * @View + * willMount: + * - ${value}(${dlNodeName}) + * didMount/willUnmount/didUnmount: + * - View.addDidMount(${dlNodeName}, ${value}) + */ + addLifecycle( + dlNodeName: string, + key: (typeof LifecycleGenerator.lifecycle)[number], + value: t.Expression + ): t.Statement { + if (key === 'willMount') { + return this.addWillMount(dlNodeName, value); + } + return this.addOtherLifecycle(dlNodeName, value, key); + } + + /** + * @View + * ${value}(${dlNodeName}) + */ + addWillMount(dlNodeName: string, value: t.Expression): t.ExpressionStatement { + return this.t.expressionStatement(this.t.callExpression(value, [this.t.identifier(dlNodeName)])); + } + + /** + * @View + * View.addDidMount(${dlNodeName}, ${value}) + */ + addOtherLifecycle( + dlNodeName: string, + value: t.Expression, + type: 'didMount' | 'willUnmount' | 'didUnmount' + ): t.ExpressionStatement { + return this.t.expressionStatement( + this.t.callExpression( + this.t.memberExpression( + this.t.identifier('View'), + this.t.identifier(`add${type[0].toUpperCase()}${type.slice(1)}`) + ), + [this.t.identifier(dlNodeName), value] + ) + ); + } +} diff --git a/packages/transpiler/view-generator/src/HelperGenerators/PropViewGenerator.ts b/packages/transpiler/view-generator/src/HelperGenerators/PropViewGenerator.ts new file mode 100644 index 00000000..0e5d4627 --- /dev/null +++ b/packages/transpiler/view-generator/src/HelperGenerators/PropViewGenerator.ts @@ -0,0 +1,123 @@ +import { type types as t } from '@babel/core'; +import { type DependencyProp, type ViewParticle } from '@inula/reactivity-parser'; +import LifecycleGenerator from './LifecycleGenerator'; + +export default class PropViewGenerator extends LifecycleGenerator { + /** + * @brief Alter prop view in batch + * @param props + * @returns altered props + */ + alterPropViews | undefined>(props: T): T { + if (!props) return props; + return Object.fromEntries( + Object.entries(props).map(([key, prop]) => { + return [key, this.alterPropView(prop)!]; + }) + ) as T; + } + + /** + * @View + * ${dlNodeName} = new PropView(($addUpdate) => { + * addUpdate((changed) => { ${updateStatements} }) + * ${initStatements} + * return ${topLevelNodes} + * }) + */ + declarePropView(viewParticles: ViewParticle[]) { + // ---- Generate PropView + const [initStatements, topLevelNodes, updateStatements, nodeIdx] = this.generateChildren( + viewParticles, + false, + true + ); + // ---- Add update function to the first node + /** + * $addUpdate((changed) => { ${updateStatements} }) + */ + if (Object.keys(updateStatements).length > 0) { + initStatements.unshift( + this.t.expressionStatement( + this.t.callExpression(this.t.identifier('$addUpdate'), [ + this.t.arrowFunctionExpression(this.updateParams, this.geneUpdateBody(updateStatements)), + ]) + ) + ); + } + initStatements.unshift(...this.declareNodes(nodeIdx)); + initStatements.push(this.generateReturnStatement(topLevelNodes)); + + // ---- Assign as a dlNode + const dlNodeName = this.generateNodeName(); + const propViewNode = this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.identifier(dlNodeName), + this.t.newExpression(this.t.identifier(this.importMap.PropView), [ + this.t.arrowFunctionExpression([this.t.identifier('$addUpdate')], this.t.blockStatement(initStatements)), + ]) + ) + ); + this.addInitStatement(propViewNode); + const propViewIdentifier = this.t.identifier(dlNodeName); + + // ---- Add to update statements + /** + * ${dlNodeName}?.update(changed) + */ + this.addUpdateStatementsWithoutDep( + this.optionalExpression( + dlNodeName, + this.t.callExpression( + this.t.memberExpression(propViewIdentifier, this.t.identifier('update')), + this.updateParams + ) + ) + ); + + return dlNodeName; + } + + /** + * @brief Alter prop view by replacing prop view with a recursively generated prop view + * @param prop + * @returns altered prop + */ + alterPropView(prop: T): T { + if (!prop) return prop; + const { value, viewPropMap } = prop; + if (!viewPropMap) return { ...prop, value }; + let newValue = value; + this.traverse(this.valueWrapper(value), { + StringLiteral: innerPath => { + const id = innerPath.node.value; + const viewParticles = viewPropMap[id]; + if (!viewParticles) return; + const propViewIdentifier = this.t.identifier(this.declarePropView(viewParticles)); + + if (value === innerPath.node) newValue = propViewIdentifier; + innerPath.replaceWith(propViewIdentifier); + innerPath.skip(); + }, + }); + return { ...prop, value: newValue }; + } + + /** + * @brief Get the dependency index array from the update statements' keys + * i.e. [1, 2, 7] => [0b1, 0b10, 0b111] => [[1], [2], [0, 1, 2]] => [0, 1, 2] + * @param updateStatements + * @returns dependency index array + */ + private static reverseDependencyIndexArr(updateStatements: Record): number[] { + const allDepsNum = Object.keys(updateStatements) + .map(Number) + .reduce((acc, depNum) => acc | depNum, 0); + const allDeps = []; + for (let i = 0; i < String(allDepsNum).length; i++) { + if (allDepsNum & (1 << i)) allDeps.push(i); + } + return allDeps; + } +} diff --git a/packages/transpiler/view-generator/src/MainViewGenerator.ts b/packages/transpiler/view-generator/src/MainViewGenerator.ts new file mode 100644 index 00000000..122b70fc --- /dev/null +++ b/packages/transpiler/view-generator/src/MainViewGenerator.ts @@ -0,0 +1,82 @@ +import { type types as t } from '@babel/core'; +import { type ViewParticle } from '@inula/reactivity-parser'; +import ViewGenerator from './ViewGenerator'; + +export default class MainViewGenerator extends ViewGenerator { + /** + * @brief Generate the main view, i.e., View() { ... } + * @param viewParticles + * @returns [viewBody, classProperties, templateIdx] + */ + generate(viewParticles: ViewParticle[]): [t.BlockStatement, t.ClassProperty[], number] { + const allClassProperties: t.ClassProperty[] = []; + const allInitStatements: t.Statement[] = []; + const allUpdateStatements: Record = {}; + const topLevelNodes: string[] = []; + + viewParticles.forEach(viewParticle => { + const [initStatements, updateStatements, classProperties, nodeName] = this.generateChild(viewParticle); + allInitStatements.push(...initStatements); + Object.entries(updateStatements).forEach(([depNum, statements]) => { + if (!allUpdateStatements[Number(depNum)]) { + allUpdateStatements[Number(depNum)] = []; + } + allUpdateStatements[Number(depNum)].push(...statements); + }); + allClassProperties.push(...classProperties); + topLevelNodes.push(nodeName); + }); + + const viewBody = this.t.blockStatement([ + ...this.declareNodes(), + ...this.geneUpdate(allUpdateStatements), + ...allInitStatements, + this.geneReturn(topLevelNodes), + ]); + + return [viewBody, allClassProperties, this.templateIdx]; + } + + /** + * @View + * this._$update = ($changed) => { + * if ($changed & 1) { + * ... + * } + * ... + * } + */ + private geneUpdate(updateStatements: Record): t.Statement[] { + if (Object.keys(updateStatements).length === 0) return []; + return [ + this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.memberExpression(this.t.thisExpression(), this.t.identifier('_$update'), false), + this.t.arrowFunctionExpression( + this.updateParams, + this.t.blockStatement([ + ...Object.entries(updateStatements) + .filter(([depNum]) => depNum !== '0') + .map(([depNum, statements]) => { + return this.t.ifStatement( + this.t.binaryExpression('&', this.t.identifier('$changed'), this.t.numericLiteral(Number(depNum))), + this.t.blockStatement(statements) + ); + }), + ...(updateStatements[0] ?? []), + ]) + ) + ) + ), + ]; + } + + /** + * @View + * return [${nodeNames}] + */ + private geneReturn(topLevelNodes: string[]) { + return this.t.returnStatement(this.t.arrayExpression(topLevelNodes.map(nodeName => this.t.identifier(nodeName)))); + } +} diff --git a/packages/transpiler/view-generator/src/NodeGenerators/CompGenerator.ts b/packages/transpiler/view-generator/src/NodeGenerators/CompGenerator.ts new file mode 100644 index 00000000..012a97f5 --- /dev/null +++ b/packages/transpiler/view-generator/src/NodeGenerators/CompGenerator.ts @@ -0,0 +1,153 @@ +import { type types as t } from '@babel/core'; +import { type DependencyProp, type CompParticle, type ViewParticle } from '@inula/reactivity-parser'; +import ForwardPropGenerator from '../HelperGenerators/ForwardPropGenerator'; + +export default class CompGenerator extends ForwardPropGenerator { + run() { + let { props } = this.viewParticle as CompParticle; + props = this.alterPropViews(props); + const { tag, children } = this.viewParticle as CompParticle; + + const dlNodeName = this.generateNodeName(); + + this.addInitStatement(...this.declareCompNode(dlNodeName, tag, props, children)); + const allDependencyIndexArr: number[] = []; + + // ---- Resolve props + Object.entries(props).forEach(([key, { value, dependencyIndexArr, dependenciesNode }]) => { + if (key === 'forwardProps') return; + if (key === 'didUpdate') return; + allDependencyIndexArr.push(...dependencyIndexArr); + if (CompGenerator.lifecycle.includes(key as (typeof CompGenerator.lifecycle)[number])) { + this.addInitStatement(this.addLifecycle(dlNodeName, key as (typeof CompGenerator.lifecycle)[number], value)); + return; + } + if (key === 'ref') { + this.addInitStatement(this.initElement(dlNodeName, value)); + return; + } + if (key === 'elements') { + this.addInitStatement(this.initElement(dlNodeName, value, true)); + return; + } + if (key === '_$content') { + this.addUpdateStatements(dependencyIndexArr, this.setCompContent(dlNodeName, value, dependenciesNode)); + return; + } + if (key === 'props') { + this.addUpdateStatements(dependencyIndexArr, this.setCompProps(dlNodeName, value, dependenciesNode)); + return; + } + + this.addUpdateStatements(dependencyIndexArr, this.setCompProp(dlNodeName, key, value, dependenciesNode)); + }); + + // ---- Add addUpdate last + if (props.didUpdate) { + this.addUpdateStatements(allDependencyIndexArr, this.addOnUpdate(dlNodeName, props.didUpdate.value)); + } + + return dlNodeName; + } + + /** + * @View + * null + * or + * [[prop1, value1, deps1], [prop2, value2, deps2], ... + */ + private generateCompProps(props: Record): t.Expression { + if (Object.keys(props).length === 0) return this.t.nullLiteral(); + return this.t.arrayExpression( + Object.entries(props).map(([key, { value, dependenciesNode }]) => { + return this.t.arrayExpression([this.t.stringLiteral(key), value, dependenciesNode]); + }) + ); + } + + /** + * @View + * ${dlNodeName} = new ${tag}() + * ${dlNodeName}._$init(${props}, ${content}, ${children}, ${this}) + */ + private declareCompNode( + dlNodeName: string, + tag: t.Expression, + props: Record, + children: ViewParticle[] + ): t.Statement[] { + const willForwardProps = 'forwardProps' in props; + const newProps = Object.fromEntries( + Object.entries(props).filter( + ([key]) => + !['ref', 'elements', 'forwardProps', '_$content', 'didUpdate', 'props', ...CompGenerator.lifecycle].includes( + key + ) + ) + ); + + const content = props._$content; + + return [ + this.t.expressionStatement( + this.t.assignmentExpression('=', this.t.identifier(dlNodeName), this.t.newExpression(tag, [])) + ), + this.t.expressionStatement( + this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('_$init')), [ + this.generateCompProps(newProps), + content ? this.t.arrayExpression([content.value, content.dependenciesNode]) : this.t.nullLiteral(), + children.length > 0 ? this.t.identifier(this.declarePropView(children)) : this.t.nullLiteral(), + willForwardProps ? this.t.identifier('this') : this.t.nullLiteral(), + ]) + ), + ]; + } + + /** + * @View + * ${dlNodeName}._$setContent(() => ${value}, ${dependenciesNode}) + */ + private setCompContent(dlNodeName: string, value: t.Expression, dependenciesNode: t.ArrayExpression): t.Statement { + return this.optionalExpression( + dlNodeName, + this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('_$setContent')), [ + this.t.arrowFunctionExpression([], value), + dependenciesNode, + ]) + ); + } + + /** + * @View + * ${dlNodeName}._$setProp(${key}, () => ${value}, ${dependenciesNode}) + */ + private setCompProp( + dlNodeName: string, + key: string, + value: t.Expression, + dependenciesNode: t.ArrayExpression + ): t.Statement { + return this.optionalExpression( + dlNodeName, + this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('_$setProp')), [ + this.t.stringLiteral(key), + this.t.arrowFunctionExpression([], value), + dependenciesNode, + ]) + ); + } + + /** + * @View + * ${dlNodeName}._$setProps(() => ${value}, ${dependenciesNode}) + */ + private setCompProps(dlNodeName: string, value: t.Expression, dependenciesNode: t.ArrayExpression): t.Statement { + return this.optionalExpression( + dlNodeName, + this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('_$setProps')), [ + this.t.arrowFunctionExpression([], value), + dependenciesNode, + ]) + ); + } +} diff --git a/packages/transpiler/view-generator/src/NodeGenerators/EnvGenerator.ts b/packages/transpiler/view-generator/src/NodeGenerators/EnvGenerator.ts new file mode 100644 index 00000000..ce2299ac --- /dev/null +++ b/packages/transpiler/view-generator/src/NodeGenerators/EnvGenerator.ts @@ -0,0 +1,95 @@ +import { type types as t } from '@babel/core'; +import { type ViewParticle, type DependencyProp, type EnvParticle } from '@inula/reactivity-parser'; +import PropViewGenerator from '../HelperGenerators/PropViewGenerator'; + +export default class EnvGenerator extends PropViewGenerator { + run() { + let { props } = this.viewParticle as EnvParticle; + props = this.alterPropViews(props)!; + const { children } = this.viewParticle as EnvParticle; + + const dlNodeName = this.generateNodeName(); + + this.addInitStatement(this.declareEnvNode(dlNodeName, props)); + + // ---- Children + this.addInitStatement(this.geneEnvChildren(dlNodeName, children)); + + // ---- Update props + Object.entries(props).forEach(([key, { dependencyIndexArr, value, dependenciesNode }]) => { + if (!dependencyIndexArr) return; + this.addUpdateStatements(dependencyIndexArr, this.updateEnvNode(dlNodeName, key, value, dependenciesNode)); + }); + + return dlNodeName; + } + + /** + * @View + * { ${key}: ${value}, ... } + * { ${key}: ${deps}, ... } + */ + private generateEnvs(props: Record): t.Expression[] { + return [ + this.t.objectExpression( + Object.entries(props).map(([key, { value }]) => this.t.objectProperty(this.t.identifier(key), value)) + ), + this.t.objectExpression( + Object.entries(props) + .map( + ([key, { dependenciesNode }]) => + dependenciesNode && this.t.objectProperty(this.t.identifier(key), dependenciesNode) + ) + .filter(Boolean) as t.ObjectProperty[] + ), + ]; + } + + /** + * @View + * ${dlNodeName} = new EnvNode(envs) + */ + private declareEnvNode(dlNodeName: string, props: Record): t.Statement { + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.identifier(dlNodeName), + this.t.newExpression(this.t.identifier(this.importMap.EnvNode), this.generateEnvs(props)) + ) + ); + } + + /** + * @View + * ${dlNodeName}.initNodes([${childrenNames}]) + */ + private geneEnvChildren(dlNodeName: string, children: ViewParticle[]): t.Statement { + const [statements, childrenNames] = this.generateChildren(children); + this.addInitStatement(...statements); + return this.t.expressionStatement( + this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('initNodes')), [ + this.t.arrayExpression(childrenNames.map(name => this.t.identifier(name))), + ]) + ); + } + + /** + * @View + * ${dlNodeName}.updateEnv(${key}, () => ${value}, ${dependenciesNode}) + */ + private updateEnvNode( + dlNodeName: string, + key: string, + value: t.Expression, + dependenciesNode: t.ArrayExpression + ): t.Statement { + return this.optionalExpression( + dlNodeName, + this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('updateEnv')), [ + this.t.stringLiteral(key), + this.t.arrowFunctionExpression([], value), + dependenciesNode, + ]) + ); + } +} diff --git a/packages/transpiler/view-generator/src/NodeGenerators/ExpGenerator.ts b/packages/transpiler/view-generator/src/NodeGenerators/ExpGenerator.ts new file mode 100644 index 00000000..b9781b97 --- /dev/null +++ b/packages/transpiler/view-generator/src/NodeGenerators/ExpGenerator.ts @@ -0,0 +1,76 @@ +import { type types as t } from '@babel/core'; +import { type ExpParticle } from '@inula/reactivity-parser'; +import ElementGenerator from '../HelperGenerators/ElementGenerator'; +import { DLError } from '../error'; + +export default class ExpGenerator extends ElementGenerator { + run() { + let { content, props } = this.viewParticle as ExpParticle; + content = this.alterPropView(content)!; + props = this.alterPropViews(props); + + const dlNodeName = this.generateNodeName(); + + this.addInitStatement(this.declareExpNode(dlNodeName, content.value, content.dependenciesNode)); + + if (content.dynamic) { + this.addUpdateStatements( + content.dependencyIndexArr, + this.updateExpNode(dlNodeName, content.value, content.dependenciesNode) + ); + } + + if (props) { + Object.entries(props).forEach(([key, { value }]) => { + if (ExpGenerator.lifecycle.includes(key as (typeof ExpGenerator.lifecycle)[number])) { + return this.addInitStatement( + this.addLifecycle(dlNodeName, key as (typeof ExpGenerator.lifecycle)[number], value) + ); + } + if (key === 'ref') { + return this.addInitStatement(this.initElement(dlNodeName, value)); + } + if (key === 'elements') { + return this.addInitStatement(this.initElement(dlNodeName, value, true)); + } + if (key === 'didUpdate') { + return this.addUpdateStatements(content.dependencyIndexArr, this.addOnUpdate(dlNodeName, value)); + } + DLError.warn1(key); + }); + } + + return dlNodeName; + } + + /** + * @View + * ${dlNodeName} = new ExpNode(${value}, dependenciesNode) + */ + private declareExpNode(dlNodeName: string, value: t.Expression, dependenciesNode: t.ArrayExpression): t.Statement { + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.identifier(dlNodeName), + this.t.newExpression(this.t.identifier(this.importMap.ExpNode), [ + value, + dependenciesNode ?? this.t.nullLiteral(), + ]) + ) + ); + } + + /** + * @View + * ${dlNodeName}.update(() => value, dependenciesNode) + */ + private updateExpNode(dlNodeName: string, value: t.Expression, dependenciesNode: t.ArrayExpression): t.Statement { + return this.optionalExpression( + dlNodeName, + this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('update')), [ + this.t.arrowFunctionExpression([], value), + dependenciesNode ?? this.t.nullLiteral(), + ]) + ); + } +} diff --git a/packages/transpiler/view-generator/src/NodeGenerators/ForGenerator.ts b/packages/transpiler/view-generator/src/NodeGenerators/ForGenerator.ts new file mode 100644 index 00000000..5daa289b --- /dev/null +++ b/packages/transpiler/view-generator/src/NodeGenerators/ForGenerator.ts @@ -0,0 +1,131 @@ +import { type types as t } from '@babel/core'; +import BaseGenerator from '../HelperGenerators/BaseGenerator'; +import { type ForParticle, type ViewParticle } from '@inula/reactivity-parser'; + +export default class ForGenerator extends BaseGenerator { + run() { + const { item, array, key, children } = this.viewParticle as ForParticle; + + const dlNodeName = this.generateNodeName(); + + // ---- Declare for node + this.addInitStatement( + this.declareForNode( + dlNodeName, + array.value, + item, + children, + BaseGenerator.calcDependencyNum(array.dependencyIndexArr), + key + ) + ); + + // ---- Update statements + this.addUpdateStatements(array.dependencyIndexArr, this.updateForNode(dlNodeName, array.value, item, key)); + this.addUpdateStatementsWithoutDep(this.updateForNodeItem(dlNodeName)); + + return dlNodeName; + } + + /** + * @View + * ${dlNodeName} = new ForNode(${array}, ${depNum}, ${array}.map(${item} => ${key}), + * ((${item}, $updateArr, $idx) => { + * $updateArr[$idx] = (changed, $item) => { + * ${item} = $item + * {$updateStatements} + * }) + * ${children} + * return [...${topLevelNodes}] + * }) + */ + private declareForNode( + dlNodeName: string, + array: t.Expression, + item: t.LVal, + children: ViewParticle[], + depNum: number, + key: t.Expression + ): t.Statement { + // ---- NodeFunc + const [childStatements, topLevelNodes, updateStatements, nodeIdx] = this.generateChildren(children, false, true); + + // ---- Update func + childStatements.unshift( + ...this.declareNodes(nodeIdx), + this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.memberExpression(this.t.identifier('$updateArr'), this.t.identifier('$idx'), true), + this.t.arrowFunctionExpression( + [...this.updateParams, this.t.identifier('$item')], + this.t.blockStatement([ + this.t.expressionStatement(this.t.assignmentExpression('=', item, this.t.identifier('$item'))), + ...this.geneUpdateBody(updateStatements).body, + ]) + ) + ) + ) + ); + + // ---- Return statement + childStatements.push(this.generateReturnStatement(topLevelNodes)); + + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.identifier(dlNodeName), + this.t.newExpression(this.t.identifier(this.importMap.ForNode), [ + array, + this.t.numericLiteral(depNum), + this.getForKeyStatement(array, item, key), + this.t.arrowFunctionExpression( + [item as any, this.t.identifier('$updateArr'), this.t.identifier('$idx')], + this.t.blockStatement(childStatements) + ), + ]) + ) + ); + } + + /** + * @View + * ${array}.map(${item} => ${key}) + */ + private getForKeyStatement(array: t.Expression, item: t.LVal, key: t.Expression): t.Expression { + return this.t.isNullLiteral(key) + ? key + : this.t.callExpression(this.t.memberExpression(array, this.t.identifier('map')), [ + this.t.arrowFunctionExpression([item as any], key), + ]); + } + + /** + * @View + * ${dlNodeName}.updateArray(${array}, ${array}.map(${item} => ${key})) + */ + private updateForNode(dlNodeName: string, array: t.Expression, item: t.LVal, key: t.Expression): t.Statement { + return this.optionalExpression( + dlNodeName, + this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('updateArray')), [ + array, + ...this.updateParams.slice(1), + this.getForKeyStatement(array, item, key), + ]) + ); + } + + /** + * @View + * ${dlNodeName}?.update(changed) + */ + private updateForNodeItem(dlNodeName: string): t.Statement { + return this.optionalExpression( + dlNodeName, + this.t.callExpression( + this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('update')), + this.updateParams + ) + ); + } +} diff --git a/packages/transpiler/view-generator/src/NodeGenerators/HTMLGenerator.ts b/packages/transpiler/view-generator/src/NodeGenerators/HTMLGenerator.ts new file mode 100644 index 00000000..ea8c4b99 --- /dev/null +++ b/packages/transpiler/view-generator/src/NodeGenerators/HTMLGenerator.ts @@ -0,0 +1,88 @@ +import { type types as t } from '@babel/core'; +import { type HTMLParticle } from '@inula/reactivity-parser'; +import HTMLPropGenerator from '../HelperGenerators/HTMLPropGenerator'; + +export default class HTMLGenerator extends HTMLPropGenerator { + run() { + const { tag, props, children } = this.viewParticle as HTMLParticle; + + const dlNodeName = this.generateNodeName(); + + this.addInitStatement(this.declareHTMLNode(dlNodeName, tag)); + + // ---- Resolve props + // ---- Use the tag name to check if the prop is internal for the tag, + // for dynamic tag, we can't check it, so we just assume it's not internal + // represent by the "ANY" tag name + const tagName = this.t.isStringLiteral(tag) ? tag.value : 'ANY'; + const allDependencyIndexArr: number[] = []; + Object.entries(props).forEach(([key, { value, dependencyIndexArr, dependenciesNode, dynamic }]) => { + if (key === 'didUpdate') return; + allDependencyIndexArr.push(...(dependencyIndexArr ?? [])); + this.addInitStatement( + this.addHTMLProp(dlNodeName, tagName, key, value, dynamic, dependencyIndexArr, dependenciesNode) + ); + }); + if (props.didUpdate) { + this.addUpdateStatements(allDependencyIndexArr, this.addOnUpdate(dlNodeName, props.didUpdate.value)); + } + + // ---- Resolve children + const childNames: string[] = []; + let mutable = false; + children.forEach((child, idx) => { + const [initStatements, childName] = this.generateChild(child); + childNames.push(childName); + this.addInitStatement(...initStatements); + if (child.type === 'html') { + this.addInitStatement(this.appendChild(dlNodeName, childName)); + } else { + mutable = true; + this.addInitStatement(this.insertNode(dlNodeName, childName, idx)); + } + }); + if (mutable) this.addInitStatement(this.setHTMLNodes(dlNodeName, childNames)); + + return dlNodeName; + } + + /** + * @View + * ${dlNodeName} = createElement(${tag}) + */ + private declareHTMLNode(dlNodeName: string, tag: t.Expression): t.Statement { + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.identifier(dlNodeName), + this.t.callExpression(this.t.identifier(this.importMap.createElement), [tag]) + ) + ); + } + + /** + * @View + * ${dlNodeName}._$nodes = [...${childNames}] + */ + private setHTMLNodes(dlNodeName: string, childNames: string[]): t.Statement { + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('_$nodes')), + this.t.arrayExpression(childNames.map(name => this.t.identifier(name))) + ) + ); + } + + /** + * @View + * ${dlNodeName}.appendChild(${childNodeName}) + */ + private appendChild(dlNodeName: string, childNodeName: string): t.Statement { + return this.t.expressionStatement( + this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('appendChild')), [ + this.t.identifier(childNodeName), + ]) + ); + } +} diff --git a/packages/transpiler/view-generator/src/NodeGenerators/IfGenerator.ts b/packages/transpiler/view-generator/src/NodeGenerators/IfGenerator.ts new file mode 100644 index 00000000..000b4f16 --- /dev/null +++ b/packages/transpiler/view-generator/src/NodeGenerators/IfGenerator.ts @@ -0,0 +1,96 @@ +import { type types as t } from '@babel/core'; +import { type IfParticle, type IfBranch } from '@inula/reactivity-parser'; +import CondGenerator from '../HelperGenerators/CondGenerator'; + +export default class IfGenerator extends CondGenerator { + run() { + const { branches } = this.viewParticle as IfParticle; + const deps = branches.flatMap(({ condition }) => condition.dependencyIndexArr ?? []); + + // ---- declareIfNode + const dlNodeName = this.generateNodeName(); + this.addInitStatement(this.declareIfNode(dlNodeName, branches, deps)); + + this.addUpdateStatements(deps, this.updateCondNodeCond(dlNodeName)); + this.addUpdateStatementsWithoutDep(this.updateCondNode(dlNodeName)); + + return dlNodeName; + } + + /** + * @View + * if (${test}) { ${body} } else { ${alternate} } + */ + geneIfStatement(test: t.Expression, body: t.Statement[], alternate: t.Statement): t.IfStatement { + return this.t.ifStatement(test, this.t.blockStatement(body), alternate); + } + + /** + * @View + * const ${dlNodeName} = new IfNode(($thisCond) => { + * if (cond1) { + * if ($thisCond.cond === 0) return + * ${children} + * $thisCond.cond = 0 + * return [nodes] + * } else if (cond2) { + * if ($thisCond.cond === 1) return + * ${children} + * $thisCond.cond = 1 + * return [nodes] + * } + * }) + */ + private declareIfNode(dlNodeName: string, branches: IfBranch[], deps: number[]): t.Statement { + // ---- If no else statement, add one + if ( + !this.t.isBooleanLiteral(branches[branches.length - 1].condition.value, { + value: true, + }) + ) { + branches.push({ + condition: { + value: this.t.booleanLiteral(true), + dependencyIndexArr: [], + dependenciesNode: this.t.arrayExpression([]), + dynamic: false, + }, + children: [], + }); + } + const ifStatement = branches.reverse().reduce((acc, { condition, children }, i) => { + const idx = branches.length - i - 1; + // ---- Generate children + const [childStatements, topLevelNodes, updateStatements, nodeIdx] = this.generateChildren(children, false, true); + + // ---- Even if no updateStatements, we still need reassign an empty updateFunc + // to overwrite the previous one + /** + * $thisCond.updateFunc = (changed) => { ${updateStatements} } + */ + const updateNode = this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('updateFunc')), + this.t.arrowFunctionExpression(this.updateParams, this.geneUpdateBody(updateStatements)) + ) + ); + + // ---- Update func + childStatements.unshift(...this.declareNodes(nodeIdx), updateNode); + + // ---- Check cond and update cond + childStatements.unshift(this.geneCondCheck(idx), this.geneCondIdx(idx)); + + // ---- Return statement + childStatements.push(this.geneCondReturnStatement(topLevelNodes, idx)); + + // ---- else statement + if (i === 0) return this.t.blockStatement(childStatements); + + return this.geneIfStatement(condition.value, childStatements, acc); + }, undefined); + + return this.declareCondNode(dlNodeName, this.t.blockStatement([ifStatement]), deps); + } +} diff --git a/packages/transpiler/view-generator/src/NodeGenerators/SnippetGenerator.ts b/packages/transpiler/view-generator/src/NodeGenerators/SnippetGenerator.ts new file mode 100644 index 00000000..bc024215 --- /dev/null +++ b/packages/transpiler/view-generator/src/NodeGenerators/SnippetGenerator.ts @@ -0,0 +1,136 @@ +import { type DependencyProp, type SnippetParticle } from '@inula/reactivity-parser'; +import type { types as t } from '@babel/core'; +import PropViewGenerator from '../HelperGenerators/PropViewGenerator'; + +export default class SnippetGenerator extends PropViewGenerator { + run() { + let { props } = this.viewParticle as SnippetParticle; + props = this.alterPropViews(props); + const { tag } = this.viewParticle as SnippetParticle; + + const dlNodeName = this.generateNodeName(); + + const availableProperties = this.snippetPropMap[tag] ?? []; + + const allDependenciesNode: (t.ArrayExpression | t.NullLiteral)[] = Array.from( + { + length: availableProperties.length, + }, + () => this.t.nullLiteral() + ); + + const allDependencyIndexArr: number[] = []; + + Object.entries(props).forEach(([key, { value, dependencyIndexArr, dependenciesNode }]) => { + if (key === 'didUpdate') return; + if (SnippetGenerator.lifecycle.includes(key as (typeof SnippetGenerator.lifecycle)[number])) { + this.addInitStatement(this.addLifecycle(dlNodeName, key as (typeof SnippetGenerator.lifecycle)[number], value)); + return; + } + if (!dependencyIndexArr || dependencyIndexArr.length === 0) return; + allDependencyIndexArr.push(...dependencyIndexArr); + const depIdx = availableProperties.indexOf(key); + if (dependenciesNode) allDependenciesNode[depIdx] = dependenciesNode; + const propChange = 1 << depIdx; + this.addUpdateStatements( + dependencyIndexArr, + this.updateProp(dlNodeName, propChange, key, value, allDependenciesNode[depIdx]) + ); + }); + if (props.didUpdate) { + this.addUpdateStatements(allDependencyIndexArr, this.addOnUpdate(dlNodeName, props.didUpdate.value)); + } + + this.addInitStatement(...this.declareSnippetNode(dlNodeName, tag, props, allDependenciesNode)); + + this.addUpdateStatementsWithoutDep(this.updateSnippet(dlNodeName)); + + return dlNodeName; + } + + /** + * @View + * { ${key}: ${value}, ... } + */ + private genePropNode(props: Record): t.Expression { + return this.t.objectExpression( + Object.entries(props).map(([key, prop]) => { + return this.t.objectProperty(this.t.identifier(key), prop.value); + }) + ); + } + + /** + * @View + * ${dlNodeName} = new SnippetNode(${allDependenciesNode}) + * this.${tag}({${props}}, ${dlNodeName}) + */ + private declareSnippetNode( + dlNodeName: string, + tag: string, + props: Record, + allDependenciesNode: (t.ArrayExpression | t.NullLiteral)[] + ): t.Statement[] { + return [ + this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.identifier(dlNodeName), + this.t.newExpression(this.t.identifier(this.importMap.SnippetNode), [ + this.t.arrayExpression(allDependenciesNode), + ]) + ) + ), + this.t.expressionStatement( + this.t.callExpression(this.t.memberExpression(this.t.thisExpression(), this.t.identifier(tag)), [ + this.genePropNode(props), + this.t.identifier(dlNodeName), + ]) + ), + ]; + } + + /** + * @View + * ${dlNodeName}.updateProp?.(${propChanged}, ...updateParams, () => { ${key}: ${value} }, allDependenciesNode) + */ + private updateProp( + dlNodeName: string, + propChanged: number, + key: string, + value: t.Expression, + allDependenciesNode: t.ArrayExpression | t.NullLiteral + ): t.Statement { + return this.optionalExpression( + dlNodeName, + this.t.optionalCallExpression( + this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('updateProp')), + [ + this.t.numericLiteral(propChanged), + ...this.updateParams.slice(1), + this.t.arrowFunctionExpression( + [], + this.t.objectExpression([this.t.objectProperty(this.t.identifier(key), value)]) + ), + allDependenciesNode, + ], + true + ) + ); + } + + /** + * @View + * ${dlNodeName}.update(changed) + */ + private updateSnippet(dlNodeName: string): t.Statement { + return this.optionalExpression( + dlNodeName, + this.t.optionalCallExpression( + this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('update')), + this.updateParams, + true + ) + ); + } +} diff --git a/packages/transpiler/view-generator/src/NodeGenerators/SwitchGenerator.ts b/packages/transpiler/view-generator/src/NodeGenerators/SwitchGenerator.ts new file mode 100644 index 00000000..47d1d549 --- /dev/null +++ b/packages/transpiler/view-generator/src/NodeGenerators/SwitchGenerator.ts @@ -0,0 +1,106 @@ +import { type types as t } from '@babel/core'; +import { SwitchBranch, SwitchParticle } from '@inula/reactivity-parser'; +import CondGenerator from '../HelperGenerators/CondGenerator'; + +export default class SwitchGenerator extends CondGenerator { + run() { + const { branches, discriminant } = this.viewParticle as SwitchParticle; + + const deps = branches.flatMap(({ case: _case }) => _case.dependencyIndexArr); + deps.push(...discriminant.dependencyIndexArr); + + // ---- declareSwitchNode + const dlNodeName = this.generateNodeName(); + this.addInitStatement(this.declareSwitchNode(dlNodeName, discriminant.value, branches, deps)); + + this.addUpdateStatements(deps, this.updateCondNodeCond(dlNodeName)); + this.addUpdateStatementsWithoutDep(this.updateCondNode(dlNodeName)); + + return dlNodeName; + } + + /** + * @View + * const ${dlNodeName} = new CondNode(($thisCond) => { + * switch ($discriminant) { + * case ${case0}: + * if ($thisCond.case === 0) return + * case ${case1}: + * if ($thisCond.case === 1) return + * return [...${case1Nodes}] + * default: + * if ($thisCond.case === 2) return + * } + * _$nodes[0]._$updateFunc = (changed) => { + * _$updates.forEach(update => update(changed)) + * }) + * return _$nodes + * }) + */ + private declareSwitchNode( + dlNodeName: string, + discriminant: t.Expression, + branches: SwitchBranch[], + deps: number[] + ): t.Statement { + // ---- Format statements, make fallthrough statements append to the previous case + const formattedBranches: SwitchBranch[] = branches.map(({ case: _case, break: _break, children }, idx) => { + if (!_break) { + for (let i = idx + 1; i < branches.length; i++) { + children.push(...branches[i].children); + if (branches[i].break) break; + } + } + return { case: _case, break: _break, children }; + }); + // ---- Add default case + const defaultCaseIdx = formattedBranches.findIndex(({ case: _case }) => _case === null); + if (defaultCaseIdx === -1) { + formattedBranches.push({ + case: { + value: this.t.booleanLiteral(true), + dependencyIndexArr: [], + dependenciesNode: this.t.arrayExpression([]), + dynamic: false, + }, + break: true, + children: [], + }); + } + + const switchStatements = formattedBranches.map(({ case: _case, children }, idx) => { + // ---- Generate case statements + const [childStatements, topLevelNodes, updateStatements, nodeIdx] = this.generateChildren(children, false, true); + + // ---- Even if no updateStatements, we still need reassign an empty updateFunc + // to overwrite the previous one + /** + * $thisCond.updateFunc = (changed) => { ${updateStatements} } + */ + const updateNode = this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('updateFunc')), + this.t.arrowFunctionExpression(this.updateParams, this.geneUpdateBody(updateStatements)) + ) + ); + + // ---- Update func + childStatements.unshift(...this.declareNodes(nodeIdx), updateNode); + + // ---- Check cond and update cond + childStatements.unshift(this.geneCondCheck(idx), this.geneCondIdx(idx)); + + // ---- Return statement + childStatements.push(this.geneCondReturnStatement(topLevelNodes, idx)); + + return this.t.switchCase(_case ? _case.value : null, [this.t.blockStatement(childStatements)]); + }); + + return this.declareCondNode( + dlNodeName, + this.t.blockStatement([this.t.switchStatement(discriminant, switchStatements)]), + deps + ); + } +} diff --git a/packages/transpiler/view-generator/src/NodeGenerators/TemplateGenerator.ts b/packages/transpiler/view-generator/src/NodeGenerators/TemplateGenerator.ts new file mode 100644 index 00000000..fcad6207 --- /dev/null +++ b/packages/transpiler/view-generator/src/NodeGenerators/TemplateGenerator.ts @@ -0,0 +1,282 @@ +import { type types as t } from '@babel/core'; +import { type HTMLParticle, type TemplateParticle } from '@inula/reactivity-parser'; +import HTMLPropGenerator from '../HelperGenerators/HTMLPropGenerator'; + +export default class TemplateGenerator extends HTMLPropGenerator { + run() { + const { template, mutableParticles, props } = this.viewParticle as TemplateParticle; + + const dlNodeName = this.generateNodeName(); + // ---- Add template declaration to class + const templateName = this.addTemplate(template); + // ---- Declare template node in View body + this.addInitStatement(this.declareTemplateNode(dlNodeName, templateName)); + + // ---- Insert elements first + const paths: number[][] = []; + props.forEach(({ path }) => { + paths.push(path); + }); + mutableParticles.forEach(({ path }) => { + paths.push(path.slice(0, -1)); + }); + const [insertElementStatements, pathNameMap] = this.insertElements(paths, dlNodeName); + this.addInitStatement(...insertElementStatements); + + // ---- Resolve props + const didUpdateMap: Record = {}; + props.forEach(({ tag, path, key, value, dependencyIndexArr, dependenciesNode, dynamic }) => { + const name = pathNameMap[path.join('.')]; + if (!didUpdateMap[name]) + didUpdateMap[name] = { + deps: [], + }; + if (key === 'didUpdate') { + didUpdateMap[name].value = value; + return; + } + + didUpdateMap[name].deps.push(...dependencyIndexArr); + + this.addInitStatement(this.addHTMLProp(name, tag, key, value, dynamic, dependencyIndexArr, dependenciesNode)); + }); + + Object.entries(didUpdateMap).forEach(([name, { deps, value }]) => { + if (!value) return; + this.addUpdateStatements(deps, this.addOnUpdate(name, value)); + }); + + // ---- Resolve mutable particles + mutableParticles.forEach(particle => { + const path = particle.path; + // ---- Find parent htmlElement + const parentName = pathNameMap[path.slice(0, -1).join('.')]; + const [initStatements, childName] = this.generateChild(particle); + this.addInitStatement(...initStatements); + this.addInitStatement(this.insertNode(parentName, childName, path[path.length - 1])); + }); + + return dlNodeName; + } + + /** + * @View + * static ${templateName} = (() => { + * let _$node0, _$node1, ... + * ${template} + * + * return _$node0 + * })() + */ + private addTemplate(template: HTMLParticle): string { + const templateName = this.generateTemplateName(); + const [statements, nodeName, , nodeIdx] = this.generateChild(template, false, true); + this.addStaticClassProperty( + templateName, + this.t.callExpression( + this.t.arrowFunctionExpression( + [], + this.t.blockStatement([ + ...this.declareNodes(nodeIdx), + ...statements, + this.t.returnStatement(this.t.identifier(nodeName)), + ]) + ), + [] + ) + ); + + return templateName; + } + + /** + * @View + * ${dlNodeName} = ${this.className}.${templateName}.cloneNode(true) + */ + private declareTemplateNode(dlNodeName: string, templateName: string): t.Statement { + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.identifier(dlNodeName), + this.t.callExpression( + this.t.memberExpression( + this.t.memberExpression(this.t.identifier(this.className), this.t.identifier(templateName)), + this.t.identifier('cloneNode') + ), + [this.t.booleanLiteral(true)] + ) + ) + ); + } + + /** + * @View + * ${dlNodeName}.firstChild + * or + * ${dlNodeName}.firstChild.nextSibling + * or + * ... + * ${dlNodeName}.childNodes[${num}] + */ + private insertElement(dlNodeName: string, path: number[], offset: number): t.Statement { + const newNodeName = this.generateNodeName(); + if (path.length === 0) { + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.identifier(newNodeName), + Array.from({ length: offset }).reduce( + (acc: t.Expression) => this.t.memberExpression(acc, this.t.identifier('nextSibling')), + this.t.identifier(dlNodeName) + ) + ) + ); + } + const addFirstChild = (object: t.Expression) => + // ---- ${object}.firstChild + this.t.memberExpression(object, this.t.identifier('firstChild')); + const addSecondChild = (object: t.Expression) => + // ---- ${object}.firstChild.nextSibling + this.t.memberExpression(addFirstChild(object), this.t.identifier('nextSibling')); + const addThirdChild = (object: t.Expression) => + // ---- ${object}.firstChild.nextSibling.nextSibling + this.t.memberExpression(addSecondChild(object), this.t.identifier('nextSibling')); + const addOtherChild = (object: t.Expression, num: number) => + // ---- ${object}.childNodes[${num}] + this.t.memberExpression( + this.t.memberExpression(object, this.t.identifier('childNodes')), + this.t.numericLiteral(num), + true + ); + const addNextSibling = (object: t.Expression) => + // ---- ${object}.nextSibling + this.t.memberExpression(object, this.t.identifier('nextSibling')); + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.identifier(newNodeName), + path.reduce((acc: t.Expression, cur: number, idx) => { + if (idx === 0 && offset > 0) { + for (let i = 0; i < offset; i++) acc = addNextSibling(acc); + } + if (cur === 0) return addFirstChild(acc); + if (cur === 1) return addSecondChild(acc); + if (cur === 2) return addThirdChild(acc); + return addOtherChild(acc, cur); + }, this.t.identifier(dlNodeName)) + ) + ); + } + + /** + * @brief Insert elements to the template node from the paths + * @param paths + * @param dlNodeName + * @returns + */ + private insertElements(paths: number[][], dlNodeName: string): [t.Statement[], Record] { + const [statements, collect] = HTMLPropGenerator.statementsCollector(); + const nameMap: Record = { [dlNodeName]: [] }; + + const commonPrefixPaths = TemplateGenerator.pathWithCommonPrefix(paths); + + commonPrefixPaths.forEach(path => { + const res = TemplateGenerator.findBestNodeAndPath(nameMap, path, dlNodeName); + const [, pat, offset] = res; + let name = res[0]; + + if (pat.length !== 0 || offset !== 0) { + collect(this.insertElement(name, pat, offset)); + name = this.generateNodeName(this.nodeIdx); + nameMap[name] = path; + } + }); + const pathNameMap = Object.fromEntries(Object.entries(nameMap).map(([name, path]) => [path.join('.'), name])); + + return [statements, pathNameMap]; + } + + // ---- Path related + /** + * @brief Extract common prefix from paths + * e.g. + * [0, 1, 2, 3] + [0, 1, 2, 4] => [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 4] + * [0, 1, 2] is the common prefix + * @param paths + * @returns paths with common prefix + */ + private static pathWithCommonPrefix(paths: number[][]): number[][] { + const allPaths = [...paths]; + paths.forEach(path0 => { + paths.forEach(path1 => { + if (path0 === path1) return; + for (let i = 0; i < path0.length; i++) { + if (path0[i] !== path1[i]) { + if (i !== 0) { + allPaths.push(path0.slice(0, i)); + } + break; + } + } + }); + }); + + // ---- Sort by length and then by first element, small to large + const sortedPaths = allPaths.sort((a, b) => { + if (a.length !== b.length) return a.length - b.length; + return a[0] - b[0]; + }); + + // ---- Deduplicate + const deduplicatedPaths = [...new Set(sortedPaths.map(path => path.join('.')))].map(path => + path.split('.').filter(Boolean).map(Number) + ); + + return deduplicatedPaths; + } + + /** + * @brief Find the best node name and path for the given path by looking into the nameMap. + * If there's a full match, return the name and an empty path + * If there's a partly match, return the name and the remaining path + * If there's a nextSibling match, return the name and the remaining path with sibling offset + * @param nameMap + * @param path + * @param defaultName + * @returns [name, path, siblingOffset] + */ + private static findBestNodeAndPath( + nameMap: Record, + path: number[], + defaultName: string + ): [string, number[], number] { + let bestMatchCount = 0; + let bestMatchName: string | undefined; + let bestHalfMatch: [string, number, number] | undefined; + Object.entries(nameMap).forEach(([name, pat]) => { + let matchCount = 0; + const pathLength = pat.length; + for (let i = 0; i < pathLength; i++) { + if (pat[i] === path[i]) matchCount++; + } + if (matchCount === pathLength - 1) { + const offset = path[pathLength - 1] - pat[pathLength - 1]; + if (offset > 0 && offset <= 3) { + bestHalfMatch = [name, matchCount, offset]; + } + } + if (matchCount !== pat.length) return; + if (matchCount > bestMatchCount) { + bestMatchName = name; + bestMatchCount = matchCount; + } + }); + if (!bestMatchName) { + if (bestHalfMatch) { + return [bestHalfMatch[0], path.slice(bestHalfMatch[1] + 1), bestHalfMatch[2]]; + } + return [defaultName, path, 0]; + } + return [bestMatchName, path.slice(bestMatchCount), 0]; + } +} diff --git a/packages/transpiler/view-generator/src/NodeGenerators/TextGenerator.ts b/packages/transpiler/view-generator/src/NodeGenerators/TextGenerator.ts new file mode 100644 index 00000000..1b3d6d5c --- /dev/null +++ b/packages/transpiler/view-generator/src/NodeGenerators/TextGenerator.ts @@ -0,0 +1,54 @@ +import { type types as t } from '@babel/core'; +import { type TextParticle } from '@inula/reactivity-parser'; +import BaseGenerator from '../HelperGenerators/BaseGenerator'; + +export default class TextGenerator extends BaseGenerator { + run() { + const { content } = this.viewParticle as TextParticle; + + const dlNodeName = this.generateNodeName(); + + this.addInitStatement(this.declareTextNode(dlNodeName, content.value, content.dependenciesNode)); + + if (content.dynamic) { + this.addUpdateStatements( + content.dependencyIndexArr, + this.updateTextNode(dlNodeName, content.value, content.dependenciesNode) + ); + } + + return dlNodeName; + } + + /** + * @View + * ${dlNodeName} = createTextNode(${value}, ${deps}) + */ + private declareTextNode(dlNodeName: string, value: t.Expression, dependenciesNode: t.Expression): t.Statement { + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.identifier(dlNodeName), + this.t.callExpression(this.t.identifier(this.importMap.createTextNode), [value, dependenciesNode]) + ) + ); + } + + /** + * @View + * ${dlNodeName} && updateText(${dlNodeName}, () => ${value}, ${deps}) + */ + private updateTextNode(dlNodeName: string, value: t.Expression, dependenciesNode: t.Expression): t.Statement { + return this.t.expressionStatement( + this.t.logicalExpression( + '&&', + this.t.identifier(dlNodeName), + this.t.callExpression(this.t.identifier(this.importMap.updateText), [ + this.t.identifier(dlNodeName), + this.t.arrowFunctionExpression([], value), + dependenciesNode, + ]) + ) + ); + } +} diff --git a/packages/transpiler/view-generator/src/NodeGenerators/TryGenerator.ts b/packages/transpiler/view-generator/src/NodeGenerators/TryGenerator.ts new file mode 100644 index 00000000..5d412e47 --- /dev/null +++ b/packages/transpiler/view-generator/src/NodeGenerators/TryGenerator.ts @@ -0,0 +1,97 @@ +import { type types as t } from '@babel/core'; +import BaseGenerator from '../HelperGenerators/BaseGenerator'; +import { TryParticle, type ViewParticle } from '@inula/reactivity-parser'; + +export default class TryGenerator extends BaseGenerator { + run() { + const { children, catchChildren, exception } = this.viewParticle as TryParticle; + + const dlNodeName = this.generateNodeName(); + + // ---- Declare for node + this.addInitStatement(this.declareTryNode(dlNodeName, children, catchChildren, exception)); + + // ---- Update statements + this.addUpdateStatementsWithoutDep(this.declareUpdate(dlNodeName)); + + return dlNodeName; + } + + /** + * @View + * $setUpdate($catchable(updateStatements)) + * ${children} + * return [...${topLevelNodes}] + */ + private declareTryNodeUpdate(children: ViewParticle[], addCatchable = true): t.Statement[] { + const [childStatements, topLevelNodes, updateStatements, nodeIdx] = this.generateChildren(children, false, true); + + const updateFunc = this.t.arrowFunctionExpression( + [this.t.identifier('$changed')], + this.geneUpdateBody(updateStatements) + ); + + childStatements.unshift( + ...this.declareNodes(nodeIdx), + this.t.expressionStatement( + this.t.callExpression( + this.t.identifier('$setUpdate'), + addCatchable ? [this.t.callExpression(this.t.identifier('$catchable'), [updateFunc])] : [updateFunc] + ) + ) + ); + childStatements.push( + this.t.returnStatement(this.t.arrayExpression(topLevelNodes.map(node => this.t.identifier(node)))) + ); + + return childStatements; + } + + /** + * @View + * ${dlNodeName} = new TryNode(($setUpdate, $catchable) => { + * ${children} + * }, ($setUpdate, e) => { + * ${catchChildren} + * }) + * }) + */ + private declareTryNode( + dlNodeName: string, + children: ViewParticle[], + catchChildren: ViewParticle[], + exception: TryParticle['exception'] + ): t.Statement { + const exceptionNodes = exception ? [exception] : []; + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.identifier(dlNodeName), + this.t.newExpression(this.t.identifier(this.importMap.TryNode), [ + this.t.arrowFunctionExpression( + [this.t.identifier('$setUpdate'), this.t.identifier('$catchable')], + this.t.blockStatement(this.declareTryNodeUpdate(children, true)) + ), + this.t.arrowFunctionExpression( + [this.t.identifier('$setUpdate'), ...exceptionNodes], + this.t.blockStatement(this.declareTryNodeUpdate(catchChildren, false)) + ), + ]) + ) + ); + } + + /** + * @View + * ${dlNodeName}?.update(changed) + */ + private declareUpdate(dlNodeName: string): t.Statement { + return this.optionalExpression( + dlNodeName, + this.t.callExpression( + this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('update')), + this.updateParams + ) + ); + } +} diff --git a/packages/transpiler/view-generator/src/SnippetGenerator.ts b/packages/transpiler/view-generator/src/SnippetGenerator.ts new file mode 100644 index 00000000..708125d0 --- /dev/null +++ b/packages/transpiler/view-generator/src/SnippetGenerator.ts @@ -0,0 +1,184 @@ +import { type types as t } from '@babel/core'; +import { type ViewParticle } from '@inula/reactivity-parser'; +import ViewGenerator from './ViewGenerator'; + +export default class SnippetGenerator extends ViewGenerator { + /** + * @brief Generate the snippet, i.e., @View MySnippet({ prop1, prop2 }) { ... } + * This is different from the main view in that it has a props node + * and is needed to parse twice, + * 1. for this.deps (viewParticlesWithPropertyDep) + * 2. for props that passed in this snippet (viewParticlesWithIdentityDep) + * @param viewParticlesWithPropertyDep + * @param viewParticlesWithIdentityDep + * @param propsNode + * @returns [viewBody, classProperties, templateIdx] + */ + generate( + viewParticlesWithPropertyDep: ViewParticle[], + viewParticlesWithIdentityDep: ViewParticle[], + propsNode: t.ObjectPattern + ): [t.BlockStatement, t.ClassProperty[], number] { + const allClassProperties: t.ClassProperty[] = []; + const allInitStatements: t.Statement[] = []; + const propertyUpdateStatements: Record = {}; + const identifierUpdateStatements: Record = {}; + const topLevelNodes: string[] = []; + + const templateIdx = this.templateIdx; + viewParticlesWithPropertyDep.forEach(viewParticle => { + const [initStatements, updateStatements, classProperties, nodeName] = this.generateChild(viewParticle); + allInitStatements.push(...initStatements); + Object.entries(updateStatements).forEach(([depNum, statements]) => { + if (!propertyUpdateStatements[Number(depNum)]) { + propertyUpdateStatements[Number(depNum)] = []; + } + propertyUpdateStatements[Number(depNum)].push(...statements); + }); + allClassProperties.push(...classProperties); + topLevelNodes.push(nodeName); + }); + // ---- Recover the templateIdx and reinitialize the nodeIdx + this.templateIdx = templateIdx; + this.nodeIdx = -1; + viewParticlesWithIdentityDep.forEach(viewParticle => { + // ---- We only need the update statements for the second props parsing + // because all the init statements are already generated + // a little bit time consuming but otherwise we need to write two different generators + const [, updateStatements] = this.generateChild(viewParticle); + + Object.entries(updateStatements).forEach(([depNum, statements]) => { + if (!identifierUpdateStatements[Number(depNum)]) { + identifierUpdateStatements[Number(depNum)] = []; + } + identifierUpdateStatements[Number(depNum)].push(...statements); + }); + }); + + const hasPropertyUpdateFunc = Object.keys(propertyUpdateStatements).length > 0; + const hasIdentifierUpdateFunc = Object.keys(identifierUpdateStatements).filter(n => n !== '0').length > 0; + + const viewBody = this.t.blockStatement([ + ...this.declareNodes(), + ...(hasPropertyUpdateFunc ? [this.geneUpdateFunc('update', propertyUpdateStatements)] : []), + ...(hasIdentifierUpdateFunc ? [this.geneUpdateFunc('updateProp', identifierUpdateStatements, propsNode)] : []), + ...allInitStatements, + this.geneAddNodes(topLevelNodes), + ]); + + return [viewBody, allClassProperties, this.templateIdx]; + } + + /** + * @View + * $snippetNode._$nodes = ${topLevelNodes} + */ + geneAddNodes(topLevelNodes: string[]): t.Statement { + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.memberExpression(this.t.identifier('$snippetNode'), this.t.identifier('_$nodes')), + this.t.arrayExpression(topLevelNodes.map(nodeName => this.t.identifier(nodeName))) + ) + ); + } + + /** + * @View + * $snippetNode.${name} = (changed) => { ${updateStatements} } + */ + geneUpdateFunc( + name: string, + updateStatements: Record, + propsNode?: t.ObjectPattern + ): t.Statement { + return this.t.expressionStatement( + this.t.assignmentExpression( + '=', + this.t.memberExpression(this.t.identifier('$snippetNode'), this.t.identifier(name)), + this.geneUpdateBody(updateStatements, propsNode) + ) + ); + } + + /** + * @View + * (changed) => { + * if (changed & 1) { + * ... + * } + * ... + * } + */ + private geneUpdateBody( + updateStatements: Record, + propsNode?: t.ObjectPattern + ): t.ArrowFunctionExpression { + const bodyEntryNodes: t.Statement[] = []; + // ---- Args + const args: t.Identifier[] = this.updateParams; + if (propsNode) { + // ---- Add $snippetProps and $depsArr to args + args.push(this.t.identifier('$snippetPropsFunc'), this.t.identifier('$depsArr')); + + // ---- Add cache + /** + * if ($snippetNode.cached(depsArr, changed)) return + */ + bodyEntryNodes.push( + this.t.ifStatement( + this.t.callExpression( + this.t.memberExpression(this.t.identifier('$snippetNode'), this.t.identifier('cached')), + [this.t.identifier('$depsArr'), this.t.identifier('$changed')] + ), + this.t.blockStatement([this.t.returnStatement()]) + ) + ); + + /** + * const $snippetProps = $snippetPropsFunc() + */ + bodyEntryNodes.push( + this.t.variableDeclaration('const', [ + this.t.variableDeclarator( + this.t.identifier('$snippetProps'), + this.t.callExpression(this.t.identifier('$snippetPropsFunc'), []) + ), + ]) + ); + + /** + * ${prop} = $snippetProps + */ + propsNode.properties + .filter(prop => this.t.isObjectProperty(prop)) + .forEach((prop, idx) => { + const depNum = 1 << idx; + if (!updateStatements[depNum]) updateStatements[depNum] = []; + updateStatements[depNum].unshift( + this.t.expressionStatement( + this.t.assignmentExpression('=', this.t.objectPattern([prop]), this.t.identifier('$snippetProps')) + ) + ); + }); + } + // ---- End + const runAllStatements = propsNode ? [] : updateStatements[0] ?? []; + + return this.t.arrowFunctionExpression( + args, + this.t.blockStatement([ + ...bodyEntryNodes, + ...Object.entries(updateStatements) + .filter(([depNum]) => depNum !== '0') + .map(([depNum, statements]) => { + return this.t.ifStatement( + this.t.binaryExpression('&', this.t.identifier('$changed'), this.t.numericLiteral(Number(depNum))), + this.t.blockStatement(statements) + ); + }), + ...runAllStatements, + ]) + ); + } +} diff --git a/packages/transpiler/view-generator/src/ViewGenerator.ts b/packages/transpiler/view-generator/src/ViewGenerator.ts new file mode 100644 index 00000000..9276bd7c --- /dev/null +++ b/packages/transpiler/view-generator/src/ViewGenerator.ts @@ -0,0 +1,118 @@ +import { type types as t } from '@babel/core'; +import { type ViewParticle } from '@inula/reactivity-parser'; +import { type ViewGeneratorConfig } from './types'; +import BaseGenerator, { prefixMap } from './HelperGenerators/BaseGenerator'; +import CompGenerator from './NodeGenerators/CompGenerator'; +import HTMLGenerator from './NodeGenerators/HTMLGenerator'; +import TemplateGenerator from './NodeGenerators/TemplateGenerator'; +import ForGenerator from './NodeGenerators/ForGenerator'; +import IfGenerator from './NodeGenerators/IfGenerator'; +import EnvGenerator from './NodeGenerators/EnvGenerator'; +import TextGenerator from './NodeGenerators/TextGenerator'; +import ExpGenerator from './NodeGenerators/ExpGenerator'; +import SnippetGenerator from './NodeGenerators/SnippetGenerator'; +import SwitchGenerator from './NodeGenerators/SwitchGenerator'; +import TryGenerator from './NodeGenerators/TryGenerator'; + +export default class ViewGenerator { + config: ViewGeneratorConfig; + t: typeof t; + + /** + * @brief Construct the view generator from config + * @param config + */ + constructor(config: ViewGeneratorConfig) { + this.config = config; + this.t = config.babelApi.types; + this.templateIdx = config.templateIdx; + } + + /** + * @brief Different generator classes for different view particle types + */ + static generatorMap: Record = { + comp: CompGenerator, + html: HTMLGenerator, + template: TemplateGenerator, + for: ForGenerator, + if: IfGenerator, + switch: SwitchGenerator, + env: EnvGenerator, + text: TextGenerator, + exp: ExpGenerator, + snippet: SnippetGenerator, + try: TryGenerator, + }; + + /** + * @brief Generate the view given the view particles, mainly used for child particles parsing + * @param viewParticles + * @returns [initStatements, updateStatements, classProperties, topLevelNodes] + */ + generateChildren( + viewParticles: ViewParticle[] + ): [t.Statement[], Record, t.ClassProperty[], string[]] { + const allInitStatements: t.Statement[] = []; + const allClassProperties: t.ClassProperty[] = []; + const allUpdateStatements: Record = {}; + const topLevelNodes: string[] = []; + + viewParticles.forEach(viewParticle => { + const [initStatements, updateStatements, classProperties, nodeName] = this.generateChild(viewParticle); + allInitStatements.push(...initStatements); + Object.entries(updateStatements).forEach(([depNum, statements]) => { + if (!allUpdateStatements[Number(depNum)]) { + allUpdateStatements[Number(depNum)] = []; + } + allUpdateStatements[Number(depNum)].push(...statements); + }); + allClassProperties.push(...classProperties); + topLevelNodes.push(nodeName); + }); + + return [allInitStatements, allUpdateStatements, allClassProperties, topLevelNodes]; + } + + nodeIdx = -1; + templateIdx = -1; + /** + * @brief Generate the view given the view particle, using generator from the map + * @param viewParticle + * @returns + */ + generateChild(viewParticle: ViewParticle) { + const { type } = viewParticle; + const GeneratorClass = ViewGenerator.generatorMap[type]; + if (!GeneratorClass) { + throw new Error(`Unknown view particle type: ${type}`); + } + const generator = new GeneratorClass(viewParticle, this.config); + generator.nodeIdx = this.nodeIdx; + generator.templateIdx = this.templateIdx; + const result = generator.generate(); + this.nodeIdx = generator.nodeIdx; + this.templateIdx = generator.templateIdx; + return result; + } + + /** + * @View + * let node1, node2, ... + */ + declareNodes(): t.Statement[] { + if (this.nodeIdx === -1) return []; + return [ + this.t.variableDeclaration( + 'let', + Array.from({ length: this.nodeIdx + 1 }, (_, i) => + this.t.variableDeclarator(this.t.identifier(`${prefixMap.node}${i}`)) + ) + ), + ]; + } + + get updateParams() { + return [this.t.identifier('$changed')]; + } +} diff --git a/packages/transpiler/view-generator/src/error.ts b/packages/transpiler/view-generator/src/error.ts new file mode 100644 index 00000000..8c036f3e --- /dev/null +++ b/packages/transpiler/view-generator/src/error.ts @@ -0,0 +1,14 @@ +import { createErrorHandler } from '@inula/error-handler'; + +export const DLError = createErrorHandler( + 'ViewGenerator', + { + 1: 'Element prop in HTML should be a function or an identifier', + 2: 'Unrecognized HTML common prop', + 3: 'Do prop only accepts function or arrow function', + }, + {}, + { + 1: 'ExpressionNode only supports prop as element and lifecycle, receiving $0', + } +); diff --git a/packages/transpiler/view-generator/src/index.ts b/packages/transpiler/view-generator/src/index.ts new file mode 100644 index 00000000..f1a52ed9 --- /dev/null +++ b/packages/transpiler/view-generator/src/index.ts @@ -0,0 +1,23 @@ +import { type ViewParticle } from '@inula/reactivity-parser'; +import { type ViewGeneratorConfig } from './types'; +import { type types as t } from '@babel/core'; +import MainViewGenerator from './MainViewGenerator'; +import SnippetGenerator from './SnippetGenerator'; + +export function generateView( + viewParticles: ViewParticle[], + config: ViewGeneratorConfig +): [t.BlockStatement, t.ClassProperty[], number] { + return new MainViewGenerator(config).generate(viewParticles); +} + +export function generateSnippet( + viewParticlesWithPropertyDep: ViewParticle[], + viewParticlesWithIdentityDep: ViewParticle[], + propNode: t.ObjectPattern, + config: ViewGeneratorConfig +): [t.BlockStatement, t.ClassProperty[], number] { + return new SnippetGenerator(config).generate(viewParticlesWithPropertyDep, viewParticlesWithIdentityDep, propNode); +} + +export * from './types'; diff --git a/packages/transpiler/view-generator/src/types.ts b/packages/transpiler/view-generator/src/types.ts new file mode 100644 index 00000000..087ad830 --- /dev/null +++ b/packages/transpiler/view-generator/src/types.ts @@ -0,0 +1,12 @@ +import type Babel from '@babel/core'; + +export type SnippetPropMap = Record; +export interface ViewGeneratorConfig { + babelApi: typeof Babel; + className: string; + importMap: Record; + snippetPropMap: SnippetPropMap; + templateIdx: number; + attributeMap: Record; + alterAttributeMap: Record; +} diff --git a/packages/transpiler/view-generator/tsconfig.json b/packages/transpiler/view-generator/tsconfig.json new file mode 100644 index 00000000..e0932d78 --- /dev/null +++ b/packages/transpiler/view-generator/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "esModuleInterop": true + }, + "ts-node": { + "esm": true + } +} \ No newline at end of file diff --git a/packages/transpiler/view-parser/package.json b/packages/transpiler/view-parser/package.json new file mode 100644 index 00000000..d5617a59 --- /dev/null +++ b/packages/transpiler/view-parser/package.json @@ -0,0 +1,46 @@ +{ + "name": "@inula/view-parser", + "version": "0.0.1", + "author": { + "name": "IanDx", + "email": "iandxssxx@gmail.com" + }, + "keywords": [ + "dlight.js" + ], + "license": "MIT", + "files": [ + "dist" + ], + "type": "module", + "main": "dist/index.cjs", + "module": "dist/index.js", + "typings": "dist/index.d.ts", + "scripts": { + "build": "tsup --sourcemap", + "test": "vitest --ui" + }, + "devDependencies": { + "@babel/core": "^7.20.12", + "@types/babel__core": "^7.20.5", + "@vitest/ui": "^0.34.5", + "tsup": "^6.7.0", + "typescript": "^5.3.2", + "vitest": "^0.34.5" + }, + "dependencies": { + "@inula/error-handler": "workspace:*" + }, + "tsup": { + "entry": [ + "src/index.ts" + ], + "format": [ + "cjs", + "esm" + ], + "clean": true, + "dts": true, + "minify": true + } +} diff --git a/packages/transpiler/view-parser/src/error.ts b/packages/transpiler/view-parser/src/error.ts new file mode 100644 index 00000000..0e80994d --- /dev/null +++ b/packages/transpiler/view-parser/src/error.ts @@ -0,0 +1,22 @@ +import { createErrorHandler } from '@inula/error-handler'; + +export const DLError = createErrorHandler( + 'ViewParser', + { + 1: "Invalid syntax in DLight's View, only accepts dot chain call expression", + 2: 'First argument of $0() must be an expression', + 3: "Invalid syntax in DLight's View, only accepts expression as props", + 4: 'Invalid Snippet calling, only accepts static snippet calling like `this.Snippet()`', + }, + { + 1: 'DLight only accepts ForOfStatement as for loop, skipping this statement', + 2: 'EnvUnit must have at least one child, skipping this statement', + 3: 'Only Env/Comp/HTMLUnit can have a statement block as its children, skipping this statement', + 4: 'If you want to use a key in a for loop, make the first statement as a label statement like `key: item`, skipping this key for now', + 5: 'ForUnit must have at least one child, skipping this statement', + For: 'ForUnit must have at least one child, skipping this statement', + }, + { + 1: 'EnvUnit must have at least one prop, skipping this statement and flattening its children', + } +); diff --git a/packages/transpiler/view-parser/src/index.ts b/packages/transpiler/view-parser/src/index.ts new file mode 100644 index 00000000..5bfef6b2 --- /dev/null +++ b/packages/transpiler/view-parser/src/index.ts @@ -0,0 +1,16 @@ +import { ViewParser } from './parser'; +import { type ViewUnit, type ViewParserConfig } from './types'; +import { type types as t } from '@babel/core'; + +/** + * @brief Generate view units from a babel ast + * @param statement + * @param config + * @param options + * @returns ViewUnit[] + */ +export function parseView(statement: t.BlockStatement, config: ViewParserConfig): ViewUnit[] { + return new ViewParser(config).parse(statement); +} + +export type * from './types'; diff --git a/packages/transpiler/view-parser/src/parser.ts b/packages/transpiler/view-parser/src/parser.ts new file mode 100644 index 00000000..0af242a8 --- /dev/null +++ b/packages/transpiler/view-parser/src/parser.ts @@ -0,0 +1,597 @@ +import { type types as t, type traverse as tr } from '@babel/core'; +import { type ViewProp, type IfBranch, type ViewUnit, type ViewParserConfig, SwitchBranch } from './types'; +import { DLError } from './error'; + +export class ViewParser { + private readonly compWrapper: string = 'comp'; + private readonly htmlTagWrapper: string = 'tag'; + private readonly environmentTagName: string = 'env'; + private readonly expressionTagName: string = '_'; + + private readonly config: ViewParserConfig; + + private readonly t: typeof t; + private readonly traverse: typeof tr; + private readonly snippetNames: string[]; + private readonly htmlTags: string[]; + + readonly viewUnits: ViewUnit[] = []; + + /** + * @brief Constructor + * @param statement + * @param config + * @param options + */ + constructor(config: ViewParserConfig) { + this.config = config; + this.t = config.babelApi.types; + this.traverse = config.babelApi.traverse; + this.snippetNames = config.snippetNames; + this.htmlTags = config.htmlTags; + } + + parse(statement: t.BlockStatement) { + const statements = [...statement.directives, ...statement.body]; + statements.forEach(this.parseStatement.bind(this)); + // ---- If the view is an empty env, throw an error + if (this.viewUnits.length === 1 && this.viewUnits[0].type === 'env' && this.viewUnits[0].children.length === 0) { + DLError.error2(); + return []; + } + + return this.viewUnits; + } + + /** + * @brief Parse the statements + * @returns + */ + private parseStatement(statement: t.Statement | t.Directive): void { + if (this.isInvalidExpression(statement)) return; + if (this.t.isExpressionStatement(statement)) { + this.parseExpression(statement.expression); + return; + } + if (this.t.isTryStatement(statement)) { + this.parseTry(statement); + return; + } + if (this.t.isForOfStatement(statement)) { + this.parseFor(statement); + return; + } + if (this.t.isIfStatement(statement)) { + this.parseIf(statement); + return; + } + if (this.t.isSwitchStatement(statement)) { + this.parseSwitch(statement); + return; + } + if (this.t.isDirective(statement)) { + this.parseText(statement.value); + return; + } + if (this.t.isBlockStatement(statement)) { + // ---- If the statement is a block statement, treat it as last unit's children + const lastViewUnit = this.viewUnits[this.viewUnits.length - 1]; + const type = lastViewUnit?.type; + const childViewUnits = this.parseView(statement); + if (type === 'html') { + delete lastViewUnit.props.textContent; + lastViewUnit.children.push(...childViewUnits); + } else if (type === 'comp' || type === 'snippet') { + lastViewUnit.children.push(...childViewUnits); + } else if (type === 'env') { + if (childViewUnits.length > 0) { + lastViewUnit.children.push(...childViewUnits); + } else { + this.viewUnits.pop(); + DLError.error2(); + } + } else { + DLError.error3(); + } + } + } + + /** + * @brief Parse the expression node + * CallExpression -> Tag + * StringLiteral/TemplateLiteral -> Text + * TaggedTemplateExpression -> Tag + Text / Exp + * @param expression + */ + private parseExpression(expression: t.Expression): void { + if (this.t.isCallExpression(expression)) { + this.parseTag(expression); + return; + } + if (this.t.isStringLiteral(expression) || this.t.isTemplateLiteral(expression)) { + this.parseText(expression); + return; + } + if (this.t.isTaggedTemplateExpression(expression)) { + this.parseTaggedTemplate(expression); + return; + } + + // ---- Default ExpressionTag + // e.g. this.count -> _(this.count) + this.viewUnits.push({ + type: 'exp', + content: this.parseProp(expression), + props: {}, + }); + } + + /** + * @brief Parse if statement conditions + * @param node + * @returns IfBranch[] + */ + private parseIfBranches(node: t.IfStatement): IfBranch[] { + const conditions: IfBranch[] = []; + const condition = node.test; + const ifBody = this.t.isBlockStatement(node.consequent) + ? node.consequent + : this.t.blockStatement([node.consequent]); + conditions.push({ + condition, + children: this.parseView(ifBody), + }); + + // ---- If the alternate is an if statement, parse it recursively + if (this.t.isIfStatement(node.alternate)) { + conditions.push(...this.parseIfBranches(node.alternate)); + } else if (node.alternate) { + const altBody = this.t.isBlockStatement(node.alternate) + ? node.alternate + : this.t.blockStatement([node.alternate]); + conditions.push({ + condition: this.t.booleanLiteral(true), + children: this.parseView(altBody), + }); + } + + return conditions; + } + + /** + * @brief Parse if statement with else if and else + * @param node + */ + private parseIf(node: t.IfStatement): void { + this.viewUnits.push({ + type: 'if', + branches: this.parseIfBranches(node), + }); + } + + /** + * @brief Parse switch statement + * @param node + */ + private parseSwitch(node: t.SwitchStatement) { + const branches: SwitchBranch[] = []; + const switchBody = node.cases; + switchBody.forEach(s => { + const caseBodyPre = s.consequent; + const caseBody = + caseBodyPre.length === 1 && this.t.isBlockStatement(caseBodyPre[0]) + ? caseBodyPre[0] + : this.t.blockStatement(caseBodyPre); + const isBreak = this.t.isBreakStatement(caseBody.body[caseBody.body.length - 1]); + if (isBreak) { + caseBody.body.pop(); + } + + const children = this.parseView(caseBody); + const branch: SwitchBranch = { + case: s.test ?? this.t.booleanLiteral(true), + children, + break: isBreak, + }; + branches.push(branch); + }); + this.viewUnits.push({ + type: 'switch', + discriminant: node.discriminant, + branches, + }); + } + + /** + * @brief Parse try statement + * @param node + */ + private parseTry(node: t.TryStatement) { + const tryBody = this.t.blockStatement(node.block.body); + const catchBodyNode = node.handler?.body ?? this.t.blockStatement([]); + + this.viewUnits.push({ + type: 'try', + children: this.parseView(tryBody), + exception: node.handler?.param ?? null, + catchChildren: this.parseView(catchBodyNode), + }); + } + + /** + * @brief Parse for of loop + * Only accept for of loop with variable declaration + * e.g. for (const item of array) {} + * Key: + * 1. If the first statement is an array expression and is not a null or undefined, + * treat the first element as the key. + * e.g. for (const { idx, item } of array) { [idx]; div(item) } + * key will be "idx" + * 2. If the first statement is an array expression and is null or undefined, treat it as a non-keyed loop. + * e.g. for (const { item } of array) { [null]; div(item) } + * no specific key + * 3. If the first statement is not an array expression, treat the item itself as the key. + * e.g. for (const item of array) { div(item) } + * key will be "item" + * @param node + */ + private parseFor(node: t.ForOfStatement): void { + const left = node.left; + if (!this.t.isVariableDeclaration(left)) { + DLError.throw1(); + } + const item = (left as t.VariableDeclaration).declarations[0].id; + const array = node.right; + let key: t.Expression = this.t.nullLiteral(); + const forBody = node.body; + let forBodyStatements: Array; + if (this.t.isExpressionStatement(forBody)) { + // ---- If the for body is an expression statement, treat it as the only statement + forBodyStatements = [forBody]; + } else if (this.t.isBlockStatement(forBody)) { + const childNodes = forBody.body; + if (childNodes.length === 0) return DLError.error5(); + const firstStatement = childNodes[0]; + if (this.t.isLabeledStatement(firstStatement) && this.t.isIdentifier(firstStatement.label)) { + if (firstStatement.label.name !== 'key' || !this.t.isExpressionStatement(firstStatement.body)) { + DLError.error4(); + } else { + // ---- Treat the first array element labeled by key as the key + const keyNode = firstStatement.body.expression; + // ---- If the key is undefined or null, treat it as no key + if ( + this.t.isExpression(keyNode) && + !(this.t.isNullLiteral(keyNode) || (this.t.isIdentifier(keyNode) && keyNode.name === 'undefined')) + ) { + key = keyNode; + } + } + forBodyStatements = childNodes.slice(1); + } else { + forBodyStatements = childNodes; + } + } else return; + + const directives = forBodyStatements.filter(s => this.t.isDirective(s)) as t.Directive[]; + const statements = forBodyStatements.filter(s => !this.t.isDirective(s)) as t.Statement[]; + const forBodyBlockStatement = this.t.blockStatement(statements, directives); + // ---- Parse the for body statements + this.viewUnits.push({ + type: 'for', + item, + array, + key, + children: this.parseView(forBodyBlockStatement), + }); + } + + /** + * @brief Parse text node + * 1. `text text` + * 2. "text2 text2" + * @param node + */ + private parseText(node: t.StringLiteral | t.TemplateLiteral | t.DirectiveLiteral): void { + if (this.t.isDirectiveLiteral(node)) node = this.t.stringLiteral(node.value); + + this.viewUnits.push({ + type: 'text', + content: node, + }); + } + + /** + * @brief Parse tagged template expression + * Two tagged template expression cases + * 1. type without call expressions + * e.g. i18n`any text` + * => exp: _(i18n`any text`) + * 2. type with string literal / template literal + * e.g. "any text" `any other text`/ `any text` `any other text` + * => text: "any text" + text: `any other text` + * @param node + * @param path + */ + private parseTaggedTemplate(node: t.TaggedTemplateExpression): void { + if (this.t.isStringLiteral(node.tag) || this.t.isTemplateLiteral(node.tag)) { + // ---- Case 2 + this.viewUnits.push({ + type: 'text', + content: node.tag, + }); + this.viewUnits.push({ + type: 'text', + content: node.quasi, + }); + return; + } + // ---- Case 1 + this.viewUnits.push({ + type: 'exp', + content: this.parseProp(node), + props: {}, + }); + } + + /** + * @brief Return a block statement if the node is a prop view + * @param node + * @returns + */ + private isPropView(node: t.Node): null | t.BlockStatement { + if ( + !( + this.t.isArrowFunctionExpression(node) && + (this.t.isIdentifier(node.params[0], { name: 'View' }) || + this.t.isIdentifier(node.params[0], { name: '_View' })) + ) + ) + return null; + + const body = node.body; + if (this.t.isBlockStatement(body)) return body; + return this.t.blockStatement([this.t.expressionStatement(body)]); + } + + /** + * @brief Parse props in the type node + * @param propNode + * @returns ViewProp + */ + private parseProp(propNode: t.Node | undefined): ViewProp { + if (propNode && !this.t.isExpression(propNode)) DLError.throw3(); + propNode = propNode as t.Expression; + // ---- If there is no propNode, set the default prop as true + if (!propNode) { + return { + value: this.t.booleanLiteral(true), + viewPropMap: {}, + }; + } + + // ---- Collect View => {} nodes as DLProp + const dlViewPropResult: Record = {}; + this.traverse(this.valueWrapper(propNode), { + ArrowFunctionExpression: innerPath => { + const node = this.isPropView(innerPath.node); + if (!node) return; + const id = this.uid(); + // ---- Parse the body of View => {} as a new View + dlViewPropResult[id] = this.parseView(node); + // ---- Replace the View => {} with a id string literal + const newNode = this.t.stringLiteral(id); + if (innerPath.node === propNode) { + propNode = newNode; + } + innerPath.replaceWith(newNode); + innerPath.skip(); + }, + }); + + return { + value: propNode, + viewPropMap: dlViewPropResult, + }; + } + + /** + * @brief Parse the type node + * @param node + */ + private parseTag(node: t.CallExpression): void { + const props: Record = {}; + + // ---- Keep iterating until the node has no call expression + let n = node; + while (this.t.isMemberExpression(n?.callee) && n?.callee?.object && !this.isPureMemberExpression(n.callee)) { + const property = n.callee.property; + if (!this.t.isIdentifier(property) || !this.t.isCallExpression(n.callee.object)) { + DLError.throw1(); + continue; + } + + const key = property.name; + const prop = this.parseProp(n.arguments[0]); + props[key] = prop; + n = n.callee.object; + } + + let contentProp: ViewProp | undefined; + if (n.arguments.length > 0) { + // ---- The last argument is the content prop of the type, + // so only parse prop when it exists instead of + // treating empty prop as "true" like other props + contentProp = this.parseProp(n.arguments[0]); + } + + if (this.t.isIdentifier(n.callee)) { + // ---- Special cases for expression type + const tagName = n.callee.name; + if (tagName === this.expressionTagName && contentProp) { + // ---- Must have content prop or else just ignore it + this.viewUnits.push({ + type: 'exp', + content: contentProp, + props, + }); + return; + } + if (tagName === this.environmentTagName) { + if (Object.keys(props).length === 0) { + DLError.warn1(); + return; + } + this.viewUnits.push({ + type: 'env', + props, + children: [], + }); + return; + } + if (this.htmlTags.includes(tagName)) { + let children: ViewUnit[] = []; + if (contentProp) { + let isViewProp = false; + if (contentProp.viewPropMap && Object.keys(contentProp.viewPropMap).length === 1) { + const key = Object.keys(contentProp.viewPropMap)[0]; + if (this.t.isStringLiteral(contentProp.value, { value: key })) { + isViewProp = true; + const viewUnit = contentProp.viewPropMap[key]; + children = viewUnit; + } + } + if (!isViewProp) props.textContent = contentProp; + } + this.viewUnits.push({ + type: 'html', + tag: this.t.stringLiteral(tagName), + props, + children, + }); + return; + } + // ---- Custom tag + if (contentProp) props._$content = contentProp; + this.viewUnits.push({ + type: 'comp', + tag: n.callee, + props, + children: [], + }); + return; + } + if ( + this.t.isMemberExpression(n.callee) && + this.t.isThisExpression(n.callee.object) && + this.t.isIdentifier(n.callee.property) && + this.snippetNames.includes(n.callee.property.name) + ) { + // ---- Snippet + if (contentProp) props.content = contentProp; + if ( + !( + this.t.isMemberExpression(n.callee) && + this.t.isThisExpression(n.callee.object) && + this.t.isIdentifier(n.callee.property) + ) + ) + return DLError.throw4(); + this.viewUnits.push({ + type: 'snippet', + tag: n.callee.property.name, + props, + children: [], + }); + return; + } + if (this.t.isExpression(n.callee)) { + // ---- 1. Custom tag + // 2. htmlTag(xxx) + // 3. tag(xxx) + const [tagType, tag] = this.alterTagType(n.callee); + if (contentProp) { + props[tagType === 'html' ? 'textContent' : '_$content'] = contentProp; + } + this.viewUnits.push({ + type: tagType, + tag, + props, + children: [], + }); + } + } + + /* ---- Helper Functions ---- */ + /** + * @brief Test if the node is a pure member expression without call expression + * @param node + */ + private isPureMemberExpression(node: t.Expression): boolean { + let isPure = true; + this.traverse(this.valueWrapper(node), { + CallExpression: () => { + isPure = false; + }, + }); + return isPure; + } + + /** + * @brief Alter the tag type by checking the tag name + * @param viewUnit + * @returns ["html" | "comp", t.Expression] + */ + private alterTagType(tag: t.Expression): ['html' | 'comp', t.Expression] { + if (this.t.isCallExpression(tag) && this.t.isIdentifier(tag.callee)) { + const tagName = tag.callee.name; + const tagType = tagName === this.htmlTagWrapper ? 'html' : tagName === this.compWrapper ? 'comp' : undefined; + if (tagType) { + const tagTarget = tag.arguments[0]; + if (!this.t.isExpression(tagTarget)) DLError.throw2(tagName); + return [tagType, tagTarget as t.Expression]; + } + } + return ['comp', tag]; + } + + /** + * @brief Test if the expression is invalid + * 1. For: only accept ForOfStatement + * @param node + * @returns is this expression invalid + */ + private isInvalidExpression(node: t.Statement | t.Directive): boolean { + const isInvalidForStatement = this.t.isForStatement(node) && !this.t.isForOfStatement(node); + if (isInvalidForStatement) { + DLError.error1(); + return true; + } + return false; + } + + /** + * @brief Wrap the value in a file + * @param node + * @returns wrapped value + */ + private valueWrapper(node: t.Expression): t.File { + return this.t.file(this.t.program([this.t.expressionStatement(node)])); + } + + /** + * @brief Parse the view by duplicating current parser's classRootPath, statements and htmlTags + * @param statements + * @returns ViewUnit[] + */ + private parseView(statement: t.BlockStatement): ViewUnit[] { + return new ViewParser(this.config).parse(statement); + } + + /** + * @brief Generate a unique id + * @returns a unique id + */ + private uid(): string { + return Math.random().toString(36).slice(2); + } +} diff --git a/packages/transpiler/view-parser/src/test/CompUnit.test.ts b/packages/transpiler/view-parser/src/test/CompUnit.test.ts new file mode 100644 index 00000000..2322b814 --- /dev/null +++ b/packages/transpiler/view-parser/src/test/CompUnit.test.ts @@ -0,0 +1,160 @@ +import { expect, describe, it } from 'vitest'; +import { parse, parseCode, parseView } from './mock'; +import { type HTMLUnit } from '../types'; +import { types as t } from '@babel/core'; + +describe('CompUnit', () => { + // ---- Type + it("should identify a function call with identifier's name not in htmlTags of config as an CompUnit", () => { + const viewUnits = parse('Comp()'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('comp'); + }); + + it('should identify an expression inside tag function call as an CompUnit', () => { + const viewUnits = parse('comp(function IWillDoAnything(){return "Comp"}.call())()'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('comp'); + }); + + // ---- Tag + it('should correctly parse the tag of an CompUnit of the type of function call', () => { + const viewUnits = parse('Comp()'); + const tag = (viewUnits[0] as HTMLUnit).tag; + expect(t.isIdentifier(tag, { name: 'Comp' })).toBeTruthy(); + }); + + it('should correctly parse a member expression function call as an CompUnit', () => { + const statement = parseCode('CompList[0].MyComp.Component()'); + const viewUnits = parseView(statement); + const originalExpression = ((statement.body[0] as t.ExpressionStatement).expression as t.CallExpression).callee; + const tag = (viewUnits[0] as HTMLUnit).tag; + + expect(tag).toBe(originalExpression); + }); + + it('should correctly parse the tag of an CompUnit of the type of any expression inside tag to be original expression', () => { + const statement = parseCode('comp(function IWillDoAnything(){return "Comp"}.call())()'); + const viewUnits = parseView(statement); + + const originalExpression = ( + ((statement.body[0] as t.ExpressionStatement).expression as t.CallExpression).callee as t.CallExpression + ).arguments[0]; + + const tag = (viewUnits[0] as HTMLUnit).tag; + expect(tag).toBe(originalExpression); + }); + + // ---- Content + it('should correctly parse content for CompTag', () => { + const viewUnits = parse('Comp("hello")'); + const content = (viewUnits[0] as HTMLUnit).props._$content; + + expect(t.isStringLiteral(content.value, { value: 'hello' })).toBeTruthy(); + }); + + it('should correctly parse content with any expression as its value', () => { + const statement = parseCode('Comp(() => {doAnything()})'); + const viewUnits = parseView(statement); + const originalExpression = ((statement.body[0] as t.ExpressionStatement).expression as t.CallExpression) + .arguments[0]; + + const content = (viewUnits[0] as HTMLUnit).props._$content.value; + expect(content).toBe(originalExpression); + }); + + it('should correctly parse content with the View => {} as view units as its value', () => { + const statement = parseCode('Comp(View => { div(); span() })'); + const viewUnits = parseView(statement); + + const content = (viewUnits[0] as HTMLUnit).props._$content; + const viewContentMap = content.viewPropMap!; + expect(Object.keys(viewContentMap).length).toBe(1); + + const key = Object.keys(viewContentMap)[0]; + const viewProp = viewContentMap[key]; + const value = content.value; + + // ---- Prop View will be replaced with a random string and stored in props.viewPropMap + expect(t.isStringLiteral(value, { value: key })).toBeTruthy(); + + expect(viewProp.length).toBe(2); + expect(viewProp[0].type).toBe('html'); + expect(t.isStringLiteral((viewProp[0] as HTMLUnit).tag, { value: 'div' })).toBeTruthy(); + expect(viewProp[1].type).toBe('html'); + expect(t.isStringLiteral((viewProp[1] as HTMLUnit).tag, { value: 'span' })).toBeTruthy(); + }); + + // ---- Props + it('should correctly parse chaining function as props', () => { + const viewUnits = parse('Comp().count(1).message("hello")'); + const props = (viewUnits[0] as HTMLUnit).props; + expect(props).toBeTruthy(); + + expect(t.isNumericLiteral(props?.count?.value, { value: 1 })).toBeTruthy(); + expect(t.isStringLiteral(props?.message?.value, { value: 'hello' })).toBeTruthy(); + }); + + it('should correctly parse props with any expression as their values', () => { + const statement = parseCode('Comp().onclick(() => {console.log("hello")})'); + const viewUnits = parseView(statement); + const originalExpression = ((statement.body[0] as t.ExpressionStatement).expression as t.CallExpression) + .arguments[0]; + + const props = (viewUnits[0] as HTMLUnit).props!; + expect(props.onclick?.value).toBe(originalExpression); + }); + + it('should correctly parse the count of props', () => { + const viewUnits = parse('Comp().count(1).message("hello").go1(1).go2(2).go3(3)'); + const props = (viewUnits[0] as HTMLUnit).props!; + expect(Object.keys(props).length).toBe(5); + }); + + it('should correctly parse props with the View => {} as their values', () => { + const statement = parseCode('Comp().internalComp(View => { div() })'); + const viewUnits = parseView(statement); + const props = (viewUnits[0] as HTMLUnit).props!; + const viewPropMap = props.internalComp.viewPropMap!; + expect(Object.keys(viewPropMap).length).toBe(1); + + const key = Object.keys(viewPropMap)[0]; + const viewProp = viewPropMap[key]; + const value = props.internalComp.value; + + // ---- Prop View will be replaced with a random string and stored in props.viewPropMap + expect(t.isStringLiteral(value, { value: key })).toBeTruthy(); + + expect(viewProp.length).toBe(1); + expect(viewProp[0].type).toBe('html'); + expect(t.isStringLiteral((viewProp[0] as HTMLUnit).tag, { value: 'div' })).toBeTruthy(); + }); + + // ---- Children + it('should correctly parse children for CompTag', () => { + const viewUnits = parse('Comp(); { div() }'); + const children = (viewUnits[0] as HTMLUnit).children; + + expect(children?.length).toBe(1); + expect(children?.[0].type).toBe('html'); + expect(t.isStringLiteral((children?.[0] as HTMLUnit).tag, { value: 'div' })).toBeTruthy(); + }); + + it('should correctly parse multiple children', () => { + const viewUnits = parse('Comp(); { div(); span() }'); + const children = (viewUnits[0] as HTMLUnit).children; + + expect(children?.length).toBe(2); + expect(children?.[0].type).toBe('html'); + expect(t.isStringLiteral((children?.[0] as HTMLUnit).tag, { value: 'div' })).toBeTruthy(); + expect(children?.[1].type).toBe('html'); + expect(t.isStringLiteral((children?.[1] as HTMLUnit).tag, { value: 'span' })).toBeTruthy(); + }); + + it('should correctly parse the count of children', () => { + const viewUnits = parse('Comp(); { div(); span(); h1(); { h2() }; h3()}'); + const children = (viewUnits[0] as HTMLUnit).children; + + expect(children?.length).toBe(4); + }); +}); diff --git a/packages/transpiler/view-parser/src/test/EnvUnit.test.ts b/packages/transpiler/view-parser/src/test/EnvUnit.test.ts new file mode 100644 index 00000000..b1fd800c --- /dev/null +++ b/packages/transpiler/view-parser/src/test/EnvUnit.test.ts @@ -0,0 +1,92 @@ +import { expect, describe, it } from 'vitest'; +import { parse, parseCode, parseView } from './mock'; +import { type HTMLUnit, type EnvUnit } from '../types'; +import { types as t } from '@babel/core'; + +describe('EnvUnit', () => { + // ---- Type + it('should identify an env statement as an EnvUnit', () => { + const viewUnits = parse('env().prop(1); { div() }'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('env'); + }); + + it('should identify an env statement as an EnvUnit and ignore other statements', () => { + const viewUnits = parse('env().prop(1); { div() }; div(text)'); + expect(viewUnits.length).toBe(2); + expect(viewUnits[0].type).toBe('env'); + }); + + it('should skip an env statement if the block statement is empty', () => { + const viewUnits = parse('env().prop(1)'); + expect(viewUnits.length).toBe(0); + }); + + // ---- Props + it('should correctly parse the props of an env statement', () => { + const viewUnits = parse('env().count(2); { div() }'); + + const envUnit = viewUnits[0] as EnvUnit; + expect(t.isNumericLiteral(envUnit.props.count.value, { value: 2 })).toBeTruthy(); + }); + + it('should correctly parse the props of an env statement with a complex expression', () => { + const statement = parseCode('env().count(2 + 2); { div() }'); + const viewUnits = parseView(statement); + const originalExpression = ((statement.body[0] as t.ExpressionStatement).expression as t.CallExpression) + .arguments[0]; + + const envUnit = viewUnits[0] as EnvUnit; + expect(envUnit.props.count.value).toBe(originalExpression); + }); + + it('should correctly parse the View => {} as view props', () => { + const statement = parseCode('env().comp(View => { div() }); { div() }'); + const viewUnits = parseView(statement); + + const props = (viewUnits[0] as EnvUnit).props; + const viewPropMap = props.comp.viewPropMap!; + expect(Object.keys(viewPropMap).length).toBe(1); + + const key = Object.keys(viewPropMap)[0]; + const viewProp = viewPropMap[key]; + const value = props.comp.value; + + // ---- Prop View will be replaced with a random string and stored in props.viewPropMap + expect(t.isStringLiteral(value, { value: key })).toBeTruthy(); + + expect(viewProp.length).toBe(1); + expect(viewProp[0].type).toBe('html'); + expect(t.isStringLiteral((viewProp[0] as HTMLUnit).tag, { value: 'div' })).toBeTruthy(); + }); + + it("should be skipped if there's no props", () => { + const viewUnits = parse('env(); { div() }'); + expect(viewUnits.length).toBe(0); + }); + + // ---- Children + it('should correctly parse the children of an env statement', () => { + const viewUnits = parse('env().prop(1); { div() }'); + + const envUnit = viewUnits[0] as EnvUnit; + expect(envUnit.children.length).toBe(1); + expect(envUnit.children[0].type).toBe('html'); + }); + + it('should correctly parse the children of an env statement with multiple children', () => { + const viewUnits = parse('env().prop(1); { div(); div() }'); + + const envUnit = viewUnits[0] as EnvUnit; + expect(envUnit.children.length).toBe(2); + expect(envUnit.children[0].type).toBe('html'); + expect(envUnit.children[1].type).toBe('html'); + }); + + it('should correctly parse the count of children of an env statement with multiple children', () => { + const viewUnits = parse('env().prop(1); { div(); div(); h1(); { h1() }; h2(); h3() }'); + + const envUnit = viewUnits[0] as EnvUnit; + expect(envUnit.children.length).toBe(5); + }); +}); diff --git a/packages/transpiler/view-parser/src/test/ExpUnit.test.ts b/packages/transpiler/view-parser/src/test/ExpUnit.test.ts new file mode 100644 index 00000000..90610f18 --- /dev/null +++ b/packages/transpiler/view-parser/src/test/ExpUnit.test.ts @@ -0,0 +1,119 @@ +import { expect, describe, it } from 'vitest'; +import { parse, parseCode, parseView } from './mock'; +import { type ExpUnit, type HTMLUnit } from '../types'; +import { types as t } from '@babel/core'; + +describe('ExpUnit', () => { + // ---- Type + it('should identify an expression as an ExpUnit', () => { + const viewUnits = parse('this.flag'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('exp'); + }); + + it('should identify an expression inside an _ function call as an ExpUnit', () => { + const viewUnits = parse('_(this.flag)'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('exp'); + }); + + it('should identify the tag of a tagged template literal as an ExpUnit', () => { + const viewUnits = parse('i18n`Hello World`'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('exp'); + }); + + // ---- Content + it('should correctly parse content with any expression as its value', () => { + const statement = parseCode('this.flag'); + const viewUnits = parseView(statement); + const originalExpression = (statement.body[0] as t.ExpressionStatement).expression; + + const content = (viewUnits[0] as ExpUnit).content; + expect(content?.value).toBe(originalExpression); + }); + + it('should correctly parse content with any expression inside _ function call as its value', () => { + const statement = parseCode('_(this.flag)'); + const viewUnits = parseView(statement); + const originalExpression = ((statement.body[0] as t.ExpressionStatement).expression as t.CallExpression) + .arguments[0]; + + const content = (viewUnits[0] as ExpUnit).content; + expect(content?.value).toBe(originalExpression); + }); + + it('should correctly parse the View => {} as view props', () => { + const statement = parseCode('_(View => { div() })'); + const viewUnits = parseView(statement); + + const content = (viewUnits[0] as ExpUnit).content; + const viewPropMap = content.viewPropMap!; + expect(Object.keys(viewPropMap).length).toBe(1); + + const key = Object.keys(viewPropMap)[0]; + const viewProp = viewPropMap[key]; + const value = content.value; + + // ---- Prop View will be replaced with a random string and stored in props.viewPropMap + expect(t.isStringLiteral(value, { value: key })).toBeTruthy(); + + expect(viewProp.length).toBe(1); + expect(viewProp[0].type).toBe('html'); + expect(t.isStringLiteral((viewProp[0] as HTMLUnit).tag, { value: 'div' })).toBeTruthy(); + }); + + // ---- Props + it('should correctly parse chaining function as props', () => { + const viewUnits = parse('_(this.flag).id("id").class("class")'); + const props = (viewUnits[0] as ExpUnit).props; + expect(props).toBeTruthy(); + + expect(t.isStringLiteral(props?.id?.value, { value: 'id' })).toBeTruthy(); + expect(t.isStringLiteral(props?.class?.value, { value: 'class' })).toBeTruthy(); + }); + + it('should correctly parse props with any expression as their values', () => { + const statement = parseCode('_(this.flag).onclick(() => {console.log("hello")})'); + const viewUnits = parseView(statement); + const originalExpression = ((statement.body[0] as t.ExpressionStatement).expression as t.CallExpression) + .arguments[0]; + + const props = (viewUnits[0] as ExpUnit).props!; + expect(props.onclick?.value).toBe(originalExpression); + }); + + it('should correctly parse the count of props', () => { + const viewUnits = parse( + '_(this.flag).id("id").class("class").style("style").onclick(() => {}).onmouseover(() => {})' + ); + const props = (viewUnits[0] as ExpUnit).props!; + expect(Object.keys(props).length).toBe(5); + }); + + it('should correctly parse props with the View => {} as view units as ViewUnits stored in props.viewPropMap', () => { + const statement = parseCode('_(this.flag).anyProp(View => { div() })'); + const viewUnits = parseView(statement); + + const props = (viewUnits[0] as ExpUnit).props!; + const viewPropMap = props.anyProp.viewPropMap!; + expect(Object.keys(viewPropMap).length).toBe(1); + + const key = Object.keys(viewPropMap)[0]; + const viewProp = viewPropMap[key]; + const value = props.anyProp.value; + + // ---- Prop View will be replaced with a random string and stored in props.viewPropMap + expect(t.isStringLiteral(value, { value: key })).toBeTruthy(); + + expect(viewProp.length).toBe(1); + expect(viewProp[0].type).toBe('html'); + expect(t.isStringLiteral((viewProp[0] as HTMLUnit).tag, { value: 'div' })).toBeTruthy(); + }); + + // ---- Children + it('should not have children', () => { + const viewUnits = parse('this.flag; { div() }'); + expect(!('children' in viewUnits[0])).toBeTruthy(); + }); +}); diff --git a/packages/transpiler/view-parser/src/test/ForUnit.test.ts b/packages/transpiler/view-parser/src/test/ForUnit.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/transpiler/view-parser/src/test/HTMLUnit.test.ts b/packages/transpiler/view-parser/src/test/HTMLUnit.test.ts new file mode 100644 index 00000000..94b2b61f --- /dev/null +++ b/packages/transpiler/view-parser/src/test/HTMLUnit.test.ts @@ -0,0 +1,156 @@ +import { expect, describe, it } from 'vitest'; +import { parse, parseCode, parseView } from './mock'; +import { type HTMLUnit } from '../types'; +import { types as t } from '@babel/core'; + +describe('HTMLUnit', () => { + // ---- Type + it("should identify a function call with identifier's name in htmlTags of config as an HTMLUnit", () => { + const viewUnits = parse('div()'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('html'); + }); + + it('should identify an expression inside tag function call as an HTMLUnit', () => { + const viewUnits = parse('tag(function IWillDoAnything(){return "div"}.call())()'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('html'); + }); + + // ---- Tag + it('should correctly parse the tag of an HTMLUnit of the type of function call', () => { + const viewUnits = parse('div()'); + const tag = (viewUnits[0] as HTMLUnit).tag; + expect(t.isStringLiteral(tag, { value: 'div' })).toBeTruthy(); + }); + + it('should correctly parse the tag of an HTMLUnit of the type of any expression inside tag to be original expression', () => { + const statement = parseCode('tag(function IWillDoAnything(){return "div"}.call())()'); + const viewUnits = parseView(statement); + + const originalExpression = ( + ((statement.body[0] as t.ExpressionStatement).expression as t.CallExpression).callee as t.CallExpression + ).arguments[0]; + + const tag = (viewUnits[0] as HTMLUnit).tag; + expect(tag).toBe(originalExpression); + }); + + // ---- Content + it('should correctly parse content for HTMLTag', () => { + const viewUnits = parse('div("hello")'); + const content = (viewUnits[0] as HTMLUnit).props.textContent; + + expect(t.isStringLiteral(content?.value, { value: 'hello' })).toBeTruthy(); + }); + + it('should correctly parse content with any expression as its value', () => { + const statement = parseCode('div(() => {doAnything()})'); + const viewUnits = parseView(statement); + const originalExpression = ((statement.body[0] as t.ExpressionStatement).expression as t.CallExpression) + .arguments[0]; + + const content = (viewUnits[0] as HTMLUnit).props.textContent; + expect(content?.value).toBe(originalExpression); + }); + + // ---- Props + it('should correctly parse chaining function as props', () => { + const viewUnits = parse('div().id("id").class("class")'); + const props = (viewUnits[0] as HTMLUnit).props; + expect(props).toBeTruthy(); + + expect(t.isStringLiteral(props?.id?.value, { value: 'id' })).toBeTruthy(); + expect(t.isStringLiteral(props?.class?.value, { value: 'class' })).toBeTruthy(); + }); + + it('should correctly parse empty prop as true', () => { + const viewUnits = parse('div().id()'); + const props = (viewUnits[0] as HTMLUnit).props; + expect(props).toBeTruthy(); + + expect(t.isBooleanLiteral(props?.id?.value, { value: true })).toBeTruthy(); + }); + + it('should correctly parse props with any expression as their values', () => { + const statement = parseCode('div().onclick(() => {console.log("hello")})'); + const viewUnits = parseView(statement); + const originalExpression = ((statement.body[0] as t.ExpressionStatement).expression as t.CallExpression) + .arguments[0]; + + const props = (viewUnits[0] as HTMLUnit).props!; + expect(props.onclick?.value).toBe(originalExpression); + }); + + it('should correctly parse props for HTMLTag with htmlTag function call as its tag', () => { + const viewUnits = parse('htmlTag(function IWillDoAnything(){return "div"}.call()).id("id").class("class")'); + const props = (viewUnits[0] as HTMLUnit).props!; + + expect(t.isStringLiteral(props.id?.value, { value: 'id' })).toBeTruthy(); + expect(t.isStringLiteral(props.class?.value, { value: 'class' })).toBeTruthy(); + }); + + it('should parse props with correct count', () => { + const viewUnits = parse('div(0).id(1).class(2).style(3).onclick(4).onmouseover(5)'); + + // ---- Content stored in content instead of props + const props = (viewUnits[0] as HTMLUnit).props!; + expect(Object.keys(props).length).toBe(6); + }); + + it("should parse the View => {} as props' nested view as ViewUnits stored in props.viewPropMap", () => { + const statement = parseCode('div().anyProp(View => { span() })'); + const viewUnits = parseView(statement); + + const props = (viewUnits[0] as HTMLUnit).props!; + const viewPropMap = props.anyProp.viewPropMap!; + expect(Object.keys(viewPropMap).length).toBe(1); + + const key = Object.keys(viewPropMap)[0]; + const viewProp = viewPropMap[key]; + const value = props.anyProp.value; + + // ---- Prop View will be replaced with a random string and stored in props.viewPropMap + expect(t.isStringLiteral(value, { value: key })).toBeTruthy(); + + expect(viewProp.length).toBe(1); + expect(viewProp[0].type).toBe('html'); + expect(t.isStringLiteral((viewProp[0] as HTMLUnit).tag, { value: 'span' })).toBeTruthy(); + }); + + // ---- Children + it('should correctly parse children for HTMLTag', () => { + const viewUnits = parse('div();{ h1("hello") }'); + const children = (viewUnits[0] as HTMLUnit).children!; + + expect(children.length).toBe(1); + expect(children[0].type).toBe('html'); + expect(t.isStringLiteral((children[0] as HTMLUnit).tag, { value: 'h1' })).toBeTruthy(); + expect( + t.isStringLiteral((children[0] as HTMLUnit).props.textContent.value, { + value: 'hello', + }) + ).toBeTruthy(); + }); + + it('should parse children with correct count', () => { + const viewUnits = parse('div();{ h1("1"); h2("2"); h3("3") }'); + const children = (viewUnits[0] as HTMLUnit).children!; + + expect(children.length).toBe(3); + }); + + it('should parse nested children with correct count', () => { + const viewUnits = parse('div();{ h1("1"); { span(1); span(2) } h2("2"); h3("3") }'); + const children = (viewUnits[0] as HTMLUnit).children!; + + expect(children.length).toBe(3); + }); + + it('should drop content in HTMLTag if children exist', () => { + const viewUnits = parse('div("hello");{ h1("hello") }'); + const content = (viewUnits[0] as HTMLUnit).props.textContent; + + expect(content).toBeUndefined(); + }); +}); diff --git a/packages/transpiler/view-parser/src/test/IfUnit.test.ts b/packages/transpiler/view-parser/src/test/IfUnit.test.ts new file mode 100644 index 00000000..bb5a9216 --- /dev/null +++ b/packages/transpiler/view-parser/src/test/IfUnit.test.ts @@ -0,0 +1,72 @@ +import { expect, describe, it } from 'vitest'; +import { parse, parseCode, parseView } from './mock'; +import { type HTMLUnit, type IfUnit } from '../types'; +import { types as t } from '@babel/core'; + +describe('IfUnit', () => { + // ---- Type + it('should identify an if statement as an IfUnit', () => { + const viewUnits = parse('if(true) {}'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('if'); + }); + + it('should identify an if statement with else clause as an IfUnit', () => { + const viewUnits = parse('if(true) {} else {}'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('if'); + }); + + it('should identify an if statement with else if clause as an IfUnit', () => { + const viewUnits = parse('if(true) {} else if(false) {}'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('if'); + }); + + it('should identify an if statement with else if clause and else clause as an IfUnit', () => { + const viewUnits = parse('if(true) {} else if(false) {} else {}'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('if'); + }); + + // ---- Branch.Condition + it('should correctly parse the count of conditions for multiple else and else if clauses', () => { + const viewUnits = parse('if(true) {} else if(false) {} else if(false) {} else {}'); + const ifUnit = viewUnits[0] as IfUnit; + expect(ifUnit.branches.length).toBe(4); + }); + + it('should correctly parse the condition of an if statement', () => { + const statement = parseCode('if(this.flag) {}'); + const viewUnits = parseView(statement); + const originalExpression = (statement.body[0] as t.IfStatement).test; + const ifUnit = viewUnits[0] as IfUnit; + expect(ifUnit.branches[0].condition).toBe(originalExpression); + }); + + it('should correctly parse the condition of an else if statement', () => { + const statement = parseCode('if(true) {} else if(this.flag) {}'); + const viewUnits = parseView(statement); + const originalExpression = ((statement.body[0] as t.IfStatement).alternate as t.IfStatement).test; + const ifUnit = viewUnits[0] as IfUnit; + expect(ifUnit.branches[1].condition).toBe(originalExpression); + }); + + it('should correctly parse the condition of an else statement as plain true', () => { + const statement = parseCode('if(true) {} else {}'); + const viewUnits = parseView(statement); + const ifUnit = viewUnits[0] as IfUnit; + expect(t.isBooleanLiteral(ifUnit.branches[1].condition)).toBeTruthy(); + }); + + // ---- Branch.Children + it('should correctly parse the children of an if statement', () => { + const viewUnits = parse('if(true) {div()}'); + const ifUnit = viewUnits[0] as IfUnit; + expect(ifUnit.branches[0].children.length).toBe(1); + expect(ifUnit.branches[0].children[0].type).toBe('html'); + + const tag = (ifUnit.branches[0].children[0] as HTMLUnit).tag; + expect(t.isStringLiteral(tag, { value: 'div' })).toBeTruthy(); + }); +}); diff --git a/packages/transpiler/view-parser/src/test/SubviewUnit.test.ts b/packages/transpiler/view-parser/src/test/SubviewUnit.test.ts new file mode 100644 index 00000000..dfbee63c --- /dev/null +++ b/packages/transpiler/view-parser/src/test/SubviewUnit.test.ts @@ -0,0 +1,97 @@ +import { expect, describe, it } from 'vitest'; +import { parse, parseCode, parseView } from './mock'; +import { type SnippetUnit, type HTMLUnit } from '../types'; +import { types as t } from '@babel/core'; + +describe('SnippetUnit', () => { + // ---- Type + it("should identify a this expression function call with identifier's name in snippetNames of config as an SnippetUnit", () => { + const viewUnits = parse('this.MySnippet()'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('snippet'); + }); + + it("should not identify a this expression function call with identifier's name not in snippetNames of config as an SnippetUnit", () => { + const viewUnits = parse('this.MySnippetNotInConfig()'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).not.toBe('snippet'); + }); + + // ---- Tag + it('should correctly parse the tag of an SnippetUnit of the type of function call', () => { + const viewUnits = parse('this.MySnippet()'); + const tag = (viewUnits[0] as HTMLUnit).tag; + + expect(tag).toBe('MySnippet'); + }); + + // ---- Props + it('should correctly parse content for SnippetTag', () => { + const viewUnits = parse('this.MySnippet("hello")'); + const content = (viewUnits[0] as SnippetUnit).props?.content; + + expect(t.isStringLiteral(content?.value, { value: 'hello' })).toBeTruthy(); + }); + + it('should correctly parse props for SnippetTag', () => { + const viewUnits = parse('this.MySnippet().id("id").class("class")'); + const props = (viewUnits[0] as SnippetUnit).props; + expect(t.isStringLiteral(props?.id.value, { value: 'id' })).toBeTruthy(); + expect(t.isStringLiteral(props?.class.value, { value: 'class' })).toBeTruthy(); + }); + + it('should correctly parse props with any expression as its value', () => { + const statement = parseCode('this.MySnippet(() => {doAnything()})'); + const viewUnits = parseView(statement); + const originalExpression = ((statement.body[0] as t.ExpressionStatement).expression as t.CallExpression) + .arguments[0]; + + const content = (viewUnits[0] as SnippetUnit).props?.content; + expect(content?.value).toBe(originalExpression); + }); + + it('should correctly parse props with the View => {} as view units as its value', () => { + const statement = parseCode('this.MySnippet(View => { div() })'); + const viewUnits = parseView(statement); + + const props = (viewUnits[0] as SnippetUnit).props!; + const viewPropMap = props.content.viewPropMap!; + expect(Object.keys(viewPropMap).length).toBe(1); + + const key = Object.keys(viewPropMap)[0]; + const viewProp = viewPropMap[key]; + const value = props?.content.value; + + // ---- Prop View will be replaced with a random string and stored in props.viewPropMap + expect(t.isStringLiteral(value, { value: key })).toBeTruthy(); + + expect(viewProp.length).toBe(1); + expect(viewProp[0].type).toBe('html'); + expect(t.isStringLiteral((viewProp[0] as HTMLUnit).tag, { value: 'div' })).toBeTruthy(); + }); + + // ---- Children + it('should correctly parse the children of an SnippetUnit', () => { + const viewUnits = parse('this.MySnippet(); { div() }'); + + const snippetUnit = viewUnits[0] as SnippetUnit; + expect(snippetUnit.children?.length).toBe(1); + expect(snippetUnit.children?.[0].type).toBe('html'); + }); + + it('should correctly parse the children of an SnippetUnit with multiple children', () => { + const viewUnits = parse('this.MySnippet(); { div(); div() }'); + + const snippetUnit = viewUnits[0] as SnippetUnit; + expect(snippetUnit.children?.length).toBe(2); + expect(snippetUnit.children?.[0].type).toBe('html'); + expect(snippetUnit.children?.[1].type).toBe('html'); + }); + + it('should correctly parse the count of children of an SnippetUnit with multiple children', () => { + const viewUnits = parse('this.MySnippet(); { div(); div(); h1(); h2(); h3() }'); + + const snippetUnit = viewUnits[0] as SnippetUnit; + expect(snippetUnit.children?.length).toBe(5); + }); +}); diff --git a/packages/transpiler/view-parser/src/test/SwitchUnit.test.ts b/packages/transpiler/view-parser/src/test/SwitchUnit.test.ts new file mode 100644 index 00000000..9c523332 --- /dev/null +++ b/packages/transpiler/view-parser/src/test/SwitchUnit.test.ts @@ -0,0 +1,60 @@ +import { expect, describe, it } from 'vitest'; +import { parse, parseCode, parseView } from './mock'; +import { type SwitchUnit } from '../types'; +import { types as t } from '@babel/core'; + +describe('SwitchUnit', () => { + // ---- Type + it('should identify an switch statement as an SwitchUnit', () => { + const viewUnits = parse('switch(true) {}'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('switch'); + }); + + it('should identify an switch statement with cases', () => { + const viewUnits = parse('switch(true) { case 1: break; }'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('switch'); + expect((viewUnits[0] as SwitchUnit).branches.length).toBe(1); + }); + + it('should identify an switch statement with cases and default', () => { + const viewUnits = parse('switch(true) { case 1: break; default: break; }'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('switch'); + expect((viewUnits[0] as SwitchUnit).branches.length).toBe(2); + }); + + // ---- Discriminant + it('should correctly parse the discriminant of an switch statement', () => { + const statement = parseCode('switch(this.flag) {}'); + const viewUnits = parseView(statement); + const originalExpression = (statement.body[0] as t.SwitchStatement).discriminant; + const switchUnit = viewUnits[0] as SwitchUnit; + expect(switchUnit.discriminant).toBe(originalExpression); + }); + + // ---- Branches + it('should correctly parse the count of branches', () => { + const viewUnits = parse('switch(true) { case 1: break; case 2: break; case 3: break; }'); + const switchUnit = viewUnits[0] as SwitchUnit; + expect(switchUnit.branches.length).toBe(3); + }); + + it('should correctly parse the case of a branch', () => { + const statement = parseCode('switch(true) { case this.flag: break; }'); + const viewUnits = parseView(statement); + const originalExpression = ((statement.body[0] as t.SwitchStatement).cases[0] as t.SwitchCase).test; + const switchUnit = viewUnits[0] as SwitchUnit; + expect(switchUnit.branches[0].case).toBe(originalExpression); + }); + + // ---- Break + it('should correctly parse the break of a branch', () => { + const viewUnits = parse('switch(true) { case 1: break; }'); + + const switchUnit = viewUnits[0] as SwitchUnit; + expect(switchUnit.branches[0].break).toBe(true); + expect(switchUnit.branches[0].children.length).toBe(0); + }); +}); diff --git a/packages/transpiler/view-parser/src/test/TextUnit.test.ts b/packages/transpiler/view-parser/src/test/TextUnit.test.ts new file mode 100644 index 00000000..d9053ba3 --- /dev/null +++ b/packages/transpiler/view-parser/src/test/TextUnit.test.ts @@ -0,0 +1,86 @@ +import { describe, expect, it } from 'vitest'; +import { parse, parseCode, parseView } from './mock'; +import { types as t } from '@babel/core'; +import { type TextUnit } from '../index'; + +describe('TextUnit', () => { + // ---- Type + it('should identify a string literal as a TextUnit', () => { + // ---- Add a html unit to make it a string literal instead of a directive + const viewUnits = parse('div();"Hello World"'); + expect(viewUnits.length).toBe(2); + expect(viewUnits[1].type).toBe('text'); + }); + + it('should identify a template literal as a TextUnit', () => { + const viewUnits = parse('`Hello World`'); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('text'); + }); + + it('should identify a directive as a TextUnit', () => { + const statement = parseCode('"Hello World"'); + expect(statement.body.length).toBe(0); + expect(statement.directives.length).toBe(1); + + const viewUnits = parseView(statement); + expect(viewUnits.length).toBe(1); + expect(viewUnits[0].type).toBe('text'); + }); + + it('should identify a tagged template literal with a string literal as its tag as two TextUnits', () => { + const viewUnits = parse('"hi"`Hello World`'); + expect(viewUnits.length).toBe(2); + + expect(viewUnits[0].type).toBe('text'); + expect(viewUnits[1].type).toBe('text'); + + const content1 = (viewUnits[0] as TextUnit).content; + expect(t.isStringLiteral(content1, { value: 'hi' })).toBeTruthy(); + + const content2 = (viewUnits[1] as TextUnit).content; + expect(t.isTemplateLiteral(content2)).toBeTruthy(); + const content2Quasis = (content2 as t.TemplateLiteral).quasis[0]; + expect(t.isTemplateElement(content2Quasis)).toBeTruthy(); + const content2Value = content2Quasis.value; + expect(content2Value.raw).toBe('Hello World'); + }); + + it('should identify a tagged template literal with a template literal as its tag as two TextUnits', () => { + const viewUnits = parse('`hi``Hello World`'); + expect(viewUnits.length).toBe(2); + expect(viewUnits[0].type).toBe('text'); + expect(viewUnits[1].type).toBe('text'); + + const content1 = (viewUnits[0] as TextUnit).content; + expect(t.isTemplateLiteral(content1)).toBeTruthy(); + const content1Quasis = (content1 as t.TemplateLiteral).quasis[0]; + expect(t.isTemplateElement(content1Quasis)).toBeTruthy(); + const content1Value = content1Quasis.value; + expect(content1Value.raw).toBe('hi'); + + const content2 = (viewUnits[1] as TextUnit).content; + expect(t.isTemplateLiteral(content2)).toBeTruthy(); + const content2Quasis = (content2 as t.TemplateLiteral).quasis[0]; + expect(t.isTemplateElement(content2Quasis)).toBeTruthy(); + const content2Value = content2Quasis.value; + expect(content2Value.raw).toBe('Hello World'); + }); + + // ---- Content + it('should correctly parse a string literal as its content', () => { + const viewUnits = parse('"Hello World"'); + const content = (viewUnits[0] as TextUnit).content; + expect(t.isStringLiteral(content, { value: 'Hello World' })).toBeTruthy(); + }); + + it('should save the original node as its content for a template literal', () => { + // eslint-disable-next-line no-template-curly-in-string + const statement = parseCode('`Hello World ${name}`'); + const viewUnits = parseView(statement); + const originalNode = (statement.body[0] as t.ExpressionStatement).expression; + + const content = (viewUnits[0] as TextUnit).content; + expect(content).toBe(originalNode); + }); +}); diff --git a/packages/transpiler/view-parser/src/test/TryUnit.test.ts b/packages/transpiler/view-parser/src/test/TryUnit.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/transpiler/view-parser/src/test/mock.ts b/packages/transpiler/view-parser/src/test/mock.ts new file mode 100644 index 00000000..49b0f982 --- /dev/null +++ b/packages/transpiler/view-parser/src/test/mock.ts @@ -0,0 +1,224 @@ +import babel, { parseSync, type types as t } from '@babel/core'; +import { type ViewParserConfig } from '../types'; +import { parseView as pV } from '..'; + +const htmlTags = [ + 'a', + 'abbr', + 'address', + 'area', + 'article', + 'aside', + 'audio', + 'b', + 'base', + 'bdi', + 'bdo', + 'blockquote', + 'body', + 'br', + 'button', + 'canvas', + 'caption', + 'cite', + 'code', + 'col', + 'colgroup', + 'data', + 'datalist', + 'dd', + 'del', + 'details', + 'dfn', + 'dialog', + 'div', + 'dl', + 'dt', + 'em', + 'embed', + 'fieldset', + 'figcaption', + 'figure', + 'footer', + 'form', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'head', + 'header', + 'hgroup', + 'hr', + 'html', + 'i', + 'iframe', + 'img', + 'input', + 'ins', + 'kbd', + 'label', + 'legend', + 'li', + 'link', + 'main', + 'map', + 'mark', + 'menu', + 'meta', + 'meter', + 'nav', + 'noscript', + 'object', + 'ol', + 'optgroup', + 'option', + 'output', + 'p', + 'picture', + 'pre', + 'progress', + 'q', + 'rp', + 'rt', + 'ruby', + 's', + 'samp', + 'script', + 'section', + 'select', + 'slot', + 'small', + 'source', + 'span', + 'strong', + 'style', + 'sub', + 'summary', + 'sup', + 'table', + 'tbody', + 'td', + 'template', + 'textarea', + 'tfoot', + 'th', + 'thead', + 'time', + 'title', + 'tr', + 'track', + 'u', + 'ul', + 'var', + 'video', + 'wbr', + 'acronym', + 'applet', + 'basefont', + 'bgsound', + 'big', + 'blink', + 'center', + 'dir', + 'font', + 'frame', + 'frameset', + 'isindex', + 'keygen', + 'listing', + 'marquee', + 'menuitem', + 'multicol', + 'nextid', + 'nobr', + 'noembed', + 'noframes', + 'param', + 'plaintext', + 'rb', + 'rtc', + 'spacer', + 'strike', + 'tt', + 'xmp', + 'animate', + 'animateMotion', + 'animateTransform', + 'circle', + 'clipPath', + 'defs', + 'desc', + 'ellipse', + 'feBlend', + 'feColorMatrix', + 'feComponentTransfer', + 'feComposite', + 'feConvolveMatrix', + 'feDiffuseLighting', + 'feDisplacementMap', + 'feDistantLight', + 'feDropShadow', + 'feFlood', + 'feFuncA', + 'feFuncB', + 'feFuncG', + 'feFuncR', + 'feGaussianBlur', + 'feImage', + 'feMerge', + 'feMergeNode', + 'feMorphology', + 'feOffset', + 'fePointLight', + 'feSpecularLighting', + 'feSpotLight', + 'feTile', + 'feTurbulence', + 'filter', + 'foreignObject', + 'g', + 'image', + 'line', + 'linearGradient', + 'marker', + 'mask', + 'metadata', + 'mpath', + 'path', + 'pattern', + 'polygon', + 'polyline', + 'radialGradient', + 'rect', + 'set', + 'stop', + 'svg', + 'switch', + 'symbol', + 'text', + 'textPath', + 'tspan', + 'use', + 'view', +]; +const snippetNames = ['MySnippet', 'InnerButton']; + +export const config: ViewParserConfig = { + babelApi: babel, + htmlTags, + snippetNames, +}; + +export function parseCode(code: string) { + return (parseSync(`function code() {${code}}`)!.program.body[0] as t.FunctionDeclaration).body; +} + +export function parseView(statement: t.BlockStatement) { + return pV(statement, config); +} + +export function parse(code: string) { + return parseView(parseCode(code)); +} diff --git a/packages/transpiler/view-parser/src/types.ts b/packages/transpiler/view-parser/src/types.ts new file mode 100644 index 00000000..8f615ef7 --- /dev/null +++ b/packages/transpiler/view-parser/src/types.ts @@ -0,0 +1,100 @@ +import type Babel from '@babel/core'; +import { type types as t } from '@babel/core'; + +export interface ViewProp { + value: t.Expression; + viewPropMap: Record; +} + +export interface TextUnit { + type: 'text'; + content: t.Expression; +} + +export interface HTMLUnit { + type: 'html'; + tag: t.Expression; + props: Record; + children: ViewUnit[]; +} + +export interface CompUnit { + type: 'comp'; + tag: t.Expression; + props: Record; + children: ViewUnit[]; +} + +export interface ForUnit { + type: 'for'; + item: t.LVal; + array: t.Expression; + key: t.Expression; + children: ViewUnit[]; +} + +export interface IfBranch { + condition: t.Expression; + children: ViewUnit[]; +} + +export interface IfUnit { + type: 'if'; + branches: IfBranch[]; +} + +export interface SwitchBranch { + case: t.Expression; + children: ViewUnit[]; + break: boolean; +} + +export interface TryUnit { + type: 'try'; + children: ViewUnit[]; + exception: t.Identifier | t.ArrayPattern | t.ObjectPattern | null; + catchChildren: ViewUnit[]; +} + +export interface SwitchUnit { + type: 'switch'; + discriminant: t.Expression; + branches: SwitchBranch[]; +} + +export interface EnvUnit { + type: 'env'; + props: Record; + children: ViewUnit[]; +} + +export interface ExpUnit { + type: 'exp'; + content: ViewProp; + props: Record; +} + +export interface SnippetUnit { + type: 'snippet'; + tag: string; + props: Record; + children: ViewUnit[]; +} + +export type ViewUnit = + | TextUnit + | HTMLUnit + | CompUnit + | IfUnit + | ForUnit + | EnvUnit + | ExpUnit + | SnippetUnit + | SwitchUnit + | TryUnit; + +export interface ViewParserConfig { + babelApi: typeof Babel; + snippetNames: string[]; + htmlTags: string[]; +} diff --git a/packages/transpiler/view-parser/tsconfig.json b/packages/transpiler/view-parser/tsconfig.json new file mode 100644 index 00000000..e0932d78 --- /dev/null +++ b/packages/transpiler/view-parser/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "esModuleInterop": true + }, + "ts-node": { + "esm": true + } +} \ No newline at end of file diff --git a/packages/transpiler/vite-plugin-inula-next/dist/index.js b/packages/transpiler/vite-plugin-inula-next/dist/index.js new file mode 100644 index 00000000..dd195ca2 --- /dev/null +++ b/packages/transpiler/vite-plugin-inula-next/dist/index.js @@ -0,0 +1,2 @@ +var ut=Object.create;var _=Object.defineProperty;var mt=Object.getOwnPropertyDescriptor;var ft=Object.getOwnPropertyNames;var bt=Object.getPrototypeOf,xt=Object.prototype.hasOwnProperty;var H=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var yt=(t,e,i,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of ft(e))!xt.call(t,r)&&r!==i&&_(t,r,{get:()=>e[r],enumerable:!(s=mt(e,r))||s.enumerable});return t};var gt=(t,e,i)=>(i=t!=null?ut(bt(t)):{},yt(e||!t||!t.__esModule?_(i,"default",{value:t,enumerable:!0}):i,t));var R=H(v=>{"use strict";Object.defineProperty(v,"__esModule",{value:!0});v.declare=G;v.declarePreset=void 0;var k={assertVersion:t=>e=>{vt(e,t.version)}};Object.assign(k,{targets:()=>()=>({}),assumption:()=>()=>{}});function G(t){return(e,i,s)=>{var r;let n;for(let a of Object.keys(k)){var o;e[a]||((o=n)!=null||(n=Et(e)),n[a]=k[a](n))}return t((r=n)!=null?r:e,i||{},s)}}var Zt=v.declarePreset=G;function Et(t){let e=null;return typeof t.version=="string"&&/^7\./.test(t.version)&&(e=Object.getPrototypeOf(t),e&&(!hasOwnProperty.call(e,"version")||!hasOwnProperty.call(e,"transform")||!hasOwnProperty.call(e,"template")||!hasOwnProperty.call(e,"types"))&&(e=null)),Object.assign({},e,t)}function vt(t,e){if(typeof t=="number"){if(!Number.isInteger(t))throw new Error("Expected string or integer value.");t=`^${t}.0.0-0`}if(typeof t!="string")throw new Error("Expected string or integer value.");let i=Error.stackTraceLimit;typeof i=="number"&&i<25&&(Error.stackTraceLimit=25);let s;throw e.slice(0,2)==="7."?s=new Error(`Requires Babel "^7.0.0-beta.41", but was loaded with "${e}". You'll need to update your @babel/core version.`):s=new Error(`Requires Babel "${t}", but was loaded with "${e}". If you are sure you have a compatible version of @babel/core, it is likely that something in your build process is loading the wrong version. Inspect the stack trace of this error to look for the first entry that doesn't mention "@babel/core" or "babel-core" to see what is calling Babel.`),typeof i=="number"&&(Error.stackTraceLimit=i),Object.assign(s,{code:"BABEL_VERSION_UNSUPPORTED",version:e,range:t})}});var J=H(w=>{"use strict";Object.defineProperty(w,"__esModule",{value:!0});w.default=void 0;var Pt=R(),ee=w.default=(0,Pt.declare)((t,e)=>{t.assertVersion(7);let{version:i}=e;{let{legacy:r}=e;if(r!==void 0){if(typeof r!="boolean")throw new Error(".legacy must be a boolean.");if(i!==void 0)throw new Error("You can either use the .legacy or the .version option, not both.")}if(i===void 0)i=r?"legacy":"2018-09";else if(i!=="2023-11"&&i!=="2023-05"&&i!=="2023-01"&&i!=="2022-03"&&i!=="2021-12"&&i!=="2018-09"&&i!=="legacy")throw new Error("Unsupported decorators version: "+i);var{decoratorsBeforeExport:s}=e;if(s===void 0){if(i==="2021-12"||i==="2022-03")s=!1;else if(i==="2018-09")throw new Error("The decorators plugin, when .version is '2018-09' or not specified, requires a 'decoratorsBeforeExport' option, whose value must be a boolean.")}else{if(i==="legacy"||i==="2022-03"||i==="2023-01")throw new Error(`'decoratorsBeforeExport' can't be used with ${i} decorators.`);if(typeof s!="boolean")throw new Error("'decoratorsBeforeExport' must be a boolean.")}}return{name:"syntax-decorators",manipulateOptions({generatorOpts:r},n){i==="legacy"?n.plugins.push("decorators-legacy"):i==="2023-01"||i==="2023-05"||i==="2023-11"?n.plugins.push(["decorators",{allowCallParenthesized:!1}],"decoratorAutoAccessors"):i==="2022-03"?n.plugins.push(["decorators",{decoratorsBeforeExport:!1,allowCallParenthesized:!1}],"decoratorAutoAccessors"):i==="2021-12"?(n.plugins.push(["decorators",{decoratorsBeforeExport:s}],"decoratorAutoAccessors"),r.decoratorsBeforeExport=s):i==="2018-09"&&(n.plugins.push(["decorators",{decoratorsBeforeExport:s}]),r.decoratorsBeforeExport=s)}}})});import{transform as Yt}from"@babel/core";var V=gt(J(),1);import{minimatch as ot}from"minimatch";function E(t,e={},i={},s={}){function r(a,h,p){return Object.fromEntries(Object.entries(a).map(([l,d])=>[`${h}${l}`,(...c)=>(c.forEach((m,u)=>{d=d.replace(`$${u}`,m)}),p(`:D - ${t}[${h}${l}]: ${d}`))]))}let n={...r(e,"throw",a=>{throw new Error(a)}),...r(i,"error",console.error),...r(s,"warn",console.warn)};function o(a){return()=>`:D ${t}: ${a} not described`}return{...n,throwUnknown:o("throw"),errorUnknown:o("error"),warnUnknown:o("warn")}}var b=E("ViewParser",{1:"Invalid syntax in DLight's View, only accepts dot chain call expression",2:"First argument of $0() must be an expression",3:"Invalid syntax in DLight's View, only accepts expression as props",4:"Invalid Snippet calling, only accepts static snippet calling like `this.Snippet()`"},{1:"DLight only accepts ForOfStatement as for loop, skipping this statement",2:"EnvUnit must have at least one child, skipping this statement",3:"Only Env/Comp/HTMLUnit can have a statement block as its children, skipping this statement",4:"If you want to use a key in a for loop, make the first statement as a label statement like `key: item`, skipping this key for now",5:"ForUnit must have at least one child, skipping this statement",For:"ForUnit must have at least one child, skipping this statement"},{1:"EnvUnit must have at least one prop, skipping this statement and flattening its children"}),X=class{compWrapper="comp";htmlTagWrapper="tag";environmentTagName="env";expressionTagName="_";config;t;traverse;snippetNames;htmlTags;viewUnits=[];constructor(t){this.config=t,this.t=t.babelApi.types,this.traverse=t.babelApi.traverse,this.snippetNames=t.snippetNames,this.htmlTags=t.htmlTags}parse(t){return[...t.directives,...t.body].forEach(this.parseStatement.bind(this)),this.viewUnits.length===1&&this.viewUnits[0].type==="env"&&this.viewUnits[0].children.length===0?(b.error2(),[]):this.viewUnits}parseStatement(t){if(!this.isInvalidExpression(t)){if(this.t.isExpressionStatement(t)){this.parseExpression(t.expression);return}if(this.t.isTryStatement(t)){this.parseTry(t);return}if(this.t.isForOfStatement(t)){this.parseFor(t);return}if(this.t.isIfStatement(t)){this.parseIf(t);return}if(this.t.isSwitchStatement(t)){this.parseSwitch(t);return}if(this.t.isDirective(t)){this.parseText(t.value);return}if(this.t.isBlockStatement(t)){let e=this.viewUnits[this.viewUnits.length-1],i=e?.type,s=this.parseView(t);i==="html"?(delete e.props.textContent,e.children.push(...s)):i==="comp"||i==="snippet"?e.children.push(...s):i==="env"?s.length>0?e.children.push(...s):(this.viewUnits.pop(),b.error2()):b.error3()}}}parseExpression(t){if(this.t.isCallExpression(t)){this.parseTag(t);return}if(this.t.isStringLiteral(t)||this.t.isTemplateLiteral(t)){this.parseText(t);return}if(this.t.isTaggedTemplateExpression(t)){this.parseTaggedTemplate(t);return}this.viewUnits.push({type:"exp",content:this.parseProp(t),props:{}})}parseIfBranches(t){let e=[],i=t.test,s=this.t.isBlockStatement(t.consequent)?t.consequent:this.t.blockStatement([t.consequent]);if(e.push({condition:i,children:this.parseView(s)}),this.t.isIfStatement(t.alternate))e.push(...this.parseIfBranches(t.alternate));else if(t.alternate){let r=this.t.isBlockStatement(t.alternate)?t.alternate:this.t.blockStatement([t.alternate]);e.push({condition:this.t.booleanLiteral(!0),children:this.parseView(r)})}return e}parseIf(t){this.viewUnits.push({type:"if",branches:this.parseIfBranches(t)})}parseSwitch(t){let e=[];t.cases.forEach(i=>{let s=i.consequent,r=s.length===1&&this.t.isBlockStatement(s[0])?s[0]:this.t.blockStatement(s),n=this.t.isBreakStatement(r.body[r.body.length-1]);n&&r.body.pop();let o=this.parseView(r),a={case:i.test??this.t.booleanLiteral(!0),children:o,break:n};e.push(a)}),this.viewUnits.push({type:"switch",discriminant:t.discriminant,branches:e})}parseTry(t){let e=this.t.blockStatement(t.block.body),i=t.handler?.body??this.t.blockStatement([]);this.viewUnits.push({type:"try",children:this.parseView(e),exception:t.handler?.param??null,catchChildren:this.parseView(i)})}parseFor(t){let e=t.left;this.t.isVariableDeclaration(e)||b.throw1();let i=e.declarations[0].id,s=t.right,r=this.t.nullLiteral(),n=t.body,o;if(this.t.isExpressionStatement(n))o=[n];else if(this.t.isBlockStatement(n)){let l=n.body;if(l.length===0)return b.error5();let d=l[0];if(this.t.isLabeledStatement(d)&&this.t.isIdentifier(d.label)){if(d.label.name!=="key"||!this.t.isExpressionStatement(d.body))b.error4();else{let c=d.body.expression;this.t.isExpression(c)&&!(this.t.isNullLiteral(c)||this.t.isIdentifier(c)&&c.name==="undefined")&&(r=c)}o=l.slice(1)}else o=l}else return;let a=o.filter(l=>this.t.isDirective(l)),h=o.filter(l=>!this.t.isDirective(l)),p=this.t.blockStatement(h,a);this.viewUnits.push({type:"for",item:i,array:s,key:r,children:this.parseView(p)})}parseText(t){this.t.isDirectiveLiteral(t)&&(t=this.t.stringLiteral(t.value)),this.viewUnits.push({type:"text",content:t})}parseTaggedTemplate(t){if(this.t.isStringLiteral(t.tag)||this.t.isTemplateLiteral(t.tag)){this.viewUnits.push({type:"text",content:t.tag}),this.viewUnits.push({type:"text",content:t.quasi});return}this.viewUnits.push({type:"exp",content:this.parseProp(t),props:{}})}isPropView(t){if(!(this.t.isArrowFunctionExpression(t)&&(this.t.isIdentifier(t.params[0],{name:"View"})||this.t.isIdentifier(t.params[0],{name:"_View"}))))return null;let e=t.body;return this.t.isBlockStatement(e)?e:this.t.blockStatement([this.t.expressionStatement(e)])}parseProp(t){if(t&&!this.t.isExpression(t)&&b.throw3(),t=t,!t)return{value:this.t.booleanLiteral(!0),viewPropMap:{}};let e={};return this.traverse(this.valueWrapper(t),{ArrowFunctionExpression:i=>{let s=this.isPropView(i.node);if(!s)return;let r=this.uid();e[r]=this.parseView(s);let n=this.t.stringLiteral(r);i.node===t&&(t=n),i.replaceWith(n),i.skip()}}),{value:t,viewPropMap:e}}parseTag(t){let e={},i=t;for(;this.t.isMemberExpression(i?.callee)&&i?.callee?.object&&!this.isPureMemberExpression(i.callee);){let r=i.callee.property;if(!this.t.isIdentifier(r)||!this.t.isCallExpression(i.callee.object)){b.throw1();continue}let n=r.name,o=this.parseProp(i.arguments[0]);e[n]=o,i=i.callee.object}let s;if(i.arguments.length>0&&(s=this.parseProp(i.arguments[0])),this.t.isIdentifier(i.callee)){let r=i.callee.name;if(r===this.expressionTagName&&s){this.viewUnits.push({type:"exp",content:s,props:e});return}if(r===this.environmentTagName){if(Object.keys(e).length===0){b.warn1();return}this.viewUnits.push({type:"env",props:e,children:[]});return}if(this.htmlTags.includes(r)){let n=[];if(s){let o=!1;if(s.viewPropMap&&Object.keys(s.viewPropMap).length===1){let a=Object.keys(s.viewPropMap)[0];this.t.isStringLiteral(s.value,{value:a})&&(o=!0,n=s.viewPropMap[a])}o||(e.textContent=s)}this.viewUnits.push({type:"html",tag:this.t.stringLiteral(r),props:e,children:n});return}s&&(e._$content=s),this.viewUnits.push({type:"comp",tag:i.callee,props:e,children:[]});return}if(this.t.isMemberExpression(i.callee)&&this.t.isThisExpression(i.callee.object)&&this.t.isIdentifier(i.callee.property)&&this.snippetNames.includes(i.callee.property.name)){if(s&&(e.content=s),!(this.t.isMemberExpression(i.callee)&&this.t.isThisExpression(i.callee.object)&&this.t.isIdentifier(i.callee.property)))return b.throw4();this.viewUnits.push({type:"snippet",tag:i.callee.property.name,props:e,children:[]});return}if(this.t.isExpression(i.callee)){let[r,n]=this.alterTagType(i.callee);s&&(e[r==="html"?"textContent":"_$content"]=s),this.viewUnits.push({type:r,tag:n,props:e,children:[]})}}isPureMemberExpression(t){let e=!0;return this.traverse(this.valueWrapper(t),{CallExpression:()=>{e=!1}}),e}alterTagType(t){if(this.t.isCallExpression(t)&&this.t.isIdentifier(t.callee)){let e=t.callee.name,i=e===this.htmlTagWrapper?"html":e===this.compWrapper?"comp":void 0;if(i){let s=t.arguments[0];return this.t.isExpression(s)||b.throw2(e),[i,s]}}return["comp",t]}isInvalidExpression(t){return this.t.isForStatement(t)&&!this.t.isForOfStatement(t)?(b.error1(),!0):!1}valueWrapper(t){return this.t.file(this.t.program([this.t.expressionStatement(t)]))}parseView(t){return new X(this.config).parse(t)}uid(){return Math.random().toString(36).slice(2)}};function D(t,e){return new X(e).parse(t)}var q=class{htmlNamespace="html";htmlTagNamespace="tag";compTagNamespace="comp";envTagName="env";forTagName="for";ifTagName="if";elseIfTagName="else-if";elseTagName="else";customHTMLProps=["ref"];config;htmlTags;willParseTemplate;t;traverse;viewUnits=[];context;constructor(t,e={ifElseStack:[]}){this.config=t,this.t=t.babelApi.types,this.traverse=t.babelApi.traverse,this.htmlTags=t.htmlTags,this.willParseTemplate=t.parseTemplate??!0,this.context=e}parse(t){return this.t.isJSXText(t)?this.parseText(t):this.t.isJSXExpressionContainer(t)?this.parseExpression(t.expression):this.t.isJSXElement(t)?this.parseElement(t):this.t.isJSXFragment(t)&&t.children.forEach(e=>{this.parse(e)}),this.viewUnits}parseText(t){t.value.trim()&&this.viewUnits.push({type:"text",content:this.t.stringLiteral(t.value)})}parseExpression(t){if(!this.t.isJSXEmptyExpression(t)){if(this.t.isLiteral(t)&&!this.t.isTemplateLiteral(t)){this.viewUnits.push({type:"text",content:t});return}this.viewUnits.push({type:"exp",content:this.parseProp(t),props:{}})}}parseElement(t){let e,i,s=t.openingElement.name;if(this.t.isJSXIdentifier(s)){let h=s.name;if([this.ifTagName,this.elseIfTagName,this.elseTagName].includes(h))return this.parseIf(t);if(h===this.envTagName)return this.parseEnv(t);if(h===this.forTagName)return this.pareFor(t);this.htmlTags.includes(h)?(e="html",i=this.t.stringLiteral(h)):(e="comp",i=this.t.identifier(h))}else if(this.t.isJSXMemberExpression(s)){e="comp";let h=p=>this.t.isJSXMemberExpression(p.object)?this.t.memberExpression(h(p.object),this.t.identifier(p.property.name)):this.t.memberExpression(this.t.identifier(p.object.name),this.t.identifier(p.property.name));i=h(s)}else{let h=s.namespace.name;switch(h){case this.compTagNamespace:e="comp",i=this.t.identifier(s.name.name);break;case this.htmlNamespace:e="html",i=this.t.stringLiteral(s.name.name);break;case this.htmlTagNamespace:e="html",i=this.t.identifier(s.name.name);break;default:e="html",i=this.t.stringLiteral(`${h}:${s.name.name}`);break}}let r=t.openingElement.attributes,n=Object.fromEntries(r.map(h=>this.parseJSXProp(h))),o=t.children.map(h=>this.parseView(h)).flat(),a={type:e,tag:i,props:n,children:o};if(a.type==="html"&&o.length===1&&o[0].type==="text"){let h=o[0];a={...a,children:[],props:{...a.props,textContent:{value:h.content,viewPropMap:{}}}}}a.type==="html"&&(a=this.transformTemplate(a)),this.viewUnits.push(a)}parseEnv(t){let e=t.openingElement.attributes,i=Object.fromEntries(e.map(r=>this.parseJSXProp(r))),s=t.children.map(r=>this.parseView(r)).flat();this.viewUnits.push({type:"env",props:i,children:s})}parseIf(t){let e=t.openingElement.name.name;if(e===this.elseTagName){let r=this.context.ifElseStack[this.context.ifElseStack.length-1];if(!r||r.type!=="if")throw new Error(`Missing if for ${e}`);r.branches.push({condition:this.t.booleanLiteral(!0),children:t.children.map(n=>this.parseView(n)).flat()}),this.context.ifElseStack.pop();return}let i=t.openingElement.attributes.filter(r=>this.t.isJSXAttribute(r)&&r.name.name==="cond")[0];if(!i)throw new Error(`Missing condition for ${e}`);if(!this.t.isJSXAttribute(i))throw new Error(`JSXSpreadAttribute is not supported for ${e} condition`);if(!this.t.isJSXExpressionContainer(i.value)||!this.t.isExpression(i.value.expression))throw new Error(`Invalid condition for ${e}`);if(e===this.ifTagName){let r={type:"if",branches:[{condition:i.value.expression,children:t.children.map(n=>this.parseView(n)).flat()}]};this.viewUnits.push(r),this.context.ifElseStack.push(r);return}let s=this.context.ifElseStack[this.context.ifElseStack.length-1];if(!s||s.type!=="if")throw new Error(`Missing if for ${e}`);s.branches.push({condition:i.value.expression,children:t.children.map(r=>this.parseView(r)).flat()})}parseJSXProp(t){if(this.t.isJSXAttribute(t)){let e,i;this.t.isJSXNamespacedName(t.name)?(e=t.name.name.name,i=t.name.namespace.name):e=t.name.name;let s=this.t.isJSXExpressionContainer(t.value)?t.value.expression:t.value;return this.t.isJSXEmptyExpression(s)&&(s=void 0),[e,this.parseProp(s,i)]}return["*spread*",this.parseProp(t.argument)]}parseProp(t,e){if(!t)return{value:this.t.booleanLiteral(!0),viewPropMap:{}};let i={},s=r=>{let n=this.uid(),o=r.node;i[n]=this.parseView(o);let a=this.t.stringLiteral(n);o===t&&(t=a),r.replaceWith(a),r.skip()};return this.traverse(this.wrapWithFile(t),{JSXElement:s,JSXFragment:s}),{value:t,viewPropMap:i,specifier:e}}transformTemplate(t){return!this.willParseTemplate||!this.isHTMLTemplate(t)?t:(t=t,{type:"template",template:this.generateTemplate(t),mutableUnits:this.generateMutableUnits(t),props:this.parseTemplateProps(t)})}generateTemplate(t){let e=Object.fromEntries(this.filterTemplateProps(Object.entries(t.props??[]).filter(([,s])=>this.isStaticProp(s)&&!(this.t.isBooleanLiteral(s.value)&&!s.value.value)))),i=[];return t.children&&(i=t.children.map(s=>{if(s.type==="text")return s;if(s.type==="html"&&this.t.isStringLiteral(s.tag))return this.generateTemplate(s)}).filter(Boolean)),{type:"html",tag:t.tag,props:e,children:i}}generateMutableUnits(t){let e=[],i=(s,r=[])=>{let n=s.children?.filter(a=>a.type==="html"&&this.t.isStringLiteral(a.tag)||a.type==="text").length,o=-1;s.children?.forEach(a=>{if(!(a.type==="html"&&this.t.isStringLiteral(a.tag))&&a.type!=="text"){let h=o+1>=n?-1:o+1;e.push({path:[...r,h],...this.transformTemplate(a)})}else o++}),s.children?.filter(a=>a.type==="html"&&this.t.isStringLiteral(a.tag)).forEach((a,h)=>{i(a,[...r,h])})};return i(t),e}parseTemplateProps(t){let e=[],i=(s,r)=>{s.props&&Object.entries(s.props).filter(([,n])=>!this.isStaticProp(n)).forEach(([n,o])=>{e.push({tag:s.tag,name:s.tag.value,key:n,path:r,value:o.value})}),s.children?.filter(n=>n.type==="html"&&this.t.isStringLiteral(n.tag)).forEach((n,o)=>{i(n,[...r,o])})};return i(t,[]),e}isHTMLTemplate(t){return t.type==="html"&&this.t.isStringLiteral(t.tag)&&!!t.children?.some(e=>e.type==="html"&&this.t.isStringLiteral(e.tag))}isStaticProp(t){return this.t.isStringLiteral(t.value)||this.t.isNumericLiteral(t.value)||this.t.isBooleanLiteral(t.value)||this.t.isNullLiteral(t.value)}filterTemplateProps(t){return t.filter(([e])=>!e.startsWith("on")).filter(([e])=>!this.customHTMLProps.includes(e))}parseView(t){return new q({...this.config,parseTemplate:!1},this.context).parse(t)}wrapWithFile(t){return this.t.file(this.t.program([this.t.expressionStatement(t)]))}uid(){return Math.random().toString(36).slice(2)}findProp(t,e){return t.openingElement.attributes.find(s=>this.t.isJSXAttribute(s)&&s.name.name===e)}pareFor(t){let e=this.findProp(t,"each"),i=this.findProp(t,"key");if(!e)throw new Error("should clarify each prop for if");let s;if(!e.value.type!=="JSXExpressionContainer")throw new Error("each prop should be an expression");s=e.value.expression,i||console.warn("should clarify key prop for for, to improve performance");let r=left.declarations[0].id,n=t.body,o;if(this.t.isExpressionStatement(n))o=[n];else if(this.t.isBlockStatement(n)){let l=n.body;if(l.length===0)return DLError.error5();let d=l[0];if(this.t.isLabeledStatement(d)&&this.t.isIdentifier(d.label)){if(d.label.name!=="key"||!this.t.isExpressionStatement(d.body))DLError.error4();else{let c=d.body.expression;this.t.isExpression(c)&&!(this.t.isNullLiteral(c)||this.t.isIdentifier(c)&&c.name==="undefined")&&(key=c)}o=l.slice(1)}else o=l}else return;let a=o.filter(l=>this.t.isDirective(l)),h=o.filter(l=>!this.t.isDirective(l)),p=t.children.map(l=>this.parseView(l)).flat();this.viewUnits.push({type:"for",item:r,array:array.value,key,children:p})}};function K(t,e){return new q(e).parse(t)}var St=Object.defineProperty,wt=(t,e,i)=>e in t?St(t,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[e]=i,Mt=(t,e,i)=>(wt(t,typeof e!="symbol"?e+"":e,i),i),Nt=E("ReactivityParser",{1:"Invalid ViewUnit type"}),A=class{config;t;traverse;availableProperties;availableIdentifiers;dependencyMap;identifierDepMap;dependencyParseType;reactivityFuncNames;escapeNamings=["escape","$"];usedProperties=new Set;constructor(t){this.config=t,this.t=t.babelApi.types,this.traverse=t.babelApi.traverse,this.availableProperties=t.availableProperties,this.availableIdentifiers=t.availableIdentifiers,this.dependencyMap=t.dependencyMap,this.identifierDepMap=t.identifierDepMap??{},this.dependencyParseType=t.dependencyParseType??"property",this.reactivityFuncNames=t.reactivityFuncNames??[]}parse(t){return this.parseViewUnit(t)}parseViewUnit(t){return this.isHTMLTemplate(t)?this.parseTemplate(t):t.type==="text"?this.parseText(t):t.type==="html"?this.parseHTML(t):t.type==="comp"?this.parseComp(t):t.type==="for"?this.parseFor(t):t.type==="try"?this.parseTry(t):t.type==="if"?this.parseIf(t):t.type==="env"?this.parseEnv(t):t.type==="exp"?this.parseExp(t):t.type==="switch"?this.parseSwitch(t):t.type==="snippet"?this.parseSnippet(t):Nt.throw1()}parseTemplate(t){return{type:"template",template:this.generateTemplate(t),props:this.parseTemplateProps(t),mutableParticles:this.generateMutableParticles(t)}}generateTemplate(t){let e=this.filterTemplateProps(Object.entries(t.props).filter(([,s])=>this.isStaticProp(s)&&!(this.t.isBooleanLiteral(s.value)&&!s.value.value))).map(([s,r])=>[s,{...r,dependencyIndexArr:[],dependenciesNode:this.t.arrayExpression([]),dynamic:!1}]),i=[];return t.props.textContent||(i=t.children.map(s=>{if(s.type==="html"&&this.t.isStringLiteral(s.tag))return this.generateTemplate(s);if(s.type==="text"&&this.t.isStringLiteral(s.content))return this.parseText(s)}).filter(Boolean)),{type:"html",tag:t.tag,props:Object.fromEntries(e),children:i}}generateMutableParticles(t){let e=[],i=(s,r=[])=>{s.children?.forEach((n,o)=>{!(n.type==="html"&&this.t.isStringLiteral(n.tag))&&!(n.type==="text"&&this.t.isStringLiteral(n.content))&&e.push({path:[...r,o],...this.parseViewParticle(n)})}),s.children?.filter(n=>n.type==="html"&&this.t.isStringLiteral(n.tag)).forEach((n,o)=>{i(n,[...r,o])})};return i(t),e}parseTemplateProps(t){let e=[],i=(s,r)=>{Object.entries(s.props).filter(([,n])=>!this.isStaticProp(n)).forEach(([n,o])=>{e.push({tag:s.tag.value,key:n,path:r,value:o.value,...this.getDependencies(o.value)})}),s.children.filter(n=>n.type==="html"&&this.t.isStringLiteral(n.tag)||n.type==="text"&&this.t.isStringLiteral(n.content)).forEach((n,o)=>{n.type==="html"?i(n,[...r,o]):n.type==="text"&&e.push({tag:"text",key:"value",path:[...r,o],value:n.content,dependencyIndexArr:[],dependenciesNode:this.t.arrayExpression([]),dynamic:!1})})};return i(t,[]),e}parseText(t){return{type:"text",content:{value:t.content,...this.getDependencies(t.content)}}}parseHTML(t){let{dependencyIndexArr:e,dependenciesNode:i,dynamic:s}=this.getDependencies(t.tag),r={type:"html",tag:t.tag,props:{},children:[]};if(r.props=Object.fromEntries(Object.entries(t.props).map(([o,a])=>[o,this.generateDependencyProp(a)])),r.children=t.children.map(this.parseViewParticle.bind(this)),!s)return r;let n=this.uid();return{type:"exp",content:{value:this.t.stringLiteral(n),viewPropMap:{[n]:[r]},dependencyIndexArr:e,dependenciesNode:i,dynamic:s},props:{}}}parseComp(t){let{dependencyIndexArr:e,dependenciesNode:i,dynamic:s}=this.getDependencies(t.tag),r={type:"comp",tag:t.tag,props:{},children:[]};if(r.props=Object.fromEntries(Object.entries(t.props).map(([o,a])=>[o,this.generateDependencyProp(a)])),r.children=t.children.map(this.parseViewParticle.bind(this)),!s)return r;let n=this.uid();return{type:"exp",content:{value:this.t.stringLiteral(n),viewPropMap:{[n]:[r]},dependencyIndexArr:e,dependenciesNode:i,dynamic:s},props:{}}}parseFor(t){let{dependencyIndexArr:e,dependenciesNode:i,dynamic:s}=this.getDependencies(t.array),r=this.config.identifierDepMap,n=this.t.isIdentifier(t.key)&&t.key.name;this.config.identifierDepMap=Object.fromEntries(this.getIdentifiers(this.t.assignmentExpression("=",t.item,this.t.objectExpression([]))).filter(a=>!n||a!==n).map(a=>[a,e.map(h=>this.availableProperties[h])]));let o={type:"for",item:t.item,array:{value:t.array,dynamic:s,dependencyIndexArr:e,dependenciesNode:i},children:t.children.map(this.parseViewParticle.bind(this)),key:t.key};return this.config.identifierDepMap=r,o}parseIf(t){return{type:"if",branches:t.branches.map(e=>({condition:{value:e.condition,...this.getDependencies(e.condition)},children:e.children.map(this.parseViewParticle.bind(this))}))}}parseSwitch(t){return{type:"switch",discriminant:{value:t.discriminant,...this.getDependencies(t.discriminant)},branches:t.branches.map(e=>({case:{value:e.case,...this.getDependencies(e.case)},children:e.children.map(this.parseViewParticle.bind(this)),break:e.break}))}}parseTry(t){return{type:"try",children:t.children.map(this.parseViewParticle.bind(this)),exception:t.exception,catchChildren:t.catchChildren.map(this.parseViewParticle.bind(this))}}parseEnv(t){return{type:"env",props:Object.fromEntries(Object.entries(t.props).map(([e,i])=>[e,this.generateDependencyProp(i)])),children:t.children.map(this.parseViewParticle.bind(this))}}parseExp(t){return{type:"exp",content:this.generateDependencyProp(t.content),props:Object.fromEntries(Object.entries(t.props).map(([e,i])=>[e,this.generateDependencyProp(i)]))}}parseSnippet(t){let e={type:"snippet",tag:t.tag,props:{},children:[]};return t.props&&(e.props=Object.fromEntries(Object.entries(t.props).map(([i,s])=>[i,this.generateDependencyProp(s)]))),t.children&&(e.children=t.children.map(this.parseViewParticle.bind(this))),e}generateDependencyProp(t){return{value:t.value,...this.getDependencies(t.value),viewPropMap:Object.fromEntries(Object.entries(t.viewPropMap).map(([e,i])=>[e,i.map(this.parseViewParticle.bind(this))]))}}getDependencies(t){if(this.t.isFunctionExpression(t)||this.t.isArrowFunctionExpression(t))return{dynamic:!1,dependencyIndexArr:[],dependenciesNode:this.t.arrayExpression([])};let[e,i]=this.getIdentifierDependencies(t),[s,r]=this.getPropertyDependencies(t),n=this.dependencyParseType==="identifier"?e:s,o=this.getIdentifierMapDependencies(t),a=[...new Set([...n,...o])],h=[...i,...r];return{dynamic:h.length>0||a.length>0,dependencyIndexArr:a,dependenciesNode:this.t.arrayExpression(h)}}getIdentifierDependencies(t){let e=this.availableIdentifiers??this.availableProperties,i=new Set,s=new Set,r={},n=this.valueWrapper(t);this.traverse(n,{Identifier:a=>{let h=a.node.name;e.includes(h)&&(this.isAssignmentExpressionLeft(a)||this.isAssignmentFunction(a)?s.add(h):this.isStandAloneIdentifier(a)&&!this.isMemberInEscapeFunction(a)&&!this.isMemberInManualFunction(a)&&(i.add(h),this.dependencyMap[h]?.forEach(i.add.bind(i)),r[h]||(r[h]=[]),r[h].push(this.geneDependencyNode(a))))}}),s.forEach(a=>{i.delete(a),delete r[a]});let o=Object.values(r).flat();return o=o.filter((a,h)=>o.findIndex(p=>this.t.isNodesEquivalent(p,a))===h),i.forEach(this.usedProperties.add.bind(this.usedProperties)),[[...i].map(a=>this.availableProperties.indexOf(a)),o]}getPropertyDependencies(t){let e=new Set,i=new Set,s={},r=this.valueWrapper(t);this.traverse(r,{MemberExpression:o=>{if(!this.t.isIdentifier(o.node.property)||!this.t.isThisExpression(o.node.object))return;let a=o.node.property.name;this.isAssignmentExpressionLeft(o)||this.isAssignmentFunction(o)?i.add(a):this.availableProperties.includes(a)&&!this.isMemberInEscapeFunction(o)&&!this.isMemberInManualFunction(o)&&(e.add(a),this.dependencyMap[a]?.forEach(e.add.bind(e)),s[a]||(s[a]=[]),s[a].push(this.geneDependencyNode(o)))}}),i.forEach(o=>{e.delete(o),delete s[o]});let n=Object.values(s).flat();return n=n.filter((o,a)=>n.findIndex(h=>this.t.isNodesEquivalent(h,o))===a),e.forEach(this.usedProperties.add.bind(this.usedProperties)),[[...e].map(o=>this.availableProperties.indexOf(o)),n]}geneDependencyNode(t){let e=t;for(;e?.parentPath;){let s=e.parentPath;if(!(this.t.isMemberExpression(s.node,{computed:!1})||this.t.isOptionalMemberExpression(s.node)))break;e=s}let i=this.t.cloneNode(e.node);return this.traverse(this.valueWrapper(i),{MemberExpression:s=>{this.t.isThisExpression(s.node.object)||(s.node.optional=!0,s.node.type="OptionalMemberExpression")}}),i}getIdentifierMapDependencies(t){let e=new Set,i=this.valueWrapper(t);return this.traverse(i,{Identifier:s=>{let r=s.node.name;if(this.isAttrFromFunction(s,r))return;let n=this.identifierDepMap[r];!n||!Array.isArray(n)||this.isMemberInEscapeFunction(s)||this.isMemberInManualFunction(s)||n.forEach(e.add.bind(e))}}),e.forEach(this.usedProperties.add.bind(this.usedProperties)),[...e].map(s=>this.availableProperties.indexOf(s))}parseViewParticle(t){let e=new A(this.config),i=e.parse(t);return e.usedProperties.forEach(this.usedProperties.add.bind(this.usedProperties)),i}isHTMLTemplate(t){return t.type==="html"&&this.t.isStringLiteral(t.tag)&&!!t.children?.some(e=>e.type==="html"&&this.t.isStringLiteral(e.tag))}isStaticProp(t){let{value:e,viewPropMap:i}=t;return(!i||Object.keys(i).length===0)&&(this.t.isStringLiteral(e)||this.t.isNumericLiteral(e)||this.t.isBooleanLiteral(e))}filterTemplateProps(t){return t.filter(([e])=>!e.startsWith("on")).filter(([e])=>!A.customHTMLProps.includes(e))}valueWrapper(t){return this.t.file(this.t.program([this.t.isStatement(t)?t:this.t.expressionStatement(t)]))}isStandAloneIdentifier(t){let e=t.node,i=t.parentPath?.node;if(this.t.isMemberExpression(i)&&i.property===e||this.isAttrFromFunction(t,e.name))return!1;for(;t.parentPath;){if(this.t.isVariableDeclarator(t.parentPath.node)||this.t.isObjectProperty(t.parentPath.node)&&t.parentPath.node.key===t.node&&!t.parentPath.node.computed)return!1;t=t.parentPath}return!0}getIdentifiers(t){if(this.t.isIdentifier(t))return[t.name];let e=new Set;return this.traverse(this.valueWrapper(t),{Identifier:i=>{this.isStandAloneIdentifier(i)&&e.add(i.node.name)}}),[...e]}isAttrFromFunction(t,e){let i=t.parentPath,s=r=>this.t.isIdentifier(r)?r.name===e:this.t.isAssignmentPattern(r)?s(r.left):this.t.isArrayPattern(r)?r.elements.filter(Boolean).map(n=>s(n)).includes(!0):this.t.isObjectPattern(r)?r.properties.filter(n=>this.t.isObjectProperty(n)&&this.t.isIdentifier(n.key)).map(n=>n.key.name).includes(e):this.t.isRestElement(r)?s(r.argument):!1;for(;i;){let r=i.node;if(this.t.isArrowFunctionExpression(r)||this.t.isFunctionDeclaration(r)){for(let n of r.params)if(s(n))return!0}i=i.parentPath}return!1}isAssignmentExpressionLeft(t){let e=t.parentPath;for(;e&&!this.t.isStatement(e.node);){if(this.t.isAssignmentExpression(e.node)){if(e.node.left===t.node)return!0;let i=e.get("left");if(t.isDescendant(i))return!0}else if(this.t.isUpdateExpression(e.node))return!0;e=e.parentPath}return!1}isAssignmentFunction(t){let e=t.parentPath;for(;e&&this.t.isMemberExpression(e.node);)e=e.parentPath;return e?this.t.isCallExpression(e.node)&&this.t.isMemberExpression(e.node.callee)&&this.t.isIdentifier(e.node.callee.property)&&this.reactivityFuncNames.includes(e.node.callee.property.name):!1}isMemberInEscapeFunction(t){let e=!1,i=t.parentPath;for(;i;){let s=i.node;if(this.t.isCallExpression(s)&&this.t.isIdentifier(s.callee)&&this.escapeNamings.includes(s.callee.name)){e=!0;break}i=i.parentPath}return e}isMemberInManualFunction(t){let e=!1,i=t.parentPath;for(;i;){let s=i.node,r=i.parentPath?.node,n=this.t.isCallExpression(r)&&this.t.isIdentifier(r.callee)&&r.callee.name==="manual",o=this.t.isCallExpression(r)&&r.arguments[0]===s;if(n&&o){e=!0;break}i=i.parentPath}return e}uid(){return Math.random().toString(36).slice(2)}},z=A;Mt(z,"customHTMLProps",["didUpdate","willMount","didMount","willUnmount","didUnmount","element","innerHTML","props","attrs","dataset","forwardProps"]);function M(t,e){let i=new Set;return[t.map(s=>{let r=new z(e),n=r.parse(s);return r.usedProperties.forEach(i.add.bind(i)),n}),i]}var It=Object.defineProperty,Ct=(t,e,i)=>e in t?It(t,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[e]=i,I=(t,e,i)=>(Ct(t,typeof e!="symbol"?e+"":e,i),i),N={template:"$t",node:"$node"},g=class{viewParticle;config;t;traverse;className;importMap;snippetPropMap;elementAttributeMap;alterAttributeMap;viewGenerator;constructor(t,e){this.viewParticle=t,this.config=e,this.t=e.babelApi.types,this.traverse=e.babelApi.traverse,this.className=e.className,this.importMap=e.importMap,this.snippetPropMap=e.snippetPropMap,this.viewGenerator=new C(e),this.elementAttributeMap=e.attributeMap?Object.entries(e.attributeMap).reduce((i,[s,r])=>(r.forEach(n=>{i[n]||(i[n]=[]),i[n].push(s)}),i),{}):{},this.alterAttributeMap=e.alterAttributeMap}initStatements=[];addInitStatement(...t){this.initStatements.push(...t.filter(Boolean))}classProperties=[];addStaticClassProperty(t,e){this.classProperties.push(this.t.classProperty(this.t.identifier(t),e,void 0,void 0,void 0,!0))}updateStatements={};addUpdateStatements(t,e){if(!t||t.length===0)return;let i=g.calcDependencyNum(t);this.updateStatements[i]||(this.updateStatements[i]=[]),e&&this.updateStatements[i].push(e)}addUpdateStatementsWithoutDep(t){this.updateStatements[0]||(this.updateStatements[0]=[]),this.updateStatements[0].push(t)}generate(){let t=this.run();return[this.initStatements,this.updateStatements,this.classProperties,t]}generateChildren(t,e=!0,i=!1){this.viewGenerator.nodeIdx=i?-1:this.nodeIdx,this.viewGenerator.templateIdx=this.templateIdx;let[s,r,n,o]=this.viewGenerator.generateChildren(t);return i||(this.nodeIdx=this.viewGenerator.nodeIdx),this.templateIdx=this.viewGenerator.templateIdx,this.classProperties.push(...n),e&&this.mergeStatements(r),[s,o,r,this.viewGenerator.nodeIdx]}mergeStatements(t){Object.entries(t).forEach(([e,i])=>{this.updateStatements[Number(e)]||(this.updateStatements[Number(e)]=[]),this.updateStatements[Number(e)].push(...i)})}generateChild(t,e=!0,i=!1){this.viewGenerator.nodeIdx=i?-1:this.nodeIdx,this.viewGenerator.templateIdx=this.templateIdx;let[s,r,n,o]=this.viewGenerator.generateChild(t);return i||(this.nodeIdx=this.viewGenerator.nodeIdx),this.templateIdx=this.viewGenerator.templateIdx,this.classProperties.push(...n),e&&this.mergeStatements(r),[s,o,r,this.viewGenerator.nodeIdx]}geneUpdateFunc(t){return this.t.variableDeclaration("const",[this.t.variableDeclarator(this.t.identifier("$update"),this.t.arrowFunctionExpression([this.t.identifier("$changed")],this.geneUpdateBody(t)))])}get updateParams(){return[this.t.identifier("$changed")]}geneUpdateBody(t){return this.t.blockStatement([...Object.entries(t).filter(([e])=>e!=="0").map(([e,i])=>this.t.ifStatement(this.t.binaryExpression("&",this.t.identifier("$changed"),this.t.numericLiteral(Number(e))),this.t.blockStatement(i))),...t[0]??[]])}declareNodes(t){return t===-1?[]:[this.t.variableDeclaration("let",Array.from({length:t+1},(e,i)=>this.t.variableDeclarator(this.t.identifier(`${N.node}${i}`))))]}generateReturnStatement(t){return this.t.returnStatement(this.t.arrayExpression(t.map(e=>this.t.identifier(e))))}run(){return""}nodeIdx=-1;generateNodeName(t){return`${N.node}${t??++this.nodeIdx}`}templateIdx=-1;generateTemplateName(){return`${N.template}${++this.templateIdx}`}static calcDependencyNum(t){return!t||t.length===0?0:(t=[...new Set(t)],t.reduce((e,i)=>e+(1<{i.forEach(s=>{Array.isArray(s)?t.push(...s):t.push(s)})}]}},Y=class extends g{addOnUpdate(t,e){return this.t.expressionStatement(this.t.logicalExpression("&&",this.t.identifier(t),this.t.callExpression(e,[this.t.identifier(t),...this.updateParams.slice(1)])))}addLifecycle(t,e,i){return e==="willMount"?this.addWillMount(t,i):this.addOtherLifecycle(t,i,e)}addWillMount(t,e){return this.t.expressionStatement(this.t.callExpression(e,[this.t.identifier(t)]))}addOtherLifecycle(t,e,i){return this.t.expressionStatement(this.t.callExpression(this.t.memberExpression(this.t.identifier("View"),this.t.identifier(`add${i[0].toUpperCase()}${i.slice(1)}`)),[this.t.identifier(t),e]))}};I(Y,"lifecycle",["willMount","didMount","willUnmount","didUnmount"]);var O=class extends Y{alterPropViews(t){return t&&Object.fromEntries(Object.entries(t).map(([e,i])=>[e,this.alterPropView(i)]))}declarePropView(t){let[e,i,s,r]=this.generateChildren(t,!1,!0);Object.keys(s).length>0&&e.unshift(this.t.expressionStatement(this.t.callExpression(this.t.identifier("$addUpdate"),[this.t.arrowFunctionExpression(this.updateParams,this.geneUpdateBody(s))]))),e.unshift(...this.declareNodes(r)),e.push(this.generateReturnStatement(i));let n=this.generateNodeName(),o=this.t.expressionStatement(this.t.assignmentExpression("=",this.t.identifier(n),this.t.newExpression(this.t.identifier(this.importMap.PropView),[this.t.arrowFunctionExpression([this.t.identifier("$addUpdate")],this.t.blockStatement(e))])));this.addInitStatement(o);let a=this.t.identifier(n);return this.addUpdateStatementsWithoutDep(this.optionalExpression(n,this.t.callExpression(this.t.memberExpression(a,this.t.identifier("update")),this.updateParams))),n}alterPropView(t){if(!t)return t;let{value:e,viewPropMap:i}=t;if(!i)return{...t,value:e};let s=e;return this.traverse(this.valueWrapper(e),{StringLiteral:r=>{let n=r.node.value,o=i[n];if(!o)return;let a=this.t.identifier(this.declarePropView(o));e===r.node&&(s=a),r.replaceWith(a),r.skip()}}),{...t,value:s}}static reverseDependencyIndexArr(t){let e=Object.keys(t).map(Number).reduce((s,r)=>s|r,0),i=[];for(let s=0;s{if(n!=="forwardProps"&&n!=="didUpdate"){if(r.push(...a),j.lifecycle.includes(n)){this.addInitStatement(this.addLifecycle(s,n,o));return}if(n==="ref"){this.addInitStatement(this.initElement(s,o));return}if(n==="elements"){this.addInitStatement(this.initElement(s,o,!0));return}if(n==="_$content"){this.addUpdateStatements(a,this.setCompContent(s,o,h));return}if(n==="props"){this.addUpdateStatements(a,this.setCompProps(s,o,h));return}this.addUpdateStatements(a,this.setCompProp(s,n,o,h))}}),t.didUpdate&&this.addUpdateStatements(r,this.addOnUpdate(s,t.didUpdate.value)),s}generateCompProps(t){return Object.keys(t).length===0?this.t.nullLiteral():this.t.arrayExpression(Object.entries(t).map(([e,{value:i,dependenciesNode:s}])=>this.t.arrayExpression([this.t.stringLiteral(e),i,s])))}declareCompNode(t,e,i,s){let r="forwardProps"in i,n=Object.fromEntries(Object.entries(i).filter(([a])=>!["ref","elements","forwardProps","_$content","didUpdate","props",...j.lifecycle].includes(a))),o=i._$content;return[this.t.expressionStatement(this.t.assignmentExpression("=",this.t.identifier(t),this.t.newExpression(e,[]))),this.t.expressionStatement(this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("_$init")),[this.generateCompProps(n),o?this.t.arrayExpression([o.value,o.dependenciesNode]):this.t.nullLiteral(),s.length>0?this.t.identifier(this.declarePropView(s)):this.t.nullLiteral(),r?this.t.identifier("this"):this.t.nullLiteral()]))]}setCompContent(t,e,i){return this.optionalExpression(t,this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("_$setContent")),[this.t.arrowFunctionExpression([],e),i]))}setCompProp(t,e,i,s){return this.optionalExpression(t,this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("_$setProp")),[this.t.stringLiteral(e),this.t.arrowFunctionExpression([],i),s]))}setCompProps(t,e,i){return this.optionalExpression(t,this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("_$setProps")),[this.t.arrowFunctionExpression([],e),i]))}},tt=E("ViewGenerator",{1:"Element prop in HTML should be a function or an identifier",2:"Unrecognized HTML common prop",3:"Do prop only accepts function or arrow function"},{},{1:"ExpressionNode only supports prop as element and lifecycle, receiving $0"}),y=class extends Z{addHTMLProp(t,e,i,s,r,n,o){return r?(this.addUpdateStatements(n,this.setDynamicHTMLProp(t,e,i,s,o,!0)),this.setDynamicHTMLProp(t,e,i,s,o,!1)):this.setStaticHTMLProp(t,e,i,s)}insertNode(t,e,i){return this.t.expressionStatement(this.t.callExpression(this.t.identifier(this.importMap.insertNode),[this.t.identifier(t),this.t.identifier(e),this.t.numericLiteral(i)]))}setPropWithCheck(t,e,i){return i?this.optionalExpression(t,e):this.t.expressionStatement(e)}setHTMLStyle(t,e,i){return this.setPropWithCheck(t,this.t.callExpression(this.t.identifier(this.importMap.setStyle),[this.t.identifier(t),e]),i)}setHTMLDataset(t,e,i){return this.setPropWithCheck(t,this.t.callExpression(this.t.identifier(this.importMap.setDataset),[this.t.identifier(t),e]),i)}setHTMLProp(t,e,i){return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.memberExpression(this.t.identifier(t),this.t.identifier(e)),i))}setHTMLAttr(t,e,i){return this.t.expressionStatement(this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("setAttribute")),[this.t.stringLiteral(e),i]))}setHTMLEvent(t,e,i){return this.t.expressionStatement(this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("addEventListener")),[this.t.stringLiteral(e),i]))}setEvent(t,e,i,s){return this.setPropWithCheck(t,this.t.callExpression(this.t.identifier(this.importMap.setEvent),[this.t.identifier(t),this.t.stringLiteral(e),i]),s)}delegateEvent(t,e,i,s){return this.setPropWithCheck(t,this.t.callExpression(this.t.identifier(this.importMap.delegateEvent),[this.t.identifier(t),this.t.stringLiteral(e),i]),s)}setCachedProp(t,e,i,s,r){return this.setPropWithCheck(t,this.t.callExpression(this.t.identifier(this.importMap.setHTMLProp),[this.t.identifier(t),this.t.stringLiteral(e),this.t.arrowFunctionExpression([],i),s]),r)}setCachedAttr(t,e,i,s,r){return this.setPropWithCheck(t,this.t.callExpression(this.t.identifier(this.importMap.setHTMLAttr),[this.t.identifier(t),this.t.stringLiteral(e),this.t.arrowFunctionExpression([],i),s]),r)}setHTMLPropObject(t,e,i){return this.setPropWithCheck(t,this.t.callExpression(this.t.identifier(this.importMap.setHTMLProps),[this.t.identifier(t),e]),i)}setHTMLAttrObject(t,e,i){return this.setPropWithCheck(t,this.t.callExpression(this.t.identifier(this.importMap.setHTMLAttrs),[this.t.identifier(t),e]),i)}addCommonHTMLProp(t,e,i,s){return y.lifecycle.includes(e)?s?null:this.addLifecycle(t,e,i):e==="ref"?s?null:this.initElement(t,i):e==="style"?this.setHTMLStyle(t,i,s):e==="dataset"?this.setHTMLDataset(t,i,s):e==="props"?this.setHTMLPropObject(t,i,s):e==="attrs"?this.setHTMLAttrObject(t,i,s):e==="forwardProps"?this.forwardProps(t):tt.throw2()}setStaticHTMLProp(t,e,i,s){if(y.commonHTMLPropKeys.includes(i))return this.addCommonHTMLProp(t,i,s,!1);if(i.startsWith("on")){let r=i.slice(2).toLowerCase();return y.DelegatedEvents.has(r)?this.delegateEvent(t,r,s,!1):this.setHTMLEvent(t,r,s)}return this.isInternalAttribute(e,i)?(i==="class"?i="className":i==="for"&&(i="htmlFor"),this.setHTMLProp(t,i,s)):this.setHTMLAttr(t,i,s)}setDynamicHTMLProp(t,e,i,s,r,n){if(y.commonHTMLPropKeys.includes(i))return this.addCommonHTMLProp(t,i,s,n);if(i.startsWith("on")){let o=i.slice(2).toLowerCase();return y.DelegatedEvents.has(o)?this.delegateEvent(t,o,s,n):this.setEvent(t,o,s,n)}return this.alterAttributeMap[i]&&(i=this.alterAttributeMap[i]),this.isInternalAttribute(e,i)?this.setCachedProp(t,i,s,r,n):this.setCachedAttr(t,i,s,r,n)}isInternalAttribute(t,e){return this.elementAttributeMap["*"]?.includes(e)||this.elementAttributeMap[t]?.includes(e)}},P=y;I(P,"DelegatedEvents",new Set(["beforeinput","click","dblclick","contextmenu","focusin","focusout","input","keydown","keyup","mousedown","mousemove","mouseout","mouseover","mouseup","pointerdown","pointermove","pointerout","pointerover","pointerup","touchend","touchmove","touchstart"]));I(P,"commonHTMLPropKeys",["style","dataset","props","ref","attrs","forwardProps",...y.lifecycle]);var Lt=class extends P{run(){let{tag:t,props:e,children:i}=this.viewParticle,s=this.generateNodeName();this.addInitStatement(this.declareHTMLNode(s,t));let r=this.t.isStringLiteral(t)?t.value:"ANY",n=[];Object.entries(e).forEach(([h,{value:p,dependencyIndexArr:l,dependenciesNode:d,dynamic:c}])=>{h!=="didUpdate"&&(n.push(...l??[]),this.addInitStatement(this.addHTMLProp(s,r,h,p,c,l,d)))}),e.didUpdate&&this.addUpdateStatements(n,this.addOnUpdate(s,e.didUpdate.value));let o=[],a=!1;return i.forEach((h,p)=>{let[l,d]=this.generateChild(h);o.push(d),this.addInitStatement(...l),h.type==="html"?this.addInitStatement(this.appendChild(s,d)):(a=!0,this.addInitStatement(this.insertNode(s,d,p)))}),a&&this.addInitStatement(this.setHTMLNodes(s,o)),s}declareHTMLNode(t,e){return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.identifier(t),this.t.callExpression(this.t.identifier(this.importMap.createElement),[e])))}setHTMLNodes(t,e){return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.memberExpression(this.t.identifier(t),this.t.identifier("_$nodes")),this.t.arrayExpression(e.map(i=>this.t.identifier(i)))))}appendChild(t,e){return this.t.expressionStatement(this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("appendChild")),[this.t.identifier(e)]))}},F=class extends P{run(){let{template:t,mutableParticles:e,props:i}=this.viewParticle,s=this.generateNodeName(),r=this.addTemplate(t);this.addInitStatement(this.declareTemplateNode(s,r));let n=[];i.forEach(({path:p})=>{n.push(p)}),e.forEach(({path:p})=>{n.push(p.slice(0,-1))});let[o,a]=this.insertElements(n,s);this.addInitStatement(...o);let h={};return i.forEach(({tag:p,path:l,key:d,value:c,dependencyIndexArr:m,dependenciesNode:u,dynamic:f})=>{let x=a[l.join(".")];if(h[x]||(h[x]={deps:[]}),d==="didUpdate"){h[x].value=c;return}h[x].deps.push(...m),this.addInitStatement(this.addHTMLProp(x,p,d,c,f,m,u))}),Object.entries(h).forEach(([p,{deps:l,value:d}])=>{d&&this.addUpdateStatements(l,this.addOnUpdate(p,d))}),e.forEach(p=>{let l=p.path,d=a[l.slice(0,-1).join(".")],[c,m]=this.generateChild(p);this.addInitStatement(...c),this.addInitStatement(this.insertNode(d,m,l[l.length-1]))}),s}addTemplate(t){let e=this.generateTemplateName(),[i,s,,r]=this.generateChild(t,!1,!0);return this.addStaticClassProperty(e,this.t.callExpression(this.t.arrowFunctionExpression([],this.t.blockStatement([...this.declareNodes(r),...i,this.t.returnStatement(this.t.identifier(s))])),[])),e}declareTemplateNode(t,e){return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.identifier(t),this.t.callExpression(this.t.memberExpression(this.t.memberExpression(this.t.identifier(this.className),this.t.identifier(e)),this.t.identifier("cloneNode")),[this.t.booleanLiteral(!0)])))}insertElement(t,e,i){let s=this.generateNodeName();if(e.length===0)return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.identifier(s),Array.from({length:i}).reduce(p=>this.t.memberExpression(p,this.t.identifier("nextSibling")),this.t.identifier(t))));let r=p=>this.t.memberExpression(p,this.t.identifier("firstChild")),n=p=>this.t.memberExpression(r(p),this.t.identifier("nextSibling")),o=p=>this.t.memberExpression(n(p),this.t.identifier("nextSibling")),a=(p,l)=>this.t.memberExpression(this.t.memberExpression(p,this.t.identifier("childNodes")),this.t.numericLiteral(l),!0),h=p=>this.t.memberExpression(p,this.t.identifier("nextSibling"));return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.identifier(s),e.reduce((p,l,d)=>{if(d===0&&i>0)for(let c=0;c{let h=F.findBestNodeAndPath(r,a,e),[,p,l]=h,d=h[0];(p.length!==0||l!==0)&&(s(this.insertElement(d,p,l)),d=this.generateNodeName(this.nodeIdx),r[d]=a)});let o=Object.fromEntries(Object.entries(r).map(([a,h])=>[h.join("."),a]));return[i,o]}static pathWithCommonPrefix(t){let e=[...t];t.forEach(r=>{t.forEach(n=>{if(r!==n){for(let o=0;or.length!==n.length?r.length-n.length:r[0]-n[0]);return[...new Set(i.map(r=>r.join(".")))].map(r=>r.split(".").filter(Boolean).map(Number))}static findBestNodeAndPath(t,e,i){let s=0,r,n;return Object.entries(t).forEach(([o,a])=>{let h=0,p=a.length;for(let l=0;l0&&l<=3&&(n=[o,h,l])}h===a.length&&h>s&&(r=o,s=h)}),r?[r,e.slice(s),0]:n?[n[0],e.slice(n[1]+1),n[2]]:[i,e,0]}},Tt=class extends g{run(){let{item:t,array:e,key:i,children:s}=this.viewParticle,r=this.generateNodeName();return this.addInitStatement(this.declareForNode(r,e.value,t,s,g.calcDependencyNum(e.dependencyIndexArr),i)),this.addUpdateStatements(e.dependencyIndexArr,this.updateForNode(r,e.value,t,i)),this.addUpdateStatementsWithoutDep(this.updateForNodeItem(r)),r}declareForNode(t,e,i,s,r,n){let[o,a,h,p]=this.generateChildren(s,!1,!0);return o.unshift(...this.declareNodes(p),this.t.expressionStatement(this.t.assignmentExpression("=",this.t.memberExpression(this.t.identifier("$updateArr"),this.t.identifier("$idx"),!0),this.t.arrowFunctionExpression([...this.updateParams,this.t.identifier("$item")],this.t.blockStatement([this.t.expressionStatement(this.t.assignmentExpression("=",i,this.t.identifier("$item"))),...this.geneUpdateBody(h).body]))))),o.push(this.generateReturnStatement(a)),this.t.expressionStatement(this.t.assignmentExpression("=",this.t.identifier(t),this.t.newExpression(this.t.identifier(this.importMap.ForNode),[e,this.t.numericLiteral(r),this.getForKeyStatement(e,i,n),this.t.arrowFunctionExpression([i,this.t.identifier("$updateArr"),this.t.identifier("$idx")],this.t.blockStatement(o))])))}getForKeyStatement(t,e,i){return this.t.isNullLiteral(i)?i:this.t.callExpression(this.t.memberExpression(t,this.t.identifier("map")),[this.t.arrowFunctionExpression([e],i)])}updateForNode(t,e,i,s){return this.optionalExpression(t,this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("updateArray")),[e,...this.updateParams.slice(1),this.getForKeyStatement(e,i,s)]))}updateForNodeItem(t){return this.optionalExpression(t,this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("update")),this.updateParams))}},U=class extends g{geneCondIdx(t){return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.memberExpression(this.t.identifier("$thisCond"),this.t.identifier("cond")),this.t.numericLiteral(t)))}geneCondCheck(t){return this.t.ifStatement(this.t.binaryExpression("===",this.t.memberExpression(this.t.identifier("$thisCond"),this.t.identifier("cond")),this.t.numericLiteral(t)),this.t.blockStatement([this.t.expressionStatement(this.t.assignmentExpression("=",this.t.memberExpression(this.t.identifier("$thisCond"),this.t.identifier("didntChange")),this.t.booleanLiteral(!0))),this.t.returnStatement(this.t.arrayExpression([]))]))}updateCondNodeCond(t){return this.optionalExpression(t,this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("updateCond")),[...this.updateParams.slice(1)]))}updateCondNode(t){return this.optionalExpression(t,this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("update")),this.updateParams))}declareCondNode(t,e,i){return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.identifier(t),this.t.newExpression(this.t.identifier(this.importMap.CondNode),[this.t.numericLiteral(U.calcDependencyNum(i)),this.t.arrowFunctionExpression([this.t.identifier("$thisCond")],e)])))}geneCondReturnStatement(t,e){return this.t.returnStatement(this.t.conditionalExpression(this.t.binaryExpression("===",this.t.memberExpression(this.t.identifier("$thisCond"),this.t.identifier("cond")),this.t.numericLiteral(e)),this.t.arrayExpression(t.map(i=>this.t.identifier(i))),this.t.callExpression(this.t.memberExpression(this.t.identifier("$thisCond"),this.t.identifier("updateCond")),this.updateParams.slice(1))))}},kt=class extends U{run(){let{branches:t}=this.viewParticle,e=t.flatMap(({condition:s})=>s.dependencyIndexArr??[]),i=this.generateNodeName();return this.addInitStatement(this.declareIfNode(i,t,e)),this.addUpdateStatements(e,this.updateCondNodeCond(i)),this.addUpdateStatementsWithoutDep(this.updateCondNode(i)),i}geneIfStatement(t,e,i){return this.t.ifStatement(t,this.t.blockStatement(e),i)}declareIfNode(t,e,i){this.t.isBooleanLiteral(e[e.length-1].condition.value,{value:!0})||e.push({condition:{value:this.t.booleanLiteral(!0),dependencyIndexArr:[],dependenciesNode:this.t.arrayExpression([]),dynamic:!1},children:[]});let s=e.reverse().reduce((r,{condition:n,children:o},a)=>{let h=e.length-a-1,[p,l,d,c]=this.generateChildren(o,!1,!0),m=this.t.expressionStatement(this.t.assignmentExpression("=",this.t.memberExpression(this.t.identifier("$thisCond"),this.t.identifier("updateFunc")),this.t.arrowFunctionExpression(this.updateParams,this.geneUpdateBody(d))));return p.unshift(...this.declareNodes(c),m),p.unshift(this.geneCondCheck(h),this.geneCondIdx(h)),p.push(this.geneCondReturnStatement(l,h)),a===0?this.t.blockStatement(p):this.geneIfStatement(n.value,p,r)},void 0);return this.declareCondNode(t,this.t.blockStatement([s]),i)}},Dt=class extends O{run(){let{props:t}=this.viewParticle;t=this.alterPropViews(t);let{children:e}=this.viewParticle,i=this.generateNodeName();return this.addInitStatement(this.declareEnvNode(i,t)),this.addInitStatement(this.geneEnvChildren(i,e)),Object.entries(t).forEach(([s,{dependencyIndexArr:r,value:n,dependenciesNode:o}])=>{r&&this.addUpdateStatements(r,this.updateEnvNode(i,s,n,o))}),i}generateEnvs(t){return[this.t.objectExpression(Object.entries(t).map(([e,{value:i}])=>this.t.objectProperty(this.t.identifier(e),i))),this.t.objectExpression(Object.entries(t).map(([e,{dependenciesNode:i}])=>i&&this.t.objectProperty(this.t.identifier(e),i)).filter(Boolean))]}declareEnvNode(t,e){return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.identifier(t),this.t.newExpression(this.t.identifier(this.importMap.EnvNode),this.generateEnvs(e))))}geneEnvChildren(t,e){let[i,s]=this.generateChildren(e);return this.addInitStatement(...i),this.t.expressionStatement(this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("initNodes")),[this.t.arrayExpression(s.map(r=>this.t.identifier(r)))]))}updateEnvNode(t,e,i,s){return this.optionalExpression(t,this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("updateEnv")),[this.t.stringLiteral(e),this.t.arrowFunctionExpression([],i),s]))}},At=class extends g{run(){let{content:t}=this.viewParticle,e=this.generateNodeName();return this.addInitStatement(this.declareTextNode(e,t.value,t.dependenciesNode)),t.dynamic&&this.addUpdateStatements(t.dependencyIndexArr,this.updateTextNode(e,t.value,t.dependenciesNode)),e}declareTextNode(t,e,i){return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.identifier(t),this.t.callExpression(this.t.identifier(this.importMap.createTextNode),[e,i])))}updateTextNode(t,e,i){return this.t.expressionStatement(this.t.logicalExpression("&&",this.t.identifier(t),this.t.callExpression(this.t.identifier(this.importMap.updateText),[this.t.identifier(t),this.t.arrowFunctionExpression([],e),i])))}},et=class extends Q{run(){let{content:t,props:e}=this.viewParticle;t=this.alterPropView(t),e=this.alterPropViews(e);let i=this.generateNodeName();return this.addInitStatement(this.declareExpNode(i,t.value,t.dependenciesNode)),t.dynamic&&this.addUpdateStatements(t.dependencyIndexArr,this.updateExpNode(i,t.value,t.dependenciesNode)),e&&Object.entries(e).forEach(([s,{value:r}])=>{if(et.lifecycle.includes(s))return this.addInitStatement(this.addLifecycle(i,s,r));if(s==="ref")return this.addInitStatement(this.initElement(i,r));if(s==="elements")return this.addInitStatement(this.initElement(i,r,!0));if(s==="didUpdate")return this.addUpdateStatements(t.dependencyIndexArr,this.addOnUpdate(i,r));tt.warn1(s)}),i}declareExpNode(t,e,i){return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.identifier(t),this.t.newExpression(this.t.identifier(this.importMap.ExpNode),[e,i??this.t.nullLiteral()])))}updateExpNode(t,e,i){return this.optionalExpression(t,this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("update")),[this.t.arrowFunctionExpression([],e),i??this.t.nullLiteral()]))}},it=class extends O{run(){let{props:t}=this.viewParticle;t=this.alterPropViews(t);let{tag:e}=this.viewParticle,i=this.generateNodeName(),s=this.snippetPropMap[e]??[],r=Array.from({length:s.length},()=>this.t.nullLiteral()),n=[];return Object.entries(t).forEach(([o,{value:a,dependencyIndexArr:h,dependenciesNode:p}])=>{if(o==="didUpdate")return;if(it.lifecycle.includes(o)){this.addInitStatement(this.addLifecycle(i,o,a));return}if(!h||h.length===0)return;n.push(...h);let l=s.indexOf(o);p&&(r[l]=p);let d=1<this.t.objectProperty(this.t.identifier(e),i.value)))}declareSnippetNode(t,e,i,s){return[this.t.expressionStatement(this.t.assignmentExpression("=",this.t.identifier(t),this.t.newExpression(this.t.identifier(this.importMap.SnippetNode),[this.t.arrayExpression(s)]))),this.t.expressionStatement(this.t.callExpression(this.t.memberExpression(this.t.thisExpression(),this.t.identifier(e)),[this.genePropNode(i),this.t.identifier(t)]))]}updateProp(t,e,i,s,r){return this.optionalExpression(t,this.t.optionalCallExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("updateProp")),[this.t.numericLiteral(e),...this.updateParams.slice(1),this.t.arrowFunctionExpression([],this.t.objectExpression([this.t.objectProperty(this.t.identifier(i),s)])),r],!0))}updateSnippet(t){return this.optionalExpression(t,this.t.optionalCallExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("update")),this.updateParams,!0))}},jt=class extends U{run(){let{branches:t,discriminant:e}=this.viewParticle,i=t.flatMap(({case:r})=>r.dependencyIndexArr);i.push(...e.dependencyIndexArr);let s=this.generateNodeName();return this.addInitStatement(this.declareSwitchNode(s,e.value,t,i)),this.addUpdateStatements(i,this.updateCondNodeCond(s)),this.addUpdateStatementsWithoutDep(this.updateCondNode(s)),s}declareSwitchNode(t,e,i,s){let r=i.map(({case:a,break:h,children:p},l)=>{if(!h)for(let d=l+1;da===null)===-1&&r.push({case:{value:this.t.booleanLiteral(!0),dependencyIndexArr:[],dependenciesNode:this.t.arrayExpression([]),dynamic:!1},break:!0,children:[]});let o=r.map(({case:a,children:h},p)=>{let[l,d,c,m]=this.generateChildren(h,!1,!0),u=this.t.expressionStatement(this.t.assignmentExpression("=",this.t.memberExpression(this.t.identifier("$thisCond"),this.t.identifier("updateFunc")),this.t.arrowFunctionExpression(this.updateParams,this.geneUpdateBody(c))));return l.unshift(...this.declareNodes(m),u),l.unshift(this.geneCondCheck(p),this.geneCondIdx(p)),l.push(this.geneCondReturnStatement(d,p)),this.t.switchCase(a?a.value:null,[this.t.blockStatement(l)])});return this.declareCondNode(t,this.t.blockStatement([this.t.switchStatement(e,o)]),s)}},Ft=class extends g{run(){let{children:t,catchChildren:e,exception:i}=this.viewParticle,s=this.generateNodeName();return this.addInitStatement(this.declareTryNode(s,t,e,i)),this.addUpdateStatementsWithoutDep(this.declareUpdate(s)),s}declareTryNodeUpdate(t,e=!0){let[i,s,r,n]=this.generateChildren(t,!1,!0),o=this.t.arrowFunctionExpression([this.t.identifier("$changed")],this.geneUpdateBody(r));return i.unshift(...this.declareNodes(n),this.t.expressionStatement(this.t.callExpression(this.t.identifier("$setUpdate"),e?[this.t.callExpression(this.t.identifier("$catchable"),[o])]:[o]))),i.push(this.t.returnStatement(this.t.arrayExpression(s.map(a=>this.t.identifier(a))))),i}declareTryNode(t,e,i,s){let r=s?[s]:[];return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.identifier(t),this.t.newExpression(this.t.identifier(this.importMap.TryNode),[this.t.arrowFunctionExpression([this.t.identifier("$setUpdate"),this.t.identifier("$catchable")],this.t.blockStatement(this.declareTryNodeUpdate(e,!0))),this.t.arrowFunctionExpression([this.t.identifier("$setUpdate"),...r],this.t.blockStatement(this.declareTryNodeUpdate(i,!1)))])))}declareUpdate(t){return this.optionalExpression(t,this.t.callExpression(this.t.memberExpression(this.t.identifier(t),this.t.identifier("update")),this.updateParams))}},st=class{config;t;constructor(t){this.config=t,this.t=t.babelApi.types,this.templateIdx=t.templateIdx}generateChildren(t){let e=[],i=[],s={},r=[];return t.forEach(n=>{let[o,a,h,p]=this.generateChild(n);e.push(...o),Object.entries(a).forEach(([l,d])=>{s[Number(l)]||(s[Number(l)]=[]),s[Number(l)].push(...d)}),i.push(...h),r.push(p)}),[e,s,i,r]}nodeIdx=-1;templateIdx=-1;generateChild(t){let{type:e}=t,i=st.generatorMap[e];if(!i)throw new Error(`Unknown view particle type: ${e}`);let s=new i(t,this.config);s.nodeIdx=this.nodeIdx,s.templateIdx=this.templateIdx;let r=s.generate();return this.nodeIdx=s.nodeIdx,this.templateIdx=s.templateIdx,r}declareNodes(){return this.nodeIdx===-1?[]:[this.t.variableDeclaration("let",Array.from({length:this.nodeIdx+1},(t,e)=>this.t.variableDeclarator(this.t.identifier(`${N.node}${e}`))))]}get updateParams(){return[this.t.identifier("$changed")]}},C=st;I(C,"generatorMap",{comp:j,html:Lt,template:F,for:Tt,if:kt,switch:jt,env:Dt,text:At,exp:et,snippet:it,try:Ft});var Ot=class extends C{generate(t){let e=[],i=[],s={},r=[];return t.forEach(o=>{let[a,h,p,l]=this.generateChild(o);i.push(...a),Object.entries(h).forEach(([d,c])=>{s[Number(d)]||(s[Number(d)]=[]),s[Number(d)].push(...c)}),e.push(...p),r.push(l)}),[this.t.blockStatement([...this.declareNodes(),...this.geneUpdate(s),...i,this.geneReturn(r)]),e,this.templateIdx]}geneUpdate(t){return Object.keys(t).length===0?[]:[this.t.expressionStatement(this.t.assignmentExpression("=",this.t.memberExpression(this.t.thisExpression(),this.t.identifier("_$update"),!1),this.t.arrowFunctionExpression(this.updateParams,this.t.blockStatement([...Object.entries(t).filter(([e])=>e!=="0").map(([e,i])=>this.t.ifStatement(this.t.binaryExpression("&",this.t.identifier("$changed"),this.t.numericLiteral(Number(e))),this.t.blockStatement(i))),...t[0]??[]]))))]}geneReturn(t){return this.t.returnStatement(this.t.arrayExpression(t.map(e=>this.t.identifier(e))))}},Ut=class extends C{generate(t,e,i){let s=[],r=[],n={},o={},a=[],h=this.templateIdx;t.forEach(c=>{let[m,u,f,x]=this.generateChild(c);r.push(...m),Object.entries(u).forEach(([T,ct])=>{n[Number(T)]||(n[Number(T)]=[]),n[Number(T)].push(...ct)}),s.push(...f),a.push(x)}),this.templateIdx=h,this.nodeIdx=-1,e.forEach(c=>{let[,m]=this.generateChild(c);Object.entries(m).forEach(([u,f])=>{o[Number(u)]||(o[Number(u)]=[]),o[Number(u)].push(...f)})});let p=Object.keys(n).length>0,l=Object.keys(o).filter(c=>c!=="0").length>0;return[this.t.blockStatement([...this.declareNodes(),...p?[this.geneUpdateFunc("update",n)]:[],...l?[this.geneUpdateFunc("updateProp",o,i)]:[],...r,this.geneAddNodes(a)]),s,this.templateIdx]}geneAddNodes(t){return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.memberExpression(this.t.identifier("$snippetNode"),this.t.identifier("_$nodes")),this.t.arrayExpression(t.map(e=>this.t.identifier(e)))))}geneUpdateFunc(t,e,i){return this.t.expressionStatement(this.t.assignmentExpression("=",this.t.memberExpression(this.t.identifier("$snippetNode"),this.t.identifier(t)),this.geneUpdateBody(e,i)))}geneUpdateBody(t,e){let i=[],s=this.updateParams;e&&(s.push(this.t.identifier("$snippetPropsFunc"),this.t.identifier("$depsArr")),i.push(this.t.ifStatement(this.t.callExpression(this.t.memberExpression(this.t.identifier("$snippetNode"),this.t.identifier("cached")),[this.t.identifier("$depsArr"),this.t.identifier("$changed")]),this.t.blockStatement([this.t.returnStatement()]))),i.push(this.t.variableDeclaration("const",[this.t.variableDeclarator(this.t.identifier("$snippetProps"),this.t.callExpression(this.t.identifier("$snippetPropsFunc"),[]))])),e.properties.filter(n=>this.t.isObjectProperty(n)).forEach((n,o)=>{let a=1<n!=="0").map(([n,o])=>this.t.ifStatement(this.t.binaryExpression("&",this.t.identifier("$changed"),this.t.numericLiteral(Number(n))),this.t.blockStatement(o))),...r]))}};function rt(t,e){return new Ot(e).generate(t)}function nt(t,e,i,s){return new Ut(s).generate(t,e,i)}function $t(t,e){let i=t.node.id.name,s=t.node.id.name+"Temp",r=e.genClassComponent(s);t.replaceWith(r),t.scope.rename(s,i)}var Vt=class{constructor(t,e){this.babelApi=t,this.t=t.types}functionDeclarationVisitor(t){var e,i;if(((e=t.node.id)==null?void 0:e.name[0])!==((i=t.node.id)==null?void 0:i.name[0].toUpperCase()))return;let s=t.node.body.body.find(h=>this.t.isReturnStatement(h));if(!s||!(this.t.isJSXElement(s.argument)||this.t.isJSXFragment(s.argument)))return;let r=new Bt(this.babelApi,t),o=t.node.params[0];r.transformProps(o);let a=t.node.body.body;a.forEach((h,p)=>{if(this.t.isVariableDeclaration(h)){r.transformStateDeclaration(h);return}if(this.t.isFunctionDeclaration(h)){r.transformMethods(h);return}if(r.shouldTransformWatch(h)){r.transformWatch(h);return}if(this.t.isReturnStatement(h)){if(p!==a.length-1)throw new Error("Early return is not supported yet.");r.transformRenderMethod(h);return}}),$t(t,r)}},Bt=class{constructor(t,e){this.properties=[],this.babelApi=t,this.t=t.types,this.functionScope=e.scope}valueWrapper(t){return this.t.file(this.t.program([this.t.isStatement(t)?t:this.t.expressionStatement(t)]))}addProperty(t,e){this.properties.push(t),e&&(this.functionScope.rename(e,`this.${e}`),this.functionScope.path.traverse({Identifier:i=>{i.node.name===`this.${e}`&&i.replaceWith(this.t.memberExpression(this.t.thisExpression(),this.t.identifier(e)))}}))}genClassComponent(t){return this.t.classDeclaration(this.t.identifier(t),this.t.identifier("View"),this.t.classBody(this.properties),[])}transformStateDeclaration(t){t.declarations.forEach(e=>{let i=e.id;if(this.t.isObjectPattern(i))return this.transformPropsDestructuring(i);if(!this.t.isArrayPattern(i)){if(this.t.isIdentifier(i)){let s=this.t.cloneNode(i);this.addProperty(this.t.classProperty(s,e.init),i.name)}}})}transformRenderMethod(t){let e=this.t.classMethod("method",this.t.identifier("Body"),[],this.t.blockStatement([t]),!1,!1);this.addProperty(e,"Body")}transformLifeCycle(){}transformComputed(){}transformMethods(t){var e;let i=(e=t.id)==null?void 0:e.name;if(!i)return;let s=this.t.classMethod("method",this.t.identifier(i),t.params,t.body,t.generator,t.async);this.addProperty(s,i)}transformProps(t){if(t){if(this.isObjDestructuring(t)){this.transformPropsDestructuring(t);return}if(!this.t.isIdentifier(t))throw new Error("Unsupported props type, please use object destructuring or identifier.")}}transformWatch(t){let e=this.functionScope.generateUidIdentifier("watch"),i=this.t.classMethod("method",e,[],this.t.blockStatement([t]),!1,!1);i.decorators=[this.t.decorator(this.t.identifier("Watch"))],this.addProperty(i)}isObjDestructuring(t){return this.t.isObjectPattern(t)}transformPropsDestructuring(t){let e=[];return t.properties.forEach(i=>{if(this.t.isObjectProperty(i)){let s=i.key;if(this.t.isIdentifier(s)){if(this.t.isAssignmentPattern(i.value)){let r=i.value.right;this.addProp(s,r),e.push(s);return}else if(this.t.isIdentifier(i.value)){this.addProp(s,void 0,i.value.name==="children"),e.push(s);return}else if(this.t.isObjectPattern(i.value)){this.transformPropsDestructuring(i.value);return}return}if(this.t.isAssignmentPattern(i.value)){let r=i.value.right,n=i.value.left;this.t.isIdentifier(n)&&(this.addProp(n,r),e.push(n));return}throw new Error("Unsupported props destructuring, please use simple object destructuring.")}}),e}addProp(t,e,i=!1){let s=this.t.cloneNode(t);this.addProperty(this.t.classProperty(s,e??void 0,void 0,[this.t.decorator(this.t.identifier(i?"Children":"Prop"))],void 0,!1),t.name)}shouldTransformWatch(t){return!!(this.t.isExpressionStatement(t)&&(this.t.isCallExpression(t.expression)||this.t.isAssignmentExpression(t.expression)||this.t.isUpdateExpression(t.expression))||this.t.isForStatement(t)||this.t.isWhileStatement(t)||this.t.isIfStatement(t)||this.t.isSwitchStatement(t)||this.t.isTryStatement(t))}};function Wt(t,e){let i=new Vt(t,e);return{name:"zouyu-2",visitor:{FunctionDeclaration(s){i.functionDeclarationVisitor(s)}}}}var at=Wt;var _t=Object.defineProperty,Ht=(t,e,i)=>e in t?_t(t,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):t[e]=i,Gt=(t,e,i)=>(Ht(t,typeof e!="symbol"?e+"":e,i),i),Rt=process.env.NODE_ENV==="development",ht={class:"className",for:"htmlFor"},S=["push","pop","shift","unshift","splice","sort","reverse","add","delete","clear","set","delete","clear"],B=["a","abbr","address","area","article","aside","audio","b","base","bdi","bdo","blockquote","body","br","button","canvas","caption","cite","code","col","colgroup","data","datalist","dd","del","details","dfn","dialog","div","dl","dt","em","embed","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe","img","input","ins","kbd","label","legend","li","link","main","map","mark","menu","meta","meter","nav","noscript","object","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","slot","small","source","span","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","title","tr","track","u","ul","var","video","wbr","acronym","applet","basefont","bgsound","big","blink","center","dir","font","frame","frameset","isindex","keygen","listing","marquee","menuitem","multicol","nextid","nobr","noembed","noframes","param","plaintext","rb","rtc","spacer","strike","tt","xmp","animate","animateMotion","animateTransform","circle","clipPath","defs","desc","ellipse","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence","filter","foreignObject","g","image","line","linearGradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialGradient","rect","set","stop","svg","switch","symbol","text","textPath","tspan","use","view"],Jt=["Static","Prop","Env","Content","Children"],$="@inula/next",L=Object.fromEntries(["createElement","setStyle","setDataset","setEvent","delegateEvent","setHTMLProp","setHTMLAttr","setHTMLProps","setHTMLAttrs","createTextNode","updateText","insertNode","ForNode","CondNode","ExpNode","EnvNode","TryNode","SnippetNode","PropView","render"].map(t=>[t,`$$${t}`])),Xt=["Static","Children","Content","Prop","Env","Watch","ForwardProps","Main","App","Mount","_","env","Snippet",...B.filter(t=>t!=="use")],qt={textContent:["*"],innerHTML:["*"],accept:["form","input"],acceptCharset:["form"],accesskey:["*"],action:["form"],align:["caption","col","colgroup","hr","iframe","img","table","tbody","td","tfoot","th","thead","tr"],allow:["iframe"],alt:["area","img","input"],async:["script"],autocapitalize:["*"],autocomplete:["form","input","select","textarea"],autofocus:["button","input","select","textarea"],autoplay:["audio","video"],background:["body","table","td","th"],bgColor:["body","col","colgroup","marquee","table","tbody","tfoot","td","th","tr"],border:["img","object","table"],buffered:["audio","video"],capture:["input"],charset:["meta"],checked:["input"],cite:["blockquote","del","ins","q"],className:["*"],color:["font","hr"],cols:["textarea"],colSpan:["td","th"],content:["meta"],contentEditable:["*"],contextmenu:["*"],controls:["audio","video"],coords:["area"],crossOrigin:["audio","img","link","script","video"],csp:["iframe"],data:["object"],dateTime:["del","ins","time"],decoding:["img"],default:["track"],defer:["script"],dir:["*"],dirname:["input","textarea"],disabled:["button","fieldset","input","optgroup","option","select","textarea"],download:["a","area"],draggable:["*"],enctype:["form"],enterKeyHint:["textarea","contenteditable"],htmlFor:["label","output"],form:["button","fieldset","input","label","meter","object","output","progress","select","textarea"],formAction:["input","button"],formEnctype:["button","input"],formMethod:["button","input"],formNoValidate:["button","input"],formTarget:["button","input"],headers:["td","th"],height:["canvas","embed","iframe","img","input","object","video"],hidden:["*"],high:["meter"],href:["a","area","base","link"],hreflang:["a","link"],httpEquiv:["meta"],id:["*"],integrity:["link","script"],intrinsicSize:["img"],inputMode:["textarea","contenteditable"],ismap:["img"],itemProp:["*"],kind:["track"],label:["optgroup","option","track"],lang:["*"],language:["script"],loading:["img","iframe"],list:["input"],loop:["audio","marquee","video"],low:["meter"],manifest:["html"],max:["input","meter","progress"],maxLength:["input","textarea"],minLength:["input","textarea"],media:["a","area","link","source","style"],method:["form"],min:["input","meter"],multiple:["input","select"],muted:["audio","video"],name:["button","form","fieldset","iframe","input","object","output","select","textarea","map","meta","param"],noValidate:["form"],open:["details","dialog"],optimum:["meter"],pattern:["input"],ping:["a","area"],placeholder:["input","textarea"],playsInline:["video"],poster:["video"],preload:["audio","video"],readonly:["input","textarea"],referrerPolicy:["a","area","iframe","img","link","script"],rel:["a","area","link"],required:["input","select","textarea"],reversed:["ol"],role:["*"],rows:["textarea"],rowSpan:["td","th"],sandbox:["iframe"],scope:["th"],scoped:["style"],selected:["option"],shape:["a","area"],size:["input","select"],sizes:["link","img","source"],slot:["*"],span:["col","colgroup"],spellcheck:["*"],src:["audio","embed","iframe","img","input","script","source","track","video"],srcdoc:["iframe"],srclang:["track"],srcset:["img","source"],start:["ol"],step:["input"],style:["*"],summary:["table"],tabIndex:["*"],target:["a","area","base","form"],title:["*"],translate:["*"],type:["button","input","embed","object","ol","script","source","style","menu","link"],usemap:["img","input","object"],value:["button","data","input","li","meter","option","progress","param","text"],width:["canvas","embed","iframe","img","input","object","video"],wrap:["textarea"],ariaAutocomplete:["*"],ariaChecked:["*"],ariaDisabled:["*"],ariaErrorMessage:["*"],ariaExpanded:["*"],ariaHasPopup:["*"],ariaHidden:["*"],ariaInvalid:["*"],ariaLabel:["*"],ariaLevel:["*"],ariaModal:["*"],ariaMultiline:["*"],ariaMultiSelectable:["*"],ariaOrientation:["*"],ariaPlaceholder:["*"],ariaPressed:["*"],ariaReadonly:["*"],ariaRequired:["*"],ariaSelected:["*"],ariaSort:["*"],ariaValuemax:["*"],ariaValuemin:["*"],ariaValueNow:["*"],ariaValueText:["*"],ariaBusy:["*"],ariaLive:["*"],ariaRelevant:["*"],ariaAtomic:["*"],ariaDropEffect:["*"],ariaGrabbed:["*"],ariaActiveDescendant:["*"],ariaColCount:["*"],ariaColIndex:["*"],ariaColSpan:["*"],ariaControls:["*"],ariaDescribedBy:["*"],ariaDescription:["*"],ariaDetails:["*"],ariaFlowTo:["*"],ariaLabelledBy:["*"],ariaOwns:["*"],ariaPosInset:["*"],ariaRowCount:["*"],ariaRowIndex:["*"],ariaRowSpan:["*"],ariaSetSize:["*"]},W=class{dlightPackageName=$;babelApi;t;traverse;enableDevTools;includes;excludes;htmlTags;attributeMap;isJsx=!1;constructor(t,e,i,s,r,n,o){this.babelApi=t,this.t=e,this.traverse=t.traverse,this.includes=i,this.excludes=s,this.enableDevTools=Rt&&r,this.htmlTags=typeof n=="function"?n(B):n.includes("*")?[...new Set([...B,...n])].filter(a=>a!=="*"):n,this.attributeMap=o}classDeclarationNode;classBodyNode;propertiesContainer={};dependencyMap={};enter=!0;dLightModel=!1;enterClassNode=!1;className;programNode;allImports=[];didAlterImports=!1;clearNode(){this.classDeclarationNode=void 0,this.classBodyNode=void 0,this.propertiesContainer={},this.dependencyMap={},this.enter=!0,this.enterClassNode=!1,this.dLightModel=!1,this.className=void 0}get availableProperties(){return Object.entries(this.propertiesContainer).filter(([t,{isWatcher:e,isStatic:i,isChildren:s}])=>t!=="_$compName"&&!e&&!i&&!s).map(([t])=>t)}initNode(t){let e=t.node;if(this.classDeclarationNode=e,this.classBodyNode=e.body,this.propertiesContainer={},e.id?.name||(e.id=this.t.identifier(`Anonymous_${W.uid()}`)),this.className=e.id?.name,this.handleClassCustomDecorators(),this.enableDevTools&&this.classBodyNode.body.unshift(this.t.classProperty(this.t.identifier("_$compName"),this.t.stringLiteral(this.className))),!this.didAlterImports){let i=this.allImports.filter(s=>s.source.value===$);this.dlightPackageName!==$&&i.forEach(s=>{s.source.value=this.dlightPackageName}),i.forEach(s=>{s.specifiers=s.specifiers.filter(r=>!(this.t.isImportSpecifier(r)&&this.t.isIdentifier(r.imported)&&Xt.includes(r.imported.name)))}),this.programNode.body.unshift(this.t.importDeclaration(Object.entries(L).map(([s,r])=>this.t.importSpecifier(this.t.identifier(r),this.t.identifier(s))),this.t.stringLiteral(this.dlightPackageName))),this.didAlterImports=!0}}programEnterVisitor(t,e){this.enter=this.fileAllowed(e),this.enter&&((e&&e.endsWith(".jsx")||e&&e.endsWith(".tsx"))&&(this.isJsx=!0),this.allImports=t.node.body.filter(i=>this.t.isImportDeclaration(i)),this.programNode=t.node)}programExitVisitor(){this.enter&&(this.didAlterImports=!1,this.allImports=[],this.programNode=void 0)}classEnter(t){this.enter&&(this.enterClassNode=this.isDLightClass(t),this.enterClassNode&&(this.initNode(t),this.resolveMounting(t)))}classExit(){this.enter&&this.enterClassNode&&(this.transformDLightClass(),this.clearNode(),this.enterClassNode=!1)}classMethodVisitor(t){if(!this.enterClassNode||!this.t.isIdentifier(t.node.key))return;let e=t.node.key.name;if(e==="Body"||this.findDecoratorByName(t.node.decorators,"Snippet"))return;let s=t.node,r=this.findDecoratorByName(s.decorators,"Watch");if(this.t.isIdentifier(s.key,{name:"constructor"})||(this.autoBindMethods(s),!r))return;let n=[],o;if(this.t.isIdentifier(r))[n,o]=this.getDependencies(s);else{let a=r.arguments.filter(p=>this.t.isStringLiteral(p)).map(p=>p.value),h=this.t.classMethod("method",s.key,[],this.t.blockStatement([this.t.expressionStatement(this.t.arrayExpression(a.map(p=>this.t.memberExpression(this.t.thisExpression(),this.t.identifier(p)))))]));[n,o]=this.getDependencies(h)}this.propertiesContainer[e]={node:s,deps:n,depsNode:o,isWatcher:!0},s.decorators=this.removeDecorators(s.decorators,["Watch"])}classPropertyVisitor(t){if(!this.enterClassNode)return;let e=t.node;if(!this.t.isIdentifier(e.key))return;let i=e.key.name;if(i==="Body")return;let s=e.decorators;if(this.findDecoratorByName(s,"Snippet"))return;let n=this.parseModel(t),o=!!this.findDecoratorByName(s,"Prop"),a=!!this.findDecoratorByName(s,"Env"),h=!!this.findDecoratorByName(e.decorators,"Children"),[p,l]=h?[[]]:this.getDependencies(e);this.propertiesContainer[i]={node:e,deps:p,depsNode:l,isStatic:!!this.findDecoratorByName(s,"Static"),isContent:!!this.findDecoratorByName(s,"Content"),isChildren:h,isPropOrEnv:o?"Prop":a?"Env":void 0,isModel:n},e.decorators=this.removeDecorators(s,Jt)}resolveWatcherDecorator(t,e){if(!this.t.isIdentifier(t.key))return;let i=t.key.name,s=this.classBodyNode.body.indexOf(t),r=this.t.classProperty(this.t.identifier(`$w$${i}`));this.classBodyNode.body.splice(s,0,r),t.body.body.unshift(this.t.ifStatement(this.t.callExpression(this.t.memberExpression(this.t.thisExpression(),this.t.identifier("_$cache")),[this.t.stringLiteral(i),e]),this.t.blockStatement([this.t.returnStatement()])))}resolveChildrenDecorator(t){if(!this.classBodyNode||!this.t.isIdentifier(t.key))return;let e=t.key.name,i=this.classBodyNode.body.indexOf(t),s=this.t.memberExpression(this.t.thisExpression(),this.t.identifier("_$children")),r=this.t.classMethod("get",this.t.identifier(e),[],this.t.blockStatement([this.t.returnStatement(s)]));this.classBodyNode.body.splice(i,1,r)}resolveContentDecorator(t){if(!this.classBodyNode||!this.t.isIdentifier(t.key)||this.classBodyNode.body.some(r=>this.t.isClassProperty(r)&&r.key.name==="_$contentKey"))return;let e=t.key.name,i=this.classBodyNode.body.indexOf(t),s=this.t.classProperty(this.t.identifier("_$contentKey"),this.t.stringLiteral(e));this.classBodyNode.body.splice(i,0,s)}resolvePropDecorator(t,e){if(!this.classBodyNode||!this.t.isIdentifier(t.key))return;let i=t.key.name,s=this.classBodyNode.body.indexOf(t),r=e.toLowerCase()==="prop"?"p":"e",n=this.t.classProperty(this.t.identifier(`$${r}$${i}`));this.classBodyNode.body.splice(s,0,n)}resolveStateDecorator(t,e,i){if(!this.classBodyNode||!this.t.isIdentifier(t.key))return;let s=t.key.name,r=this.classBodyNode.body.indexOf(t),n=this.dLightModel?[]:[this.t.classProperty(this.t.identifier(`$$${s}`),this.t.numericLiteral(1<this.t.stringLiteral(a))))]:[];this.classBodyNode.body.splice(r+1,0,...n,...o)}handleClassCustomDecorators(){if(!this.classBodyNode)return;let t=this.classDeclarationNode?.decorators;if(!t)return;this.findDecoratorByName(t,"ForwardProps")&&(this.classBodyNode.body.unshift(this.t.classProperty(this.t.identifier("_$forwardProps")),this.t.classProperty(this.t.identifier("_$forwardPropsSet"),this.t.newExpression(this.t.identifier("Set"),[])),this.t.classProperty(this.t.identifier("_$forwardPropsId"),this.t.arrayExpression([]))),this.classDeclarationNode.decorators=this.removeDecorators(t,["ForwardProps"]))}transformDLightClass(){let t=this.handleView();this.addAutoUpdate(this.dLightModel?this.availableProperties:t);let e=Object.entries(this.propertiesContainer).reverse(),i=this.dependencyMapReversed();for(let[s,{node:r,deps:n,isStatic:o,isChildren:a,isPropOrEnv:h,isWatcher:p,isContent:l,isModel:d,depsNode:c}]of e){if(a){this.resolveChildrenDecorator(r);continue}n.length>0&&(t.push(...n),p?this.resolveWatcherDecorator(r,c):d||this.handleDerivedProperty(r,c)),h&&this.resolvePropDecorator(r,h),l&&(this.resolvePropDecorator(r,"Prop"),this.resolveContentDecorator(r)),!o&&t.includes(s)&&this.resolveStateDecorator(r,this.availableProperties.indexOf(s),i[s])}}addAutoUpdate(t){if(!this.classBodyNode)return;this.classBodyNode.body.filter(i=>!((this.t.isClassProperty(i)||this.t.isClassMethod(i))&&["constructor","_$compName"].includes(i.key.name)||this.t.isClassMethod(i,{static:!0})||this.t.isClassProperty(i,{static:!0}))).forEach(i=>{let s=this.t.isClassProperty(i)?i.value:this.t.isClassMethod(i)?i.body:null;s&&this.addUpdateDerived(s,t)})}addUpdateDerived(t,e){let i=(s,r)=>this.t.callExpression(this.t.memberExpression(this.t.thisExpression(),this.t.identifier("_$ud")),[s,this.t.stringLiteral(r)]);this.traverse(this.valueWrapper(t),{MemberExpression:s=>{if(!this.t.isThisExpression(s.node.object)||!this.t.isIdentifier(s.node.property))return;let r=s.node.property.name;if(!e.includes(r))return;let n=this.isAssignmentExpressionLeft(s);n&&(n.replaceWith(i(n.node,r)),n.skip())},CallExpression:s=>{if(!this.t.isMemberExpression(s.node.callee))return;let r=s.node.callee.property;if(!this.t.isIdentifier(r)||!S.includes(r.name))return;let n=s.get("callee").get("object");for(;this.t.isMemberExpression(n.node);)n=n.get("object");if(!this.t.isThisExpression(n?.node))return;let o=n.parentPath.node.property.name;s.replaceWith(i(s.node,o)),s.skip()}})}handleView(){if(!this.classBodyNode)return[];let t=new Set,e,i=[];for(let a of this.classBodyNode.body){if(!this.t.isClassProperty(a)&&!this.t.isClassMethod(a)||!this.t.isIdentifier(a.key))continue;let h=this.findDecoratorByName(a.decorators,"Snippet"),p=a.key.name==="Body";if(!(!h&&!p)){if(this.t.isClassProperty(a)){let l=a.value;for(;this.t.isTSAsExpression(l);)l=l.expression;if(!this.t.isArrowFunctionExpression(l))continue;a.value=l;let d=this.arrowFunctionPropertyToMethod(a);if(!d)continue;a=d}h?(a.decorators=null,i.push(a)):e=a}}let s=i.map(a=>a.key.name),r=Object.fromEntries(i.map(a=>{let h=a.params[0];if(!h||!this.t.isObjectPattern(h))return["-",null];let p=Object.fromEntries(h.properties.map(l=>{if(!this.t.isObjectProperty(l))return["-",null];let d=l.key.name,c=this.getIdentifiers(this.t.assignmentExpression("=",this.t.objectPattern([this.t.objectProperty(this.t.numericLiteral(0),l.value)]),this.t.numericLiteral(0))).filter(m=>m!==d);return[d,c]}).filter(([l,d])=>d));return[a.key.name,p]}).filter(([a,h])=>h)),n=-1;if(e){let a;[a,n]=this.alterMainView(e,s,r),a.forEach(t.add.bind(t))}i.forEach(a=>{let h;[h,n]=this.alterSnippet(a,s,r,n),h.forEach(t.add.bind(t))});let o=[];return this.availableProperties.forEach(a=>{t.has(a)&&o.push(a)}),o}alterMainView(t,e,i){let s=[];if(this.isJsx){let p=t.body.body.find(l=>this.t.isReturnStatement(l));(this.t.isJSXElement(p.argument)||this.t.isJSXFragment(p.argument))&&(s=K(p.argument,{babelApi:this.babelApi,htmlTags:this.htmlTags,parseTemplate:!1}))}else s=D(t.body,{babelApi:this.babelApi,snippetNames:e,htmlTags:this.htmlTags});let[r,n]=M(s,{babelApi:this.babelApi,availableProperties:this.availableProperties,dependencyMap:this.dependencyMap,reactivityFuncNames:S}),[o,a,h]=rt(r,{babelApi:this.babelApi,className:this.className,importMap:L,snippetPropMap:Object.fromEntries(Object.entries(i).map(([p,l])=>[p,Object.keys(l)])),templateIdx:-1,attributeMap:this.attributeMap,alterAttributeMap:ht});return t.body=o,this.classBodyNode?.body.push(...a),[n,h]}alterSnippet(t,e,i,s){t.params.length===0?t.params.push(this.t.identifier("_$"),this.t.identifier("$snippetNode")):t.params.length===1?t.params.push(this.t.identifier("$snippetNode")):(t.params[1]=this.t.identifier("$snippetNode"),t.params.length=2);let r=D(t.body,{babelApi:this.babelApi,snippetNames:e,htmlTags:this.htmlTags}),n=i[t.key.name]??[],o={};Object.entries(n).forEach(([u,f])=>{f.forEach(x=>{o[x]=[u]})});let[a,h]=M(r,{babelApi:this.babelApi,availableProperties:this.availableProperties,availableIdentifiers:Object.keys(n),dependencyMap:this.dependencyMap,dependencyParseType:"property",reactivityFuncNames:S}),[p]=M(r,{babelApi:this.babelApi,availableProperties:Object.keys(n),dependencyMap:this.dependencyMap,dependencyParseType:"identifier",identifierDepMap:o,reactivityFuncNames:S}),l=Object.fromEntries(Object.entries(i).map(([u,f])=>[u,Object.keys(f)])),[d,c,m]=nt(a,p,t.params[0],{babelApi:this.babelApi,className:this.className,importMap:L,snippetPropMap:l,templateIdx:s,attributeMap:this.attributeMap,alterAttributeMap:ht});return t.body=d,this.classBodyNode?.body.push(...c),[h,m]}fileAllowed(t){return this.includes.includes("*")?!0:!(!t||this.excludes.some(e=>ot(t,e))||!this.includes.some(e=>ot(t,e)))}isDLightView(t){let e=t.node;return(e.decorators??[]).find(r=>this.t.isIdentifier(r.expression,{name:"View"}))&&(e.superClass=this.t.identifier("View"),e.decorators=e.decorators?.filter(r=>!this.t.isIdentifier(r.expression,{name:"View"}))),this.t.isIdentifier(e.superClass,{name:"View"})}isDLightModel(t){let e=t.node;return(e.decorators??[]).find(r=>this.t.isIdentifier(r.expression,{name:"Model"}))&&(e.superClass=this.t.identifier("Model"),e.decorators=e.decorators?.filter(r=>!this.t.isIdentifier(r.expression,{name:"Model"}))),e.body.body.unshift(this.t.classProperty(this.t.identifier("_$model"))),e.body.body=e.body.body.filter(r=>!((this.t.isClassProperty(r)||this.t.isClassMethod(r,{kind:"method"}))&&(this.findDecoratorByName(r.decorators,"Snippet")||this.t.isIdentifier(r.key)&&r.key.name==="Body"))),this.dLightModel=!0,this.t.isIdentifier(e.superClass,{name:"Model"})}isDLightClass(t){return this.isDLightView(t)||this.isDLightModel(t)}parseModel(t){if(!this.allImports.some(c=>c.source.value===this.dlightPackageName&&c.specifiers.some(m=>{if(this.t.isImportSpecifier(m)&&this.t.isIdentifier(m.imported,{name:"use"}))return!0})))return;let i=t.node,s=i.key;if(!this.t.isIdentifier(s))return;let r=i.value;if(!this.t.isCallExpression(r)||!this.t.isIdentifier(r.callee,{name:"use"}))return;let n=r.arguments,o=n[1],a=n[2],h=this.t.nullLiteral();if(o){let c=[],m=[];if(this.t.isObjectExpression(o))o.properties.forEach(u=>{if(this.t.isSpreadElement(u)){let[,f]=this.getDependenciesFromNode(u.argument);c.push([u.argument,f??this.t.nullLiteral()])}else if(this.t.isObjectProperty(u)){let[,f]=this.getDependenciesFromNode(u.value);m.push([!u.computed&&this.t.isIdentifier(u.key)?this.t.stringLiteral(u.key.name):u.key,u.value,f??this.t.nullLiteral()])}else m.push([!u.computed&&this.t.isIdentifier(u.key)?this.t.stringLiteral(u.key.name):u.key,this.t.arrowFunctionExpression([],u.body),this.t.nullLiteral()])});else{let[,u]=this.getDependenciesFromNode(o);c.push([o,u??this.t.nullLiteral()])}h=this.t.objectExpression([this.t.objectProperty(this.t.identifier("m"),this.t.arrayExpression(c.map(u=>this.t.arrayExpression(u)))),this.t.objectProperty(this.t.identifier("s"),this.t.arrayExpression(m.map(u=>this.t.arrayExpression(u))))])}let p=this.t.nullLiteral();if(a){let[,c]=this.getDependenciesFromNode(a);p=this.t.arrayExpression([a,c??this.t.nullLiteral()])}n[1]=this.t.arrowFunctionExpression([],h),n[2]=this.t.arrowFunctionExpression([],p),n[3]=this.t.stringLiteral(s.name),r.callee=this.t.memberExpression(this.t.thisExpression(),this.t.identifier("_$injectModel")),i.value=this.t.arrowFunctionExpression([],r);let l=this.classBodyNode.body.indexOf(i),d=this.t.classProperty(this.t.identifier(`$md$${s.name}`));return this.classBodyNode.body.splice(l,0,d),!0}removeDecorators(t,e){return t?t.filter(i=>!(this.t.isIdentifier(i.expression)&&e.includes(i.expression.name)||this.t.isCallExpression(i.expression)&&this.t.isIdentifier(i.expression.callee)&&e.includes(i.expression.callee.name))):[]}findDecoratorByName(t,e){if(t)return t.find(i=>this.t.isIdentifier(i.expression,{name:e})||this.t.isCallExpression(i.expression)&&this.t.isIdentifier(i.expression.callee,{name:e}))?.expression}geneDependencyNode(t){let e=t;for(;e?.parentPath;){let s=e.parentPath;if(!(this.t.isMemberExpression(s.node,{computed:!1})||this.t.isOptionalMemberExpression(s.node)))break;e=s}let i=this.t.cloneNode(e.node);return this.traverse(this.valueWrapper(i),{MemberExpression:s=>{this.t.isThisExpression(s.node.object)||(s.node.optional=!0,s.node.type="OptionalMemberExpression")}}),i}addConstructor(){let t=this.classBodyNode.body.find(e=>this.t.isClassMethod(e,{kind:"constructor"}));return t||(t=this.t.classMethod("constructor",this.t.identifier("constructor"),[],this.t.blockStatement([this.t.expressionStatement(this.t.callExpression(this.t.super(),[]))])),this.classBodyNode.body.unshift(t),t)}autoBindMethods(t){this.addConstructor().body.body.push(this.t.expressionStatement(this.t.assignmentExpression("=",this.t.memberExpression(this.t.thisExpression(),t.key),this.t.callExpression(this.t.memberExpression(this.t.memberExpression(this.t.thisExpression(),t.key),this.t.identifier("bind")),[this.t.thisExpression()]))))}handleDerivedProperty(t,e){if(!this.t.isIdentifier(t.key))return;let i=t.key.name,s=t.value,r=this.classBodyNode.body.indexOf(t),n=this.t.classMethod("get",this.t.identifier(`$f$${i}`),[],this.t.blockStatement([this.t.ifStatement(this.t.callExpression(this.t.memberExpression(this.t.thisExpression(),this.t.identifier("_$cache")),[this.t.stringLiteral(i),e]),this.t.blockStatement([this.t.returnStatement(this.t.memberExpression(this.t.thisExpression(),this.t.identifier(i)))])),this.t.returnStatement(s)]));this.classBodyNode.body.splice(r+1,0,n),t.value=null}getDependenciesFromNode(t,e=!1){let i=new Set,s=new Set,r={};this.traverse(this.valueWrapper(t),{MemberExpression:a=>{if(!this.t.isIdentifier(a.node.property)||!this.t.isThisExpression(a.node.object))return;let h=a.node.property.name;this.isAssignmentExpressionLeft(a)||this.isAssignmentFunction(a)?s.add(h):this.availableProperties.includes(h)&&!this.isMemberInEscapeFunction(a,this.classDeclarationNode)&&!this.isMemberInManualFunction(a,this.classDeclarationNode)&&(i.add(h),e&&this.dependencyMap[h]?.forEach(i.add.bind(i)),r[h]||(r[h]=[]),r[h].push(this.geneDependencyNode(a)))}}),s.forEach(a=>{i.delete(a),delete r[a]});let n=Object.values(r).flat();n=n.filter((a,h)=>n.findIndex(l=>this.t.isNodesEquivalent(l,a))===h);let o=[...i];if(e&&i.size>0){let a=t.body.body[0].key.name;this.dependencyMap[a]=o}return[o,this.t.arrayExpression(n)]}getDependencies(t){if(!this.t.isIdentifier(t.key))return[[],void 0];let e=this.t.classDeclaration(null,null,this.t.classBody([t]));return this.getDependenciesFromNode(e,!0)}dependencyMapReversed(){let t={};return Object.entries(this.dependencyMap).forEach(([e,i])=>{i.forEach(s=>{t[s]||(t[s]=new Set),t[s].add(e)})}),t}resolveMounting(t){let e=t.node;if(!this.t.isIdentifier(e.id))return;let i=e.decorators??[],s=h=>{let p=i.find(l=>this.t.isIdentifier(l.expression,{name:h}));return p&&i.splice(i.findIndex(l=>l===p),1),p},r=s("Main")?"main":s("App")?"app":null,n;if(r)n=this.t.stringLiteral(r);else{let h=i.find(p=>this.t.isCallExpression(p.expression)&&this.t.isIdentifier(p.expression.callee,{name:"Mount"})&&p.expression.arguments.length===1);if(!h)return;i.splice(i.findIndex(p=>p===h),1),n=h.expression.arguments[0]}let o=t.parentPath.node;if(!this.t.isBlockStatement(o)&&!this.t.isProgram(o))return;let a=o.body.indexOf(e);o.body.splice(a+1,0,this.t.expressionStatement(this.t.callExpression(this.t.identifier(L.render),[n,e.id])))}arrowFunctionPropertyToMethod(t){if(!this.t.isArrowFunctionExpression(t.value))return;let e=t.value;if(!this.t.isBlockStatement(e.body))return;let i=this.classBodyNode.body.indexOf(t),s=this.t.classMethod("method",t.key,e.params,e.body);return this.classBodyNode.body.splice(i,1,s),s}isMemberExpressionProperty(t,e){return this.t.isMemberExpression(t)&&!t.computed&&t.property===e}isObjectKey(t,e){return this.t.isObjectProperty(t)&&t.key===e}valueWithArrowFunc(t){t.value||(t.value=this.t.identifier("undefined")),t.value=this.t.arrowFunctionExpression([],t.value)}getAllTopLevelReturnBlock(t){let e=[],i=!1;return this.traverse(this.valueWrapper(t),{Function:s=>{i||(i=!0,s.skip())},ReturnStatement:s=>{if(i)return;let r=s.parentPath.node;if(this.t.isBlockStatement(r))e.push(r);else{let n=this.t.blockStatement([s.node]);s.replaceWith(n),e.push(n)}s.skip()},exit:s=>{this.t.isFunction(s.node)&&(i=!1)}}),e}valueWrapper(t){return this.t.file(this.t.program([this.t.isStatement(t)?t:this.t.expressionStatement(t)]))}isAttrFromFunction(t,e){let i=t.parentPath,s=r=>this.t.isIdentifier(r)?r.name===e:this.t.isAssignmentPattern(r)?s(r.left):this.t.isArrayPattern(r)?r.elements.filter(Boolean).map(n=>s(n)).includes(!0):this.t.isObjectPattern(r)?r.properties.filter(n=>this.t.isObjectProperty(n)&&this.t.isIdentifier(n.key)).map(n=>n.key.name).includes(e):this.t.isRestElement(r)?s(r.argument):!1;for(;i;){let r=i.node;if(this.t.isArrowFunctionExpression(r)||this.t.isFunctionDeclaration(r)){for(let n of r.params)if(s(n))return!0}i=i.parentPath}return!1}isStandAloneIdentifier(t){let e=t.node,i=t.parentPath?.node;if(this.t.isMemberExpression(i)&&i.property===e||this.isAttrFromFunction(t,e.name))return!1;for(;t.parentPath;){if(this.t.isVariableDeclarator(t.parentPath.node)||this.t.isObjectProperty(t.parentPath.node)&&t.parentPath.node.key===t.node&&!t.parentPath.node.computed)return!1;t=t.parentPath}return!0}getIdentifiers(t){if(this.t.isIdentifier(t))return[t.name];let e=new Set;return this.traverse(this.valueWrapper(t),{Identifier:i=>{this.isStandAloneIdentifier(i)&&e.add(i.node.name)}}),[...e]}isAssignmentExpressionLeft(t){let e=t.parentPath;for(;e&&!this.t.isStatement(e.node);){if(this.t.isAssignmentExpression(e.node)){if(e.node.left===t.node)return e;let i=e.get("left");if(t.isDescendant(i))return e}else if(this.t.isUpdateExpression(e.node))return e;e=e.parentPath}return null}isAssignmentFunction(t){let e=t.parentPath;for(;e&&this.t.isMemberExpression(e.node);)e=e.parentPath;return e?this.t.isCallExpression(e.node)&&this.t.isMemberExpression(e.node.callee)&&this.t.isIdentifier(e.node.callee.property)&&S.includes(e.node.callee.property.name):!1}isMemberInEscapeFunction(t,e){let i=!1,s=t.parentPath;for(;s&&s.node!==e;){let r=s.node;if(this.t.isCallExpression(r)&&this.t.isIdentifier(r.callee)&&W.escapeNamings.includes(r.callee.name)){i=!0;break}s=s.parentPath}return i}isMemberInManualFunction(t,e){let i=!1,s=t.parentPath;for(;s&&s.node!==e;){let r=s.node,n=s.parentPath?.node,o=this.t.isFunctionExpression(r)||this.t.isArrowFunctionExpression(r),a=this.t.isCallExpression(n)&&this.t.isIdentifier(n.callee)&&n.callee.name==="manual";if(o&&a){i=!0;break}s=s.parentPath}return i}static uid(t=4){return Math.random().toString(32).slice(2,t+2)}},pt=W;Gt(pt,"escapeNamings",["escape","$"]);var Kt=pt;function zt(t,e){let{types:i}=t,{files:s="**/*.{js,ts,jsx,tsx}",excludeFiles:r="**/{dist,node_modules,lib}/*",enableDevTools:n=!1,htmlTags:o=p=>p,attributeMap:a=qt}=e,h=new Kt(t,i,Array.isArray(s)?s:[s],Array.isArray(r)?r:[r],n,o,a);return{visitor:{Program:{enter(p,{filename:l}){return h.programEnterVisitor(p,l)},exit:h.programExitVisitor.bind(h)},ClassDeclaration:{enter:h.classEnter.bind(h),exit:h.classExit.bind(h)},ClassMethod:h.classMethodVisitor.bind(h),ClassProperty:h.classPropertyVisitor.bind(h)}}}function lt(t,e){return{plugins:[["@babel/plugin-syntax-jsx"],["@babel/plugin-syntax-typescript",{isTSX:!0}],[V.default.default??V.default,{legacy:!0}],at,[zt,e]]}}import{minimatch as dt}from"minimatch";function Ke(t={}){let{files:e="**/*.{js,jsx,ts,tsx}",excludeFiles:i="**/{dist,node_modules,lib}/*.{js,ts}"}=t,s=Array.isArray(e)?e:[e],r=Array.isArray(i)?i:[i];return{name:"dlight",enforce:"pre",transform(n,o){let a=!1;for(let h of s)if(dt(o,h)){a=!0;break}for(let h of r)if(dt(o,h)){a=!1;break}if(a)return Yt(n,{babelrc:!1,configFile:!1,presets:[[lt,t]],sourceMaps:!0,filename:o})}}}export{Ke as default}; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/transpiler/vite-plugin-inula-next/dist/index.js.map b/packages/transpiler/vite-plugin-inula-next/dist/index.js.map new file mode 100644 index 00000000..82981e7c --- /dev/null +++ b/packages/transpiler/vite-plugin-inula-next/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../../../node_modules/.pnpm/@babel+helper-plugin-utils@7.24.0/node_modules/@babel/helper-plugin-utils/src/index.ts","../../../../node_modules/.pnpm/@babel+plugin-syntax-decorators@7.24.1_@babel+core@7.23.9/node_modules/@babel/plugin-syntax-decorators/src/index.ts","../src/index.ts","../../babel-preset-inula-next/src/index.ts","../../babel-preset-inula-next/src/pluginProvider.ts","../../babel-preset-inula-next/src/const.ts","../../babel-preset-inula-next/src/plugin.ts","../../error-handler/src/index.ts","../../view-parser/src/error.ts","../../view-parser/src/parser.ts","../../view-parser/src/index.ts","../../jsx-parser/src/parser.ts","../../jsx-parser/src/index.ts","../../reactivity-parser/src/error.ts","../../reactivity-parser/src/parser.ts","../../reactivity-parser/src/index.ts","../../view-generator/src/HelperGenerators/BaseGenerator.ts","../../view-generator/src/HelperGenerators/LifecycleGenerator.ts","../../view-generator/src/HelperGenerators/PropViewGenerator.ts","../../view-generator/src/HelperGenerators/ElementGenerator.ts","../../view-generator/src/HelperGenerators/ForwardPropGenerator.ts","../../view-generator/src/NodeGenerators/CompGenerator.ts","../../view-generator/src/error.ts","../../view-generator/src/HelperGenerators/HTMLPropGenerator.ts","../../view-generator/src/NodeGenerators/HTMLGenerator.ts","../../view-generator/src/NodeGenerators/TemplateGenerator.ts","../../view-generator/src/NodeGenerators/ForGenerator.ts","../../view-generator/src/HelperGenerators/CondGenerator.ts","../../view-generator/src/NodeGenerators/IfGenerator.ts","../../view-generator/src/NodeGenerators/EnvGenerator.ts","../../view-generator/src/NodeGenerators/TextGenerator.ts","../../view-generator/src/NodeGenerators/ExpGenerator.ts","../../view-generator/src/NodeGenerators/SnippetGenerator.ts","../../view-generator/src/NodeGenerators/SwitchGenerator.ts","../../view-generator/src/NodeGenerators/TryGenerator.ts","../../view-generator/src/ViewGenerator.ts","../../view-generator/src/MainViewGenerator.ts","../../view-generator/src/SnippetGenerator.ts","../../view-generator/src/index.ts","../../class-transformer/src/pluginProvider.ts","../../class-transformer/src/plugin.ts","../../class-transformer/src/index.ts"],"sourcesContent":["import type {\n PluginAPI,\n PluginObject,\n PluginPass,\n PresetAPI,\n PresetObject,\n} from \"@babel/core\";\n\ntype APIPolyfillFactory = (\n api: PluginAPI,\n) => PluginAPI[T];\n\ntype APIPolyfills = {\n assertVersion: APIPolyfillFactory<\"assertVersion\">;\n};\n\nconst apiPolyfills: APIPolyfills = {\n // Not supported by Babel 7 and early versions of Babel 7 beta.\n // It's important that this is polyfilled for older Babel versions\n // since it's needed to report the version mismatch.\n assertVersion: (api: PluginAPI) => (range: number | string) => {\n throwVersionError(range, api.version);\n },\n};\nif (!process.env.BABEL_8_BREAKING) {\n Object.assign(apiPolyfills, {\n // This is supported starting from Babel 7.13\n targets: () => () => {\n return {};\n },\n // This is supported starting from Babel 7.13\n assumption: () => () => {\n return undefined;\n },\n });\n}\n\nexport function declare(\n builder: (\n api: PluginAPI,\n options: Option,\n dirname: string,\n ) => PluginObject,\n): (\n api: PluginAPI,\n options: Option,\n dirname: string,\n) => PluginObject {\n return (api, options: Option, dirname: string) => {\n let clonedApi: PluginAPI;\n\n for (const name of Object.keys(\n apiPolyfills,\n ) as (keyof typeof apiPolyfills)[]) {\n if (api[name]) continue;\n\n clonedApi ??= copyApiObject(api);\n clonedApi[name] = apiPolyfills[name](clonedApi);\n }\n\n // @ts-expect-error options || {} may not be assigned to Options\n return builder(clonedApi ?? api, options || {}, dirname);\n };\n}\n\nexport const declarePreset = declare as
(\r\n errorSpace: string,\r\n throwMap: A = {} as any,\r\n errorMap: B = {} as any,\r\n warningMap: C = {} as any\r\n) {\r\n function handleError(map: DLightErrMap, type: string, func: (msg: string) => any) {\r\n return Object.fromEntries(\r\n Object.entries(map).map(([code, msg]) => [\r\n `${type}${code}`,\r\n (...args: string[]) => {\r\n args.forEach((arg, i) => {\r\n msg = msg.replace(`$${i}`, arg);\r\n });\r\n return func(`:D - ${errorSpace}[${type}${code}]: ${msg}`);\r\n },\r\n ])\r\n );\r\n }\r\n const methods: ErrorMethod & ErrorMethod & ErrorMethod = {\r\n ...handleError(throwMap, 'throw', msg => {\r\n throw new Error(msg);\r\n }),\r\n ...handleError(errorMap, 'error', console.error),\r\n ...handleError(warningMap, 'warn', console.warn),\r\n } as any;\r\n\r\n function notDescribed(type: string) {\r\n return () => `:D ${errorSpace}: ${type} not described`;\r\n }\r\n\r\n return {\r\n ...methods,\r\n throwUnknown: notDescribed('throw'),\r\n errorUnknown: notDescribed('error'),\r\n warnUnknown: notDescribed('warn'),\r\n };\r\n}\r\n","import { createErrorHandler } from '@inula/error-handler';\r\n\r\nexport const DLError = createErrorHandler(\r\n 'ViewParser',\r\n {\r\n 1: \"Invalid syntax in DLight's View, only accepts dot chain call expression\",\r\n 2: 'First argument of $0() must be an expression',\r\n 3: \"Invalid syntax in DLight's View, only accepts expression as props\",\r\n 4: 'Invalid Snippet calling, only accepts static snippet calling like `this.Snippet()`',\r\n },\r\n {\r\n 1: 'DLight only accepts ForOfStatement as for loop, skipping this statement',\r\n 2: 'EnvUnit must have at least one child, skipping this statement',\r\n 3: 'Only Env/Comp/HTMLUnit can have a statement block as its children, skipping this statement',\r\n 4: 'If you want to use a key in a for loop, make the first statement as a label statement like `key: item`, skipping this key for now',\r\n 5: 'ForUnit must have at least one child, skipping this statement',\r\n For: 'ForUnit must have at least one child, skipping this statement',\r\n },\r\n {\r\n 1: 'EnvUnit must have at least one prop, skipping this statement and flattening its children',\r\n }\r\n);\r\n","import { type types as t, type traverse as tr } from '@babel/core';\r\nimport { type ViewProp, type IfBranch, type ViewUnit, type ViewParserConfig, SwitchBranch } from './types';\r\nimport { DLError } from './error';\r\n\r\nexport class ViewParser {\r\n private readonly compWrapper: string = 'comp';\r\n private readonly htmlTagWrapper: string = 'tag';\r\n private readonly environmentTagName: string = 'env';\r\n private readonly expressionTagName: string = '_';\r\n\r\n private readonly config: ViewParserConfig;\r\n\r\n private readonly t: typeof t;\r\n private readonly traverse: typeof tr;\r\n private readonly snippetNames: string[];\r\n private readonly htmlTags: string[];\r\n\r\n readonly viewUnits: ViewUnit[] = [];\r\n\r\n /**\r\n * @brief Constructor\r\n * @param statement\r\n * @param config\r\n * @param options\r\n */\r\n constructor(config: ViewParserConfig) {\r\n this.config = config;\r\n this.t = config.babelApi.types;\r\n this.traverse = config.babelApi.traverse;\r\n this.snippetNames = config.snippetNames;\r\n this.htmlTags = config.htmlTags;\r\n }\r\n\r\n parse(statement: t.BlockStatement) {\r\n const statements = [...statement.directives, ...statement.body];\r\n statements.forEach(this.parseStatement.bind(this));\r\n // ---- If the view is an empty env, throw an error\r\n if (this.viewUnits.length === 1 && this.viewUnits[0].type === 'env' && this.viewUnits[0].children.length === 0) {\r\n DLError.error2();\r\n return [];\r\n }\r\n\r\n return this.viewUnits;\r\n }\r\n\r\n /**\r\n * @brief Parse the statements\r\n * @returns\r\n */\r\n private parseStatement(statement: t.Statement | t.Directive): void {\r\n if (this.isInvalidExpression(statement)) return;\r\n if (this.t.isExpressionStatement(statement)) {\r\n this.parseExpression(statement.expression);\r\n return;\r\n }\r\n if (this.t.isTryStatement(statement)) {\r\n this.parseTry(statement);\r\n return;\r\n }\r\n if (this.t.isForOfStatement(statement)) {\r\n this.parseFor(statement);\r\n return;\r\n }\r\n if (this.t.isIfStatement(statement)) {\r\n this.parseIf(statement);\r\n return;\r\n }\r\n if (this.t.isSwitchStatement(statement)) {\r\n this.parseSwitch(statement);\r\n return;\r\n }\r\n if (this.t.isDirective(statement)) {\r\n this.parseText(statement.value);\r\n return;\r\n }\r\n if (this.t.isBlockStatement(statement)) {\r\n // ---- If the statement is a block statement, treat it as last unit's children\r\n const lastViewUnit = this.viewUnits[this.viewUnits.length - 1];\r\n const type = lastViewUnit?.type;\r\n const childViewUnits = this.parseView(statement);\r\n if (type === 'html') {\r\n delete lastViewUnit.props.textContent;\r\n lastViewUnit.children.push(...childViewUnits);\r\n } else if (type === 'comp' || type === 'snippet') {\r\n lastViewUnit.children.push(...childViewUnits);\r\n } else if (type === 'env') {\r\n if (childViewUnits.length > 0) {\r\n lastViewUnit.children.push(...childViewUnits);\r\n } else {\r\n this.viewUnits.pop();\r\n DLError.error2();\r\n }\r\n } else {\r\n DLError.error3();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * @brief Parse the expression node\r\n * CallExpression -> Tag\r\n * StringLiteral/TemplateLiteral -> Text\r\n * TaggedTemplateExpression -> Tag + Text / Exp\r\n * @param expression\r\n */\r\n private parseExpression(expression: t.Expression): void {\r\n if (this.t.isCallExpression(expression)) {\r\n this.parseTag(expression);\r\n return;\r\n }\r\n if (this.t.isStringLiteral(expression) || this.t.isTemplateLiteral(expression)) {\r\n this.parseText(expression);\r\n return;\r\n }\r\n if (this.t.isTaggedTemplateExpression(expression)) {\r\n this.parseTaggedTemplate(expression);\r\n return;\r\n }\r\n\r\n // ---- Default ExpressionTag\r\n // e.g. this.count -> _(this.count)\r\n this.viewUnits.push({\r\n type: 'exp',\r\n content: this.parseProp(expression),\r\n props: {},\r\n });\r\n }\r\n\r\n /**\r\n * @brief Parse if statement conditions\r\n * @param node\r\n * @returns IfBranch[]\r\n */\r\n private parseIfBranches(node: t.IfStatement): IfBranch[] {\r\n const conditions: IfBranch[] = [];\r\n const condition = node.test;\r\n const ifBody = this.t.isBlockStatement(node.consequent)\r\n ? node.consequent\r\n : this.t.blockStatement([node.consequent]);\r\n conditions.push({\r\n condition,\r\n children: this.parseView(ifBody),\r\n });\r\n\r\n // ---- If the alternate is an if statement, parse it recursively\r\n if (this.t.isIfStatement(node.alternate)) {\r\n conditions.push(...this.parseIfBranches(node.alternate));\r\n } else if (node.alternate) {\r\n const altBody = this.t.isBlockStatement(node.alternate)\r\n ? node.alternate\r\n : this.t.blockStatement([node.alternate]);\r\n conditions.push({\r\n condition: this.t.booleanLiteral(true),\r\n children: this.parseView(altBody),\r\n });\r\n }\r\n\r\n return conditions;\r\n }\r\n\r\n /**\r\n * @brief Parse if statement with else if and else\r\n * @param node\r\n */\r\n private parseIf(node: t.IfStatement): void {\r\n this.viewUnits.push({\r\n type: 'if',\r\n branches: this.parseIfBranches(node),\r\n });\r\n }\r\n\r\n /**\r\n * @brief Parse switch statement\r\n * @param node\r\n */\r\n private parseSwitch(node: t.SwitchStatement) {\r\n const branches: SwitchBranch[] = [];\r\n const switchBody = node.cases;\r\n switchBody.forEach(s => {\r\n const caseBodyPre = s.consequent;\r\n const caseBody =\r\n caseBodyPre.length === 1 && this.t.isBlockStatement(caseBodyPre[0])\r\n ? caseBodyPre[0]\r\n : this.t.blockStatement(caseBodyPre);\r\n const isBreak = this.t.isBreakStatement(caseBody.body[caseBody.body.length - 1]);\r\n if (isBreak) {\r\n caseBody.body.pop();\r\n }\r\n\r\n const children = this.parseView(caseBody);\r\n const branch: SwitchBranch = {\r\n case: s.test ?? this.t.booleanLiteral(true),\r\n children,\r\n break: isBreak,\r\n };\r\n branches.push(branch);\r\n });\r\n this.viewUnits.push({\r\n type: 'switch',\r\n discriminant: node.discriminant,\r\n branches,\r\n });\r\n }\r\n\r\n /**\r\n * @brief Parse try statement\r\n * @param node\r\n */\r\n private parseTry(node: t.TryStatement) {\r\n const tryBody = this.t.blockStatement(node.block.body);\r\n const catchBodyNode = node.handler?.body ?? this.t.blockStatement([]);\r\n\r\n this.viewUnits.push({\r\n type: 'try',\r\n children: this.parseView(tryBody),\r\n exception: node.handler?.param ?? null,\r\n catchChildren: this.parseView(catchBodyNode),\r\n });\r\n }\r\n\r\n /**\r\n * @brief Parse for of loop\r\n * Only accept for of loop with variable declaration\r\n * e.g. for (const item of array) {}\r\n * Key:\r\n * 1. If the first statement is an array expression and is not a null or undefined,\r\n * treat the first element as the key.\r\n * e.g. for (const { idx, item } of array) { [idx]; div(item) }\r\n * key will be \"idx\"\r\n * 2. If the first statement is an array expression and is null or undefined, treat it as a non-keyed loop.\r\n * e.g. for (const { item } of array) { [null]; div(item) }\r\n * no specific key\r\n * 3. If the first statement is not an array expression, treat the item itself as the key.\r\n * e.g. for (const item of array) { div(item) }\r\n * key will be \"item\"\r\n * @param node\r\n */\r\n private parseFor(node: t.ForOfStatement): void {\r\n const left = node.left;\r\n if (!this.t.isVariableDeclaration(left)) {\r\n DLError.throw1();\r\n }\r\n const item = (left as t.VariableDeclaration).declarations[0].id;\r\n const array = node.right;\r\n let key: t.Expression = this.t.nullLiteral();\r\n const forBody = node.body;\r\n let forBodyStatements: Array;\r\n if (this.t.isExpressionStatement(forBody)) {\r\n // ---- If the for body is an expression statement, treat it as the only statement\r\n forBodyStatements = [forBody];\r\n } else if (this.t.isBlockStatement(forBody)) {\r\n const childNodes = forBody.body;\r\n if (childNodes.length === 0) return DLError.error5();\r\n const firstStatement = childNodes[0];\r\n if (this.t.isLabeledStatement(firstStatement) && this.t.isIdentifier(firstStatement.label)) {\r\n if (firstStatement.label.name !== 'key' || !this.t.isExpressionStatement(firstStatement.body)) {\r\n DLError.error4();\r\n } else {\r\n // ---- Treat the first array element labeled by key as the key\r\n const keyNode = firstStatement.body.expression;\r\n // ---- If the key is undefined or null, treat it as no key\r\n if (\r\n this.t.isExpression(keyNode) &&\r\n !(this.t.isNullLiteral(keyNode) || (this.t.isIdentifier(keyNode) && keyNode.name === 'undefined'))\r\n ) {\r\n key = keyNode;\r\n }\r\n }\r\n forBodyStatements = childNodes.slice(1);\r\n } else {\r\n forBodyStatements = childNodes;\r\n }\r\n } else return;\r\n\r\n const directives = forBodyStatements.filter(s => this.t.isDirective(s)) as t.Directive[];\r\n const statements = forBodyStatements.filter(s => !this.t.isDirective(s)) as t.Statement[];\r\n const forBodyBlockStatement = this.t.blockStatement(statements, directives);\r\n // ---- Parse the for body statements\r\n this.viewUnits.push({\r\n type: 'for',\r\n item,\r\n array,\r\n key,\r\n children: this.parseView(forBodyBlockStatement),\r\n });\r\n }\r\n\r\n /**\r\n * @brief Parse text node\r\n * 1. `text text`\r\n * 2. \"text2 text2\"\r\n * @param node\r\n */\r\n private parseText(node: t.StringLiteral | t.TemplateLiteral | t.DirectiveLiteral): void {\r\n if (this.t.isDirectiveLiteral(node)) node = this.t.stringLiteral(node.value);\r\n\r\n this.viewUnits.push({\r\n type: 'text',\r\n content: node,\r\n });\r\n }\r\n\r\n /**\r\n * @brief Parse tagged template expression\r\n * Two tagged template expression cases\r\n * 1. type without call expressions\r\n * e.g. i18n`any text`\r\n * => exp: _(i18n`any text`)\r\n * 2. type with string literal / template literal\r\n * e.g. \"any text\" `any other text`/ `any text` `any other text`\r\n * => text: \"any text\" + text: `any other text`\r\n * @param node\r\n * @param path\r\n */\r\n private parseTaggedTemplate(node: t.TaggedTemplateExpression): void {\r\n if (this.t.isStringLiteral(node.tag) || this.t.isTemplateLiteral(node.tag)) {\r\n // ---- Case 2\r\n this.viewUnits.push({\r\n type: 'text',\r\n content: node.tag,\r\n });\r\n this.viewUnits.push({\r\n type: 'text',\r\n content: node.quasi,\r\n });\r\n return;\r\n }\r\n // ---- Case 1\r\n this.viewUnits.push({\r\n type: 'exp',\r\n content: this.parseProp(node),\r\n props: {},\r\n });\r\n }\r\n\r\n /**\r\n * @brief Return a block statement if the node is a prop view\r\n * @param node\r\n * @returns\r\n */\r\n private isPropView(node: t.Node): null | t.BlockStatement {\r\n if (\r\n !(\r\n this.t.isArrowFunctionExpression(node) &&\r\n (this.t.isIdentifier(node.params[0], { name: 'View' }) ||\r\n this.t.isIdentifier(node.params[0], { name: '_View' }))\r\n )\r\n )\r\n return null;\r\n\r\n const body = node.body;\r\n if (this.t.isBlockStatement(body)) return body;\r\n return this.t.blockStatement([this.t.expressionStatement(body)]);\r\n }\r\n\r\n /**\r\n * @brief Parse props in the type node\r\n * @param propNode\r\n * @returns ViewProp\r\n */\r\n private parseProp(propNode: t.Node | undefined): ViewProp {\r\n if (propNode && !this.t.isExpression(propNode)) DLError.throw3();\r\n propNode = propNode as t.Expression;\r\n // ---- If there is no propNode, set the default prop as true\r\n if (!propNode) {\r\n return {\r\n value: this.t.booleanLiteral(true),\r\n viewPropMap: {},\r\n };\r\n }\r\n\r\n // ---- Collect View => {} nodes as DLProp\r\n const dlViewPropResult: Record = {};\r\n this.traverse(this.valueWrapper(propNode), {\r\n ArrowFunctionExpression: innerPath => {\r\n const node = this.isPropView(innerPath.node);\r\n if (!node) return;\r\n const id = this.uid();\r\n // ---- Parse the body of View => {} as a new View\r\n dlViewPropResult[id] = this.parseView(node);\r\n // ---- Replace the View => {} with a id string literal\r\n const newNode = this.t.stringLiteral(id);\r\n if (innerPath.node === propNode) {\r\n propNode = newNode;\r\n }\r\n innerPath.replaceWith(newNode);\r\n innerPath.skip();\r\n },\r\n });\r\n\r\n return {\r\n value: propNode,\r\n viewPropMap: dlViewPropResult,\r\n };\r\n }\r\n\r\n /**\r\n * @brief Parse the type node\r\n * @param node\r\n */\r\n private parseTag(node: t.CallExpression): void {\r\n const props: Record = {};\r\n\r\n // ---- Keep iterating until the node has no call expression\r\n let n = node;\r\n while (this.t.isMemberExpression(n?.callee) && n?.callee?.object && !this.isPureMemberExpression(n.callee)) {\r\n const property = n.callee.property;\r\n if (!this.t.isIdentifier(property) || !this.t.isCallExpression(n.callee.object)) {\r\n DLError.throw1();\r\n continue;\r\n }\r\n\r\n const key = property.name;\r\n const prop = this.parseProp(n.arguments[0]);\r\n props[key] = prop;\r\n n = n.callee.object;\r\n }\r\n\r\n let contentProp: ViewProp | undefined;\r\n if (n.arguments.length > 0) {\r\n // ---- The last argument is the content prop of the type,\r\n // so only parse prop when it exists instead of\r\n // treating empty prop as \"true\" like other props\r\n contentProp = this.parseProp(n.arguments[0]);\r\n }\r\n\r\n if (this.t.isIdentifier(n.callee)) {\r\n // ---- Special cases for expression type\r\n const tagName = n.callee.name;\r\n if (tagName === this.expressionTagName && contentProp) {\r\n // ---- Must have content prop or else just ignore it\r\n this.viewUnits.push({\r\n type: 'exp',\r\n content: contentProp,\r\n props,\r\n });\r\n return;\r\n }\r\n if (tagName === this.environmentTagName) {\r\n if (Object.keys(props).length === 0) {\r\n DLError.warn1();\r\n return;\r\n }\r\n this.viewUnits.push({\r\n type: 'env',\r\n props,\r\n children: [],\r\n });\r\n return;\r\n }\r\n if (this.htmlTags.includes(tagName)) {\r\n let children: ViewUnit[] = [];\r\n if (contentProp) {\r\n let isViewProp = false;\r\n if (contentProp.viewPropMap && Object.keys(contentProp.viewPropMap).length === 1) {\r\n const key = Object.keys(contentProp.viewPropMap)[0];\r\n if (this.t.isStringLiteral(contentProp.value, { value: key })) {\r\n isViewProp = true;\r\n const viewUnit = contentProp.viewPropMap[key];\r\n children = viewUnit;\r\n }\r\n }\r\n if (!isViewProp) props.textContent = contentProp;\r\n }\r\n this.viewUnits.push({\r\n type: 'html',\r\n tag: this.t.stringLiteral(tagName),\r\n props,\r\n children,\r\n });\r\n return;\r\n }\r\n // ---- Custom tag\r\n if (contentProp) props._$content = contentProp;\r\n this.viewUnits.push({\r\n type: 'comp',\r\n tag: n.callee,\r\n props,\r\n children: [],\r\n });\r\n return;\r\n }\r\n if (\r\n this.t.isMemberExpression(n.callee) &&\r\n this.t.isThisExpression(n.callee.object) &&\r\n this.t.isIdentifier(n.callee.property) &&\r\n this.snippetNames.includes(n.callee.property.name)\r\n ) {\r\n // ---- Snippet\r\n if (contentProp) props.content = contentProp;\r\n if (\r\n !(\r\n this.t.isMemberExpression(n.callee) &&\r\n this.t.isThisExpression(n.callee.object) &&\r\n this.t.isIdentifier(n.callee.property)\r\n )\r\n )\r\n return DLError.throw4();\r\n this.viewUnits.push({\r\n type: 'snippet',\r\n tag: n.callee.property.name,\r\n props,\r\n children: [],\r\n });\r\n return;\r\n }\r\n if (this.t.isExpression(n.callee)) {\r\n // ---- 1. Custom tag\r\n // 2. htmlTag(xxx)\r\n // 3. tag(xxx)\r\n const [tagType, tag] = this.alterTagType(n.callee);\r\n if (contentProp) {\r\n props[tagType === 'html' ? 'textContent' : '_$content'] = contentProp;\r\n }\r\n this.viewUnits.push({\r\n type: tagType,\r\n tag,\r\n props,\r\n children: [],\r\n });\r\n }\r\n }\r\n\r\n /* ---- Helper Functions ---- */\r\n /**\r\n * @brief Test if the node is a pure member expression without call expression\r\n * @param node\r\n */\r\n private isPureMemberExpression(node: t.Expression): boolean {\r\n let isPure = true;\r\n this.traverse(this.valueWrapper(node), {\r\n CallExpression: () => {\r\n isPure = false;\r\n },\r\n });\r\n return isPure;\r\n }\r\n\r\n /**\r\n * @brief Alter the tag type by checking the tag name\r\n * @param viewUnit\r\n * @returns [\"html\" | \"comp\", t.Expression]\r\n */\r\n private alterTagType(tag: t.Expression): ['html' | 'comp', t.Expression] {\r\n if (this.t.isCallExpression(tag) && this.t.isIdentifier(tag.callee)) {\r\n const tagName = tag.callee.name;\r\n const tagType = tagName === this.htmlTagWrapper ? 'html' : tagName === this.compWrapper ? 'comp' : undefined;\r\n if (tagType) {\r\n const tagTarget = tag.arguments[0];\r\n if (!this.t.isExpression(tagTarget)) DLError.throw2(tagName);\r\n return [tagType, tagTarget as t.Expression];\r\n }\r\n }\r\n return ['comp', tag];\r\n }\r\n\r\n /**\r\n * @brief Test if the expression is invalid\r\n * 1. For: only accept ForOfStatement\r\n * @param node\r\n * @returns is this expression invalid\r\n */\r\n private isInvalidExpression(node: t.Statement | t.Directive): boolean {\r\n const isInvalidForStatement = this.t.isForStatement(node) && !this.t.isForOfStatement(node);\r\n if (isInvalidForStatement) {\r\n DLError.error1();\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * @brief Wrap the value in a file\r\n * @param node\r\n * @returns wrapped value\r\n */\r\n private valueWrapper(node: t.Expression): t.File {\r\n return this.t.file(this.t.program([this.t.expressionStatement(node)]));\r\n }\r\n\r\n /**\r\n * @brief Parse the view by duplicating current parser's classRootPath, statements and htmlTags\r\n * @param statements\r\n * @returns ViewUnit[]\r\n */\r\n private parseView(statement: t.BlockStatement): ViewUnit[] {\r\n return new ViewParser(this.config).parse(statement);\r\n }\r\n\r\n /**\r\n * @brief Generate a unique id\r\n * @returns a unique id\r\n */\r\n private uid(): string {\r\n return Math.random().toString(36).slice(2);\r\n }\r\n}\r\n","import { ViewParser } from './parser';\nimport { type ViewUnit, type ViewParserConfig } from './types';\nimport { type types as t } from '@babel/core';\n\n/**\n * @brief Generate view units from a babel ast\n * @param statement\n * @param config\n * @param options\n * @returns ViewUnit[]\n */\nexport function parseView(statement: t.BlockStatement, config: ViewParserConfig): ViewUnit[] {\n return new ViewParser(config).parse(statement);\n}\n\nexport type * from './types';\n","import type { NodePath, types as t, traverse as tr } from '@babel/core';\r\nimport type {\r\n UnitProp,\r\n ViewUnit,\r\n ViewParserConfig,\r\n AllowedJSXNode,\r\n HTMLUnit,\r\n TextUnit,\r\n MutableUnit,\r\n TemplateProp,\r\n Context,\r\n} from './types';\r\n\r\nexport class ViewParser {\r\n // ---- Namespace and tag name\r\n private readonly htmlNamespace: string = 'html';\r\n private readonly htmlTagNamespace: string = 'tag';\r\n private readonly compTagNamespace: string = 'comp';\r\n private readonly envTagName: string = 'env';\r\n private readonly forTagName: string = 'for';\r\n private readonly ifTagName: string = 'if';\r\n private readonly elseIfTagName: string = 'else-if';\r\n private readonly elseTagName: string = 'else';\r\n private readonly customHTMLProps: string[] = ['ref'];\r\n\r\n private readonly config: ViewParserConfig;\r\n private readonly htmlTags: string[];\r\n private readonly willParseTemplate: boolean;\r\n\r\n private readonly t: typeof t;\r\n private readonly traverse: typeof tr;\r\n\r\n private readonly viewUnits: ViewUnit[] = [];\r\n private context: Context;\r\n\r\n /**\r\n * @brief Constructor\r\n * @param config\r\n * @param context\r\n */\r\n constructor(config: ViewParserConfig, context: Context = { ifElseStack: [] }) {\r\n this.config = config;\r\n this.t = config.babelApi.types;\r\n this.traverse = config.babelApi.traverse;\r\n this.htmlTags = config.htmlTags;\r\n this.willParseTemplate = config.parseTemplate ?? true;\r\n this.context = context;\r\n }\r\n\r\n /**\r\n * @brief Parse the node into view units\r\n * @param node\r\n * @returns ViewUnit[]\r\n */\r\n parse(node: AllowedJSXNode): ViewUnit[] {\r\n if (this.t.isJSXText(node)) this.parseText(node);\r\n else if (this.t.isJSXExpressionContainer(node)) this.parseExpression(node.expression);\r\n else if (this.t.isJSXElement(node)) this.parseElement(node);\r\n else if (this.t.isJSXFragment(node)) {\r\n node.children.forEach(child => {\r\n this.parse(child);\r\n });\r\n }\r\n\r\n return this.viewUnits;\r\n }\r\n\r\n /**\r\n * @brief Parse JSXText\r\n * @param node\r\n */\r\n private parseText(node: t.JSXText): void {\r\n const text = node.value.trim();\r\n if (!text) return;\r\n this.viewUnits.push({\r\n type: 'text',\r\n content: this.t.stringLiteral(node.value),\r\n });\r\n }\r\n\r\n /**\r\n * @brief Parse JSXExpressionContainer\r\n * @param node\r\n */\r\n private parseExpression(node: t.Expression | t.JSXEmptyExpression): void {\r\n if (this.t.isJSXEmptyExpression(node)) return;\r\n if (this.t.isLiteral(node) && !this.t.isTemplateLiteral(node)) {\r\n // ---- Treat literal as text except template literal\r\n // Cuz template literal may have viewProp inside like:\r\n // <>{i18n`hello ${}`}\r\n this.viewUnits.push({\r\n type: 'text',\r\n content: node,\r\n });\r\n return;\r\n }\r\n this.viewUnits.push({\r\n type: 'exp',\r\n content: this.parseProp(node),\r\n props: {},\r\n });\r\n }\r\n\r\n /**\r\n * @brief Parse JSXElement\r\n * @param node\r\n */\r\n private parseElement(node: t.JSXElement): void {\r\n let type: 'html' | 'comp';\r\n let tag: t.Expression;\r\n\r\n // ---- Parse tag and type\r\n const openingName = node.openingElement.name;\r\n if (this.t.isJSXIdentifier(openingName)) {\r\n // ---- Opening name is a JSXIdentifier, e.g.,
\r\n const name = openingName.name;\r\n // ---- Specially parse if and env\r\n if ([this.ifTagName, this.elseIfTagName, this.elseTagName].includes(name)) return this.parseIf(node);\r\n if (name === this.envTagName) return this.parseEnv(node);\r\n if (name === this.forTagName) return this.pareFor(node);\r\n else if (this.htmlTags.includes(name)) {\r\n type = 'html';\r\n tag = this.t.stringLiteral(name);\r\n } else {\r\n // ---- If the name is not in htmlTags, treat it as a comp\r\n type = 'comp';\r\n tag = this.t.identifier(name);\r\n }\r\n } else if (this.t.isJSXMemberExpression(openingName)) {\r\n // ---- Opening name is a JSXMemberExpression, e.g., \r\n // Treat it as a comp and set the tag as the opening name\r\n type = 'comp';\r\n // ---- Turn JSXMemberExpression into MemberExpression recursively\r\n const toMemberExpression = (node: t.JSXMemberExpression): t.MemberExpression => {\r\n if (this.t.isJSXMemberExpression(node.object)) {\r\n return this.t.memberExpression(toMemberExpression(node.object), this.t.identifier(node.property.name));\r\n }\r\n return this.t.memberExpression(this.t.identifier(node.object.name), this.t.identifier(node.property.name));\r\n };\r\n tag = toMemberExpression(openingName);\r\n } else {\r\n // ---- isJSXNamespacedName\r\n const namespace = openingName.namespace.name;\r\n switch (namespace) {\r\n case this.compTagNamespace:\r\n // ---- If the namespace is the same as the compTagNamespace, treat it as a comp\r\n // and set the tag as an identifier\r\n // e.g., => [\"comp\", div]\r\n // this means you've declared a component named \"div\" and force it to be a comp instead an html\r\n type = 'comp';\r\n tag = this.t.identifier(openingName.name.name);\r\n break;\r\n case this.htmlNamespace:\r\n // ---- If the namespace is the same as the htmlTagNamespace, treat it as an html\r\n // and set the tag as a string literal\r\n // e.g., => [\"html\", \"MyWebComponent\"]\r\n // the tag will be treated as a string, i.e., \r\n type = 'html';\r\n tag = this.t.stringLiteral(openingName.name.name);\r\n break;\r\n case this.htmlTagNamespace:\r\n // ---- If the namespace is the same as the htmlTagNamespace, treat it as an html\r\n // and set the tag as an identifier\r\n // e.g., => [\"html\", variable]\r\n // this unit will be htmlUnit and the html string tag is stored in \"variable\"\r\n type = 'html';\r\n tag = this.t.identifier(openingName.name.name);\r\n break;\r\n default:\r\n // ---- Otherwise, treat it as an html tag and make the tag as the namespace:name\r\n type = 'html';\r\n tag = this.t.stringLiteral(`${namespace}:${openingName.name.name}`);\r\n break;\r\n }\r\n }\r\n\r\n // ---- Parse the props\r\n const props = node.openingElement.attributes;\r\n const propMap: Record = Object.fromEntries(props.map(prop => this.parseJSXProp(prop)));\r\n\r\n // ---- Parse the children\r\n const childUnits = node.children.map(child => this.parseView(child)).flat();\r\n\r\n let unit: ViewUnit = { type, tag, props: propMap, children: childUnits };\r\n\r\n if (unit.type === 'html' && childUnits.length === 1 && childUnits[0].type === 'text') {\r\n // ---- If the html unit only has one text child, merge the text into the html unit\r\n const text = childUnits[0] as TextUnit;\r\n unit = {\r\n ...unit,\r\n children: [],\r\n props: {\r\n ...unit.props,\r\n textContent: {\r\n value: text.content,\r\n viewPropMap: {},\r\n },\r\n },\r\n };\r\n }\r\n\r\n if (unit.type === 'html') unit = this.transformTemplate(unit);\r\n\r\n this.viewUnits.push(unit);\r\n }\r\n\r\n /**\r\n * @brief Parse EnvUnit\r\n * @param node\r\n */\r\n private parseEnv(node: t.JSXElement): void {\r\n const props = node.openingElement.attributes;\r\n const propMap: Record = Object.fromEntries(props.map(prop => this.parseJSXProp(prop)));\r\n const children = node.children.map(child => this.parseView(child)).flat();\r\n this.viewUnits.push({\r\n type: 'env',\r\n props: propMap,\r\n children,\r\n });\r\n }\r\n\r\n private parseIf(node: t.JSXElement): void {\r\n const name = (node.openingElement.name as t.JSXIdentifier).name;\r\n // ---- else\r\n if (name === this.elseTagName) {\r\n const lastUnit = this.context.ifElseStack[this.context.ifElseStack.length - 1];\r\n if (!lastUnit || lastUnit.type !== 'if') throw new Error(`Missing if for ${name}`);\r\n lastUnit.branches.push({\r\n condition: this.t.booleanLiteral(true),\r\n children: node.children.map(child => this.parseView(child)).flat(),\r\n });\r\n this.context.ifElseStack.pop();\r\n return;\r\n }\r\n\r\n const condition = node.openingElement.attributes.filter(\r\n attr => this.t.isJSXAttribute(attr) && attr.name.name === 'cond'\r\n )[0];\r\n if (!condition) throw new Error(`Missing condition for ${name}`);\r\n if (!this.t.isJSXAttribute(condition)) throw new Error(`JSXSpreadAttribute is not supported for ${name} condition`);\r\n if (!this.t.isJSXExpressionContainer(condition.value) || !this.t.isExpression(condition.value.expression))\r\n throw new Error(`Invalid condition for ${name}`);\r\n\r\n // ---- if\r\n if (name === this.ifTagName) {\r\n const unit = {\r\n type: 'if' as const,\r\n branches: [\r\n {\r\n condition: condition.value.expression,\r\n children: node.children.map(child => this.parseView(child)).flat(),\r\n },\r\n ],\r\n };\r\n this.viewUnits.push(unit);\r\n this.context.ifElseStack.push(unit);\r\n return;\r\n }\r\n\r\n // ---- else-if\r\n const lastUnit = this.context.ifElseStack[this.context.ifElseStack.length - 1];\r\n if (!lastUnit || lastUnit.type !== 'if') throw new Error(`Missing if for ${name}`);\r\n\r\n lastUnit.branches.push({\r\n condition: condition.value.expression,\r\n children: node.children.map(child => this.parseView(child)).flat(),\r\n });\r\n }\r\n\r\n /**\r\n * @brief Parse JSXAttribute or JSXSpreadAttribute into UnitProp,\r\n * considering both namespace and expression\r\n * @param prop\r\n * @returns [propName, propValue]\r\n */\r\n private parseJSXProp(prop: t.JSXAttribute | t.JSXSpreadAttribute): [string, UnitProp] {\r\n if (this.t.isJSXAttribute(prop)) {\r\n let propName: string, specifier: string | undefined;\r\n if (this.t.isJSXNamespacedName(prop.name)) {\r\n // ---- If the prop name is a JSXNamespacedName, e.g., bind:value\r\n // give it a special tag\r\n propName = prop.name.name.name;\r\n specifier = prop.name.namespace.name;\r\n } else {\r\n propName = prop.name.name;\r\n }\r\n let value = this.t.isJSXExpressionContainer(prop.value) ? prop.value.expression : prop.value;\r\n if (this.t.isJSXEmptyExpression(value)) value = undefined;\r\n return [propName, this.parseProp(value, specifier)];\r\n }\r\n // ---- Use *spread* as the propName to avoid conflict with other props\r\n return ['*spread*', this.parseProp(prop.argument)];\r\n }\r\n\r\n /**\r\n * @brief Parse the prop node into UnitProp\r\n * @param propNode\r\n * @param specifier\r\n * @returns UnitProp\r\n */\r\n private parseProp(propNode: t.Expression | undefined | null, specifier?: string): UnitProp {\r\n // ---- If there is no propNode, set the default prop as true\r\n if (!propNode) {\r\n return {\r\n value: this.t.booleanLiteral(true),\r\n viewPropMap: {},\r\n };\r\n }\r\n\r\n // ---- Collect sub jsx nodes as Prop\r\n const viewPropMap: Record = {};\r\n const parseViewProp = (innerPath: NodePath): void => {\r\n const id = this.uid();\r\n const node = innerPath.node;\r\n viewPropMap[id] = this.parseView(node);\r\n const newNode = this.t.stringLiteral(id);\r\n if (node === propNode) {\r\n // ---- If the node is the propNode, replace it with the new node\r\n propNode = newNode;\r\n }\r\n // ---- Replace the node and skip the inner path\r\n innerPath.replaceWith(newNode);\r\n innerPath.skip();\r\n };\r\n\r\n // ---- Apply the parseViewProp to JSXElement and JSXFragment\r\n this.traverse(this.wrapWithFile(propNode), {\r\n JSXElement: parseViewProp,\r\n JSXFragment: parseViewProp,\r\n });\r\n\r\n return {\r\n value: propNode,\r\n viewPropMap,\r\n specifier,\r\n };\r\n }\r\n\r\n transformTemplate(unit: ViewUnit): ViewUnit {\r\n if (!this.willParseTemplate) return unit;\r\n if (!this.isHTMLTemplate(unit)) return unit;\r\n unit = unit as HTMLUnit;\r\n return {\r\n type: 'template',\r\n template: this.generateTemplate(unit),\r\n mutableUnits: this.generateMutableUnits(unit),\r\n props: this.parseTemplateProps(unit),\r\n };\r\n }\r\n\r\n /**\r\n * @brief Generate the entire HTMLUnit\r\n * @param unit\r\n * @returns HTMLUnit\r\n */\r\n private generateTemplate(unit: HTMLUnit): HTMLUnit {\r\n const staticProps = Object.fromEntries(\r\n this.filterTemplateProps(\r\n // ---- Get all the static props\r\n Object.entries(unit.props ?? []).filter(\r\n ([, prop]) =>\r\n this.isStaticProp(prop) &&\r\n // ---- Filter out props with false values\r\n !(this.t.isBooleanLiteral(prop.value) && !prop.value.value)\r\n )\r\n )\r\n );\r\n\r\n let children: (HTMLUnit | TextUnit)[] = [];\r\n if (unit.children) {\r\n children = unit.children\r\n .map(unit => {\r\n if (unit.type === 'text') return unit;\r\n if (unit.type === 'html' && this.t.isStringLiteral(unit.tag)) {\r\n return this.generateTemplate(unit);\r\n }\r\n })\r\n .filter(Boolean) as (HTMLUnit | TextUnit)[];\r\n }\r\n return {\r\n type: 'html',\r\n tag: unit.tag,\r\n props: staticProps,\r\n children,\r\n };\r\n }\r\n\r\n /**\r\n * @brief Collect all the mutable nodes in a static HTMLUnit\r\n * We use this function to collect mutable nodes' path and props,\r\n * so that in the generator, we know which position to insert the mutable nodes\r\n * @param htmlUnit\r\n * @returns mutable particles\r\n */\r\n private generateMutableUnits(htmlUnit: HTMLUnit): MutableUnit[] {\r\n const mutableUnits: MutableUnit[] = [];\r\n\r\n const generateMutableUnit = (unit: HTMLUnit, path: number[] = []) => {\r\n const maxHtmlIdx = unit.children?.filter(\r\n child => (child.type === 'html' && this.t.isStringLiteral(child.tag)) || child.type === 'text'\r\n ).length;\r\n let htmlIdx = -1;\r\n // ---- Generate mutable unit for current HTMLUnit\r\n unit.children?.forEach(child => {\r\n if (!(child.type === 'html' && this.t.isStringLiteral(child.tag)) && !(child.type === 'text')) {\r\n const idx = htmlIdx + 1 >= maxHtmlIdx ? -1 : htmlIdx + 1;\r\n mutableUnits.push({\r\n path: [...path, idx],\r\n ...this.transformTemplate(child),\r\n });\r\n } else {\r\n htmlIdx++;\r\n }\r\n });\r\n // ---- Recursively generate mutable units for static HTMLUnit children\r\n unit.children\r\n ?.filter(child => child.type === 'html' && this.t.isStringLiteral(child.tag))\r\n .forEach((child, idx) => {\r\n generateMutableUnit(child as HTMLUnit, [...path, idx]);\r\n });\r\n };\r\n generateMutableUnit(htmlUnit);\r\n\r\n return mutableUnits;\r\n }\r\n\r\n /**\r\n * @brief Collect all the props in a static HTMLUnit or its nested HTMLUnit children\r\n * Just like the mutable nodes, props are also equipped with path,\r\n * so that we know which HTML ChildNode to insert the props\r\n * @param htmlUnit\r\n * @returns props\r\n */\r\n private parseTemplateProps(htmlUnit: HTMLUnit): TemplateProp[] {\r\n const templateProps: TemplateProp[] = [];\r\n const generateVariableProp = (unit: HTMLUnit, path: number[]) => {\r\n // ---- Generate all non-static(string/number/boolean) props for current HTMLUnit\r\n // to be inserted further in the generator\r\n unit.props &&\r\n Object.entries(unit.props)\r\n .filter(([, prop]) => !this.isStaticProp(prop))\r\n .forEach(([key, prop]) => {\r\n templateProps.push({\r\n tag: unit.tag,\r\n name: (unit.tag as t.StringLiteral).value,\r\n key,\r\n path,\r\n value: prop.value,\r\n });\r\n });\r\n // ---- Recursively generate props for static HTMLUnit children\r\n unit.children\r\n ?.filter(child => child.type === 'html' && this.t.isStringLiteral(child.tag))\r\n .forEach((child, idx) => {\r\n generateVariableProp(child as HTMLUnit, [...path, idx]);\r\n });\r\n };\r\n generateVariableProp(htmlUnit, []);\r\n\r\n return templateProps;\r\n }\r\n\r\n /**\r\n * @brief Check if a ViewUnit is a static HTMLUnit that can be parsed into a template\r\n * Must satisfy:\r\n * 1. type is html\r\n * 2. tag is a string literal, i.e., non-dynamic tag\r\n * 3. has at least one child that is a static HTMLUnit,\r\n * or else just call a createElement function, no need for template clone\r\n * @param viewUnit\r\n * @returns is a static HTMLUnit\r\n */\r\n private isHTMLTemplate(viewUnit: ViewUnit): boolean {\r\n return (\r\n viewUnit.type === 'html' &&\r\n this.t.isStringLiteral(viewUnit.tag) &&\r\n !!viewUnit.children?.some(child => child.type === 'html' && this.t.isStringLiteral(child.tag))\r\n );\r\n }\r\n\r\n private isStaticProp(prop: UnitProp): boolean {\r\n return (\r\n this.t.isStringLiteral(prop.value) ||\r\n this.t.isNumericLiteral(prop.value) ||\r\n this.t.isBooleanLiteral(prop.value) ||\r\n this.t.isNullLiteral(prop.value)\r\n );\r\n }\r\n\r\n /**\r\n * @brief Filter out some props that are not needed in the template,\r\n * these are all special props to be parsed differently in the generator\r\n * @param props\r\n * @returns filtered props\r\n */\r\n private filterTemplateProps(props: Array<[string, T]>): Array<[string, T]> {\r\n return (\r\n props\r\n // ---- Filter out event listeners\r\n .filter(([key]) => !key.startsWith('on'))\r\n // ---- Filter out specific props\r\n .filter(([key]) => !this.customHTMLProps.includes(key))\r\n );\r\n }\r\n\r\n /**\r\n * @brief Parse the view by duplicating current parser's classRootPath, statements and htmlTags\r\n * @param statements\r\n * @returns ViewUnit[]\r\n */\r\n private parseView(node: AllowedJSXNode): ViewUnit[] {\r\n return new ViewParser({ ...this.config, parseTemplate: false }, this.context).parse(node);\r\n }\r\n\r\n /**\r\n * @brief Wrap the value in a file\r\n * @param node\r\n * @returns wrapped value\r\n */\r\n private wrapWithFile(node: t.Expression): t.File {\r\n return this.t.file(this.t.program([this.t.expressionStatement(node)]));\r\n }\r\n\r\n /**\r\n * @brief Generate a unique id\r\n * @returns a unique id\r\n */\r\n private uid(): string {\r\n return Math.random().toString(36).slice(2);\r\n }\r\n\r\n private findProp(node: t.JSXElement, name: string) {\r\n const props = node.openingElement.attributes;\r\n\r\n return props.find((prop): prop is t.JSXAttribute => this.t.isJSXAttribute(prop) && prop.name.name === name);\r\n }\r\n\r\n private pareFor(node: t.JSXElement) {\r\n // get prop named 'each'\r\n // get the first prop\r\n const arrayAttr = this.findProp(node, 'each');\r\n const keyAttr = this.findProp(node, 'key');\r\n\r\n if (!arrayAttr) {\r\n throw new Error('should clarify each prop for if');\r\n }\r\n let arr\r\n if (!arrayAttr.value.type !== 'JSXExpressionContainer') {\r\n throw new Error('each prop should be an expression');\r\n }\r\n arr = arrayAttr.value.expression;\r\n if(!keyAttr) {\r\n console.warn('should clarify key prop for for, to improve performance');\r\n }\r\n const item = (left as t.VariableDeclaration).declarations[0].id;\r\n const forBody = node.body;\r\n let forBodyStatements: Array;\r\n if (this.t.isExpressionStatement(forBody)) {\r\n // ---- If the for body is an expression statement, treat it as the only statement\r\n forBodyStatements = [forBody];\r\n } else if (this.t.isBlockStatement(forBody)) {\r\n const childNodes = forBody.body;\r\n if (childNodes.length === 0) return DLError.error5();\r\n const firstStatement = childNodes[0];\r\n if (this.t.isLabeledStatement(firstStatement) && this.t.isIdentifier(firstStatement.label)) {\r\n if (firstStatement.label.name !== 'key' || !this.t.isExpressionStatement(firstStatement.body)) {\r\n DLError.error4();\r\n } else {\r\n // ---- Treat the first array element labeled by key as the key\r\n const keyNode = firstStatement.body.expression;\r\n // ---- If the key is undefined or null, treat it as no key\r\n if (\r\n this.t.isExpression(keyNode) &&\r\n !(this.t.isNullLiteral(keyNode) || (this.t.isIdentifier(keyNode) && keyNode.name === 'undefined'))\r\n ) {\r\n key = keyNode;\r\n }\r\n }\r\n forBodyStatements = childNodes.slice(1);\r\n } else {\r\n forBodyStatements = childNodes;\r\n }\r\n } else return;\r\n\r\n const directives = forBodyStatements.filter(s => this.t.isDirective(s)) as t.Directive[];\r\n const statements = forBodyStatements.filter(s => !this.t.isDirective(s)) as t.Statement[];\r\n const children = node.children.map(child => this.parseView(child)).flat();\r\n // ---- Parse the for body statements\r\n this.viewUnits.push({\r\n type: 'for',\r\n item,\r\n array: array.value,\r\n key,\r\n children,\r\n });\r\n }\r\n}\r\n","import { ViewParser } from './parser';\r\nimport type { ViewUnit, ViewParserConfig, AllowedJSXNode } from './types';\r\n\r\n/**\r\n * @brief Generate view units from a babel ast\r\n * @param statement\r\n * @param config\r\n * @returns ViewUnit[]\r\n */\r\nexport function parseView(node: AllowedJSXNode, config: ViewParserConfig): ViewUnit[] {\r\n return new ViewParser(config).parse(node);\r\n}\r\n\r\nexport * from './types';\r\n","import { createErrorHandler } from '@inula/error-handler';\r\n\r\nexport const DLError = createErrorHandler('ReactivityParser', {\r\n 1: 'Invalid ViewUnit type',\r\n});\r\n","import {\r\n type TemplateProp,\r\n type ReactivityParserConfig,\r\n type MutableParticle,\r\n type ViewParticle,\r\n type TemplateParticle,\r\n type TextParticle,\r\n type HTMLParticle,\r\n type DependencyProp,\r\n type ExpParticle,\r\n type CompParticle,\r\n type ForParticle,\r\n type IfParticle,\r\n type EnvParticle,\r\n type SnippetParticle,\r\n SwitchParticle,\r\n TryParticle,\r\n} from './types';\r\nimport { type NodePath, type types as t, type traverse } from '@babel/core';\r\nimport {\r\n type TextUnit,\r\n type HTMLUnit,\r\n type ViewUnit,\r\n type CompUnit,\r\n type ViewProp,\r\n type ForUnit,\r\n type IfUnit,\r\n type EnvUnit,\r\n type ExpUnit,\r\n type SnippetUnit,\r\n SwitchUnit,\r\n TryUnit,\r\n} from '@inula/view-parser';\r\nimport { DLError } from './error';\r\n\r\nexport class ReactivityParser {\r\n private readonly config: ReactivityParserConfig;\r\n\r\n private readonly t: typeof t;\r\n private readonly traverse: typeof traverse;\r\n private readonly availableProperties: string[];\r\n private readonly availableIdentifiers?: string[];\r\n private readonly dependencyMap: Record;\r\n private readonly identifierDepMap: Record;\r\n private readonly dependencyParseType;\r\n private readonly reactivityFuncNames;\r\n\r\n private readonly escapeNamings = ['escape', '$'];\r\n private static readonly customHTMLProps = [\r\n 'didUpdate',\r\n 'willMount',\r\n 'didMount',\r\n 'willUnmount',\r\n 'didUnmount',\r\n 'element',\r\n 'innerHTML',\r\n 'props',\r\n 'attrs',\r\n 'dataset',\r\n 'forwardProps',\r\n ];\r\n\r\n readonly usedProperties = new Set();\r\n\r\n /**\r\n * @brief Constructor\r\n * @param viewUnit\r\n * @param config\r\n * @param options\r\n */\r\n constructor(config: ReactivityParserConfig) {\r\n this.config = config;\r\n this.t = config.babelApi.types;\r\n this.traverse = config.babelApi.traverse;\r\n this.availableProperties = config.availableProperties;\r\n this.availableIdentifiers = config.availableIdentifiers;\r\n this.dependencyMap = config.dependencyMap;\r\n this.identifierDepMap = config.identifierDepMap ?? {};\r\n this.dependencyParseType = config.dependencyParseType ?? 'property';\r\n this.reactivityFuncNames = config.reactivityFuncNames ?? [];\r\n }\r\n\r\n /**\r\n * @brief Parse the ViewUnit into a ViewParticle\r\n * @returns\r\n */\r\n parse(viewUnit: ViewUnit): ViewParticle {\r\n return this.parseViewUnit(viewUnit);\r\n }\r\n\r\n /**\r\n * @brief Parse a ViewUnit into a ViewParticle\r\n * @param viewUnit\r\n * @returns ViewParticle\r\n */\r\n private parseViewUnit(viewUnit: ViewUnit): ViewParticle {\r\n if (this.isHTMLTemplate(viewUnit)) return this.parseTemplate(viewUnit as HTMLUnit);\r\n if (viewUnit.type === 'text') return this.parseText(viewUnit);\r\n if (viewUnit.type === 'html') return this.parseHTML(viewUnit);\r\n if (viewUnit.type === 'comp') return this.parseComp(viewUnit);\r\n if (viewUnit.type === 'for') return this.parseFor(viewUnit);\r\n if (viewUnit.type === 'try') return this.parseTry(viewUnit);\r\n if (viewUnit.type === 'if') return this.parseIf(viewUnit);\r\n if (viewUnit.type === 'env') return this.parseEnv(viewUnit);\r\n if (viewUnit.type === 'exp') return this.parseExp(viewUnit);\r\n if (viewUnit.type === 'switch') return this.parseSwitch(viewUnit);\r\n if (viewUnit.type === 'snippet') return this.parseSnippet(viewUnit);\r\n return DLError.throw1();\r\n }\r\n\r\n // ---- Parsers ----\r\n // ---- @Template ----\r\n /**\r\n * @brief Collect static HTMLUnit into a template particle and generate a template string\r\n * MutableParticle means whatever unit that is not a static HTMLUnit or a TextUnit\r\n * Props means all the non-static props of the nested HTMLUnit or TextUnit, e.g. div().className(this.name)\r\n * @param htmlUnit\r\n * @returns TemplateParticle\r\n */\r\n private parseTemplate(htmlUnit: HTMLUnit): TemplateParticle {\r\n return {\r\n type: 'template',\r\n template: this.generateTemplate(htmlUnit),\r\n props: this.parseTemplateProps(htmlUnit),\r\n mutableParticles: this.generateMutableParticles(htmlUnit),\r\n };\r\n }\r\n\r\n /**\r\n * @brief Generate a template\r\n * There'll be a situation where the tag is dynamic, e.g. tag(this.htmlTag),\r\n * which we can't generate a template string for it, so we'll wrap it in an ExpParticle in parseHTML() section\r\n * @param htmlUnit\r\n * @returns template string\r\n */\r\n private generateTemplate(unit: HTMLUnit): HTMLParticle {\r\n const staticProps = this.filterTemplateProps(\r\n // ---- Get all the static props\r\n Object.entries(unit.props).filter(\r\n ([, prop]) =>\r\n this.isStaticProp(prop) &&\r\n // ---- Filter out props with false values\r\n !(this.t.isBooleanLiteral(prop.value) && !prop.value.value)\r\n )\r\n ).map(([key, prop]) => [\r\n key,\r\n {\r\n ...prop,\r\n dependencyIndexArr: [],\r\n dependenciesNode: this.t.arrayExpression([]),\r\n dynamic: false,\r\n },\r\n ]);\r\n\r\n let children: ViewParticle[] = [];\r\n if (!unit.props.textContent) {\r\n children = unit.children\r\n .map(unit => {\r\n if (unit.type === 'html' && this.t.isStringLiteral(unit.tag)) {\r\n return this.generateTemplate(unit);\r\n }\r\n if (unit.type === 'text' && this.t.isStringLiteral(unit.content)) {\r\n return this.parseText(unit);\r\n }\r\n })\r\n .filter(Boolean) as HTMLParticle[];\r\n }\r\n return {\r\n type: 'html',\r\n tag: unit.tag,\r\n props: Object.fromEntries(staticProps),\r\n children,\r\n };\r\n }\r\n\r\n /**\r\n * @brief Collect all the mutable nodes in a static HTMLUnit\r\n * We use this function to collect mutable nodes' path and props,\r\n * so that in the generator, we know which position to insert the mutable nodes\r\n * @param htmlUnit\r\n * @returns mutable particles\r\n */\r\n private generateMutableParticles(htmlUnit: HTMLUnit): MutableParticle[] {\r\n const mutableParticles: MutableParticle[] = [];\r\n const generateMutableUnit = (unit: HTMLUnit, path: number[] = []) => {\r\n // ---- Generate mutable particles for current HTMLUnit\r\n unit.children?.forEach((child, idx) => {\r\n if (\r\n !(child.type === 'html' && this.t.isStringLiteral(child.tag)) &&\r\n !(child.type === 'text' && this.t.isStringLiteral(child.content))\r\n ) {\r\n mutableParticles.push({\r\n path: [...path, idx],\r\n ...this.parseViewParticle(child),\r\n });\r\n }\r\n });\r\n // ---- Recursively generate mutable particles for static HTMLUnit children\r\n unit.children\r\n ?.filter(child => child.type === 'html' && this.t.isStringLiteral(child.tag))\r\n .forEach((child, idx) => {\r\n generateMutableUnit(child as HTMLUnit, [...path, idx]);\r\n });\r\n };\r\n generateMutableUnit(htmlUnit);\r\n\r\n return mutableParticles;\r\n }\r\n\r\n /**\r\n * @brief Collect all the props in a static HTMLUnit or its nested HTMLUnit or TextUnit children\r\n * Just like the mutable nodes, props are also equipped with path,\r\n * so that we know which HTML ChildNode to insert the props\r\n * @param htmlUnit\r\n * @returns props\r\n */\r\n private parseTemplateProps(htmlUnit: HTMLUnit): TemplateProp[] {\r\n const templateProps: TemplateProp[] = [];\r\n const generateVariableProp = (unit: HTMLUnit, path: number[]) => {\r\n // ---- Generate all non-static(string/number/boolean) props for current HTMLUnit\r\n // to be inserted further in the generator\r\n Object.entries(unit.props)\r\n .filter(([, prop]) => !this.isStaticProp(prop))\r\n .forEach(([key, prop]) => {\r\n templateProps.push({\r\n tag: (unit.tag as t.StringLiteral).value,\r\n key,\r\n path,\r\n value: prop.value,\r\n ...this.getDependencies(prop.value),\r\n });\r\n });\r\n // ---- Recursively generate props for static HTMLUnit children\r\n unit.children\r\n .filter(\r\n child =>\r\n (child.type === 'html' && this.t.isStringLiteral(child.tag)) ||\r\n (child.type === 'text' && this.t.isStringLiteral(child.content))\r\n )\r\n .forEach((child, idx) => {\r\n if (child.type === 'html') {\r\n generateVariableProp(child, [...path, idx]);\r\n } else if (child.type === 'text') {\r\n // ---- if the child is a TextUnit, we just insert the text content\r\n templateProps.push({\r\n tag: 'text',\r\n key: 'value',\r\n path: [...path, idx],\r\n value: child.content,\r\n dependencyIndexArr: [],\r\n dependenciesNode: this.t.arrayExpression([]),\r\n dynamic: false,\r\n });\r\n }\r\n });\r\n };\r\n generateVariableProp(htmlUnit, []);\r\n\r\n return templateProps;\r\n }\r\n\r\n // ---- @Text ----\r\n /**\r\n * @brief Parse a TextUnit into a TextParticle.\r\n * This is only for a top level TextUnit, because if nested in HTMLUnit, it'll be parsed in the template string\r\n * @param textUnit\r\n * @returns TextParticle\r\n */\r\n private parseText(textUnit: TextUnit): TextParticle {\r\n return {\r\n type: 'text',\r\n content: {\r\n value: textUnit.content,\r\n ...this.getDependencies(textUnit.content),\r\n },\r\n };\r\n }\r\n\r\n // ---- @HTML ----\r\n /**\r\n * @brief Parse an HTMLUnit with a dynamic tag into an ExpParticle or an HTMLParticle\r\n * We detect dependencies in the tag, if there's no dependency,\r\n * we parse it as an HTMLParticle and dynamically append it to the parent node;\r\n * if there's dependency, we parse it as an ExpParticle and wrap it in an ExpParticle\r\n * so that we can make the tag reactive\r\n * @param htmlUnit\r\n * @returns ExpParticle | HTMLParticle\r\n */\r\n private parseHTML(htmlUnit: HTMLUnit): ExpParticle | HTMLParticle {\r\n const { dependencyIndexArr, dependenciesNode, dynamic } = this.getDependencies(htmlUnit.tag);\r\n\r\n const innerHTMLParticle: HTMLParticle = {\r\n type: 'html',\r\n tag: htmlUnit.tag,\r\n props: {},\r\n children: [],\r\n };\r\n\r\n innerHTMLParticle.props = Object.fromEntries(\r\n Object.entries(htmlUnit.props).map(([key, prop]) => [key, this.generateDependencyProp(prop)])\r\n );\r\n\r\n innerHTMLParticle.children = htmlUnit.children.map(this.parseViewParticle.bind(this));\r\n\r\n // ---- Not a dynamic tag\r\n if (!dynamic) return innerHTMLParticle;\r\n\r\n // ---- Dynamic tag, wrap it in an ExpParticle to make the tag reactive\r\n const id = this.uid();\r\n return {\r\n type: 'exp',\r\n content: {\r\n value: this.t.stringLiteral(id),\r\n viewPropMap: {\r\n [id]: [innerHTMLParticle],\r\n },\r\n dependencyIndexArr,\r\n dependenciesNode,\r\n dynamic,\r\n },\r\n props: {},\r\n };\r\n }\r\n\r\n // ---- @Comp ----\r\n /**\r\n * @brief Parse a CompUnit into a CompParticle or an ExpParticle\r\n * Similar to parseHTML(), we detect dependencies in the tag, if there's no dependency,\r\n * we parse it as a regular CompParticle, otherwise we wrap it with an ExpParticle.\r\n * @param compUnit\r\n * @returns CompParticle | ExpParticle\r\n */\r\n private parseComp(compUnit: CompUnit): CompParticle | ExpParticle {\r\n const { dependencyIndexArr, dependenciesNode, dynamic } = this.getDependencies(compUnit.tag);\r\n\r\n const compParticle: CompParticle = {\r\n type: 'comp',\r\n tag: compUnit.tag,\r\n props: {},\r\n children: [],\r\n };\r\n\r\n compParticle.props = Object.fromEntries(\r\n Object.entries(compUnit.props).map(([key, prop]) => [key, this.generateDependencyProp(prop)])\r\n );\r\n compParticle.children = compUnit.children.map(this.parseViewParticle.bind(this));\r\n\r\n if (!dynamic) return compParticle;\r\n\r\n const id = this.uid();\r\n return {\r\n type: 'exp',\r\n content: {\r\n value: this.t.stringLiteral(id),\r\n viewPropMap: {\r\n [id]: [compParticle],\r\n },\r\n dependencyIndexArr,\r\n dependenciesNode,\r\n dynamic,\r\n },\r\n props: {},\r\n };\r\n }\r\n\r\n // ---- @For ----\r\n /**\r\n * @brief Parse a ForUnit into a ForParticle with dependencies\r\n * Key and item doesn't need to be reactive, so here we don't collect dependencies for it\r\n * @param forUnit\r\n * @returns ForParticle\r\n */\r\n private parseFor(forUnit: ForUnit): ForParticle {\r\n const { dependencyIndexArr, dependenciesNode, dynamic } = this.getDependencies(forUnit.array);\r\n const prevIdentifierDepMap = this.config.identifierDepMap;\r\n // ---- Find all the identifiers in the key and remove them from the identifierDepMap\r\n // because once the key is changed, that identifier related dependencies will be changed too,\r\n // so no need to update them\r\n const keyDep = this.t.isIdentifier(forUnit.key) && forUnit.key.name;\r\n // ---- Generate an identifierDepMap to track identifiers in item and make them reactive\r\n // based on the dependencies from the array\r\n this.config.identifierDepMap = Object.fromEntries(\r\n this.getIdentifiers(this.t.assignmentExpression('=', forUnit.item, this.t.objectExpression([])))\r\n .filter(id => !keyDep || id !== keyDep)\r\n .map(id => [id, dependencyIndexArr.map(n => this.availableProperties[n])])\r\n );\r\n\r\n const forParticle: ForParticle = {\r\n type: 'for',\r\n item: forUnit.item,\r\n array: {\r\n value: forUnit.array,\r\n dynamic,\r\n dependencyIndexArr,\r\n dependenciesNode,\r\n },\r\n children: forUnit.children.map(this.parseViewParticle.bind(this)),\r\n key: forUnit.key,\r\n };\r\n this.config.identifierDepMap = prevIdentifierDepMap;\r\n return forParticle;\r\n }\r\n\r\n // ---- @If ----\r\n /**\r\n * @brief Parse an IfUnit into an IfParticle with dependencies\r\n * @param ifUnit\r\n * @returns IfParticle\r\n */\r\n private parseIf(ifUnit: IfUnit): IfParticle {\r\n return {\r\n type: 'if',\r\n branches: ifUnit.branches.map(branch => ({\r\n condition: {\r\n value: branch.condition,\r\n ...this.getDependencies(branch.condition),\r\n },\r\n children: branch.children.map(this.parseViewParticle.bind(this)),\r\n })),\r\n };\r\n }\r\n\r\n // ---- @Switch ----\r\n /**\r\n * @brief Parse a SwitchUnit into an SwitchParticle with dependencies\r\n * @param switchUnit\r\n * @returns SwitchParticle\r\n */\r\n private parseSwitch(switchUnit: SwitchUnit): SwitchParticle {\r\n return {\r\n type: 'switch',\r\n discriminant: {\r\n value: switchUnit.discriminant,\r\n ...this.getDependencies(switchUnit.discriminant),\r\n },\r\n branches: switchUnit.branches.map(branch => ({\r\n case: {\r\n value: branch.case,\r\n ...this.getDependencies(branch.case),\r\n },\r\n children: branch.children.map(this.parseViewParticle.bind(this)),\r\n break: branch.break,\r\n })),\r\n };\r\n }\r\n\r\n // ---- @Try ----\r\n /**\r\n * @brief Parse a TryUnit into an TryParticle with dependencies\r\n * @param tryUnit\r\n * @returns TryParticle\r\n */\r\n private parseTry(tryUnit: TryUnit): TryParticle {\r\n return {\r\n type: 'try',\r\n children: tryUnit.children.map(this.parseViewParticle.bind(this)),\r\n exception: tryUnit.exception,\r\n catchChildren: tryUnit.catchChildren.map(this.parseViewParticle.bind(this)),\r\n };\r\n }\r\n\r\n // ---- @Env ----\r\n /**\r\n * @brief Parse an EnvUnit into an EnvParticle with dependencies\r\n * @param envUnit\r\n * @returns EnvParticle\r\n */\r\n private parseEnv(envUnit: EnvUnit): EnvParticle {\r\n return {\r\n type: 'env',\r\n props: Object.fromEntries(\r\n Object.entries(envUnit.props).map(([key, prop]) => [key, this.generateDependencyProp(prop)])\r\n ),\r\n children: envUnit.children.map(this.parseViewParticle.bind(this)),\r\n };\r\n }\r\n\r\n // ---- @Exp ----\r\n /**\r\n * @brief Parse an ExpUnit into an ExpParticle with dependencies\r\n * @param expUnit\r\n * @returns ExpParticle\r\n */\r\n private parseExp(expUnit: ExpUnit): ExpParticle {\r\n const expParticle: ExpParticle = {\r\n type: 'exp',\r\n content: this.generateDependencyProp(expUnit.content),\r\n props: Object.fromEntries(\r\n Object.entries(expUnit.props).map(([key, prop]) => [key, this.generateDependencyProp(prop)])\r\n ),\r\n };\r\n return expParticle;\r\n }\r\n\r\n // ---- @Snippet ----\r\n /**\r\n * @brief Parse a SnippetUnit into a SnippetParticle with dependencies\r\n * @param snippetUnit\r\n * @returns SnippetParticle\r\n */\r\n private parseSnippet(snippetUnit: SnippetUnit): SnippetParticle {\r\n const snippetParticle: SnippetParticle = {\r\n type: 'snippet',\r\n tag: snippetUnit.tag,\r\n props: {},\r\n children: [],\r\n };\r\n if (snippetUnit.props) {\r\n snippetParticle.props = Object.fromEntries(\r\n Object.entries(snippetUnit.props).map(([key, prop]) => [key, this.generateDependencyProp(prop)])\r\n );\r\n }\r\n if (snippetUnit.children) {\r\n snippetParticle.children = snippetUnit.children.map(this.parseViewParticle.bind(this));\r\n }\r\n\r\n return snippetParticle;\r\n }\r\n\r\n // ---- Dependencies ----\r\n /**\r\n * @brief Generate a dependency prop with dependencies\r\n * @param prop\r\n * @returns DependencyProp\r\n */\r\n private generateDependencyProp(prop: ViewProp): DependencyProp {\r\n const dependencyProp: DependencyProp = {\r\n value: prop.value,\r\n ...this.getDependencies(prop.value),\r\n viewPropMap: Object.fromEntries(\r\n Object.entries(prop.viewPropMap).map(([key, units]) => [key, units.map(this.parseViewParticle.bind(this))])\r\n ),\r\n };\r\n return dependencyProp;\r\n }\r\n\r\n /**\r\n * @brief Get all the dependencies of a node\r\n * this.dependencyParseType controls how we parse the dependencies\r\n * 1. property: parse the dependencies of a node as a property, e.g. this.name\r\n * 2. identifier: parse the dependencies of a node as an identifier, e.g. name\r\n * The availableProperties is the list of all the properties that can be used in the template,\r\n * no matter it's a property or an identifier\r\n * @param node\r\n * @returns dependency index array\r\n */\r\n private getDependencies(node: t.Expression | t.Statement): {\r\n dynamic: boolean;\r\n dependencyIndexArr: number[];\r\n dependenciesNode: t.ArrayExpression;\r\n } {\r\n if (this.t.isFunctionExpression(node) || this.t.isArrowFunctionExpression(node)) {\r\n return {\r\n dynamic: false,\r\n dependencyIndexArr: [],\r\n dependenciesNode: this.t.arrayExpression([]),\r\n };\r\n }\r\n // ---- Both id and prop deps need to be calculated because\r\n // id is for snippet update, prop is normal update\r\n // in a snippet, the depsNode should be both id and prop\r\n const [directIdentifierDeps, identifierDepNodes] = this.getIdentifierDependencies(node);\r\n const [directPropertyDeps, propertyDepNodes] = this.getPropertyDependencies(node);\r\n const directDependencies = this.dependencyParseType === 'identifier' ? directIdentifierDeps : directPropertyDeps;\r\n const identifierMapDependencies = this.getIdentifierMapDependencies(node);\r\n const deps = [...new Set([...directDependencies, ...identifierMapDependencies])];\r\n\r\n const depNodes = [...identifierDepNodes, ...propertyDepNodes] as t.Expression[];\r\n\r\n return {\r\n dynamic: depNodes.length > 0 || deps.length > 0,\r\n dependencyIndexArr: deps,\r\n dependenciesNode: this.t.arrayExpression(depNodes),\r\n };\r\n }\r\n\r\n /**\r\n * @brief Get all the dependencies of a node if a property is a valid dependency as\r\n * 1. the identifier is in the availableProperties\r\n * 2. the identifier is a stand alone identifier\r\n * 3. the identifier is not in an escape function\r\n * 4. the identifier is not in a manual function\r\n * 5. the identifier is not the left side of an assignment expression, which is an assignment expression\r\n * 6. the identifier is not the right side of an assignment expression, which is an update expression\r\n * @param node\r\n * @returns dependency index array\r\n */\r\n private getIdentifierDependencies(node: t.Expression | t.Statement): [number[], t.Node[]] {\r\n const availableIdentifiers = this.availableIdentifiers ?? this.availableProperties;\r\n\r\n const deps = new Set();\r\n const assignDeps = new Set();\r\n const depNodes: Record = {};\r\n\r\n const wrappedNode = this.valueWrapper(node);\r\n this.traverse(wrappedNode, {\r\n Identifier: innerPath => {\r\n const identifier = innerPath.node;\r\n const idName = identifier.name;\r\n if (!availableIdentifiers.includes(idName)) return;\r\n if (this.isAssignmentExpressionLeft(innerPath) || this.isAssignmentFunction(innerPath)) {\r\n assignDeps.add(idName);\r\n } else if (\r\n this.isStandAloneIdentifier(innerPath) &&\r\n !this.isMemberInEscapeFunction(innerPath) &&\r\n !this.isMemberInManualFunction(innerPath)\r\n ) {\r\n deps.add(idName);\r\n this.dependencyMap[idName]?.forEach(deps.add.bind(deps));\r\n if (!depNodes[idName]) depNodes[idName] = [];\r\n depNodes[idName].push(this.geneDependencyNode(innerPath));\r\n }\r\n },\r\n });\r\n\r\n assignDeps.forEach(dep => {\r\n deps.delete(dep);\r\n delete depNodes[dep];\r\n });\r\n let dependencyNodes = Object.values(depNodes).flat();\r\n // ---- deduplicate the dependency nodes\r\n dependencyNodes = dependencyNodes.filter((n, i) => {\r\n const idx = dependencyNodes.findIndex(m => this.t.isNodesEquivalent(m, n));\r\n return idx === i;\r\n });\r\n\r\n deps.forEach(this.usedProperties.add.bind(this.usedProperties));\r\n return [[...deps].map(dep => this.availableProperties.indexOf(dep)), dependencyNodes];\r\n }\r\n\r\n /**\r\n * @brief Get all the dependencies of a node if a member expression is a valid dependency as\r\n * 1. the property is in the availableProperties\r\n * 2. the object is this\r\n * 3. the member expression is not in an escape function\r\n * 4. the member expression is not in a manual function\r\n * 5. the member expression is not the left side of an assignment expression, which is an assignment expression\r\n * 6. the member is not a pure function declaration\r\n * @param node\r\n * @returns dependency index array\r\n */\r\n private getPropertyDependencies(node: t.Expression | t.Statement): [number[], t.Node[]] {\r\n const deps = new Set();\r\n const assignDeps = new Set();\r\n const depNodes: Record = {};\r\n\r\n const wrappedNode = this.valueWrapper(node);\r\n this.traverse(wrappedNode, {\r\n MemberExpression: innerPath => {\r\n if (!this.t.isIdentifier(innerPath.node.property) || !this.t.isThisExpression(innerPath.node.object)) return;\r\n const propertyKey = innerPath.node.property.name;\r\n if (this.isAssignmentExpressionLeft(innerPath) || this.isAssignmentFunction(innerPath)) {\r\n assignDeps.add(propertyKey);\r\n } else if (\r\n this.availableProperties.includes(propertyKey) &&\r\n !this.isMemberInEscapeFunction(innerPath) &&\r\n !this.isMemberInManualFunction(innerPath)\r\n ) {\r\n deps.add(propertyKey);\r\n this.dependencyMap[propertyKey]?.forEach(deps.add.bind(deps));\r\n if (!depNodes[propertyKey]) depNodes[propertyKey] = [];\r\n depNodes[propertyKey].push(this.geneDependencyNode(innerPath));\r\n }\r\n },\r\n });\r\n\r\n assignDeps.forEach(dep => {\r\n deps.delete(dep);\r\n delete depNodes[dep];\r\n });\r\n let dependencyNodes = Object.values(depNodes).flat();\r\n // ---- deduplicate the dependency nodes\r\n dependencyNodes = dependencyNodes.filter((n, i) => {\r\n const idx = dependencyNodes.findIndex(m => this.t.isNodesEquivalent(m, n));\r\n return idx === i;\r\n });\r\n\r\n deps.forEach(this.usedProperties.add.bind(this.usedProperties));\r\n return [[...deps].map(dep => this.availableProperties.indexOf(dep)), dependencyNodes];\r\n }\r\n\r\n /**\r\n * @brief Generate a dependency node from a dependency identifier,\r\n * loop until the parent node is not a binary expression or a member expression\r\n * @param path\r\n * @returns\r\n */\r\n private geneDependencyNode(path: NodePath): t.Node {\r\n let parentPath = path;\r\n while (parentPath?.parentPath) {\r\n const pParentPath = parentPath.parentPath;\r\n if (\r\n !(\r\n this.t.isMemberExpression(pParentPath.node, { computed: false }) ||\r\n this.t.isOptionalMemberExpression(pParentPath.node)\r\n )\r\n ) {\r\n break;\r\n }\r\n parentPath = pParentPath;\r\n }\r\n const depNode = this.t.cloneNode(parentPath.node);\r\n // ---- Turn memberExpression to optionalMemberExpression\r\n this.traverse(this.valueWrapper(depNode as t.Expression), {\r\n MemberExpression: innerPath => {\r\n if (this.t.isThisExpression(innerPath.node.object)) return;\r\n innerPath.node.optional = true;\r\n innerPath.node.type = 'OptionalMemberExpression' as any;\r\n },\r\n });\r\n return depNode;\r\n }\r\n\r\n /**\r\n * @brief Get dependencies from the identifierDepMap\r\n * e.g.\r\n * map: { \"a\": [\"dep1\", \"dep2\"] }\r\n * expression: const b = a\r\n * deps for b: [\"dep1\", \"dep2\"]\r\n * @param node\r\n * @returns dependency index array\r\n */\r\n private getIdentifierMapDependencies(node: t.Expression | t.Statement): number[] {\r\n const deps = new Set();\r\n\r\n const wrappedNode = this.valueWrapper(node);\r\n this.traverse(wrappedNode, {\r\n Identifier: innerPath => {\r\n const identifier = innerPath.node;\r\n const idName = identifier.name;\r\n if (this.isAttrFromFunction(innerPath, idName)) return;\r\n const depsArray = this.identifierDepMap[idName];\r\n\r\n if (!depsArray || !Array.isArray(depsArray)) return;\r\n if (this.isMemberInEscapeFunction(innerPath) || this.isMemberInManualFunction(innerPath)) return;\r\n depsArray.forEach(deps.add.bind(deps));\r\n },\r\n });\r\n\r\n deps.forEach(this.usedProperties.add.bind(this.usedProperties));\r\n return [...deps].map(dep => this.availableProperties.indexOf(dep));\r\n }\r\n\r\n // ---- Utils ----\r\n /**\r\n * @brief Parse a ViewUnit into a ViewParticle by new-ing a ReactivityParser\r\n * @param viewUnit\r\n * @returns ViewParticle\r\n */\r\n private parseViewParticle(viewUnit: ViewUnit): ViewParticle {\r\n const parser = new ReactivityParser(this.config);\r\n const parsedUnit = parser.parse(viewUnit);\r\n // ---- Collect used properties\r\n parser.usedProperties.forEach(this.usedProperties.add.bind(this.usedProperties));\r\n return parsedUnit;\r\n }\r\n\r\n /**\r\n * @brief Check if a ViewUnit is a static HTMLUnit that can be parsed into a template\r\n * Must satisfy:\r\n * 1. type is html\r\n * 2. tag is a string literal, i.e., non-dynamic tag\r\n * 3. has at least one child that is a static HTMLUnit,\r\n * or else just call a createElement function, no need for template clone\r\n * @param viewUnit\r\n * @returns is a static HTMLUnit\r\n */\r\n private isHTMLTemplate(viewUnit: ViewUnit): boolean {\r\n return (\r\n viewUnit.type === 'html' &&\r\n this.t.isStringLiteral(viewUnit.tag) &&\r\n !!viewUnit.children?.some(child => child.type === 'html' && this.t.isStringLiteral(child.tag))\r\n );\r\n }\r\n\r\n /**\r\n * @brief Check if a prop is a static prop\r\n * i.e.\r\n * 1. no viewPropMap\r\n * 2. value is a string/number/boolean literal\r\n * @param prop\r\n * @returns is a static prop\r\n */\r\n private isStaticProp(prop: ViewProp): boolean {\r\n const { value, viewPropMap } = prop;\r\n return (\r\n (!viewPropMap || Object.keys(viewPropMap).length === 0) &&\r\n (this.t.isStringLiteral(value) || this.t.isNumericLiteral(value) || this.t.isBooleanLiteral(value))\r\n );\r\n }\r\n\r\n /**\r\n * @brief Filter out some props that are not needed in the template,\r\n * these are all special props to be parsed differently in the generator\r\n * @param props\r\n * @returns filtered props\r\n */\r\n private filterTemplateProps(props: Array<[string, T]>): Array<[string, T]> {\r\n return (\r\n props\r\n // ---- Filter out event listeners\r\n .filter(([key]) => !key.startsWith('on'))\r\n // ---- Filter out specific props\r\n .filter(([key]) => !ReactivityParser.customHTMLProps.includes(key))\r\n );\r\n }\r\n\r\n /**\r\n * @brief Wrap the value in a file\r\n * @param node\r\n * @returns wrapped value\r\n */\r\n private valueWrapper(node: t.Expression | t.Statement): t.File {\r\n return this.t.file(this.t.program([this.t.isStatement(node) ? node : this.t.expressionStatement(node)]));\r\n }\r\n\r\n /**\r\n * @brief Check if an identifier is a simple stand alone identifier,\r\n * i.e., not a member expression, nor a function param\r\n * @param path\r\n * 1. not a member expression\r\n * 2. not a function param\r\n * 3. not in a declaration\r\n * 4. not as object property's not computed key\r\n * @returns is a stand alone identifier\r\n */\r\n private isStandAloneIdentifier(path: NodePath): boolean {\r\n const node = path.node;\r\n const parentNode = path.parentPath?.node;\r\n const isMemberExpression = this.t.isMemberExpression(parentNode) && parentNode.property === node;\r\n if (isMemberExpression) return false;\r\n const isFunctionParam = this.isAttrFromFunction(path, node.name);\r\n if (isFunctionParam) return false;\r\n while (path.parentPath) {\r\n if (this.t.isVariableDeclarator(path.parentPath.node)) return false;\r\n if (\r\n this.t.isObjectProperty(path.parentPath.node) &&\r\n path.parentPath.node.key === path.node &&\r\n !path.parentPath.node.computed\r\n )\r\n return false;\r\n path = path.parentPath as NodePath;\r\n }\r\n return true;\r\n }\r\n\r\n /**\r\n * @brief Get all identifiers as strings in a node\r\n * @param node\r\n * @returns identifiers\r\n */\r\n private getIdentifiers(node: t.Node): string[] {\r\n if (this.t.isIdentifier(node)) return [node.name];\r\n const identifierKeys = new Set();\r\n this.traverse(this.valueWrapper(node as t.Expression), {\r\n Identifier: innerPath => {\r\n if (!this.isStandAloneIdentifier(innerPath)) return;\r\n identifierKeys.add(innerPath.node.name);\r\n },\r\n });\r\n return [...identifierKeys];\r\n }\r\n\r\n /**\r\n * @brief check if the identifier is from a function param till the stopNode\r\n * e.g:\r\n * function myFunc1(ok) { // stopNode = functionBody\r\n * const myFunc2 = ok => ok // from function param\r\n * console.log(ok) // not from function param\r\n * }\r\n */\r\n private isAttrFromFunction(path: NodePath, idName: string) {\r\n let reversePath = path.parentPath;\r\n\r\n const checkParam: (param: t.Node) => boolean = (param: t.Node) => {\r\n // ---- 3 general types:\r\n // * represent allow nesting\r\n // ---0 Identifier: (a)\r\n // ---1 RestElement: (...a) *\r\n // ---1 Pattern: 3 sub Pattern\r\n // -----0 AssignmentPattern: (a=1) *\r\n // -----1 ArrayPattern: ([a, b]) *\r\n // -----2 ObjectPattern: ({a, b})\r\n if (this.t.isIdentifier(param)) return param.name === idName;\r\n if (this.t.isAssignmentPattern(param)) return checkParam(param.left);\r\n if (this.t.isArrayPattern(param)) {\r\n return param.elements\r\n .filter(Boolean)\r\n .map(el => checkParam(el!))\r\n .includes(true);\r\n }\r\n if (this.t.isObjectPattern(param)) {\r\n return (\r\n param.properties.filter(\r\n prop => this.t.isObjectProperty(prop) && this.t.isIdentifier(prop.key)\r\n ) as t.ObjectProperty[]\r\n )\r\n .map(prop => (prop.key as t.Identifier).name)\r\n .includes(idName);\r\n }\r\n if (this.t.isRestElement(param)) return checkParam(param.argument);\r\n\r\n return false;\r\n };\r\n\r\n while (reversePath) {\r\n const node = reversePath.node;\r\n if (this.t.isArrowFunctionExpression(node) || this.t.isFunctionDeclaration(node)) {\r\n for (const param of node.params) {\r\n if (checkParam(param)) return true;\r\n }\r\n }\r\n reversePath = reversePath.parentPath;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n /**\r\n * @brief Check if it's the left side of an assignment expression, e.g. this.count = 1\r\n * @param innerPath\r\n * @returns is left side of an assignment expression\r\n */\r\n private isAssignmentExpressionLeft(innerPath: NodePath): boolean {\r\n let parentPath = innerPath.parentPath;\r\n while (parentPath && !this.t.isStatement(parentPath.node)) {\r\n if (this.t.isAssignmentExpression(parentPath.node)) {\r\n if (parentPath.node.left === innerPath.node) return true;\r\n const leftPath = parentPath.get('left') as NodePath;\r\n if (innerPath.isDescendant(leftPath)) return true;\r\n } else if (this.t.isUpdateExpression(parentPath.node)) {\r\n return true;\r\n }\r\n parentPath = parentPath.parentPath;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n /**\r\n * @brief Check if it's a reactivity function, e.g. arr.push\r\n * @param innerPath\r\n * @returns\r\n */\r\n private isAssignmentFunction(innerPath: NodePath): boolean {\r\n let parentPath = innerPath.parentPath;\r\n\r\n while (parentPath && this.t.isMemberExpression(parentPath.node)) {\r\n parentPath = parentPath.parentPath;\r\n }\r\n if (!parentPath) return false;\r\n return (\r\n this.t.isCallExpression(parentPath.node) &&\r\n this.t.isMemberExpression(parentPath.node.callee) &&\r\n this.t.isIdentifier(parentPath.node.callee.property) &&\r\n this.reactivityFuncNames.includes(parentPath.node.callee.property.name)\r\n );\r\n }\r\n\r\n /**\r\n * @brief Check if it's in an \"escape\" function,\r\n * e.g. escape(() => { console.log(this.count) })\r\n * deps will be empty instead of [\"count\"]\r\n * @param innerPath\r\n * @param classDeclarationNode\r\n * @returns is in escape function\r\n */\r\n private isMemberInEscapeFunction(innerPath: NodePath): boolean {\r\n let isInFunction = false;\r\n let reversePath = innerPath.parentPath;\r\n while (reversePath) {\r\n const node = reversePath.node;\r\n if (\r\n this.t.isCallExpression(node) &&\r\n this.t.isIdentifier(node.callee) &&\r\n this.escapeNamings.includes(node.callee.name)\r\n ) {\r\n isInFunction = true;\r\n break;\r\n }\r\n reversePath = reversePath.parentPath;\r\n }\r\n return isInFunction;\r\n }\r\n\r\n /**\r\n * @brief Check if it's in a \"manual\" function,\r\n * e.g. manual(() => { console.log(this.count) }, [\"flag\"])\r\n * deps will be [\"flag\"] instead of [\"count\"]\r\n * @param innerPath\r\n * @param classDeclarationNode\r\n * @returns is in manual function\r\n */\r\n private isMemberInManualFunction(innerPath: NodePath): boolean {\r\n let isInFunction = false;\r\n let reversePath = innerPath.parentPath;\r\n\r\n while (reversePath) {\r\n const node = reversePath.node;\r\n const parentNode = reversePath.parentPath?.node;\r\n const isManual =\r\n this.t.isCallExpression(parentNode) &&\r\n this.t.isIdentifier(parentNode.callee) &&\r\n parentNode.callee.name === 'manual';\r\n const isFirstParam = this.t.isCallExpression(parentNode) && parentNode.arguments[0] === node;\r\n if (isManual && isFirstParam) {\r\n isInFunction = true;\r\n break;\r\n }\r\n reversePath = reversePath.parentPath;\r\n }\r\n\r\n return isInFunction;\r\n }\r\n\r\n /**\r\n * @brief Generate a random string\r\n * @returns\r\n */\r\n private uid(): string {\r\n return Math.random().toString(36).slice(2);\r\n }\r\n}\r\n","import { type ViewUnit } from '@inula/view-parser';\r\nimport { ReactivityParser } from './parser';\r\nimport { type ViewParticle, type ReactivityParserConfig } from './types';\r\n\r\n/**\r\n * @brief Parse view units to get used properties and view particles with reactivity\r\n * @param viewUnits\r\n * @param config\r\n * @param options\r\n * @returns [viewParticles, usedProperties]\r\n */\r\nexport function parseReactivity(viewUnits: ViewUnit[], config: ReactivityParserConfig): [ViewParticle[], Set] {\r\n // ---- ReactivityParser only accepts one view unit at a time,\r\n // so we loop through the view units and get all the used properties\r\n const usedProperties = new Set();\r\n const dlParticles = viewUnits.map(viewUnit => {\r\n const parser = new ReactivityParser(config);\r\n const dlParticle = parser.parse(viewUnit);\r\n parser.usedProperties.forEach(usedProperties.add.bind(usedProperties));\r\n return dlParticle;\r\n });\r\n return [dlParticles, usedProperties];\r\n}\r\n\r\nexport type * from './types';\r\n","import { type types as t, type traverse } from '@babel/core';\r\nimport { type ViewParticle } from '@inula/reactivity-parser';\r\nimport { type SnippetPropMap, type ViewGeneratorConfig } from '../types';\r\nimport ViewGenerator from '../ViewGenerator';\r\n\r\nexport const prefixMap = { template: '$t', node: '$node' };\r\n\r\nexport default class BaseGenerator {\r\n readonly viewParticle: ViewParticle;\r\n readonly config: ViewGeneratorConfig;\r\n\r\n readonly t: typeof t;\r\n readonly traverse: typeof traverse;\r\n readonly className: string;\r\n readonly importMap: Record;\r\n readonly snippetPropMap: SnippetPropMap;\r\n readonly elementAttributeMap;\r\n readonly alterAttributeMap;\r\n\r\n readonly viewGenerator;\r\n\r\n /**\r\n * @brief Constructor\r\n * @param viewUnit\r\n * @param config\r\n */\r\n constructor(viewParticle: ViewParticle, config: ViewGeneratorConfig) {\r\n this.viewParticle = viewParticle;\r\n this.config = config;\r\n this.t = config.babelApi.types;\r\n this.traverse = config.babelApi.traverse;\r\n this.className = config.className;\r\n this.importMap = config.importMap;\r\n this.snippetPropMap = config.snippetPropMap;\r\n this.viewGenerator = new ViewGenerator(config);\r\n this.elementAttributeMap = config.attributeMap\r\n ? Object.entries(config.attributeMap).reduce>((acc, [key, elements]) => {\r\n elements.forEach(element => {\r\n if (!acc[element]) acc[element] = [];\r\n acc[element].push(key);\r\n });\r\n return acc;\r\n }, {})\r\n : {};\r\n this.alterAttributeMap = config.alterAttributeMap;\r\n }\r\n\r\n // ---- Init Statements\r\n private readonly initStatements: t.Statement[] = [];\r\n addInitStatement(...statements: (t.Statement | null)[]) {\r\n this.initStatements.push(...(statements.filter(Boolean) as t.Statement[]));\r\n }\r\n\r\n // ---- Added Class Properties, typically used in for Template\r\n private readonly classProperties: t.ClassProperty[] = [];\r\n addStaticClassProperty(key: string, value: t.Expression) {\r\n this.classProperties.push(\r\n this.t.classProperty(this.t.identifier(key), value, undefined, undefined, undefined, true)\r\n );\r\n }\r\n\r\n // ---- Update Statements\r\n private readonly updateStatements: Record = {};\r\n addUpdateStatements(dependencies: number[] | undefined, statement: t.Statement | undefined | null) {\r\n if (!dependencies || dependencies.length === 0) return;\r\n const depNum = BaseGenerator.calcDependencyNum(dependencies);\r\n if (!this.updateStatements[depNum]) this.updateStatements[depNum] = [];\r\n if (statement) this.updateStatements[depNum].push(statement);\r\n }\r\n\r\n addUpdateStatementsWithoutDep(statement: t.Statement) {\r\n if (!this.updateStatements[0]) this.updateStatements[0] = [];\r\n this.updateStatements[0].push(statement);\r\n }\r\n\r\n /**\r\n * @returns [initStatements, updateStatements, classProperties, nodeName]\r\n */\r\n generate(): [t.Statement[], Record, t.ClassProperty[], string] {\r\n const nodeName = this.run();\r\n return [this.initStatements, this.updateStatements, this.classProperties, nodeName];\r\n }\r\n\r\n /**\r\n * @brief Generate the view given the view particles, mainly used for child particles parsing\r\n * @param viewParticles\r\n * @param mergeStatements\r\n * @returns [initStatements, topLevelNodes, updateStatements]\r\n */\r\n generateChildren(\r\n viewParticles: ViewParticle[],\r\n mergeStatements = true,\r\n newIdx = false\r\n ): [t.Statement[], string[], Record, number] {\r\n this.viewGenerator.nodeIdx = newIdx ? -1 : this.nodeIdx;\r\n this.viewGenerator.templateIdx = this.templateIdx;\r\n const [initStatements, updateStatements, classProperties, topLevelNodes] =\r\n this.viewGenerator.generateChildren(viewParticles);\r\n if (!newIdx) this.nodeIdx = this.viewGenerator.nodeIdx;\r\n this.templateIdx = this.viewGenerator.templateIdx;\r\n this.classProperties.push(...classProperties);\r\n if (mergeStatements) this.mergeStatements(updateStatements);\r\n\r\n return [initStatements, topLevelNodes, updateStatements, this.viewGenerator.nodeIdx];\r\n }\r\n\r\n /**\r\n * @brief Merge the update statements\r\n * @param statements\r\n */\r\n private mergeStatements(statements: Record): void {\r\n Object.entries(statements).forEach(([depNum, statements]) => {\r\n if (!this.updateStatements[Number(depNum)]) {\r\n this.updateStatements[Number(depNum)] = [];\r\n }\r\n this.updateStatements[Number(depNum)].push(...statements);\r\n });\r\n }\r\n\r\n /**\r\n * @brief Generate the view given the view particle\r\n * @param viewParticle\r\n * @param mergeStatements\r\n * @returns [initStatements, nodeName, updateStatements]\r\n */\r\n generateChild(\r\n viewParticle: ViewParticle,\r\n mergeStatements = true,\r\n newIdx = false\r\n ): [t.Statement[], string, Record, number] {\r\n this.viewGenerator.nodeIdx = newIdx ? -1 : this.nodeIdx;\r\n this.viewGenerator.templateIdx = this.templateIdx;\r\n const [initStatements, updateStatements, classProperties, nodeName] =\r\n this.viewGenerator.generateChild(viewParticle);\r\n if (!newIdx) this.nodeIdx = this.viewGenerator.nodeIdx;\r\n this.templateIdx = this.viewGenerator.templateIdx;\r\n this.classProperties.push(...classProperties);\r\n if (mergeStatements) this.mergeStatements(updateStatements);\r\n\r\n return [initStatements, nodeName, updateStatements, this.viewGenerator.nodeIdx];\r\n }\r\n\r\n /**\r\n * @View\r\n * const $update = (changed) => { ${updateStatements} }\r\n */\r\n geneUpdateFunc(updateStatements: Record): t.Statement {\r\n return this.t.variableDeclaration('const', [\r\n this.t.variableDeclarator(\r\n this.t.identifier('$update'),\r\n this.t.arrowFunctionExpression([this.t.identifier('$changed')], this.geneUpdateBody(updateStatements))\r\n ),\r\n ]);\r\n }\r\n\r\n get updateParams() {\r\n return [this.t.identifier('$changed')];\r\n }\r\n\r\n /**\r\n * @View\r\n * (changed) => {\r\n * if (changed & 1) {\r\n * ...\r\n * }\r\n * ...\r\n * }\r\n */\r\n geneUpdateBody(updateStatements: Record): t.BlockStatement {\r\n return this.t.blockStatement([\r\n ...Object.entries(updateStatements)\r\n .filter(([depNum]) => depNum !== '0')\r\n .map(([depNum, statements]) => {\r\n return this.t.ifStatement(\r\n this.t.binaryExpression('&', this.t.identifier('$changed'), this.t.numericLiteral(Number(depNum))),\r\n this.t.blockStatement(statements)\r\n );\r\n }),\r\n ...(updateStatements[0] ?? []),\r\n ]);\r\n }\r\n\r\n /**\r\n * @View\r\n * let node1, node2, ...\r\n */\r\n declareNodes(nodeIdx: number): t.VariableDeclaration[] {\r\n if (nodeIdx === -1) return [];\r\n return [\r\n this.t.variableDeclaration(\r\n 'let',\r\n Array.from({ length: nodeIdx + 1 }, (_, i) =>\r\n this.t.variableDeclarator(this.t.identifier(`${prefixMap.node}${i}`))\r\n )\r\n ),\r\n ];\r\n }\r\n\r\n /**\r\n * @View\r\n * return [${topLevelNodes}]\r\n */\r\n generateReturnStatement(topLevelNodes: string[]): t.ReturnStatement {\r\n return this.t.returnStatement(this.t.arrayExpression(topLevelNodes.map(name => this.t.identifier(name))));\r\n }\r\n\r\n /**\r\n * @brief To be implemented by the subclass as the main node generation function\r\n * @returns dlNodeName\r\n */\r\n run(): string {\r\n return '';\r\n }\r\n\r\n // ---- Name ----\r\n // ---- Used as dlNodeName for any node declaration\r\n nodeIdx = -1;\r\n generateNodeName(idx?: number): string {\r\n return `${prefixMap.node}${idx ?? ++this.nodeIdx}`;\r\n }\r\n\r\n // ---- Used as template generation as class property\r\n templateIdx = -1;\r\n generateTemplateName(): string {\r\n return `${prefixMap.template}${++this.templateIdx}`;\r\n }\r\n\r\n // ---- @Utils -----\r\n /**\r\n *\r\n * @param updateStatements\r\n * @returns\r\n */\r\n\r\n /**\r\n * @brief Calculate the dependency number from an array of dependency index\r\n * e.g.\r\n * [0, 1, 2] => 0b111 => 7\r\n * [1, 3] => 0b1010 => 10\r\n * @param dependencies\r\n * @returns dependency number\r\n */\r\n static calcDependencyNum(dependencies: number[] | undefined): number {\r\n if (!dependencies || dependencies.length === 0) return 0;\r\n dependencies = [...new Set(dependencies)];\r\n return dependencies.reduce((acc, dep) => acc + (1 << dep), 0);\r\n }\r\n\r\n /**\r\n * @brief Wrap the value in a file\r\n * @param node\r\n * @returns wrapped value\r\n */\r\n valueWrapper(node: t.Expression | t.Statement): t.File {\r\n return this.t.file(this.t.program([this.t.isStatement(node) ? node : this.t.expressionStatement(node)]));\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} && ${expression}\r\n */\r\n optionalExpression(dlNodeName: string, expression: t.Expression): t.Statement {\r\n return this.t.expressionStatement(this.t.logicalExpression('&&', this.t.identifier(dlNodeName), expression));\r\n }\r\n\r\n /**\r\n * @brief Shorthand function for collecting statements in batch\r\n * @returns [statements, collect]\r\n */\r\n static statementsCollector(): [t.Statement[], (...statements: t.Statement[] | t.Statement[][]) => void] {\r\n const statements: t.Statement[] = [];\r\n const collect = (...newStatements: t.Statement[] | t.Statement[][]) => {\r\n newStatements.forEach(s => {\r\n if (Array.isArray(s)) {\r\n statements.push(...s);\r\n } else {\r\n statements.push(s);\r\n }\r\n });\r\n };\r\n\r\n return [statements, collect];\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport BaseGenerator from './BaseGenerator';\r\n\r\nexport default class LifecycleGenerator extends BaseGenerator {\r\n static lifecycle = ['willMount', 'didMount', 'willUnmount', 'didUnmount'] as const;\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} && ${value}(${dlNodeName}, changed)\r\n */\r\n addOnUpdate(dlNodeName: string, value: t.Expression): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.logicalExpression(\r\n '&&',\r\n this.t.identifier(dlNodeName),\r\n this.t.callExpression(value, [this.t.identifier(dlNodeName), ...this.updateParams.slice(1)])\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * willMount:\r\n * - ${value}(${dlNodeName})\r\n * didMount/willUnmount/didUnmount:\r\n * - View.addDidMount(${dlNodeName}, ${value})\r\n */\r\n addLifecycle(\r\n dlNodeName: string,\r\n key: (typeof LifecycleGenerator.lifecycle)[number],\r\n value: t.Expression\r\n ): t.Statement {\r\n if (key === 'willMount') {\r\n return this.addWillMount(dlNodeName, value);\r\n }\r\n return this.addOtherLifecycle(dlNodeName, value, key);\r\n }\r\n\r\n /**\r\n * @View\r\n * ${value}(${dlNodeName})\r\n */\r\n addWillMount(dlNodeName: string, value: t.Expression): t.ExpressionStatement {\r\n return this.t.expressionStatement(this.t.callExpression(value, [this.t.identifier(dlNodeName)]));\r\n }\r\n\r\n /**\r\n * @View\r\n * View.addDidMount(${dlNodeName}, ${value})\r\n */\r\n addOtherLifecycle(\r\n dlNodeName: string,\r\n value: t.Expression,\r\n type: 'didMount' | 'willUnmount' | 'didUnmount'\r\n ): t.ExpressionStatement {\r\n return this.t.expressionStatement(\r\n this.t.callExpression(\r\n this.t.memberExpression(\r\n this.t.identifier('View'),\r\n this.t.identifier(`add${type[0].toUpperCase()}${type.slice(1)}`)\r\n ),\r\n [this.t.identifier(dlNodeName), value]\r\n )\r\n );\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport { type DependencyProp, type ViewParticle } from '@inula/reactivity-parser';\r\nimport LifecycleGenerator from './LifecycleGenerator';\r\n\r\nexport default class PropViewGenerator extends LifecycleGenerator {\r\n /**\r\n * @brief Alter prop view in batch\r\n * @param props\r\n * @returns altered props\r\n */\r\n alterPropViews | undefined>(props: T): T {\r\n if (!props) return props;\r\n return Object.fromEntries(\r\n Object.entries(props).map(([key, prop]) => {\r\n return [key, this.alterPropView(prop)!];\r\n })\r\n ) as T;\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} = new PropView(($addUpdate) => {\r\n * addUpdate((changed) => { ${updateStatements} })\r\n * ${initStatements}\r\n * return ${topLevelNodes}\r\n * })\r\n */\r\n declarePropView(viewParticles: ViewParticle[]) {\r\n // ---- Generate PropView\r\n const [initStatements, topLevelNodes, updateStatements, nodeIdx] = this.generateChildren(\r\n viewParticles,\r\n false,\r\n true\r\n );\r\n // ---- Add update function to the first node\r\n /**\r\n * $addUpdate((changed) => { ${updateStatements} })\r\n */\r\n if (Object.keys(updateStatements).length > 0) {\r\n initStatements.unshift(\r\n this.t.expressionStatement(\r\n this.t.callExpression(this.t.identifier('$addUpdate'), [\r\n this.t.arrowFunctionExpression(this.updateParams, this.geneUpdateBody(updateStatements)),\r\n ])\r\n )\r\n );\r\n }\r\n initStatements.unshift(...this.declareNodes(nodeIdx));\r\n initStatements.push(this.generateReturnStatement(topLevelNodes));\r\n\r\n // ---- Assign as a dlNode\r\n const dlNodeName = this.generateNodeName();\r\n const propViewNode = this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.identifier(dlNodeName),\r\n this.t.newExpression(this.t.identifier(this.importMap.PropView), [\r\n this.t.arrowFunctionExpression([this.t.identifier('$addUpdate')], this.t.blockStatement(initStatements)),\r\n ])\r\n )\r\n );\r\n this.addInitStatement(propViewNode);\r\n const propViewIdentifier = this.t.identifier(dlNodeName);\r\n\r\n // ---- Add to update statements\r\n /**\r\n * ${dlNodeName}?.update(changed)\r\n */\r\n this.addUpdateStatementsWithoutDep(\r\n this.optionalExpression(\r\n dlNodeName,\r\n this.t.callExpression(\r\n this.t.memberExpression(propViewIdentifier, this.t.identifier('update')),\r\n this.updateParams\r\n )\r\n )\r\n );\r\n\r\n return dlNodeName;\r\n }\r\n\r\n /**\r\n * @brief Alter prop view by replacing prop view with a recursively generated prop view\r\n * @param prop\r\n * @returns altered prop\r\n */\r\n alterPropView(prop: T): T {\r\n if (!prop) return prop;\r\n const { value, viewPropMap } = prop;\r\n if (!viewPropMap) return { ...prop, value };\r\n let newValue = value;\r\n this.traverse(this.valueWrapper(value), {\r\n StringLiteral: innerPath => {\r\n const id = innerPath.node.value;\r\n const viewParticles = viewPropMap[id];\r\n if (!viewParticles) return;\r\n const propViewIdentifier = this.t.identifier(this.declarePropView(viewParticles));\r\n\r\n if (value === innerPath.node) newValue = propViewIdentifier;\r\n innerPath.replaceWith(propViewIdentifier);\r\n innerPath.skip();\r\n },\r\n });\r\n return { ...prop, value: newValue };\r\n }\r\n\r\n /**\r\n * @brief Get the dependency index array from the update statements' keys\r\n * i.e. [1, 2, 7] => [0b1, 0b10, 0b111] => [[1], [2], [0, 1, 2]] => [0, 1, 2]\r\n * @param updateStatements\r\n * @returns dependency index array\r\n */\r\n private static reverseDependencyIndexArr(updateStatements: Record): number[] {\r\n const allDepsNum = Object.keys(updateStatements)\r\n .map(Number)\r\n .reduce((acc, depNum) => acc | depNum, 0);\r\n const allDeps = [];\r\n for (let i = 0; i < String(allDepsNum).length; i++) {\r\n if (allDepsNum & (1 << i)) allDeps.push(i);\r\n }\r\n return allDeps;\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport PropViewGenerator from './PropViewGenerator';\r\n\r\nexport default class ElementGenerator extends PropViewGenerator {\r\n /**\r\n * @View\r\n * el:\r\n * View.addDidMount(${dlNodeName}, () => (\r\n * typeof ${value} === \"function\" ? ${value}($nodeEl) : ${value} = $nodeEl\r\n * ))\r\n * not el:\r\n * typeof ${value} === \"function\" ? ${value}($nodeEl) : ${value} = $nodeEl\r\n * @param el true: dlNodeName._$el, false: dlNodeName\r\n */\r\n initElement(dlNodeName: string, value: t.Expression, el = false): t.Statement {\r\n const elNode = el\r\n ? this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('_$el'))\r\n : this.t.identifier(dlNodeName);\r\n let elementNode;\r\n if (this.isOnlyMemberExpression(value)) {\r\n elementNode = this.t.conditionalExpression(\r\n this.t.binaryExpression('===', this.t.unaryExpression('typeof', value, true), this.t.stringLiteral('function')),\r\n this.t.callExpression(value, [elNode]),\r\n this.t.assignmentExpression('=', value as t.LVal, elNode)\r\n );\r\n } else {\r\n elementNode = this.t.callExpression(value, [elNode]);\r\n }\r\n\r\n return el\r\n ? this.t.expressionStatement(\r\n this.t.callExpression(this.t.memberExpression(this.t.identifier('View'), this.t.identifier('addDidMount')), [\r\n this.t.identifier(dlNodeName),\r\n this.t.arrowFunctionExpression([], elementNode),\r\n ])\r\n )\r\n : this.t.expressionStatement(elementNode);\r\n }\r\n\r\n // --- Utils\r\n private isOnlyMemberExpression(value: t.Expression): boolean {\r\n if (!this.t.isMemberExpression(value)) return false;\r\n while (value.property) {\r\n if (this.t.isMemberExpression(value.property)) {\r\n value = value.property;\r\n continue;\r\n } else if (this.t.isIdentifier(value.property)) break;\r\n else return false;\r\n }\r\n return true;\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport ElementGenerator from './ElementGenerator';\r\n\r\nexport default class ForwardPropsGenerator extends ElementGenerator {\r\n /**\r\n * @View\r\n * this._$forwardProp(${dlNodeName})\r\n */\r\n forwardProps(dlNodeName: string): t.ExpressionStatement {\r\n return this.t.expressionStatement(\r\n this.t.callExpression(this.t.memberExpression(this.t.thisExpression(), this.t.identifier('_$addForwardProps')), [\r\n this.t.identifier(dlNodeName),\r\n ])\r\n );\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport { type DependencyProp, type CompParticle, type ViewParticle } from '@inula/reactivity-parser';\r\nimport ForwardPropGenerator from '../HelperGenerators/ForwardPropGenerator';\r\n\r\nexport default class CompGenerator extends ForwardPropGenerator {\r\n run() {\r\n let { props } = this.viewParticle as CompParticle;\r\n props = this.alterPropViews(props);\r\n const { tag, children } = this.viewParticle as CompParticle;\r\n\r\n const dlNodeName = this.generateNodeName();\r\n\r\n this.addInitStatement(...this.declareCompNode(dlNodeName, tag, props, children));\r\n const allDependencyIndexArr: number[] = [];\r\n\r\n // ---- Resolve props\r\n Object.entries(props).forEach(([key, { value, dependencyIndexArr, dependenciesNode }]) => {\r\n if (key === 'forwardProps') return;\r\n if (key === 'didUpdate') return;\r\n allDependencyIndexArr.push(...dependencyIndexArr);\r\n if (CompGenerator.lifecycle.includes(key as (typeof CompGenerator.lifecycle)[number])) {\r\n this.addInitStatement(this.addLifecycle(dlNodeName, key as (typeof CompGenerator.lifecycle)[number], value));\r\n return;\r\n }\r\n if (key === 'ref') {\r\n this.addInitStatement(this.initElement(dlNodeName, value));\r\n return;\r\n }\r\n if (key === 'elements') {\r\n this.addInitStatement(this.initElement(dlNodeName, value, true));\r\n return;\r\n }\r\n if (key === '_$content') {\r\n this.addUpdateStatements(dependencyIndexArr, this.setCompContent(dlNodeName, value, dependenciesNode));\r\n return;\r\n }\r\n if (key === 'props') {\r\n this.addUpdateStatements(dependencyIndexArr, this.setCompProps(dlNodeName, value, dependenciesNode));\r\n return;\r\n }\r\n\r\n this.addUpdateStatements(dependencyIndexArr, this.setCompProp(dlNodeName, key, value, dependenciesNode));\r\n });\r\n\r\n // ---- Add addUpdate last\r\n if (props.didUpdate) {\r\n this.addUpdateStatements(allDependencyIndexArr, this.addOnUpdate(dlNodeName, props.didUpdate.value));\r\n }\r\n\r\n return dlNodeName;\r\n }\r\n\r\n /**\r\n * @View\r\n * null\r\n * or\r\n * [[prop1, value1, deps1], [prop2, value2, deps2], ...\r\n */\r\n private generateCompProps(props: Record): t.Expression {\r\n if (Object.keys(props).length === 0) return this.t.nullLiteral();\r\n return this.t.arrayExpression(\r\n Object.entries(props).map(([key, { value, dependenciesNode }]) => {\r\n return this.t.arrayExpression([this.t.stringLiteral(key), value, dependenciesNode]);\r\n })\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} = new ${tag}()\r\n * ${dlNodeName}._$init(${props}, ${content}, ${children}, ${this})\r\n */\r\n private declareCompNode(\r\n dlNodeName: string,\r\n tag: t.Expression,\r\n props: Record,\r\n children: ViewParticle[]\r\n ): t.Statement[] {\r\n const willForwardProps = 'forwardProps' in props;\r\n const newProps = Object.fromEntries(\r\n Object.entries(props).filter(\r\n ([key]) =>\r\n !['ref', 'elements', 'forwardProps', '_$content', 'didUpdate', 'props', ...CompGenerator.lifecycle].includes(\r\n key\r\n )\r\n )\r\n );\r\n\r\n const content = props._$content;\r\n\r\n return [\r\n this.t.expressionStatement(\r\n this.t.assignmentExpression('=', this.t.identifier(dlNodeName), this.t.newExpression(tag, []))\r\n ),\r\n this.t.expressionStatement(\r\n this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('_$init')), [\r\n this.generateCompProps(newProps),\r\n content ? this.t.arrayExpression([content.value, content.dependenciesNode]) : this.t.nullLiteral(),\r\n children.length > 0 ? this.t.identifier(this.declarePropView(children)) : this.t.nullLiteral(),\r\n willForwardProps ? this.t.identifier('this') : this.t.nullLiteral(),\r\n ])\r\n ),\r\n ];\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}._$setContent(() => ${value}, ${dependenciesNode})\r\n */\r\n private setCompContent(dlNodeName: string, value: t.Expression, dependenciesNode: t.ArrayExpression): t.Statement {\r\n return this.optionalExpression(\r\n dlNodeName,\r\n this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('_$setContent')), [\r\n this.t.arrowFunctionExpression([], value),\r\n dependenciesNode,\r\n ])\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}._$setProp(${key}, () => ${value}, ${dependenciesNode})\r\n */\r\n private setCompProp(\r\n dlNodeName: string,\r\n key: string,\r\n value: t.Expression,\r\n dependenciesNode: t.ArrayExpression\r\n ): t.Statement {\r\n return this.optionalExpression(\r\n dlNodeName,\r\n this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('_$setProp')), [\r\n this.t.stringLiteral(key),\r\n this.t.arrowFunctionExpression([], value),\r\n dependenciesNode,\r\n ])\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}._$setProps(() => ${value}, ${dependenciesNode})\r\n */\r\n private setCompProps(dlNodeName: string, value: t.Expression, dependenciesNode: t.ArrayExpression): t.Statement {\r\n return this.optionalExpression(\r\n dlNodeName,\r\n this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('_$setProps')), [\r\n this.t.arrowFunctionExpression([], value),\r\n dependenciesNode,\r\n ])\r\n );\r\n }\r\n}\r\n","import { createErrorHandler } from '@inula/error-handler';\r\n\r\nexport const DLError = createErrorHandler(\r\n 'ViewGenerator',\r\n {\r\n 1: 'Element prop in HTML should be a function or an identifier',\r\n 2: 'Unrecognized HTML common prop',\r\n 3: 'Do prop only accepts function or arrow function',\r\n },\r\n {},\r\n {\r\n 1: 'ExpressionNode only supports prop as element and lifecycle, receiving $0',\r\n }\r\n);\r\n","import { type types as t } from '@babel/core';\r\nimport { DLError } from '../error';\r\nimport ForwardPropGenerator from './ForwardPropGenerator';\r\n\r\nexport default class HTMLPropGenerator extends ForwardPropGenerator {\r\n static DelegatedEvents = new Set([\r\n 'beforeinput',\r\n 'click',\r\n 'dblclick',\r\n 'contextmenu',\r\n 'focusin',\r\n 'focusout',\r\n 'input',\r\n 'keydown',\r\n 'keyup',\r\n 'mousedown',\r\n 'mousemove',\r\n 'mouseout',\r\n 'mouseover',\r\n 'mouseup',\r\n 'pointerdown',\r\n 'pointermove',\r\n 'pointerout',\r\n 'pointerover',\r\n 'pointerup',\r\n 'touchend',\r\n 'touchmove',\r\n 'touchstart',\r\n ]);\r\n\r\n /**\r\n * @brief Add any HTML props according to the key\r\n * @param name\r\n * @param tag\r\n * @param key\r\n * @param value\r\n * @param dependencyIndexArr\r\n * @returns t.Statement\r\n */\r\n addHTMLProp(\r\n name: string,\r\n tag: string,\r\n key: string,\r\n value: t.Expression,\r\n dynamic: boolean,\r\n dependencyIndexArr: number[],\r\n dependenciesNode: t.ArrayExpression\r\n ): t.Statement | null {\r\n // ---- Dynamic HTML prop with init and update\r\n if (dynamic) {\r\n this.addUpdateStatements(\r\n dependencyIndexArr,\r\n this.setDynamicHTMLProp(name, tag, key, value, dependenciesNode, true)\r\n );\r\n return this.setDynamicHTMLProp(name, tag, key, value, dependenciesNode, false);\r\n }\r\n // ---- Static HTML prop with init only\r\n return this.setStaticHTMLProp(name, tag, key, value);\r\n }\r\n\r\n /**\r\n * @View\r\n * insertNode(${dlNodeName}, ${childNodeName}, ${position})\r\n */\r\n insertNode(dlNodeName: string, childNodeName: string, position: number): t.ExpressionStatement {\r\n return this.t.expressionStatement(\r\n this.t.callExpression(this.t.identifier(this.importMap.insertNode), [\r\n this.t.identifier(dlNodeName),\r\n this.t.identifier(childNodeName),\r\n this.t.numericLiteral(position),\r\n ])\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} && ${expression}\r\n */\r\n private setPropWithCheck(dlNodeName: string, expression: t.Expression, check: boolean): t.Statement {\r\n if (check) {\r\n return this.optionalExpression(dlNodeName, expression);\r\n }\r\n return this.t.expressionStatement(expression);\r\n }\r\n\r\n /**\r\n * @View\r\n * setStyle(${dlNodeName}, ${value})\r\n */\r\n private setHTMLStyle(dlNodeName: string, value: t.Expression, check: boolean): t.Statement {\r\n return this.setPropWithCheck(\r\n dlNodeName,\r\n this.t.callExpression(this.t.identifier(this.importMap.setStyle), [this.t.identifier(dlNodeName), value]),\r\n check\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * setStyle(${dlNodeName}, ${value})\r\n */\r\n private setHTMLDataset(dlNodeName: string, value: t.Expression, check: boolean): t.Statement {\r\n return this.setPropWithCheck(\r\n dlNodeName,\r\n this.t.callExpression(this.t.identifier(this.importMap.setDataset), [this.t.identifier(dlNodeName), value]),\r\n check\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}.${key} = ${value}\r\n */\r\n private setHTMLProp(dlNodeName: string, key: string, value: t.Expression): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier(key)),\r\n value\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}.setAttribute(${key}, ${value})\r\n */\r\n private setHTMLAttr(dlNodeName: string, key: string, value: t.Expression): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('setAttribute')), [\r\n this.t.stringLiteral(key),\r\n value,\r\n ])\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}.addEventListener(${key}, ${value})\r\n */\r\n private setHTMLEvent(dlNodeName: string, key: string, value: t.Expression): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.callExpression(\r\n this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('addEventListener')),\r\n [this.t.stringLiteral(key), value]\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * setEvent(${dlNodeName}, ${key}, ${value})\r\n */\r\n private setEvent(dlNodeName: string, key: string, value: t.Expression, check: boolean): t.Statement {\r\n return this.setPropWithCheck(\r\n dlNodeName,\r\n this.t.callExpression(this.t.identifier(this.importMap.setEvent), [\r\n this.t.identifier(dlNodeName),\r\n this.t.stringLiteral(key),\r\n value,\r\n ]),\r\n check\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * delegateEvent(${dlNodeName}, ${key}, ${value})\r\n */\r\n private delegateEvent(dlNodeName: string, key: string, value: t.Expression, check: boolean): t.Statement {\r\n return this.setPropWithCheck(\r\n dlNodeName,\r\n this.t.callExpression(this.t.identifier(this.importMap.delegateEvent), [\r\n this.t.identifier(dlNodeName),\r\n this.t.stringLiteral(key),\r\n value,\r\n ]),\r\n check\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * setHTMLProp(${dlNodeName}, ${key}, ${valueFunc}, ${dependenciesNode})\r\n */\r\n private setCachedProp(\r\n dlNodeName: string,\r\n key: string,\r\n value: t.Expression,\r\n dependenciesNode: t.ArrayExpression,\r\n check: boolean\r\n ): t.Statement {\r\n return this.setPropWithCheck(\r\n dlNodeName,\r\n this.t.callExpression(this.t.identifier(this.importMap.setHTMLProp), [\r\n this.t.identifier(dlNodeName),\r\n this.t.stringLiteral(key),\r\n this.t.arrowFunctionExpression([], value),\r\n dependenciesNode,\r\n ]),\r\n check\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * setHTMLAttr(${dlNodeName}, ${key}, ${valueFunc}, ${dependenciesNode}, ${check})\r\n */\r\n private setCachedAttr(\r\n dlNodeName: string,\r\n key: string,\r\n value: t.Expression,\r\n dependenciesNode: t.ArrayExpression,\r\n check: boolean\r\n ): t.Statement {\r\n return this.setPropWithCheck(\r\n dlNodeName,\r\n this.t.callExpression(this.t.identifier(this.importMap.setHTMLAttr), [\r\n this.t.identifier(dlNodeName),\r\n this.t.stringLiteral(key),\r\n this.t.arrowFunctionExpression([], value),\r\n dependenciesNode,\r\n ]),\r\n check\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * setHTMLProps(${dlNodeName}, ${value})\r\n */\r\n private setHTMLPropObject(dlNodeName: string, value: t.Expression, check: boolean): t.Statement {\r\n return this.setPropWithCheck(\r\n dlNodeName,\r\n this.t.callExpression(this.t.identifier(this.importMap.setHTMLProps), [this.t.identifier(dlNodeName), value]),\r\n check\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * setHTMLAttrs(${dlNodeName}, ${value})\r\n */\r\n private setHTMLAttrObject(dlNodeName: string, value: t.Expression, check: boolean): t.Statement {\r\n return this.setPropWithCheck(\r\n dlNodeName,\r\n this.t.callExpression(this.t.identifier(this.importMap.setHTMLAttrs), [this.t.identifier(dlNodeName), value]),\r\n check\r\n );\r\n }\r\n\r\n private static commonHTMLPropKeys = [\r\n 'style',\r\n 'dataset',\r\n 'props',\r\n 'ref',\r\n 'attrs',\r\n 'forwardProps',\r\n ...HTMLPropGenerator.lifecycle,\r\n ];\r\n\r\n /**\r\n * For style/dataset/ref/attr/prop\r\n */\r\n private addCommonHTMLProp(\r\n dlNodeName: string,\r\n attrName: string,\r\n value: t.Expression,\r\n check: boolean\r\n ): t.Statement | null {\r\n if (HTMLPropGenerator.lifecycle.includes(attrName as (typeof HTMLPropGenerator.lifecycle)[number])) {\r\n if (!check) return this.addLifecycle(dlNodeName, attrName as (typeof HTMLPropGenerator.lifecycle)[number], value);\r\n return null;\r\n }\r\n if (attrName === 'ref') {\r\n if (!check) return this.initElement(dlNodeName, value);\r\n return null;\r\n }\r\n if (attrName === 'style') return this.setHTMLStyle(dlNodeName, value, check);\r\n if (attrName === 'dataset') return this.setHTMLDataset(dlNodeName, value, check);\r\n if (attrName === 'props') return this.setHTMLPropObject(dlNodeName, value, check);\r\n if (attrName === 'attrs') return this.setHTMLAttrObject(dlNodeName, value, check);\r\n if (attrName === 'forwardProps') return this.forwardProps(dlNodeName);\r\n return DLError.throw2();\r\n }\r\n\r\n /**\r\n * @View\r\n * 1. Event listener\r\n * - ${dlNodeName}.addEventListener(${key}, ${value})\r\n * 2. HTML internal attribute -> DOM property\r\n * - ${dlNodeName}.${key} = ${value}\r\n * 3. HTML custom attribute\r\n * - ${dlNodeName}.setAttribute(${key}, ${value})\r\n */\r\n private setStaticHTMLProp(\r\n dlNodeName: string,\r\n tag: string,\r\n attrName: string,\r\n value: t.Expression\r\n ): t.Statement | null {\r\n if (HTMLPropGenerator.commonHTMLPropKeys.includes(attrName))\r\n return this.addCommonHTMLProp(dlNodeName, attrName, value, false);\r\n if (attrName.startsWith('on')) {\r\n const eventName = attrName.slice(2).toLowerCase();\r\n if (HTMLPropGenerator.DelegatedEvents.has(eventName)) {\r\n return this.delegateEvent(dlNodeName, eventName, value, false);\r\n }\r\n return this.setHTMLEvent(dlNodeName, eventName, value);\r\n }\r\n if (this.isInternalAttribute(tag, attrName)) {\r\n if (attrName === 'class') attrName = 'className';\r\n else if (attrName === 'for') attrName = 'htmlFor';\r\n return this.setHTMLProp(dlNodeName, attrName, value);\r\n }\r\n return this.setHTMLAttr(dlNodeName, attrName, value);\r\n }\r\n\r\n /**\r\n * @View\r\n * 1. Event listener\r\n * - ${setEvent}(${dlNodeName}, ${key}, ${value})\r\n * 2. HTML internal attribute -> DOM property\r\n * - ${setHTMLProp}(${dlNodeName}, ${key}, ${value})\r\n * 3. HTML custom attribute\r\n * - ${setHTMLAttr}(${dlNodeName}, ${key}, ${value})\r\n */\r\n private setDynamicHTMLProp(\r\n dlNodeName: string,\r\n tag: string,\r\n attrName: string,\r\n value: t.Expression,\r\n dependenciesNode: t.ArrayExpression,\r\n check: boolean\r\n ): t.Statement | null {\r\n if (HTMLPropGenerator.commonHTMLPropKeys.includes(attrName))\r\n return this.addCommonHTMLProp(dlNodeName, attrName, value, check);\r\n if (attrName.startsWith('on')) {\r\n const eventName = attrName.slice(2).toLowerCase();\r\n if (HTMLPropGenerator.DelegatedEvents.has(eventName)) {\r\n return this.delegateEvent(dlNodeName, eventName, value, check);\r\n }\r\n return this.setEvent(dlNodeName, eventName, value, check);\r\n }\r\n if (this.alterAttributeMap[attrName]) {\r\n attrName = this.alterAttributeMap[attrName];\r\n }\r\n if (this.isInternalAttribute(tag, attrName)) {\r\n return this.setCachedProp(dlNodeName, attrName, value, dependenciesNode, check);\r\n }\r\n return this.setCachedAttr(dlNodeName, attrName, value, dependenciesNode, check);\r\n }\r\n\r\n /**\r\n * @brief Check if the attribute is internal, i.e., can be accessed as js property\r\n * @param tag\r\n * @param attribute\r\n * @returns true if the attribute is internal\r\n */\r\n isInternalAttribute(tag: string, attribute: string): boolean {\r\n return this.elementAttributeMap['*']?.includes(attribute) || this.elementAttributeMap[tag]?.includes(attribute);\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport { type HTMLParticle } from '@inula/reactivity-parser';\r\nimport HTMLPropGenerator from '../HelperGenerators/HTMLPropGenerator';\r\n\r\nexport default class HTMLGenerator extends HTMLPropGenerator {\r\n run() {\r\n const { tag, props, children } = this.viewParticle as HTMLParticle;\r\n\r\n const dlNodeName = this.generateNodeName();\r\n\r\n this.addInitStatement(this.declareHTMLNode(dlNodeName, tag));\r\n\r\n // ---- Resolve props\r\n // ---- Use the tag name to check if the prop is internal for the tag,\r\n // for dynamic tag, we can't check it, so we just assume it's not internal\r\n // represent by the \"ANY\" tag name\r\n const tagName = this.t.isStringLiteral(tag) ? tag.value : 'ANY';\r\n const allDependencyIndexArr: number[] = [];\r\n Object.entries(props).forEach(([key, { value, dependencyIndexArr, dependenciesNode, dynamic }]) => {\r\n if (key === 'didUpdate') return;\r\n allDependencyIndexArr.push(...(dependencyIndexArr ?? []));\r\n this.addInitStatement(\r\n this.addHTMLProp(dlNodeName, tagName, key, value, dynamic, dependencyIndexArr, dependenciesNode)\r\n );\r\n });\r\n if (props.didUpdate) {\r\n this.addUpdateStatements(allDependencyIndexArr, this.addOnUpdate(dlNodeName, props.didUpdate.value));\r\n }\r\n\r\n // ---- Resolve children\r\n const childNames: string[] = [];\r\n let mutable = false;\r\n children.forEach((child, idx) => {\r\n const [initStatements, childName] = this.generateChild(child);\r\n childNames.push(childName);\r\n this.addInitStatement(...initStatements);\r\n if (child.type === 'html') {\r\n this.addInitStatement(this.appendChild(dlNodeName, childName));\r\n } else {\r\n mutable = true;\r\n this.addInitStatement(this.insertNode(dlNodeName, childName, idx));\r\n }\r\n });\r\n if (mutable) this.addInitStatement(this.setHTMLNodes(dlNodeName, childNames));\r\n\r\n return dlNodeName;\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} = createElement(${tag})\r\n */\r\n private declareHTMLNode(dlNodeName: string, tag: t.Expression): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.identifier(dlNodeName),\r\n this.t.callExpression(this.t.identifier(this.importMap.createElement), [tag])\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}._$nodes = [...${childNames}]\r\n */\r\n private setHTMLNodes(dlNodeName: string, childNames: string[]): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('_$nodes')),\r\n this.t.arrayExpression(childNames.map(name => this.t.identifier(name)))\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}.appendChild(${childNodeName})\r\n */\r\n private appendChild(dlNodeName: string, childNodeName: string): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('appendChild')), [\r\n this.t.identifier(childNodeName),\r\n ])\r\n );\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport { type HTMLParticle, type TemplateParticle } from '@inula/reactivity-parser';\r\nimport HTMLPropGenerator from '../HelperGenerators/HTMLPropGenerator';\r\n\r\nexport default class TemplateGenerator extends HTMLPropGenerator {\r\n run() {\r\n const { template, mutableParticles, props } = this.viewParticle as TemplateParticle;\r\n\r\n const dlNodeName = this.generateNodeName();\r\n // ---- Add template declaration to class\r\n const templateName = this.addTemplate(template);\r\n // ---- Declare template node in View body\r\n this.addInitStatement(this.declareTemplateNode(dlNodeName, templateName));\r\n\r\n // ---- Insert elements first\r\n const paths: number[][] = [];\r\n props.forEach(({ path }) => {\r\n paths.push(path);\r\n });\r\n mutableParticles.forEach(({ path }) => {\r\n paths.push(path.slice(0, -1));\r\n });\r\n const [insertElementStatements, pathNameMap] = this.insertElements(paths, dlNodeName);\r\n this.addInitStatement(...insertElementStatements);\r\n\r\n // ---- Resolve props\r\n const didUpdateMap: Record = {};\r\n props.forEach(({ tag, path, key, value, dependencyIndexArr, dependenciesNode, dynamic }) => {\r\n const name = pathNameMap[path.join('.')];\r\n if (!didUpdateMap[name])\r\n didUpdateMap[name] = {\r\n deps: [],\r\n };\r\n if (key === 'didUpdate') {\r\n didUpdateMap[name].value = value;\r\n return;\r\n }\r\n\r\n didUpdateMap[name].deps.push(...dependencyIndexArr);\r\n\r\n this.addInitStatement(this.addHTMLProp(name, tag, key, value, dynamic, dependencyIndexArr, dependenciesNode));\r\n });\r\n\r\n Object.entries(didUpdateMap).forEach(([name, { deps, value }]) => {\r\n if (!value) return;\r\n this.addUpdateStatements(deps, this.addOnUpdate(name, value));\r\n });\r\n\r\n // ---- Resolve mutable particles\r\n mutableParticles.forEach(particle => {\r\n const path = particle.path;\r\n // ---- Find parent htmlElement\r\n const parentName = pathNameMap[path.slice(0, -1).join('.')];\r\n const [initStatements, childName] = this.generateChild(particle);\r\n this.addInitStatement(...initStatements);\r\n this.addInitStatement(this.insertNode(parentName, childName, path[path.length - 1]));\r\n });\r\n\r\n return dlNodeName;\r\n }\r\n\r\n /**\r\n * @View\r\n * static ${templateName} = (() => {\r\n * let _$node0, _$node1, ...\r\n * ${template}\r\n *\r\n * return _$node0\r\n * })()\r\n */\r\n private addTemplate(template: HTMLParticle): string {\r\n const templateName = this.generateTemplateName();\r\n const [statements, nodeName, , nodeIdx] = this.generateChild(template, false, true);\r\n this.addStaticClassProperty(\r\n templateName,\r\n this.t.callExpression(\r\n this.t.arrowFunctionExpression(\r\n [],\r\n this.t.blockStatement([\r\n ...this.declareNodes(nodeIdx),\r\n ...statements,\r\n this.t.returnStatement(this.t.identifier(nodeName)),\r\n ])\r\n ),\r\n []\r\n )\r\n );\r\n\r\n return templateName;\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} = ${this.className}.${templateName}.cloneNode(true)\r\n */\r\n private declareTemplateNode(dlNodeName: string, templateName: string): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.identifier(dlNodeName),\r\n this.t.callExpression(\r\n this.t.memberExpression(\r\n this.t.memberExpression(this.t.identifier(this.className), this.t.identifier(templateName)),\r\n this.t.identifier('cloneNode')\r\n ),\r\n [this.t.booleanLiteral(true)]\r\n )\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}.firstChild\r\n * or\r\n * ${dlNodeName}.firstChild.nextSibling\r\n * or\r\n * ...\r\n * ${dlNodeName}.childNodes[${num}]\r\n */\r\n private insertElement(dlNodeName: string, path: number[], offset: number): t.Statement {\r\n const newNodeName = this.generateNodeName();\r\n if (path.length === 0) {\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.identifier(newNodeName),\r\n Array.from({ length: offset }).reduce(\r\n (acc: t.Expression) => this.t.memberExpression(acc, this.t.identifier('nextSibling')),\r\n this.t.identifier(dlNodeName)\r\n )\r\n )\r\n );\r\n }\r\n const addFirstChild = (object: t.Expression) =>\r\n // ---- ${object}.firstChild\r\n this.t.memberExpression(object, this.t.identifier('firstChild'));\r\n const addSecondChild = (object: t.Expression) =>\r\n // ---- ${object}.firstChild.nextSibling\r\n this.t.memberExpression(addFirstChild(object), this.t.identifier('nextSibling'));\r\n const addThirdChild = (object: t.Expression) =>\r\n // ---- ${object}.firstChild.nextSibling.nextSibling\r\n this.t.memberExpression(addSecondChild(object), this.t.identifier('nextSibling'));\r\n const addOtherChild = (object: t.Expression, num: number) =>\r\n // ---- ${object}.childNodes[${num}]\r\n this.t.memberExpression(\r\n this.t.memberExpression(object, this.t.identifier('childNodes')),\r\n this.t.numericLiteral(num),\r\n true\r\n );\r\n const addNextSibling = (object: t.Expression) =>\r\n // ---- ${object}.nextSibling\r\n this.t.memberExpression(object, this.t.identifier('nextSibling'));\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.identifier(newNodeName),\r\n path.reduce((acc: t.Expression, cur: number, idx) => {\r\n if (idx === 0 && offset > 0) {\r\n for (let i = 0; i < offset; i++) acc = addNextSibling(acc);\r\n }\r\n if (cur === 0) return addFirstChild(acc);\r\n if (cur === 1) return addSecondChild(acc);\r\n if (cur === 2) return addThirdChild(acc);\r\n return addOtherChild(acc, cur);\r\n }, this.t.identifier(dlNodeName))\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @brief Insert elements to the template node from the paths\r\n * @param paths\r\n * @param dlNodeName\r\n * @returns\r\n */\r\n private insertElements(paths: number[][], dlNodeName: string): [t.Statement[], Record] {\r\n const [statements, collect] = HTMLPropGenerator.statementsCollector();\r\n const nameMap: Record = { [dlNodeName]: [] };\r\n\r\n const commonPrefixPaths = TemplateGenerator.pathWithCommonPrefix(paths);\r\n\r\n commonPrefixPaths.forEach(path => {\r\n const res = TemplateGenerator.findBestNodeAndPath(nameMap, path, dlNodeName);\r\n const [, pat, offset] = res;\r\n let name = res[0];\r\n\r\n if (pat.length !== 0 || offset !== 0) {\r\n collect(this.insertElement(name, pat, offset));\r\n name = this.generateNodeName(this.nodeIdx);\r\n nameMap[name] = path;\r\n }\r\n });\r\n const pathNameMap = Object.fromEntries(Object.entries(nameMap).map(([name, path]) => [path.join('.'), name]));\r\n\r\n return [statements, pathNameMap];\r\n }\r\n\r\n // ---- Path related\r\n /**\r\n * @brief Extract common prefix from paths\r\n * e.g.\r\n * [0, 1, 2, 3] + [0, 1, 2, 4] => [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 4]\r\n * [0, 1, 2] is the common prefix\r\n * @param paths\r\n * @returns paths with common prefix\r\n */\r\n private static pathWithCommonPrefix(paths: number[][]): number[][] {\r\n const allPaths = [...paths];\r\n paths.forEach(path0 => {\r\n paths.forEach(path1 => {\r\n if (path0 === path1) return;\r\n for (let i = 0; i < path0.length; i++) {\r\n if (path0[i] !== path1[i]) {\r\n if (i !== 0) {\r\n allPaths.push(path0.slice(0, i));\r\n }\r\n break;\r\n }\r\n }\r\n });\r\n });\r\n\r\n // ---- Sort by length and then by first element, small to large\r\n const sortedPaths = allPaths.sort((a, b) => {\r\n if (a.length !== b.length) return a.length - b.length;\r\n return a[0] - b[0];\r\n });\r\n\r\n // ---- Deduplicate\r\n const deduplicatedPaths = [...new Set(sortedPaths.map(path => path.join('.')))].map(path =>\r\n path.split('.').filter(Boolean).map(Number)\r\n );\r\n\r\n return deduplicatedPaths;\r\n }\r\n\r\n /**\r\n * @brief Find the best node name and path for the given path by looking into the nameMap.\r\n * If there's a full match, return the name and an empty path\r\n * If there's a partly match, return the name and the remaining path\r\n * If there's a nextSibling match, return the name and the remaining path with sibling offset\r\n * @param nameMap\r\n * @param path\r\n * @param defaultName\r\n * @returns [name, path, siblingOffset]\r\n */\r\n private static findBestNodeAndPath(\r\n nameMap: Record,\r\n path: number[],\r\n defaultName: string\r\n ): [string, number[], number] {\r\n let bestMatchCount = 0;\r\n let bestMatchName: string | undefined;\r\n let bestHalfMatch: [string, number, number] | undefined;\r\n Object.entries(nameMap).forEach(([name, pat]) => {\r\n let matchCount = 0;\r\n const pathLength = pat.length;\r\n for (let i = 0; i < pathLength; i++) {\r\n if (pat[i] === path[i]) matchCount++;\r\n }\r\n if (matchCount === pathLength - 1) {\r\n const offset = path[pathLength - 1] - pat[pathLength - 1];\r\n if (offset > 0 && offset <= 3) {\r\n bestHalfMatch = [name, matchCount, offset];\r\n }\r\n }\r\n if (matchCount !== pat.length) return;\r\n if (matchCount > bestMatchCount) {\r\n bestMatchName = name;\r\n bestMatchCount = matchCount;\r\n }\r\n });\r\n if (!bestMatchName) {\r\n if (bestHalfMatch) {\r\n return [bestHalfMatch[0], path.slice(bestHalfMatch[1] + 1), bestHalfMatch[2]];\r\n }\r\n return [defaultName, path, 0];\r\n }\r\n return [bestMatchName, path.slice(bestMatchCount), 0];\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport BaseGenerator from '../HelperGenerators/BaseGenerator';\r\nimport { type ForParticle, type ViewParticle } from '@inula/reactivity-parser';\r\n\r\nexport default class ForGenerator extends BaseGenerator {\r\n run() {\r\n const { item, array, key, children } = this.viewParticle as ForParticle;\r\n\r\n const dlNodeName = this.generateNodeName();\r\n\r\n // ---- Declare for node\r\n this.addInitStatement(\r\n this.declareForNode(\r\n dlNodeName,\r\n array.value,\r\n item,\r\n children,\r\n BaseGenerator.calcDependencyNum(array.dependencyIndexArr),\r\n key\r\n )\r\n );\r\n\r\n // ---- Update statements\r\n this.addUpdateStatements(array.dependencyIndexArr, this.updateForNode(dlNodeName, array.value, item, key));\r\n this.addUpdateStatementsWithoutDep(this.updateForNodeItem(dlNodeName));\r\n\r\n return dlNodeName;\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} = new ForNode(${array}, ${depNum}, ${array}.map(${item} => ${key}),\r\n * ((${item}, $updateArr, $idx) => {\r\n * $updateArr[$idx] = (changed, $item) => {\r\n * ${item} = $item\r\n * {$updateStatements}\r\n * })\r\n * ${children}\r\n * return [...${topLevelNodes}]\r\n * })\r\n */\r\n private declareForNode(\r\n dlNodeName: string,\r\n array: t.Expression,\r\n item: t.LVal,\r\n children: ViewParticle[],\r\n depNum: number,\r\n key: t.Expression\r\n ): t.Statement {\r\n // ---- NodeFunc\r\n const [childStatements, topLevelNodes, updateStatements, nodeIdx] = this.generateChildren(children, false, true);\r\n\r\n // ---- Update func\r\n childStatements.unshift(\r\n ...this.declareNodes(nodeIdx),\r\n this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.memberExpression(this.t.identifier('$updateArr'), this.t.identifier('$idx'), true),\r\n this.t.arrowFunctionExpression(\r\n [...this.updateParams, this.t.identifier('$item')],\r\n this.t.blockStatement([\r\n this.t.expressionStatement(this.t.assignmentExpression('=', item, this.t.identifier('$item'))),\r\n ...this.geneUpdateBody(updateStatements).body,\r\n ])\r\n )\r\n )\r\n )\r\n );\r\n\r\n // ---- Return statement\r\n childStatements.push(this.generateReturnStatement(topLevelNodes));\r\n\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.identifier(dlNodeName),\r\n this.t.newExpression(this.t.identifier(this.importMap.ForNode), [\r\n array,\r\n this.t.numericLiteral(depNum),\r\n this.getForKeyStatement(array, item, key),\r\n this.t.arrowFunctionExpression(\r\n [item as any, this.t.identifier('$updateArr'), this.t.identifier('$idx')],\r\n this.t.blockStatement(childStatements)\r\n ),\r\n ])\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${array}.map(${item} => ${key})\r\n */\r\n private getForKeyStatement(array: t.Expression, item: t.LVal, key: t.Expression): t.Expression {\r\n return this.t.isNullLiteral(key)\r\n ? key\r\n : this.t.callExpression(this.t.memberExpression(array, this.t.identifier('map')), [\r\n this.t.arrowFunctionExpression([item as any], key),\r\n ]);\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}.updateArray(${array}, ${array}.map(${item} => ${key}))\r\n */\r\n private updateForNode(dlNodeName: string, array: t.Expression, item: t.LVal, key: t.Expression): t.Statement {\r\n return this.optionalExpression(\r\n dlNodeName,\r\n this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('updateArray')), [\r\n array,\r\n ...this.updateParams.slice(1),\r\n this.getForKeyStatement(array, item, key),\r\n ])\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}?.update(changed)\r\n */\r\n private updateForNodeItem(dlNodeName: string): t.Statement {\r\n return this.optionalExpression(\r\n dlNodeName,\r\n this.t.callExpression(\r\n this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('update')),\r\n this.updateParams\r\n )\r\n );\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport BaseGenerator from './BaseGenerator';\r\n\r\nexport default class CondGenerator extends BaseGenerator {\r\n /**\r\n * @View\r\n * $thisCond.cond = ${idx}\r\n */\r\n geneCondIdx(idx: number): t.ExpressionStatement {\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('cond')),\r\n this.t.numericLiteral(idx)\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * if ($thisCond.cond === ${idx}) {\r\n * $thisCond.didntChange = true\r\n * return []\r\n * }\r\n */\r\n geneCondCheck(idx: number): t.IfStatement {\r\n return this.t.ifStatement(\r\n this.t.binaryExpression(\r\n '===',\r\n this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('cond')),\r\n this.t.numericLiteral(idx)\r\n ),\r\n this.t.blockStatement([\r\n this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('didntChange')),\r\n this.t.booleanLiteral(true)\r\n )\r\n ),\r\n this.t.returnStatement(this.t.arrayExpression([])),\r\n ])\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}?.updateCond(key)\r\n */\r\n updateCondNodeCond(dlNodeName: string): t.Statement {\r\n return this.optionalExpression(\r\n dlNodeName,\r\n this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('updateCond')), [\r\n ...this.updateParams.slice(1),\r\n ])\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}?.update(changed)\r\n */\r\n updateCondNode(dlNodeName: string): t.Statement {\r\n return this.optionalExpression(\r\n dlNodeName,\r\n this.t.callExpression(\r\n this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('update')),\r\n this.updateParams\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} = new CondNode(${depNum}, ($thisCond) => {})\r\n */\r\n declareCondNode(dlNodeName: string, condFunc: t.BlockStatement, deps: number[]): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.identifier(dlNodeName),\r\n this.t.newExpression(this.t.identifier(this.importMap.CondNode), [\r\n this.t.numericLiteral(CondGenerator.calcDependencyNum(deps)),\r\n this.t.arrowFunctionExpression([this.t.identifier('$thisCond')], condFunc),\r\n ])\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * return $thisCond.cond === ${branchIdx} ? [${nodeNames}] : $thisCond.updateCond()\r\n */\r\n geneCondReturnStatement(nodeNames: string[], branchIdx: number): t.Statement {\r\n // ---- If the returned cond is not the last one,\r\n // it means it's been altered in the childrenNodes,\r\n // so we update the cond again to get the right one\r\n return this.t.returnStatement(\r\n this.t.conditionalExpression(\r\n this.t.binaryExpression(\r\n '===',\r\n this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('cond')),\r\n this.t.numericLiteral(branchIdx)\r\n ),\r\n this.t.arrayExpression(nodeNames.map(name => this.t.identifier(name))),\r\n this.t.callExpression(\r\n this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('updateCond')),\r\n this.updateParams.slice(1)\r\n )\r\n )\r\n );\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport { type IfParticle, type IfBranch } from '@inula/reactivity-parser';\r\nimport CondGenerator from '../HelperGenerators/CondGenerator';\r\n\r\nexport default class IfGenerator extends CondGenerator {\r\n run() {\r\n const { branches } = this.viewParticle as IfParticle;\r\n const deps = branches.flatMap(({ condition }) => condition.dependencyIndexArr ?? []);\r\n\r\n // ---- declareIfNode\r\n const dlNodeName = this.generateNodeName();\r\n this.addInitStatement(this.declareIfNode(dlNodeName, branches, deps));\r\n\r\n this.addUpdateStatements(deps, this.updateCondNodeCond(dlNodeName));\r\n this.addUpdateStatementsWithoutDep(this.updateCondNode(dlNodeName));\r\n\r\n return dlNodeName;\r\n }\r\n\r\n /**\r\n * @View\r\n * if (${test}) { ${body} } else { ${alternate} }\r\n */\r\n geneIfStatement(test: t.Expression, body: t.Statement[], alternate: t.Statement): t.IfStatement {\r\n return this.t.ifStatement(test, this.t.blockStatement(body), alternate);\r\n }\r\n\r\n /**\r\n * @View\r\n * const ${dlNodeName} = new IfNode(($thisCond) => {\r\n * if (cond1) {\r\n * if ($thisCond.cond === 0) return\r\n * ${children}\r\n * $thisCond.cond = 0\r\n * return [nodes]\r\n * } else if (cond2) {\r\n * if ($thisCond.cond === 1) return\r\n * ${children}\r\n * $thisCond.cond = 1\r\n * return [nodes]\r\n * }\r\n * })\r\n */\r\n private declareIfNode(dlNodeName: string, branches: IfBranch[], deps: number[]): t.Statement {\r\n // ---- If no else statement, add one\r\n if (\r\n !this.t.isBooleanLiteral(branches[branches.length - 1].condition.value, {\r\n value: true,\r\n })\r\n ) {\r\n branches.push({\r\n condition: {\r\n value: this.t.booleanLiteral(true),\r\n dependencyIndexArr: [],\r\n dependenciesNode: this.t.arrayExpression([]),\r\n dynamic: false,\r\n },\r\n children: [],\r\n });\r\n }\r\n const ifStatement = branches.reverse().reduce((acc, { condition, children }, i) => {\r\n const idx = branches.length - i - 1;\r\n // ---- Generate children\r\n const [childStatements, topLevelNodes, updateStatements, nodeIdx] = this.generateChildren(children, false, true);\r\n\r\n // ---- Even if no updateStatements, we still need reassign an empty updateFunc\r\n // to overwrite the previous one\r\n /**\r\n * $thisCond.updateFunc = (changed) => { ${updateStatements} }\r\n */\r\n const updateNode = this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('updateFunc')),\r\n this.t.arrowFunctionExpression(this.updateParams, this.geneUpdateBody(updateStatements))\r\n )\r\n );\r\n\r\n // ---- Update func\r\n childStatements.unshift(...this.declareNodes(nodeIdx), updateNode);\r\n\r\n // ---- Check cond and update cond\r\n childStatements.unshift(this.geneCondCheck(idx), this.geneCondIdx(idx));\r\n\r\n // ---- Return statement\r\n childStatements.push(this.geneCondReturnStatement(topLevelNodes, idx));\r\n\r\n // ---- else statement\r\n if (i === 0) return this.t.blockStatement(childStatements);\r\n\r\n return this.geneIfStatement(condition.value, childStatements, acc);\r\n }, undefined);\r\n\r\n return this.declareCondNode(dlNodeName, this.t.blockStatement([ifStatement]), deps);\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport { type ViewParticle, type DependencyProp, type EnvParticle } from '@inula/reactivity-parser';\r\nimport PropViewGenerator from '../HelperGenerators/PropViewGenerator';\r\n\r\nexport default class EnvGenerator extends PropViewGenerator {\r\n run() {\r\n let { props } = this.viewParticle as EnvParticle;\r\n props = this.alterPropViews(props)!;\r\n const { children } = this.viewParticle as EnvParticle;\r\n\r\n const dlNodeName = this.generateNodeName();\r\n\r\n this.addInitStatement(this.declareEnvNode(dlNodeName, props));\r\n\r\n // ---- Children\r\n this.addInitStatement(this.geneEnvChildren(dlNodeName, children));\r\n\r\n // ---- Update props\r\n Object.entries(props).forEach(([key, { dependencyIndexArr, value, dependenciesNode }]) => {\r\n if (!dependencyIndexArr) return;\r\n this.addUpdateStatements(dependencyIndexArr, this.updateEnvNode(dlNodeName, key, value, dependenciesNode));\r\n });\r\n\r\n return dlNodeName;\r\n }\r\n\r\n /**\r\n * @View\r\n * { ${key}: ${value}, ... }\r\n * { ${key}: ${deps}, ... }\r\n */\r\n private generateEnvs(props: Record): t.Expression[] {\r\n return [\r\n this.t.objectExpression(\r\n Object.entries(props).map(([key, { value }]) => this.t.objectProperty(this.t.identifier(key), value))\r\n ),\r\n this.t.objectExpression(\r\n Object.entries(props)\r\n .map(\r\n ([key, { dependenciesNode }]) =>\r\n dependenciesNode && this.t.objectProperty(this.t.identifier(key), dependenciesNode)\r\n )\r\n .filter(Boolean) as t.ObjectProperty[]\r\n ),\r\n ];\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} = new EnvNode(envs)\r\n */\r\n private declareEnvNode(dlNodeName: string, props: Record): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.identifier(dlNodeName),\r\n this.t.newExpression(this.t.identifier(this.importMap.EnvNode), this.generateEnvs(props))\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}.initNodes([${childrenNames}])\r\n */\r\n private geneEnvChildren(dlNodeName: string, children: ViewParticle[]): t.Statement {\r\n const [statements, childrenNames] = this.generateChildren(children);\r\n this.addInitStatement(...statements);\r\n return this.t.expressionStatement(\r\n this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('initNodes')), [\r\n this.t.arrayExpression(childrenNames.map(name => this.t.identifier(name))),\r\n ])\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}.updateEnv(${key}, () => ${value}, ${dependenciesNode})\r\n */\r\n private updateEnvNode(\r\n dlNodeName: string,\r\n key: string,\r\n value: t.Expression,\r\n dependenciesNode: t.ArrayExpression\r\n ): t.Statement {\r\n return this.optionalExpression(\r\n dlNodeName,\r\n this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('updateEnv')), [\r\n this.t.stringLiteral(key),\r\n this.t.arrowFunctionExpression([], value),\r\n dependenciesNode,\r\n ])\r\n );\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport { type TextParticle } from '@inula/reactivity-parser';\r\nimport BaseGenerator from '../HelperGenerators/BaseGenerator';\r\n\r\nexport default class TextGenerator extends BaseGenerator {\r\n run() {\r\n const { content } = this.viewParticle as TextParticle;\r\n\r\n const dlNodeName = this.generateNodeName();\r\n\r\n this.addInitStatement(this.declareTextNode(dlNodeName, content.value, content.dependenciesNode));\r\n\r\n if (content.dynamic) {\r\n this.addUpdateStatements(\r\n content.dependencyIndexArr,\r\n this.updateTextNode(dlNodeName, content.value, content.dependenciesNode)\r\n );\r\n }\r\n\r\n return dlNodeName;\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} = createTextNode(${value}, ${deps})\r\n */\r\n private declareTextNode(dlNodeName: string, value: t.Expression, dependenciesNode: t.Expression): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.identifier(dlNodeName),\r\n this.t.callExpression(this.t.identifier(this.importMap.createTextNode), [value, dependenciesNode])\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} && updateText(${dlNodeName}, () => ${value}, ${deps})\r\n */\r\n private updateTextNode(dlNodeName: string, value: t.Expression, dependenciesNode: t.Expression): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.logicalExpression(\r\n '&&',\r\n this.t.identifier(dlNodeName),\r\n this.t.callExpression(this.t.identifier(this.importMap.updateText), [\r\n this.t.identifier(dlNodeName),\r\n this.t.arrowFunctionExpression([], value),\r\n dependenciesNode,\r\n ])\r\n )\r\n );\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport { type ExpParticle } from '@inula/reactivity-parser';\r\nimport ElementGenerator from '../HelperGenerators/ElementGenerator';\r\nimport { DLError } from '../error';\r\n\r\nexport default class ExpGenerator extends ElementGenerator {\r\n run() {\r\n let { content, props } = this.viewParticle as ExpParticle;\r\n content = this.alterPropView(content)!;\r\n props = this.alterPropViews(props);\r\n\r\n const dlNodeName = this.generateNodeName();\r\n\r\n this.addInitStatement(this.declareExpNode(dlNodeName, content.value, content.dependenciesNode));\r\n\r\n if (content.dynamic) {\r\n this.addUpdateStatements(\r\n content.dependencyIndexArr,\r\n this.updateExpNode(dlNodeName, content.value, content.dependenciesNode)\r\n );\r\n }\r\n\r\n if (props) {\r\n Object.entries(props).forEach(([key, { value }]) => {\r\n if (ExpGenerator.lifecycle.includes(key as (typeof ExpGenerator.lifecycle)[number])) {\r\n return this.addInitStatement(\r\n this.addLifecycle(dlNodeName, key as (typeof ExpGenerator.lifecycle)[number], value)\r\n );\r\n }\r\n if (key === 'ref') {\r\n return this.addInitStatement(this.initElement(dlNodeName, value));\r\n }\r\n if (key === 'elements') {\r\n return this.addInitStatement(this.initElement(dlNodeName, value, true));\r\n }\r\n if (key === 'didUpdate') {\r\n return this.addUpdateStatements(content.dependencyIndexArr, this.addOnUpdate(dlNodeName, value));\r\n }\r\n DLError.warn1(key);\r\n });\r\n }\r\n\r\n return dlNodeName;\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} = new ExpNode(${value}, dependenciesNode)\r\n */\r\n private declareExpNode(dlNodeName: string, value: t.Expression, dependenciesNode: t.ArrayExpression): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.identifier(dlNodeName),\r\n this.t.newExpression(this.t.identifier(this.importMap.ExpNode), [\r\n value,\r\n dependenciesNode ?? this.t.nullLiteral(),\r\n ])\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}.update(() => value, dependenciesNode)\r\n */\r\n private updateExpNode(dlNodeName: string, value: t.Expression, dependenciesNode: t.ArrayExpression): t.Statement {\r\n return this.optionalExpression(\r\n dlNodeName,\r\n this.t.callExpression(this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('update')), [\r\n this.t.arrowFunctionExpression([], value),\r\n dependenciesNode ?? this.t.nullLiteral(),\r\n ])\r\n );\r\n }\r\n}\r\n","import { type DependencyProp, type SnippetParticle } from '@inula/reactivity-parser';\r\nimport type { types as t } from '@babel/core';\r\nimport PropViewGenerator from '../HelperGenerators/PropViewGenerator';\r\n\r\nexport default class SnippetGenerator extends PropViewGenerator {\r\n run() {\r\n let { props } = this.viewParticle as SnippetParticle;\r\n props = this.alterPropViews(props);\r\n const { tag } = this.viewParticle as SnippetParticle;\r\n\r\n const dlNodeName = this.generateNodeName();\r\n\r\n const availableProperties = this.snippetPropMap[tag] ?? [];\r\n\r\n const allDependenciesNode: (t.ArrayExpression | t.NullLiteral)[] = Array.from(\r\n {\r\n length: availableProperties.length,\r\n },\r\n () => this.t.nullLiteral()\r\n );\r\n\r\n const allDependencyIndexArr: number[] = [];\r\n\r\n Object.entries(props).forEach(([key, { value, dependencyIndexArr, dependenciesNode }]) => {\r\n if (key === 'didUpdate') return;\r\n if (SnippetGenerator.lifecycle.includes(key as (typeof SnippetGenerator.lifecycle)[number])) {\r\n this.addInitStatement(this.addLifecycle(dlNodeName, key as (typeof SnippetGenerator.lifecycle)[number], value));\r\n return;\r\n }\r\n if (!dependencyIndexArr || dependencyIndexArr.length === 0) return;\r\n allDependencyIndexArr.push(...dependencyIndexArr);\r\n const depIdx = availableProperties.indexOf(key);\r\n if (dependenciesNode) allDependenciesNode[depIdx] = dependenciesNode;\r\n const propChange = 1 << depIdx;\r\n this.addUpdateStatements(\r\n dependencyIndexArr,\r\n this.updateProp(dlNodeName, propChange, key, value, allDependenciesNode[depIdx])\r\n );\r\n });\r\n if (props.didUpdate) {\r\n this.addUpdateStatements(allDependencyIndexArr, this.addOnUpdate(dlNodeName, props.didUpdate.value));\r\n }\r\n\r\n this.addInitStatement(...this.declareSnippetNode(dlNodeName, tag, props, allDependenciesNode));\r\n\r\n this.addUpdateStatementsWithoutDep(this.updateSnippet(dlNodeName));\r\n\r\n return dlNodeName;\r\n }\r\n\r\n /**\r\n * @View\r\n * { ${key}: ${value}, ... }\r\n */\r\n private genePropNode(props: Record): t.Expression {\r\n return this.t.objectExpression(\r\n Object.entries(props).map(([key, prop]) => {\r\n return this.t.objectProperty(this.t.identifier(key), prop.value);\r\n })\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} = new SnippetNode(${allDependenciesNode})\r\n * this.${tag}({${props}}, ${dlNodeName})\r\n */\r\n private declareSnippetNode(\r\n dlNodeName: string,\r\n tag: string,\r\n props: Record,\r\n allDependenciesNode: (t.ArrayExpression | t.NullLiteral)[]\r\n ): t.Statement[] {\r\n return [\r\n this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.identifier(dlNodeName),\r\n this.t.newExpression(this.t.identifier(this.importMap.SnippetNode), [\r\n this.t.arrayExpression(allDependenciesNode),\r\n ])\r\n )\r\n ),\r\n this.t.expressionStatement(\r\n this.t.callExpression(this.t.memberExpression(this.t.thisExpression(), this.t.identifier(tag)), [\r\n this.genePropNode(props),\r\n this.t.identifier(dlNodeName),\r\n ])\r\n ),\r\n ];\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}.updateProp?.(${propChanged}, ...updateParams, () => { ${key}: ${value} }, allDependenciesNode)\r\n */\r\n private updateProp(\r\n dlNodeName: string,\r\n propChanged: number,\r\n key: string,\r\n value: t.Expression,\r\n allDependenciesNode: t.ArrayExpression | t.NullLiteral\r\n ): t.Statement {\r\n return this.optionalExpression(\r\n dlNodeName,\r\n this.t.optionalCallExpression(\r\n this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('updateProp')),\r\n [\r\n this.t.numericLiteral(propChanged),\r\n ...this.updateParams.slice(1),\r\n this.t.arrowFunctionExpression(\r\n [],\r\n this.t.objectExpression([this.t.objectProperty(this.t.identifier(key), value)])\r\n ),\r\n allDependenciesNode,\r\n ],\r\n true\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}.update(changed)\r\n */\r\n private updateSnippet(dlNodeName: string): t.Statement {\r\n return this.optionalExpression(\r\n dlNodeName,\r\n this.t.optionalCallExpression(\r\n this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('update')),\r\n this.updateParams,\r\n true\r\n )\r\n );\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport { SwitchBranch, SwitchParticle } from '@inula/reactivity-parser';\r\nimport CondGenerator from '../HelperGenerators/CondGenerator';\r\n\r\nexport default class SwitchGenerator extends CondGenerator {\r\n run() {\r\n const { branches, discriminant } = this.viewParticle as SwitchParticle;\r\n\r\n const deps = branches.flatMap(({ case: _case }) => _case.dependencyIndexArr);\r\n deps.push(...discriminant.dependencyIndexArr);\r\n\r\n // ---- declareSwitchNode\r\n const dlNodeName = this.generateNodeName();\r\n this.addInitStatement(this.declareSwitchNode(dlNodeName, discriminant.value, branches, deps));\r\n\r\n this.addUpdateStatements(deps, this.updateCondNodeCond(dlNodeName));\r\n this.addUpdateStatementsWithoutDep(this.updateCondNode(dlNodeName));\r\n\r\n return dlNodeName;\r\n }\r\n\r\n /**\r\n * @View\r\n * const ${dlNodeName} = new CondNode(($thisCond) => {\r\n * switch ($discriminant) {\r\n * case ${case0}:\r\n * if ($thisCond.case === 0) return\r\n * case ${case1}:\r\n * if ($thisCond.case === 1) return\r\n * return [...${case1Nodes}]\r\n * default:\r\n * if ($thisCond.case === 2) return\r\n * }\r\n * _$nodes[0]._$updateFunc = (changed) => {\r\n * _$updates.forEach(update => update(changed))\r\n * })\r\n * return _$nodes\r\n * })\r\n */\r\n private declareSwitchNode(\r\n dlNodeName: string,\r\n discriminant: t.Expression,\r\n branches: SwitchBranch[],\r\n deps: number[]\r\n ): t.Statement {\r\n // ---- Format statements, make fallthrough statements append to the previous case\r\n const formattedBranches: SwitchBranch[] = branches.map(({ case: _case, break: _break, children }, idx) => {\r\n if (!_break) {\r\n for (let i = idx + 1; i < branches.length; i++) {\r\n children.push(...branches[i].children);\r\n if (branches[i].break) break;\r\n }\r\n }\r\n return { case: _case, break: _break, children };\r\n });\r\n // ---- Add default case\r\n const defaultCaseIdx = formattedBranches.findIndex(({ case: _case }) => _case === null);\r\n if (defaultCaseIdx === -1) {\r\n formattedBranches.push({\r\n case: {\r\n value: this.t.booleanLiteral(true),\r\n dependencyIndexArr: [],\r\n dependenciesNode: this.t.arrayExpression([]),\r\n dynamic: false,\r\n },\r\n break: true,\r\n children: [],\r\n });\r\n }\r\n\r\n const switchStatements = formattedBranches.map(({ case: _case, children }, idx) => {\r\n // ---- Generate case statements\r\n const [childStatements, topLevelNodes, updateStatements, nodeIdx] = this.generateChildren(children, false, true);\r\n\r\n // ---- Even if no updateStatements, we still need reassign an empty updateFunc\r\n // to overwrite the previous one\r\n /**\r\n * $thisCond.updateFunc = (changed) => { ${updateStatements} }\r\n */\r\n const updateNode = this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.memberExpression(this.t.identifier('$thisCond'), this.t.identifier('updateFunc')),\r\n this.t.arrowFunctionExpression(this.updateParams, this.geneUpdateBody(updateStatements))\r\n )\r\n );\r\n\r\n // ---- Update func\r\n childStatements.unshift(...this.declareNodes(nodeIdx), updateNode);\r\n\r\n // ---- Check cond and update cond\r\n childStatements.unshift(this.geneCondCheck(idx), this.geneCondIdx(idx));\r\n\r\n // ---- Return statement\r\n childStatements.push(this.geneCondReturnStatement(topLevelNodes, idx));\r\n\r\n return this.t.switchCase(_case ? _case.value : null, [this.t.blockStatement(childStatements)]);\r\n });\r\n\r\n return this.declareCondNode(\r\n dlNodeName,\r\n this.t.blockStatement([this.t.switchStatement(discriminant, switchStatements)]),\r\n deps\r\n );\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport BaseGenerator from '../HelperGenerators/BaseGenerator';\r\nimport { TryParticle, type ViewParticle } from '@inula/reactivity-parser';\r\n\r\nexport default class TryGenerator extends BaseGenerator {\r\n run() {\r\n const { children, catchChildren, exception } = this.viewParticle as TryParticle;\r\n\r\n const dlNodeName = this.generateNodeName();\r\n\r\n // ---- Declare for node\r\n this.addInitStatement(this.declareTryNode(dlNodeName, children, catchChildren, exception));\r\n\r\n // ---- Update statements\r\n this.addUpdateStatementsWithoutDep(this.declareUpdate(dlNodeName));\r\n\r\n return dlNodeName;\r\n }\r\n\r\n /**\r\n * @View\r\n * $setUpdate($catchable(updateStatements))\r\n * ${children}\r\n * return [...${topLevelNodes}]\r\n */\r\n private declareTryNodeUpdate(children: ViewParticle[], addCatchable = true): t.Statement[] {\r\n const [childStatements, topLevelNodes, updateStatements, nodeIdx] = this.generateChildren(children, false, true);\r\n\r\n const updateFunc = this.t.arrowFunctionExpression(\r\n [this.t.identifier('$changed')],\r\n this.geneUpdateBody(updateStatements)\r\n );\r\n\r\n childStatements.unshift(\r\n ...this.declareNodes(nodeIdx),\r\n this.t.expressionStatement(\r\n this.t.callExpression(\r\n this.t.identifier('$setUpdate'),\r\n addCatchable ? [this.t.callExpression(this.t.identifier('$catchable'), [updateFunc])] : [updateFunc]\r\n )\r\n )\r\n );\r\n childStatements.push(\r\n this.t.returnStatement(this.t.arrayExpression(topLevelNodes.map(node => this.t.identifier(node))))\r\n );\r\n\r\n return childStatements;\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName} = new TryNode(($setUpdate, $catchable) => {\r\n * ${children}\r\n * }, ($setUpdate, e) => {\r\n * ${catchChildren}\r\n * })\r\n * })\r\n */\r\n private declareTryNode(\r\n dlNodeName: string,\r\n children: ViewParticle[],\r\n catchChildren: ViewParticle[],\r\n exception: TryParticle['exception']\r\n ): t.Statement {\r\n const exceptionNodes = exception ? [exception] : [];\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.identifier(dlNodeName),\r\n this.t.newExpression(this.t.identifier(this.importMap.TryNode), [\r\n this.t.arrowFunctionExpression(\r\n [this.t.identifier('$setUpdate'), this.t.identifier('$catchable')],\r\n this.t.blockStatement(this.declareTryNodeUpdate(children, true))\r\n ),\r\n this.t.arrowFunctionExpression(\r\n [this.t.identifier('$setUpdate'), ...exceptionNodes],\r\n this.t.blockStatement(this.declareTryNodeUpdate(catchChildren, false))\r\n ),\r\n ])\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * ${dlNodeName}?.update(changed)\r\n */\r\n private declareUpdate(dlNodeName: string): t.Statement {\r\n return this.optionalExpression(\r\n dlNodeName,\r\n this.t.callExpression(\r\n this.t.memberExpression(this.t.identifier(dlNodeName), this.t.identifier('update')),\r\n this.updateParams\r\n )\r\n );\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport { type ViewParticle } from '@inula/reactivity-parser';\r\nimport { type ViewGeneratorConfig } from './types';\r\nimport BaseGenerator, { prefixMap } from './HelperGenerators/BaseGenerator';\r\nimport CompGenerator from './NodeGenerators/CompGenerator';\r\nimport HTMLGenerator from './NodeGenerators/HTMLGenerator';\r\nimport TemplateGenerator from './NodeGenerators/TemplateGenerator';\r\nimport ForGenerator from './NodeGenerators/ForGenerator';\r\nimport IfGenerator from './NodeGenerators/IfGenerator';\r\nimport EnvGenerator from './NodeGenerators/EnvGenerator';\r\nimport TextGenerator from './NodeGenerators/TextGenerator';\r\nimport ExpGenerator from './NodeGenerators/ExpGenerator';\r\nimport SnippetGenerator from './NodeGenerators/SnippetGenerator';\r\nimport SwitchGenerator from './NodeGenerators/SwitchGenerator';\r\nimport TryGenerator from './NodeGenerators/TryGenerator';\r\n\r\nexport default class ViewGenerator {\r\n config: ViewGeneratorConfig;\r\n t: typeof t;\r\n\r\n /**\r\n * @brief Construct the view generator from config\r\n * @param config\r\n */\r\n constructor(config: ViewGeneratorConfig) {\r\n this.config = config;\r\n this.t = config.babelApi.types;\r\n this.templateIdx = config.templateIdx;\r\n }\r\n\r\n /**\r\n * @brief Different generator classes for different view particle types\r\n */\r\n static generatorMap: Record = {\r\n comp: CompGenerator,\r\n html: HTMLGenerator,\r\n template: TemplateGenerator,\r\n for: ForGenerator,\r\n if: IfGenerator,\r\n switch: SwitchGenerator,\r\n env: EnvGenerator,\r\n text: TextGenerator,\r\n exp: ExpGenerator,\r\n snippet: SnippetGenerator,\r\n try: TryGenerator,\r\n };\r\n\r\n /**\r\n * @brief Generate the view given the view particles, mainly used for child particles parsing\r\n * @param viewParticles\r\n * @returns [initStatements, updateStatements, classProperties, topLevelNodes]\r\n */\r\n generateChildren(\r\n viewParticles: ViewParticle[]\r\n ): [t.Statement[], Record, t.ClassProperty[], string[]] {\r\n const allInitStatements: t.Statement[] = [];\r\n const allClassProperties: t.ClassProperty[] = [];\r\n const allUpdateStatements: Record = {};\r\n const topLevelNodes: string[] = [];\r\n\r\n viewParticles.forEach(viewParticle => {\r\n const [initStatements, updateStatements, classProperties, nodeName] = this.generateChild(viewParticle);\r\n allInitStatements.push(...initStatements);\r\n Object.entries(updateStatements).forEach(([depNum, statements]) => {\r\n if (!allUpdateStatements[Number(depNum)]) {\r\n allUpdateStatements[Number(depNum)] = [];\r\n }\r\n allUpdateStatements[Number(depNum)].push(...statements);\r\n });\r\n allClassProperties.push(...classProperties);\r\n topLevelNodes.push(nodeName);\r\n });\r\n\r\n return [allInitStatements, allUpdateStatements, allClassProperties, topLevelNodes];\r\n }\r\n\r\n nodeIdx = -1;\r\n templateIdx = -1;\r\n /**\r\n * @brief Generate the view given the view particle, using generator from the map\r\n * @param viewParticle\r\n * @returns\r\n */\r\n generateChild(viewParticle: ViewParticle) {\r\n const { type } = viewParticle;\r\n const GeneratorClass = ViewGenerator.generatorMap[type];\r\n if (!GeneratorClass) {\r\n throw new Error(`Unknown view particle type: ${type}`);\r\n }\r\n const generator = new GeneratorClass(viewParticle, this.config);\r\n generator.nodeIdx = this.nodeIdx;\r\n generator.templateIdx = this.templateIdx;\r\n const result = generator.generate();\r\n this.nodeIdx = generator.nodeIdx;\r\n this.templateIdx = generator.templateIdx;\r\n return result;\r\n }\r\n\r\n /**\r\n * @View\r\n * let node1, node2, ...\r\n */\r\n declareNodes(): t.Statement[] {\r\n if (this.nodeIdx === -1) return [];\r\n return [\r\n this.t.variableDeclaration(\r\n 'let',\r\n Array.from({ length: this.nodeIdx + 1 }, (_, i) =>\r\n this.t.variableDeclarator(this.t.identifier(`${prefixMap.node}${i}`))\r\n )\r\n ),\r\n ];\r\n }\r\n\r\n get updateParams() {\r\n return [this.t.identifier('$changed')];\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport { type ViewParticle } from '@inula/reactivity-parser';\r\nimport ViewGenerator from './ViewGenerator';\r\n\r\nexport default class MainViewGenerator extends ViewGenerator {\r\n /**\r\n * @brief Generate the main view, i.e., View() { ... }\r\n * @param viewParticles\r\n * @returns [viewBody, classProperties, templateIdx]\r\n */\r\n generate(viewParticles: ViewParticle[]): [t.BlockStatement, t.ClassProperty[], number] {\r\n const allClassProperties: t.ClassProperty[] = [];\r\n const allInitStatements: t.Statement[] = [];\r\n const allUpdateStatements: Record = {};\r\n const topLevelNodes: string[] = [];\r\n\r\n viewParticles.forEach(viewParticle => {\r\n const [initStatements, updateStatements, classProperties, nodeName] = this.generateChild(viewParticle);\r\n allInitStatements.push(...initStatements);\r\n Object.entries(updateStatements).forEach(([depNum, statements]) => {\r\n if (!allUpdateStatements[Number(depNum)]) {\r\n allUpdateStatements[Number(depNum)] = [];\r\n }\r\n allUpdateStatements[Number(depNum)].push(...statements);\r\n });\r\n allClassProperties.push(...classProperties);\r\n topLevelNodes.push(nodeName);\r\n });\r\n\r\n const viewBody = this.t.blockStatement([\r\n ...this.declareNodes(),\r\n ...this.geneUpdate(allUpdateStatements),\r\n ...allInitStatements,\r\n this.geneReturn(topLevelNodes),\r\n ]);\r\n\r\n return [viewBody, allClassProperties, this.templateIdx];\r\n }\r\n\r\n /**\r\n * @View\r\n * this._$update = ($changed) => {\r\n * if ($changed & 1) {\r\n * ...\r\n * }\r\n * ...\r\n * }\r\n */\r\n private geneUpdate(updateStatements: Record): t.Statement[] {\r\n if (Object.keys(updateStatements).length === 0) return [];\r\n return [\r\n this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.memberExpression(this.t.thisExpression(), this.t.identifier('_$update'), false),\r\n this.t.arrowFunctionExpression(\r\n this.updateParams,\r\n this.t.blockStatement([\r\n ...Object.entries(updateStatements)\r\n .filter(([depNum]) => depNum !== '0')\r\n .map(([depNum, statements]) => {\r\n return this.t.ifStatement(\r\n this.t.binaryExpression('&', this.t.identifier('$changed'), this.t.numericLiteral(Number(depNum))),\r\n this.t.blockStatement(statements)\r\n );\r\n }),\r\n ...(updateStatements[0] ?? []),\r\n ])\r\n )\r\n )\r\n ),\r\n ];\r\n }\r\n\r\n /**\r\n * @View\r\n * return [${nodeNames}]\r\n */\r\n private geneReturn(topLevelNodes: string[]) {\r\n return this.t.returnStatement(this.t.arrayExpression(topLevelNodes.map(nodeName => this.t.identifier(nodeName))));\r\n }\r\n}\r\n","import { type types as t } from '@babel/core';\r\nimport { type ViewParticle } from '@inula/reactivity-parser';\r\nimport ViewGenerator from './ViewGenerator';\r\n\r\nexport default class SnippetGenerator extends ViewGenerator {\r\n /**\r\n * @brief Generate the snippet, i.e., @View MySnippet({ prop1, prop2 }) { ... }\r\n * This is different from the main view in that it has a props node\r\n * and is needed to parse twice,\r\n * 1. for this.deps (viewParticlesWithPropertyDep)\r\n * 2. for props that passed in this snippet (viewParticlesWithIdentityDep)\r\n * @param viewParticlesWithPropertyDep\r\n * @param viewParticlesWithIdentityDep\r\n * @param propsNode\r\n * @returns [viewBody, classProperties, templateIdx]\r\n */\r\n generate(\r\n viewParticlesWithPropertyDep: ViewParticle[],\r\n viewParticlesWithIdentityDep: ViewParticle[],\r\n propsNode: t.ObjectPattern\r\n ): [t.BlockStatement, t.ClassProperty[], number] {\r\n const allClassProperties: t.ClassProperty[] = [];\r\n const allInitStatements: t.Statement[] = [];\r\n const propertyUpdateStatements: Record = {};\r\n const identifierUpdateStatements: Record = {};\r\n const topLevelNodes: string[] = [];\r\n\r\n const templateIdx = this.templateIdx;\r\n viewParticlesWithPropertyDep.forEach(viewParticle => {\r\n const [initStatements, updateStatements, classProperties, nodeName] = this.generateChild(viewParticle);\r\n allInitStatements.push(...initStatements);\r\n Object.entries(updateStatements).forEach(([depNum, statements]) => {\r\n if (!propertyUpdateStatements[Number(depNum)]) {\r\n propertyUpdateStatements[Number(depNum)] = [];\r\n }\r\n propertyUpdateStatements[Number(depNum)].push(...statements);\r\n });\r\n allClassProperties.push(...classProperties);\r\n topLevelNodes.push(nodeName);\r\n });\r\n // ---- Recover the templateIdx and reinitialize the nodeIdx\r\n this.templateIdx = templateIdx;\r\n this.nodeIdx = -1;\r\n viewParticlesWithIdentityDep.forEach(viewParticle => {\r\n // ---- We only need the update statements for the second props parsing\r\n // because all the init statements are already generated\r\n // a little bit time consuming but otherwise we need to write two different generators\r\n const [, updateStatements] = this.generateChild(viewParticle);\r\n\r\n Object.entries(updateStatements).forEach(([depNum, statements]) => {\r\n if (!identifierUpdateStatements[Number(depNum)]) {\r\n identifierUpdateStatements[Number(depNum)] = [];\r\n }\r\n identifierUpdateStatements[Number(depNum)].push(...statements);\r\n });\r\n });\r\n\r\n const hasPropertyUpdateFunc = Object.keys(propertyUpdateStatements).length > 0;\r\n const hasIdentifierUpdateFunc = Object.keys(identifierUpdateStatements).filter(n => n !== '0').length > 0;\r\n\r\n const viewBody = this.t.blockStatement([\r\n ...this.declareNodes(),\r\n ...(hasPropertyUpdateFunc ? [this.geneUpdateFunc('update', propertyUpdateStatements)] : []),\r\n ...(hasIdentifierUpdateFunc ? [this.geneUpdateFunc('updateProp', identifierUpdateStatements, propsNode)] : []),\r\n ...allInitStatements,\r\n this.geneAddNodes(topLevelNodes),\r\n ]);\r\n\r\n return [viewBody, allClassProperties, this.templateIdx];\r\n }\r\n\r\n /**\r\n * @View\r\n * $snippetNode._$nodes = ${topLevelNodes}\r\n */\r\n geneAddNodes(topLevelNodes: string[]): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.memberExpression(this.t.identifier('$snippetNode'), this.t.identifier('_$nodes')),\r\n this.t.arrayExpression(topLevelNodes.map(nodeName => this.t.identifier(nodeName)))\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * $snippetNode.${name} = (changed) => { ${updateStatements} }\r\n */\r\n geneUpdateFunc(\r\n name: string,\r\n updateStatements: Record,\r\n propsNode?: t.ObjectPattern\r\n ): t.Statement {\r\n return this.t.expressionStatement(\r\n this.t.assignmentExpression(\r\n '=',\r\n this.t.memberExpression(this.t.identifier('$snippetNode'), this.t.identifier(name)),\r\n this.geneUpdateBody(updateStatements, propsNode)\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @View\r\n * (changed) => {\r\n * if (changed & 1) {\r\n * ...\r\n * }\r\n * ...\r\n * }\r\n */\r\n private geneUpdateBody(\r\n updateStatements: Record,\r\n propsNode?: t.ObjectPattern\r\n ): t.ArrowFunctionExpression {\r\n const bodyEntryNodes: t.Statement[] = [];\r\n // ---- Args\r\n const args: t.Identifier[] = this.updateParams;\r\n if (propsNode) {\r\n // ---- Add $snippetProps and $depsArr to args\r\n args.push(this.t.identifier('$snippetPropsFunc'), this.t.identifier('$depsArr'));\r\n\r\n // ---- Add cache\r\n /**\r\n * if ($snippetNode.cached(depsArr, changed)) return\r\n */\r\n bodyEntryNodes.push(\r\n this.t.ifStatement(\r\n this.t.callExpression(\r\n this.t.memberExpression(this.t.identifier('$snippetNode'), this.t.identifier('cached')),\r\n [this.t.identifier('$depsArr'), this.t.identifier('$changed')]\r\n ),\r\n this.t.blockStatement([this.t.returnStatement()])\r\n )\r\n );\r\n\r\n /**\r\n * const $snippetProps = $snippetPropsFunc()\r\n */\r\n bodyEntryNodes.push(\r\n this.t.variableDeclaration('const', [\r\n this.t.variableDeclarator(\r\n this.t.identifier('$snippetProps'),\r\n this.t.callExpression(this.t.identifier('$snippetPropsFunc'), [])\r\n ),\r\n ])\r\n );\r\n\r\n /**\r\n * ${prop} = $snippetProps\r\n */\r\n propsNode.properties\r\n .filter(prop => this.t.isObjectProperty(prop))\r\n .forEach((prop, idx) => {\r\n const depNum = 1 << idx;\r\n if (!updateStatements[depNum]) updateStatements[depNum] = [];\r\n updateStatements[depNum].unshift(\r\n this.t.expressionStatement(\r\n this.t.assignmentExpression('=', this.t.objectPattern([prop]), this.t.identifier('$snippetProps'))\r\n )\r\n );\r\n });\r\n }\r\n // ---- End\r\n const runAllStatements = propsNode ? [] : updateStatements[0] ?? [];\r\n\r\n return this.t.arrowFunctionExpression(\r\n args,\r\n this.t.blockStatement([\r\n ...bodyEntryNodes,\r\n ...Object.entries(updateStatements)\r\n .filter(([depNum]) => depNum !== '0')\r\n .map(([depNum, statements]) => {\r\n return this.t.ifStatement(\r\n this.t.binaryExpression('&', this.t.identifier('$changed'), this.t.numericLiteral(Number(depNum))),\r\n this.t.blockStatement(statements)\r\n );\r\n }),\r\n ...runAllStatements,\r\n ])\r\n );\r\n }\r\n}\r\n","import { type ViewParticle } from '@inula/reactivity-parser';\r\nimport { type ViewGeneratorConfig } from './types';\r\nimport { type types as t } from '@babel/core';\r\nimport MainViewGenerator from './MainViewGenerator';\r\nimport SnippetGenerator from './SnippetGenerator';\r\n\r\nexport function generateView(\r\n viewParticles: ViewParticle[],\r\n config: ViewGeneratorConfig\r\n): [t.BlockStatement, t.ClassProperty[], number] {\r\n return new MainViewGenerator(config).generate(viewParticles);\r\n}\r\n\r\nexport function generateSnippet(\r\n viewParticlesWithPropertyDep: ViewParticle[],\r\n viewParticlesWithIdentityDep: ViewParticle[],\r\n propNode: t.ObjectPattern,\r\n config: ViewGeneratorConfig\r\n): [t.BlockStatement, t.ClassProperty[], number] {\r\n return new SnippetGenerator(config).generate(viewParticlesWithPropertyDep, viewParticlesWithIdentityDep, propNode);\r\n}\r\n\r\nexport * from './types';\r\n","import { type types as t, NodePath } from '@babel/core';\r\nimport * as babel from '@babel/core';\r\nimport { Option } from './types';\r\nimport type { Scope } from '@babel/traverse';\r\n\r\nfunction replaceFnWithClass(path: NodePath, classTransformer: ClassComponentTransformer) {\r\n const originalName = path.node.id.name;\r\n const tempName = path.node.id.name + 'Temp';\r\n const classComp = classTransformer.genClassComponent(tempName);\r\n path.replaceWith(classComp);\r\n path.scope.rename(tempName, originalName);\r\n}\r\n\r\nexport class PluginProvider {\r\n // ---- Plugin Level ----\r\n private readonly babelApi: typeof babel;\r\n private readonly t: typeof t;\r\n\r\n private programNode: t.Program | undefined;\r\n\r\n constructor(babelApi: typeof babel, options: Option) {\r\n this.babelApi = babelApi;\r\n this.t = babelApi.types;\r\n }\r\n\r\n functionDeclarationVisitor(path: NodePath): void {\r\n // find Component function by:\r\n // 1. has JSXElement as return value\r\n // 2. name is capitalized\r\n if (path.node.id?.name[0] !== path.node.id?.name[0].toUpperCase()) return;\r\n const returnStatement = path.node.body.body.find(n => this.t.isReturnStatement(n)) as t.ReturnStatement;\r\n if (!returnStatement) return;\r\n if (!(this.t.isJSXElement(returnStatement.argument) || this.t.isJSXFragment(returnStatement.argument))) return;\r\n const classTransformer = new ClassComponentTransformer(this.babelApi, path);\r\n // transform the parameters to props\r\n const params = path.node.params;\r\n const props = params[0];\r\n classTransformer.transformProps(props);\r\n // iterate the function body orderly\r\n const body = path.node.body.body;\r\n body.forEach((node, idx) => {\r\n if (this.t.isVariableDeclaration(node)) {\r\n classTransformer.transformStateDeclaration(node);\r\n return;\r\n }\r\n // handle method\r\n if (this.t.isFunctionDeclaration(node)) {\r\n classTransformer.transformMethods(node);\r\n return;\r\n }\r\n\r\n // handle watch\r\n if (classTransformer.shouldTransformWatch(node)) {\r\n // transform the watch statement to watch method\r\n classTransformer.transformWatch(node);\r\n return;\r\n }\r\n\r\n // handle return statement\r\n if (this.t.isReturnStatement(node)) {\r\n // handle early return\r\n if (idx !== body.length - 1) {\r\n // transform the return statement to render method\r\n // TODO: handle early return\r\n throw new Error('Early return is not supported yet.');\r\n }\r\n // transform the return statement to render method\r\n classTransformer.transformRenderMethod(node);\r\n return;\r\n }\r\n });\r\n\r\n // replace the function declaration with class declaration\r\n replaceFnWithClass(path, classTransformer);\r\n }\r\n}\r\n\r\ntype ToWatchNode =\r\n | t.ExpressionStatement\r\n | t.ForStatement\r\n | t.WhileStatement\r\n | t.IfStatement\r\n | t.SwitchStatement\r\n | t.TryStatement;\r\n\r\nclass ClassComponentTransformer {\r\n properties: (t.ClassProperty | t.ClassMethod)[] = [];\r\n private readonly babelApi: typeof babel;\r\n private readonly t: typeof t;\r\n private readonly functionScope: Scope;\r\n\r\n valueWrapper(node) {\r\n return this.t.file(this.t.program([this.t.isStatement(node) ? node : this.t.expressionStatement(node)]));\r\n }\r\n\r\n addProperty(prop: t.ClassProperty | t.ClassMethod, name?: string) {\r\n this.properties.push(prop);\r\n if (name) {\r\n // replace the variable in scope to process the variable in class scope\r\n // e.g. replace () => count++ to () => this.count++\r\n // TODO: search for better solution\r\n this.functionScope.rename(name, `this.${name}`);\r\n this.functionScope.path.traverse({\r\n Identifier: path => {\r\n if (path.node.name === `this.${name}`) {\r\n path.replaceWith(this.t.memberExpression(this.t.thisExpression(), this.t.identifier(name)));\r\n }\r\n },\r\n });\r\n }\r\n }\r\n\r\n constructor(babelApi: typeof babel, fnNode: NodePath) {\r\n this.babelApi = babelApi;\r\n this.t = babelApi.types;\r\n // get the function body scope\r\n this.functionScope = fnNode.scope;\r\n }\r\n\r\n // transform function component to class component extends View\r\n genClassComponent(name: string) {\r\n return this.t.classDeclaration(\r\n this.t.identifier(name),\r\n this.t.identifier('View'),\r\n this.t.classBody(this.properties),\r\n []\r\n );\r\n }\r\n\r\n /**\r\n * Transform state declaration to class property\r\n * if the variable is declared with `let` or `const`, it should be transformed to class property\r\n * @param node\r\n */\r\n transformStateDeclaration(node: t.VariableDeclaration) {\r\n // iterate the declarations\r\n node.declarations.forEach(declaration => {\r\n const id = declaration.id;\r\n // handle destructuring\r\n if (this.t.isObjectPattern(id)) {\r\n return this.transformPropsDestructuring(id);\r\n } else if (this.t.isArrayPattern(id)) {\r\n // TODO: handle array destructuring\r\n } else if (this.t.isIdentifier(id)) {\r\n // clone the id\r\n const cloneId = this.t.cloneNode(id);\r\n this.addProperty(this.t.classProperty(cloneId, declaration.init), id.name);\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Transform render method to Body method\r\n * The Body method should return the original return statement\r\n * @param node\r\n */\r\n transformRenderMethod(node: t.ReturnStatement) {\r\n const body = this.t.classMethod(\r\n 'method',\r\n this.t.identifier('Body'),\r\n [],\r\n this.t.blockStatement([node]),\r\n false,\r\n false\r\n );\r\n this.addProperty(body, 'Body');\r\n }\r\n\r\n transformLifeCycle() {}\r\n\r\n transformComputed() {}\r\n\r\n transformMethods(node: t.FunctionDeclaration) {\r\n // transform the function declaration to class method\r\n const methodName = node.id?.name;\r\n if (!methodName) return;\r\n const method = this.t.classMethod(\r\n 'method',\r\n this.t.identifier(methodName),\r\n node.params,\r\n node.body,\r\n node.generator,\r\n node.async\r\n );\r\n this.addProperty(method, methodName);\r\n }\r\n\r\n transformProps(param: t.Identifier | t.RestElement | t.Pattern) {\r\n if (!param) return;\r\n // handle destructuring\r\n if (this.isObjDestructuring(param)) {\r\n this.transformPropsDestructuring(param);\r\n return;\r\n }\r\n if (this.t.isIdentifier(param)) {\r\n // TODO: handle props identifier\r\n return;\r\n }\r\n throw new Error('Unsupported props type, please use object destructuring or identifier.');\r\n }\r\n\r\n // transform node to method with watch decorator\r\n transformWatch(node: ToWatchNode) {\r\n const id = this.functionScope.generateUidIdentifier('watch');\r\n const method = this.t.classMethod('method', id, [], this.t.blockStatement([node]), false, false);\r\n method.decorators = [this.t.decorator(this.t.identifier('Watch'))];\r\n this.addProperty(method);\r\n }\r\n\r\n private isObjDestructuring(param: t.Identifier | t.RestElement | t.Pattern): param is t.ObjectPattern {\r\n return this.t.isObjectPattern(param);\r\n }\r\n\r\n private transformPropsDestructuring(param: t.ObjectPattern) {\r\n const propNames: t.Identifier[] = [];\r\n param.properties.forEach(prop => {\r\n if (this.t.isObjectProperty(prop)) {\r\n const key = prop.key;\r\n if (this.t.isIdentifier(key)) {\r\n if (this.t.isAssignmentPattern(prop.value)) {\r\n // handle default value\r\n const defaultValue = prop.value.right;\r\n this.addProp(key, defaultValue);\r\n propNames.push(key);\r\n return;\r\n } else if (this.t.isIdentifier(prop.value)) {\r\n // handle simple destructuring\r\n this.addProp(key, undefined, prop.value.name === 'children');\r\n propNames.push(key);\r\n return;\r\n } else if (this.t.isObjectPattern(prop.value)) {\r\n // TODO: handle nested destructuring\r\n this.transformPropsDestructuring(prop.value);\r\n return;\r\n }\r\n return;\r\n }\r\n // handle default value\r\n if (this.t.isAssignmentPattern(prop.value)) {\r\n const defaultValue = prop.value.right;\r\n const propName = prop.value.left;\r\n if (this.t.isIdentifier(propName)) {\r\n this.addProp(propName, defaultValue);\r\n propNames.push(propName);\r\n }\r\n // TODO: handle nested destructuring\r\n return;\r\n }\r\n throw new Error('Unsupported props destructuring, please use simple object destructuring.');\r\n } else {\r\n // TODO: handle rest element\r\n }\r\n });\r\n\r\n return propNames;\r\n }\r\n\r\n // add prop to class, like @prop name = '';\r\n private addProp(key: t.Identifier, defaultValue?: t.Expression, isChildren = false) {\r\n // clone the key to avoid reference issue\r\n const id = this.t.cloneNode(key);\r\n this.addProperty(\r\n this.t.classProperty(\r\n id,\r\n defaultValue ?? undefined,\r\n undefined,\r\n // use prop decorator\r\n [this.t.decorator(this.t.identifier(isChildren ? 'Children' : 'Prop'))],\r\n undefined,\r\n false\r\n ),\r\n key.name\r\n );\r\n }\r\n\r\n /**\r\n * Check if the node should be transformed to watch method, including:\r\n * 1. call expression.\r\n * 2. for loop\r\n * 3. while loop\r\n * 4. if statement\r\n * 5. switch statement\r\n * 6. assignment expression\r\n * 7. try statement\r\n * 8. ++/-- expression\r\n * @param node\r\n */\r\n shouldTransformWatch(node: t.Node): node is ToWatchNode {\r\n if (this.t.isExpressionStatement(node)) {\r\n if (this.t.isCallExpression(node.expression)) {\r\n return true;\r\n }\r\n if (this.t.isAssignmentExpression(node.expression)) {\r\n return true;\r\n }\r\n if (this.t.isUpdateExpression(node.expression)) {\r\n return true;\r\n }\r\n }\r\n if (this.t.isForStatement(node)) {\r\n return true;\r\n }\r\n if (this.t.isWhileStatement(node)) {\r\n return true;\r\n }\r\n if (this.t.isIfStatement(node)) {\r\n return true;\r\n }\r\n if (this.t.isSwitchStatement(node)) {\r\n return true;\r\n }\r\n if (this.t.isTryStatement(node)) {\r\n return true;\r\n }\r\n\r\n return false;\r\n }\r\n}\r\n","/*\n * Copyright (c) 2024 Huawei Technologies Co.,Ltd.\n *\n * openInula is licensed under Mulan PSL v2.\n * You can use this software according to the terms and conditions of the Mulan PSL v2.\n * You may obtain a copy of Mulan PSL v2 at:\n *\n * http://license.coscl.org.cn/MulanPSL2\n *\n * THIS SOFTWARE IS PROVIDED ON AN \"AS IS\" BASIS, WITHOUT WARRANTIES OF ANY KIND,\n * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,\n * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.\n * See the Mulan PSL v2 for more details.\n */\n\nimport { PluginObj } from '@babel/core';\nimport { Option } from './types';\nimport * as babel from '@babel/core';\nimport { PluginProvider } from './pluginProvider';\n\nexport default function (api: typeof babel, options: Option): PluginObj {\n const pluginProvider = new PluginProvider(api, options);\n\n return {\n name: 'zouyu-2',\n visitor: {\n FunctionDeclaration(path) {\n pluginProvider.functionDeclarationVisitor(path);\n },\n },\n };\n}\n","/*\n * Copyright (c) 2024 Huawei Technologies Co.,Ltd.\n *\n * openInula is licensed under Mulan PSL v2.\n * You can use this software according to the terms and conditions of the Mulan PSL v2.\n * You may obtain a copy of Mulan PSL v2 at:\n *\n * http://license.coscl.org.cn/MulanPSL2\n *\n * THIS SOFTWARE IS PROVIDED ON AN \"AS IS\" BASIS, WITHOUT WARRANTIES OF ANY KIND,\n * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,\n * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.\n * See the Mulan PSL v2 for more details.\n */\n\nimport { Option } from './types';\nimport type { ConfigAPI, TransformOptions } from '@babel/core';\nimport transformer from './plugin';\n\nexport default function (_: ConfigAPI, options: Option): TransformOptions {\n return {\n plugins: [\n ['@babel/plugin-syntax-jsx'],\n ['@babel/plugin-syntax-typescript', { isTSX: true }],\n [transformer, options],\n ],\n };\n}\nexport const plugin = transformer;\n\nexport type { Option };\n"],"mappings":"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AM,CACxBiC,EAAe,2BAA2BjC,CAAI,CAChD,CACF,CACF,CACF,CCHO,IAAMkC,GAASC,wKpC5BTC,GAAU,QAAQ,IAAI,WAAa,cAEnCC,GAAoB,CAC/B,MAAO,YACP,IAAK,SACP,EAEaC,EAAsB,CAEjC,OACA,MACA,QACA,UACA,SACA,OACA,UAEA,MACA,SACA,QAEA,MACA,SACA,OACF,EAEaC,EAAkB,CAC7B,IACA,OACA,UACA,OACA,UACA,QACA,QACA,IACA,OACA,MACA,MACA,aACA,OACA,KACA,SACA,SACA,UACA,OACA,OACA,MACA,WACA,OACA,WACA,KACA,MACA,UACA,MACA,SACA,MACA,KACA,KACA,KACA,QACA,WACA,aACA,SACA,SACA,OACA,KACA,KACA,KACA,KACA,KACA,KACA,OACA,SACA,SACA,KACA,OACA,IACA,SACA,MACA,QACA,MACA,MACA,QACA,SACA,KACA,OACA,OACA,MACA,OACA,OACA,OACA,QACA,MACA,WACA,SACA,KACA,WACA,SACA,SACA,IACA,UACA,MACA,WACA,IACA,KACA,KACA,OACA,IACA,OACA,SACA,UACA,SACA,OACA,QACA,SACA,OACA,SACA,QACA,MACA,UACA,MACA,QACA,QACA,KACA,WACA,WACA,QACA,KACA,QACA,OACA,QACA,KACA,QACA,IACA,KACA,MACA,QACA,MACA,UACA,SACA,WACA,UACA,MACA,QACA,SACA,MACA,OACA,QACA,WACA,UACA,SACA,UACA,UACA,WACA,WACA,SACA,OACA,UACA,WACA,QACA,YACA,KACA,MACA,SACA,SACA,KACA,MACA,UACA,gBACA,mBACA,SACA,WACA,OACA,OACA,UACA,UACA,gBACA,sBACA,cACA,mBACA,oBACA,oBACA,iBACA,eACA,UACA,UACA,UACA,UACA,UACA,iBACA,UACA,UACA,cACA,eACA,WACA,eACA,qBACA,cACA,SACA,eACA,SACA,gBACA,IACA,QACA,OACA,iBACA,SACA,OACA,WACA,QACA,OACA,UACA,UACA,WACA,iBACA,OACA,MACA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","names":["apiPolyfills","assertVersion","api","range","throwVersionError","version","Object","assign","targets","assumption","declare","builder","options","dirname","_clonedApi2","clonedApi","name","keys","_clonedApi","copyApiObject","declarePreset","exports","proto","test","getPrototypeOf","hasOwnProperty","call","Number","isInteger","Error","limit","stackTraceLimit","err","slice","code","_helperPluginUtils","require","_default","exports","default","declare","api","options","assertVersion","version","legacy","undefined","Error","decoratorsBeforeExport","name","manipulateOptions","generatorOpts","parserOpts","plugins","push","allowCallParenthesized","transform","import_plugin_syntax_decorators","minimatch","createErrorHandler","errorSpace","throwMap","errorMap","warningMap","handleError","map","type","func","code","msg","args","arg","i","methods","notDescribed","DLError","createErrorHandler","ViewParser","config","statement","lastViewUnit","type","childViewUnits","expression","node","conditions","condition","ifBody","altBody","branches","s","caseBodyPre","caseBody","isBreak","children","branch","tryBody","catchBodyNode","left","item","array","key","forBody","forBodyStatements","childNodes","firstStatement","keyNode","directives","statements","forBodyBlockStatement","body","propNode","dlViewPropResult","innerPath","id","newNode","props","n","property","prop","contentProp","tagName","isViewProp","tagType","tag","isPure","tagTarget","parseView","ViewParser","config","context","node","child","type","tag","openingName","name","toMemberExpression","namespace","props","propMap","prop","childUnits","unit","text","children","lastUnit","condition","attr","propName","specifier","value","propNode","viewPropMap","parseViewProp","innerPath","id","newNode","staticProps","htmlUnit","mutableUnits","generateMutableUnit","path","maxHtmlIdx","htmlIdx","idx","templateProps","generateVariableProp","key","viewUnit","arrayAttr","keyAttr","arr","item","forBody","forBodyStatements","childNodes","firstStatement","keyNode","directives","s","statements","parseView","DLError","createErrorHandler","_ReactivityParser","config","viewUnit","htmlUnit","unit","staticProps","prop","key","children","mutableParticles","generateMutableUnit","path","child","idx","templateProps","generateVariableProp","textUnit","dependencyIndexArr","dependenciesNode","dynamic","innerHTMLParticle","id","compUnit","compParticle","forUnit","prevIdentifierDepMap","keyDep","n","forParticle","ifUnit","branch","switchUnit","tryUnit","envUnit","expUnit","snippetUnit","snippetParticle","units","node","directIdentifierDeps","identifierDepNodes","directPropertyDeps","propertyDepNodes","directDependencies","identifierMapDependencies","deps","depNodes","availableIdentifiers","assignDeps","wrappedNode","innerPath","idName","dep","dependencyNodes","i","m","propertyKey","parentPath","pParentPath","depNode","depsArray","parser","parsedUnit","value","viewPropMap","props","parentNode","identifierKeys","reversePath","checkParam","param","el","leftPath","isInFunction","isManual","isFirstParam","ReactivityParser","__publicField","parseReactivity","viewUnits","usedProperties","dlParticle","prefixMap","BaseGenerator","viewParticle","config","ViewGenerator","acc","key","elements","element","statements","value","dependencies","statement","depNum","nodeName","viewParticles","mergeStatements","newIdx","initStatements","updateStatements","classProperties","topLevelNodes","nodeIdx","_","name","idx","dep","node","dlNodeName","expression","newStatements","LifecycleGenerator","type","__publicField","PropViewGenerator","props","prop","propViewNode","propViewIdentifier","viewPropMap","newValue","innerPath","id","allDepsNum","allDeps","i","ElementGenerator","el","elNode","elementNode","ForwardPropsGenerator","CompGenerator","tag","children","allDependencyIndexArr","dependencyIndexArr","dependenciesNode","willForwardProps","newProps","content","DLError","M","_HTMLPropGenerator","dynamic","childNodeName","position","check","attrName","eventName","attribute","HTMLPropGenerator","HTMLGenerator","tagName","childNames","mutable","child","childName","TemplateGenerator","template","mutableParticles","templateName","paths","path","insertElementStatements","pathNameMap","didUpdateMap","deps","particle","parentName","offset","newNodeName","addFirstChild","object","addSecondChild","addThirdChild","addOtherChild","num","addNextSibling","cur","collect","nameMap","res","pat","allPaths","path0","path1","sortedPaths","a","b","defaultName","bestMatchCount","bestMatchName","bestHalfMatch","matchCount","pathLength","ForGenerator","item","array","childStatements","CondGenerator","condFunc","nodeNames","branchIdx","IfGenerator","branches","condition","test","body","alternate","ifStatement","updateNode","EnvGenerator","childrenNames","TextGenerator","ExpGenerator","SnippetGenerator","availableProperties","allDependenciesNode","depIdx","propChange","propChanged","SwitchGenerator","discriminant","_case","formattedBranches","_break","switchStatements","TryGenerator","catchChildren","exception","addCatchable","updateFunc","exceptionNodes","_ViewGenerator","allInitStatements","allClassProperties","allUpdateStatements","GeneratorClass","generator","result","MainViewGenerator","viewParticlesWithPropertyDep","viewParticlesWithIdentityDep","propsNode","propertyUpdateStatements","identifierUpdateStatements","templateIdx","hasPropertyUpdateFunc","hasIdentifierUpdateFunc","n","bodyEntryNodes","args","runAllStatements","generateView","generateSnippet","propNode","replaceFnWithClass","path","classTransformer","originalName","tempName","classComp","PluginProvider","babelApi","options","_a","_b","returnStatement","n","ClassComponentTransformer","props","body","node","idx","fnNode","prop","name","declaration","id","cloneId","methodName","method","param","propNames","key","defaultValue","propName","isChildren","plugin_default","api","pluginProvider","plugin","plugin_default","devMode","alterAttributeMap","reactivityFuncNames","defaultHTMLTags","availableDecoNames","dlightDefaultPackageName","importMap","name","importsToDelete","tag","defaultAttributeMap","_PluginProvider","babelApi","types","includes","excludes","enableDevTools","htmlTags","attributeMap","key","isWatcher","isStatic","isChildren","path","node","dlightImports","n","i","importNode","specifier","value","filename","watchDeco","deps","depsNode","listenDepStrings","arg","pseudoMethod","str","decorators","isModel","isProp","isEnv","propertyIdx","watcherNode","childrenFuncCallNode","getterNode","derivedStatusKey","decoratorName","idx","reverseDeps","idxNode","d","usedProperties","propertyArr","depReversedMap","isPropOrEnv","isContent","newUpdateProp","assignPath","funcNameNode","callee","usedPropertySet","mainView","snippetNodes","viewNode","isSnippet","isMainView","exp","newViewNode","snippetNames","v","snippetPropSubDepMap","prop","props","p","subDeps","_","templateIdx","viewUnits","returnNode","parseView","b","viewParticles","N","body","classProperties","generateView","snippetProp","identifierDepMap","dep","viewParticlesProperty","viewParticlesIdentifier","snippetPropMap","newTemplateIdx","generateSnippet","fileName","pattern","minimatch","deco","imp","s","args","propsArg","contentArg","propsNode","mergedPropsNode","spreadPropsNode","contentNode","modelDecorator","names","parentPath","pParentPath","depNode","innerPath","constructor","isClassLevel","assignDeps","depNodes","propertyKey","dependencyNodes","m","depArr","wrappedNode","reversedMap","findEntry","found","entryValue","mountNode","mounting","parentNode","propertyNode","methodNode","currentNode","returns","inNestedFunction","newNode","idName","reversePath","checkParam","param","el","identifierKeys","leftPath","stopNode","isInFunction","isFunction","isManual","length","PluginProvider","__publicField","PluginProviderClass","plugin_default","api","options","files","excludeFiles","defaultHtmlTags","pluginProvider","src_default","syntaxDecorators","plugin","minimatch","src_default","options","preFiles","preExcludeFiles","files","excludeFiles","code","id","enter","allowedPath","notAllowedPath","transform"]} \ No newline at end of file diff --git a/packages/transpiler/vite-plugin-inula-next/package.json b/packages/transpiler/vite-plugin-inula-next/package.json new file mode 100644 index 00000000..b83d65bd --- /dev/null +++ b/packages/transpiler/vite-plugin-inula-next/package.json @@ -0,0 +1,45 @@ +{ + "name": "vite-plugin-inula-next", + "version": "0.0.1", + "author": { + "name": "IanDx", + "email": "iandxssxx@gmail.com" + }, + "keywords": [ + "dlight.js", + "vite-plugin" + ], + "license": "MIT", + "files": [ + "dist" + ], + "type": "module", + "main": "dist/index.js", + "module": "dist/index.js", + "typings": "dist/index.d.ts", + "scripts": { + "build": "tsup --sourcemap" + }, + "devDependencies": { + "@types/babel__core": "^7.20.5", + "tsup": "^6.5.0", + "typescript": "^5.3.2", + "vite": "^4.4.9" + }, + "dependencies": { + "@babel/core": "^7.20.12", + "babel-preset-inula-next": "workspace:*", + "minimatch": "^9.0.3" + }, + "tsup": { + "entry": [ + "src/index.ts" + ], + "format": [ + "esm" + ], + "clean": true, + "dts": true, + "minify": true + } +} diff --git a/packages/transpiler/vite-plugin-inula-next/src/index.ts b/packages/transpiler/vite-plugin-inula-next/src/index.ts new file mode 100644 index 00000000..64042809 --- /dev/null +++ b/packages/transpiler/vite-plugin-inula-next/src/index.ts @@ -0,0 +1,40 @@ +import { transform } from '@babel/core'; +import dlight, { type DLightOption } from '../../babel-preset-inula-next'; +import { minimatch } from 'minimatch'; +import { Plugin, TransformResult } from 'vite'; +export default function (options: DLightOption = {}): Plugin { + const { + files: preFiles = '**/*.{js,jsx,ts,tsx}', + excludeFiles: preExcludeFiles = '**/{dist,node_modules,lib}/*.{js,ts}', + } = options; + const files = Array.isArray(preFiles) ? preFiles : [preFiles]; + const excludeFiles = Array.isArray(preExcludeFiles) ? preExcludeFiles : [preExcludeFiles]; + + return { + name: 'dlight', + enforce: 'pre', + transform(code: string, id: string) { + let enter = false; + for (const allowedPath of files) { + if (minimatch(id, allowedPath)) { + enter = true; + break; + } + } + for (const notAllowedPath of excludeFiles) { + if (minimatch(id, notAllowedPath)) { + enter = false; + break; + } + } + if (!enter) return; + return transform(code, { + babelrc: false, + configFile: false, + presets: [[dlight, options]], + sourceMaps: true, + filename: id, + }) as TransformResult; + }, + }; +} diff --git a/packages/transpiler/vite-plugin-inula-next/tsconfig.json b/packages/transpiler/vite-plugin-inula-next/tsconfig.json new file mode 100644 index 00000000..e0932d78 --- /dev/null +++ b/packages/transpiler/vite-plugin-inula-next/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "strict": true, + "esModuleInterop": true + }, + "ts-node": { + "esm": true + } +} \ No newline at end of file diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 067a01bf..609f587e 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,5 @@ packages: # all packages in direct subdirs of packages/ - 'packages/*' + - 'packages/**/*' + - 'demos/*'