@fluixi/compiler 1.0.0-alpha.75 → 1.0.0-alpha.77
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/README.md +38 -2
- package/dist/analyze/intrinsics.cjs +1 -0
- package/dist/analyze/intrinsics.d.ts +67 -0
- package/dist/analyze/intrinsics.d.ts.map +1 -0
- package/dist/analyze/intrinsics.js +91 -0
- package/dist/analyze/intrinsics.mjs +1 -0
- package/dist/analyze/props-destructure.cjs +1 -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 +1 -0
- package/dist/analyze/reactive-bindings.cjs +1 -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 +1 -0
- package/dist/{babel-GXO3KAVI.mjs → babel-YQ42WUS3.mjs} +1 -1
- package/dist/chunk-545LMC5W.mjs +1 -0
- package/dist/{chunk-TIE4XNAM.mjs → chunk-L2QPV25P.mjs} +1 -1
- package/dist/frontend/babel/build-ir-lit.cjs +1 -1
- package/dist/frontend/babel/build-ir-lit.mjs +1 -1
- package/dist/frontend/babel/build-ir.cjs +1 -1
- package/dist/frontend/babel/build-ir.mjs +1 -1
- package/dist/frontend/babel/index.cjs +1 -1
- package/dist/frontend/babel/index.mjs +1 -1
- package/dist/frontend/babel/lower-template.cjs +1 -1
- package/dist/frontend/babel/lower-template.mjs +1 -1
- package/dist/frontend/babel/plugin.cjs +1 -1
- package/dist/frontend/babel/plugin.mjs +1 -1
- package/dist/index.cjs +8 -8
- package/dist/index.mjs +9 -9
- package/dist/integrations.cjs +19 -14
- package/dist/integrations.d.ts +13 -0
- package/dist/integrations.d.ts.map +1 -1
- package/dist/integrations.js +52 -2
- package/dist/integrations.mjs +11 -11
- package/dist/lower/compile-template.cjs +1 -1
- package/dist/lower/compile-template.mjs +1 -1
- package/dist/lower/template.cjs +1 -1
- package/dist/lower/template.d.ts.map +1 -1
- package/dist/lower/template.js +7 -5
- package/dist/lower/template.mjs +1 -1
- package/dist/resolve/component-globals.cjs +9 -9
- package/dist/resolve/component-globals.d.ts +8 -0
- package/dist/resolve/component-globals.d.ts.map +1 -1
- package/dist/resolve/component-globals.js +17 -3
- package/dist/resolve/component-globals.mjs +9 -9
- package/dist/resolve/write-globals.cjs +9 -9
- package/dist/resolve/write-globals.mjs +9 -9
- package/dist/transform/index.cjs +6 -1
- package/dist/transform/index.d.ts +4 -0
- package/dist/transform/index.d.ts.map +1 -1
- package/dist/transform/index.js +2 -0
- package/dist/transform/index.mjs +15 -10
- package/dist/transform/intrinsics.cjs +3 -0
- package/dist/transform/intrinsics.d.ts +22 -0
- package/dist/transform/intrinsics.d.ts.map +1 -0
- package/dist/transform/intrinsics.js +78 -0
- package/dist/transform/intrinsics.mjs +3 -0
- package/dist/transform/props.cjs +3 -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 +12 -0
- package/dist/transform/templates.cjs +5 -1
- package/dist/transform/templates.d.ts +36 -0
- package/dist/transform/templates.d.ts.map +1 -1
- package/dist/transform/templates.js +175 -7
- package/dist/transform/templates.mjs +14 -10
- package/dist/transform-ZGXUDFEZ.mjs +8 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/dist/transform-PJX2GNSV.mjs +0 -3
package/README.md
CHANGED
|
@@ -38,6 +38,42 @@ gets an RPC stub, the server build keeps and registers the body.
|
|
|
38
38
|
Most apps don't use this package directly — [`@fluixi/vite-plugin`](../vite-plugin) (and
|
|
39
39
|
[`@fluixi/start`](../start)) wrap it.
|
|
40
40
|
|
|
41
|
+
## What it does to your code
|
|
42
|
+
|
|
43
|
+
Beyond turning markup into DOM calls:
|
|
44
|
+
|
|
45
|
+
**Props you destructured stay reactive.** `function Card({ title })` compiles to
|
|
46
|
+
`props.title` at each use, so the binding keeps updating, and `...rest` becomes the
|
|
47
|
+
`splitProps` call you would have written. Anything it can't prove safe — a reassigned
|
|
48
|
+
binding, a computed key, a default it would have to re-run per read — is left exactly as
|
|
49
|
+
written and reported. On by default; set `propsDestructure: false` for a package that ships
|
|
50
|
+
its **source** for consumers to compile, since the rewrite belongs to whoever compiles.
|
|
51
|
+
|
|
52
|
+
**`$` intrinsics resolve without an import.** `$signal`, `$memo`, `$effect`, `$store`,
|
|
53
|
+
`$resource`, `$selector`, `$deferred`, `$untrack`, `$untrackStore` compile to the runtime
|
|
54
|
+
calls they stand for, and the import is added for what a module actually used. A primitive
|
|
55
|
+
is recognised by the binding it came from, never by name: `import { signal as state }` is
|
|
56
|
+
recognised, a local `function signal()` is not. A module that binds a `$` name keeps its
|
|
57
|
+
own meaning and gets a diagnostic.
|
|
58
|
+
|
|
59
|
+
**Unimported tags resolve through rules.** `resolve` says where a capitalised tag comes
|
|
60
|
+
from — an entry per component, or one pattern for a family:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
createVitePlugin({ resolve: [{ match: /^([A-Z]\w+)$/, module: '/src/components/$1.tsx' }] })
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**`load:` splits a component into its own chunk.** `load:visible`, `load:idle`,
|
|
67
|
+
`load:interaction`, `load:media` decide when its module is fetched. It works on a component
|
|
68
|
+
a rule supplies and on one you imported yourself — in the second case the static import is
|
|
69
|
+
removed, which only happens when the name is used nowhere but as a tag.
|
|
70
|
+
|
|
71
|
+
**It says when it couldn't do these things.** A tag nothing supplies and a `load:` that
|
|
72
|
+
can't apply both build cleanly and fail at runtime, so they're reported at build time with
|
|
73
|
+
the reason. The bundler adapters print them; `TransformResult` carries them as
|
|
74
|
+
`propsDiagnostics`, `intrinsicDiagnostics`, `loadDiagnostics` and `unresolvedDiagnostics`
|
|
75
|
+
if you're driving the transform yourself.
|
|
76
|
+
|
|
41
77
|
## 📦 Installation
|
|
42
78
|
|
|
43
79
|
```sh
|
|
@@ -68,8 +104,8 @@ import { transformTemplates } from '@fluixi/compiler/transform';
|
|
|
68
104
|
const { code, map } = transformTemplates(source, 'App.tsx', {})!;
|
|
69
105
|
```
|
|
70
106
|
|
|
71
|
-
`transformTemplates` returns `null` when
|
|
72
|
-
bundler can leave the file untouched.
|
|
107
|
+
`transformTemplates` returns `null` when there is nothing to do — no JSX, no templates, no
|
|
108
|
+
`$` intrinsics and nothing to report — so a bundler can leave the file untouched.
|
|
73
109
|
|
|
74
110
|
### Compiling through Babel
|
|
75
111
|
|
|
@@ -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 N=Object.prototype.hasOwnProperty;var I=(r,e)=>{for(var i in e)f(r,i,{get:e[i],enumerable:!0})},x=(r,e,i,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of y(e))!N.call(r,n)&&n!==i&&f(r,n,{get:()=>e[n],enumerable:!(t=g(e,n))||t.enumerable});return r};var v=r=>x(f({},"__esModule",{value:!0}),r);var b={};I(b,{declaredNames:()=>E,resolveReactiveBindings:()=>w});module.exports=v(b);var o="@fluixi/reactive/signal",l={$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"}},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"},R=["@fluixi/reactive","@fluixi/reactive/signal","@fluixi/reactive/signal-next","@fluixi/reactive/store","@fluixi/core","@fluixi/core/rx"],D=new RegExp(`\\$(?:${Object.keys(l).map(r=>r.slice(1)).join("|")})\\s*\\(`);function d(r){return R.includes(r)}function s(r,e){if(r)switch(r.type){case"Identifier":e.add(r.name);return;case"ObjectPattern":for(let i of r.properties??[])i.type==="RestElement"?s(i.argument,e):s(i.value,e);return;case"ArrayPattern":for(let i of r.elements??[])s(i,e);return;case"AssignmentPattern":s(r.left,e);return;case"RestElement":s(r.argument,e);return}}function c(r,e){if(!(!r||typeof r!="object"||typeof r.type!="string")){e(r);for(let i of Object.keys(r)){if(i==="loc"||i==="leadingComments"||i==="trailingComments")continue;let t=r[i];if(Array.isArray(t))for(let n of t)c(n,e);else t&&typeof t=="object"&&c(t,e)}}}function S(r,e){if(r.type!=="CallExpression")return;let i=r.callee;if(i?.type!=="Identifier")return;let t=i.name,n=e.primitives.get(t);if(n)return n;let a=l[t];if(a&&!e.shadowed.has(t))return a.returns}function h(r,e,i){if(Array.isArray(e)){r.type==="ArrayPattern"&&r.elements.forEach((t,n)=>{let a=e[n];t?.type==="Identifier"&&a&&i.set(t.name,a)});return}r.type==="Identifier"&&i.set(r.name,e)}function E(r){let e=new Set;return c(r,i=>{switch(i.type){case"VariableDeclarator":s(i.id,e);return;case"FunctionDeclaration":case"FunctionExpression":case"ArrowFunctionExpression":s(i.id,e);for(let t of i.params??[])s(t,e);return;case"ClassDeclaration":case"ClassExpression":s(i.id,e);return;case"ImportSpecifier":case"ImportDefaultSpecifier":case"ImportNamespaceSpecifier":s(i.local,e);return;case"CatchClause":s(i.param,e);return}}),e}function w(r){let e={kinds:new Map,primitives:new Map,shadowed:new Set},i=r;c(i,t=>{let n=new Set;if(t.type==="VariableDeclarator")s(t.id,n);else if(t.type==="FunctionDeclaration"||t.type==="ClassDeclaration")s(t.id,n);else if(t.type==="ImportDefaultSpecifier"||t.type==="ImportNamespaceSpecifier")s(t.local,n);else if(t.type==="ImportSpecifier")s(t.local,n);else if(t.type==="FunctionExpression"||t.type==="ArrowFunctionExpression"||t.type==="FunctionDeclaration")for(let a of t.params??[])s(a,n);for(let a of n)l[a]&&e.shadowed.add(a)});for(let t of i.body??[])if(t.type==="ImportDeclaration"&&d(t.source?.value))for(let n of t.specifiers??[]){if(n.type!=="ImportSpecifier")continue;let a=n.imported,m=a.type==="Identifier"?a.name:a.value,u=p[m];u&&e.primitives.set(n.local.name,u)}return c(i,t=>{if(t.type!=="VariableDeclarator"||!t.init)return;let n=S(t.init,e);n&&h(t.id,n,e.kinds)}),e}
|