@zenithbuild/cli 0.6.3 → 0.6.5
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/build.js +429 -75
- package/dist/component-instance-ir.js +217 -0
- package/dist/component-occurrences.js +152 -0
- package/dist/index.js +9 -0
- package/dist/toolchain-paths.js +110 -0
- package/dist/version-check.js +378 -0
- package/package.json +2 -2
package/dist/build.js
CHANGED
|
@@ -16,13 +16,13 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
|
16
16
|
import { mkdir, readdir, rm, stat } from 'node:fs/promises';
|
|
17
17
|
import { createRequire } from 'node:module';
|
|
18
18
|
import { basename, dirname, extname, join, relative, resolve } from 'node:path';
|
|
19
|
-
import { fileURLToPath } from 'node:url';
|
|
20
19
|
import { generateManifest } from './manifest.js';
|
|
21
20
|
import { buildComponentRegistry, expandComponents, extractTemplate, isDocumentMode } from './resolve-components.js';
|
|
21
|
+
import { collectExpandedComponentOccurrences } from './component-occurrences.js';
|
|
22
|
+
import { applyOccurrenceRewritePlans, cloneComponentIrForInstance } from './component-instance-ir.js';
|
|
23
|
+
import { resolveBundlerBin, resolveCompilerBin } from './toolchain-paths.js';
|
|
24
|
+
import { maybeWarnAboutZenithVersionMismatch } from './version-check.js';
|
|
22
25
|
|
|
23
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
-
const __dirname = dirname(__filename);
|
|
25
|
-
const CLI_ROOT = resolve(__dirname, '..');
|
|
26
26
|
const require = createRequire(import.meta.url);
|
|
27
27
|
let cachedTypeScript = undefined;
|
|
28
28
|
|
|
@@ -40,40 +40,6 @@ function loadTypeScriptApi() {
|
|
|
40
40
|
return cachedTypeScript;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
/**
|
|
44
|
-
* Resolve a binary path from deterministic candidates.
|
|
45
|
-
*
|
|
46
|
-
* Supports both repository layout (../zenith-*) and installed package layout
|
|
47
|
-
* under node_modules/@zenithbuild (../compiler, ../bundler).
|
|
48
|
-
*
|
|
49
|
-
* @param {string[]} candidates
|
|
50
|
-
* @returns {string}
|
|
51
|
-
*/
|
|
52
|
-
function resolveBinary(candidates) {
|
|
53
|
-
for (const candidate of candidates) {
|
|
54
|
-
if (existsSync(candidate)) {
|
|
55
|
-
return candidate;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return candidates[0];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const COMPILER_BIN = resolveBinary([
|
|
62
|
-
resolve(CLI_ROOT, '../compiler/target/release/zenith-compiler'),
|
|
63
|
-
resolve(CLI_ROOT, '../zenith-compiler/target/release/zenith-compiler')
|
|
64
|
-
]);
|
|
65
|
-
|
|
66
|
-
function getBundlerBin() {
|
|
67
|
-
const envBin = process.env.ZENITH_BUNDLER_BIN;
|
|
68
|
-
if (envBin && typeof envBin === 'string' && existsSync(envBin)) {
|
|
69
|
-
return envBin;
|
|
70
|
-
}
|
|
71
|
-
return resolveBinary([
|
|
72
|
-
resolve(CLI_ROOT, '../bundler/target/release/zenith-bundler'),
|
|
73
|
-
resolve(CLI_ROOT, '../zenith-bundler/target/release/zenith-bundler')
|
|
74
|
-
]);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
43
|
/**
|
|
78
44
|
* Build a per-build warning emitter that deduplicates repeated compiler lines.
|
|
79
45
|
*
|
|
@@ -133,9 +99,11 @@ function forwardStreamLines(stream, onLine) {
|
|
|
133
99
|
* @param {object} compilerRunOptions
|
|
134
100
|
* @param {(warning: string) => void} [compilerRunOptions.onWarning]
|
|
135
101
|
* @param {boolean} [compilerRunOptions.suppressWarnings]
|
|
102
|
+
* @param {string} [compilerRunOptions.compilerBin]
|
|
136
103
|
* @returns {object}
|
|
137
104
|
*/
|
|
138
105
|
function runCompiler(filePath, stdinSource, compilerOpts = {}, compilerRunOptions = {}) {
|
|
106
|
+
const compilerBin = compilerRunOptions.compilerBin || resolveCompilerBin();
|
|
139
107
|
const args = stdinSource !== undefined
|
|
140
108
|
? ['--stdin', filePath]
|
|
141
109
|
: [filePath];
|
|
@@ -150,7 +118,7 @@ function runCompiler(filePath, stdinSource, compilerOpts = {}, compilerRunOption
|
|
|
150
118
|
opts.input = stdinSource;
|
|
151
119
|
}
|
|
152
120
|
|
|
153
|
-
const result = spawnSync(
|
|
121
|
+
const result = spawnSync(compilerBin, args, opts);
|
|
154
122
|
|
|
155
123
|
if (result.error) {
|
|
156
124
|
throw new Error(`Compiler spawn failed for ${filePath}: ${result.error.message}`);
|
|
@@ -200,6 +168,8 @@ function stripStyleBlocks(source) {
|
|
|
200
168
|
* @param {string} compPath
|
|
201
169
|
* @param {string} componentSource
|
|
202
170
|
* @param {object} compIr
|
|
171
|
+
* @param {object} compilerOpts
|
|
172
|
+
* @param {string} compilerBin
|
|
203
173
|
* @returns {{
|
|
204
174
|
* map: Map<string, string>,
|
|
205
175
|
* bindings: Map<string, {
|
|
@@ -215,13 +185,14 @@ function stripStyleBlocks(source) {
|
|
|
215
185
|
* ambiguous: Set<string>
|
|
216
186
|
* }}
|
|
217
187
|
*/
|
|
218
|
-
function buildComponentExpressionRewrite(compPath, componentSource, compIr, compilerOpts) {
|
|
188
|
+
function buildComponentExpressionRewrite(compPath, componentSource, compIr, compilerOpts, compilerBin) {
|
|
219
189
|
const out = {
|
|
220
190
|
map: new Map(),
|
|
221
191
|
bindings: new Map(),
|
|
222
192
|
signals: Array.isArray(compIr?.signals) ? compIr.signals : [],
|
|
223
193
|
stateBindings: Array.isArray(compIr?.hoisted?.state) ? compIr.hoisted.state : [],
|
|
224
|
-
ambiguous: new Set()
|
|
194
|
+
ambiguous: new Set(),
|
|
195
|
+
sequence: []
|
|
225
196
|
};
|
|
226
197
|
const rewrittenExpressions = Array.isArray(compIr?.expressions) ? compIr.expressions : [];
|
|
227
198
|
const rewrittenBindings = Array.isArray(compIr?.expression_bindings) ? compIr.expression_bindings : [];
|
|
@@ -236,7 +207,10 @@ function buildComponentExpressionRewrite(compPath, componentSource, compIr, comp
|
|
|
236
207
|
|
|
237
208
|
let templateIr;
|
|
238
209
|
try {
|
|
239
|
-
templateIr = runCompiler(compPath, templateOnly, compilerOpts, {
|
|
210
|
+
templateIr = runCompiler(compPath, templateOnly, compilerOpts, {
|
|
211
|
+
suppressWarnings: true,
|
|
212
|
+
compilerBin
|
|
213
|
+
});
|
|
240
214
|
} catch {
|
|
241
215
|
return out;
|
|
242
216
|
}
|
|
@@ -264,6 +238,12 @@ function buildComponentExpressionRewrite(compPath, componentSource, compIr, comp
|
|
|
264
238
|
}
|
|
265
239
|
: null;
|
|
266
240
|
|
|
241
|
+
out.sequence.push({
|
|
242
|
+
raw,
|
|
243
|
+
rewritten,
|
|
244
|
+
binding: normalizedBinding
|
|
245
|
+
});
|
|
246
|
+
|
|
267
247
|
if (!out.ambiguous.has(raw) && normalizedBinding) {
|
|
268
248
|
const existingBinding = out.bindings.get(raw);
|
|
269
249
|
if (existingBinding) {
|
|
@@ -327,7 +307,7 @@ function resolveRewrittenBindingMetadata(pageIr, componentRewrite, binding) {
|
|
|
327
307
|
}
|
|
328
308
|
|
|
329
309
|
const pageStateBindings = Array.isArray(pageIr?.hoisted?.state) ? pageIr.hoisted.state : [];
|
|
330
|
-
const pageSignals = Array.isArray(pageIr?.
|
|
310
|
+
const pageSignals = Array.isArray(pageIr?.signals) ? pageIr.signals : [];
|
|
331
311
|
const pageStateIndexByKey = new Map();
|
|
332
312
|
const pageSignalIndexByStateKey = new Map();
|
|
333
313
|
|
|
@@ -620,6 +600,107 @@ function applyExpressionRewrites(pageIr, expressionMap, bindingMap, ambiguous) {
|
|
|
620
600
|
}
|
|
621
601
|
}
|
|
622
602
|
|
|
603
|
+
function applyScopedIdentifierRewrites(pageIr, scopeRewrite) {
|
|
604
|
+
if (!Array.isArray(pageIr?.expressions) || pageIr.expressions.length === 0) {
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
const bindings = Array.isArray(pageIr.expression_bindings) ? pageIr.expression_bindings : [];
|
|
608
|
+
const rewriteContext = {
|
|
609
|
+
scopeRewrite
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
for (let index = 0; index < pageIr.expressions.length; index++) {
|
|
613
|
+
const current = pageIr.expressions[index];
|
|
614
|
+
if (typeof current === 'string') {
|
|
615
|
+
pageIr.expressions[index] = rewritePropsExpression(current, rewriteContext);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
if (!bindings[index] || typeof bindings[index] !== 'object') {
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (typeof bindings[index].literal === 'string') {
|
|
623
|
+
bindings[index].literal = rewritePropsExpression(bindings[index].literal, rewriteContext);
|
|
624
|
+
}
|
|
625
|
+
if (typeof bindings[index].compiled_expr === 'string') {
|
|
626
|
+
bindings[index].compiled_expr = rewritePropsExpression(bindings[index].compiled_expr, rewriteContext);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function synthesizeSignalBackedCompiledExpressions(pageIr) {
|
|
632
|
+
if (!Array.isArray(pageIr?.expression_bindings) || pageIr.expression_bindings.length === 0) {
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const stateBindings = Array.isArray(pageIr?.hoisted?.state) ? pageIr.hoisted.state : [];
|
|
637
|
+
const signals = Array.isArray(pageIr?.signals) ? pageIr.signals : [];
|
|
638
|
+
if (stateBindings.length === 0 || signals.length === 0) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const signalIndexByStateKey = new Map();
|
|
643
|
+
for (let index = 0; index < signals.length; index++) {
|
|
644
|
+
const stateIndex = signals[index]?.state_index;
|
|
645
|
+
const stateKey = Number.isInteger(stateIndex) ? stateBindings[stateIndex]?.key : null;
|
|
646
|
+
if (typeof stateKey === 'string' && stateKey.length > 0) {
|
|
647
|
+
signalIndexByStateKey.set(stateKey, index);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (signalIndexByStateKey.size === 0) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
for (let index = 0; index < pageIr.expression_bindings.length; index++) {
|
|
655
|
+
const binding = pageIr.expression_bindings[index];
|
|
656
|
+
if (!binding || typeof binding !== 'object') {
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
if (typeof binding.compiled_expr === 'string' && binding.compiled_expr.includes('signalMap.get(')) {
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
const candidate = typeof binding.literal === 'string' && binding.literal.trim().length > 0
|
|
664
|
+
? binding.literal
|
|
665
|
+
: typeof pageIr.expressions?.[index] === 'string'
|
|
666
|
+
? pageIr.expressions[index]
|
|
667
|
+
: null;
|
|
668
|
+
if (typeof candidate !== 'string' || candidate.trim().length === 0) {
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
let rewritten = candidate;
|
|
673
|
+
const signalIndices = [];
|
|
674
|
+
for (const [stateKey, signalIndex] of signalIndexByStateKey.entries()) {
|
|
675
|
+
if (!rewritten.includes(stateKey)) {
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
const escaped = stateKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
679
|
+
const pattern = new RegExp(`\\b${escaped}\\b`, 'g');
|
|
680
|
+
if (!pattern.test(rewritten)) {
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
rewritten = rewritten.replace(pattern, `signalMap.get(${signalIndex}).get()`);
|
|
684
|
+
signalIndices.push(signalIndex);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (rewritten === candidate || signalIndices.length === 0) {
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const uniqueSignalIndices = [...new Set(signalIndices)].sort((a, b) => a - b);
|
|
692
|
+
binding.compiled_expr = rewritten;
|
|
693
|
+
binding.signal_indices = uniqueSignalIndices;
|
|
694
|
+
if (uniqueSignalIndices.length === 1) {
|
|
695
|
+
binding.signal_index = uniqueSignalIndices[0];
|
|
696
|
+
const stateIndex = signals[uniqueSignalIndices[0]]?.state_index;
|
|
697
|
+
if (Number.isInteger(stateIndex)) {
|
|
698
|
+
binding.state_index = stateIndex;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
623
704
|
function normalizeExpressionBindingDependencies(pageIr) {
|
|
624
705
|
if (!Array.isArray(pageIr?.expression_bindings) || pageIr.expression_bindings.length === 0) {
|
|
625
706
|
return;
|
|
@@ -1653,6 +1734,62 @@ function deriveScopedIdentifierAlias(value) {
|
|
|
1653
1734
|
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(candidate) ? candidate : ident;
|
|
1654
1735
|
}
|
|
1655
1736
|
|
|
1737
|
+
/**
|
|
1738
|
+
* @param {string} source
|
|
1739
|
+
* @returns {string[]}
|
|
1740
|
+
*/
|
|
1741
|
+
function extractDeclaredIdentifiers(source) {
|
|
1742
|
+
const text = String(source || '').trim();
|
|
1743
|
+
if (!text) {
|
|
1744
|
+
return [];
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
const ts = loadTypeScriptApi();
|
|
1748
|
+
if (ts) {
|
|
1749
|
+
const sourceFile = ts.createSourceFile('zenith-hoisted-declaration.ts', text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
1750
|
+
const identifiers = [];
|
|
1751
|
+
const collectBindingNames = (name) => {
|
|
1752
|
+
if (ts.isIdentifier(name)) {
|
|
1753
|
+
identifiers.push(name.text);
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
if (ts.isObjectBindingPattern(name) || ts.isArrayBindingPattern(name)) {
|
|
1757
|
+
for (const element of name.elements) {
|
|
1758
|
+
if (ts.isBindingElement(element)) {
|
|
1759
|
+
collectBindingNames(element.name);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
};
|
|
1764
|
+
|
|
1765
|
+
for (const statement of sourceFile.statements) {
|
|
1766
|
+
if (!ts.isVariableStatement(statement)) {
|
|
1767
|
+
continue;
|
|
1768
|
+
}
|
|
1769
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
1770
|
+
collectBindingNames(declaration.name);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
if (identifiers.length > 0) {
|
|
1775
|
+
return identifiers;
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
const fallback = [];
|
|
1780
|
+
const match = text.match(/^\s*(?:const|let|var)\s+([\s\S]+?);?\s*$/);
|
|
1781
|
+
if (!match) {
|
|
1782
|
+
return fallback;
|
|
1783
|
+
}
|
|
1784
|
+
const declarationList = match[1];
|
|
1785
|
+
const identifierRe = /(?:^|,)\s*([A-Za-z_$][A-Za-z0-9_$]*)\s*(?::[^=,]+)?=/g;
|
|
1786
|
+
let found;
|
|
1787
|
+
while ((found = identifierRe.exec(declarationList)) !== null) {
|
|
1788
|
+
fallback.push(found[1]);
|
|
1789
|
+
}
|
|
1790
|
+
return fallback;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1656
1793
|
/**
|
|
1657
1794
|
* @param {Map<string, string>} map
|
|
1658
1795
|
* @param {Set<string>} ambiguous
|
|
@@ -1698,9 +1835,131 @@ function buildScopedIdentifierRewrite(ir) {
|
|
|
1698
1835
|
recordScopedIdentifierRewrite(out.map, out.ambiguous, deriveScopedIdentifierAlias(fnName), fnName);
|
|
1699
1836
|
}
|
|
1700
1837
|
|
|
1838
|
+
const declarations = Array.isArray(ir?.hoisted?.declarations) ? ir.hoisted.declarations : [];
|
|
1839
|
+
for (const declaration of declarations) {
|
|
1840
|
+
if (typeof declaration !== 'string') {
|
|
1841
|
+
continue;
|
|
1842
|
+
}
|
|
1843
|
+
for (const identifier of extractDeclaredIdentifiers(declaration)) {
|
|
1844
|
+
recordScopedIdentifierRewrite(out.map, out.ambiguous, deriveScopedIdentifierAlias(identifier), identifier);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1701
1848
|
return out;
|
|
1702
1849
|
}
|
|
1703
1850
|
|
|
1851
|
+
function rewriteIdentifiersWithinExpression(expr, scopeMap, scopeAmbiguous) {
|
|
1852
|
+
const ts = loadTypeScriptApi();
|
|
1853
|
+
if (!(scopeMap instanceof Map) || !ts) {
|
|
1854
|
+
return expr;
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
const wrapped = `const __zenith_expr__ = (${expr});`;
|
|
1858
|
+
let sourceFile;
|
|
1859
|
+
try {
|
|
1860
|
+
sourceFile = ts.createSourceFile('zenith-expression.ts', wrapped, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
1861
|
+
} catch {
|
|
1862
|
+
return expr;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
const statement = sourceFile.statements[0];
|
|
1866
|
+
if (!statement || !ts.isVariableStatement(statement)) {
|
|
1867
|
+
return expr;
|
|
1868
|
+
}
|
|
1869
|
+
const initializer = statement.declarationList.declarations[0]?.initializer;
|
|
1870
|
+
const root = initializer && ts.isParenthesizedExpression(initializer) ? initializer.expression : initializer;
|
|
1871
|
+
if (!root) {
|
|
1872
|
+
return expr;
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
const replacements = [];
|
|
1876
|
+
const collectBoundNames = (name, target) => {
|
|
1877
|
+
if (ts.isIdentifier(name)) {
|
|
1878
|
+
target.add(name.text);
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
if (ts.isObjectBindingPattern(name) || ts.isArrayBindingPattern(name)) {
|
|
1882
|
+
for (const element of name.elements) {
|
|
1883
|
+
if (ts.isBindingElement(element)) {
|
|
1884
|
+
collectBoundNames(element.name, target);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
};
|
|
1889
|
+
const shouldSkipIdentifier = (node, localBindings) => {
|
|
1890
|
+
if (localBindings.has(node.text)) {
|
|
1891
|
+
return true;
|
|
1892
|
+
}
|
|
1893
|
+
const parent = node.parent;
|
|
1894
|
+
if (!parent) {
|
|
1895
|
+
return false;
|
|
1896
|
+
}
|
|
1897
|
+
if (ts.isPropertyAccessExpression(parent) && parent.name === node) {
|
|
1898
|
+
return true;
|
|
1899
|
+
}
|
|
1900
|
+
if (ts.isPropertyAssignment(parent) && parent.name === node) {
|
|
1901
|
+
return true;
|
|
1902
|
+
}
|
|
1903
|
+
if (ts.isShorthandPropertyAssignment(parent)) {
|
|
1904
|
+
return true;
|
|
1905
|
+
}
|
|
1906
|
+
if (ts.isBindingElement(parent) && parent.name === node) {
|
|
1907
|
+
return true;
|
|
1908
|
+
}
|
|
1909
|
+
if (ts.isParameter(parent) && parent.name === node) {
|
|
1910
|
+
return true;
|
|
1911
|
+
}
|
|
1912
|
+
return false;
|
|
1913
|
+
};
|
|
1914
|
+
const visit = (node, localBindings) => {
|
|
1915
|
+
let nextBindings = localBindings;
|
|
1916
|
+
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
|
|
1917
|
+
nextBindings = new Set(localBindings);
|
|
1918
|
+
if (node.name && ts.isIdentifier(node.name)) {
|
|
1919
|
+
nextBindings.add(node.name.text);
|
|
1920
|
+
}
|
|
1921
|
+
for (const param of node.parameters) {
|
|
1922
|
+
collectBoundNames(param.name, nextBindings);
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
if (ts.isIdentifier(node) && !shouldSkipIdentifier(node, nextBindings)) {
|
|
1927
|
+
const rewritten = scopeMap.get(node.text);
|
|
1928
|
+
if (
|
|
1929
|
+
typeof rewritten === 'string' &&
|
|
1930
|
+
rewritten.length > 0 &&
|
|
1931
|
+
rewritten !== node.text &&
|
|
1932
|
+
!(scopeAmbiguous instanceof Set && scopeAmbiguous.has(node.text))
|
|
1933
|
+
) {
|
|
1934
|
+
replacements.push({
|
|
1935
|
+
start: node.getStart(sourceFile),
|
|
1936
|
+
end: node.getEnd(),
|
|
1937
|
+
text: rewritten
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
ts.forEachChild(node, (child) => visit(child, nextBindings));
|
|
1943
|
+
};
|
|
1944
|
+
|
|
1945
|
+
visit(root, new Set());
|
|
1946
|
+
if (replacements.length === 0) {
|
|
1947
|
+
return expr;
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
let rewritten = wrapped;
|
|
1951
|
+
for (const replacement of replacements.sort((a, b) => b.start - a.start)) {
|
|
1952
|
+
rewritten = `${rewritten.slice(0, replacement.start)}${replacement.text}${rewritten.slice(replacement.end)}`;
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
const prefix = 'const __zenith_expr__ = (';
|
|
1956
|
+
const suffix = ');';
|
|
1957
|
+
if (!rewritten.startsWith(prefix) || !rewritten.endsWith(suffix)) {
|
|
1958
|
+
return expr;
|
|
1959
|
+
}
|
|
1960
|
+
return rewritten.slice(prefix.length, rewritten.length - suffix.length);
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1704
1963
|
/**
|
|
1705
1964
|
* @param {string} expr
|
|
1706
1965
|
* @param {{
|
|
@@ -1730,18 +1989,21 @@ function rewritePropsExpression(expr, rewriteContext = null) {
|
|
|
1730
1989
|
const scopeMap = rewriteContext?.scopeRewrite?.map;
|
|
1731
1990
|
const scopeAmbiguous = rewriteContext?.scopeRewrite?.ambiguous;
|
|
1732
1991
|
const rootMatch = trimmed.match(/^([A-Za-z_$][A-Za-z0-9_$]*)([\s\S]*)$/);
|
|
1733
|
-
if (!
|
|
1992
|
+
if (!(scopeMap instanceof Map)) {
|
|
1734
1993
|
return trimmed;
|
|
1735
1994
|
}
|
|
1995
|
+
if (!rootMatch) {
|
|
1996
|
+
return rewriteIdentifiersWithinExpression(trimmed, scopeMap, scopeAmbiguous);
|
|
1997
|
+
}
|
|
1736
1998
|
|
|
1737
1999
|
const root = rootMatch[1];
|
|
1738
2000
|
if (scopeAmbiguous instanceof Set && scopeAmbiguous.has(root)) {
|
|
1739
|
-
return trimmed;
|
|
2001
|
+
return rewriteIdentifiersWithinExpression(trimmed, scopeMap, scopeAmbiguous);
|
|
1740
2002
|
}
|
|
1741
2003
|
|
|
1742
2004
|
const rewrittenRoot = scopeMap.get(root);
|
|
1743
2005
|
if (typeof rewrittenRoot !== 'string' || rewrittenRoot.length === 0 || rewrittenRoot === root) {
|
|
1744
|
-
return trimmed;
|
|
2006
|
+
return rewriteIdentifiersWithinExpression(trimmed, scopeMap, scopeAmbiguous);
|
|
1745
2007
|
}
|
|
1746
2008
|
|
|
1747
2009
|
return `${rewrittenRoot}${rootMatch[2]}`;
|
|
@@ -1874,13 +2136,21 @@ function deferComponentRuntimeBlock(source) {
|
|
|
1874
2136
|
* @param {string} projectRoot
|
|
1875
2137
|
* @param {object | null} [logger]
|
|
1876
2138
|
* @param {boolean} [showInfo]
|
|
2139
|
+
* @param {string} [bundlerBin]
|
|
1877
2140
|
* @returns {Promise<void>}
|
|
1878
2141
|
*/
|
|
1879
|
-
function runBundler(
|
|
2142
|
+
function runBundler(
|
|
2143
|
+
envelope,
|
|
2144
|
+
outDir,
|
|
2145
|
+
projectRoot,
|
|
2146
|
+
logger = null,
|
|
2147
|
+
showInfo = true,
|
|
2148
|
+
bundlerBin = resolveBundlerBin(projectRoot)
|
|
2149
|
+
) {
|
|
1880
2150
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
1881
2151
|
const useStructuredLogger = Boolean(logger && typeof logger.childLine === 'function');
|
|
1882
2152
|
const child = spawn(
|
|
1883
|
-
|
|
2153
|
+
bundlerBin,
|
|
1884
2154
|
['--out-dir', outDir],
|
|
1885
2155
|
{
|
|
1886
2156
|
cwd: projectRoot,
|
|
@@ -1968,6 +2238,8 @@ async function collectAssets(rootDir) {
|
|
|
1968
2238
|
export async function build(options) {
|
|
1969
2239
|
const { pagesDir, outDir, config = {}, logger = null, showBundlerInfo = true } = options;
|
|
1970
2240
|
const projectRoot = deriveProjectRootFromPagesDir(pagesDir);
|
|
2241
|
+
const compilerBin = resolveCompilerBin(projectRoot);
|
|
2242
|
+
const bundlerBin = resolveBundlerBin(projectRoot);
|
|
1971
2243
|
const softNavigationEnabled = config.softNavigation === true || config.router === true;
|
|
1972
2244
|
const compilerOpts = {
|
|
1973
2245
|
typescriptDefault: config.typescriptDefault === true,
|
|
@@ -1978,6 +2250,15 @@ export async function build(options) {
|
|
|
1978
2250
|
await rm(outDir, { recursive: true, force: true });
|
|
1979
2251
|
await mkdir(outDir, { recursive: true });
|
|
1980
2252
|
|
|
2253
|
+
if (logger) {
|
|
2254
|
+
await maybeWarnAboutZenithVersionMismatch({
|
|
2255
|
+
projectRoot,
|
|
2256
|
+
logger,
|
|
2257
|
+
command: 'build',
|
|
2258
|
+
bundlerBinPath: bundlerBin
|
|
2259
|
+
});
|
|
2260
|
+
}
|
|
2261
|
+
|
|
1981
2262
|
// Derive src/ directory from pages/ directory
|
|
1982
2263
|
const srcDir = resolve(pagesDir, '..');
|
|
1983
2264
|
|
|
@@ -2015,7 +2296,7 @@ export async function build(options) {
|
|
|
2015
2296
|
for (const entry of manifest) {
|
|
2016
2297
|
const sourceFile = join(pagesDir, entry.file);
|
|
2017
2298
|
const rawSource = readFileSync(sourceFile, 'utf8');
|
|
2018
|
-
const
|
|
2299
|
+
const componentOccurrences = collectExpandedComponentOccurrences(rawSource, registry, sourceFile);
|
|
2019
2300
|
|
|
2020
2301
|
const baseName = sourceFile.slice(0, -extname(sourceFile).length);
|
|
2021
2302
|
let adjacentGuard = null;
|
|
@@ -2026,7 +2307,7 @@ export async function build(options) {
|
|
|
2026
2307
|
}
|
|
2027
2308
|
|
|
2028
2309
|
// 2a. Expand PascalCase component tags
|
|
2029
|
-
const { expandedSource
|
|
2310
|
+
const { expandedSource } = expandComponents(
|
|
2030
2311
|
rawSource, registry, sourceFile
|
|
2031
2312
|
);
|
|
2032
2313
|
const extractedServer = extractServerScript(expandedSource, sourceFile, compilerOpts);
|
|
@@ -2037,7 +2318,10 @@ export async function build(options) {
|
|
|
2037
2318
|
sourceFile,
|
|
2038
2319
|
compileSource,
|
|
2039
2320
|
compilerOpts,
|
|
2040
|
-
{
|
|
2321
|
+
{
|
|
2322
|
+
compilerBin,
|
|
2323
|
+
onWarning: emitCompilerWarning
|
|
2324
|
+
}
|
|
2041
2325
|
);
|
|
2042
2326
|
|
|
2043
2327
|
const hasGuard = (extractedServer.serverScript && extractedServer.serverScript.has_guard) || adjacentGuard !== null;
|
|
@@ -2077,19 +2361,42 @@ export async function build(options) {
|
|
|
2077
2361
|
pageIr.hoisted.state = pageIr.hoisted.state || [];
|
|
2078
2362
|
pageIr.hoisted.code = pageIr.hoisted.code || [];
|
|
2079
2363
|
const seenStaticImports = new Set();
|
|
2364
|
+
const occurrenceCountByPath = new Map();
|
|
2365
|
+
for (const occurrence of componentOccurrences) {
|
|
2366
|
+
const key = occurrence.componentPath || occurrence.name;
|
|
2367
|
+
occurrenceCountByPath.set(key, (occurrenceCountByPath.get(key) || 0) + 1);
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2080
2370
|
const pageExpressionRewriteMap = new Map();
|
|
2081
2371
|
const pageExpressionBindingMap = new Map();
|
|
2082
2372
|
const pageAmbiguousExpressionMap = new Set();
|
|
2083
2373
|
const knownRefKeys = new Set();
|
|
2374
|
+
const componentOccurrencePlans = [];
|
|
2084
2375
|
const pageScopeRewrite = buildScopedIdentifierRewrite(pageIr);
|
|
2085
|
-
const pageSelfExpressionRewrite = buildComponentExpressionRewrite(
|
|
2376
|
+
const pageSelfExpressionRewrite = buildComponentExpressionRewrite(
|
|
2377
|
+
sourceFile,
|
|
2378
|
+
compileSource,
|
|
2379
|
+
pageIr,
|
|
2380
|
+
compilerOpts,
|
|
2381
|
+
compilerBin
|
|
2382
|
+
);
|
|
2383
|
+
mergeExpressionRewriteMaps(
|
|
2384
|
+
pageExpressionRewriteMap,
|
|
2385
|
+
pageExpressionBindingMap,
|
|
2386
|
+
pageAmbiguousExpressionMap,
|
|
2387
|
+
pageSelfExpressionRewrite,
|
|
2388
|
+
pageIr
|
|
2389
|
+
);
|
|
2086
2390
|
const componentScopeRewriteCache = new Map();
|
|
2391
|
+
let componentInstanceCounter = 0;
|
|
2087
2392
|
|
|
2088
2393
|
// 2c. Compile each used component separately for its script IR
|
|
2089
|
-
for (const
|
|
2090
|
-
const
|
|
2394
|
+
for (const occurrence of componentOccurrences) {
|
|
2395
|
+
const compName = occurrence.name;
|
|
2396
|
+
const compPath = occurrence.componentPath || registry.get(compName);
|
|
2091
2397
|
if (!compPath) continue;
|
|
2092
2398
|
const componentSource = readFileSync(compPath, 'utf8');
|
|
2399
|
+
const occurrenceCount = occurrenceCountByPath.get(compPath) || 0;
|
|
2093
2400
|
|
|
2094
2401
|
let compIr;
|
|
2095
2402
|
if (componentIrCache.has(compPath)) {
|
|
@@ -2100,7 +2407,10 @@ export async function build(options) {
|
|
|
2100
2407
|
compPath,
|
|
2101
2408
|
componentCompileSource,
|
|
2102
2409
|
compilerOpts,
|
|
2103
|
-
{
|
|
2410
|
+
{
|
|
2411
|
+
compilerBin,
|
|
2412
|
+
onWarning: emitCompilerWarning
|
|
2413
|
+
}
|
|
2104
2414
|
);
|
|
2105
2415
|
componentIrCache.set(compPath, compIr);
|
|
2106
2416
|
}
|
|
@@ -2113,19 +2423,20 @@ export async function build(options) {
|
|
|
2113
2423
|
|
|
2114
2424
|
let expressionRewrite = componentExpressionRewriteCache.get(compPath);
|
|
2115
2425
|
if (!expressionRewrite) {
|
|
2116
|
-
expressionRewrite = buildComponentExpressionRewrite(
|
|
2426
|
+
expressionRewrite = buildComponentExpressionRewrite(
|
|
2427
|
+
compPath,
|
|
2428
|
+
componentSource,
|
|
2429
|
+
compIr,
|
|
2430
|
+
compilerOpts,
|
|
2431
|
+
compilerBin
|
|
2432
|
+
);
|
|
2117
2433
|
componentExpressionRewriteCache.set(compPath, expressionRewrite);
|
|
2118
2434
|
}
|
|
2119
2435
|
|
|
2120
|
-
let usageEntry = (componentUsageAttrs.get(compName) || [])[0] || { attrs: '', ownerPath: sourceFile };
|
|
2121
|
-
if (!usageEntry || typeof usageEntry !== 'object') {
|
|
2122
|
-
usageEntry = { attrs: '', ownerPath: sourceFile };
|
|
2123
|
-
}
|
|
2124
|
-
|
|
2125
2436
|
let attrExpressionRewrite = pageSelfExpressionRewrite;
|
|
2126
2437
|
let attrScopeRewrite = pageScopeRewrite;
|
|
2127
|
-
const ownerPath = typeof
|
|
2128
|
-
?
|
|
2438
|
+
const ownerPath = typeof occurrence.ownerPath === 'string' && occurrence.ownerPath.length > 0
|
|
2439
|
+
? occurrence.ownerPath
|
|
2129
2440
|
: sourceFile;
|
|
2130
2441
|
|
|
2131
2442
|
if (ownerPath !== sourceFile) {
|
|
@@ -2136,7 +2447,10 @@ export async function build(options) {
|
|
|
2136
2447
|
ownerPath,
|
|
2137
2448
|
stripStyleBlocks(ownerSource),
|
|
2138
2449
|
compilerOpts,
|
|
2139
|
-
{
|
|
2450
|
+
{
|
|
2451
|
+
compilerBin,
|
|
2452
|
+
onWarning: emitCompilerWarning
|
|
2453
|
+
}
|
|
2140
2454
|
);
|
|
2141
2455
|
componentIrCache.set(ownerPath, ownerIr);
|
|
2142
2456
|
}
|
|
@@ -2144,7 +2458,13 @@ export async function build(options) {
|
|
|
2144
2458
|
attrExpressionRewrite = componentExpressionRewriteCache.get(ownerPath);
|
|
2145
2459
|
if (!attrExpressionRewrite) {
|
|
2146
2460
|
const ownerSource = readFileSync(ownerPath, 'utf8');
|
|
2147
|
-
attrExpressionRewrite = buildComponentExpressionRewrite(
|
|
2461
|
+
attrExpressionRewrite = buildComponentExpressionRewrite(
|
|
2462
|
+
ownerPath,
|
|
2463
|
+
ownerSource,
|
|
2464
|
+
ownerIr,
|
|
2465
|
+
compilerOpts,
|
|
2466
|
+
compilerBin
|
|
2467
|
+
);
|
|
2148
2468
|
componentExpressionRewriteCache.set(ownerPath, attrExpressionRewrite);
|
|
2149
2469
|
}
|
|
2150
2470
|
|
|
@@ -2155,17 +2475,36 @@ export async function build(options) {
|
|
|
2155
2475
|
}
|
|
2156
2476
|
}
|
|
2157
2477
|
|
|
2478
|
+
const useIsolatedInstance = occurrenceCount > 1;
|
|
2479
|
+
const { ir: instanceIr, refIdentifierPairs } = useIsolatedInstance
|
|
2480
|
+
? cloneComponentIrForInstance(
|
|
2481
|
+
compIr,
|
|
2482
|
+
componentInstanceCounter++,
|
|
2483
|
+
extractDeclaredIdentifiers,
|
|
2484
|
+
resolveStateKeyFromBindings
|
|
2485
|
+
)
|
|
2486
|
+
: { ir: compIr, refIdentifierPairs: [] };
|
|
2487
|
+
const instanceRewrite = useIsolatedInstance
|
|
2488
|
+
? buildComponentExpressionRewrite(
|
|
2489
|
+
compPath,
|
|
2490
|
+
componentSource,
|
|
2491
|
+
instanceIr,
|
|
2492
|
+
compilerOpts,
|
|
2493
|
+
compilerBin
|
|
2494
|
+
)
|
|
2495
|
+
: expressionRewrite;
|
|
2496
|
+
|
|
2158
2497
|
// 2d. Merge component IR into page IR
|
|
2159
2498
|
mergeComponentIr(
|
|
2160
2499
|
pageIr,
|
|
2161
|
-
|
|
2500
|
+
instanceIr,
|
|
2162
2501
|
compPath,
|
|
2163
2502
|
sourceFile,
|
|
2164
2503
|
{
|
|
2165
2504
|
includeCode: true,
|
|
2166
2505
|
cssImportsOnly: isDocMode,
|
|
2167
2506
|
documentMode: isDocMode,
|
|
2168
|
-
componentAttrs: typeof
|
|
2507
|
+
componentAttrs: typeof occurrence.attrs === 'string' ? occurrence.attrs : '',
|
|
2169
2508
|
componentAttrsRewrite: {
|
|
2170
2509
|
expressionRewrite: attrExpressionRewrite,
|
|
2171
2510
|
scopeRewrite: attrScopeRewrite
|
|
@@ -2175,21 +2514,36 @@ export async function build(options) {
|
|
|
2175
2514
|
knownRefKeys
|
|
2176
2515
|
);
|
|
2177
2516
|
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2517
|
+
if (useIsolatedInstance) {
|
|
2518
|
+
componentOccurrencePlans.push({
|
|
2519
|
+
rewrite: instanceRewrite,
|
|
2520
|
+
expressionSequence: instanceRewrite.sequence,
|
|
2521
|
+
refSequence: refIdentifierPairs
|
|
2522
|
+
});
|
|
2523
|
+
} else {
|
|
2524
|
+
mergeExpressionRewriteMaps(
|
|
2525
|
+
pageExpressionRewriteMap,
|
|
2526
|
+
pageExpressionBindingMap,
|
|
2527
|
+
pageAmbiguousExpressionMap,
|
|
2528
|
+
expressionRewrite,
|
|
2529
|
+
pageIr
|
|
2530
|
+
);
|
|
2531
|
+
}
|
|
2185
2532
|
}
|
|
2186
2533
|
|
|
2534
|
+
applyOccurrenceRewritePlans(
|
|
2535
|
+
pageIr,
|
|
2536
|
+
componentOccurrencePlans,
|
|
2537
|
+
(rewrite, binding) => resolveRewrittenBindingMetadata(pageIr, rewrite, binding)
|
|
2538
|
+
);
|
|
2187
2539
|
applyExpressionRewrites(
|
|
2188
2540
|
pageIr,
|
|
2189
2541
|
pageExpressionRewriteMap,
|
|
2190
2542
|
pageExpressionBindingMap,
|
|
2191
2543
|
pageAmbiguousExpressionMap
|
|
2192
2544
|
);
|
|
2545
|
+
applyScopedIdentifierRewrites(pageIr, buildScopedIdentifierRewrite(pageIr));
|
|
2546
|
+
synthesizeSignalBackedCompiledExpressions(pageIr);
|
|
2193
2547
|
normalizeExpressionBindingDependencies(pageIr);
|
|
2194
2548
|
|
|
2195
2549
|
rewriteLegacyMarkupIdentifiers(pageIr);
|
|
@@ -2204,7 +2558,7 @@ export async function build(options) {
|
|
|
2204
2558
|
}
|
|
2205
2559
|
|
|
2206
2560
|
if (envelopes.length > 0) {
|
|
2207
|
-
|
|
2561
|
+
await runBundler(envelopes, outDir, projectRoot, logger, showBundlerInfo, bundlerBin);
|
|
2208
2562
|
}
|
|
2209
2563
|
|
|
2210
2564
|
const assets = await collectAssets(outDir);
|