@fluixi/compiler 1.0.0-alpha.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +131 -0
- package/dist/analyze/expr-shape.cjs +62 -0
- package/dist/analyze/expr-shape.d.cts +14 -0
- package/dist/analyze/expr-shape.d.ts +15 -0
- package/dist/analyze/expr-shape.d.ts.map +1 -0
- package/dist/analyze/expr-shape.js +37 -0
- package/dist/analyze/expr-shape.mjs +41 -0
- package/dist/analyze/intrinsics.cjs +112 -0
- package/dist/analyze/intrinsics.d.cts +80 -0
- package/dist/analyze/intrinsics.d.ts +81 -0
- package/dist/analyze/intrinsics.d.ts.map +1 -0
- package/dist/analyze/intrinsics.js +126 -0
- package/dist/analyze/intrinsics.mjs +91 -0
- package/dist/analyze/paren.cjs +134 -0
- package/dist/analyze/paren.d.cts +23 -0
- package/dist/analyze/paren.d.ts +24 -0
- package/dist/analyze/paren.d.ts.map +1 -0
- package/dist/analyze/paren.js +168 -0
- package/dist/analyze/paren.mjs +113 -0
- package/dist/analyze/props-destructure.cjs +226 -0
- package/dist/analyze/props-destructure.d.cts +83 -0
- package/dist/analyze/props-destructure.d.ts +84 -0
- package/dist/analyze/props-destructure.d.ts.map +1 -0
- package/dist/analyze/props-destructure.js +272 -0
- package/dist/analyze/props-destructure.mjs +205 -0
- package/dist/analyze/reactive-bindings.cjs +210 -0
- package/dist/analyze/reactive-bindings.d.cts +42 -0
- package/dist/analyze/reactive-bindings.d.ts +43 -0
- package/dist/analyze/reactive-bindings.d.ts.map +1 -0
- package/dist/analyze/reactive-bindings.js +191 -0
- package/dist/analyze/reactive-bindings.mjs +187 -0
- package/dist/analyze/reactive-expr.cjs +38 -0
- package/dist/analyze/reactive-expr.d.cts +38 -0
- package/dist/analyze/reactive-expr.d.ts +39 -0
- package/dist/analyze/reactive-expr.d.ts.map +1 -0
- package/dist/analyze/reactive-expr.js +31 -0
- package/dist/analyze/reactive-expr.mjs +17 -0
- package/dist/analyze/static-graph.cjs +831 -0
- package/dist/analyze/static-graph.d.cts +102 -0
- package/dist/analyze/static-graph.d.ts +103 -0
- package/dist/analyze/static-graph.d.ts.map +1 -0
- package/dist/analyze/static-graph.js +871 -0
- package/dist/analyze/static-graph.mjs +813 -0
- package/dist/babel-I6BUWUCU.mjs +636 -0
- package/dist/chunk-3AICWNF7.mjs +92 -0
- package/dist/chunk-6CWJWGE2.mjs +2170 -0
- package/dist/chunk-C3XPDB4Y.mjs +16 -0
- package/dist/chunk-GX5XV6Z7.mjs +190 -0
- package/dist/chunk-HXEXPWJ2.mjs +999 -0
- package/dist/chunk-NU3W67UZ.mjs +584 -0
- package/dist/chunk-SKAVY75W.mjs +11 -0
- package/dist/codegen/backend.cjs +18 -0
- package/dist/codegen/backend.d.cts +35 -0
- package/dist/codegen/backend.d.ts +36 -0
- package/dist/codegen/backend.d.ts.map +1 -0
- package/dist/codegen/backend.js +1 -0
- package/dist/codegen/backend.mjs +0 -0
- package/dist/codegen/backends/imperative.cjs +577 -0
- package/dist/codegen/backends/imperative.d.cts +11 -0
- package/dist/codegen/backends/imperative.d.ts +12 -0
- package/dist/codegen/backends/imperative.d.ts.map +1 -0
- package/dist/codegen/backends/imperative.js +362 -0
- package/dist/codegen/backends/imperative.mjs +554 -0
- package/dist/codegen/contract.cjs +52 -0
- package/dist/codegen/contract.d.cts +24 -0
- package/dist/codegen/contract.d.ts +25 -0
- package/dist/codegen/contract.d.ts.map +1 -0
- package/dist/codegen/contract.js +30 -0
- package/dist/codegen/contract.mjs +31 -0
- package/dist/codegen/partial-template.cjs +176 -0
- package/dist/codegen/partial-template.d.cts +61 -0
- package/dist/codegen/partial-template.d.ts +62 -0
- package/dist/codegen/partial-template.d.ts.map +1 -0
- package/dist/codegen/partial-template.js +0 -0
- package/dist/codegen/partial-template.mjs +153 -0
- package/dist/codegen/serialize-static.cjs +125 -0
- package/dist/codegen/serialize-static.d.cts +38 -0
- package/dist/codegen/serialize-static.d.ts +39 -0
- package/dist/codegen/serialize-static.d.ts.map +1 -0
- package/dist/codegen/serialize-static.js +165 -0
- package/dist/codegen/serialize-static.mjs +104 -0
- package/dist/frontend/babel/build-ir-lit.cjs +1933 -0
- package/dist/frontend/babel/build-ir-lit.d.cts +26 -0
- package/dist/frontend/babel/build-ir-lit.d.ts +27 -0
- package/dist/frontend/babel/build-ir-lit.d.ts.map +1 -0
- package/dist/frontend/babel/build-ir-lit.js +13 -0
- package/dist/frontend/babel/build-ir-lit.mjs +1902 -0
- package/dist/frontend/babel/build-ir.cjs +1948 -0
- package/dist/frontend/babel/build-ir.d.cts +35 -0
- package/dist/frontend/babel/build-ir.d.ts +36 -0
- package/dist/frontend/babel/build-ir.d.ts.map +1 -0
- package/dist/frontend/babel/build-ir.js +124 -0
- package/dist/frontend/babel/build-ir.mjs +1919 -0
- package/dist/frontend/babel/index.cjs +2610 -0
- package/dist/frontend/babel/index.d.cts +33 -0
- package/dist/frontend/babel/index.d.ts +34 -0
- package/dist/frontend/babel/index.d.ts.map +1 -0
- package/dist/frontend/babel/index.js +65 -0
- package/dist/frontend/babel/index.mjs +2580 -0
- package/dist/frontend/babel/lower-template.cjs +1931 -0
- package/dist/frontend/babel/lower-template.d.cts +23 -0
- package/dist/frontend/babel/lower-template.d.ts +24 -0
- package/dist/frontend/babel/lower-template.d.ts.map +1 -0
- package/dist/frontend/babel/lower-template.js +64 -0
- package/dist/frontend/babel/lower-template.mjs +1902 -0
- package/dist/frontend/babel/plugin.cjs +2556 -0
- package/dist/frontend/babel/plugin.d.cts +95 -0
- package/dist/frontend/babel/plugin.d.ts +96 -0
- package/dist/frontend/babel/plugin.d.ts.map +1 -0
- package/dist/frontend/babel/plugin.js +523 -0
- package/dist/frontend/babel/plugin.mjs +2527 -0
- package/dist/frontend/babel/server-functions.cjs +110 -0
- package/dist/frontend/babel/server-functions.d.cts +10 -0
- package/dist/frontend/babel/server-functions.d.ts +11 -0
- package/dist/frontend/babel/server-functions.d.ts.map +1 -0
- package/dist/frontend/babel/server-functions.js +81 -0
- package/dist/frontend/babel/server-functions.mjs +89 -0
- package/dist/frontend/babel/types.cjs +18 -0
- package/dist/frontend/babel/types.d.cts +33 -0
- package/dist/frontend/babel/types.d.ts +34 -0
- package/dist/frontend/babel/types.d.ts.map +1 -0
- package/dist/frontend/babel/types.js +1 -0
- package/dist/frontend/babel/types.mjs +0 -0
- package/dist/frontend/types.cjs +18 -0
- package/dist/frontend/types.d.cts +25 -0
- package/dist/frontend/types.d.ts +26 -0
- package/dist/frontend/types.d.ts.map +1 -0
- package/dist/frontend/types.js +1 -0
- package/dist/frontend/types.mjs +0 -0
- package/dist/frontend/vocabulary.cjs +98 -0
- package/dist/frontend/vocabulary.d.cts +79 -0
- package/dist/frontend/vocabulary.d.ts +80 -0
- package/dist/frontend/vocabulary.d.ts.map +1 -0
- package/dist/frontend/vocabulary.js +124 -0
- package/dist/frontend/vocabulary.mjs +77 -0
- package/dist/index.cjs +1957 -0
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.mjs +1949 -0
- package/dist/integrations.cjs +5705 -0
- package/dist/integrations.d.cts +215 -0
- package/dist/integrations.d.ts +216 -0
- package/dist/integrations.d.ts.map +1 -0
- package/dist/integrations.js +566 -0
- package/dist/integrations.mjs +810 -0
- package/dist/ir/nodes.cjs +18 -0
- package/dist/ir/nodes.d.cts +168 -0
- package/dist/ir/nodes.d.ts +169 -0
- package/dist/ir/nodes.d.ts.map +1 -0
- package/dist/ir/nodes.js +1 -0
- package/dist/ir/nodes.mjs +0 -0
- package/dist/lower/compile-template.cjs +1670 -0
- package/dist/lower/compile-template.d.cts +82 -0
- package/dist/lower/compile-template.d.ts +83 -0
- package/dist/lower/compile-template.d.ts.map +1 -0
- package/dist/lower/compile-template.js +79 -0
- package/dist/lower/compile-template.mjs +1662 -0
- package/dist/lower/entities.cjs +288 -0
- package/dist/lower/entities.d.cts +1 -0
- package/dist/lower/entities.d.ts +2 -0
- package/dist/lower/entities.d.ts.map +1 -0
- package/dist/lower/entities.js +54 -0
- package/dist/lower/entities.mjs +267 -0
- package/dist/lower/jsx.cjs +1101 -0
- package/dist/lower/jsx.d.cts +33 -0
- package/dist/lower/jsx.d.ts +34 -0
- package/dist/lower/jsx.d.ts.map +1 -0
- package/dist/lower/jsx.js +414 -0
- package/dist/lower/jsx.mjs +1078 -0
- package/dist/lower/template.cjs +1354 -0
- package/dist/lower/template.d.cts +53 -0
- package/dist/lower/template.d.ts +54 -0
- package/dist/lower/template.d.ts.map +1 -0
- package/dist/lower/template.js +542 -0
- package/dist/lower/template.mjs +1335 -0
- package/dist/lower/text.cjs +42 -0
- package/dist/lower/text.d.cts +10 -0
- package/dist/lower/text.d.ts +11 -0
- package/dist/lower/text.d.ts.map +1 -0
- package/dist/lower/text.js +31 -0
- package/dist/lower/text.mjs +21 -0
- package/dist/optimize/analyze-static.cjs +112 -0
- package/dist/optimize/analyze-static.d.cts +39 -0
- package/dist/optimize/analyze-static.d.ts +40 -0
- package/dist/optimize/analyze-static.d.ts.map +1 -0
- package/dist/optimize/analyze-static.js +131 -0
- package/dist/optimize/analyze-static.mjs +91 -0
- package/dist/optimize/collapse-strategies.cjs +90 -0
- package/dist/optimize/collapse-strategies.d.cts +30 -0
- package/dist/optimize/collapse-strategies.d.ts +31 -0
- package/dist/optimize/collapse-strategies.d.ts.map +1 -0
- package/dist/optimize/collapse-strategies.js +33 -0
- package/dist/optimize/collapse-strategies.mjs +67 -0
- package/dist/options.cjs +33 -0
- package/dist/options.d.cts +39 -0
- package/dist/options.d.ts +40 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +8 -0
- package/dist/options.mjs +12 -0
- package/dist/resolve/builtins.cjs +73 -0
- package/dist/resolve/builtins.d.cts +68 -0
- package/dist/resolve/builtins.d.ts +69 -0
- package/dist/resolve/builtins.d.ts.map +1 -0
- package/dist/resolve/builtins.js +64 -0
- package/dist/resolve/builtins.mjs +53 -0
- package/dist/resolve/component-globals.cjs +272 -0
- package/dist/resolve/component-globals.d.cts +88 -0
- package/dist/resolve/component-globals.d.ts +89 -0
- package/dist/resolve/component-globals.d.ts.map +1 -0
- package/dist/resolve/component-globals.js +160 -0
- package/dist/resolve/component-globals.mjs +249 -0
- package/dist/resolve/component-resolver.cjs +80 -0
- package/dist/resolve/component-resolver.d.cts +68 -0
- package/dist/resolve/component-resolver.d.ts +69 -0
- package/dist/resolve/component-resolver.d.ts.map +1 -0
- package/dist/resolve/component-resolver.js +95 -0
- package/dist/resolve/component-resolver.mjs +59 -0
- package/dist/resolve/load-directive.cjs +93 -0
- package/dist/resolve/load-directive.d.cts +53 -0
- package/dist/resolve/load-directive.d.ts +54 -0
- package/dist/resolve/load-directive.d.ts.map +1 -0
- package/dist/resolve/load-directive.js +93 -0
- package/dist/resolve/load-directive.mjs +72 -0
- package/dist/resolve/resolve-ir.cjs +108 -0
- package/dist/resolve/resolve-ir.d.cts +53 -0
- package/dist/resolve/resolve-ir.d.ts +54 -0
- package/dist/resolve/resolve-ir.d.ts.map +1 -0
- package/dist/resolve/resolve-ir.js +58 -0
- package/dist/resolve/resolve-ir.mjs +85 -0
- package/dist/resolve/write-globals.cjs +383 -0
- package/dist/resolve/write-globals.d.cts +36 -0
- package/dist/resolve/write-globals.d.ts +37 -0
- package/dist/resolve/write-globals.d.ts.map +1 -0
- package/dist/resolve/write-globals.js +169 -0
- package/dist/resolve/write-globals.mjs +362 -0
- package/dist/server-fns-VZLHMQTC.mjs +79 -0
- package/dist/server-functions-UAZKOLZW.mjs +82 -0
- package/dist/transform/bindings.cjs +71 -0
- package/dist/transform/bindings.d.cts +16 -0
- package/dist/transform/bindings.d.ts +17 -0
- package/dist/transform/bindings.d.ts.map +1 -0
- package/dist/transform/bindings.js +69 -0
- package/dist/transform/bindings.mjs +50 -0
- package/dist/transform/index.cjs +3837 -0
- package/dist/transform/index.d.cts +15 -0
- package/dist/transform/index.d.ts +16 -0
- package/dist/transform/index.d.ts.map +1 -0
- package/dist/transform/index.js +12 -0
- package/dist/transform/index.mjs +4882 -0
- package/dist/transform/intrinsics.cjs +261 -0
- package/dist/transform/intrinsics.d.cts +33 -0
- package/dist/transform/intrinsics.d.ts +34 -0
- package/dist/transform/intrinsics.d.ts.map +1 -0
- package/dist/transform/intrinsics.js +120 -0
- package/dist/transform/intrinsics.mjs +238 -0
- package/dist/transform/props.cjs +441 -0
- package/dist/transform/props.d.cts +45 -0
- package/dist/transform/props.d.ts +46 -0
- package/dist/transform/props.d.ts.map +1 -0
- package/dist/transform/props.js +221 -0
- package/dist/transform/props.mjs +1487 -0
- package/dist/transform/server-fn-id.cjs +35 -0
- package/dist/transform/server-fn-id.d.cts +9 -0
- package/dist/transform/server-fn-id.d.ts +10 -0
- package/dist/transform/server-fn-id.d.ts.map +1 -0
- package/dist/transform/server-fn-id.js +18 -0
- package/dist/transform/server-fn-id.mjs +14 -0
- package/dist/transform/server-fns.cjs +114 -0
- package/dist/transform/server-fns.d.cts +12 -0
- package/dist/transform/server-fns.d.ts +13 -0
- package/dist/transform/server-fns.d.ts.map +1 -0
- package/dist/transform/server-fns.js +94 -0
- package/dist/transform/server-fns.mjs +1160 -0
- package/dist/transform/source-locations.cjs +393 -0
- package/dist/transform/source-locations.d.cts +48 -0
- package/dist/transform/source-locations.d.ts +49 -0
- package/dist/transform/source-locations.d.ts.map +1 -0
- package/dist/transform/source-locations.js +303 -0
- package/dist/transform/source-locations.mjs +370 -0
- package/dist/transform/styles.cjs +606 -0
- package/dist/transform/styles.d.cts +237 -0
- package/dist/transform/styles.d.ts +238 -0
- package/dist/transform/styles.d.ts.map +1 -0
- package/dist/transform/styles.js +743 -0
- package/dist/transform/styles.mjs +671 -0
- package/dist/transform/templates.cjs +3806 -0
- package/dist/transform/templates.d.cts +82 -0
- package/dist/transform/templates.d.ts +83 -0
- package/dist/transform/templates.d.ts.map +1 -0
- package/dist/transform/templates.js +613 -0
- package/dist/transform/templates.mjs +4852 -0
- package/dist/transform-3W45L3EA.mjs +951 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/dist/vite-styles.cjs +223 -0
- package/dist/vite-styles.d.cts +68 -0
- package/dist/vite-styles.d.ts +69 -0
- package/dist/vite-styles.d.ts.map +1 -0
- package/dist/vite-styles.js +241 -0
- package/dist/vite-styles.mjs +214 -0
- package/package.json +141 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/analyze/props-destructure.ts
|
|
21
|
+
var props_destructure_exports = {};
|
|
22
|
+
__export(props_destructure_exports, {
|
|
23
|
+
explainBail: () => explainBail,
|
|
24
|
+
planPropsDestructure: () => planPropsDestructure
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(props_destructure_exports);
|
|
27
|
+
function patternNames(node, out) {
|
|
28
|
+
if (!node) return;
|
|
29
|
+
switch (node.type) {
|
|
30
|
+
case "Identifier":
|
|
31
|
+
out.add(node.name);
|
|
32
|
+
return;
|
|
33
|
+
case "ObjectPattern":
|
|
34
|
+
for (const p of node.properties) {
|
|
35
|
+
if (p.type === "RestElement") patternNames(p.argument, out);
|
|
36
|
+
else patternNames(p.value, out);
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
case "ArrayPattern":
|
|
40
|
+
for (const e of node.elements) patternNames(e, out);
|
|
41
|
+
return;
|
|
42
|
+
case "AssignmentPattern":
|
|
43
|
+
patternNames(node.left, out);
|
|
44
|
+
return;
|
|
45
|
+
case "RestElement":
|
|
46
|
+
patternNames(node.argument, out);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function isSafeDefault(node) {
|
|
51
|
+
switch (node.type) {
|
|
52
|
+
case "StringLiteral":
|
|
53
|
+
case "NumericLiteral":
|
|
54
|
+
case "BooleanLiteral":
|
|
55
|
+
case "NullLiteral":
|
|
56
|
+
case "BigIntLiteral":
|
|
57
|
+
case "RegExpLiteral":
|
|
58
|
+
case "Identifier":
|
|
59
|
+
return true;
|
|
60
|
+
// ESTree spells every literal as `Literal`; both shapes reach this analysis.
|
|
61
|
+
case "Literal":
|
|
62
|
+
return true;
|
|
63
|
+
case "TemplateLiteral":
|
|
64
|
+
return node.expressions.every(isSafeDefault);
|
|
65
|
+
case "UnaryExpression":
|
|
66
|
+
return isSafeDefault(node.argument);
|
|
67
|
+
case "MemberExpression":
|
|
68
|
+
return false;
|
|
69
|
+
default:
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function walk(root, visit) {
|
|
74
|
+
if (!root || typeof root !== "object") return;
|
|
75
|
+
visit(root);
|
|
76
|
+
for (const key of Object.keys(root)) {
|
|
77
|
+
if (key === "loc" || key === "range" || key === "leadingComments" || key === "trailingComments") continue;
|
|
78
|
+
const value = root[key];
|
|
79
|
+
if (Array.isArray(value)) {
|
|
80
|
+
for (const item of value) if (item && typeof item === "object") walk(item, visit);
|
|
81
|
+
} else if (value && typeof value === "object" && typeof value.type === "string") {
|
|
82
|
+
walk(value, visit);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function reassignedNames(body) {
|
|
87
|
+
const written = /* @__PURE__ */ new Set();
|
|
88
|
+
walk(body, (node) => {
|
|
89
|
+
if (node.type === "AssignmentExpression") patternNames(node.left, written);
|
|
90
|
+
else if (node.type === "UpdateExpression") {
|
|
91
|
+
const arg = node.argument;
|
|
92
|
+
if (arg?.type === "Identifier") written.add(arg.name);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
return written;
|
|
96
|
+
}
|
|
97
|
+
function shadowedNames(body, candidates) {
|
|
98
|
+
const shadowed = /* @__PURE__ */ new Set();
|
|
99
|
+
const note = (node) => {
|
|
100
|
+
const names = /* @__PURE__ */ new Set();
|
|
101
|
+
patternNames(node, names);
|
|
102
|
+
for (const n of names) if (candidates.has(n)) shadowed.add(n);
|
|
103
|
+
};
|
|
104
|
+
walk(body, (node) => {
|
|
105
|
+
switch (node.type) {
|
|
106
|
+
case "VariableDeclarator":
|
|
107
|
+
note(node.id);
|
|
108
|
+
return;
|
|
109
|
+
case "FunctionDeclaration":
|
|
110
|
+
case "FunctionExpression":
|
|
111
|
+
case "ArrowFunctionExpression":
|
|
112
|
+
note(node.id);
|
|
113
|
+
for (const p of node.params ?? []) note(p);
|
|
114
|
+
return;
|
|
115
|
+
case "CatchClause":
|
|
116
|
+
note(node.param);
|
|
117
|
+
return;
|
|
118
|
+
case "ClassDeclaration":
|
|
119
|
+
case "ClassExpression":
|
|
120
|
+
note(node.id);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
return shadowed;
|
|
125
|
+
}
|
|
126
|
+
function collect(pattern, path, plan) {
|
|
127
|
+
for (const property of pattern.properties ?? []) {
|
|
128
|
+
if (property.type === "RestElement") {
|
|
129
|
+
const argument = property.argument;
|
|
130
|
+
if (argument?.type !== "Identifier" || path.length > 0) {
|
|
131
|
+
const names2 = /* @__PURE__ */ new Set();
|
|
132
|
+
patternNames(argument, names2);
|
|
133
|
+
for (const name of names2) plan.bails.push({ name, reason: "rest" });
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
plan.rest = { name: argument.name, keys: [] };
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (property.computed) {
|
|
140
|
+
const names2 = /* @__PURE__ */ new Set();
|
|
141
|
+
patternNames(property.value, names2);
|
|
142
|
+
for (const name of names2) plan.bails.push({ name, reason: "computed" });
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const key = property.key;
|
|
146
|
+
const propertyName = key.type === "Identifier" ? key.name : key.value;
|
|
147
|
+
let value = property.value;
|
|
148
|
+
let fallback;
|
|
149
|
+
if (value.type === "AssignmentPattern") {
|
|
150
|
+
fallback = value.right;
|
|
151
|
+
value = value.left;
|
|
152
|
+
}
|
|
153
|
+
const here = [...path, propertyName];
|
|
154
|
+
if (value.type === "Identifier") {
|
|
155
|
+
if (fallback && !isSafeDefault(fallback)) {
|
|
156
|
+
plan.bails.push({ name: value.name, reason: "unsafe-default" });
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
plan.reads.push({ name: value.name, path: here, ...fallback ? { fallback } : {} });
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (value.type === "ObjectPattern") {
|
|
163
|
+
if (fallback) {
|
|
164
|
+
const names2 = /* @__PURE__ */ new Set();
|
|
165
|
+
patternNames(value, names2);
|
|
166
|
+
for (const name of names2) plan.bails.push({ name, reason: "unsafe-default" });
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
collect(value, here, plan);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const names = /* @__PURE__ */ new Set();
|
|
173
|
+
patternNames(value, names);
|
|
174
|
+
for (const name of names) plan.bails.push({ name, reason: "computed" });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function planPropsDestructure(pattern, body) {
|
|
178
|
+
const plan = { reads: [], bails: [] };
|
|
179
|
+
if (pattern?.type !== "ObjectPattern") return plan;
|
|
180
|
+
collect(pattern, [], plan);
|
|
181
|
+
if (plan.rest) {
|
|
182
|
+
if (body?.type !== "BlockStatement") {
|
|
183
|
+
plan.bails.push({ name: plan.rest.name, reason: "rest" });
|
|
184
|
+
delete plan.rest;
|
|
185
|
+
} else {
|
|
186
|
+
plan.rest.keys = (pattern.properties ?? []).filter((p) => p.type !== "RestElement" && !p.computed).map((p) => {
|
|
187
|
+
const key = p.key;
|
|
188
|
+
return key.type === "Identifier" ? key.name : key.value;
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (plan.reads.length === 0 && !plan.rest) return plan;
|
|
193
|
+
const candidates = new Set(plan.reads.map((r) => r.name));
|
|
194
|
+
if (plan.rest) candidates.add(plan.rest.name);
|
|
195
|
+
const written = reassignedNames(body);
|
|
196
|
+
const shadowed = shadowedNames(body, candidates);
|
|
197
|
+
const kept = [];
|
|
198
|
+
for (const read of plan.reads) {
|
|
199
|
+
if (written.has(read.name)) plan.bails.push({ name: read.name, reason: "reassigned" });
|
|
200
|
+
else if (shadowed.has(read.name)) plan.bails.push({ name: read.name, reason: "shadowed" });
|
|
201
|
+
else kept.push(read);
|
|
202
|
+
}
|
|
203
|
+
plan.reads = kept;
|
|
204
|
+
if (plan.rest && written.has(plan.rest.name)) {
|
|
205
|
+
plan.bails.push({ name: plan.rest.name, reason: "reassigned" });
|
|
206
|
+
delete plan.rest;
|
|
207
|
+
} else if (plan.rest && shadowed.has(plan.rest.name)) {
|
|
208
|
+
plan.bails.push({ name: plan.rest.name, reason: "shadowed" });
|
|
209
|
+
delete plan.rest;
|
|
210
|
+
}
|
|
211
|
+
return plan;
|
|
212
|
+
}
|
|
213
|
+
function explainBail(reason) {
|
|
214
|
+
switch (reason) {
|
|
215
|
+
case "rest":
|
|
216
|
+
return "this rest element cannot be served by splitProps, and copying the props into a plain object would lose the getters";
|
|
217
|
+
case "computed":
|
|
218
|
+
return "the property is not known until it runs";
|
|
219
|
+
case "reassigned":
|
|
220
|
+
return "the binding is assigned to, and props are read-only";
|
|
221
|
+
case "shadowed":
|
|
222
|
+
return "an inner scope binds the same name";
|
|
223
|
+
case "unsafe-default":
|
|
224
|
+
return "the default would have to run again on every read";
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
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 {};
|
|
@@ -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,8EAA8E;GAC5E,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,0FAA0F;IAC1F,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
|
+
}
|