@zenithbuild/cli 0.7.3 → 0.7.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.
Files changed (84) hide show
  1. package/README.md +18 -13
  2. package/dist/adapters/adapter-netlify.d.ts +1 -1
  3. package/dist/adapters/adapter-netlify.js +56 -13
  4. package/dist/adapters/adapter-node.js +8 -0
  5. package/dist/adapters/adapter-static-export.d.ts +5 -0
  6. package/dist/adapters/adapter-static-export.js +115 -0
  7. package/dist/adapters/adapter-types.d.ts +3 -1
  8. package/dist/adapters/adapter-types.js +5 -2
  9. package/dist/adapters/adapter-vercel.d.ts +1 -1
  10. package/dist/adapters/adapter-vercel.js +70 -13
  11. package/dist/adapters/copy-hosted-page-runtime.d.ts +1 -0
  12. package/dist/adapters/copy-hosted-page-runtime.js +49 -0
  13. package/dist/adapters/resolve-adapter.js +4 -0
  14. package/dist/adapters/route-rules.d.ts +5 -0
  15. package/dist/adapters/route-rules.js +9 -0
  16. package/dist/adapters/validate-hosted-resource-routes.d.ts +1 -0
  17. package/dist/adapters/validate-hosted-resource-routes.js +13 -0
  18. package/dist/auth/route-auth.d.ts +6 -0
  19. package/dist/auth/route-auth.js +236 -0
  20. package/dist/build/compiler-runtime.d.ts +10 -9
  21. package/dist/build/compiler-runtime.js +58 -2
  22. package/dist/build/compiler-signal-expression.d.ts +1 -0
  23. package/dist/build/compiler-signal-expression.js +155 -0
  24. package/dist/build/expression-rewrites.d.ts +1 -6
  25. package/dist/build/expression-rewrites.js +61 -65
  26. package/dist/build/page-component-loop.d.ts +3 -13
  27. package/dist/build/page-component-loop.js +21 -46
  28. package/dist/build/page-ir-normalization.d.ts +0 -8
  29. package/dist/build/page-ir-normalization.js +13 -234
  30. package/dist/build/page-loop-state.d.ts +6 -9
  31. package/dist/build/page-loop-state.js +9 -8
  32. package/dist/build/page-loop.js +27 -22
  33. package/dist/build/scoped-identifier-rewrite.d.ts +37 -44
  34. package/dist/build/scoped-identifier-rewrite.js +28 -128
  35. package/dist/build/server-script.d.ts +3 -1
  36. package/dist/build/server-script.js +35 -5
  37. package/dist/build-output-manifest.d.ts +3 -2
  38. package/dist/build-output-manifest.js +3 -0
  39. package/dist/build.js +32 -18
  40. package/dist/component-instance-ir.js +158 -52
  41. package/dist/dev-build-session.js +20 -6
  42. package/dist/dev-server.js +152 -55
  43. package/dist/download-result.d.ts +14 -0
  44. package/dist/download-result.js +148 -0
  45. package/dist/framework-components/Image.zen +1 -1
  46. package/dist/images/materialization-plan.d.ts +1 -0
  47. package/dist/images/materialization-plan.js +6 -0
  48. package/dist/images/materialize.d.ts +5 -3
  49. package/dist/images/materialize.js +24 -109
  50. package/dist/images/router-manifest.d.ts +1 -0
  51. package/dist/images/router-manifest.js +49 -0
  52. package/dist/images/service.d.ts +13 -1
  53. package/dist/images/service.js +45 -15
  54. package/dist/index.js +8 -2
  55. package/dist/manifest.d.ts +15 -1
  56. package/dist/manifest.js +27 -7
  57. package/dist/preview.d.ts +13 -4
  58. package/dist/preview.js +261 -101
  59. package/dist/request-body.d.ts +1 -0
  60. package/dist/request-body.js +7 -0
  61. package/dist/request-origin.d.ts +2 -0
  62. package/dist/request-origin.js +45 -0
  63. package/dist/resource-manifest.d.ts +16 -0
  64. package/dist/resource-manifest.js +53 -0
  65. package/dist/resource-response.d.ts +34 -0
  66. package/dist/resource-response.js +71 -0
  67. package/dist/resource-route-module.d.ts +15 -0
  68. package/dist/resource-route-module.js +129 -0
  69. package/dist/route-check-support.d.ts +1 -0
  70. package/dist/route-check-support.js +4 -0
  71. package/dist/server-contract.d.ts +29 -6
  72. package/dist/server-contract.js +304 -42
  73. package/dist/server-error.d.ts +4 -0
  74. package/dist/server-error.js +36 -0
  75. package/dist/server-output.d.ts +4 -1
  76. package/dist/server-output.js +71 -10
  77. package/dist/server-runtime/node-server.js +67 -31
  78. package/dist/server-runtime/route-render.d.ts +27 -3
  79. package/dist/server-runtime/route-render.js +94 -53
  80. package/dist/server-script-composition.d.ts +13 -5
  81. package/dist/server-script-composition.js +29 -11
  82. package/dist/static-export-paths.d.ts +3 -0
  83. package/dist/static-export-paths.js +160 -0
  84. package/package.json +6 -3
@@ -1,4 +1,3 @@
1
- import { rewritePropsExpression } from './scoped-identifier-rewrite.js';
2
1
  import { resolveStateKeyFromBindings } from './expression-rewrites.js';
3
2
  import { expandScopedShorthandPropertiesInSource, normalizeTypeScriptExpression } from './typescript-expression-utils.js';
4
3
  /**
@@ -69,236 +68,6 @@ export function applyExpressionRewrites(pageIr, expressionMap, bindingMap, ambig
69
68
  }
70
69
  }
71
70
  }
72
- /**
73
- * @param {object} pageIr
74
- * @param {object} scopeRewrite
75
- * @param {Record<string, number> | null} [scopedMetrics]
76
- */
77
- export function applyScopedIdentifierRewrites(pageIr, scopeRewrite, scopedMetrics = null) {
78
- if (!Array.isArray(pageIr?.expressions) || pageIr.expressions.length === 0) {
79
- return;
80
- }
81
- const bindings = Array.isArray(pageIr.expression_bindings) ? pageIr.expression_bindings : [];
82
- const scopeMap = scopeRewrite?.map instanceof Map ? scopeRewrite.map : null;
83
- const scopeAmbiguous = scopeRewrite?.ambiguous instanceof Set ? scopeRewrite.ambiguous : null;
84
- const scopeCandidates = scopeMap instanceof Map
85
- ? new Set([...scopeMap.keys()].filter((identifier) => typeof identifier === 'string'
86
- && identifier.length > 0
87
- && !(scopeAmbiguous instanceof Set && scopeAmbiguous.has(identifier))))
88
- : null;
89
- const rewriteContext = { scopeRewrite };
90
- const rewriteCache = new Map();
91
- const shouldCheckIdentifierCandidate = (charCode, isStart) => {
92
- if (charCode === 36 || charCode === 95)
93
- return true;
94
- if (charCode >= 65 && charCode <= 90)
95
- return true;
96
- if (charCode >= 97 && charCode <= 122)
97
- return true;
98
- if (!isStart && charCode >= 48 && charCode <= 57)
99
- return true;
100
- return false;
101
- };
102
- const canSkipScopedRewrite = (value) => {
103
- if (!(scopeCandidates instanceof Set) || scopeCandidates.size === 0) {
104
- return true;
105
- }
106
- let token = '';
107
- for (let index = 0; index < value.length; index++) {
108
- const charCode = value.charCodeAt(index);
109
- if (shouldCheckIdentifierCandidate(charCode, token.length === 0)) {
110
- token += value[index];
111
- continue;
112
- }
113
- if (token.length > 0) {
114
- if (scopeCandidates.has(token)) {
115
- return false;
116
- }
117
- token = '';
118
- }
119
- }
120
- return token.length > 0 ? !scopeCandidates.has(token) : true;
121
- };
122
- const addScopedMetric = (key, value) => {
123
- if (!scopedMetrics || typeof scopedMetrics !== 'object' || !Number.isFinite(value)) {
124
- return;
125
- }
126
- scopedMetrics[key] = (scopedMetrics[key] || 0) + value;
127
- };
128
- const rewriteScoped = (value) => {
129
- if (typeof value !== 'string') {
130
- return value;
131
- }
132
- addScopedMetric('totalValues', 1);
133
- const cached = rewriteCache.get(value);
134
- if (typeof cached === 'string') {
135
- addScopedMetric('cacheHitCount', 1);
136
- return cached;
137
- }
138
- addScopedMetric('cacheMissCount', 1);
139
- const missStartedAt = performance.now();
140
- const identifierCheckStartedAt = performance.now();
141
- const shouldSkip = canSkipScopedRewrite(value);
142
- const identifierCheckMs = performance.now() - identifierCheckStartedAt;
143
- addScopedMetric('identifierCheckMs', identifierCheckMs);
144
- if (shouldSkip) {
145
- addScopedMetric('fastSkipCount', 1);
146
- addScopedMetric('fastSkipMs', identifierCheckMs);
147
- rewriteCache.set(value, value);
148
- addScopedMetric('unchangedMissCount', 1);
149
- addScopedMetric('cacheMissMs', performance.now() - missStartedAt);
150
- return value;
151
- }
152
- const rewriteCallStartedAt = performance.now();
153
- const rewritten = rewritePropsExpression(value, rewriteContext);
154
- addScopedMetric('rewriteCallMs', performance.now() - rewriteCallStartedAt);
155
- addScopedMetric(rewritten === value ? 'unchangedMissCount' : 'changedMissCount', 1);
156
- addScopedMetric('cacheMissMs', performance.now() - missStartedAt);
157
- rewriteCache.set(value, rewritten);
158
- return rewritten;
159
- };
160
- for (let index = 0; index < pageIr.expressions.length; index++) {
161
- const current = pageIr.expressions[index];
162
- if (typeof current === 'string') {
163
- pageIr.expressions[index] = rewriteScoped(current);
164
- }
165
- if (!bindings[index] || typeof bindings[index] !== 'object') {
166
- continue;
167
- }
168
- if (typeof bindings[index].literal === 'string') {
169
- bindings[index].literal = rewriteScoped(bindings[index].literal);
170
- }
171
- if (typeof bindings[index].compiled_expr === 'string') {
172
- bindings[index].compiled_expr = rewriteScoped(bindings[index].compiled_expr);
173
- }
174
- }
175
- }
176
- export function synthesizeSignalBackedCompiledExpressions(pageIr) {
177
- if (!Array.isArray(pageIr?.expression_bindings) || pageIr.expression_bindings.length === 0) {
178
- return;
179
- }
180
- const stateBindings = Array.isArray(pageIr?.hoisted?.state) ? pageIr.hoisted.state : [];
181
- const signals = Array.isArray(pageIr?.signals) ? pageIr.signals : [];
182
- if (stateBindings.length === 0 || signals.length === 0) {
183
- return;
184
- }
185
- const signalBackedStateIndices = new Set(signals
186
- .map((signal) => signal?.state_index)
187
- .filter((value) => Number.isInteger(value)));
188
- const signalIndexByStateKey = new Map();
189
- for (let index = 0; index < signals.length; index++) {
190
- const stateIndex = signals[index]?.state_index;
191
- const stateKey = Number.isInteger(stateIndex) ? stateBindings[stateIndex]?.key : null;
192
- if (typeof stateKey === 'string' && stateKey.length > 0) {
193
- signalIndexByStateKey.set(stateKey, index);
194
- }
195
- }
196
- if (signalIndexByStateKey.size === 0) {
197
- return;
198
- }
199
- for (let index = 0; index < pageIr.expression_bindings.length; index++) {
200
- const binding = pageIr.expression_bindings[index];
201
- if (!binding || typeof binding !== 'object') {
202
- continue;
203
- }
204
- if (typeof binding.compiled_expr === 'string' && binding.compiled_expr.includes('signalMap.get(')) {
205
- continue;
206
- }
207
- const candidate = typeof binding.literal === 'string' && binding.literal.trim().length > 0
208
- ? binding.literal
209
- : typeof pageIr.expressions?.[index] === 'string'
210
- ? pageIr.expressions[index]
211
- : null;
212
- if (typeof candidate !== 'string' || candidate.trim().length === 0) {
213
- continue;
214
- }
215
- if (Number.isInteger(binding.state_index) &&
216
- !signalBackedStateIndices.has(binding.state_index)) {
217
- continue;
218
- }
219
- let rewritten = candidate;
220
- const signalIndices = [];
221
- for (const [stateKey, signalIndex] of signalIndexByStateKey.entries()) {
222
- if (!rewritten.includes(stateKey)) {
223
- continue;
224
- }
225
- const escaped = stateKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
226
- const pattern = new RegExp(`(?<!\\.)\\b${escaped}\\b`, 'g');
227
- if (!pattern.test(rewritten)) {
228
- continue;
229
- }
230
- rewritten = rewritten.replace(pattern, `signalMap.get(${signalIndex}).get()`);
231
- signalIndices.push(signalIndex);
232
- }
233
- if (rewritten === candidate || signalIndices.length === 0) {
234
- continue;
235
- }
236
- const uniqueSignalIndices = [...new Set(signalIndices)].sort((a, b) => a - b);
237
- binding.compiled_expr = rewritten;
238
- binding.signal_indices = uniqueSignalIndices;
239
- if (uniqueSignalIndices.length === 1) {
240
- binding.signal_index = uniqueSignalIndices[0];
241
- const stateIndex = signals[uniqueSignalIndices[0]]?.state_index;
242
- if (Number.isInteger(stateIndex)) {
243
- binding.state_index = stateIndex;
244
- }
245
- }
246
- }
247
- }
248
- export function normalizeExpressionBindingDependencies(pageIr) {
249
- if (!Array.isArray(pageIr?.expression_bindings) || pageIr.expression_bindings.length === 0) {
250
- return;
251
- }
252
- const signals = Array.isArray(pageIr.signals) ? pageIr.signals : [];
253
- const dependencyRe = /signalMap\.get\((\d+)\)/g;
254
- for (const binding of pageIr.expression_bindings) {
255
- if (!binding || typeof binding !== 'object' || typeof binding.compiled_expr !== 'string') {
256
- continue;
257
- }
258
- const indices = [];
259
- dependencyRe.lastIndex = 0;
260
- let match;
261
- while ((match = dependencyRe.exec(binding.compiled_expr)) !== null) {
262
- const index = Number.parseInt(match[1], 10);
263
- if (Number.isInteger(index)) {
264
- indices.push(index);
265
- }
266
- }
267
- if (indices.length === 0) {
268
- continue;
269
- }
270
- let signalIndices = [...new Set(indices)].sort((a, b) => a - b);
271
- if (Number.isInteger(binding.state_index)) {
272
- const owningSignalIndices = signals
273
- .map((signal, index) => signal?.state_index === binding.state_index ? index : null)
274
- .filter((value) => Number.isInteger(value));
275
- const extractedMatchState = signalIndices.length > 0 &&
276
- signalIndices.every((index) => signals[index]?.state_index === binding.state_index);
277
- if (owningSignalIndices.length > 0 && !extractedMatchState) {
278
- signalIndices = owningSignalIndices;
279
- }
280
- }
281
- if (!Array.isArray(binding.signal_indices) ||
282
- binding.signal_indices.length === 0 ||
283
- binding.signal_indices.some((index) => signals[index]?.state_index !== binding.state_index)) {
284
- binding.signal_indices = signalIndices;
285
- }
286
- if ((!Number.isInteger(binding.signal_index) ||
287
- signals[binding.signal_index]?.state_index !== binding.state_index) &&
288
- signalIndices.length === 1) {
289
- binding.signal_index = signalIndices[0];
290
- }
291
- if (!Number.isInteger(binding.state_index) && Number.isInteger(binding.signal_index)) {
292
- const stateIndex = signals[binding.signal_index]?.state_index;
293
- if (Number.isInteger(stateIndex)) {
294
- binding.state_index = stateIndex;
295
- }
296
- }
297
- if (signalIndices.length === 1) {
298
- binding.compiled_expr = binding.compiled_expr.replace(/signalMap\.get\(\d+\)/g, `signalMap.get(${signalIndices[0]})`);
299
- }
300
- }
301
- }
302
71
  export function normalizeExpressionPayload(pageIr) {
303
72
  if (!Array.isArray(pageIr?.expressions) || pageIr.expressions.length === 0) {
304
73
  return;
@@ -347,24 +116,34 @@ export function rewriteLegacyMarkupIdentifiers(pageIr) {
347
116
  return;
348
117
  }
349
118
  const bindings = Array.isArray(pageIr.expression_bindings) ? pageIr.expression_bindings : [];
119
+ function failLegacyMarkup(source) {
120
+ throw new Error(`[Zenith:Build] Legacy ${LEGACY_MARKUP_IDENT}\`...\` markup syntax is unsupported. ` +
121
+ 'Use embedded markup expressions with embeddedMarkupExpressions: true, or unsafeHTML={value} for explicit raw HTML.');
122
+ }
350
123
  for (let i = 0; i < pageIr.expressions.length; i++) {
351
124
  if (typeof pageIr.expressions[i] === 'string' && pageIr.expressions[i].includes(LEGACY_MARKUP_IDENT)) {
352
125
  LEGACY_MARKUP_RE.lastIndex = 0;
353
- pageIr.expressions[i] = pageIr.expressions[i].replace(LEGACY_MARKUP_RE, '__ZENITH_INTERNAL_ZENHTML');
126
+ if (LEGACY_MARKUP_RE.test(pageIr.expressions[i])) {
127
+ failLegacyMarkup(pageIr.expressions[i]);
128
+ }
354
129
  }
355
130
  if (bindings[i] &&
356
131
  typeof bindings[i] === 'object' &&
357
132
  typeof bindings[i].literal === 'string' &&
358
133
  bindings[i].literal.includes(LEGACY_MARKUP_IDENT)) {
359
134
  LEGACY_MARKUP_RE.lastIndex = 0;
360
- bindings[i].literal = bindings[i].literal.replace(LEGACY_MARKUP_RE, '__ZENITH_INTERNAL_ZENHTML');
135
+ if (LEGACY_MARKUP_RE.test(bindings[i].literal)) {
136
+ failLegacyMarkup(bindings[i].literal);
137
+ }
361
138
  }
362
139
  if (bindings[i] &&
363
140
  typeof bindings[i] === 'object' &&
364
141
  typeof bindings[i].compiled_expr === 'string' &&
365
142
  bindings[i].compiled_expr.includes(LEGACY_MARKUP_IDENT)) {
366
143
  LEGACY_MARKUP_RE.lastIndex = 0;
367
- bindings[i].compiled_expr = bindings[i].compiled_expr.replace(LEGACY_MARKUP_RE, '__ZENITH_INTERNAL_ZENHTML');
144
+ if (LEGACY_MARKUP_RE.test(bindings[i].compiled_expr)) {
145
+ failLegacyMarkup(bindings[i].compiled_expr);
146
+ }
368
147
  }
369
148
  }
370
149
  }
@@ -1,9 +1,8 @@
1
1
  export function createPageLoopState(): {
2
2
  expressionRewriteMetrics: {
3
3
  calls: number;
4
- cacheHits: number;
5
- cacheMisses: number;
6
- templateCompileMs: number;
4
+ compilerOwnedBindings: number;
5
+ ambiguousBindings: number;
7
6
  };
8
7
  pagePhaseTotals: {
9
8
  occurrenceCollectMs: number;
@@ -78,7 +77,6 @@ export function createPageLoopState(): {
78
77
  componentIrCache: Map<any, any>;
79
78
  componentDocumentModeCache: Map<any, any>;
80
79
  componentExpressionRewriteCache: Map<any, any>;
81
- templateExpressionCache: Map<any, any>;
82
80
  hoistedCodeTransformCache: {
83
81
  transpileToJs: Map<any, any>;
84
82
  deferRuntime: Map<any, any>;
@@ -88,7 +86,6 @@ export function createPageLoopCaches(): {
88
86
  componentIrCache: Map<any, any>;
89
87
  componentDocumentModeCache: Map<any, any>;
90
88
  componentExpressionRewriteCache: Map<any, any>;
91
- templateExpressionCache: Map<any, any>;
92
89
  hoistedCodeTransformCache: {
93
90
  transpileToJs: Map<any, any>;
94
91
  deferRuntime: Map<any, any>;
@@ -97,9 +94,8 @@ export function createPageLoopCaches(): {
97
94
  export function createPageLoopExecutionState(): {
98
95
  expressionRewriteMetrics: {
99
96
  calls: number;
100
- cacheHits: number;
101
- cacheMisses: number;
102
- templateCompileMs: number;
97
+ compilerOwnedBindings: number;
98
+ ambiguousBindings: number;
103
99
  };
104
100
  pagePhaseTotals: {
105
101
  occurrenceCollectMs: number;
@@ -173,11 +169,12 @@ export function createPageLoopExecutionState(): {
173
169
  envelopes: never[];
174
170
  };
175
171
  export function preparePageIrForMerge(pageIr: any): void;
176
- export function applyServerEnvelopeToPageIr({ pageIr, composedServer, hasGuard, hasLoad, entry, srcDir, sourceFile }: {
172
+ export function applyServerEnvelopeToPageIr({ pageIr, composedServer, hasGuard, hasLoad, hasAction, entry, srcDir, sourceFile }: {
177
173
  pageIr: any;
178
174
  composedServer: any;
179
175
  hasGuard: any;
180
176
  hasLoad: any;
177
+ hasAction: any;
181
178
  entry: any;
182
179
  srcDir: any;
183
180
  sourceFile: any;
@@ -11,7 +11,6 @@ export function createPageLoopCaches() {
11
11
  componentIrCache: new Map(),
12
12
  componentDocumentModeCache: new Map(),
13
13
  componentExpressionRewriteCache: new Map(),
14
- templateExpressionCache: new Map(),
15
14
  hoistedCodeTransformCache: {
16
15
  transpileToJs: new Map(),
17
16
  deferRuntime: new Map()
@@ -22,9 +21,8 @@ export function createPageLoopExecutionState() {
22
21
  return {
23
22
  expressionRewriteMetrics: {
24
23
  calls: 0,
25
- cacheHits: 0,
26
- cacheMisses: 0,
27
- templateCompileMs: 0
24
+ compilerOwnedBindings: 0,
25
+ ambiguousBindings: 0
28
26
  },
29
27
  pagePhaseTotals: createPagePhaseTotals(),
30
28
  occurrenceApplyPhaseTotals: createOccurrenceApplyPhaseTotals(),
@@ -48,22 +46,25 @@ export function preparePageIrForMerge(pageIr) {
48
46
  pageIr.hoisted.state = pageIr.hoisted.state || [];
49
47
  pageIr.hoisted.code = pageIr.hoisted.code || [];
50
48
  }
51
- export function applyServerEnvelopeToPageIr({ pageIr, composedServer, hasGuard, hasLoad, entry, srcDir, sourceFile }) {
49
+ export function applyServerEnvelopeToPageIr({ pageIr, composedServer, hasGuard, hasLoad, hasAction, entry, srcDir, sourceFile }) {
52
50
  if (composedServer.serverScript) {
53
- pageIr.server_script = composedServer.serverScript;
51
+ const { has_action: _unusedHasAction, export_paths: _unusedExportPaths, ...serverScript } = composedServer.serverScript;
52
+ pageIr.server_script = serverScript;
54
53
  pageIr.prerender = composedServer.serverScript.prerender === true;
55
54
  if (pageIr.ssr_data === undefined) {
56
55
  pageIr.ssr_data = null;
57
56
  }
58
57
  }
59
- if (pageIr.prerender === true && (hasGuard || hasLoad)) {
58
+ if (pageIr.prerender === true && (hasGuard || hasLoad || hasAction)) {
60
59
  throw new Error(`[zenith] Build failed for ${entry.file}: protected routes require SSR/runtime. ` +
61
- 'Cannot prerender a static route with a `guard` or `load` function.');
60
+ 'Cannot prerender a static route with a `guard`, `load`, or `action` function.');
62
61
  }
63
62
  pageIr.has_guard = hasGuard;
64
63
  pageIr.has_load = hasLoad;
64
+ pageIr.has_action = hasAction;
65
65
  pageIr.guard_module_ref = composedServer.guardPath ? relative(srcDir, composedServer.guardPath).replaceAll('\\', '/') : null;
66
66
  pageIr.load_module_ref = composedServer.loadPath ? relative(srcDir, composedServer.loadPath).replaceAll('\\', '/') : null;
67
+ pageIr.action_module_ref = composedServer.actionPath ? relative(srcDir, composedServer.actionPath).replaceAll('\\', '/') : null;
67
68
  preparePageIrForMerge(pageIr);
68
69
  }
69
70
  export function buildOccurrenceCountByPath(componentOccurrences) {
@@ -5,14 +5,14 @@ import { applyOccurrenceRewritePlans } from '../component-instance-ir.js';
5
5
  import { collectExpandedComponentOccurrences } from '../component-occurrences.js';
6
6
  import { expandComponents } from '../resolve-components.js';
7
7
  import { composeServerScriptEnvelope, resolveAdjacentServerModules } from '../server-script-composition.js';
8
- import { createTimedCompilerRunner } from './compiler-runtime.js';
8
+ import { createTimedCompilerRunner, mergePageImageMaterialization } from './compiler-runtime.js';
9
9
  import { buildComponentExpressionRewrite, mergeExpressionRewriteMaps, resolveRewrittenBindingMetadata } from './expression-rewrites.js';
10
10
  import { createPageIrMergeCache } from './merge-component-ir.js';
11
11
  import { buildPageOwnerContext, runPageComponentLoop } from './page-component-loop.js';
12
- import { applyExpressionRewrites, applyScopedIdentifierRewrites, normalizeExpressionBindingDependencies, normalizeExpressionPayload, normalizeHoistedSourcePayload, rewriteLegacyMarkupIdentifiers, rewriteRefBindingIdentifiers, synthesizeSignalBackedCompiledExpressions } from './page-ir-normalization.js';
12
+ import { applyExpressionRewrites, normalizeExpressionPayload, normalizeHoistedSourcePayload, rewriteLegacyMarkupIdentifiers, rewriteRefBindingIdentifiers } from './page-ir-normalization.js';
13
+ import { deferComponentRuntimeBlock } from './hoisted-code-transforms.js';
13
14
  import { addBreakdown, emitPageLoopSummary, recordPageProfile } from './page-loop-metrics.js';
14
15
  import { applyServerEnvelopeToPageIr, buildOccurrenceCountByPath, createPageBuildState, createPageLoopCaches, createPageLoopExecutionState } from './page-loop-state.js';
15
- import { buildScopedIdentifierRewrite } from './scoped-identifier-rewrite.js';
16
16
  import { extractServerScript } from './server-script.js';
17
17
  /**
18
18
  * @param {{
@@ -33,7 +33,7 @@ export async function buildPageEnvelopes(input) {
33
33
  const { manifest, pagesDir, srcDir, registry, compilerOpts, compilerBin, routerEnabled, startupProfile, compilerTotals, emitCompilerWarning } = input;
34
34
  const cacheState = input.pageLoopCaches || createPageLoopCaches();
35
35
  const executionState = createPageLoopExecutionState();
36
- const { componentIrCache, componentDocumentModeCache, componentExpressionRewriteCache, templateExpressionCache, hoistedCodeTransformCache } = cacheState;
36
+ const { componentIrCache, componentDocumentModeCache, componentExpressionRewriteCache, hoistedCodeTransformCache } = cacheState;
37
37
  const { expressionRewriteMetrics, pagePhaseTotals, occurrenceApplyPhaseTotals, bindingResolutionTotals, scopedRewritePhaseTotals, mergePhaseTotals, componentLoopPhaseTotals, pageProfiles, envelopes } = executionState;
38
38
  async function cooperativeYield() {
39
39
  await new Promise((resolve) => setImmediate(resolve));
@@ -52,7 +52,7 @@ export async function buildPageEnvelopes(input) {
52
52
  const pageOwnerExtractStartedAt = performance.now();
53
53
  const pageOwnerSource = extractServerScript(rawSource, sourceFile, compilerOpts).source;
54
54
  pagePhase.serverExtractMs += startupProfile.roundMs(performance.now() - pageOwnerExtractStartedAt);
55
- const { guardPath: adjacentGuard, loadPath: adjacentLoad } = resolveAdjacentServerModules(sourceFile);
55
+ const { guardPath: adjacentGuard, loadPath: adjacentLoad, actionPath: adjacentAction } = resolveAdjacentServerModules(sourceFile);
56
56
  const expandedStartedAt = performance.now();
57
57
  const { expandedSource } = expandComponents(rawSource, registry, sourceFile);
58
58
  pageExpandMs = startupProfile.roundMs(performance.now() - expandedStartedAt);
@@ -62,21 +62,24 @@ export async function buildPageEnvelopes(input) {
62
62
  const compileSource = extractedServer.source;
63
63
  await cooperativeYield();
64
64
  const pageCompileStartedAt = performance.now();
65
- const pageIr = timedRunCompiler('page', sourceFile, compileSource, compilerOpts, { compilerToolchain: compilerBin, onWarning: emitCompilerWarning });
65
+ let pageIr = timedRunCompiler('page', sourceFile, compileSource, compilerOpts, { compilerToolchain: compilerBin, onWarning: emitCompilerWarning });
66
66
  pageCompileMs = startupProfile.roundMs(performance.now() - pageCompileStartedAt);
67
67
  const composedServer = composeServerScriptEnvelope({
68
68
  sourceFile,
69
69
  inlineServerScript: extractedServer.serverScript,
70
70
  adjacentGuardPath: adjacentGuard,
71
- adjacentLoadPath: adjacentLoad
71
+ adjacentLoadPath: adjacentLoad,
72
+ adjacentActionPath: adjacentAction
72
73
  });
73
74
  const hasGuard = composedServer.serverScript?.has_guard === true;
74
75
  const hasLoad = composedServer.serverScript?.has_load === true;
76
+ const hasAction = composedServer.serverScript?.has_action === true;
75
77
  applyServerEnvelopeToPageIr({
76
78
  pageIr,
77
79
  composedServer,
78
80
  hasGuard,
79
81
  hasLoad,
82
+ hasAction,
80
83
  entry,
81
84
  srcDir,
82
85
  sourceFile
@@ -89,7 +92,8 @@ export async function buildPageEnvelopes(input) {
89
92
  const pageAmbiguousExpressionMap = new Set();
90
93
  const knownRefKeys = new Set();
91
94
  const componentOccurrencePlans = [];
92
- const { pageOwnerCompileMs: resolvedPageOwnerCompileMs, pageOwnerExpressionRewrite, pageOwnerScopeRewrite } = await buildPageOwnerContext({
95
+ const imagePropsLiterals = [];
96
+ const { pageOwnerCompileMs: resolvedPageOwnerCompileMs, pageOwnerExpressionRewrite } = await buildPageOwnerContext({
93
97
  componentOccurrences,
94
98
  sourceFile,
95
99
  pageOwnerSource,
@@ -97,13 +101,12 @@ export async function buildPageEnvelopes(input) {
97
101
  compilerBin,
98
102
  timedRunCompiler,
99
103
  cooperativeYield,
100
- templateExpressionCache,
101
104
  expressionRewriteMetrics,
102
105
  startupProfile
103
106
  });
104
107
  pageOwnerCompileMs = resolvedPageOwnerCompileMs;
105
108
  const pageSelfRewriteStartedAt = performance.now();
106
- const pageSelfExpressionRewrite = buildComponentExpressionRewrite(sourceFile, compileSource, pageIr, compilerOpts, compilerBin, templateExpressionCache, expressionRewriteMetrics);
109
+ const pageSelfExpressionRewrite = buildComponentExpressionRewrite(pageIr, expressionRewriteMetrics);
107
110
  pagePhase.selfRewriteMs = startupProfile.roundMs(performance.now() - pageSelfRewriteStartedAt);
108
111
  mergeExpressionRewriteMaps(pageExpressionRewriteMap, pageExpressionBindingMap, pageAmbiguousExpressionMap, pageSelfExpressionRewrite, pageIrMergeCache, pageBindingResolutionBreakdown);
109
112
  const componentLoopStartedAt = performance.now();
@@ -127,10 +130,8 @@ export async function buildPageEnvelopes(input) {
127
130
  componentIrCache,
128
131
  componentDocumentModeCache,
129
132
  componentExpressionRewriteCache,
130
- templateExpressionCache,
131
133
  expressionRewriteMetrics,
132
134
  pageOwnerExpressionRewrite,
133
- pageOwnerScopeRewrite,
134
135
  pageIr,
135
136
  pageIrMergeCache,
136
137
  seenStaticImports,
@@ -139,6 +140,7 @@ export async function buildPageEnvelopes(input) {
139
140
  pageAmbiguousExpressionMap,
140
141
  knownRefKeys,
141
142
  componentOccurrencePlans,
143
+ imagePropsLiterals,
142
144
  pagePhase,
143
145
  pageBindingResolutionBreakdown,
144
146
  pageMergeBreakdown,
@@ -150,25 +152,25 @@ export async function buildPageEnvelopes(input) {
150
152
  pageComponentCacheHits = pageStats.pageComponentCacheHits;
151
153
  pageComponentCacheMisses = pageStats.pageComponentCacheMisses;
152
154
  pagePhase.componentLoopMs = startupProfile.roundMs(performance.now() - componentLoopStartedAt);
155
+ if (imagePropsLiterals.length > 0) {
156
+ pageIr = mergePageImageMaterialization(pageIr, imagePropsLiterals, {
157
+ compilerToolchain: compilerBin
158
+ });
159
+ }
153
160
  const occurrencePlanApplyStartedAt = performance.now();
154
161
  applyOccurrenceRewritePlans(pageIr, componentOccurrencePlans, (rewrite, binding) => resolveRewrittenBindingMetadata(pageIrMergeCache, rewrite, binding, pageBindingResolutionBreakdown), pageOccurrenceApplyBreakdown);
155
162
  pagePhase.occurrencePlanApplyMs = startupProfile.roundMs(performance.now() - occurrencePlanApplyStartedAt);
156
163
  const expressionApplyStartedAt = performance.now();
157
164
  applyExpressionRewrites(pageIr, pageExpressionRewriteMap, pageExpressionBindingMap, pageAmbiguousExpressionMap);
158
165
  pagePhase.expressionApplyMs = startupProfile.roundMs(performance.now() - expressionApplyStartedAt);
159
- const scopedRewriteStartedAt = performance.now();
160
- const scopedRewritePlanStartedAt = performance.now();
161
- const scopedRewritePlan = buildScopedIdentifierRewrite(pageIr);
162
- pagePhase.scopedRewritePlanMs = startupProfile.roundMs(performance.now() - scopedRewritePlanStartedAt);
163
- const scopedRewriteApplyStartedAt = performance.now();
164
- applyScopedIdentifierRewrites(pageIr, scopedRewritePlan, pageScopedRewriteBreakdown);
165
- pagePhase.scopedRewriteApplyMs = startupProfile.roundMs(performance.now() - scopedRewriteApplyStartedAt);
166
- pagePhase.scopedRewriteMs = startupProfile.roundMs(performance.now() - scopedRewriteStartedAt);
167
166
  const normalizeStartedAt = performance.now();
168
- synthesizeSignalBackedCompiledExpressions(pageIr);
169
167
  normalizeExpressionPayload(pageIr);
170
168
  normalizeHoistedSourcePayload(pageIr);
171
- normalizeExpressionBindingDependencies(pageIr);
169
+ if (Array.isArray(pageIr?.hoisted?.code) && pageIr.hoisted.code.length > 0) {
170
+ pageIr.hoisted.code = pageIr.hoisted.code
171
+ .map((entry) => deferComponentRuntimeBlock(entry, hoistedCodeTransformCache, expressionRewriteMetrics))
172
+ .filter((entry) => typeof entry === 'string' && entry.trim().length > 0);
173
+ }
172
174
  rewriteLegacyMarkupIdentifiers(pageIr);
173
175
  rewriteRefBindingIdentifiers(pageIr, knownRefKeys);
174
176
  pagePhase.normalizeMs = startupProfile.roundMs(performance.now() - normalizeStartedAt);
@@ -182,6 +184,9 @@ export async function buildPageEnvelopes(input) {
182
184
  route: entry.path,
183
185
  file: sourceFile,
184
186
  ir: pageIr,
187
+ image_materialization: Array.isArray(pageIr.image_materialization)
188
+ ? pageIr.image_materialization
189
+ : [],
185
190
  router: routerEnabled
186
191
  });
187
192
  recordPageProfile({
@@ -1,29 +1,3 @@
1
- /**
2
- * @param {object | null | undefined} ir
3
- * @returns {{ map: Map<string, string>, ambiguous: Set<string> }}
4
- */
5
- export function buildScopedIdentifierRewrite(ir: object | null | undefined): {
6
- map: Map<string, string>;
7
- ambiguous: Set<string>;
8
- };
9
- /**
10
- * @param {string} expr
11
- * @param {{
12
- * expressionRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null,
13
- * scopeRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null
14
- * } | null} rewriteContext
15
- * @returns {string}
16
- */
17
- export function rewritePropsExpression(expr: string, rewriteContext?: {
18
- expressionRewrite?: {
19
- map?: Map<string, string>;
20
- ambiguous?: Set<string>;
21
- } | null;
22
- scopeRewrite?: {
23
- map?: Map<string, string>;
24
- ambiguous?: Set<string>;
25
- } | null;
26
- } | null): string;
27
1
  /**
28
2
  * @param {string | null | undefined} compiledExpr
29
3
  * @param {{
@@ -41,6 +15,10 @@ export function resolveCompiledPropsExpression(compiledExpr: string | null | und
41
15
  }>;
42
16
  } | null | undefined): string | null;
43
17
  /**
18
+ * The only allowed downstream props rewrite boundary is compiler-owned exact lookup.
19
+ * If the compiler did not emit a mapping for the attr expression, CLI keeps the
20
+ * original expression text without attempting identifier reinterpretation.
21
+ *
44
22
  * @param {string} expr
45
23
  * @param {{
46
24
  * expressionRewrite?: {
@@ -49,8 +27,7 @@ export function resolveCompiledPropsExpression(compiledExpr: string | null | und
49
27
  * ambiguous?: Set<string>,
50
28
  * signals?: Array<{ state_index?: number }>,
51
29
  * stateBindings?: Array<{ key?: string }>
52
- * } | null,
53
- * scopeRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null
30
+ * } | null
54
31
  * } | null} rewriteContext
55
32
  * @returns {string}
56
33
  */
@@ -68,45 +45,61 @@ export function resolvePropsValueCode(expr: string, rewriteContext?: {
68
45
  key?: string;
69
46
  }>;
70
47
  } | null;
71
- scopeRewrite?: {
72
- map?: Map<string, string>;
73
- ambiguous?: Set<string>;
74
- } | null;
75
48
  } | null): string;
76
49
  /**
77
50
  * @param {string} attrs
78
51
  * @param {{
79
- * expressionRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null,
80
- * scopeRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null
52
+ * expressionRewrite?: {
53
+ * map?: Map<string, string>,
54
+ * bindings?: Map<string, { compiled_expr?: string | null }>,
55
+ * ambiguous?: Set<string>,
56
+ * signals?: Array<{ state_index?: number }>,
57
+ * stateBindings?: Array<{ key?: string }>
58
+ * } | null
81
59
  * } | null} rewriteContext
82
60
  * @returns {string}
83
61
  */
84
62
  export function renderPropsLiteralFromAttrs(attrs: string, rewriteContext?: {
85
63
  expressionRewrite?: {
86
64
  map?: Map<string, string>;
65
+ bindings?: Map<string, {
66
+ compiled_expr?: string | null;
67
+ }>;
87
68
  ambiguous?: Set<string>;
88
- } | null;
89
- scopeRewrite?: {
90
- map?: Map<string, string>;
91
- ambiguous?: Set<string>;
69
+ signals?: Array<{
70
+ state_index?: number;
71
+ }>;
72
+ stateBindings?: Array<{
73
+ key?: string;
74
+ }>;
92
75
  } | null;
93
76
  } | null): string;
94
77
  /**
95
78
  * @param {string} source
96
79
  * @param {string} attrs
97
80
  * @param {{
98
- * expressionRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null,
99
- * scopeRewrite?: { map?: Map<string, string>, ambiguous?: Set<string> } | null
81
+ * expressionRewrite?: {
82
+ * map?: Map<string, string>,
83
+ * bindings?: Map<string, { compiled_expr?: string | null }>,
84
+ * ambiguous?: Set<string>,
85
+ * signals?: Array<{ state_index?: number }>,
86
+ * stateBindings?: Array<{ key?: string }>
87
+ * } | null
100
88
  * } | null} rewriteContext
101
89
  * @returns {string}
102
90
  */
103
91
  export function injectPropsPrelude(source: string, attrs: string, rewriteContext?: {
104
92
  expressionRewrite?: {
105
93
  map?: Map<string, string>;
94
+ bindings?: Map<string, {
95
+ compiled_expr?: string | null;
96
+ }>;
106
97
  ambiguous?: Set<string>;
107
- } | null;
108
- scopeRewrite?: {
109
- map?: Map<string, string>;
110
- ambiguous?: Set<string>;
98
+ signals?: Array<{
99
+ state_index?: number;
100
+ }>;
101
+ stateBindings?: Array<{
102
+ key?: string;
103
+ }>;
111
104
  } | null;
112
105
  } | null): string;