@fluixi/compiler 1.0.0-alpha.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/analyze/expr-shape.cjs +62 -0
- package/dist/analyze/expr-shape.d.cts +14 -0
- package/dist/analyze/expr-shape.d.ts +15 -0
- package/dist/analyze/expr-shape.d.ts.map +1 -0
- package/dist/analyze/expr-shape.js +37 -0
- package/dist/analyze/expr-shape.mjs +41 -0
- package/dist/analyze/intrinsics.cjs +112 -0
- package/dist/analyze/intrinsics.d.cts +80 -0
- package/dist/analyze/intrinsics.d.ts +81 -0
- package/dist/analyze/intrinsics.d.ts.map +1 -0
- package/dist/analyze/intrinsics.js +126 -0
- package/dist/analyze/intrinsics.mjs +91 -0
- package/dist/analyze/paren.cjs +134 -0
- package/dist/analyze/paren.d.cts +23 -0
- package/dist/analyze/paren.d.ts +24 -0
- package/dist/analyze/paren.d.ts.map +1 -0
- package/dist/analyze/paren.js +168 -0
- package/dist/analyze/paren.mjs +113 -0
- package/dist/analyze/props-destructure.cjs +226 -0
- package/dist/analyze/props-destructure.d.cts +83 -0
- package/dist/analyze/props-destructure.d.ts +84 -0
- package/dist/analyze/props-destructure.d.ts.map +1 -0
- package/dist/analyze/props-destructure.js +272 -0
- package/dist/analyze/props-destructure.mjs +205 -0
- package/dist/analyze/reactive-bindings.cjs +210 -0
- package/dist/analyze/reactive-bindings.d.cts +42 -0
- package/dist/analyze/reactive-bindings.d.ts +43 -0
- package/dist/analyze/reactive-bindings.d.ts.map +1 -0
- package/dist/analyze/reactive-bindings.js +191 -0
- package/dist/analyze/reactive-bindings.mjs +187 -0
- package/dist/analyze/reactive-expr.cjs +38 -0
- package/dist/analyze/reactive-expr.d.cts +38 -0
- package/dist/analyze/reactive-expr.d.ts +39 -0
- package/dist/analyze/reactive-expr.d.ts.map +1 -0
- package/dist/analyze/reactive-expr.js +31 -0
- package/dist/analyze/reactive-expr.mjs +17 -0
- package/dist/analyze/static-graph.cjs +831 -0
- package/dist/analyze/static-graph.d.cts +102 -0
- package/dist/analyze/static-graph.d.ts +103 -0
- package/dist/analyze/static-graph.d.ts.map +1 -0
- package/dist/analyze/static-graph.js +871 -0
- package/dist/analyze/static-graph.mjs +813 -0
- package/dist/babel-I6BUWUCU.mjs +636 -0
- package/dist/chunk-3AICWNF7.mjs +92 -0
- package/dist/chunk-6CWJWGE2.mjs +2170 -0
- package/dist/chunk-C3XPDB4Y.mjs +16 -0
- package/dist/chunk-GX5XV6Z7.mjs +190 -0
- package/dist/chunk-HXEXPWJ2.mjs +999 -0
- package/dist/chunk-NU3W67UZ.mjs +584 -0
- package/dist/chunk-SKAVY75W.mjs +11 -0
- package/dist/codegen/backend.cjs +18 -0
- package/dist/codegen/backend.d.cts +35 -0
- package/dist/codegen/backend.d.ts +36 -0
- package/dist/codegen/backend.d.ts.map +1 -0
- package/dist/codegen/backend.js +1 -0
- package/dist/codegen/backend.mjs +0 -0
- package/dist/codegen/backends/imperative.cjs +577 -0
- package/dist/codegen/backends/imperative.d.cts +11 -0
- package/dist/codegen/backends/imperative.d.ts +12 -0
- package/dist/codegen/backends/imperative.d.ts.map +1 -0
- package/dist/codegen/backends/imperative.js +362 -0
- package/dist/codegen/backends/imperative.mjs +554 -0
- package/dist/codegen/contract.cjs +52 -0
- package/dist/codegen/contract.d.cts +24 -0
- package/dist/codegen/contract.d.ts +25 -0
- package/dist/codegen/contract.d.ts.map +1 -0
- package/dist/codegen/contract.js +30 -0
- package/dist/codegen/contract.mjs +31 -0
- package/dist/codegen/partial-template.cjs +176 -0
- package/dist/codegen/partial-template.d.cts +61 -0
- package/dist/codegen/partial-template.d.ts +62 -0
- package/dist/codegen/partial-template.d.ts.map +1 -0
- package/dist/codegen/partial-template.js +0 -0
- package/dist/codegen/partial-template.mjs +153 -0
- package/dist/codegen/serialize-static.cjs +125 -0
- package/dist/codegen/serialize-static.d.cts +38 -0
- package/dist/codegen/serialize-static.d.ts +39 -0
- package/dist/codegen/serialize-static.d.ts.map +1 -0
- package/dist/codegen/serialize-static.js +165 -0
- package/dist/codegen/serialize-static.mjs +104 -0
- package/dist/frontend/babel/build-ir-lit.cjs +1933 -0
- package/dist/frontend/babel/build-ir-lit.d.cts +26 -0
- package/dist/frontend/babel/build-ir-lit.d.ts +27 -0
- package/dist/frontend/babel/build-ir-lit.d.ts.map +1 -0
- package/dist/frontend/babel/build-ir-lit.js +13 -0
- package/dist/frontend/babel/build-ir-lit.mjs +1902 -0
- package/dist/frontend/babel/build-ir.cjs +1948 -0
- package/dist/frontend/babel/build-ir.d.cts +35 -0
- package/dist/frontend/babel/build-ir.d.ts +36 -0
- package/dist/frontend/babel/build-ir.d.ts.map +1 -0
- package/dist/frontend/babel/build-ir.js +124 -0
- package/dist/frontend/babel/build-ir.mjs +1919 -0
- package/dist/frontend/babel/index.cjs +2610 -0
- package/dist/frontend/babel/index.d.cts +33 -0
- package/dist/frontend/babel/index.d.ts +34 -0
- package/dist/frontend/babel/index.d.ts.map +1 -0
- package/dist/frontend/babel/index.js +65 -0
- package/dist/frontend/babel/index.mjs +2580 -0
- package/dist/frontend/babel/lower-template.cjs +1931 -0
- package/dist/frontend/babel/lower-template.d.cts +23 -0
- package/dist/frontend/babel/lower-template.d.ts +24 -0
- package/dist/frontend/babel/lower-template.d.ts.map +1 -0
- package/dist/frontend/babel/lower-template.js +64 -0
- package/dist/frontend/babel/lower-template.mjs +1902 -0
- package/dist/frontend/babel/plugin.cjs +2556 -0
- package/dist/frontend/babel/plugin.d.cts +95 -0
- package/dist/frontend/babel/plugin.d.ts +96 -0
- package/dist/frontend/babel/plugin.d.ts.map +1 -0
- package/dist/frontend/babel/plugin.js +523 -0
- package/dist/frontend/babel/plugin.mjs +2527 -0
- package/dist/frontend/babel/server-functions.cjs +110 -0
- package/dist/frontend/babel/server-functions.d.cts +10 -0
- package/dist/frontend/babel/server-functions.d.ts +11 -0
- package/dist/frontend/babel/server-functions.d.ts.map +1 -0
- package/dist/frontend/babel/server-functions.js +81 -0
- package/dist/frontend/babel/server-functions.mjs +89 -0
- package/dist/frontend/babel/types.cjs +18 -0
- package/dist/frontend/babel/types.d.cts +33 -0
- package/dist/frontend/babel/types.d.ts +34 -0
- package/dist/frontend/babel/types.d.ts.map +1 -0
- package/dist/frontend/babel/types.js +1 -0
- package/dist/frontend/babel/types.mjs +0 -0
- package/dist/frontend/types.cjs +18 -0
- package/dist/frontend/types.d.cts +25 -0
- package/dist/frontend/types.d.ts +26 -0
- package/dist/frontend/types.d.ts.map +1 -0
- package/dist/frontend/types.js +1 -0
- package/dist/frontend/types.mjs +0 -0
- package/dist/frontend/vocabulary.cjs +98 -0
- package/dist/frontend/vocabulary.d.cts +79 -0
- package/dist/frontend/vocabulary.d.ts +80 -0
- package/dist/frontend/vocabulary.d.ts.map +1 -0
- package/dist/frontend/vocabulary.js +124 -0
- package/dist/frontend/vocabulary.mjs +77 -0
- package/dist/index.cjs +1957 -0
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.mjs +1949 -0
- package/dist/integrations.cjs +5705 -0
- package/dist/integrations.d.cts +215 -0
- package/dist/integrations.d.ts +216 -0
- package/dist/integrations.d.ts.map +1 -0
- package/dist/integrations.js +566 -0
- package/dist/integrations.mjs +810 -0
- package/dist/ir/nodes.cjs +18 -0
- package/dist/ir/nodes.d.cts +168 -0
- package/dist/ir/nodes.d.ts +169 -0
- package/dist/ir/nodes.d.ts.map +1 -0
- package/dist/ir/nodes.js +1 -0
- package/dist/ir/nodes.mjs +0 -0
- package/dist/lower/compile-template.cjs +1670 -0
- package/dist/lower/compile-template.d.cts +82 -0
- package/dist/lower/compile-template.d.ts +83 -0
- package/dist/lower/compile-template.d.ts.map +1 -0
- package/dist/lower/compile-template.js +79 -0
- package/dist/lower/compile-template.mjs +1662 -0
- package/dist/lower/entities.cjs +288 -0
- package/dist/lower/entities.d.cts +1 -0
- package/dist/lower/entities.d.ts +2 -0
- package/dist/lower/entities.d.ts.map +1 -0
- package/dist/lower/entities.js +54 -0
- package/dist/lower/entities.mjs +267 -0
- package/dist/lower/jsx.cjs +1101 -0
- package/dist/lower/jsx.d.cts +33 -0
- package/dist/lower/jsx.d.ts +34 -0
- package/dist/lower/jsx.d.ts.map +1 -0
- package/dist/lower/jsx.js +414 -0
- package/dist/lower/jsx.mjs +1078 -0
- package/dist/lower/template.cjs +1354 -0
- package/dist/lower/template.d.cts +53 -0
- package/dist/lower/template.d.ts +54 -0
- package/dist/lower/template.d.ts.map +1 -0
- package/dist/lower/template.js +542 -0
- package/dist/lower/template.mjs +1335 -0
- package/dist/lower/text.cjs +42 -0
- package/dist/lower/text.d.cts +10 -0
- package/dist/lower/text.d.ts +11 -0
- package/dist/lower/text.d.ts.map +1 -0
- package/dist/lower/text.js +31 -0
- package/dist/lower/text.mjs +21 -0
- package/dist/optimize/analyze-static.cjs +112 -0
- package/dist/optimize/analyze-static.d.cts +39 -0
- package/dist/optimize/analyze-static.d.ts +40 -0
- package/dist/optimize/analyze-static.d.ts.map +1 -0
- package/dist/optimize/analyze-static.js +131 -0
- package/dist/optimize/analyze-static.mjs +91 -0
- package/dist/optimize/collapse-strategies.cjs +90 -0
- package/dist/optimize/collapse-strategies.d.cts +30 -0
- package/dist/optimize/collapse-strategies.d.ts +31 -0
- package/dist/optimize/collapse-strategies.d.ts.map +1 -0
- package/dist/optimize/collapse-strategies.js +33 -0
- package/dist/optimize/collapse-strategies.mjs +67 -0
- package/dist/options.cjs +33 -0
- package/dist/options.d.cts +39 -0
- package/dist/options.d.ts +40 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +8 -0
- package/dist/options.mjs +12 -0
- package/dist/resolve/builtins.cjs +73 -0
- package/dist/resolve/builtins.d.cts +68 -0
- package/dist/resolve/builtins.d.ts +69 -0
- package/dist/resolve/builtins.d.ts.map +1 -0
- package/dist/resolve/builtins.js +64 -0
- package/dist/resolve/builtins.mjs +53 -0
- package/dist/resolve/component-globals.cjs +272 -0
- package/dist/resolve/component-globals.d.cts +88 -0
- package/dist/resolve/component-globals.d.ts +89 -0
- package/dist/resolve/component-globals.d.ts.map +1 -0
- package/dist/resolve/component-globals.js +160 -0
- package/dist/resolve/component-globals.mjs +249 -0
- package/dist/resolve/component-resolver.cjs +80 -0
- package/dist/resolve/component-resolver.d.cts +68 -0
- package/dist/resolve/component-resolver.d.ts +69 -0
- package/dist/resolve/component-resolver.d.ts.map +1 -0
- package/dist/resolve/component-resolver.js +95 -0
- package/dist/resolve/component-resolver.mjs +59 -0
- package/dist/resolve/load-directive.cjs +93 -0
- package/dist/resolve/load-directive.d.cts +53 -0
- package/dist/resolve/load-directive.d.ts +54 -0
- package/dist/resolve/load-directive.d.ts.map +1 -0
- package/dist/resolve/load-directive.js +93 -0
- package/dist/resolve/load-directive.mjs +72 -0
- package/dist/resolve/resolve-ir.cjs +108 -0
- package/dist/resolve/resolve-ir.d.cts +53 -0
- package/dist/resolve/resolve-ir.d.ts +54 -0
- package/dist/resolve/resolve-ir.d.ts.map +1 -0
- package/dist/resolve/resolve-ir.js +58 -0
- package/dist/resolve/resolve-ir.mjs +85 -0
- package/dist/resolve/write-globals.cjs +383 -0
- package/dist/resolve/write-globals.d.cts +36 -0
- package/dist/resolve/write-globals.d.ts +37 -0
- package/dist/resolve/write-globals.d.ts.map +1 -0
- package/dist/resolve/write-globals.js +169 -0
- package/dist/resolve/write-globals.mjs +362 -0
- package/dist/server-fns-VZLHMQTC.mjs +79 -0
- package/dist/server-functions-UAZKOLZW.mjs +82 -0
- package/dist/transform/bindings.cjs +71 -0
- package/dist/transform/bindings.d.cts +16 -0
- package/dist/transform/bindings.d.ts +17 -0
- package/dist/transform/bindings.d.ts.map +1 -0
- package/dist/transform/bindings.js +69 -0
- package/dist/transform/bindings.mjs +50 -0
- package/dist/transform/index.cjs +3837 -0
- package/dist/transform/index.d.cts +15 -0
- package/dist/transform/index.d.ts +16 -0
- package/dist/transform/index.d.ts.map +1 -0
- package/dist/transform/index.js +12 -0
- package/dist/transform/index.mjs +4882 -0
- package/dist/transform/intrinsics.cjs +261 -0
- package/dist/transform/intrinsics.d.cts +33 -0
- package/dist/transform/intrinsics.d.ts +34 -0
- package/dist/transform/intrinsics.d.ts.map +1 -0
- package/dist/transform/intrinsics.js +120 -0
- package/dist/transform/intrinsics.mjs +238 -0
- package/dist/transform/props.cjs +441 -0
- package/dist/transform/props.d.cts +45 -0
- package/dist/transform/props.d.ts +46 -0
- package/dist/transform/props.d.ts.map +1 -0
- package/dist/transform/props.js +221 -0
- package/dist/transform/props.mjs +1487 -0
- package/dist/transform/server-fn-id.cjs +35 -0
- package/dist/transform/server-fn-id.d.cts +9 -0
- package/dist/transform/server-fn-id.d.ts +10 -0
- package/dist/transform/server-fn-id.d.ts.map +1 -0
- package/dist/transform/server-fn-id.js +18 -0
- package/dist/transform/server-fn-id.mjs +14 -0
- package/dist/transform/server-fns.cjs +114 -0
- package/dist/transform/server-fns.d.cts +12 -0
- package/dist/transform/server-fns.d.ts +13 -0
- package/dist/transform/server-fns.d.ts.map +1 -0
- package/dist/transform/server-fns.js +94 -0
- package/dist/transform/server-fns.mjs +1160 -0
- package/dist/transform/source-locations.cjs +393 -0
- package/dist/transform/source-locations.d.cts +48 -0
- package/dist/transform/source-locations.d.ts +49 -0
- package/dist/transform/source-locations.d.ts.map +1 -0
- package/dist/transform/source-locations.js +303 -0
- package/dist/transform/source-locations.mjs +370 -0
- package/dist/transform/styles.cjs +606 -0
- package/dist/transform/styles.d.cts +237 -0
- package/dist/transform/styles.d.ts +238 -0
- package/dist/transform/styles.d.ts.map +1 -0
- package/dist/transform/styles.js +743 -0
- package/dist/transform/styles.mjs +671 -0
- package/dist/transform/templates.cjs +3806 -0
- package/dist/transform/templates.d.cts +82 -0
- package/dist/transform/templates.d.ts +83 -0
- package/dist/transform/templates.d.ts.map +1 -0
- package/dist/transform/templates.js +613 -0
- package/dist/transform/templates.mjs +4852 -0
- package/dist/transform-3W45L3EA.mjs +951 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/dist/vite-styles.cjs +223 -0
- package/dist/vite-styles.d.cts +68 -0
- package/dist/vite-styles.d.ts +69 -0
- package/dist/vite-styles.d.ts.map +1 -0
- package/dist/vite-styles.js +241 -0
- package/dist/vite-styles.mjs +214 -0
- package/package.json +141 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { planPartialTemplate } from '../partial-template.js';
|
|
2
|
+
import { isSerializable, serializeStatic, templatePositions, isPlainLiteralAttr, htmlAttrName, } from '../serialize-static.js';
|
|
3
|
+
import { RUNTIME_CONTRACT_V1 } from '../contract.js';
|
|
4
|
+
import { markHole, markProps, SOURCE_MARKER } from '../../transform/source-locations.js';
|
|
5
|
+
import { paren } from '../../analyze/paren.js';
|
|
6
|
+
/**
|
|
7
|
+
* Imperative fine-grained backend: emits createNativeElement/insert/spread/
|
|
8
|
+
* createComponent (RUNTIME_CONTRACT_V1), the runtime proven by the @fluixi/dom
|
|
9
|
+
* contract tests.
|
|
10
|
+
*
|
|
11
|
+
* emit() consumes the host-agnostic IR and returns a code string, so any
|
|
12
|
+
* front-end (babel now, swc later) that can produce IR reuses this exact codegen.
|
|
13
|
+
* The output mirrors what the babel front-end emits inline today:
|
|
14
|
+
*
|
|
15
|
+
* element -> (() => { const _el$ = createNativeElement(tag);
|
|
16
|
+
* spread({ element: _el$, props });
|
|
17
|
+
* insert(_el$, children); return _el$; })()
|
|
18
|
+
* component -> createMemo(() => createComponent(Name, props))
|
|
19
|
+
* fragment -> [ ...children ]
|
|
20
|
+
*/
|
|
21
|
+
const CONTROL_COMPONENT = {
|
|
22
|
+
show: 'Show',
|
|
23
|
+
for: 'For',
|
|
24
|
+
index: 'Index',
|
|
25
|
+
switch: 'Switch',
|
|
26
|
+
match: 'Match',
|
|
27
|
+
dynamic: 'Dynamic',
|
|
28
|
+
errorBoundary: 'ErrorBoundary',
|
|
29
|
+
suspense: 'Suspense',
|
|
30
|
+
};
|
|
31
|
+
function isIdent(name) {
|
|
32
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
33
|
+
}
|
|
34
|
+
function propKey(name) {
|
|
35
|
+
return isIdent(name) ? name : JSON.stringify(name);
|
|
36
|
+
}
|
|
37
|
+
function propValue(p) {
|
|
38
|
+
if (p.expr !== undefined) {
|
|
39
|
+
// A ref is not reactive, but which variable ends up holding the element is worth
|
|
40
|
+
// saying, so it carries its own text and position.
|
|
41
|
+
if (p.name === 'ref' && p.at)
|
|
42
|
+
return markHole(paren(p.expr, 'value'), p.at.line, p.at.column, p.expr);
|
|
43
|
+
// Events and non-reactive expressions pass through; reactive ones become thunks
|
|
44
|
+
// so the runtime (spread/insert) can wire a render effect.
|
|
45
|
+
if (!p.reactive)
|
|
46
|
+
return paren(p.expr, 'value');
|
|
47
|
+
const accessor = `() => ${paren(p.expr, 'arrowBody')}`;
|
|
48
|
+
// `at` is the gate: a production build carries no marker.
|
|
49
|
+
return p.at ? markHole(accessor, p.at.line, p.at.column, p.directive) : accessor;
|
|
50
|
+
}
|
|
51
|
+
return JSON.stringify(p.literal ?? true);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Component props are passed as VALUES, not thunks (unlike native-element attrs).
|
|
55
|
+
* The whole `createComponent(...)` call sits inside a `createMemo`, so reading a
|
|
56
|
+
* signal in a prop re-evaluates when the memo re-runs, no per-prop thunk needed.
|
|
57
|
+
* This matches the inline codegen; wrapping a prop like
|
|
58
|
+
* `history={createMemoryHistory()}` in `() => …` would hand the component a
|
|
59
|
+
* function instead of the value (e.g. `history.get is not a function`).
|
|
60
|
+
*/
|
|
61
|
+
function componentPropValue(p) {
|
|
62
|
+
// Sits in a `return`, where a top-level comma would read as a sequence.
|
|
63
|
+
if (p.expr !== undefined)
|
|
64
|
+
return paren(p.expr, 'value');
|
|
65
|
+
return JSON.stringify(p.literal ?? true);
|
|
66
|
+
}
|
|
67
|
+
/** Build a props object literal (or mergeProps(...) when spreads are present). */
|
|
68
|
+
function emitProps(props, childrenExpr, used) {
|
|
69
|
+
const spreads = props.filter((p) => p.kind === 'spread');
|
|
70
|
+
const normal = props.filter((p) => p.kind !== 'spread');
|
|
71
|
+
const entries = normal.map((p) => `${propKey(p.name)}: ${propValue(p)}`);
|
|
72
|
+
if (childrenExpr != null)
|
|
73
|
+
entries.push(`children: ${childrenExpr}`);
|
|
74
|
+
const obj = `{ ${entries.join(', ')} }`;
|
|
75
|
+
if (spreads.length > 0) {
|
|
76
|
+
used.add('mergeProps');
|
|
77
|
+
return `mergeProps(${spreads.map((s) => s.expr).join(', ')}, ${obj})`;
|
|
78
|
+
}
|
|
79
|
+
return obj;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The memo a component lowers to is an accessor, so it takes the same hole mark as any
|
|
83
|
+
* other reactive binding. Without it, the insert that places the subtree reports whatever
|
|
84
|
+
* the runtime stack says: a position in the compiled file.
|
|
85
|
+
*/
|
|
86
|
+
function markBind(code, at) {
|
|
87
|
+
return at ? markHole(code, at.line, at.column) : code;
|
|
88
|
+
}
|
|
89
|
+
function emitChildren(children, used, templates) {
|
|
90
|
+
if (children.length === 1)
|
|
91
|
+
return emitNode(children[0], used, templates);
|
|
92
|
+
// An element is built, and while hydrating claims its server node, when the array literal
|
|
93
|
+
// runs; a component, control flow or reactive expression is read later, when the array is
|
|
94
|
+
// placed. An element after one of those would claim first and take the server node that
|
|
95
|
+
// belongs to it, so from the first one on elements are deferred too (`deferElement`: built in
|
|
96
|
+
// order when the array is placed, once, in the owner the array was written in).
|
|
97
|
+
let deferred = false;
|
|
98
|
+
const items = children.map((child) => {
|
|
99
|
+
const code = emitNode(child, used, templates);
|
|
100
|
+
if (child.kind === 'element' && deferred) {
|
|
101
|
+
used.add('deferElement');
|
|
102
|
+
return `deferElement(() => ${paren(code, 'arrowBody')})`;
|
|
103
|
+
}
|
|
104
|
+
if (child.kind === 'component' || child.kind === 'control' || (child.kind === 'expr' && child.reactive))
|
|
105
|
+
deferred = true;
|
|
106
|
+
return code;
|
|
107
|
+
});
|
|
108
|
+
return `[${items.join(', ')}]`;
|
|
109
|
+
}
|
|
110
|
+
function emitComponent(name, props, children, used, templates, at) {
|
|
111
|
+
used.add('createMemo');
|
|
112
|
+
used.add('createComponent');
|
|
113
|
+
const spreads = props.filter((p) => p.kind === 'spread');
|
|
114
|
+
const normal = props.filter((p) => p.kind !== 'spread');
|
|
115
|
+
// ALL component props are emitted as getters (Solid model): reading a signal in a prop
|
|
116
|
+
// eagerly inside `createMemo(() => createComponent(...))` would re-create the whole component
|
|
117
|
+
// on every change (a <Portal> then piles up). As getters the component is built ONCE and
|
|
118
|
+
// reactivity flows through the getter into its own effects. Static props as getters are
|
|
119
|
+
// harmless (they return the same value). Matches lazyProps() in the inline codegen.
|
|
120
|
+
// Each body emitted once: emitting children registers their templates.
|
|
121
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
122
|
+
if (children.length > 0) {
|
|
123
|
+
// Children are a LAZY getter: evaluated inside the component's execution, so
|
|
124
|
+
// child components are created in the component's owner scope (preserves owner
|
|
125
|
+
// nesting for context/Suspense), after the component runs (correct provider
|
|
126
|
+
// ordering), and lazily (ErrorBoundary can still catch a throwing child).
|
|
127
|
+
bodies.push(['children', emitChildren(children, used, templates)]);
|
|
128
|
+
}
|
|
129
|
+
let obj = `{ ${bodies.map(([name, body]) => `get ${propKey(name)}() { return ${body}; }`).join(', ')} }`;
|
|
130
|
+
// A getter is a method: `this` inside it is the props object. Written in a class method
|
|
131
|
+
// (a custom element's render), `title={() => this.name}` then read the props, not the
|
|
132
|
+
// element. Arrows keep the enclosing `this`, so those props go out as arrow thunks and the
|
|
133
|
+
// getters call them. Only when some prop mentions `this`; everything else is unchanged.
|
|
134
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
135
|
+
// Parenthesised: an object literal body would otherwise parse as a block.
|
|
136
|
+
const thunks = bodies.map(([name, body]) => `${JSON.stringify(name)}: () => (${body})`);
|
|
137
|
+
const getters = bodies.map(([name]) => `get ${propKey(name)}() { return _$t[${JSON.stringify(name)}](); }`);
|
|
138
|
+
obj = `((_$t) => ({ ${getters.join(', ')} }))({ ${thunks.join(', ')} })`;
|
|
139
|
+
}
|
|
140
|
+
if (spreads.length > 0) {
|
|
141
|
+
used.add('mergeProps');
|
|
142
|
+
obj = `mergeProps(${spreads.map((s) => s.expr).join(', ')}, ${obj})`;
|
|
143
|
+
}
|
|
144
|
+
// Every prop is a getter, so none of them can carry a position. The map rides alongside,
|
|
145
|
+
// and a build that recorded no locations emits nothing.
|
|
146
|
+
const propAt = {};
|
|
147
|
+
for (const p of normal)
|
|
148
|
+
if (p.at)
|
|
149
|
+
propAt[p.name] = [p.at.line, p.at.column];
|
|
150
|
+
if (Object.keys(propAt).length)
|
|
151
|
+
obj = markProps(obj, propAt);
|
|
152
|
+
// The memo never recomputes: every prop is a getter, so this callback reads nothing
|
|
153
|
+
// reactive. It is here for what it makes `insert` do, and it cannot be elided.
|
|
154
|
+
//
|
|
155
|
+
// A component whose return value is *itself* bare control flow (`return <Show …/>`)
|
|
156
|
+
// produces a memo, which `createComponent` deliberately does not stamp as a component.
|
|
157
|
+
// Without this wrapper `insert` stops re-reading it, and the subtree goes stale when a
|
|
158
|
+
// sibling updates the context it depends on: see `show-context-insert.spec.ts`.
|
|
159
|
+
//
|
|
160
|
+
// Static props are not a sufficient condition for dropping it either: safety depends on
|
|
161
|
+
// what the callee *returns*, which lives in another module and is not visible here.
|
|
162
|
+
//
|
|
163
|
+
// Nor can it be a plain `() => createComponent(...)` thunk. The memo also provides
|
|
164
|
+
// *identity*: it runs once and caches. A thunk is re-invoked by insert's render effect
|
|
165
|
+
// on every run, rebuilding the component with a fresh owner, which loses context,
|
|
166
|
+
// duplicates the tree on hydration and piles up Portals. Measured: dropping the wrapper
|
|
167
|
+
// fails 3 tests, replacing it with a thunk fails 14.
|
|
168
|
+
//
|
|
169
|
+
// Control flow does use a bare arrow, and that is not a counter-example: control flow
|
|
170
|
+
// is the reactive boundary and is *meant* to re-run. A user component is meant to run
|
|
171
|
+
// once.
|
|
172
|
+
// The mark goes inside the call, where it runs before createMemo makes the node and so
|
|
173
|
+
// is the one the node takes. Devtools installs the global; without it this is a no-op.
|
|
174
|
+
const mark = at
|
|
175
|
+
? `globalThis.${SOURCE_MARKER}?.(${JSON.stringify(at.file)},${at.line},${at.column},${JSON.stringify(name)}), `
|
|
176
|
+
: '';
|
|
177
|
+
// The mark makes this a sequence, which has to stay wrapped to pass as one argument.
|
|
178
|
+
// Without one there is nothing to wrap, and a production build never has one.
|
|
179
|
+
return `createMemo(${paren(`${mark}() => createComponent(${name}, ${obj})`, 'value')})`;
|
|
180
|
+
}
|
|
181
|
+
/** `el.setAttribute("class", "card")` for a compile-time constant. */
|
|
182
|
+
function staticAttrAssignment(el, prop) {
|
|
183
|
+
const name = htmlAttrName(prop.name);
|
|
184
|
+
const value = prop.literal;
|
|
185
|
+
// A valueless attribute is `true` in the IR and the empty string in the DOM.
|
|
186
|
+
if (value === true || value === undefined)
|
|
187
|
+
return `${el}.setAttribute(${JSON.stringify(name)}, "");`;
|
|
188
|
+
if (value === false || value === null)
|
|
189
|
+
return '';
|
|
190
|
+
return `${el}.setAttribute(${JSON.stringify(name)}, ${JSON.stringify(String(value))});`;
|
|
191
|
+
}
|
|
192
|
+
let partial = false;
|
|
193
|
+
function emitNode(node, used, templates) {
|
|
194
|
+
switch (node.kind) {
|
|
195
|
+
case 'text':
|
|
196
|
+
return JSON.stringify(node.value);
|
|
197
|
+
case 'expr': {
|
|
198
|
+
if (!node.reactive)
|
|
199
|
+
return paren(node.code, 'value');
|
|
200
|
+
const accessor = `() => ${paren(node.code, 'arrowBody')}`;
|
|
201
|
+
return node.at ? markHole(accessor, node.at.line, node.at.column) : accessor;
|
|
202
|
+
}
|
|
203
|
+
case 'fragment':
|
|
204
|
+
if (node.children.length === 0)
|
|
205
|
+
return 'null';
|
|
206
|
+
return emitChildren(node.children, used, templates);
|
|
207
|
+
case 'component':
|
|
208
|
+
// No reactive prop means no `bindAt`, which is most tags. The insert that places the
|
|
209
|
+
// subtree still has to report somewhere, and the tag itself is where it was written ,
|
|
210
|
+
// without this it reports whatever the runtime stack says, a line in the compiled file.
|
|
211
|
+
return markBind(emitComponent(node.name, node.props, node.children, used, templates, node.at), node.bindAt ?? node.at);
|
|
212
|
+
case 'control': {
|
|
213
|
+
const name = CONTROL_COMPONENT[node.control] ?? node.control;
|
|
214
|
+
used.add(name);
|
|
215
|
+
return markBind(emitComponent(name, node.props, node.children, used, templates), node.bindAt);
|
|
216
|
+
}
|
|
217
|
+
case 'element': {
|
|
218
|
+
// A static subtree is parsed once into a <template> and cloned per instance:
|
|
219
|
+
// one cloneNode instead of an createNativeElement + spread per element.
|
|
220
|
+
// SVG is excluded: the runtime wraps SVG template HTML in an <svg> host and
|
|
221
|
+
// `cloneTemplate(t, true)` returns that wrapper, not the serialized element, so
|
|
222
|
+
// cloning would hand back the wrong node. Correctness first, SVG keeps the
|
|
223
|
+
// imperative path until the runtime grows an unwrapping clone.
|
|
224
|
+
if (templates && node.static && !node.svg && isSerializable(node)) {
|
|
225
|
+
used.add('templateNode');
|
|
226
|
+
const id = `_tmpl$${templates.length}`;
|
|
227
|
+
templates.push({ id, html: serializeStatic(node), tag: node.tag, svg: false });
|
|
228
|
+
// The positions ride along so devtools can name the line each element was written
|
|
229
|
+
// on; the template string itself maps back to the root and nothing finer.
|
|
230
|
+
const at = templatePositions(node);
|
|
231
|
+
return at
|
|
232
|
+
? `templateNode(${id}, ${JSON.stringify(node.tag)}, false, false, ${JSON.stringify(at)})`
|
|
233
|
+
: `templateNode(${id}, ${JSON.stringify(node.tag)})`;
|
|
234
|
+
}
|
|
235
|
+
// Structure static, contents dynamic: the common shape. One template for the
|
|
236
|
+
// structure, then each hole filled through a reference into the clone. Every
|
|
237
|
+
// reference is read before any insertion, since inserting shifts later positions.
|
|
238
|
+
if (templates && partial) {
|
|
239
|
+
const plan = planPartialTemplate(node);
|
|
240
|
+
if (plan) {
|
|
241
|
+
used.add('templateNode');
|
|
242
|
+
used.add('insert');
|
|
243
|
+
used.add('holeEnd');
|
|
244
|
+
used.add('holeContent');
|
|
245
|
+
used.add('holeScope');
|
|
246
|
+
const id = `_tmpl$${templates.length}`;
|
|
247
|
+
templates.push({ id, html: plan.html, tag: plan.tag, svg: false });
|
|
248
|
+
const at = templatePositions(node);
|
|
249
|
+
const lines = [
|
|
250
|
+
`const _el$ = templateNode(${id}, ${JSON.stringify(plan.tag)}, true${at ? `, false, ${JSON.stringify(at)}` : ''});`,
|
|
251
|
+
];
|
|
252
|
+
// Every reference first: inserting shifts what follows, so the walk has to be
|
|
253
|
+
// complete before any content goes in.
|
|
254
|
+
for (const step of plan.steps)
|
|
255
|
+
lines.push(`const ${step.ref} = ${step.expr};`);
|
|
256
|
+
for (const hole of plan.holes) {
|
|
257
|
+
// The fourth argument is what the hole already holds, nothing on a fresh
|
|
258
|
+
// clone, the server's content when hydrating. `insert` reconciles against it,
|
|
259
|
+
// so hydration reuses those nodes rather than building new ones.
|
|
260
|
+
// `holeScope` places the hydration cursor at what the server rendered for
|
|
261
|
+
// this hole, so anything the content builds internally adopts too.
|
|
262
|
+
lines.push(`insert(${hole.parentRef}, holeScope(${hole.startRef}, () => ` +
|
|
263
|
+
`${paren(emitNode(hole.node, used, templates), 'arrowBody')}), ` +
|
|
264
|
+
`${hole.endRef}, holeContent(${hole.startRef}, ${hole.endRef}));`);
|
|
265
|
+
}
|
|
266
|
+
lines.push('return _el$;');
|
|
267
|
+
return `(() => { ${lines.join(' ')} })()`;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
used.add('createNativeElement');
|
|
271
|
+
const tag = JSON.stringify(node.tag);
|
|
272
|
+
const el = '_el$';
|
|
273
|
+
const parts = [];
|
|
274
|
+
// The third argument is where the tag was written. An element built this way is not
|
|
275
|
+
// in a template, so nothing else records it.
|
|
276
|
+
const createArgs = node.at
|
|
277
|
+
? `${tag}, ${node.svg}, ${JSON.stringify(node.at)}`
|
|
278
|
+
: node.svg
|
|
279
|
+
? `${tag}, true`
|
|
280
|
+
: tag;
|
|
281
|
+
parts.push(`const ${el} = createNativeElement(${createArgs});`);
|
|
282
|
+
if (node.props.length > 0) {
|
|
283
|
+
// Literal attributes are written straight on. `spread` is a generic helper that
|
|
284
|
+
// walks every prop checking for reactivity, events, refs, class/style objects and
|
|
285
|
+
// namespaces: none of which a compile-time constant needs. Same predicate the
|
|
286
|
+
// template serializer uses, so the two agree on what a plain attribute is.
|
|
287
|
+
if (!node.svg && node.props.every(isPlainLiteralAttr)) {
|
|
288
|
+
for (const p of node.props)
|
|
289
|
+
parts.push(staticAttrAssignment(el, p));
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
used.add('spread');
|
|
293
|
+
const svgFlag = node.svg ? ', isSVG: true' : '';
|
|
294
|
+
// A dash in the tag is what makes it a custom element, and the tag is known
|
|
295
|
+
// here, so the runtime does not have to read it back off the node. It falls
|
|
296
|
+
// back to that check when the flag is absent, which is how a hand-written
|
|
297
|
+
// `spread` call still behaves.
|
|
298
|
+
// For a custom element it also names the props wrapped as accessors: any other
|
|
299
|
+
// function is a property value (an action, a callback) and must not be called.
|
|
300
|
+
const accessors = node.props
|
|
301
|
+
.filter((p) => p.kind !== 'spread' && p.expr !== undefined && p.reactive && p.name !== 'ref')
|
|
302
|
+
.map((p) => p.name);
|
|
303
|
+
const customFlag = node.tag.includes('-') ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : '';
|
|
304
|
+
parts.push(`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// Insert each child individually (like the jsx runtime's applyProps), so a
|
|
308
|
+
// reactive/component child that isn't the only child still gets its own
|
|
309
|
+
// reactive binding. A single insert(el, [array]) does NOT bind function
|
|
310
|
+
// elements sitting inside the array.
|
|
311
|
+
for (const child of node.children) {
|
|
312
|
+
used.add('insert');
|
|
313
|
+
const childCode = emitNode(child, used, templates);
|
|
314
|
+
// `null` asks insert for an anchor of its own, which holds the child's place among
|
|
315
|
+
// its siblings. Every child whose content can change needs one: an expression the
|
|
316
|
+
// classifier calls opaque (an arrow, a function passed by name) may still be one.
|
|
317
|
+
// Only a literal never changes, and it goes straight in.
|
|
318
|
+
const anchored = (child.kind === 'expr' && (child.reactive || !isLiteralCode(child.code))) ||
|
|
319
|
+
child.kind === 'component' ||
|
|
320
|
+
child.kind === 'control';
|
|
321
|
+
parts.push(anchored ? `insert(${el}, ${childCode}, null);` : `insert(${el}, ${childCode});`);
|
|
322
|
+
}
|
|
323
|
+
parts.push(`return ${el};`);
|
|
324
|
+
return `(() => { ${parts.join(' ')} })()`;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/** A string, number, boolean or nullish literal: an expression that never renders anything else. */
|
|
329
|
+
const LITERAL = /^\s*(?:'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`[^`$]*`|-?\d[\d_.eE+-]*|true|false|null|undefined)\s*$/;
|
|
330
|
+
const isLiteralCode = (code) => LITERAL.test(code);
|
|
331
|
+
/**
|
|
332
|
+
* Substitute template declarations into the code as inline `createTemplate` calls.
|
|
333
|
+
*
|
|
334
|
+
* For hosts that cannot hoist: nested JSX lowered inside an expression, where there is
|
|
335
|
+
* no module scope to reach. `createTemplate` caches by HTML string, so this costs a Map
|
|
336
|
+
* lookup per instantiation rather than a re-parse, and is still far cheaper than
|
|
337
|
+
* building the subtree element by element.
|
|
338
|
+
*/
|
|
339
|
+
export function inlineTemplates(code, templates) {
|
|
340
|
+
if (!templates || templates.length === 0)
|
|
341
|
+
return code;
|
|
342
|
+
const byId = new Map(templates.map((t) => [t.id, JSON.stringify(t.html)]));
|
|
343
|
+
// Single pass, for the same reason `adoptTemplates` needs one.
|
|
344
|
+
return code.replace(/_tmpl\$\d+/g, (id) => byId.get(id) ?? id);
|
|
345
|
+
}
|
|
346
|
+
export const imperativeBackend = {
|
|
347
|
+
name: 'imperative',
|
|
348
|
+
contract: RUNTIME_CONTRACT_V1,
|
|
349
|
+
emit(node, options) {
|
|
350
|
+
const used = new Set();
|
|
351
|
+
// Off by default: `createTemplate` builds real DOM nodes, so on the server they
|
|
352
|
+
// land in a ServerElement tree and clash, and during hydration a clone creates a
|
|
353
|
+
// fresh node where the existing one must be adopted. Both need runtime support
|
|
354
|
+
// that does not exist yet, so the client-only win stays opt-in until it does.
|
|
355
|
+
const templates = options?.templateClone !== false
|
|
356
|
+
? []
|
|
357
|
+
: undefined;
|
|
358
|
+
partial = options?.partialTemplates === true;
|
|
359
|
+
const code = emitNode(node, used, templates);
|
|
360
|
+
return { code, imports: Array.from(used), templates };
|
|
361
|
+
},
|
|
362
|
+
};
|