@fluixi/compiler 1.0.0-alpha.75 → 1.0.0-alpha.76

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.
Files changed (72) hide show
  1. package/dist/analyze/intrinsics.cjs +1 -0
  2. package/dist/analyze/intrinsics.d.ts +67 -0
  3. package/dist/analyze/intrinsics.d.ts.map +1 -0
  4. package/dist/analyze/intrinsics.js +91 -0
  5. package/dist/analyze/intrinsics.mjs +1 -0
  6. package/dist/analyze/props-destructure.cjs +1 -0
  7. package/dist/analyze/props-destructure.d.ts +84 -0
  8. package/dist/analyze/props-destructure.d.ts.map +1 -0
  9. package/dist/analyze/props-destructure.js +272 -0
  10. package/dist/analyze/props-destructure.mjs +1 -0
  11. package/dist/analyze/reactive-bindings.cjs +1 -0
  12. package/dist/analyze/reactive-bindings.d.ts +34 -0
  13. package/dist/analyze/reactive-bindings.d.ts.map +1 -0
  14. package/dist/analyze/reactive-bindings.js +153 -0
  15. package/dist/analyze/reactive-bindings.mjs +1 -0
  16. package/dist/{babel-GXO3KAVI.mjs → babel-YQ42WUS3.mjs} +1 -1
  17. package/dist/chunk-545LMC5W.mjs +1 -0
  18. package/dist/{chunk-TIE4XNAM.mjs → chunk-L2QPV25P.mjs} +1 -1
  19. package/dist/frontend/babel/build-ir-lit.cjs +1 -1
  20. package/dist/frontend/babel/build-ir-lit.mjs +1 -1
  21. package/dist/frontend/babel/build-ir.cjs +1 -1
  22. package/dist/frontend/babel/build-ir.mjs +1 -1
  23. package/dist/frontend/babel/index.cjs +1 -1
  24. package/dist/frontend/babel/index.mjs +1 -1
  25. package/dist/frontend/babel/lower-template.cjs +1 -1
  26. package/dist/frontend/babel/lower-template.mjs +1 -1
  27. package/dist/frontend/babel/plugin.cjs +1 -1
  28. package/dist/frontend/babel/plugin.mjs +1 -1
  29. package/dist/index.cjs +8 -8
  30. package/dist/index.mjs +9 -9
  31. package/dist/integrations.cjs +18 -14
  32. package/dist/integrations.d.ts +14 -0
  33. package/dist/integrations.d.ts.map +1 -1
  34. package/dist/integrations.js +16 -1
  35. package/dist/integrations.mjs +11 -11
  36. package/dist/lower/compile-template.cjs +1 -1
  37. package/dist/lower/compile-template.mjs +1 -1
  38. package/dist/lower/template.cjs +1 -1
  39. package/dist/lower/template.d.ts.map +1 -1
  40. package/dist/lower/template.js +7 -5
  41. package/dist/lower/template.mjs +1 -1
  42. package/dist/resolve/component-globals.cjs +9 -9
  43. package/dist/resolve/component-globals.d.ts +8 -0
  44. package/dist/resolve/component-globals.d.ts.map +1 -1
  45. package/dist/resolve/component-globals.js +17 -3
  46. package/dist/resolve/component-globals.mjs +9 -9
  47. package/dist/resolve/write-globals.cjs +9 -9
  48. package/dist/resolve/write-globals.mjs +9 -9
  49. package/dist/transform/index.cjs +5 -1
  50. package/dist/transform/index.d.ts +4 -0
  51. package/dist/transform/index.d.ts.map +1 -1
  52. package/dist/transform/index.js +2 -0
  53. package/dist/transform/index.mjs +14 -10
  54. package/dist/transform/intrinsics.cjs +3 -0
  55. package/dist/transform/intrinsics.d.ts +22 -0
  56. package/dist/transform/intrinsics.d.ts.map +1 -0
  57. package/dist/transform/intrinsics.js +78 -0
  58. package/dist/transform/intrinsics.mjs +3 -0
  59. package/dist/transform/props.cjs +3 -0
  60. package/dist/transform/props.d.ts +46 -0
  61. package/dist/transform/props.d.ts.map +1 -0
  62. package/dist/transform/props.js +221 -0
  63. package/dist/transform/props.mjs +12 -0
  64. package/dist/transform/templates.cjs +4 -1
  65. package/dist/transform/templates.d.ts +20 -0
  66. package/dist/transform/templates.d.ts.map +1 -1
  67. package/dist/transform/templates.js +41 -6
  68. package/dist/transform/templates.mjs +13 -10
  69. package/dist/transform-ZWIHEZVN.mjs +7 -0
  70. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  71. package/package.json +3 -3
  72. package/dist/transform-PJX2GNSV.mjs +0 -3
@@ -0,0 +1 @@
1
+ "use strict";var c=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var m=Object.prototype.hasOwnProperty;var d=(e,r)=>{for(var n in r)c(e,n,{get:r[n],enumerable:!0})},f=(e,r,n,i)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of u(r))!m.call(e,o)&&o!==n&&c(e,o,{get:()=>r[o],enumerable:!(i=l(r,o))||i.enumerable});return e};var x=e=>f(c({},"__esModule",{value:!0}),e);var h={};d(h,{INTRINSICS:()=>s,PRIMITIVE_MODULES:()=>a,REACTIVE_MODULE:()=>t,RUNTIME_PRIMITIVES:()=>p,isIntrinsicName:()=>R,isPrimitiveModule:()=>v,mayHaveIntrinsic:()=>I});module.exports=x(h);var t="@fluixi/reactive/signal",s={$signal:{export:"signal",module:t,returns:"signal-handle"},$memo:{export:"memo",module:t,returns:"memo-handle"},$effect:{export:"effect",module:t,returns:"effect"},$store:{export:"store",module:"@fluixi/reactive/store",returns:"store-handle"},$resource:{export:"resource",module:t,returns:"resource-handle"},$selector:{export:"createSelector",module:t,returns:"memo-accessor"},$deferred:{export:"createDeferred",module:t,returns:"memo-accessor"},$untrack:{export:"untrack",module:t,returns:"plain"},$untrackStore:{export:"untrackStore",module:"@fluixi/reactive/store",returns:"plain"}},p={signal:"signal-handle",memo:"memo-handle",effect:"effect",store:"store-handle",createSignal:["signal-accessor","signal-setter"],createMemo:"memo-accessor",createEffect:"effect",createRenderEffect:"effect",createStore:"reactive-object",resource:"resource-handle",createResource:"reactive-object",createSelector:"memo-accessor",createDeferred:"memo-accessor"},a=["@fluixi/reactive","@fluixi/reactive/signal","@fluixi/reactive/signal-next","@fluixi/reactive/store","@fluixi/core","@fluixi/core/rx"],g=new RegExp(`\\$(?:${Object.keys(s).map(e=>e.slice(1)).join("|")})\\s*\\(`);function I(e){return g.test(e)}function R(e){return Object.prototype.hasOwnProperty.call(s,e)}function v(e){return a.includes(e)}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The reactive primitives the compiler knows by name, in one place.
3
+ *
4
+ * Two ways to write the same thing. The runtime call, which needs an import and works
5
+ * without us:
6
+ *
7
+ * import { createSignal } from '@fluixi/reactive/signal';
8
+ * const count = createSignal(0);
9
+ *
10
+ * And the intrinsic, which the compiler resolves and imports for you:
11
+ *
12
+ * const count = $signal(0);
13
+ *
14
+ * They are not two reactive systems. `$signal` compiles to the same `createSignal` call,
15
+ * so there's one graph, one lifecycle, one SSR story. The `$` form is authoring sugar
16
+ * the compiler understands; the runtime form stays public, because a library that never
17
+ * runs this compiler still has to be able to write reactive code.
18
+ *
19
+ * Everything the compiler needs to know about a primitive lives in this table rather
20
+ * than spread across passes, so adding one is an entry, not a new code path.
21
+ */
22
+ /** What a binding holds, once we know where it came from. */
23
+ export type ReactiveKind = 'plain'
24
+ /** `signal(0)` — one object, read with `.get()` and written with `.set()`. */
25
+ | 'signal-handle'
26
+ /** `memo(fn)` — read-only, so `.get()` only. */
27
+ | 'memo-handle'
28
+ /** `store(obj)` — read properties directly, `.set()` writes. */
29
+ | 'store-handle'
30
+ /** `resource(fn)` — `.get()` reads, with `.loading`/`.latest`/`.refetch()` on it. */
31
+ | 'resource-handle'
32
+ /** The accessor half of the tuple form, `const [count] = createSignal(0)`. */
33
+ | 'signal-accessor' | 'signal-setter' | 'memo-accessor' | 'effect' | 'reactive-object';
34
+ export interface Intrinsic {
35
+ /** Runtime export this compiles to. */
36
+ export: string;
37
+ /** Module it comes from. */
38
+ module: string;
39
+ /**
40
+ * What the call yields. A tuple means the call is destructured into that many
41
+ * bindings — `createSignal` gives an accessor and a setter, in that order.
42
+ */
43
+ returns: ReactiveKind | readonly ReactiveKind[];
44
+ }
45
+ /** Module the reactive primitives are imported from when the compiler adds the import. */
46
+ export declare const REACTIVE_MODULE = "@fluixi/reactive/signal";
47
+ /**
48
+ * `$name` → the runtime it stands for.
49
+ *
50
+ * The `$` prefix is reserved. A local binding of the same name wins — it has to, or the
51
+ * compiler would rewrite code that means something else — and that's reported rather
52
+ * than done quietly.
53
+ */
54
+ export declare const INTRINSICS: Readonly<Record<string, Intrinsic>>;
55
+ /**
56
+ * The runtime names, for the other direction: source that imports `createSignal` gets
57
+ * classified the same way as source that writes `$signal`, so later passes see one
58
+ * vocabulary whichever form the author used.
59
+ */
60
+ export declare const RUNTIME_PRIMITIVES: Readonly<Record<string, Intrinsic['returns']>>;
61
+ /** Modules whose exports count as reactive primitives. */
62
+ export declare const PRIMITIVE_MODULES: readonly string[];
63
+ export declare function mayHaveIntrinsic(code: string): boolean;
64
+ export declare function isIntrinsicName(name: string): boolean;
65
+ /** Whether a module path exports the reactive primitives. */
66
+ export declare function isPrimitiveModule(source: string): boolean;
67
+ //# sourceMappingURL=intrinsics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intrinsics.d.ts","sourceRoot":"","sources":["../../src/analyze/intrinsics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,6DAA6D;AAC7D,MAAM,MAAM,YAAY,GACpB,OAAO;AACT,8EAA8E;GAC5E,eAAe;AACjB,gDAAgD;GAC9C,aAAa;AACf,gEAAgE;GAC9D,cAAc;AAChB,qFAAqF;GACnF,iBAAiB;AACnB,8EAA8E;GAC5E,iBAAiB,GACjB,eAAe,GACf,eAAe,GACf,QAAQ,GACR,iBAAiB,CAAC;AAEtB,MAAM,WAAW,SAAS;IACxB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,EAAE,YAAY,GAAG,SAAS,YAAY,EAAE,CAAC;CACjD;AAED,0FAA0F;AAC1F,eAAO,MAAM,eAAe,4BAA4B,CAAC;AAEzD;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAmB1D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAc7E,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAO9C,CAAC;AAQF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,6DAA6D;AAC7D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzD"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * The reactive primitives the compiler knows by name, in one place.
3
+ *
4
+ * Two ways to write the same thing. The runtime call, which needs an import and works
5
+ * without us:
6
+ *
7
+ * import { createSignal } from '@fluixi/reactive/signal';
8
+ * const count = createSignal(0);
9
+ *
10
+ * And the intrinsic, which the compiler resolves and imports for you:
11
+ *
12
+ * const count = $signal(0);
13
+ *
14
+ * They are not two reactive systems. `$signal` compiles to the same `createSignal` call,
15
+ * so there's one graph, one lifecycle, one SSR story. The `$` form is authoring sugar
16
+ * the compiler understands; the runtime form stays public, because a library that never
17
+ * runs this compiler still has to be able to write reactive code.
18
+ *
19
+ * Everything the compiler needs to know about a primitive lives in this table rather
20
+ * than spread across passes, so adding one is an entry, not a new code path.
21
+ */
22
+ /** Module the reactive primitives are imported from when the compiler adds the import. */
23
+ export const REACTIVE_MODULE = '@fluixi/reactive/signal';
24
+ /**
25
+ * `$name` → the runtime it stands for.
26
+ *
27
+ * The `$` prefix is reserved. A local binding of the same name wins — it has to, or the
28
+ * compiler would rewrite code that means something else — and that's reported rather
29
+ * than done quietly.
30
+ */
31
+ export const INTRINSICS = {
32
+ $signal: { export: 'signal', module: REACTIVE_MODULE, returns: 'signal-handle' },
33
+ $memo: { export: 'memo', module: REACTIVE_MODULE, returns: 'memo-handle' },
34
+ $effect: { export: 'effect', module: REACTIVE_MODULE, returns: 'effect' },
35
+ $store: { export: 'store', module: '@fluixi/reactive/store', returns: 'store-handle' },
36
+ $resource: { export: 'resource', module: REACTIVE_MODULE, returns: 'resource-handle' },
37
+ // No short runtime form for these yet, so the intrinsic stands for the `create*`
38
+ // name. Still worth having — no import to write — and when a shorter form is
39
+ // designed, the change is this line rather than a compiler pass.
40
+ $selector: { export: 'createSelector', module: REACTIVE_MODULE, returns: 'memo-accessor' },
41
+ $deferred: { export: 'createDeferred', module: REACTIVE_MODULE, returns: 'memo-accessor' },
42
+ // Not value constructors, but the compiler has to know them: they change what a read
43
+ // does. `untrack` suppresses dependency collection, which is exactly the kind of thing
44
+ // dependency analysis must not guess at.
45
+ $untrack: { export: 'untrack', module: REACTIVE_MODULE, returns: 'plain' },
46
+ $untrackStore: { export: 'untrackStore', module: '@fluixi/reactive/store', returns: 'plain' },
47
+ };
48
+ /**
49
+ * The runtime names, for the other direction: source that imports `createSignal` gets
50
+ * classified the same way as source that writes `$signal`, so later passes see one
51
+ * vocabulary whichever form the author used.
52
+ */
53
+ export const RUNTIME_PRIMITIVES = {
54
+ signal: 'signal-handle',
55
+ memo: 'memo-handle',
56
+ effect: 'effect',
57
+ store: 'store-handle',
58
+ createSignal: ['signal-accessor', 'signal-setter'],
59
+ createMemo: 'memo-accessor',
60
+ createEffect: 'effect',
61
+ createRenderEffect: 'effect',
62
+ createStore: 'reactive-object',
63
+ resource: 'resource-handle',
64
+ createResource: 'reactive-object',
65
+ createSelector: 'memo-accessor',
66
+ createDeferred: 'memo-accessor',
67
+ };
68
+ /** Modules whose exports count as reactive primitives. */
69
+ export const PRIMITIVE_MODULES = [
70
+ '@fluixi/reactive',
71
+ '@fluixi/reactive/signal',
72
+ '@fluixi/reactive/signal-next',
73
+ '@fluixi/reactive/store',
74
+ '@fluixi/core',
75
+ '@fluixi/core/rx',
76
+ ];
77
+ /**
78
+ * Cheap text check for the file-skipping fast path: a module with no `$` call never
79
+ * needs parsing. Non-global, so it has no lastIndex to carry between calls.
80
+ */
81
+ const INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join('|')})\\s*\\(`);
82
+ export function mayHaveIntrinsic(code) {
83
+ return INTRINSIC_CALL.test(code);
84
+ }
85
+ export function isIntrinsicName(name) {
86
+ return Object.prototype.hasOwnProperty.call(INTRINSICS, name);
87
+ }
88
+ /** Whether a module path exports the reactive primitives. */
89
+ export function isPrimitiveModule(source) {
90
+ return PRIMITIVE_MODULES.includes(source);
91
+ }
@@ -0,0 +1 @@
1
+ var r="@fluixi/reactive/signal",t={$signal:{export:"signal",module:r,returns:"signal-handle"},$memo:{export:"memo",module:r,returns:"memo-handle"},$effect:{export:"effect",module:r,returns:"effect"},$store:{export:"store",module:"@fluixi/reactive/store",returns:"store-handle"},$resource:{export:"resource",module:r,returns:"resource-handle"},$selector:{export:"createSelector",module:r,returns:"memo-accessor"},$deferred:{export:"createDeferred",module:r,returns:"memo-accessor"},$untrack:{export:"untrack",module:r,returns:"plain"},$untrackStore:{export:"untrackStore",module:"@fluixi/reactive/store",returns:"plain"}},c={signal:"signal-handle",memo:"memo-handle",effect:"effect",store:"store-handle",createSignal:["signal-accessor","signal-setter"],createMemo:"memo-accessor",createEffect:"effect",createRenderEffect:"effect",createStore:"reactive-object",resource:"resource-handle",createResource:"reactive-object",createSelector:"memo-accessor",createDeferred:"memo-accessor"},o=["@fluixi/reactive","@fluixi/reactive/signal","@fluixi/reactive/signal-next","@fluixi/reactive/store","@fluixi/core","@fluixi/core/rx"],n=new RegExp(`\\$(?:${Object.keys(t).map(e=>e.slice(1)).join("|")})\\s*\\(`);function s(e){return n.test(e)}function i(e){return Object.prototype.hasOwnProperty.call(t,e)}function a(e){return o.includes(e)}export{t as INTRINSICS,o as PRIMITIVE_MODULES,r as REACTIVE_MODULE,c as RUNTIME_PRIMITIVES,i as isIntrinsicName,a as isPrimitiveModule,s as mayHaveIntrinsic};
@@ -0,0 +1 @@
1
+ "use strict";var p=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var k=(s,t)=>{for(var e in t)p(s,e,{get:t[e],enumerable:!0})},v=(s,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of w(t))!b.call(s,a)&&a!==e&&p(s,a,{get:()=>t[a],enumerable:!(n=h(t,a))||n.enumerable});return s};var x=s=>v(p({},"__esModule",{value:!0}),s);var R={};k(R,{explainBail:()=>E,planPropsDestructure:()=>D});module.exports=x(R);function u(s,t){if(s)switch(s.type){case"Identifier":t.add(s.name);return;case"ObjectPattern":for(let e of s.properties)e.type==="RestElement"?u(e.argument,t):u(e.value,t);return;case"ArrayPattern":for(let e of s.elements)u(e,t);return;case"AssignmentPattern":u(s.left,t);return;case"RestElement":u(s.argument,t);return}}function m(s){switch(s.type){case"StringLiteral":case"NumericLiteral":case"BooleanLiteral":case"NullLiteral":case"BigIntLiteral":case"RegExpLiteral":case"Identifier":return!0;case"Literal":return!0;case"TemplateLiteral":return s.expressions.every(m);case"UnaryExpression":return m(s.argument);case"MemberExpression":return!1;default:return!1}}function f(s,t){if(!(!s||typeof s!="object")){t(s);for(let e of Object.keys(s)){if(e==="loc"||e==="range"||e==="leadingComments"||e==="trailingComments")continue;let n=s[e];if(Array.isArray(n))for(let a of n)a&&typeof a=="object"&&f(a,t);else n&&typeof n=="object"&&typeof n.type=="string"&&f(n,t)}}}function P(s){let t=new Set;return f(s,e=>{if(e.type==="AssignmentExpression")u(e.left,t);else if(e.type==="UpdateExpression"){let n=e.argument;n?.type==="Identifier"&&t.add(n.name)}}),t}function S(s,t){let e=new Set,n=a=>{let c=new Set;u(a,c);for(let i of c)t.has(i)&&e.add(i)};return f(s,a=>{switch(a.type){case"VariableDeclarator":n(a.id);return;case"FunctionDeclaration":case"FunctionExpression":case"ArrowFunctionExpression":n(a.id);for(let c of a.params??[])n(c);return;case"CatchClause":n(a.param);return;case"ClassDeclaration":case"ClassExpression":n(a.id);return}}),e}function N(s,t,e){for(let n of s.properties??[]){if(n.type==="RestElement"){let o=n.argument;if(o?.type!=="Identifier"||t.length>0){let l=new Set;u(o,l);for(let y of l)e.bails.push({name:y,reason:"rest"});continue}e.rest={name:o.name,keys:[]};continue}if(n.computed){let o=new Set;u(n.value,o);for(let l of o)e.bails.push({name:l,reason:"computed"});continue}let a=n.key,c=a.type==="Identifier"?a.name:a.value,i=n.value,r;i.type==="AssignmentPattern"&&(r=i.right,i=i.left);let d=[...t,c];if(i.type==="Identifier"){if(r&&!m(r)){e.bails.push({name:i.name,reason:"unsafe-default"});continue}e.reads.push({name:i.name,path:d,...r?{fallback:r}:{}});continue}if(i.type==="ObjectPattern"){if(r){let o=new Set;u(i,o);for(let l of o)e.bails.push({name:l,reason:"unsafe-default"});continue}N(i,d,e);continue}let g=new Set;u(i,g);for(let o of g)e.bails.push({name:o,reason:"computed"})}}function D(s,t){let e={reads:[],bails:[]};if(s?.type!=="ObjectPattern"||(N(s,[],e),e.rest&&(t?.type!=="BlockStatement"?(e.bails.push({name:e.rest.name,reason:"rest"}),delete e.rest):e.rest.keys=(s.properties??[]).filter(r=>r.type!=="RestElement"&&!r.computed).map(r=>{let d=r.key;return d.type==="Identifier"?d.name:d.value})),e.reads.length===0&&!e.rest))return e;let n=new Set(e.reads.map(r=>r.name));e.rest&&n.add(e.rest.name);let a=P(t),c=S(t,n),i=[];for(let r of e.reads)a.has(r.name)?e.bails.push({name:r.name,reason:"reassigned"}):c.has(r.name)?e.bails.push({name:r.name,reason:"shadowed"}):i.push(r);return e.reads=i,e.rest&&a.has(e.rest.name)?(e.bails.push({name:e.rest.name,reason:"reassigned"}),delete e.rest):e.rest&&c.has(e.rest.name)&&(e.bails.push({name:e.rest.name,reason:"shadowed"}),delete e.rest),e}function E(s){switch(s){case"rest":return"this rest element cannot be served by splitProps, and copying the props into a plain object would lose the getters";case"computed":return"the property is not known until it runs";case"reassigned":return"the binding is assigned to, and props are read-only";case"shadowed":return"an inner scope binds the same name";case"unsafe-default":return"the default would have to run again on every read"}}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Props destructuring, without losing reactivity.
3
+ *
4
+ * Props come in as an object of getters, so `props.name` re-reads every time and that
5
+ * keeps a binding current. `const { name } = props` copies the value out once, which is
6
+ * why we call splitProps in about 84 components.
7
+ *
8
+ * The plan here rewrites `name` to `props.name` at each use rather than making it an
9
+ * accessor. `() => props.name` would change the binding's type, and then every use has
10
+ * to change with it: passed to a plain function, stored in an array, compared with
11
+ * `===`. That's a whole-program job. Substitution is one function body and a rename.
12
+ *
13
+ * It does change one thing. A read that happens later sees the current value instead of
14
+ * the value at destructure time, which is the reason to do this at all. Anything past
15
+ * that I leave as written and report, so splitProps stays correct where it's still the
16
+ * right answer.
17
+ *
18
+ * No babel in here (see no-babel-in-core.spec.ts) — nodes are read structurally, the way
19
+ * transform/bindings.ts does, so either front-end can drive it.
20
+ */
21
+ interface Node {
22
+ type: string;
23
+ [key: string]: unknown;
24
+ }
25
+ /** Why a binding was left alone. All of these change more than when the read happens. */
26
+ export type DestructureBail =
27
+ /**
28
+ * A rest we can't serve: destructured further (`...{ a }`), or in a function with no
29
+ * block to put the splitProps call in (`({ a, ...rest }) => …`).
30
+ */
31
+ 'rest'
32
+ /** `const { [key]: a } = props` — we don't know the property until it runs. */
33
+ | 'computed'
34
+ /** The binding gets assigned to, and props are read-only, so it can't become a member. */
35
+ | 'reassigned'
36
+ /** An inner scope binds the same name, so a use site could mean either one. */
37
+ | 'shadowed'
38
+ /**
39
+ * A default we can't re-run on every read: a function hands out a new identity each
40
+ * time, a call runs its side effect each time.
41
+ */
42
+ | 'unsafe-default';
43
+ export interface PropsRead {
44
+ /** The local name the pattern introduced. */
45
+ name: string;
46
+ /** Property path to read through instead — `['user', 'name']` for `{ user: { name } }`. */
47
+ path: string[];
48
+ /**
49
+ * Default from the pattern. JS only applies one for `undefined`, not for null, so
50
+ * codegen has to test `=== undefined` rather than reach for `??`, and re-run it on
51
+ * every read.
52
+ */
53
+ fallback?: Node;
54
+ }
55
+ export interface DestructurePlan {
56
+ /** Bindings we can rewrite to a property read. */
57
+ reads: PropsRead[];
58
+ /** Bindings we left alone, with the reason to report. */
59
+ bails: {
60
+ name: string;
61
+ reason: DestructureBail;
62
+ }[];
63
+ /**
64
+ * A `...rest`, and the keys it has to exclude.
65
+ *
66
+ * This is what splitProps is for: it copies the getters across rather than reading
67
+ * them, which is the reason a plain `...rest` can't be rewritten. So instead of
68
+ * refusing the pattern, we write the splitProps call the author would have written.
69
+ */
70
+ rest?: {
71
+ name: string;
72
+ keys: string[];
73
+ };
74
+ }
75
+ /**
76
+ * Plan one props pattern. `pattern` is the ObjectPattern, `body` the function body its
77
+ * bindings live in. Every binding ends up in `reads` (rewrite it) or `bails` (leave it
78
+ * and say why).
79
+ */
80
+ export declare function planPropsDestructure(pattern: Node, body: Node | null | undefined): DestructurePlan;
81
+ /** Reason text for the diagnostic. */
82
+ export declare function explainBail(reason: DestructureBail): string;
83
+ export {};
84
+ //# sourceMappingURL=props-destructure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"props-destructure.d.ts","sourceRoot":"","sources":["../../src/analyze/props-destructure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,UAAU,IAAI;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,yFAAyF;AACzF,MAAM,MAAM,eAAe;AACzB;;;GAGG;AACD,MAAM;AACR,+EAA+E;GAC7E,UAAU;AACZ,0FAA0F;GACxF,YAAY;AACd,+EAA+E;GAC7E,UAAU;AACZ;;;GAGG;GACD,gBAAgB,CAAC;AAErB,MAAM,WAAW,SAAS;IACxB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,2FAA2F;IAC3F,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,yDAAyD;IACzD,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,eAAe,CAAA;KAAE,EAAE,CAAC;IACnD;;;;;;OAMG;IACH,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CACzC;AAoLD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,GAAG,eAAe,CAgDlG;AAED,sCAAsC;AACtC,wBAAgB,WAAW,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAa3D"}
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Props destructuring, without losing reactivity.
3
+ *
4
+ * Props come in as an object of getters, so `props.name` re-reads every time and that
5
+ * keeps a binding current. `const { name } = props` copies the value out once, which is
6
+ * why we call splitProps in about 84 components.
7
+ *
8
+ * The plan here rewrites `name` to `props.name` at each use rather than making it an
9
+ * accessor. `() => props.name` would change the binding's type, and then every use has
10
+ * to change with it: passed to a plain function, stored in an array, compared with
11
+ * `===`. That's a whole-program job. Substitution is one function body and a rename.
12
+ *
13
+ * It does change one thing. A read that happens later sees the current value instead of
14
+ * the value at destructure time, which is the reason to do this at all. Anything past
15
+ * that I leave as written and report, so splitProps stays correct where it's still the
16
+ * right answer.
17
+ *
18
+ * No babel in here (see no-babel-in-core.spec.ts) — nodes are read structurally, the way
19
+ * transform/bindings.ts does, so either front-end can drive it.
20
+ */
21
+ /** Every name a pattern introduces, so we can check each one for shadowing and writes. */
22
+ function patternNames(node, out) {
23
+ if (!node)
24
+ return;
25
+ switch (node.type) {
26
+ case 'Identifier':
27
+ out.add(node.name);
28
+ return;
29
+ case 'ObjectPattern':
30
+ for (const p of node.properties) {
31
+ if (p.type === 'RestElement')
32
+ patternNames(p.argument, out);
33
+ else
34
+ patternNames(p.value, out);
35
+ }
36
+ return;
37
+ case 'ArrayPattern':
38
+ for (const e of node.elements)
39
+ patternNames(e, out);
40
+ return;
41
+ case 'AssignmentPattern':
42
+ patternNames(node.left, out);
43
+ return;
44
+ case 'RestElement':
45
+ patternNames(node.argument, out);
46
+ return;
47
+ }
48
+ }
49
+ /**
50
+ * Defaults get re-run on every read now, so they have to be cheap and stable. Literals
51
+ * and identifiers are. A function isn't — it hands out a new identity each read, which
52
+ * breaks anything comparing props — and neither is a call.
53
+ */
54
+ function isSafeDefault(node) {
55
+ switch (node.type) {
56
+ case 'StringLiteral':
57
+ case 'NumericLiteral':
58
+ case 'BooleanLiteral':
59
+ case 'NullLiteral':
60
+ case 'BigIntLiteral':
61
+ case 'RegExpLiteral':
62
+ case 'Identifier':
63
+ return true;
64
+ // ESTree spells every literal as `Literal`; both shapes reach this analysis.
65
+ case 'Literal':
66
+ return true;
67
+ case 'TemplateLiteral':
68
+ return node.expressions.every(isSafeDefault);
69
+ case 'UnaryExpression':
70
+ return isSafeDefault(node.argument);
71
+ case 'MemberExpression':
72
+ // Could be a getter, and we'd be running it on every read.
73
+ return false;
74
+ default:
75
+ return false;
76
+ }
77
+ }
78
+ /** Walk everything under `root`. No visitor API here, so it's hand-rolled. */
79
+ function walk(root, visit) {
80
+ if (!root || typeof root !== 'object')
81
+ return;
82
+ visit(root);
83
+ for (const key of Object.keys(root)) {
84
+ if (key === 'loc' || key === 'range' || key === 'leadingComments' || key === 'trailingComments')
85
+ continue;
86
+ const value = root[key];
87
+ if (Array.isArray(value)) {
88
+ for (const item of value)
89
+ if (item && typeof item === 'object')
90
+ walk(item, visit);
91
+ }
92
+ else if (value && typeof value === 'object' && typeof value.type === 'string') {
93
+ walk(value, visit);
94
+ }
95
+ }
96
+ }
97
+ /** Anything written to in the body — `name = x`, `name++`, `[name] = …`. */
98
+ function reassignedNames(body) {
99
+ const written = new Set();
100
+ walk(body, (node) => {
101
+ if (node.type === 'AssignmentExpression')
102
+ patternNames(node.left, written);
103
+ else if (node.type === 'UpdateExpression') {
104
+ const arg = node.argument;
105
+ if (arg?.type === 'Identifier')
106
+ written.add(arg.name);
107
+ }
108
+ });
109
+ return written;
110
+ }
111
+ /** Names an inner scope re-binds, where a use site would mean something else. */
112
+ function shadowedNames(body, candidates) {
113
+ const shadowed = new Set();
114
+ const note = (node) => {
115
+ const names = new Set();
116
+ patternNames(node, names);
117
+ for (const n of names)
118
+ if (candidates.has(n))
119
+ shadowed.add(n);
120
+ };
121
+ walk(body, (node) => {
122
+ switch (node.type) {
123
+ case 'VariableDeclarator':
124
+ note(node.id);
125
+ return;
126
+ case 'FunctionDeclaration':
127
+ case 'FunctionExpression':
128
+ case 'ArrowFunctionExpression':
129
+ note(node.id);
130
+ for (const p of node.params ?? [])
131
+ note(p);
132
+ return;
133
+ case 'CatchClause':
134
+ note(node.param);
135
+ return;
136
+ case 'ClassDeclaration':
137
+ case 'ClassExpression':
138
+ note(node.id);
139
+ return;
140
+ }
141
+ });
142
+ return shadowed;
143
+ }
144
+ /** One level of an object pattern, recursing into nested ones. */
145
+ function collect(pattern, path, plan) {
146
+ for (const property of pattern.properties ?? []) {
147
+ if (property.type === 'RestElement') {
148
+ const argument = property.argument;
149
+ // Only a plain name. `...{ a }` would need its own splitProps and its own rules.
150
+ if (argument?.type !== 'Identifier' || path.length > 0) {
151
+ const names = new Set();
152
+ patternNames(argument, names);
153
+ for (const name of names)
154
+ plan.bails.push({ name, reason: 'rest' });
155
+ continue;
156
+ }
157
+ plan.rest = { name: argument.name, keys: [] };
158
+ continue;
159
+ }
160
+ if (property.computed) {
161
+ const names = new Set();
162
+ patternNames(property.value, names);
163
+ for (const name of names)
164
+ plan.bails.push({ name, reason: 'computed' });
165
+ continue;
166
+ }
167
+ const key = property.key;
168
+ const propertyName = (key.type === 'Identifier' ? key.name : key.value);
169
+ let value = property.value;
170
+ let fallback;
171
+ if (value.type === 'AssignmentPattern') {
172
+ fallback = value.right;
173
+ value = value.left;
174
+ }
175
+ const here = [...path, propertyName];
176
+ if (value.type === 'Identifier') {
177
+ if (fallback && !isSafeDefault(fallback)) {
178
+ plan.bails.push({ name: value.name, reason: 'unsafe-default' });
179
+ continue;
180
+ }
181
+ plan.reads.push({ name: value.name, path: here, ...(fallback ? { fallback } : {}) });
182
+ continue;
183
+ }
184
+ if (value.type === 'ObjectPattern') {
185
+ // `{ user: { name } = {} }` would have to build the default object on every read.
186
+ if (fallback) {
187
+ const names = new Set();
188
+ patternNames(value, names);
189
+ for (const name of names)
190
+ plan.bails.push({ name, reason: 'unsafe-default' });
191
+ continue;
192
+ }
193
+ collect(value, here, plan);
194
+ continue;
195
+ }
196
+ // An array pattern indexes into a value we can't prove is stable, so leave it.
197
+ const names = new Set();
198
+ patternNames(value, names);
199
+ for (const name of names)
200
+ plan.bails.push({ name, reason: 'computed' });
201
+ }
202
+ }
203
+ /**
204
+ * Plan one props pattern. `pattern` is the ObjectPattern, `body` the function body its
205
+ * bindings live in. Every binding ends up in `reads` (rewrite it) or `bails` (leave it
206
+ * and say why).
207
+ */
208
+ export function planPropsDestructure(pattern, body) {
209
+ const plan = { reads: [], bails: [] };
210
+ if (pattern?.type !== 'ObjectPattern')
211
+ return plan;
212
+ collect(pattern, [], plan);
213
+ if (plan.rest) {
214
+ // The splitProps call is a statement, so there has to be somewhere to put it.
215
+ if (body?.type !== 'BlockStatement') {
216
+ plan.bails.push({ name: plan.rest.name, reason: 'rest' });
217
+ delete plan.rest;
218
+ }
219
+ else {
220
+ // Keys the rest has to exclude: every top-level property the pattern names.
221
+ plan.rest.keys = (pattern.properties ?? [])
222
+ .filter((p) => p.type !== 'RestElement' && !p.computed)
223
+ .map((p) => {
224
+ const key = p.key;
225
+ return (key.type === 'Identifier' ? key.name : key.value);
226
+ });
227
+ }
228
+ }
229
+ if (plan.reads.length === 0 && !plan.rest)
230
+ return plan;
231
+ const candidates = new Set(plan.reads.map((r) => r.name));
232
+ if (plan.rest)
233
+ candidates.add(plan.rest.name);
234
+ const written = reassignedNames(body);
235
+ const shadowed = shadowedNames(body, candidates);
236
+ const kept = [];
237
+ for (const read of plan.reads) {
238
+ if (written.has(read.name))
239
+ plan.bails.push({ name: read.name, reason: 'reassigned' });
240
+ else if (shadowed.has(read.name))
241
+ plan.bails.push({ name: read.name, reason: 'shadowed' });
242
+ else
243
+ kept.push(read);
244
+ }
245
+ plan.reads = kept;
246
+ // The rest binding is declared by the code we emit, so it has to survive the same
247
+ // checks: reassigning it or shadowing the name would break what we wrote.
248
+ if (plan.rest && written.has(plan.rest.name)) {
249
+ plan.bails.push({ name: plan.rest.name, reason: 'reassigned' });
250
+ delete plan.rest;
251
+ }
252
+ else if (plan.rest && shadowed.has(plan.rest.name)) {
253
+ plan.bails.push({ name: plan.rest.name, reason: 'shadowed' });
254
+ delete plan.rest;
255
+ }
256
+ return plan;
257
+ }
258
+ /** Reason text for the diagnostic. */
259
+ export function explainBail(reason) {
260
+ switch (reason) {
261
+ case 'rest':
262
+ return 'this rest element cannot be served by splitProps, and copying the props into a plain object would lose the getters';
263
+ case 'computed':
264
+ return 'the property is not known until it runs';
265
+ case 'reassigned':
266
+ return 'the binding is assigned to, and props are read-only';
267
+ case 'shadowed':
268
+ return 'an inner scope binds the same name';
269
+ case 'unsafe-default':
270
+ return 'the default would have to run again on every read';
271
+ }
272
+ }
@@ -0,0 +1 @@
1
+ function u(s,n){if(s)switch(s.type){case"Identifier":n.add(s.name);return;case"ObjectPattern":for(let e of s.properties)e.type==="RestElement"?u(e.argument,n):u(e.value,n);return;case"ArrayPattern":for(let e of s.elements)u(e,n);return;case"AssignmentPattern":u(s.left,n);return;case"RestElement":u(s.argument,n);return}}function p(s){switch(s.type){case"StringLiteral":case"NumericLiteral":case"BooleanLiteral":case"NullLiteral":case"BigIntLiteral":case"RegExpLiteral":case"Identifier":return!0;case"Literal":return!0;case"TemplateLiteral":return s.expressions.every(p);case"UnaryExpression":return p(s.argument);case"MemberExpression":return!1;default:return!1}}function f(s,n){if(!(!s||typeof s!="object")){n(s);for(let e of Object.keys(s)){if(e==="loc"||e==="range"||e==="leadingComments"||e==="trailingComments")continue;let t=s[e];if(Array.isArray(t))for(let a of t)a&&typeof a=="object"&&f(a,n);else t&&typeof t=="object"&&typeof t.type=="string"&&f(t,n)}}}function y(s){let n=new Set;return f(s,e=>{if(e.type==="AssignmentExpression")u(e.left,n);else if(e.type==="UpdateExpression"){let t=e.argument;t?.type==="Identifier"&&n.add(t.name)}}),n}function h(s,n){let e=new Set,t=a=>{let c=new Set;u(a,c);for(let i of c)n.has(i)&&e.add(i)};return f(s,a=>{switch(a.type){case"VariableDeclarator":t(a.id);return;case"FunctionDeclaration":case"FunctionExpression":case"ArrowFunctionExpression":t(a.id);for(let c of a.params??[])t(c);return;case"CatchClause":t(a.param);return;case"ClassDeclaration":case"ClassExpression":t(a.id);return}}),e}function g(s,n,e){for(let t of s.properties??[]){if(t.type==="RestElement"){let o=t.argument;if(o?.type!=="Identifier"||n.length>0){let l=new Set;u(o,l);for(let N of l)e.bails.push({name:N,reason:"rest"});continue}e.rest={name:o.name,keys:[]};continue}if(t.computed){let o=new Set;u(t.value,o);for(let l of o)e.bails.push({name:l,reason:"computed"});continue}let a=t.key,c=a.type==="Identifier"?a.name:a.value,i=t.value,r;i.type==="AssignmentPattern"&&(r=i.right,i=i.left);let d=[...n,c];if(i.type==="Identifier"){if(r&&!p(r)){e.bails.push({name:i.name,reason:"unsafe-default"});continue}e.reads.push({name:i.name,path:d,...r?{fallback:r}:{}});continue}if(i.type==="ObjectPattern"){if(r){let o=new Set;u(i,o);for(let l of o)e.bails.push({name:l,reason:"unsafe-default"});continue}g(i,d,e);continue}let m=new Set;u(i,m);for(let o of m)e.bails.push({name:o,reason:"computed"})}}function w(s,n){let e={reads:[],bails:[]};if(s?.type!=="ObjectPattern"||(g(s,[],e),e.rest&&(n?.type!=="BlockStatement"?(e.bails.push({name:e.rest.name,reason:"rest"}),delete e.rest):e.rest.keys=(s.properties??[]).filter(r=>r.type!=="RestElement"&&!r.computed).map(r=>{let d=r.key;return d.type==="Identifier"?d.name:d.value})),e.reads.length===0&&!e.rest))return e;let t=new Set(e.reads.map(r=>r.name));e.rest&&t.add(e.rest.name);let a=y(n),c=h(n,t),i=[];for(let r of e.reads)a.has(r.name)?e.bails.push({name:r.name,reason:"reassigned"}):c.has(r.name)?e.bails.push({name:r.name,reason:"shadowed"}):i.push(r);return e.reads=i,e.rest&&a.has(e.rest.name)?(e.bails.push({name:e.rest.name,reason:"reassigned"}),delete e.rest):e.rest&&c.has(e.rest.name)&&(e.bails.push({name:e.rest.name,reason:"shadowed"}),delete e.rest),e}function b(s){switch(s){case"rest":return"this rest element cannot be served by splitProps, and copying the props into a plain object would lose the getters";case"computed":return"the property is not known until it runs";case"reassigned":return"the binding is assigned to, and props are read-only";case"shadowed":return"an inner scope binds the same name";case"unsafe-default":return"the default would have to run again on every read"}}export{b as explainBail,w as planPropsDestructure};
@@ -0,0 +1 @@
1
+ "use strict";var f=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var N=(t,r)=>{for(var n in r)f(t,n,{get:r[n],enumerable:!0})},v=(t,r,n,e)=>{if(r&&typeof r=="object"||typeof r=="function")for(let i of y(r))!I.call(t,i)&&i!==n&&f(t,i,{get:()=>r[i],enumerable:!(e=g(r,i))||e.enumerable});return t};var x=t=>v(f({},"__esModule",{value:!0}),t);var b={};N(b,{resolveReactiveBindings:()=>E});module.exports=x(b);var o="@fluixi/reactive/signal",c={$signal:{export:"signal",module:o,returns:"signal-handle"},$memo:{export:"memo",module:o,returns:"memo-handle"},$effect:{export:"effect",module:o,returns:"effect"},$store:{export:"store",module:"@fluixi/reactive/store",returns:"store-handle"},$resource:{export:"resource",module:o,returns:"resource-handle"},$selector:{export:"createSelector",module:o,returns:"memo-accessor"},$deferred:{export:"createDeferred",module:o,returns:"memo-accessor"},$untrack:{export:"untrack",module:o,returns:"plain"},$untrackStore:{export:"untrackStore",module:"@fluixi/reactive/store",returns:"plain"}},u={signal:"signal-handle",memo:"memo-handle",effect:"effect",store:"store-handle",createSignal:["signal-accessor","signal-setter"],createMemo:"memo-accessor",createEffect:"effect",createRenderEffect:"effect",createStore:"reactive-object",resource:"resource-handle",createResource:"reactive-object",createSelector:"memo-accessor",createDeferred:"memo-accessor"},R=["@fluixi/reactive","@fluixi/reactive/signal","@fluixi/reactive/signal-next","@fluixi/reactive/store","@fluixi/core","@fluixi/core/rx"],M=new RegExp(`\\$(?:${Object.keys(c).map(t=>t.slice(1)).join("|")})\\s*\\(`);function p(t){return R.includes(t)}function a(t,r){if(t)switch(t.type){case"Identifier":r.add(t.name);return;case"ObjectPattern":for(let n of t.properties??[])n.type==="RestElement"?a(n.argument,r):a(n.value,r);return;case"ArrayPattern":for(let n of t.elements??[])a(n,r);return;case"AssignmentPattern":a(t.left,r);return;case"RestElement":a(t.argument,r);return}}function l(t,r){if(!(!t||typeof t!="object"||typeof t.type!="string")){r(t);for(let n of Object.keys(t)){if(n==="loc"||n==="leadingComments"||n==="trailingComments")continue;let e=t[n];if(Array.isArray(e))for(let i of e)l(i,r);else e&&typeof e=="object"&&l(e,r)}}}function S(t,r){if(t.type!=="CallExpression")return;let n=t.callee;if(n?.type!=="Identifier")return;let e=n.name,i=r.primitives.get(e);if(i)return i;let s=c[e];if(s&&!r.shadowed.has(e))return s.returns}function h(t,r,n){if(Array.isArray(r)){t.type==="ArrayPattern"&&t.elements.forEach((e,i)=>{let s=r[i];e?.type==="Identifier"&&s&&n.set(e.name,s)});return}t.type==="Identifier"&&n.set(t.name,r)}function E(t){let r={kinds:new Map,primitives:new Map,shadowed:new Set},n=t;l(n,e=>{let i=new Set;if(e.type==="VariableDeclarator")a(e.id,i);else if(e.type==="FunctionDeclaration"||e.type==="ClassDeclaration")a(e.id,i);else if(e.type==="ImportDefaultSpecifier"||e.type==="ImportNamespaceSpecifier")a(e.local,i);else if(e.type==="ImportSpecifier")a(e.local,i);else if(e.type==="FunctionExpression"||e.type==="ArrowFunctionExpression"||e.type==="FunctionDeclaration")for(let s of e.params??[])a(s,i);for(let s of i)c[s]&&r.shadowed.add(s)});for(let e of n.body??[])if(e.type==="ImportDeclaration"&&p(e.source?.value))for(let i of e.specifiers??[]){if(i.type!=="ImportSpecifier")continue;let s=i.imported,m=s.type==="Identifier"?s.name:s.value,d=u[m];d&&r.primitives.set(i.local.name,d)}return l(n,e=>{if(e.type!=="VariableDeclarator"||!e.init)return;let i=S(e.init,r);i&&h(e.id,i,r.kinds)}),r}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Which names in a module hold reactive values, and what kind.
3
+ *
4
+ * Resolved from where a name came from, never from how it reads. This has to hold:
5
+ *
6
+ * import { createSignal as useState } from '@fluixi/reactive/signal';
7
+ * const count = useState(0); // a signal
8
+ *
9
+ * function createSignal() { return 42; }
10
+ * const value = createSignal(); // not a signal
11
+ *
12
+ * A name tells you nothing; the binding it resolves to tells you everything. So this
13
+ * walks the imports first, then the declarations that call them, and a local
14
+ * declaration always wins over anything the compiler would otherwise assume.
15
+ *
16
+ * Babel-free by the rule in no-babel-in-core: nodes are read structurally.
17
+ */
18
+ import { type Intrinsic, type ReactiveKind } from './intrinsics.js';
19
+ export interface ReactiveBindings {
20
+ /** Local name → what it holds. */
21
+ kinds: Map<string, ReactiveKind>;
22
+ /** Local name → the primitive it calls, for names imported from the runtime. */
23
+ primitives: Map<string, Intrinsic['returns']>;
24
+ /** `$name`s the module shadows with its own binding, so the intrinsic must not fire. */
25
+ shadowed: Set<string>;
26
+ }
27
+ /**
28
+ * Read a module's reactive bindings.
29
+ *
30
+ * Two passes, because a call can only be classified once its callee is known: imports
31
+ * and local declarations first, then the declarations that call them.
32
+ */
33
+ export declare function resolveReactiveBindings(program: unknown): ReactiveBindings;
34
+ //# sourceMappingURL=reactive-bindings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactive-bindings.d.ts","sourceRoot":"","sources":["../../src/analyze/reactive-bindings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,YAAY,EAClB,MAAM,iBAAiB,CAAC;AAOzB,MAAM,WAAW,gBAAgB;IAC/B,kCAAkC;IAClC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjC,gFAAgF;IAChF,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC;IAC9C,wFAAwF;IACxF,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB;AAsED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,CA6C1E"}