@zenithbuild/cli 0.6.2 → 0.6.4
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 +972 -85
- 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,11 @@ 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 { resolveBundlerBin, resolveCompilerBin } from './toolchain-paths.js';
|
|
22
|
+
import { maybeWarnAboutZenithVersionMismatch } from './version-check.js';
|
|
22
23
|
|
|
23
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
-
const __dirname = dirname(__filename);
|
|
25
|
-
const CLI_ROOT = resolve(__dirname, '..');
|
|
26
24
|
const require = createRequire(import.meta.url);
|
|
27
25
|
let cachedTypeScript = undefined;
|
|
28
26
|
|
|
@@ -40,40 +38,6 @@ function loadTypeScriptApi() {
|
|
|
40
38
|
return cachedTypeScript;
|
|
41
39
|
}
|
|
42
40
|
|
|
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
41
|
/**
|
|
78
42
|
* Build a per-build warning emitter that deduplicates repeated compiler lines.
|
|
79
43
|
*
|
|
@@ -133,9 +97,11 @@ function forwardStreamLines(stream, onLine) {
|
|
|
133
97
|
* @param {object} compilerRunOptions
|
|
134
98
|
* @param {(warning: string) => void} [compilerRunOptions.onWarning]
|
|
135
99
|
* @param {boolean} [compilerRunOptions.suppressWarnings]
|
|
100
|
+
* @param {string} [compilerRunOptions.compilerBin]
|
|
136
101
|
* @returns {object}
|
|
137
102
|
*/
|
|
138
103
|
function runCompiler(filePath, stdinSource, compilerOpts = {}, compilerRunOptions = {}) {
|
|
104
|
+
const compilerBin = compilerRunOptions.compilerBin || resolveCompilerBin();
|
|
139
105
|
const args = stdinSource !== undefined
|
|
140
106
|
? ['--stdin', filePath]
|
|
141
107
|
: [filePath];
|
|
@@ -150,7 +116,7 @@ function runCompiler(filePath, stdinSource, compilerOpts = {}, compilerRunOption
|
|
|
150
116
|
opts.input = stdinSource;
|
|
151
117
|
}
|
|
152
118
|
|
|
153
|
-
const result = spawnSync(
|
|
119
|
+
const result = spawnSync(compilerBin, args, opts);
|
|
154
120
|
|
|
155
121
|
if (result.error) {
|
|
156
122
|
throw new Error(`Compiler spawn failed for ${filePath}: ${result.error.message}`);
|
|
@@ -200,11 +166,33 @@ function stripStyleBlocks(source) {
|
|
|
200
166
|
* @param {string} compPath
|
|
201
167
|
* @param {string} componentSource
|
|
202
168
|
* @param {object} compIr
|
|
203
|
-
* @
|
|
169
|
+
* @param {object} compilerOpts
|
|
170
|
+
* @param {string} compilerBin
|
|
171
|
+
* @returns {{
|
|
172
|
+
* map: Map<string, string>,
|
|
173
|
+
* bindings: Map<string, {
|
|
174
|
+
* compiled_expr: string | null,
|
|
175
|
+
* signal_index: number | null,
|
|
176
|
+
* signal_indices: number[],
|
|
177
|
+
* state_index: number | null,
|
|
178
|
+
* component_instance: string | null,
|
|
179
|
+
* component_binding: string | null
|
|
180
|
+
* }>,
|
|
181
|
+
* signals: Array<{ id?: number, kind?: string, state_index?: number }>,
|
|
182
|
+
* stateBindings: Array<{ key?: string, value?: string }>,
|
|
183
|
+
* ambiguous: Set<string>
|
|
184
|
+
* }}
|
|
204
185
|
*/
|
|
205
|
-
function buildComponentExpressionRewrite(compPath, componentSource, compIr, compilerOpts) {
|
|
206
|
-
const out = {
|
|
186
|
+
function buildComponentExpressionRewrite(compPath, componentSource, compIr, compilerOpts, compilerBin) {
|
|
187
|
+
const out = {
|
|
188
|
+
map: new Map(),
|
|
189
|
+
bindings: new Map(),
|
|
190
|
+
signals: Array.isArray(compIr?.signals) ? compIr.signals : [],
|
|
191
|
+
stateBindings: Array.isArray(compIr?.hoisted?.state) ? compIr.hoisted.state : [],
|
|
192
|
+
ambiguous: new Set()
|
|
193
|
+
};
|
|
207
194
|
const rewrittenExpressions = Array.isArray(compIr?.expressions) ? compIr.expressions : [];
|
|
195
|
+
const rewrittenBindings = Array.isArray(compIr?.expression_bindings) ? compIr.expression_bindings : [];
|
|
208
196
|
if (rewrittenExpressions.length === 0) {
|
|
209
197
|
return out;
|
|
210
198
|
}
|
|
@@ -216,7 +204,10 @@ function buildComponentExpressionRewrite(compPath, componentSource, compIr, comp
|
|
|
216
204
|
|
|
217
205
|
let templateIr;
|
|
218
206
|
try {
|
|
219
|
-
templateIr = runCompiler(compPath, templateOnly, compilerOpts, {
|
|
207
|
+
templateIr = runCompiler(compPath, templateOnly, compilerOpts, {
|
|
208
|
+
suppressWarnings: true,
|
|
209
|
+
compilerBin
|
|
210
|
+
});
|
|
220
211
|
} catch {
|
|
221
212
|
return out;
|
|
222
213
|
}
|
|
@@ -229,34 +220,243 @@ function buildComponentExpressionRewrite(compPath, componentSource, compIr, comp
|
|
|
229
220
|
if (typeof raw !== 'string' || typeof rewritten !== 'string') {
|
|
230
221
|
continue;
|
|
231
222
|
}
|
|
232
|
-
|
|
233
|
-
|
|
223
|
+
|
|
224
|
+
const binding = rewrittenBindings[i];
|
|
225
|
+
const normalizedBinding = binding && typeof binding === 'object'
|
|
226
|
+
? {
|
|
227
|
+
compiled_expr: typeof binding.compiled_expr === 'string' ? binding.compiled_expr : null,
|
|
228
|
+
signal_index: Number.isInteger(binding.signal_index) ? binding.signal_index : null,
|
|
229
|
+
signal_indices: Array.isArray(binding.signal_indices)
|
|
230
|
+
? binding.signal_indices.filter((value) => Number.isInteger(value))
|
|
231
|
+
: [],
|
|
232
|
+
state_index: Number.isInteger(binding.state_index) ? binding.state_index : null,
|
|
233
|
+
component_instance: typeof binding.component_instance === 'string' ? binding.component_instance : null,
|
|
234
|
+
component_binding: typeof binding.component_binding === 'string' ? binding.component_binding : null
|
|
235
|
+
}
|
|
236
|
+
: null;
|
|
237
|
+
|
|
238
|
+
if (!out.ambiguous.has(raw) && normalizedBinding) {
|
|
239
|
+
const existingBinding = out.bindings.get(raw);
|
|
240
|
+
if (existingBinding) {
|
|
241
|
+
if (JSON.stringify(existingBinding) !== JSON.stringify(normalizedBinding)) {
|
|
242
|
+
out.bindings.delete(raw);
|
|
243
|
+
out.map.delete(raw);
|
|
244
|
+
out.ambiguous.add(raw);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
out.bindings.set(raw, normalizedBinding);
|
|
249
|
+
}
|
|
234
250
|
}
|
|
235
|
-
|
|
236
|
-
if (
|
|
237
|
-
out.map.
|
|
238
|
-
|
|
251
|
+
|
|
252
|
+
if (raw !== rewritten) {
|
|
253
|
+
const existing = out.map.get(raw);
|
|
254
|
+
if (existing && existing !== rewritten) {
|
|
255
|
+
out.bindings.delete(raw);
|
|
256
|
+
out.map.delete(raw);
|
|
257
|
+
out.ambiguous.add(raw);
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (!out.ambiguous.has(raw)) {
|
|
261
|
+
out.map.set(raw, rewritten);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return out;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function remapCompiledExpressionSignals(compiledExpr, componentSignals, componentStateBindings, pageSignalIndexByStateKey) {
|
|
270
|
+
if (typeof compiledExpr !== 'string' || compiledExpr.length === 0) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return compiledExpr.replace(/signalMap\.get\((\d+)\)/g, (full, rawIndex) => {
|
|
275
|
+
const localIndex = Number.parseInt(rawIndex, 10);
|
|
276
|
+
if (!Number.isInteger(localIndex)) {
|
|
277
|
+
return full;
|
|
278
|
+
}
|
|
279
|
+
const signal = componentSignals[localIndex];
|
|
280
|
+
if (!signal || !Number.isInteger(signal.state_index)) {
|
|
281
|
+
return full;
|
|
282
|
+
}
|
|
283
|
+
const stateKey = componentStateBindings[signal.state_index]?.key;
|
|
284
|
+
if (typeof stateKey !== 'string' || stateKey.length === 0) {
|
|
285
|
+
return full;
|
|
286
|
+
}
|
|
287
|
+
const pageIndex = pageSignalIndexByStateKey.get(stateKey);
|
|
288
|
+
if (!Number.isInteger(pageIndex)) {
|
|
289
|
+
return full;
|
|
290
|
+
}
|
|
291
|
+
return `signalMap.get(${pageIndex})`;
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function resolveRewrittenBindingMetadata(pageIr, componentRewrite, binding) {
|
|
296
|
+
if (!binding || typeof binding !== 'object') {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const pageStateBindings = Array.isArray(pageIr?.hoisted?.state) ? pageIr.hoisted.state : [];
|
|
301
|
+
const pageSignals = Array.isArray(pageIr?.signals) ? pageIr.signals : [];
|
|
302
|
+
const pageStateIndexByKey = new Map();
|
|
303
|
+
const pageSignalIndexByStateKey = new Map();
|
|
304
|
+
|
|
305
|
+
for (let index = 0; index < pageStateBindings.length; index++) {
|
|
306
|
+
const key = pageStateBindings[index]?.key;
|
|
307
|
+
if (typeof key === 'string' && key.length > 0 && !pageStateIndexByKey.has(key)) {
|
|
308
|
+
pageStateIndexByKey.set(key, index);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
for (let index = 0; index < pageSignals.length; index++) {
|
|
313
|
+
const stateIndex = pageSignals[index]?.state_index;
|
|
314
|
+
if (!Number.isInteger(stateIndex)) {
|
|
239
315
|
continue;
|
|
240
316
|
}
|
|
241
|
-
|
|
242
|
-
|
|
317
|
+
const stateKey = pageStateBindings[stateIndex]?.key;
|
|
318
|
+
if (typeof stateKey === 'string' && stateKey.length > 0 && !pageSignalIndexByStateKey.has(stateKey)) {
|
|
319
|
+
pageSignalIndexByStateKey.set(stateKey, index);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const componentSignals = Array.isArray(componentRewrite?.signals) ? componentRewrite.signals : [];
|
|
324
|
+
const componentStateBindings = Array.isArray(componentRewrite?.stateBindings) ? componentRewrite.stateBindings : [];
|
|
325
|
+
|
|
326
|
+
let signalIndices = Array.isArray(binding.signal_indices)
|
|
327
|
+
? [...new Set(
|
|
328
|
+
binding.signal_indices
|
|
329
|
+
.map((signalIndex) => {
|
|
330
|
+
if (!Number.isInteger(signalIndex)) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
const signal = componentSignals[signalIndex];
|
|
334
|
+
if (!signal || !Number.isInteger(signal.state_index)) {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
const stateKey = componentStateBindings[signal.state_index]?.key;
|
|
338
|
+
if (typeof stateKey !== 'string' || stateKey.length === 0) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
const pageIndex = pageSignalIndexByStateKey.get(stateKey);
|
|
342
|
+
return Number.isInteger(pageIndex) ? pageIndex : null;
|
|
343
|
+
})
|
|
344
|
+
.filter((value) => Number.isInteger(value))
|
|
345
|
+
)].sort((a, b) => a - b)
|
|
346
|
+
: [];
|
|
347
|
+
|
|
348
|
+
let signalIndex = null;
|
|
349
|
+
if (Number.isInteger(binding.signal_index)) {
|
|
350
|
+
const signal = componentSignals[binding.signal_index];
|
|
351
|
+
const stateKey = signal && Number.isInteger(signal.state_index)
|
|
352
|
+
? componentStateBindings[signal.state_index]?.key
|
|
353
|
+
: null;
|
|
354
|
+
const pageIndex = typeof stateKey === 'string' ? pageSignalIndexByStateKey.get(stateKey) : null;
|
|
355
|
+
signalIndex = Number.isInteger(pageIndex) ? pageIndex : null;
|
|
356
|
+
}
|
|
357
|
+
if (signalIndex === null && signalIndices.length === 1) {
|
|
358
|
+
signalIndex = signalIndices[0];
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let stateIndex = null;
|
|
362
|
+
if (Number.isInteger(binding.state_index)) {
|
|
363
|
+
const stateKey = componentStateBindings[binding.state_index]?.key;
|
|
364
|
+
const pageIndex = typeof stateKey === 'string' ? pageStateIndexByKey.get(stateKey) : null;
|
|
365
|
+
stateIndex = Number.isInteger(pageIndex) ? pageIndex : null;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (Number.isInteger(stateIndex)) {
|
|
369
|
+
const fallbackSignalIndices = pageSignals
|
|
370
|
+
.map((signal, index) => signal?.state_index === stateIndex ? index : null)
|
|
371
|
+
.filter((value) => Number.isInteger(value));
|
|
372
|
+
const signalIndicesMatchState = signalIndices.every(
|
|
373
|
+
(index) => pageSignals[index]?.state_index === stateIndex
|
|
374
|
+
);
|
|
375
|
+
if ((!signalIndicesMatchState || signalIndices.length === 0) && fallbackSignalIndices.length > 0) {
|
|
376
|
+
signalIndices = fallbackSignalIndices;
|
|
377
|
+
}
|
|
378
|
+
if (
|
|
379
|
+
(signalIndex === null || pageSignals[signalIndex]?.state_index !== stateIndex) &&
|
|
380
|
+
fallbackSignalIndices.length === 1
|
|
381
|
+
) {
|
|
382
|
+
signalIndex = fallbackSignalIndices[0];
|
|
243
383
|
}
|
|
244
384
|
}
|
|
245
385
|
|
|
246
|
-
|
|
386
|
+
let compiledExpr = remapCompiledExpressionSignals(
|
|
387
|
+
binding.compiled_expr,
|
|
388
|
+
componentSignals,
|
|
389
|
+
componentStateBindings,
|
|
390
|
+
pageSignalIndexByStateKey
|
|
391
|
+
);
|
|
392
|
+
if (
|
|
393
|
+
typeof compiledExpr === 'string' &&
|
|
394
|
+
signalIndices.length === 1 &&
|
|
395
|
+
Array.isArray(binding.signal_indices) &&
|
|
396
|
+
binding.signal_indices.length <= 1
|
|
397
|
+
) {
|
|
398
|
+
compiledExpr = compiledExpr.replace(/signalMap\.get\(\d+\)/g, `signalMap.get(${signalIndices[0]})`);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return {
|
|
402
|
+
compiled_expr: compiledExpr,
|
|
403
|
+
signal_index: signalIndex,
|
|
404
|
+
signal_indices: signalIndices,
|
|
405
|
+
state_index: stateIndex,
|
|
406
|
+
component_instance: typeof binding.component_instance === 'string' ? binding.component_instance : null,
|
|
407
|
+
component_binding: typeof binding.component_binding === 'string' ? binding.component_binding : null
|
|
408
|
+
};
|
|
247
409
|
}
|
|
248
410
|
|
|
249
411
|
/**
|
|
250
412
|
* Merge a per-component rewrite table into the page-level rewrite table.
|
|
251
413
|
*
|
|
252
414
|
* @param {Map<string, string>} pageMap
|
|
415
|
+
* @param {Map<string, {
|
|
416
|
+
* compiled_expr: string | null,
|
|
417
|
+
* signal_index: number | null,
|
|
418
|
+
* signal_indices: number[],
|
|
419
|
+
* state_index: number | null,
|
|
420
|
+
* component_instance: string | null,
|
|
421
|
+
* component_binding: string | null
|
|
422
|
+
* }>} pageBindingMap
|
|
253
423
|
* @param {Set<string>} pageAmbiguous
|
|
254
|
-
* @param {{
|
|
424
|
+
* @param {{
|
|
425
|
+
* map: Map<string, string>,
|
|
426
|
+
* bindings: Map<string, {
|
|
427
|
+
* compiled_expr: string | null,
|
|
428
|
+
* signal_index: number | null,
|
|
429
|
+
* signal_indices: number[],
|
|
430
|
+
* state_index: number | null,
|
|
431
|
+
* component_instance: string | null,
|
|
432
|
+
* component_binding: string | null
|
|
433
|
+
* }>,
|
|
434
|
+
* signals: Array<{ id?: number, kind?: string, state_index?: number }>,
|
|
435
|
+
* stateBindings: Array<{ key?: string, value?: string }>,
|
|
436
|
+
* ambiguous: Set<string>
|
|
437
|
+
* }} componentRewrite
|
|
438
|
+
* @param {object} pageIr
|
|
255
439
|
*/
|
|
256
|
-
function mergeExpressionRewriteMaps(pageMap, pageAmbiguous, componentRewrite) {
|
|
440
|
+
function mergeExpressionRewriteMaps(pageMap, pageBindingMap, pageAmbiguous, componentRewrite, pageIr) {
|
|
257
441
|
for (const raw of componentRewrite.ambiguous) {
|
|
258
442
|
pageAmbiguous.add(raw);
|
|
259
443
|
pageMap.delete(raw);
|
|
444
|
+
pageBindingMap.delete(raw);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
for (const [raw, binding] of componentRewrite.bindings.entries()) {
|
|
448
|
+
if (pageAmbiguous.has(raw)) {
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
const resolved = resolveRewrittenBindingMetadata(pageIr, componentRewrite, binding);
|
|
452
|
+
const existing = pageBindingMap.get(raw);
|
|
453
|
+
if (existing && JSON.stringify(existing) !== JSON.stringify(resolved)) {
|
|
454
|
+
pageAmbiguous.add(raw);
|
|
455
|
+
pageMap.delete(raw);
|
|
456
|
+
pageBindingMap.delete(raw);
|
|
457
|
+
continue;
|
|
458
|
+
}
|
|
459
|
+
pageBindingMap.set(raw, resolved);
|
|
260
460
|
}
|
|
261
461
|
|
|
262
462
|
for (const [raw, rewritten] of componentRewrite.map.entries()) {
|
|
@@ -267,6 +467,7 @@ function mergeExpressionRewriteMaps(pageMap, pageAmbiguous, componentRewrite) {
|
|
|
267
467
|
if (existing && existing !== rewritten) {
|
|
268
468
|
pageAmbiguous.add(raw);
|
|
269
469
|
pageMap.delete(raw);
|
|
470
|
+
pageBindingMap.delete(raw);
|
|
270
471
|
continue;
|
|
271
472
|
}
|
|
272
473
|
pageMap.set(raw, rewritten);
|
|
@@ -329,9 +530,17 @@ function rewriteRefBindingIdentifiers(pageIr, preferredKeys = null) {
|
|
|
329
530
|
*
|
|
330
531
|
* @param {object} pageIr
|
|
331
532
|
* @param {Map<string, string>} expressionMap
|
|
533
|
+
* @param {Map<string, {
|
|
534
|
+
* compiled_expr: string | null,
|
|
535
|
+
* signal_index: number | null,
|
|
536
|
+
* signal_indices: number[],
|
|
537
|
+
* state_index: number | null,
|
|
538
|
+
* component_instance: string | null,
|
|
539
|
+
* component_binding: string | null
|
|
540
|
+
* }>} bindingMap
|
|
332
541
|
* @param {Set<string>} ambiguous
|
|
333
542
|
*/
|
|
334
|
-
function applyExpressionRewrites(pageIr, expressionMap, ambiguous) {
|
|
543
|
+
function applyExpressionRewrites(pageIr, expressionMap, bindingMap, ambiguous) {
|
|
335
544
|
if (!Array.isArray(pageIr?.expressions) || pageIr.expressions.length === 0) {
|
|
336
545
|
return;
|
|
337
546
|
}
|
|
@@ -345,24 +554,213 @@ function applyExpressionRewrites(pageIr, expressionMap, ambiguous) {
|
|
|
345
554
|
if (ambiguous.has(current)) {
|
|
346
555
|
continue;
|
|
347
556
|
}
|
|
557
|
+
|
|
348
558
|
const rewritten = expressionMap.get(current);
|
|
349
|
-
|
|
559
|
+
const rewrittenBinding = bindingMap.get(current);
|
|
560
|
+
if (rewritten && rewritten !== current) {
|
|
561
|
+
pageIr.expressions[index] = rewritten;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if (!bindings[index] || typeof bindings[index] !== 'object') {
|
|
350
565
|
continue;
|
|
351
566
|
}
|
|
352
|
-
|
|
567
|
+
|
|
568
|
+
if (rewritten && rewritten !== current && bindings[index].literal === current) {
|
|
569
|
+
bindings[index].literal = rewritten;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (rewrittenBinding) {
|
|
573
|
+
bindings[index].compiled_expr = rewrittenBinding.compiled_expr;
|
|
574
|
+
bindings[index].signal_index = rewrittenBinding.signal_index;
|
|
575
|
+
bindings[index].signal_indices = rewrittenBinding.signal_indices;
|
|
576
|
+
bindings[index].state_index = rewrittenBinding.state_index;
|
|
577
|
+
bindings[index].component_instance = rewrittenBinding.component_instance;
|
|
578
|
+
bindings[index].component_binding = rewrittenBinding.component_binding;
|
|
579
|
+
} else if (rewritten && rewritten !== current && bindings[index].compiled_expr === current) {
|
|
580
|
+
bindings[index].compiled_expr = rewritten;
|
|
581
|
+
}
|
|
582
|
+
|
|
353
583
|
if (
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
bindings[index].literal === current
|
|
584
|
+
!rewrittenBinding &&
|
|
585
|
+
(!rewritten || rewritten === current) &&
|
|
586
|
+
bindings[index].literal === current &&
|
|
587
|
+
bindings[index].compiled_expr === current
|
|
357
588
|
) {
|
|
358
|
-
bindings[index].
|
|
359
|
-
|
|
360
|
-
|
|
589
|
+
bindings[index].compiled_expr = current;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function applyScopedIdentifierRewrites(pageIr, scopeRewrite) {
|
|
595
|
+
if (!Array.isArray(pageIr?.expressions) || pageIr.expressions.length === 0) {
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
const bindings = Array.isArray(pageIr.expression_bindings) ? pageIr.expression_bindings : [];
|
|
599
|
+
const rewriteContext = {
|
|
600
|
+
scopeRewrite
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
for (let index = 0; index < pageIr.expressions.length; index++) {
|
|
604
|
+
const current = pageIr.expressions[index];
|
|
605
|
+
if (typeof current === 'string') {
|
|
606
|
+
pageIr.expressions[index] = rewritePropsExpression(current, rewriteContext);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (!bindings[index] || typeof bindings[index] !== 'object') {
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (typeof bindings[index].literal === 'string') {
|
|
614
|
+
bindings[index].literal = rewritePropsExpression(bindings[index].literal, rewriteContext);
|
|
615
|
+
}
|
|
616
|
+
if (typeof bindings[index].compiled_expr === 'string') {
|
|
617
|
+
bindings[index].compiled_expr = rewritePropsExpression(bindings[index].compiled_expr, rewriteContext);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function synthesizeSignalBackedCompiledExpressions(pageIr) {
|
|
623
|
+
if (!Array.isArray(pageIr?.expression_bindings) || pageIr.expression_bindings.length === 0) {
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
const stateBindings = Array.isArray(pageIr?.hoisted?.state) ? pageIr.hoisted.state : [];
|
|
628
|
+
const signals = Array.isArray(pageIr?.signals) ? pageIr.signals : [];
|
|
629
|
+
if (stateBindings.length === 0 || signals.length === 0) {
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const signalIndexByStateKey = new Map();
|
|
634
|
+
for (let index = 0; index < signals.length; index++) {
|
|
635
|
+
const stateIndex = signals[index]?.state_index;
|
|
636
|
+
const stateKey = Number.isInteger(stateIndex) ? stateBindings[stateIndex]?.key : null;
|
|
637
|
+
if (typeof stateKey === 'string' && stateKey.length > 0) {
|
|
638
|
+
signalIndexByStateKey.set(stateKey, index);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (signalIndexByStateKey.size === 0) {
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
for (let index = 0; index < pageIr.expression_bindings.length; index++) {
|
|
646
|
+
const binding = pageIr.expression_bindings[index];
|
|
647
|
+
if (!binding || typeof binding !== 'object') {
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
if (typeof binding.compiled_expr === 'string' && binding.compiled_expr.includes('signalMap.get(')) {
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const candidate = typeof binding.literal === 'string' && binding.literal.trim().length > 0
|
|
655
|
+
? binding.literal
|
|
656
|
+
: typeof pageIr.expressions?.[index] === 'string'
|
|
657
|
+
? pageIr.expressions[index]
|
|
658
|
+
: null;
|
|
659
|
+
if (typeof candidate !== 'string' || candidate.trim().length === 0) {
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
let rewritten = candidate;
|
|
664
|
+
const signalIndices = [];
|
|
665
|
+
for (const [stateKey, signalIndex] of signalIndexByStateKey.entries()) {
|
|
666
|
+
if (!rewritten.includes(stateKey)) {
|
|
667
|
+
continue;
|
|
668
|
+
}
|
|
669
|
+
const escaped = stateKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
670
|
+
const pattern = new RegExp(`\\b${escaped}\\b`, 'g');
|
|
671
|
+
if (!pattern.test(rewritten)) {
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
rewritten = rewritten.replace(pattern, `signalMap.get(${signalIndex}).get()`);
|
|
675
|
+
signalIndices.push(signalIndex);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (rewritten === candidate || signalIndices.length === 0) {
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const uniqueSignalIndices = [...new Set(signalIndices)].sort((a, b) => a - b);
|
|
683
|
+
binding.compiled_expr = rewritten;
|
|
684
|
+
binding.signal_indices = uniqueSignalIndices;
|
|
685
|
+
if (uniqueSignalIndices.length === 1) {
|
|
686
|
+
binding.signal_index = uniqueSignalIndices[0];
|
|
687
|
+
const stateIndex = signals[uniqueSignalIndices[0]]?.state_index;
|
|
688
|
+
if (Number.isInteger(stateIndex)) {
|
|
689
|
+
binding.state_index = stateIndex;
|
|
361
690
|
}
|
|
362
691
|
}
|
|
363
692
|
}
|
|
364
693
|
}
|
|
365
694
|
|
|
695
|
+
function normalizeExpressionBindingDependencies(pageIr) {
|
|
696
|
+
if (!Array.isArray(pageIr?.expression_bindings) || pageIr.expression_bindings.length === 0) {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const signals = Array.isArray(pageIr.signals) ? pageIr.signals : [];
|
|
701
|
+
const dependencyRe = /signalMap\.get\((\d+)\)/g;
|
|
702
|
+
|
|
703
|
+
for (const binding of pageIr.expression_bindings) {
|
|
704
|
+
if (!binding || typeof binding !== 'object' || typeof binding.compiled_expr !== 'string') {
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
const indices = [];
|
|
709
|
+
dependencyRe.lastIndex = 0;
|
|
710
|
+
let match;
|
|
711
|
+
while ((match = dependencyRe.exec(binding.compiled_expr)) !== null) {
|
|
712
|
+
const index = Number.parseInt(match[1], 10);
|
|
713
|
+
if (Number.isInteger(index)) {
|
|
714
|
+
indices.push(index);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
if (indices.length === 0) {
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
let signalIndices = [...new Set(indices)].sort((a, b) => a - b);
|
|
723
|
+
if (Number.isInteger(binding.state_index)) {
|
|
724
|
+
const owningSignalIndices = signals
|
|
725
|
+
.map((signal, index) => signal?.state_index === binding.state_index ? index : null)
|
|
726
|
+
.filter((value) => Number.isInteger(value));
|
|
727
|
+
const extractedMatchState =
|
|
728
|
+
signalIndices.length > 0 &&
|
|
729
|
+
signalIndices.every((index) => signals[index]?.state_index === binding.state_index);
|
|
730
|
+
if (owningSignalIndices.length > 0 && !extractedMatchState) {
|
|
731
|
+
signalIndices = owningSignalIndices;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
if (
|
|
736
|
+
!Array.isArray(binding.signal_indices) ||
|
|
737
|
+
binding.signal_indices.length === 0 ||
|
|
738
|
+
binding.signal_indices.some((index) => signals[index]?.state_index !== binding.state_index)
|
|
739
|
+
) {
|
|
740
|
+
binding.signal_indices = signalIndices;
|
|
741
|
+
}
|
|
742
|
+
if (
|
|
743
|
+
(!Number.isInteger(binding.signal_index) ||
|
|
744
|
+
signals[binding.signal_index]?.state_index !== binding.state_index) &&
|
|
745
|
+
signalIndices.length === 1
|
|
746
|
+
) {
|
|
747
|
+
binding.signal_index = signalIndices[0];
|
|
748
|
+
}
|
|
749
|
+
if (!Number.isInteger(binding.state_index) && Number.isInteger(binding.signal_index)) {
|
|
750
|
+
const stateIndex = signals[binding.signal_index]?.state_index;
|
|
751
|
+
if (Number.isInteger(stateIndex)) {
|
|
752
|
+
binding.state_index = stateIndex;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
if (signalIndices.length === 1) {
|
|
756
|
+
binding.compiled_expr = binding.compiled_expr.replace(
|
|
757
|
+
/signalMap\.get\(\d+\)/g,
|
|
758
|
+
`signalMap.get(${signalIndices[0]})`
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
366
764
|
/**
|
|
367
765
|
* Rewrite legacy markup-literal identifiers in expression literals to the
|
|
368
766
|
* internal `__ZENITH_INTERNAL_ZENHTML` binding used by the runtime.
|
|
@@ -798,9 +1196,10 @@ const OPEN_COMPONENT_TAG_RE = /<([A-Z][a-zA-Z0-9]*)(\s[^<>]*?)?\s*(\/?)>/g;
|
|
|
798
1196
|
*
|
|
799
1197
|
* @param {string} source
|
|
800
1198
|
* @param {Map<string, string>} registry
|
|
801
|
-
* @
|
|
1199
|
+
* @param {string | null} ownerPath
|
|
1200
|
+
* @returns {Map<string, Array<{ attrs: string, ownerPath: string | null }>>}
|
|
802
1201
|
*/
|
|
803
|
-
function collectComponentUsageAttrs(source, registry) {
|
|
1202
|
+
function collectComponentUsageAttrs(source, registry, ownerPath = null) {
|
|
804
1203
|
const out = new Map();
|
|
805
1204
|
OPEN_COMPONENT_TAG_RE.lastIndex = 0;
|
|
806
1205
|
let match;
|
|
@@ -813,11 +1212,47 @@ function collectComponentUsageAttrs(source, registry) {
|
|
|
813
1212
|
if (!out.has(name)) {
|
|
814
1213
|
out.set(name, []);
|
|
815
1214
|
}
|
|
816
|
-
out.get(name).push(attrs);
|
|
1215
|
+
out.get(name).push({ attrs, ownerPath });
|
|
817
1216
|
}
|
|
818
1217
|
return out;
|
|
819
1218
|
}
|
|
820
1219
|
|
|
1220
|
+
/**
|
|
1221
|
+
* Collect component usage attrs recursively so nested component callsites
|
|
1222
|
+
* receive deterministic props preludes during page-hoist merging.
|
|
1223
|
+
*
|
|
1224
|
+
* Current Zenith architecture still resolves one attrs set per component type.
|
|
1225
|
+
* This helper preserves that model while ensuring nested usages are not lost.
|
|
1226
|
+
*
|
|
1227
|
+
* @param {string} source
|
|
1228
|
+
* @param {Map<string, string>} registry
|
|
1229
|
+
* @param {string | null} ownerPath
|
|
1230
|
+
* @param {Set<string>} visitedFiles
|
|
1231
|
+
* @param {Map<string, Array<{ attrs: string, ownerPath: string | null }>>} out
|
|
1232
|
+
* @returns {Map<string, Array<{ attrs: string, ownerPath: string | null }>>}
|
|
1233
|
+
*/
|
|
1234
|
+
function collectRecursiveComponentUsageAttrs(source, registry, ownerPath = null, visitedFiles = new Set(), out = new Map()) {
|
|
1235
|
+
const local = collectComponentUsageAttrs(source, registry, ownerPath);
|
|
1236
|
+
for (const [name, attrsList] of local.entries()) {
|
|
1237
|
+
if (!out.has(name)) {
|
|
1238
|
+
out.set(name, []);
|
|
1239
|
+
}
|
|
1240
|
+
out.get(name).push(...attrsList);
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
for (const name of local.keys()) {
|
|
1244
|
+
const compPath = registry.get(name);
|
|
1245
|
+
if (!compPath || visitedFiles.has(compPath)) {
|
|
1246
|
+
continue;
|
|
1247
|
+
}
|
|
1248
|
+
visitedFiles.add(compPath);
|
|
1249
|
+
const componentSource = readFileSync(compPath, 'utf8');
|
|
1250
|
+
collectRecursiveComponentUsageAttrs(componentSource, registry, compPath, visitedFiles, out);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
return out;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
821
1256
|
/**
|
|
822
1257
|
* Merge a component's IR into the page IR.
|
|
823
1258
|
*
|
|
@@ -828,7 +1263,16 @@ function collectComponentUsageAttrs(source, registry) {
|
|
|
828
1263
|
* @param {object} compIr — the component's compiled IR
|
|
829
1264
|
* @param {string} compPath — component file path
|
|
830
1265
|
* @param {string} pageFile — page file path
|
|
831
|
-
* @param {{
|
|
1266
|
+
* @param {{
|
|
1267
|
+
* includeCode: boolean,
|
|
1268
|
+
* cssImportsOnly: boolean,
|
|
1269
|
+
* documentMode?: boolean,
|
|
1270
|
+
* componentAttrs?: string,
|
|
1271
|
+
* componentAttrsRewrite?: {
|
|
1272
|
+
* expressionRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null,
|
|
1273
|
+
* scopeRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null
|
|
1274
|
+
* } | null
|
|
1275
|
+
* }} options
|
|
832
1276
|
* @param {Set<string>} seenStaticImports
|
|
833
1277
|
*/
|
|
834
1278
|
function mergeComponentIr(pageIr, compIr, compPath, pageFile, options, seenStaticImports, knownRefKeys = null) {
|
|
@@ -920,6 +1364,41 @@ function mergeComponentIr(pageIr, compIr, compPath, pageFile, options, seenStati
|
|
|
920
1364
|
}
|
|
921
1365
|
}
|
|
922
1366
|
|
|
1367
|
+
if (options.includeCode && Array.isArray(compIr.signals)) {
|
|
1368
|
+
pageIr.signals = Array.isArray(pageIr.signals) ? pageIr.signals : [];
|
|
1369
|
+
const existingSignalStateKeys = new Set(
|
|
1370
|
+
pageIr.signals
|
|
1371
|
+
.map((signal) => {
|
|
1372
|
+
const stateIndex = signal?.state_index;
|
|
1373
|
+
return Number.isInteger(stateIndex) ? pageIr.hoisted.state?.[stateIndex]?.key : null;
|
|
1374
|
+
})
|
|
1375
|
+
.filter(Boolean)
|
|
1376
|
+
);
|
|
1377
|
+
|
|
1378
|
+
for (const signal of compIr.signals) {
|
|
1379
|
+
if (!signal || !Number.isInteger(signal.state_index)) {
|
|
1380
|
+
continue;
|
|
1381
|
+
}
|
|
1382
|
+
const stateKey = compIr.hoisted?.state?.[signal.state_index]?.key;
|
|
1383
|
+
if (typeof stateKey !== 'string' || stateKey.length === 0) {
|
|
1384
|
+
continue;
|
|
1385
|
+
}
|
|
1386
|
+
const pageStateIndex = pageIr.hoisted.state.findIndex((entry) => entry?.key === stateKey);
|
|
1387
|
+
if (!Number.isInteger(pageStateIndex) || pageStateIndex < 0) {
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
if (existingSignalStateKeys.has(stateKey)) {
|
|
1391
|
+
continue;
|
|
1392
|
+
}
|
|
1393
|
+
existingSignalStateKeys.add(stateKey);
|
|
1394
|
+
pageIr.signals.push({
|
|
1395
|
+
id: pageIr.signals.length,
|
|
1396
|
+
kind: typeof signal.kind === 'string' && signal.kind.length > 0 ? signal.kind : 'signal',
|
|
1397
|
+
state_index: pageStateIndex
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
|
|
923
1402
|
// Merge hoisted code blocks (rebased to the page file path)
|
|
924
1403
|
if (options.includeCode && compIr.hoisted?.code?.length) {
|
|
925
1404
|
for (const block of compIr.hoisted.code) {
|
|
@@ -927,7 +1406,11 @@ function mergeComponentIr(pageIr, compIr, compPath, pageFile, options, seenStati
|
|
|
927
1406
|
const filteredImports = options.cssImportsOnly
|
|
928
1407
|
? stripNonCssStaticImportsInSource(rebased)
|
|
929
1408
|
: rebased;
|
|
930
|
-
const withPropsPrelude = injectPropsPrelude(
|
|
1409
|
+
const withPropsPrelude = injectPropsPrelude(
|
|
1410
|
+
filteredImports,
|
|
1411
|
+
options.componentAttrs || '',
|
|
1412
|
+
options.componentAttrsRewrite || null
|
|
1413
|
+
);
|
|
931
1414
|
const transpiled = transpileTypeScriptToJs(withPropsPrelude, compPath);
|
|
932
1415
|
const deduped = dedupeStaticImportsInSource(transpiled, seenStaticImports);
|
|
933
1416
|
const deferred = deferComponentRuntimeBlock(deduped);
|
|
@@ -1228,11 +1711,304 @@ function renderObjectKey(key) {
|
|
|
1228
1711
|
return JSON.stringify(key);
|
|
1229
1712
|
}
|
|
1230
1713
|
|
|
1714
|
+
/**
|
|
1715
|
+
* @param {string} value
|
|
1716
|
+
* @returns {string | null}
|
|
1717
|
+
*/
|
|
1718
|
+
function deriveScopedIdentifierAlias(value) {
|
|
1719
|
+
const ident = String(value || '').trim();
|
|
1720
|
+
if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(ident)) {
|
|
1721
|
+
return null;
|
|
1722
|
+
}
|
|
1723
|
+
const parts = ident.split('_').filter(Boolean);
|
|
1724
|
+
const candidate = parts.length > 1 ? parts[parts.length - 1] : ident;
|
|
1725
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(candidate) ? candidate : ident;
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
/**
|
|
1729
|
+
* @param {string} source
|
|
1730
|
+
* @returns {string[]}
|
|
1731
|
+
*/
|
|
1732
|
+
function extractDeclaredIdentifiers(source) {
|
|
1733
|
+
const text = String(source || '').trim();
|
|
1734
|
+
if (!text) {
|
|
1735
|
+
return [];
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
const ts = loadTypeScriptApi();
|
|
1739
|
+
if (ts) {
|
|
1740
|
+
const sourceFile = ts.createSourceFile('zenith-hoisted-declaration.ts', text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
1741
|
+
const identifiers = [];
|
|
1742
|
+
const collectBindingNames = (name) => {
|
|
1743
|
+
if (ts.isIdentifier(name)) {
|
|
1744
|
+
identifiers.push(name.text);
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1747
|
+
if (ts.isObjectBindingPattern(name) || ts.isArrayBindingPattern(name)) {
|
|
1748
|
+
for (const element of name.elements) {
|
|
1749
|
+
if (ts.isBindingElement(element)) {
|
|
1750
|
+
collectBindingNames(element.name);
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
};
|
|
1755
|
+
|
|
1756
|
+
for (const statement of sourceFile.statements) {
|
|
1757
|
+
if (!ts.isVariableStatement(statement)) {
|
|
1758
|
+
continue;
|
|
1759
|
+
}
|
|
1760
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
1761
|
+
collectBindingNames(declaration.name);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
if (identifiers.length > 0) {
|
|
1766
|
+
return identifiers;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
const fallback = [];
|
|
1771
|
+
const match = text.match(/^\s*(?:const|let|var)\s+([\s\S]+?);?\s*$/);
|
|
1772
|
+
if (!match) {
|
|
1773
|
+
return fallback;
|
|
1774
|
+
}
|
|
1775
|
+
const declarationList = match[1];
|
|
1776
|
+
const identifierRe = /(?:^|,)\s*([A-Za-z_$][A-Za-z0-9_$]*)\s*(?::[^=,]+)?=/g;
|
|
1777
|
+
let found;
|
|
1778
|
+
while ((found = identifierRe.exec(declarationList)) !== null) {
|
|
1779
|
+
fallback.push(found[1]);
|
|
1780
|
+
}
|
|
1781
|
+
return fallback;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
/**
|
|
1785
|
+
* @param {Map<string, string>} map
|
|
1786
|
+
* @param {Set<string>} ambiguous
|
|
1787
|
+
* @param {string | null} raw
|
|
1788
|
+
* @param {string | null} rewritten
|
|
1789
|
+
*/
|
|
1790
|
+
function recordScopedIdentifierRewrite(map, ambiguous, raw, rewritten) {
|
|
1791
|
+
if (typeof raw !== 'string' || raw.length === 0 || typeof rewritten !== 'string' || rewritten.length === 0) {
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
const existing = map.get(raw);
|
|
1795
|
+
if (existing && existing !== rewritten) {
|
|
1796
|
+
map.delete(raw);
|
|
1797
|
+
ambiguous.add(raw);
|
|
1798
|
+
return;
|
|
1799
|
+
}
|
|
1800
|
+
if (!ambiguous.has(raw)) {
|
|
1801
|
+
map.set(raw, rewritten);
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
/**
|
|
1806
|
+
* @param {object | null | undefined} ir
|
|
1807
|
+
* @returns {{ map: Map<string, string>, ambiguous: Set<string> }}
|
|
1808
|
+
*/
|
|
1809
|
+
function buildScopedIdentifierRewrite(ir) {
|
|
1810
|
+
const out = { map: new Map(), ambiguous: new Set() };
|
|
1811
|
+
if (!ir || typeof ir !== 'object') {
|
|
1812
|
+
return out;
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
const stateBindings = Array.isArray(ir?.hoisted?.state) ? ir.hoisted.state : [];
|
|
1816
|
+
for (const stateEntry of stateBindings) {
|
|
1817
|
+
const key = typeof stateEntry?.key === 'string' ? stateEntry.key : null;
|
|
1818
|
+
recordScopedIdentifierRewrite(out.map, out.ambiguous, deriveScopedIdentifierAlias(key), key);
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
const functionBindings = Array.isArray(ir?.hoisted?.functions) ? ir.hoisted.functions : [];
|
|
1822
|
+
for (const fnName of functionBindings) {
|
|
1823
|
+
if (typeof fnName !== 'string') {
|
|
1824
|
+
continue;
|
|
1825
|
+
}
|
|
1826
|
+
recordScopedIdentifierRewrite(out.map, out.ambiguous, deriveScopedIdentifierAlias(fnName), fnName);
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
const declarations = Array.isArray(ir?.hoisted?.declarations) ? ir.hoisted.declarations : [];
|
|
1830
|
+
for (const declaration of declarations) {
|
|
1831
|
+
if (typeof declaration !== 'string') {
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1834
|
+
for (const identifier of extractDeclaredIdentifiers(declaration)) {
|
|
1835
|
+
recordScopedIdentifierRewrite(out.map, out.ambiguous, deriveScopedIdentifierAlias(identifier), identifier);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
return out;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
function rewriteIdentifiersWithinExpression(expr, scopeMap, scopeAmbiguous) {
|
|
1843
|
+
const ts = loadTypeScriptApi();
|
|
1844
|
+
if (!(scopeMap instanceof Map) || !ts) {
|
|
1845
|
+
return expr;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
const wrapped = `const __zenith_expr__ = (${expr});`;
|
|
1849
|
+
let sourceFile;
|
|
1850
|
+
try {
|
|
1851
|
+
sourceFile = ts.createSourceFile('zenith-expression.ts', wrapped, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
1852
|
+
} catch {
|
|
1853
|
+
return expr;
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
const statement = sourceFile.statements[0];
|
|
1857
|
+
if (!statement || !ts.isVariableStatement(statement)) {
|
|
1858
|
+
return expr;
|
|
1859
|
+
}
|
|
1860
|
+
const initializer = statement.declarationList.declarations[0]?.initializer;
|
|
1861
|
+
const root = initializer && ts.isParenthesizedExpression(initializer) ? initializer.expression : initializer;
|
|
1862
|
+
if (!root) {
|
|
1863
|
+
return expr;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
const replacements = [];
|
|
1867
|
+
const collectBoundNames = (name, target) => {
|
|
1868
|
+
if (ts.isIdentifier(name)) {
|
|
1869
|
+
target.add(name.text);
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
if (ts.isObjectBindingPattern(name) || ts.isArrayBindingPattern(name)) {
|
|
1873
|
+
for (const element of name.elements) {
|
|
1874
|
+
if (ts.isBindingElement(element)) {
|
|
1875
|
+
collectBoundNames(element.name, target);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
};
|
|
1880
|
+
const shouldSkipIdentifier = (node, localBindings) => {
|
|
1881
|
+
if (localBindings.has(node.text)) {
|
|
1882
|
+
return true;
|
|
1883
|
+
}
|
|
1884
|
+
const parent = node.parent;
|
|
1885
|
+
if (!parent) {
|
|
1886
|
+
return false;
|
|
1887
|
+
}
|
|
1888
|
+
if (ts.isPropertyAccessExpression(parent) && parent.name === node) {
|
|
1889
|
+
return true;
|
|
1890
|
+
}
|
|
1891
|
+
if (ts.isPropertyAssignment(parent) && parent.name === node) {
|
|
1892
|
+
return true;
|
|
1893
|
+
}
|
|
1894
|
+
if (ts.isShorthandPropertyAssignment(parent)) {
|
|
1895
|
+
return true;
|
|
1896
|
+
}
|
|
1897
|
+
if (ts.isBindingElement(parent) && parent.name === node) {
|
|
1898
|
+
return true;
|
|
1899
|
+
}
|
|
1900
|
+
if (ts.isParameter(parent) && parent.name === node) {
|
|
1901
|
+
return true;
|
|
1902
|
+
}
|
|
1903
|
+
return false;
|
|
1904
|
+
};
|
|
1905
|
+
const visit = (node, localBindings) => {
|
|
1906
|
+
let nextBindings = localBindings;
|
|
1907
|
+
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
|
|
1908
|
+
nextBindings = new Set(localBindings);
|
|
1909
|
+
if (node.name && ts.isIdentifier(node.name)) {
|
|
1910
|
+
nextBindings.add(node.name.text);
|
|
1911
|
+
}
|
|
1912
|
+
for (const param of node.parameters) {
|
|
1913
|
+
collectBoundNames(param.name, nextBindings);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
if (ts.isIdentifier(node) && !shouldSkipIdentifier(node, nextBindings)) {
|
|
1918
|
+
const rewritten = scopeMap.get(node.text);
|
|
1919
|
+
if (
|
|
1920
|
+
typeof rewritten === 'string' &&
|
|
1921
|
+
rewritten.length > 0 &&
|
|
1922
|
+
rewritten !== node.text &&
|
|
1923
|
+
!(scopeAmbiguous instanceof Set && scopeAmbiguous.has(node.text))
|
|
1924
|
+
) {
|
|
1925
|
+
replacements.push({
|
|
1926
|
+
start: node.getStart(sourceFile),
|
|
1927
|
+
end: node.getEnd(),
|
|
1928
|
+
text: rewritten
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
ts.forEachChild(node, (child) => visit(child, nextBindings));
|
|
1934
|
+
};
|
|
1935
|
+
|
|
1936
|
+
visit(root, new Set());
|
|
1937
|
+
if (replacements.length === 0) {
|
|
1938
|
+
return expr;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
let rewritten = wrapped;
|
|
1942
|
+
for (const replacement of replacements.sort((a, b) => b.start - a.start)) {
|
|
1943
|
+
rewritten = `${rewritten.slice(0, replacement.start)}${replacement.text}${rewritten.slice(replacement.end)}`;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
const prefix = 'const __zenith_expr__ = (';
|
|
1947
|
+
const suffix = ');';
|
|
1948
|
+
if (!rewritten.startsWith(prefix) || !rewritten.endsWith(suffix)) {
|
|
1949
|
+
return expr;
|
|
1950
|
+
}
|
|
1951
|
+
return rewritten.slice(prefix.length, rewritten.length - suffix.length);
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
/**
|
|
1955
|
+
* @param {string} expr
|
|
1956
|
+
* @param {{
|
|
1957
|
+
* expressionRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null,
|
|
1958
|
+
* scopeRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null
|
|
1959
|
+
* } | null} rewriteContext
|
|
1960
|
+
* @returns {string}
|
|
1961
|
+
*/
|
|
1962
|
+
function rewritePropsExpression(expr, rewriteContext = null) {
|
|
1963
|
+
const trimmed = String(expr || '').trim();
|
|
1964
|
+
if (!trimmed) {
|
|
1965
|
+
return trimmed;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
const expressionMap = rewriteContext?.expressionRewrite?.map;
|
|
1969
|
+
const expressionAmbiguous = rewriteContext?.expressionRewrite?.ambiguous;
|
|
1970
|
+
if (
|
|
1971
|
+
expressionMap instanceof Map &&
|
|
1972
|
+
!(expressionAmbiguous instanceof Set && expressionAmbiguous.has(trimmed))
|
|
1973
|
+
) {
|
|
1974
|
+
const exact = expressionMap.get(trimmed);
|
|
1975
|
+
if (typeof exact === 'string' && exact.length > 0) {
|
|
1976
|
+
return exact;
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
const scopeMap = rewriteContext?.scopeRewrite?.map;
|
|
1981
|
+
const scopeAmbiguous = rewriteContext?.scopeRewrite?.ambiguous;
|
|
1982
|
+
const rootMatch = trimmed.match(/^([A-Za-z_$][A-Za-z0-9_$]*)([\s\S]*)$/);
|
|
1983
|
+
if (!(scopeMap instanceof Map)) {
|
|
1984
|
+
return trimmed;
|
|
1985
|
+
}
|
|
1986
|
+
if (!rootMatch) {
|
|
1987
|
+
return rewriteIdentifiersWithinExpression(trimmed, scopeMap, scopeAmbiguous);
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
const root = rootMatch[1];
|
|
1991
|
+
if (scopeAmbiguous instanceof Set && scopeAmbiguous.has(root)) {
|
|
1992
|
+
return rewriteIdentifiersWithinExpression(trimmed, scopeMap, scopeAmbiguous);
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
const rewrittenRoot = scopeMap.get(root);
|
|
1996
|
+
if (typeof rewrittenRoot !== 'string' || rewrittenRoot.length === 0 || rewrittenRoot === root) {
|
|
1997
|
+
return rewriteIdentifiersWithinExpression(trimmed, scopeMap, scopeAmbiguous);
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
return `${rewrittenRoot}${rootMatch[2]}`;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
1231
2003
|
/**
|
|
1232
2004
|
* @param {string} attrs
|
|
2005
|
+
* @param {{
|
|
2006
|
+
* expressionRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null,
|
|
2007
|
+
* scopeRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null
|
|
2008
|
+
* } | null} rewriteContext
|
|
1233
2009
|
* @returns {string}
|
|
1234
2010
|
*/
|
|
1235
|
-
function renderPropsLiteralFromAttrs(attrs) {
|
|
2011
|
+
function renderPropsLiteralFromAttrs(attrs, rewriteContext = null) {
|
|
1236
2012
|
const src = String(attrs || '').trim();
|
|
1237
2013
|
if (!src) {
|
|
1238
2014
|
return '{}';
|
|
@@ -1257,7 +2033,7 @@ function renderPropsLiteralFromAttrs(attrs) {
|
|
|
1257
2033
|
valueCode = JSON.stringify(singleQuoted);
|
|
1258
2034
|
} else if (expressionValue !== undefined) {
|
|
1259
2035
|
const trimmed = String(expressionValue).trim();
|
|
1260
|
-
valueCode = trimmed.length > 0 ? trimmed : 'undefined';
|
|
2036
|
+
valueCode = trimmed.length > 0 ? rewritePropsExpression(trimmed, rewriteContext) : 'undefined';
|
|
1261
2037
|
}
|
|
1262
2038
|
|
|
1263
2039
|
entries.push(`${renderObjectKey(rawName)}: ${valueCode}`);
|
|
@@ -1273,9 +2049,13 @@ function renderPropsLiteralFromAttrs(attrs) {
|
|
|
1273
2049
|
/**
|
|
1274
2050
|
* @param {string} source
|
|
1275
2051
|
* @param {string} attrs
|
|
2052
|
+
* @param {{
|
|
2053
|
+
* expressionRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null,
|
|
2054
|
+
* scopeRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null
|
|
2055
|
+
* } | null} rewriteContext
|
|
1276
2056
|
* @returns {string}
|
|
1277
2057
|
*/
|
|
1278
|
-
function injectPropsPrelude(source, attrs) {
|
|
2058
|
+
function injectPropsPrelude(source, attrs, rewriteContext = null) {
|
|
1279
2059
|
if (typeof source !== 'string' || source.trim().length === 0) {
|
|
1280
2060
|
return source;
|
|
1281
2061
|
}
|
|
@@ -1286,7 +2066,7 @@ function injectPropsPrelude(source, attrs) {
|
|
|
1286
2066
|
return source;
|
|
1287
2067
|
}
|
|
1288
2068
|
|
|
1289
|
-
const propsLiteral = renderPropsLiteralFromAttrs(attrs);
|
|
2069
|
+
const propsLiteral = renderPropsLiteralFromAttrs(attrs, rewriteContext);
|
|
1290
2070
|
return `var props = ${propsLiteral};\n${source}`;
|
|
1291
2071
|
}
|
|
1292
2072
|
|
|
@@ -1347,13 +2127,21 @@ function deferComponentRuntimeBlock(source) {
|
|
|
1347
2127
|
* @param {string} projectRoot
|
|
1348
2128
|
* @param {object | null} [logger]
|
|
1349
2129
|
* @param {boolean} [showInfo]
|
|
2130
|
+
* @param {string} [bundlerBin]
|
|
1350
2131
|
* @returns {Promise<void>}
|
|
1351
2132
|
*/
|
|
1352
|
-
function runBundler(
|
|
2133
|
+
function runBundler(
|
|
2134
|
+
envelope,
|
|
2135
|
+
outDir,
|
|
2136
|
+
projectRoot,
|
|
2137
|
+
logger = null,
|
|
2138
|
+
showInfo = true,
|
|
2139
|
+
bundlerBin = resolveBundlerBin(projectRoot)
|
|
2140
|
+
) {
|
|
1353
2141
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
1354
2142
|
const useStructuredLogger = Boolean(logger && typeof logger.childLine === 'function');
|
|
1355
2143
|
const child = spawn(
|
|
1356
|
-
|
|
2144
|
+
bundlerBin,
|
|
1357
2145
|
['--out-dir', outDir],
|
|
1358
2146
|
{
|
|
1359
2147
|
cwd: projectRoot,
|
|
@@ -1441,6 +2229,8 @@ async function collectAssets(rootDir) {
|
|
|
1441
2229
|
export async function build(options) {
|
|
1442
2230
|
const { pagesDir, outDir, config = {}, logger = null, showBundlerInfo = true } = options;
|
|
1443
2231
|
const projectRoot = deriveProjectRootFromPagesDir(pagesDir);
|
|
2232
|
+
const compilerBin = resolveCompilerBin(projectRoot);
|
|
2233
|
+
const bundlerBin = resolveBundlerBin(projectRoot);
|
|
1444
2234
|
const softNavigationEnabled = config.softNavigation === true || config.router === true;
|
|
1445
2235
|
const compilerOpts = {
|
|
1446
2236
|
typescriptDefault: config.typescriptDefault === true,
|
|
@@ -1451,6 +2241,15 @@ export async function build(options) {
|
|
|
1451
2241
|
await rm(outDir, { recursive: true, force: true });
|
|
1452
2242
|
await mkdir(outDir, { recursive: true });
|
|
1453
2243
|
|
|
2244
|
+
if (logger) {
|
|
2245
|
+
await maybeWarnAboutZenithVersionMismatch({
|
|
2246
|
+
projectRoot,
|
|
2247
|
+
logger,
|
|
2248
|
+
command: 'build',
|
|
2249
|
+
bundlerBinPath: bundlerBin
|
|
2250
|
+
});
|
|
2251
|
+
}
|
|
2252
|
+
|
|
1454
2253
|
// Derive src/ directory from pages/ directory
|
|
1455
2254
|
const srcDir = resolve(pagesDir, '..');
|
|
1456
2255
|
|
|
@@ -1488,7 +2287,7 @@ export async function build(options) {
|
|
|
1488
2287
|
for (const entry of manifest) {
|
|
1489
2288
|
const sourceFile = join(pagesDir, entry.file);
|
|
1490
2289
|
const rawSource = readFileSync(sourceFile, 'utf8');
|
|
1491
|
-
const componentUsageAttrs =
|
|
2290
|
+
const componentUsageAttrs = collectRecursiveComponentUsageAttrs(rawSource, registry, sourceFile);
|
|
1492
2291
|
|
|
1493
2292
|
const baseName = sourceFile.slice(0, -extname(sourceFile).length);
|
|
1494
2293
|
let adjacentGuard = null;
|
|
@@ -1510,7 +2309,10 @@ export async function build(options) {
|
|
|
1510
2309
|
sourceFile,
|
|
1511
2310
|
compileSource,
|
|
1512
2311
|
compilerOpts,
|
|
1513
|
-
{
|
|
2312
|
+
{
|
|
2313
|
+
compilerBin,
|
|
2314
|
+
onWarning: emitCompilerWarning
|
|
2315
|
+
}
|
|
1514
2316
|
);
|
|
1515
2317
|
|
|
1516
2318
|
const hasGuard = (extractedServer.serverScript && extractedServer.serverScript.has_guard) || adjacentGuard !== null;
|
|
@@ -1541,6 +2343,7 @@ export async function build(options) {
|
|
|
1541
2343
|
// Ensure IR has required array fields for merging
|
|
1542
2344
|
pageIr.components_scripts = pageIr.components_scripts || {};
|
|
1543
2345
|
pageIr.component_instances = pageIr.component_instances || [];
|
|
2346
|
+
pageIr.signals = Array.isArray(pageIr.signals) ? pageIr.signals : [];
|
|
1544
2347
|
pageIr.hoisted = pageIr.hoisted || { imports: [], declarations: [], functions: [], signals: [], state: [], code: [] };
|
|
1545
2348
|
pageIr.hoisted.imports = pageIr.hoisted.imports || [];
|
|
1546
2349
|
pageIr.hoisted.declarations = pageIr.hoisted.declarations || [];
|
|
@@ -1550,8 +2353,25 @@ export async function build(options) {
|
|
|
1550
2353
|
pageIr.hoisted.code = pageIr.hoisted.code || [];
|
|
1551
2354
|
const seenStaticImports = new Set();
|
|
1552
2355
|
const pageExpressionRewriteMap = new Map();
|
|
2356
|
+
const pageExpressionBindingMap = new Map();
|
|
1553
2357
|
const pageAmbiguousExpressionMap = new Set();
|
|
1554
2358
|
const knownRefKeys = new Set();
|
|
2359
|
+
const pageScopeRewrite = buildScopedIdentifierRewrite(pageIr);
|
|
2360
|
+
const pageSelfExpressionRewrite = buildComponentExpressionRewrite(
|
|
2361
|
+
sourceFile,
|
|
2362
|
+
compileSource,
|
|
2363
|
+
pageIr,
|
|
2364
|
+
compilerOpts,
|
|
2365
|
+
compilerBin
|
|
2366
|
+
);
|
|
2367
|
+
mergeExpressionRewriteMaps(
|
|
2368
|
+
pageExpressionRewriteMap,
|
|
2369
|
+
pageExpressionBindingMap,
|
|
2370
|
+
pageAmbiguousExpressionMap,
|
|
2371
|
+
pageSelfExpressionRewrite,
|
|
2372
|
+
pageIr
|
|
2373
|
+
);
|
|
2374
|
+
const componentScopeRewriteCache = new Map();
|
|
1555
2375
|
|
|
1556
2376
|
// 2c. Compile each used component separately for its script IR
|
|
1557
2377
|
for (const compName of usedComponents) {
|
|
@@ -1568,7 +2388,10 @@ export async function build(options) {
|
|
|
1568
2388
|
compPath,
|
|
1569
2389
|
componentCompileSource,
|
|
1570
2390
|
compilerOpts,
|
|
1571
|
-
{
|
|
2391
|
+
{
|
|
2392
|
+
compilerBin,
|
|
2393
|
+
onWarning: emitCompilerWarning
|
|
2394
|
+
}
|
|
1572
2395
|
);
|
|
1573
2396
|
componentIrCache.set(compPath, compIr);
|
|
1574
2397
|
}
|
|
@@ -1581,14 +2404,62 @@ export async function build(options) {
|
|
|
1581
2404
|
|
|
1582
2405
|
let expressionRewrite = componentExpressionRewriteCache.get(compPath);
|
|
1583
2406
|
if (!expressionRewrite) {
|
|
1584
|
-
expressionRewrite = buildComponentExpressionRewrite(
|
|
2407
|
+
expressionRewrite = buildComponentExpressionRewrite(
|
|
2408
|
+
compPath,
|
|
2409
|
+
componentSource,
|
|
2410
|
+
compIr,
|
|
2411
|
+
compilerOpts,
|
|
2412
|
+
compilerBin
|
|
2413
|
+
);
|
|
1585
2414
|
componentExpressionRewriteCache.set(compPath, expressionRewrite);
|
|
1586
2415
|
}
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
2416
|
+
|
|
2417
|
+
let usageEntry = (componentUsageAttrs.get(compName) || [])[0] || { attrs: '', ownerPath: sourceFile };
|
|
2418
|
+
if (!usageEntry || typeof usageEntry !== 'object') {
|
|
2419
|
+
usageEntry = { attrs: '', ownerPath: sourceFile };
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
let attrExpressionRewrite = pageSelfExpressionRewrite;
|
|
2423
|
+
let attrScopeRewrite = pageScopeRewrite;
|
|
2424
|
+
const ownerPath = typeof usageEntry.ownerPath === 'string' && usageEntry.ownerPath.length > 0
|
|
2425
|
+
? usageEntry.ownerPath
|
|
2426
|
+
: sourceFile;
|
|
2427
|
+
|
|
2428
|
+
if (ownerPath !== sourceFile) {
|
|
2429
|
+
let ownerIr = componentIrCache.get(ownerPath);
|
|
2430
|
+
if (!ownerIr) {
|
|
2431
|
+
const ownerSource = readFileSync(ownerPath, 'utf8');
|
|
2432
|
+
ownerIr = runCompiler(
|
|
2433
|
+
ownerPath,
|
|
2434
|
+
stripStyleBlocks(ownerSource),
|
|
2435
|
+
compilerOpts,
|
|
2436
|
+
{
|
|
2437
|
+
compilerBin,
|
|
2438
|
+
onWarning: emitCompilerWarning
|
|
2439
|
+
}
|
|
2440
|
+
);
|
|
2441
|
+
componentIrCache.set(ownerPath, ownerIr);
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
attrExpressionRewrite = componentExpressionRewriteCache.get(ownerPath);
|
|
2445
|
+
if (!attrExpressionRewrite) {
|
|
2446
|
+
const ownerSource = readFileSync(ownerPath, 'utf8');
|
|
2447
|
+
attrExpressionRewrite = buildComponentExpressionRewrite(
|
|
2448
|
+
ownerPath,
|
|
2449
|
+
ownerSource,
|
|
2450
|
+
ownerIr,
|
|
2451
|
+
compilerOpts,
|
|
2452
|
+
compilerBin
|
|
2453
|
+
);
|
|
2454
|
+
componentExpressionRewriteCache.set(ownerPath, attrExpressionRewrite);
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
attrScopeRewrite = componentScopeRewriteCache.get(ownerPath);
|
|
2458
|
+
if (!attrScopeRewrite) {
|
|
2459
|
+
attrScopeRewrite = buildScopedIdentifierRewrite(ownerIr);
|
|
2460
|
+
componentScopeRewriteCache.set(ownerPath, attrScopeRewrite);
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
1592
2463
|
|
|
1593
2464
|
// 2d. Merge component IR into page IR
|
|
1594
2465
|
mergeComponentIr(
|
|
@@ -1600,18 +2471,34 @@ export async function build(options) {
|
|
|
1600
2471
|
includeCode: true,
|
|
1601
2472
|
cssImportsOnly: isDocMode,
|
|
1602
2473
|
documentMode: isDocMode,
|
|
1603
|
-
componentAttrs:
|
|
2474
|
+
componentAttrs: typeof usageEntry.attrs === 'string' ? usageEntry.attrs : '',
|
|
2475
|
+
componentAttrsRewrite: {
|
|
2476
|
+
expressionRewrite: attrExpressionRewrite,
|
|
2477
|
+
scopeRewrite: attrScopeRewrite
|
|
2478
|
+
}
|
|
1604
2479
|
},
|
|
1605
2480
|
seenStaticImports,
|
|
1606
2481
|
knownRefKeys
|
|
1607
2482
|
);
|
|
2483
|
+
|
|
2484
|
+
mergeExpressionRewriteMaps(
|
|
2485
|
+
pageExpressionRewriteMap,
|
|
2486
|
+
pageExpressionBindingMap,
|
|
2487
|
+
pageAmbiguousExpressionMap,
|
|
2488
|
+
expressionRewrite,
|
|
2489
|
+
pageIr
|
|
2490
|
+
);
|
|
1608
2491
|
}
|
|
1609
2492
|
|
|
1610
2493
|
applyExpressionRewrites(
|
|
1611
2494
|
pageIr,
|
|
1612
2495
|
pageExpressionRewriteMap,
|
|
2496
|
+
pageExpressionBindingMap,
|
|
1613
2497
|
pageAmbiguousExpressionMap
|
|
1614
2498
|
);
|
|
2499
|
+
applyScopedIdentifierRewrites(pageIr, buildScopedIdentifierRewrite(pageIr));
|
|
2500
|
+
synthesizeSignalBackedCompiledExpressions(pageIr);
|
|
2501
|
+
normalizeExpressionBindingDependencies(pageIr);
|
|
1615
2502
|
|
|
1616
2503
|
rewriteLegacyMarkupIdentifiers(pageIr);
|
|
1617
2504
|
rewriteRefBindingIdentifiers(pageIr, knownRefKeys);
|
|
@@ -1625,7 +2512,7 @@ export async function build(options) {
|
|
|
1625
2512
|
}
|
|
1626
2513
|
|
|
1627
2514
|
if (envelopes.length > 0) {
|
|
1628
|
-
|
|
2515
|
+
await runBundler(envelopes, outDir, projectRoot, logger, showBundlerInfo, bundlerBin);
|
|
1629
2516
|
}
|
|
1630
2517
|
|
|
1631
2518
|
const assets = await collectAssets(outDir);
|