@fluixi/compiler 1.0.0-alpha.91 → 1.0.0-alpha.93
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/dist/analyze/intrinsics.cjs +21 -1
- package/dist/analyze/intrinsics.d.ts.map +1 -1
- package/dist/analyze/intrinsics.js +20 -0
- package/dist/analyze/intrinsics.mjs +21 -1
- package/dist/analyze/reactive-bindings.cjs +21 -1
- package/dist/analyze/reactive-bindings.mjs +21 -1
- package/dist/analyze/static-graph.cjs +25 -5
- package/dist/analyze/static-graph.d.ts.map +1 -1
- package/dist/analyze/static-graph.js +6 -4
- package/dist/analyze/static-graph.mjs +25 -5
- package/dist/{babel-VBLQRHQ2.mjs → babel-RJHJQVEM.mjs} +4 -4
- package/dist/{chunk-D53J7ICY.mjs → chunk-56QSOVVL.mjs} +1 -1
- package/dist/{chunk-S4J4OCWK.mjs → chunk-OD53TMER.mjs} +22 -2
- package/dist/{chunk-TWDJUXWZ.mjs → chunk-R7PPFBCL.mjs} +1 -1
- package/dist/{chunk-QX6Y3OAR.mjs → chunk-TJIH7TLX.mjs} +1 -1
- package/dist/{chunk-3JWMCK4A.mjs → chunk-U7HCFLZ2.mjs} +153 -48
- package/dist/codegen/backends/imperative.cjs +27 -7
- package/dist/codegen/backends/imperative.d.ts.map +1 -1
- package/dist/codegen/backends/imperative.js +19 -4
- package/dist/codegen/backends/imperative.mjs +27 -7
- package/dist/frontend/babel/build-ir-lit.cjs +123 -44
- package/dist/frontend/babel/build-ir-lit.mjs +123 -44
- package/dist/frontend/babel/build-ir.cjs +123 -44
- package/dist/frontend/babel/build-ir.mjs +123 -44
- package/dist/frontend/babel/index.cjs +124 -45
- package/dist/frontend/babel/index.mjs +124 -45
- package/dist/frontend/babel/lower-template.cjs +123 -44
- package/dist/frontend/babel/lower-template.mjs +123 -44
- package/dist/frontend/babel/plugin.cjs +123 -44
- package/dist/frontend/babel/plugin.mjs +123 -44
- package/dist/index.cjs +86 -46
- package/dist/index.mjs +88 -48
- package/dist/integrations.cjs +246 -61
- package/dist/integrations.mjs +53 -19
- package/dist/lower/compile-template.cjs +44 -39
- package/dist/lower/compile-template.mjs +44 -39
- package/dist/lower/jsx.cjs +509 -6
- package/dist/lower/jsx.d.ts.map +1 -1
- package/dist/lower/jsx.js +114 -6
- package/dist/lower/jsx.mjs +509 -6
- package/dist/lower/template.cjs +43 -38
- package/dist/lower/template.d.ts.map +1 -1
- package/dist/lower/template.js +22 -30
- package/dist/lower/template.mjs +43 -38
- package/dist/resolve/builtins.cjs +1 -1
- package/dist/resolve/builtins.mjs +1 -1
- package/dist/resolve/component-globals.cjs +33 -8
- package/dist/resolve/component-globals.d.cts +8 -0
- 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 +21 -12
- package/dist/resolve/component-globals.mjs +33 -8
- package/dist/resolve/write-globals.cjs +59 -8
- package/dist/resolve/write-globals.d.cts +6 -0
- package/dist/resolve/write-globals.d.ts +6 -0
- package/dist/resolve/write-globals.d.ts.map +1 -1
- package/dist/resolve/write-globals.js +35 -2
- package/dist/resolve/write-globals.mjs +61 -10
- package/dist/{server-fns-J56G3PFP.mjs → server-fns-ZFRDIQ2X.mjs} +4 -4
- package/dist/{server-functions-7PUFLRGF.mjs → server-functions-CTDXX67X.mjs} +3 -3
- package/dist/transform/index.cjs +199 -49
- package/dist/transform/index.mjs +199 -49
- package/dist/transform/intrinsics.cjs +47 -2
- package/dist/transform/intrinsics.d.cts +12 -0
- package/dist/transform/intrinsics.d.ts +12 -0
- package/dist/transform/intrinsics.d.ts.map +1 -1
- package/dist/transform/intrinsics.js +43 -1
- package/dist/transform/intrinsics.mjs +47 -2
- package/dist/transform/source-locations.cjs +67 -4
- package/dist/transform/source-locations.d.cts +7 -2
- package/dist/transform/source-locations.d.ts +7 -2
- package/dist/transform/source-locations.d.ts.map +1 -1
- package/dist/transform/source-locations.js +68 -3
- package/dist/transform/source-locations.mjs +67 -4
- package/dist/transform/templates.cjs +198 -48
- package/dist/transform/templates.d.ts.map +1 -1
- package/dist/transform/templates.js +5 -3
- package/dist/transform/templates.mjs +198 -48
- package/dist/{transform-D536WXGM.mjs → transform-QWWQDCXE.mjs} +33 -8
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +9 -3
package/dist/transform/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @fluixi/compiler v1.0.0-alpha.
|
|
1
|
+
/*! @fluixi/compiler v1.0.0-alpha.93 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
2
|
|
|
3
3
|
// src/transform/templates.ts
|
|
4
4
|
import { parse as parse2 } from "@babel/parser";
|
|
@@ -1521,6 +1521,7 @@ var INTRINSICS = {
|
|
|
1521
1521
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
1522
1522
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
1523
1523
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
1524
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
1524
1525
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
1525
1526
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
1526
1527
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -1530,7 +1531,22 @@ var INTRINSICS = {
|
|
|
1530
1531
|
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
1531
1532
|
// dependency analysis must not guess at.
|
|
1532
1533
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
1533
|
-
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
1534
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
1535
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
1536
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
1537
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
1538
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
1539
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
1540
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
1541
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
1542
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
1543
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
1544
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
1545
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
1546
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
1547
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
1548
|
+
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
1549
|
+
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
1534
1550
|
};
|
|
1535
1551
|
var RUNTIME_PRIMITIVES = {
|
|
1536
1552
|
signal: "signal-handle",
|
|
@@ -1541,8 +1557,11 @@ var RUNTIME_PRIMITIVES = {
|
|
|
1541
1557
|
createMemo: "memo-accessor",
|
|
1542
1558
|
createEffect: "effect",
|
|
1543
1559
|
createRenderEffect: "effect",
|
|
1560
|
+
renderEffect: "effect",
|
|
1561
|
+
computed: "effect",
|
|
1544
1562
|
createStore: "reactive-object",
|
|
1545
1563
|
resource: "resource-handle",
|
|
1564
|
+
cachedResource: "resource-handle",
|
|
1546
1565
|
createResource: "reactive-object",
|
|
1547
1566
|
createSelector: "memo-accessor",
|
|
1548
1567
|
createDeferred: "memo-accessor"
|
|
@@ -1551,6 +1570,7 @@ var PRIMITIVE_MODULES = [
|
|
|
1551
1570
|
"@fluixi/reactive",
|
|
1552
1571
|
"@fluixi/reactive/signal",
|
|
1553
1572
|
"@fluixi/reactive/store",
|
|
1573
|
+
"@fluixi/reactive/cached-resource",
|
|
1554
1574
|
"@fluixi/core"
|
|
1555
1575
|
];
|
|
1556
1576
|
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n2) => n2.slice(1)).join("|")})\\s*\\(`);
|
|
@@ -1686,6 +1706,7 @@ function resolveReactiveBindings(program) {
|
|
|
1686
1706
|
// src/transform/source-locations.ts
|
|
1687
1707
|
var SOURCE_MARKER = "__fx_source";
|
|
1688
1708
|
var COMPONENT_MARKER = "__fx_component";
|
|
1709
|
+
var CLASS_MARKER = "__fx_class";
|
|
1689
1710
|
var HOLE_MARKER = "__fx_hole";
|
|
1690
1711
|
var markHole = (accessor, line, column, name) => `(globalThis.${HOLE_MARKER} ?? ((f) => f))(${accessor},${line},${column}${name === void 0 ? "" : `,${JSON.stringify(name)}`})`;
|
|
1691
1712
|
var PROPS_MARKER = "__fx_props";
|
|
@@ -1703,11 +1724,12 @@ var RUNTIME_PRIMITIVES2 = /* @__PURE__ */ new Set([
|
|
|
1703
1724
|
"createStore",
|
|
1704
1725
|
"createResource"
|
|
1705
1726
|
]);
|
|
1706
|
-
function
|
|
1727
|
+
function mayHaveSourceMarks(code) {
|
|
1707
1728
|
for (const name of RUNTIME_PRIMITIVES2) {
|
|
1708
1729
|
if (code.includes(name)) return true;
|
|
1709
1730
|
}
|
|
1710
|
-
|
|
1731
|
+
if (code.includes("$signal") || code.includes("$memo") || code.includes("$effect") || code.includes("$store")) return true;
|
|
1732
|
+
return /\bclass\b[^{;]*\bextends\b/.test(code);
|
|
1711
1733
|
}
|
|
1712
1734
|
var SINGLE_STATEMENT_PARENTS = /* @__PURE__ */ new Set([
|
|
1713
1735
|
"IfStatement",
|
|
@@ -1726,6 +1748,7 @@ function isLoneBranch(statement, parent) {
|
|
|
1726
1748
|
function isExport(node) {
|
|
1727
1749
|
return node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration";
|
|
1728
1750
|
}
|
|
1751
|
+
var CLASS_FIELDS = /* @__PURE__ */ new Set(["PropertyDefinition", "ClassProperty", "ClassPrivateProperty", "AccessorProperty"]);
|
|
1729
1752
|
function isStatement(node) {
|
|
1730
1753
|
return node.type.endsWith("Statement") || node.type === "VariableDeclaration";
|
|
1731
1754
|
}
|
|
@@ -1746,6 +1769,14 @@ function walk3(node, visit, stack = []) {
|
|
|
1746
1769
|
}
|
|
1747
1770
|
function nameFor(stack) {
|
|
1748
1771
|
const declarator = stack[stack.length - 1];
|
|
1772
|
+
if (declarator && CLASS_FIELDS.has(declarator.type)) {
|
|
1773
|
+
const key = declarator.key;
|
|
1774
|
+
if (key?.type === "Identifier" || key?.type === "PrivateIdentifier" || key?.type === "PrivateName") {
|
|
1775
|
+
const name = key.name ?? key.id?.name;
|
|
1776
|
+
return name;
|
|
1777
|
+
}
|
|
1778
|
+
return void 0;
|
|
1779
|
+
}
|
|
1749
1780
|
if (declarator?.type !== "VariableDeclarator") return void 0;
|
|
1750
1781
|
const id = declarator.id;
|
|
1751
1782
|
if (id?.type === "Identifier") return id.name;
|
|
@@ -1794,6 +1825,17 @@ function collectSourceEdits(program, filename) {
|
|
|
1794
1825
|
});
|
|
1795
1826
|
}
|
|
1796
1827
|
}
|
|
1828
|
+
if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.superClass && !node.declare) {
|
|
1829
|
+
const at = node.loc?.start;
|
|
1830
|
+
const body = node.body;
|
|
1831
|
+
if (at && body) {
|
|
1832
|
+
edits.push({
|
|
1833
|
+
start: body.start + 1,
|
|
1834
|
+
end: body.start + 1,
|
|
1835
|
+
code: `static{globalThis.${CLASS_MARKER}?.(this,${JSON.stringify(filename)},${at.line},${at.column});}`
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1797
1839
|
if (node.type !== "CallExpression") return;
|
|
1798
1840
|
const callee = node.callee;
|
|
1799
1841
|
if (callee?.type !== "Identifier") return;
|
|
@@ -1803,6 +1845,26 @@ function collectSourceEdits(program, filename) {
|
|
|
1803
1845
|
if (!intrinsic && !imported) return;
|
|
1804
1846
|
const position = node.loc?.start;
|
|
1805
1847
|
if (!position) return;
|
|
1848
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
1849
|
+
const up = stack[i];
|
|
1850
|
+
if (isStatement(up)) break;
|
|
1851
|
+
if (CLASS_FIELDS.has(up.type)) {
|
|
1852
|
+
if (claimed.has(node.start)) return;
|
|
1853
|
+
claimed.add(node.start);
|
|
1854
|
+
const label2 = nameFor(stack);
|
|
1855
|
+
const own = !up.static;
|
|
1856
|
+
const args2 = [
|
|
1857
|
+
JSON.stringify(filename),
|
|
1858
|
+
String(position.line),
|
|
1859
|
+
String(position.column),
|
|
1860
|
+
label2 ? JSON.stringify(label2) : own ? "undefined" : void 0,
|
|
1861
|
+
own ? "this" : void 0
|
|
1862
|
+
].filter((a) => a !== void 0).join(",");
|
|
1863
|
+
edits.push({ start: node.start, end: node.start, code: `(globalThis.${SOURCE_MARKER}?.(${args2}),` });
|
|
1864
|
+
edits.push({ start: node.end, end: node.end, code: ")" });
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1806
1868
|
let statement;
|
|
1807
1869
|
let parent;
|
|
1808
1870
|
let index = -1;
|
|
@@ -2008,13 +2070,16 @@ function emitComponent(name, props, children, used, templates, at) {
|
|
|
2008
2070
|
used.add("createComponent");
|
|
2009
2071
|
const spreads = props.filter((p) => p.kind === "spread");
|
|
2010
2072
|
const normal = props.filter((p) => p.kind !== "spread");
|
|
2011
|
-
const
|
|
2012
|
-
(p) => `get ${propKey(p.name)}() { return ${componentPropValue(p)}; }`
|
|
2013
|
-
);
|
|
2073
|
+
const bodies = normal.map((p) => [p.name, componentPropValue(p)]);
|
|
2014
2074
|
if (children.length > 0) {
|
|
2015
|
-
|
|
2075
|
+
bodies.push(["children", emitChildren(children, used, templates)]);
|
|
2076
|
+
}
|
|
2077
|
+
let obj = `{ ${bodies.map(([name2, body]) => `get ${propKey(name2)}() { return ${body}; }`).join(", ")} }`;
|
|
2078
|
+
if (bodies.some(([, body]) => /\bthis\b/.test(body))) {
|
|
2079
|
+
const thunks = bodies.map(([name2, body]) => `${JSON.stringify(name2)}: () => ${body}`);
|
|
2080
|
+
const getters = bodies.map(([name2]) => `get ${propKey(name2)}() { return _$t[${JSON.stringify(name2)}](); }`);
|
|
2081
|
+
obj = `((_$t) => ({ ${getters.join(", ")} }))({ ${thunks.join(", ")} })`;
|
|
2016
2082
|
}
|
|
2017
|
-
let obj = `{ ${entries.join(", ")} }`;
|
|
2018
2083
|
if (spreads.length > 0) {
|
|
2019
2084
|
used.add("mergeProps");
|
|
2020
2085
|
obj = `mergeProps(${spreads.map((s) => s.expr).join(", ")}, ${obj})`;
|
|
@@ -2096,7 +2161,8 @@ function emitNode(node, used, templates) {
|
|
|
2096
2161
|
} else {
|
|
2097
2162
|
used.add("spread");
|
|
2098
2163
|
const svgFlag = node.svg ? ", isSVG: true" : "";
|
|
2099
|
-
const
|
|
2164
|
+
const accessors = node.props.filter((p) => p.kind !== "spread" && p.expr !== void 0 && p.reactive && p.name !== "ref").map((p) => p.name);
|
|
2165
|
+
const customFlag = node.tag.includes("-") ? `, isCustomElement: true, accessors: ${JSON.stringify(accessors)}` : "";
|
|
2100
2166
|
parts.push(
|
|
2101
2167
|
`spread({ element: ${el}, props: ${emitProps(node.props, null, used)}${svgFlag}${customFlag} });`
|
|
2102
2168
|
);
|
|
@@ -2229,6 +2295,7 @@ var DELEGATED = /* @__PURE__ */ new Set([
|
|
|
2229
2295
|
function escapeTemplate(s) {
|
|
2230
2296
|
return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
2231
2297
|
}
|
|
2298
|
+
var CONTAINER = "container";
|
|
2232
2299
|
function capitalize(s) {
|
|
2233
2300
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
2234
2301
|
}
|
|
@@ -2283,10 +2350,6 @@ var Lowerer = class {
|
|
|
2283
2350
|
if (hasElse) i = elseIdx;
|
|
2284
2351
|
continue;
|
|
2285
2352
|
}
|
|
2286
|
-
if (child.attributes.some((a) => a.kind === "EachDirective")) {
|
|
2287
|
-
out.push(this.lowerEach(child));
|
|
2288
|
-
continue;
|
|
2289
|
-
}
|
|
2290
2353
|
}
|
|
2291
2354
|
const node = this.lowerNode(child);
|
|
2292
2355
|
if (node) out.push(node);
|
|
@@ -2328,13 +2391,16 @@ var Lowerer = class {
|
|
|
2328
2391
|
node.props.unshift({ name: "component", kind: "attr", expr: `() => ${paren(this.hole(isAttr.value.hole).code, "arrowBody")}` });
|
|
2329
2392
|
return node;
|
|
2330
2393
|
}
|
|
2394
|
+
const each = this.lowerEach(el);
|
|
2395
|
+
const children = each ? [each] : this.lowerChildren(el.children);
|
|
2396
|
+
if (el.tag === CONTAINER) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
2331
2397
|
const at = this.tagAt(el);
|
|
2332
2398
|
return {
|
|
2333
2399
|
kind: "element",
|
|
2334
2400
|
tag: el.tag,
|
|
2335
2401
|
svg: el.namespace === "svg",
|
|
2336
2402
|
props: this.lowerAttributes(el.attributes),
|
|
2337
|
-
children
|
|
2403
|
+
children,
|
|
2338
2404
|
static: false,
|
|
2339
2405
|
...at ? { at } : {}
|
|
2340
2406
|
};
|
|
@@ -2406,10 +2472,14 @@ var Lowerer = class {
|
|
|
2406
2472
|
const at = this.tagAt(el);
|
|
2407
2473
|
return { kind: "component", name: "Show", props, children: [child], ...at ? { at } : {}, ...cond.at ? { bindAt: cond.at } : {} };
|
|
2408
2474
|
}
|
|
2409
|
-
/**
|
|
2475
|
+
/**
|
|
2476
|
+
* `<el each=${items} key?>${item => …}</el>` → `<el><For each>{item => …}</For></el>`: the
|
|
2477
|
+
* content is repeated per item. Content that is not a `${item => …}` arrow is repeated as
|
|
2478
|
+
* it is. Null when the element has no `each`.
|
|
2479
|
+
*/
|
|
2410
2480
|
lowerEach(el) {
|
|
2411
2481
|
const eachDir = el.attributes.find((a) => a.kind === "EachDirective");
|
|
2412
|
-
if (!eachDir || eachDir.kind !== "EachDirective") return
|
|
2482
|
+
if (!eachDir || eachDir.kind !== "EachDirective") return null;
|
|
2413
2483
|
const each = this.hole(eachDir.hole);
|
|
2414
2484
|
const props = [{ name: "each", kind: "attr", expr: each.code, reactive: each.reactive, ...each.at ? { at: each.at } : {} }];
|
|
2415
2485
|
if (eachDir.key) {
|
|
@@ -2419,12 +2489,12 @@ var Lowerer = class {
|
|
|
2419
2489
|
const arrow = this.itemArrow(el);
|
|
2420
2490
|
let childrenCode;
|
|
2421
2491
|
if (arrow) {
|
|
2422
|
-
const
|
|
2423
|
-
|
|
2424
|
-
childrenCode = `(${arrow.params.join(", ")}) => ${paren(this.emit(repeated), "arrowBody")}`;
|
|
2492
|
+
const body = arrow.bodyReactive ? `() => ${paren(arrow.body, "arrowBody")}` : paren(arrow.body, "arrowBody");
|
|
2493
|
+
childrenCode = `(${arrow.params.join(", ")}) => ${body}`;
|
|
2425
2494
|
} else {
|
|
2426
|
-
const
|
|
2427
|
-
|
|
2495
|
+
const content = this.lowerChildren(el.children);
|
|
2496
|
+
const ir = content.length === 1 ? content[0] : { kind: "fragment", children: content };
|
|
2497
|
+
childrenCode = `() => ${paren(this.emit(ir), "arrowBody")}`;
|
|
2428
2498
|
}
|
|
2429
2499
|
const children = [{ kind: "expr", code: childrenCode, reactive: false }];
|
|
2430
2500
|
const at = this.tagAt(el);
|
|
@@ -2441,25 +2511,6 @@ var Lowerer = class {
|
|
|
2441
2511
|
const clone = { ...el, attributes: el.attributes.filter((a) => !drop(a)) };
|
|
2442
2512
|
return clone.kind === "Element" ? this.lowerElement(clone) : this.lowerComponent(clone);
|
|
2443
2513
|
}
|
|
2444
|
-
/** Same element (minus each/key) but with explicit IR children. */
|
|
2445
|
-
rebuildWithChildren(el, children) {
|
|
2446
|
-
const props = this.lowerAttributes(
|
|
2447
|
-
el.attributes.filter((a) => a.kind !== "EachDirective")
|
|
2448
|
-
);
|
|
2449
|
-
if (el.kind === "Element") {
|
|
2450
|
-
const at = this.tagAt(el);
|
|
2451
|
-
return {
|
|
2452
|
-
kind: "element",
|
|
2453
|
-
tag: el.tag,
|
|
2454
|
-
svg: el.namespace === "svg",
|
|
2455
|
-
props,
|
|
2456
|
-
children,
|
|
2457
|
-
static: false,
|
|
2458
|
-
...at ? { at } : {}
|
|
2459
|
-
};
|
|
2460
|
-
}
|
|
2461
|
-
return { kind: "component", name: el.tag, props, children };
|
|
2462
|
-
}
|
|
2463
2514
|
// --- attributes ---------------------------------------------------------
|
|
2464
2515
|
lowerAttributes(attrs) {
|
|
2465
2516
|
const props = [];
|
|
@@ -2778,6 +2829,22 @@ var DELEGATED2 = /* @__PURE__ */ new Set([
|
|
|
2778
2829
|
"mouseup"
|
|
2779
2830
|
]);
|
|
2780
2831
|
var isReactive = (node) => isReactiveShape(shapeOf(node));
|
|
2832
|
+
var CONTAINER2 = "container";
|
|
2833
|
+
function directive(node, name) {
|
|
2834
|
+
if (node.type !== "JSXElement") return void 0;
|
|
2835
|
+
const opening = node.openingElement;
|
|
2836
|
+
if (opening.name.type !== "JSXIdentifier" || opening.name.name[0] !== opening.name.name[0].toLowerCase()) return void 0;
|
|
2837
|
+
return opening.attributes.find((a) => a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && a.name.name === name);
|
|
2838
|
+
}
|
|
2839
|
+
function directiveExpr(attr) {
|
|
2840
|
+
const v = attr.value;
|
|
2841
|
+
return v?.type === "JSXExpressionContainer" && v.expression?.type !== "JSXEmptyExpression" ? v.expression : void 0;
|
|
2842
|
+
}
|
|
2843
|
+
function emitIR(ir, ctx) {
|
|
2844
|
+
const { code, imports, templates } = imperativeBackend.emit(ir, {});
|
|
2845
|
+
for (const s of imports) ctx.used.add(s);
|
|
2846
|
+
return inlineTemplates(code, templates);
|
|
2847
|
+
}
|
|
2781
2848
|
var posOf = (node, ctx) => {
|
|
2782
2849
|
if (!ctx.sourceFile) return void 0;
|
|
2783
2850
|
const at = node.loc?.start;
|
|
@@ -2886,7 +2953,16 @@ function buildProps(attrs, ctx) {
|
|
|
2886
2953
|
}
|
|
2887
2954
|
function buildChildren(children, ctx) {
|
|
2888
2955
|
const out = [];
|
|
2889
|
-
for (
|
|
2956
|
+
for (let i = 0; i < children.length; i++) {
|
|
2957
|
+
const child = children[i];
|
|
2958
|
+
if (directive(child, "if")) {
|
|
2959
|
+
let next = i + 1;
|
|
2960
|
+
while (next < children.length && children[next].type === "JSXText" && !cleanTemplateText(children[next].value)) next++;
|
|
2961
|
+
const fallback = directive(children[next], "else") ? children[next] : null;
|
|
2962
|
+
out.push(buildIf(child, fallback, ctx));
|
|
2963
|
+
if (fallback) i = next;
|
|
2964
|
+
continue;
|
|
2965
|
+
}
|
|
2890
2966
|
if (child.type === "JSXText") {
|
|
2891
2967
|
const text = cleanTemplateText(child.value);
|
|
2892
2968
|
if (text) out.push({ kind: "text", value: text });
|
|
@@ -2923,19 +2999,68 @@ function readLoad(attrs) {
|
|
|
2923
2999
|
}
|
|
2924
3000
|
return void 0;
|
|
2925
3001
|
}
|
|
3002
|
+
function buildIf(node, fallback, ctx) {
|
|
3003
|
+
const cond = directiveExpr(directive(node, "if"));
|
|
3004
|
+
const props = [
|
|
3005
|
+
cond ? { name: "when", kind: "attr", expr: ctx.code(cond), reactive: isReactive(cond), at: posOf(cond, ctx) } : { name: "when", kind: "attr", literal: true }
|
|
3006
|
+
];
|
|
3007
|
+
if (fallback) {
|
|
3008
|
+
props.push({ name: "fallback", kind: "attr", expr: emitIR(buildElement(fallback, ctx, ["else"]), ctx), jsxElement: true });
|
|
3009
|
+
}
|
|
3010
|
+
return { kind: "component", name: "Show", props, children: [buildElement(node, ctx, ["if"])] };
|
|
3011
|
+
}
|
|
3012
|
+
function buildEach(node, ctx) {
|
|
3013
|
+
const attr = directive(node, "each");
|
|
3014
|
+
if (!attr) return null;
|
|
3015
|
+
const list = directiveExpr(attr);
|
|
3016
|
+
const props = [
|
|
3017
|
+
list ? { name: "each", kind: "attr", expr: ctx.code(list), reactive: isReactive(list), at: posOf(list, ctx) } : { name: "each", kind: "attr", expr: "[]" }
|
|
3018
|
+
];
|
|
3019
|
+
const key = directive(node, "key");
|
|
3020
|
+
if (key) {
|
|
3021
|
+
const keyExpr = directiveExpr(key);
|
|
3022
|
+
const by = isString(key.value) ? `(item) => item[${JSON.stringify(literalOf(key.value))}]` : keyExpr ? ctx.code(keyExpr) : void 0;
|
|
3023
|
+
if (by) props.push({ name: "by", kind: "attr", expr: by });
|
|
3024
|
+
}
|
|
3025
|
+
const content = node.children.filter((c) => !(c.type === "JSXText" && !cleanTemplateText(c.value)));
|
|
3026
|
+
const only = content.length === 1 && content[0].type === "JSXExpressionContainer" ? content[0].expression : null;
|
|
3027
|
+
let render;
|
|
3028
|
+
if (only && (only.type === "ArrowFunctionExpression" || only.type === "FunctionExpression")) {
|
|
3029
|
+
const fn = ctx.code(only);
|
|
3030
|
+
const live = only.type === "ArrowFunctionExpression" && only.body.type !== "BlockStatement" && isReactive(only.body);
|
|
3031
|
+
render = live ? `(...args) => () => (${fn})(...args)` : fn;
|
|
3032
|
+
} else {
|
|
3033
|
+
const built = buildChildren(node.children, ctx);
|
|
3034
|
+
const ir = built.length === 1 ? built[0] : { kind: "fragment", children: built };
|
|
3035
|
+
render = `() => ${paren(emitIR(ir, ctx), "arrowBody")}`;
|
|
3036
|
+
}
|
|
3037
|
+
return { kind: "component", name: "For", props, children: [{ kind: "expr", code: render, reactive: false }] };
|
|
3038
|
+
}
|
|
3039
|
+
function buildElement(node, ctx, handled) {
|
|
3040
|
+
const skip = new Set(handled);
|
|
3041
|
+
const directives = /* @__PURE__ */ new Set(["if", "else", "each", ...directive(node, "each") ? ["key"] : []]);
|
|
3042
|
+
const attributes = node.openingElement.attributes.filter(
|
|
3043
|
+
(a) => !(a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" && (skip.has(a.name.name) || directives.has(a.name.name)))
|
|
3044
|
+
);
|
|
3045
|
+
const { tag } = tagInfo(node.openingElement.name, ctx);
|
|
3046
|
+
const each = buildEach(node, ctx);
|
|
3047
|
+
const children = each ? [each] : buildChildren(node.children, ctx);
|
|
3048
|
+
if (tag === CONTAINER2) return children.length === 1 ? children[0] : { kind: "fragment", children };
|
|
3049
|
+
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
3050
|
+
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props: buildProps(attributes, ctx), children, static: false, ...at ? { at } : {} };
|
|
3051
|
+
}
|
|
2926
3052
|
function buildNode(node, ctx) {
|
|
2927
3053
|
if (node.type === "JSXFragment") {
|
|
2928
3054
|
return { kind: "fragment", children: buildChildren(node.children, ctx) };
|
|
2929
3055
|
}
|
|
3056
|
+
if (directive(node, "if")) return buildIf(node, null, ctx);
|
|
2930
3057
|
const { tag, component } = tagInfo(node.openingElement.name, ctx);
|
|
3058
|
+
if (!component) return buildElement(node, ctx, []);
|
|
2931
3059
|
const props = buildProps(node.openingElement.attributes, ctx);
|
|
2932
3060
|
const children = buildChildren(node.children, ctx);
|
|
2933
3061
|
const at = ctx.sourceFile && node.loc?.start ? { file: ctx.sourceFile, line: node.loc.start.line, column: node.loc.start.column } : void 0;
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
2937
|
-
}
|
|
2938
|
-
return { kind: "element", tag, svg: SVG_TAGS.has(tag), props, children, static: false, ...at ? { at } : {} };
|
|
3062
|
+
const load = readLoad(node.openingElement.attributes);
|
|
3063
|
+
return { kind: "component", name: tag, props, children, ...load ? { load } : {}, ...at ? { at } : {} };
|
|
2939
3064
|
}
|
|
2940
3065
|
function buildJsxIR(node, ctx) {
|
|
2941
3066
|
const ir = buildNode(node, ctx);
|
|
@@ -3391,10 +3516,34 @@ function collectIntrinsicEdits(program) {
|
|
|
3391
3516
|
});
|
|
3392
3517
|
return { edits, diagnostics, imports };
|
|
3393
3518
|
}
|
|
3519
|
+
function avoidBindingClashes(program, result, bound) {
|
|
3520
|
+
const importedAsIs = /* @__PURE__ */ new Set();
|
|
3521
|
+
for (const stmt of program.body ?? []) {
|
|
3522
|
+
if (stmt.type !== "ImportDeclaration") continue;
|
|
3523
|
+
const module = stmt.source.value;
|
|
3524
|
+
for (const spec of stmt.specifiers ?? []) {
|
|
3525
|
+
if (spec.type !== "ImportSpecifier") continue;
|
|
3526
|
+
const imported = spec.imported.name ?? spec.imported.value;
|
|
3527
|
+
if (imported === spec.local.name && result.imports.get(module)?.has(imported)) {
|
|
3528
|
+
importedAsIs.add(imported);
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
3533
|
+
for (const names of result.imports.values()) {
|
|
3534
|
+
for (const name of names) if (bound.has(name) && !importedAsIs.has(name)) aliases.set(name, `__fx_${name}`);
|
|
3535
|
+
}
|
|
3536
|
+
if (!aliases.size) return;
|
|
3537
|
+
for (const edit of result.edits) {
|
|
3538
|
+
const alias = aliases.get(edit.code);
|
|
3539
|
+
if (alias) edit.code = alias;
|
|
3540
|
+
}
|
|
3541
|
+
result.aliases = aliases;
|
|
3542
|
+
}
|
|
3394
3543
|
function intrinsicImports(result, bound) {
|
|
3395
3544
|
const lines = [];
|
|
3396
3545
|
for (const [module, names] of [...result.imports].sort(([a], [b]) => a.localeCompare(b))) {
|
|
3397
|
-
const wanted = [...names].filter((name) => !bound.has(name)).sort();
|
|
3546
|
+
const wanted = [...names].filter((name) => result.aliases?.has(name) || !bound.has(name)).sort().map((name) => result.aliases?.has(name) ? `${name} as ${result.aliases.get(name)}` : name);
|
|
3398
3547
|
if (wanted.length) lines.push(`import { ${wanted.join(", ")} } from ${JSON.stringify(module)};`);
|
|
3399
3548
|
}
|
|
3400
3549
|
return lines.length ? `${lines.join("\n")}
|
|
@@ -3579,7 +3728,7 @@ function transformTemplates(code, id, options = {}) {
|
|
|
3579
3728
|
const wantsJsx = format !== "lit";
|
|
3580
3729
|
const mayHaveTemplate = wantsLit && (code.includes(`${litTag}\``) || code.includes("svg`"));
|
|
3581
3730
|
const wantsIntrinsics = options.intrinsics !== false && mayHaveIntrinsic(code);
|
|
3582
|
-
const wantsSources = options.sourceLocations === true &&
|
|
3731
|
+
const wantsSources = options.sourceLocations === true && mayHaveSourceMarks(code);
|
|
3583
3732
|
const sourceName = relativeToRoot(id, options.sourceRoot);
|
|
3584
3733
|
if (!mayHaveTemplate && !(wantsJsx && code.includes("<")) && !wantsIntrinsics && !wantsSources) return null;
|
|
3585
3734
|
const ast = parse2(code, {
|
|
@@ -3594,6 +3743,7 @@ function transformTemplates(code, id, options = {}) {
|
|
|
3594
3743
|
});
|
|
3595
3744
|
const props = options.propsDestructure === false ? { edits: [], diagnostics: [], used: /* @__PURE__ */ new Set() } : collectPropsEdits(ast.program, code, { runtimeModule: options.runtimeModule });
|
|
3596
3745
|
const intrinsics = options.intrinsics === false ? { edits: [], diagnostics: [], imports: /* @__PURE__ */ new Map() } : collectIntrinsicEdits(ast.program);
|
|
3746
|
+
if (intrinsics.edits.length) avoidBindingClashes(ast.program, intrinsics, moduleBindings(ast.program));
|
|
3597
3747
|
const sources = options.sourceLocations === true ? collectSourceEdits(ast.program, sourceName) : { edits: [], marked: 0 };
|
|
3598
3748
|
const silent = found.length === 0 && props.edits.length === 0 && sources.edits.length === 0 && intrinsics.edits.length === 0 && intrinsics.diagnostics.length === 0 && props.diagnostics.length === 0;
|
|
3599
3749
|
if (silent) return null;
|
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/transform/intrinsics.ts
|
|
21
21
|
var intrinsics_exports = {};
|
|
22
22
|
__export(intrinsics_exports, {
|
|
23
|
+
avoidBindingClashes: () => avoidBindingClashes,
|
|
23
24
|
collectIntrinsicEdits: () => collectIntrinsicEdits,
|
|
24
25
|
intrinsicImports: () => intrinsicImports
|
|
25
26
|
});
|
|
@@ -33,6 +34,7 @@ var INTRINSICS = {
|
|
|
33
34
|
$effect: { export: "effect", module: REACTIVE_MODULE, returns: "effect" },
|
|
34
35
|
$store: { export: "store", module: "@fluixi/reactive/store", returns: "store-handle" },
|
|
35
36
|
$resource: { export: "resource", module: REACTIVE_MODULE, returns: "resource-handle" },
|
|
37
|
+
$cachedResource: { export: "cachedResource", module: "@fluixi/reactive/cached-resource", returns: "resource-handle" },
|
|
36
38
|
// No short runtime form for these yet, so the intrinsic stands for the `create*`
|
|
37
39
|
// name. Still worth having: no import to write, and when a shorter form is
|
|
38
40
|
// designed, the change is this line rather than a compiler pass.
|
|
@@ -42,7 +44,22 @@ var INTRINSICS = {
|
|
|
42
44
|
// does. `untrack` suppresses dependency collection, which is exactly the kind of thing
|
|
43
45
|
// dependency analysis must not guess at.
|
|
44
46
|
$untrack: { export: "untrack", module: REACTIVE_MODULE, returns: "plain" },
|
|
45
|
-
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" }
|
|
47
|
+
$untrackStore: { export: "untrackStore", module: "@fluixi/reactive/store", returns: "plain" },
|
|
48
|
+
// Scopes, scheduling and lifecycle: no value to classify, but the same authoring promise,
|
|
49
|
+
// no import to write. `$cleanup` registers a callback on the current owner (`onCleanup`).
|
|
50
|
+
$batch: { export: "batch", module: REACTIVE_MODULE, returns: "plain" },
|
|
51
|
+
$cleanup: { export: "onCleanup", module: REACTIVE_MODULE, returns: "plain" },
|
|
52
|
+
$root: { export: "root", module: REACTIVE_MODULE, returns: "plain" },
|
|
53
|
+
$context: { export: "context", module: REACTIVE_MODULE, returns: "plain" },
|
|
54
|
+
$renderEffect: { export: "renderEffect", module: REACTIVE_MODULE, returns: "effect" },
|
|
55
|
+
$computed: { export: "computed", module: REACTIVE_MODULE, returns: "effect" },
|
|
56
|
+
// Storage for where the code runs, and a signal kept in it. `$persisted` is a signal
|
|
57
|
+
// handle as far as reads go: the same accessor with `set` on it.
|
|
58
|
+
$storage: { export: "useStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
59
|
+
$asyncStorage: { export: "useAsyncStorage", module: "@fluixi/core/storage", returns: "plain" },
|
|
60
|
+
$persisted: { export: "persisted", module: "@fluixi/core/storage", returns: "signal-handle" },
|
|
61
|
+
// An environment variable, public or server-only; the browser build refuses a secret.
|
|
62
|
+
$env: { export: "env", module: "@fluixi/start/env", returns: "plain" }
|
|
46
63
|
};
|
|
47
64
|
var RUNTIME_PRIMITIVES = {
|
|
48
65
|
signal: "signal-handle",
|
|
@@ -53,8 +70,11 @@ var RUNTIME_PRIMITIVES = {
|
|
|
53
70
|
createMemo: "memo-accessor",
|
|
54
71
|
createEffect: "effect",
|
|
55
72
|
createRenderEffect: "effect",
|
|
73
|
+
renderEffect: "effect",
|
|
74
|
+
computed: "effect",
|
|
56
75
|
createStore: "reactive-object",
|
|
57
76
|
resource: "resource-handle",
|
|
77
|
+
cachedResource: "resource-handle",
|
|
58
78
|
createResource: "reactive-object",
|
|
59
79
|
createSelector: "memo-accessor",
|
|
60
80
|
createDeferred: "memo-accessor"
|
|
@@ -63,6 +83,7 @@ var PRIMITIVE_MODULES = [
|
|
|
63
83
|
"@fluixi/reactive",
|
|
64
84
|
"@fluixi/reactive/signal",
|
|
65
85
|
"@fluixi/reactive/store",
|
|
86
|
+
"@fluixi/reactive/cached-resource",
|
|
66
87
|
"@fluixi/core"
|
|
67
88
|
];
|
|
68
89
|
var INTRINSIC_CALL = new RegExp(`\\$(?:${Object.keys(INTRINSICS).map((n) => n.slice(1)).join("|")})\\s*\\(`);
|
|
@@ -205,10 +226,34 @@ function collectIntrinsicEdits(program) {
|
|
|
205
226
|
});
|
|
206
227
|
return { edits, diagnostics, imports };
|
|
207
228
|
}
|
|
229
|
+
function avoidBindingClashes(program, result, bound) {
|
|
230
|
+
const importedAsIs = /* @__PURE__ */ new Set();
|
|
231
|
+
for (const stmt of program.body ?? []) {
|
|
232
|
+
if (stmt.type !== "ImportDeclaration") continue;
|
|
233
|
+
const module2 = stmt.source.value;
|
|
234
|
+
for (const spec of stmt.specifiers ?? []) {
|
|
235
|
+
if (spec.type !== "ImportSpecifier") continue;
|
|
236
|
+
const imported = spec.imported.name ?? spec.imported.value;
|
|
237
|
+
if (imported === spec.local.name && result.imports.get(module2)?.has(imported)) {
|
|
238
|
+
importedAsIs.add(imported);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
243
|
+
for (const names of result.imports.values()) {
|
|
244
|
+
for (const name of names) if (bound.has(name) && !importedAsIs.has(name)) aliases.set(name, `__fx_${name}`);
|
|
245
|
+
}
|
|
246
|
+
if (!aliases.size) return;
|
|
247
|
+
for (const edit of result.edits) {
|
|
248
|
+
const alias = aliases.get(edit.code);
|
|
249
|
+
if (alias) edit.code = alias;
|
|
250
|
+
}
|
|
251
|
+
result.aliases = aliases;
|
|
252
|
+
}
|
|
208
253
|
function intrinsicImports(result, bound) {
|
|
209
254
|
const lines = [];
|
|
210
255
|
for (const [module2, names] of [...result.imports].sort(([a], [b]) => a.localeCompare(b))) {
|
|
211
|
-
const wanted = [...names].filter((name) => !bound.has(name)).sort();
|
|
256
|
+
const wanted = [...names].filter((name) => result.aliases?.has(name) || !bound.has(name)).sort().map((name) => result.aliases?.has(name) ? `${name} as ${result.aliases.get(name)}` : name);
|
|
212
257
|
if (wanted.length) lines.push(`import { ${wanted.join(", ")} } from ${JSON.stringify(module2)};`);
|
|
213
258
|
}
|
|
214
259
|
return lines.length ? `${lines.join("\n")}
|
|
@@ -14,8 +14,20 @@ export interface IntrinsicsResult {
|
|
|
14
14
|
diagnostics: IntrinsicDiagnostic[];
|
|
15
15
|
/** Runtime exports the rewritten code needs, by module. */
|
|
16
16
|
imports: Map<string, Set<string>>;
|
|
17
|
+
/** Runtime exports imported under another name, because the module binds theirs. */
|
|
18
|
+
aliases?: Map<string, string>;
|
|
17
19
|
}
|
|
18
20
|
/** Find the `$` calls in a module and say what they compile to. */
|
|
19
21
|
export declare function collectIntrinsicEdits(program: unknown): IntrinsicsResult;
|
|
22
|
+
/**
|
|
23
|
+
* Point a rewritten call past a local binding of the same name.
|
|
24
|
+
*
|
|
25
|
+
* `$env('X')` becomes `env('X')`, and the import of `env` used to be skipped when the module
|
|
26
|
+
* already bound `env`, on the assumption that it was the same import. A config module with its
|
|
27
|
+
* own `const env = {…}` is common, and there the call reached the app's object instead. Such a
|
|
28
|
+
* name is now imported under a private alias. A module that imports that very export from that
|
|
29
|
+
* very module keeps the old behaviour: the binding is the runtime already.
|
|
30
|
+
*/
|
|
31
|
+
export declare function avoidBindingClashes(program: unknown, result: IntrinsicsResult, bound: Set<string>): void;
|
|
20
32
|
/** The import statements a result needs, minus anything the module already binds. */
|
|
21
33
|
export declare function intrinsicImports(result: IntrinsicsResult, bound: Set<string>): string;
|
|
@@ -14,9 +14,21 @@ export interface IntrinsicsResult {
|
|
|
14
14
|
diagnostics: IntrinsicDiagnostic[];
|
|
15
15
|
/** Runtime exports the rewritten code needs, by module. */
|
|
16
16
|
imports: Map<string, Set<string>>;
|
|
17
|
+
/** Runtime exports imported under another name, because the module binds theirs. */
|
|
18
|
+
aliases?: Map<string, string>;
|
|
17
19
|
}
|
|
18
20
|
/** Find the `$` calls in a module and say what they compile to. */
|
|
19
21
|
export declare function collectIntrinsicEdits(program: unknown): IntrinsicsResult;
|
|
22
|
+
/**
|
|
23
|
+
* Point a rewritten call past a local binding of the same name.
|
|
24
|
+
*
|
|
25
|
+
* `$env('X')` becomes `env('X')`, and the import of `env` used to be skipped when the module
|
|
26
|
+
* already bound `env`, on the assumption that it was the same import. A config module with its
|
|
27
|
+
* own `const env = {…}` is common, and there the call reached the app's object instead. Such a
|
|
28
|
+
* name is now imported under a private alias. A module that imports that very export from that
|
|
29
|
+
* very module keeps the old behaviour: the binding is the runtime already.
|
|
30
|
+
*/
|
|
31
|
+
export declare function avoidBindingClashes(program: unknown, result: IntrinsicsResult, bound: Set<string>): void;
|
|
20
32
|
/** The import statements a result needs, minus anything the module already binds. */
|
|
21
33
|
export declare function intrinsicImports(result: IntrinsicsResult, bound: Set<string>): string;
|
|
22
34
|
//# sourceMappingURL=intrinsics.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intrinsics.d.ts","sourceRoot":"","sources":["../../src/transform/intrinsics.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,2DAA2D;IAC3D,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"intrinsics.d.ts","sourceRoot":"","sources":["../../src/transform/intrinsics.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,WAAW,EAAE,mBAAmB,EAAE,CAAC;IACnC,2DAA2D;IAC3D,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAClC,oFAAoF;IACpF,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAaD,mEAAmE;AACnE,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,CAuCxE;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAwBxG;AAED,qFAAqF;AACrF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAUrF"}
|
|
@@ -66,11 +66,53 @@ export function collectIntrinsicEdits(program) {
|
|
|
66
66
|
});
|
|
67
67
|
return { edits, diagnostics, imports };
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Point a rewritten call past a local binding of the same name.
|
|
71
|
+
*
|
|
72
|
+
* `$env('X')` becomes `env('X')`, and the import of `env` used to be skipped when the module
|
|
73
|
+
* already bound `env`, on the assumption that it was the same import. A config module with its
|
|
74
|
+
* own `const env = {…}` is common, and there the call reached the app's object instead. Such a
|
|
75
|
+
* name is now imported under a private alias. A module that imports that very export from that
|
|
76
|
+
* very module keeps the old behaviour: the binding is the runtime already.
|
|
77
|
+
*/
|
|
78
|
+
export function avoidBindingClashes(program, result, bound) {
|
|
79
|
+
const importedAsIs = new Set();
|
|
80
|
+
for (const stmt of program.body ?? []) {
|
|
81
|
+
if (stmt.type !== 'ImportDeclaration')
|
|
82
|
+
continue;
|
|
83
|
+
const module = stmt.source.value;
|
|
84
|
+
for (const spec of stmt.specifiers ?? []) {
|
|
85
|
+
if (spec.type !== 'ImportSpecifier')
|
|
86
|
+
continue;
|
|
87
|
+
const imported = (spec.imported.name ?? spec.imported.value);
|
|
88
|
+
if (imported === spec.local.name && result.imports.get(module)?.has(imported)) {
|
|
89
|
+
importedAsIs.add(imported);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const aliases = new Map();
|
|
94
|
+
for (const names of result.imports.values()) {
|
|
95
|
+
for (const name of names)
|
|
96
|
+
if (bound.has(name) && !importedAsIs.has(name))
|
|
97
|
+
aliases.set(name, `__fx_${name}`);
|
|
98
|
+
}
|
|
99
|
+
if (!aliases.size)
|
|
100
|
+
return;
|
|
101
|
+
for (const edit of result.edits) {
|
|
102
|
+
const alias = aliases.get(edit.code);
|
|
103
|
+
if (alias)
|
|
104
|
+
edit.code = alias;
|
|
105
|
+
}
|
|
106
|
+
result.aliases = aliases;
|
|
107
|
+
}
|
|
69
108
|
/** The import statements a result needs, minus anything the module already binds. */
|
|
70
109
|
export function intrinsicImports(result, bound) {
|
|
71
110
|
const lines = [];
|
|
72
111
|
for (const [module, names] of [...result.imports].sort(([a], [b]) => a.localeCompare(b))) {
|
|
73
|
-
const wanted = [...names]
|
|
112
|
+
const wanted = [...names]
|
|
113
|
+
.filter((name) => result.aliases?.has(name) || !bound.has(name))
|
|
114
|
+
.sort()
|
|
115
|
+
.map((name) => (result.aliases?.has(name) ? `${name} as ${result.aliases.get(name)}` : name));
|
|
74
116
|
if (wanted.length)
|
|
75
117
|
lines.push(`import { ${wanted.join(', ')} } from ${JSON.stringify(module)};`);
|
|
76
118
|
}
|