@zenithbuild/cli 0.7.5 → 0.7.7
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/adapters/adapter-netlify.js +0 -8
- package/dist/adapters/adapter-vercel.js +6 -14
- package/dist/adapters/copy-hosted-page-runtime.js +2 -1
- package/dist/build/hoisted-code-transforms.d.ts +4 -1
- package/dist/build/hoisted-code-transforms.js +5 -3
- package/dist/build/page-ir-normalization.d.ts +1 -1
- package/dist/build/page-ir-normalization.js +33 -3
- package/dist/build/page-loop.js +46 -2
- package/dist/dev-build-session/helpers.d.ts +29 -0
- package/dist/dev-build-session/helpers.js +223 -0
- package/dist/dev-build-session/session.d.ts +24 -0
- package/dist/dev-build-session/session.js +204 -0
- package/dist/dev-build-session/state.d.ts +37 -0
- package/dist/dev-build-session/state.js +17 -0
- package/dist/dev-build-session.d.ts +1 -24
- package/dist/dev-build-session.js +1 -434
- package/dist/dev-server/css-state.d.ts +7 -0
- package/dist/dev-server/css-state.js +92 -0
- package/dist/dev-server/not-found.d.ts +23 -0
- package/dist/dev-server/not-found.js +129 -0
- package/dist/dev-server/request-handler.d.ts +1 -0
- package/dist/dev-server/request-handler.js +376 -0
- package/dist/dev-server/route-check.d.ts +9 -0
- package/dist/dev-server/route-check.js +100 -0
- package/dist/dev-server/watcher.d.ts +5 -0
- package/dist/dev-server/watcher.js +216 -0
- package/dist/dev-server.js +123 -924
- package/dist/images/payload.js +4 -0
- package/dist/manifest.js +46 -1
- package/dist/preview/create-preview-server.d.ts +18 -0
- package/dist/preview/create-preview-server.js +71 -0
- package/dist/preview/manifest.d.ts +42 -0
- package/dist/preview/manifest.js +57 -0
- package/dist/preview/paths.d.ts +3 -0
- package/dist/preview/paths.js +38 -0
- package/dist/preview/payload.d.ts +6 -0
- package/dist/preview/payload.js +34 -0
- package/dist/preview/request-handler.d.ts +1 -0
- package/dist/preview/request-handler.js +300 -0
- package/dist/preview/server-runner.d.ts +49 -0
- package/dist/preview/server-runner.js +220 -0
- package/dist/preview/server-script-runner-template.d.ts +1 -0
- package/dist/preview/server-script-runner-template.js +425 -0
- package/dist/preview.d.ts +5 -112
- package/dist/preview.js +7 -1119
- package/dist/resource-response.d.ts +15 -0
- package/dist/resource-response.js +91 -2
- package/dist/server-contract/constants.d.ts +5 -0
- package/dist/server-contract/constants.js +5 -0
- package/dist/server-contract/export-validation.d.ts +5 -0
- package/dist/server-contract/export-validation.js +59 -0
- package/dist/server-contract/json-serializable.d.ts +1 -0
- package/dist/server-contract/json-serializable.js +52 -0
- package/dist/server-contract/resolve.d.ts +15 -0
- package/dist/server-contract/resolve.js +271 -0
- package/dist/server-contract/result-helpers.d.ts +51 -0
- package/dist/server-contract/result-helpers.js +59 -0
- package/dist/server-contract/route-result-validation.d.ts +2 -0
- package/dist/server-contract/route-result-validation.js +73 -0
- package/dist/server-contract/stage.d.ts +6 -0
- package/dist/server-contract/stage.js +22 -0
- package/dist/server-contract.d.ts +6 -62
- package/dist/server-contract.js +9 -493
- package/dist/server-middleware.d.ts +10 -0
- package/dist/server-middleware.js +30 -0
- package/dist/server-output.js +13 -1
- package/dist/server-runtime/node-server.js +25 -3
- package/package.json +3 -3
|
@@ -45,17 +45,9 @@ function createFunctionSource(route) {
|
|
|
45
45
|
" return new Response(message, { status: 501, headers: { 'Content-Type': 'text/plain; charset=utf-8' } });",
|
|
46
46
|
'}',
|
|
47
47
|
'',
|
|
48
|
-
'function isMultipartFormData(request) {',
|
|
49
|
-
" const contentType = request.headers.get('content-type') || '';",
|
|
50
|
-
" return /^multipart\\/form-data(?:\\s*;|$)/i.test(contentType.trim());",
|
|
51
|
-
'}',
|
|
52
|
-
'',
|
|
53
48
|
'export default async function(request) {',
|
|
54
49
|
' const params = extractInternalParams(request.url, route);',
|
|
55
50
|
" if (route.route_kind === 'resource') {",
|
|
56
|
-
' if (isMultipartFormData(request)) {',
|
|
57
|
-
" return createHostedUnsupportedResponse('Hosted multipart resource routes are unsupported in this milestone');",
|
|
58
|
-
' }',
|
|
59
51
|
' const response = await renderResourceRouteRequest({',
|
|
60
52
|
' request,',
|
|
61
53
|
' route,',
|
|
@@ -66,23 +66,15 @@ function createFunctionSource(route) {
|
|
|
66
66
|
" return new Response(message, { status: 501, headers: { 'Content-Type': 'text/plain; charset=utf-8' } });",
|
|
67
67
|
'}',
|
|
68
68
|
'',
|
|
69
|
-
'function isMultipartFormData(request) {',
|
|
70
|
-
" const contentType = request.headers.get('content-type') || '';",
|
|
71
|
-
" return /^multipart\\/form-data(?:\\s*;|$)/i.test(contentType.trim());",
|
|
72
|
-
'}',
|
|
73
|
-
'',
|
|
74
69
|
'export default {',
|
|
75
70
|
' async fetch(request) {',
|
|
76
71
|
' const params = extractInternalParams(request.url, route);',
|
|
77
72
|
" if (route.route_kind === 'resource') {",
|
|
78
|
-
' if (isMultipartFormData(request)) {',
|
|
79
|
-
" return createHostedUnsupportedResponse('Hosted multipart resource routes are unsupported in this milestone');",
|
|
80
|
-
' }',
|
|
81
73
|
' const response = await renderResourceRouteRequest({',
|
|
82
74
|
' request,',
|
|
83
75
|
' route,',
|
|
84
76
|
' params,',
|
|
85
|
-
|
|
77
|
+
` routeModulePath: join(__dirname, 'routes', ${JSON.stringify(route.name)}, 'route', 'entry.js')`,
|
|
86
78
|
' });',
|
|
87
79
|
" if (response.headers.has('content-disposition')) {",
|
|
88
80
|
" return createHostedUnsupportedResponse('Hosted resource downloads are unsupported in this milestone');",
|
|
@@ -93,10 +85,10 @@ function createFunctionSource(route) {
|
|
|
93
85
|
' request,',
|
|
94
86
|
' route,',
|
|
95
87
|
' params,',
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
` pageAssetPath: ${route.page_asset_file ? "join(__dirname, 'route', " + JSON.stringify(route.page_asset_file) + ')' : 'null'},`,
|
|
99
|
-
` imageManifestPath: ${route.image_manifest_file ? "join(__dirname, 'route', " + JSON.stringify(route.image_manifest_file) + ')' : 'null'},`,
|
|
88
|
+
` routeModulePath: join(__dirname, 'routes', ${JSON.stringify(route.name)}, 'route', 'entry.js'),`,
|
|
89
|
+
` shellHtmlPath: join(__dirname, 'routes', ${JSON.stringify(route.name)}, 'route', 'page.html'),`,
|
|
90
|
+
` pageAssetPath: ${route.page_asset_file ? "join(__dirname, 'routes', " + JSON.stringify(route.name) + ", 'route', " + JSON.stringify(route.page_asset_file) + ')' : 'null'},`,
|
|
91
|
+
` imageManifestPath: ${route.image_manifest_file ? "join(__dirname, 'routes', " + JSON.stringify(route.name) + ", 'route', " + JSON.stringify(route.image_manifest_file) + ')' : 'null'},`,
|
|
100
92
|
` imageConfig: ${JSON.stringify(route.image_config || {}, null, 2)}`,
|
|
101
93
|
' });',
|
|
102
94
|
' }',
|
|
@@ -146,7 +138,7 @@ export const vercelAdapter = {
|
|
|
146
138
|
const functionDir = join(options.outDir, 'functions', '__zenith', `${route.name}.func`);
|
|
147
139
|
await mkdir(functionDir, { recursive: true });
|
|
148
140
|
await copyHostedPageRuntime(options.coreOutput, functionDir);
|
|
149
|
-
await cp(join(options.coreOutput, 'server', 'routes', route.name), functionDir, { recursive: true, force: true });
|
|
141
|
+
await cp(join(options.coreOutput, 'server', 'routes', route.name), join(functionDir, 'routes', route.name), { recursive: true, force: true });
|
|
150
142
|
await writeFile(join(functionDir, 'package.json'), '{\n "type": "module"\n}\n', 'utf8');
|
|
151
143
|
await writeFile(join(functionDir, 'index.js'), createFunctionSource(route), 'utf8');
|
|
152
144
|
await writeFile(join(functionDir, '.vc-config.json'), vercelFunctionConfig(), 'utf8');
|
|
@@ -3,10 +3,11 @@ import { createRequire } from 'node:module';
|
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { pathToFileURL } from 'node:url';
|
|
5
5
|
const PACKAGE_REQUIRE = createRequire(import.meta.url);
|
|
6
|
-
const HOSTED_PAGE_RUNTIME_DIRS = ['runtime', 'images', 'auth'];
|
|
6
|
+
const HOSTED_PAGE_RUNTIME_DIRS = ['runtime', 'images', 'auth', 'server-contract'];
|
|
7
7
|
const HOSTED_PAGE_RUNTIME_FILES = [
|
|
8
8
|
'base-path.js',
|
|
9
9
|
'server-contract.js',
|
|
10
|
+
'server-middleware.js',
|
|
10
11
|
'server-error.js',
|
|
11
12
|
'resource-response.js',
|
|
12
13
|
'download-result.js'
|
|
@@ -21,9 +21,12 @@ export function rewriteStaticImportsInSource(source: string, fromFile: string, t
|
|
|
21
21
|
* @param {string} sourceFile
|
|
22
22
|
* @param {object | null} [transformCache]
|
|
23
23
|
* @param {Record<string, number> | null} [mergeMetrics]
|
|
24
|
+
* @param {{ target?: 'es5' | 'esnext' }} [options]
|
|
24
25
|
* @returns {string}
|
|
25
26
|
*/
|
|
26
|
-
export function transpileTypeScriptToJs(source: string, sourceFile: string, transformCache?: object | null, mergeMetrics?: Record<string, number> | null
|
|
27
|
+
export function transpileTypeScriptToJs(source: string, sourceFile: string, transformCache?: object | null, mergeMetrics?: Record<string, number> | null, options?: {
|
|
28
|
+
target?: "es5" | "esnext";
|
|
29
|
+
}): string;
|
|
27
30
|
/**
|
|
28
31
|
* @param {string} source
|
|
29
32
|
* @param {Set<string>} seenStaticImports
|
|
@@ -76,11 +76,13 @@ export function rewriteStaticImportsInSource(source, fromFile, toFile) {
|
|
|
76
76
|
* @param {string} sourceFile
|
|
77
77
|
* @param {object | null} [transformCache]
|
|
78
78
|
* @param {Record<string, number> | null} [mergeMetrics]
|
|
79
|
+
* @param {{ target?: 'es5' | 'esnext' }} [options]
|
|
79
80
|
* @returns {string}
|
|
80
81
|
*/
|
|
81
|
-
export function transpileTypeScriptToJs(source, sourceFile, transformCache = null, mergeMetrics = null) {
|
|
82
|
+
export function transpileTypeScriptToJs(source, sourceFile, transformCache = null, mergeMetrics = null, options = {}) {
|
|
83
|
+
const target = options?.target === 'esnext' ? 'esnext' : 'es5';
|
|
82
84
|
const cacheKey = transformCache?.transpileToJs instanceof Map
|
|
83
|
-
? `${sourceFile}\u0000${source}`
|
|
85
|
+
? `${sourceFile}\u0000${target}\u0000${source}`
|
|
84
86
|
: null;
|
|
85
87
|
if (cacheKey && transformCache.transpileToJs.has(cacheKey)) {
|
|
86
88
|
if (mergeMetrics && typeof mergeMetrics === 'object') {
|
|
@@ -97,7 +99,7 @@ export function transpileTypeScriptToJs(source, sourceFile, transformCache = nul
|
|
|
97
99
|
fileName: sourceFile,
|
|
98
100
|
compilerOptions: {
|
|
99
101
|
module: ts.ModuleKind.ESNext,
|
|
100
|
-
target: ts.ScriptTarget.ES5,
|
|
102
|
+
target: target === 'esnext' ? ts.ScriptTarget.ESNext : ts.ScriptTarget.ES5,
|
|
101
103
|
importsNotUsedAsValues: ts.ImportsNotUsedAsValues.Preserve,
|
|
102
104
|
verbatimModuleSyntax: true,
|
|
103
105
|
newLine: ts.NewLineKind.LineFeed,
|
|
@@ -11,5 +11,5 @@ export function rewriteRefBindingIdentifiers(pageIr: object, preferredKeys?: Set
|
|
|
11
11
|
*/
|
|
12
12
|
export function applyExpressionRewrites(pageIr: object, expressionMap: Map<string, string>, bindingMap: Map<string, object>, ambiguous: Set<string>): void;
|
|
13
13
|
export function normalizeExpressionPayload(pageIr: any): void;
|
|
14
|
-
export function normalizeHoistedSourcePayload(pageIr: any): void;
|
|
14
|
+
export function normalizeHoistedSourcePayload(pageIr: any, sourceFile?: string, transformCache?: null, mergeMetrics?: null): void;
|
|
15
15
|
export function rewriteLegacyMarkupIdentifiers(pageIr: any): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolveStateKeyFromBindings } from './expression-rewrites.js';
|
|
2
|
+
import { transpileTypeScriptToJs } from './hoisted-code-transforms.js';
|
|
2
3
|
import { expandScopedShorthandPropertiesInSource, normalizeTypeScriptExpression } from './typescript-expression-utils.js';
|
|
3
4
|
/**
|
|
4
5
|
* @param {object} pageIr
|
|
@@ -89,7 +90,7 @@ export function normalizeExpressionPayload(pageIr) {
|
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
|
-
export function normalizeHoistedSourcePayload(pageIr) {
|
|
93
|
+
export function normalizeHoistedSourcePayload(pageIr, sourceFile = 'component.zen', transformCache = null, mergeMetrics = null) {
|
|
93
94
|
const declarations = Array.isArray(pageIr?.hoisted?.declarations) ? pageIr.hoisted.declarations : null;
|
|
94
95
|
if (declarations) {
|
|
95
96
|
pageIr.hoisted.declarations = declarations.map((entry) => {
|
|
@@ -101,11 +102,40 @@ export function normalizeHoistedSourcePayload(pageIr) {
|
|
|
101
102
|
}
|
|
102
103
|
const codeBlocks = Array.isArray(pageIr?.hoisted?.code) ? pageIr.hoisted.code : null;
|
|
103
104
|
if (codeBlocks) {
|
|
104
|
-
pageIr.hoisted.code = codeBlocks.map((entry) => {
|
|
105
|
+
pageIr.hoisted.code = codeBlocks.map((entry, index) => {
|
|
105
106
|
if (typeof entry !== 'string') {
|
|
106
107
|
return entry;
|
|
107
108
|
}
|
|
108
|
-
|
|
109
|
+
const expanded = expandScopedShorthandPropertiesInSource(entry);
|
|
110
|
+
return transpileTypeScriptToJs(expanded, `${sourceFile}#hoisted-${index}.ts`, transformCache, mergeMetrics, { target: 'esnext' });
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const componentScripts = pageIr?.components_scripts && typeof pageIr.components_scripts === 'object'
|
|
114
|
+
? pageIr.components_scripts
|
|
115
|
+
: null;
|
|
116
|
+
if (componentScripts) {
|
|
117
|
+
for (const [hoistId, script] of Object.entries(componentScripts)) {
|
|
118
|
+
if (!script || typeof script !== 'object' || typeof script.code !== 'string') {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const expanded = expandScopedShorthandPropertiesInSource(script.code);
|
|
122
|
+
script.code = transpileTypeScriptToJs(expanded, `${sourceFile}#component-${hoistId}.ts`, transformCache, mergeMetrics, { target: 'esnext' });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const modules = Array.isArray(pageIr?.modules) ? pageIr.modules : null;
|
|
126
|
+
if (modules) {
|
|
127
|
+
pageIr.modules = modules.map((module, index) => {
|
|
128
|
+
if (!module || typeof module !== 'object' || typeof module.source !== 'string') {
|
|
129
|
+
return module;
|
|
130
|
+
}
|
|
131
|
+
const moduleId = typeof module.id === 'string' && module.id.length > 0
|
|
132
|
+
? module.id
|
|
133
|
+
: `${sourceFile}#module-${index}.ts`;
|
|
134
|
+
const expanded = expandScopedShorthandPropertiesInSource(module.source);
|
|
135
|
+
return {
|
|
136
|
+
...module,
|
|
137
|
+
source: transpileTypeScriptToJs(expanded, moduleId, transformCache, mergeMetrics, { target: 'esnext' })
|
|
138
|
+
};
|
|
109
139
|
});
|
|
110
140
|
}
|
|
111
141
|
}
|
package/dist/build/page-loop.js
CHANGED
|
@@ -165,7 +165,7 @@ export async function buildPageEnvelopes(input) {
|
|
|
165
165
|
pagePhase.expressionApplyMs = startupProfile.roundMs(performance.now() - expressionApplyStartedAt);
|
|
166
166
|
const normalizeStartedAt = performance.now();
|
|
167
167
|
normalizeExpressionPayload(pageIr);
|
|
168
|
-
normalizeHoistedSourcePayload(pageIr);
|
|
168
|
+
normalizeHoistedSourcePayload(pageIr, sourceFile, hoistedCodeTransformCache, expressionRewriteMetrics);
|
|
169
169
|
if (Array.isArray(pageIr?.hoisted?.code) && pageIr.hoisted.code.length > 0) {
|
|
170
170
|
pageIr.hoisted.code = pageIr.hoisted.code
|
|
171
171
|
.map((entry) => deferComponentRuntimeBlock(entry, hoistedCodeTransformCache, expressionRewriteMetrics))
|
|
@@ -174,6 +174,26 @@ export async function buildPageEnvelopes(input) {
|
|
|
174
174
|
rewriteLegacyMarkupIdentifiers(pageIr);
|
|
175
175
|
rewriteRefBindingIdentifiers(pageIr, knownRefKeys);
|
|
176
176
|
pagePhase.normalizeMs = startupProfile.roundMs(performance.now() - normalizeStartedAt);
|
|
177
|
+
const requiresJs = detectRequiresJs(pageIr, routerEnabled);
|
|
178
|
+
if (!requiresJs) {
|
|
179
|
+
console.log(`[DEBUG] Route ${entry.path} is STATIC (no JS required)`);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
const reasons = [];
|
|
183
|
+
if (routerEnabled)
|
|
184
|
+
reasons.push('routerEnabled');
|
|
185
|
+
if (pageIr.signals.length > 0)
|
|
186
|
+
reasons.push('signals');
|
|
187
|
+
if (pageIr.event_bindings.length > 0)
|
|
188
|
+
reasons.push('event_bindings');
|
|
189
|
+
if (pageIr.marker_bindings.length > 0)
|
|
190
|
+
reasons.push('marker_bindings');
|
|
191
|
+
if (pageIr.component_instances.length > 0)
|
|
192
|
+
reasons.push('component_instances');
|
|
193
|
+
if (pageIr.hoisted?.code?.length > 0)
|
|
194
|
+
reasons.push('hoisted.code');
|
|
195
|
+
console.log(`[DEBUG] Route ${entry.path} is INTERACTIVE. Reasons: ${reasons.join(', ')}`);
|
|
196
|
+
}
|
|
177
197
|
addBreakdown(pagePhaseTotals, pagePhase);
|
|
178
198
|
addBreakdown(occurrenceApplyPhaseTotals, pageOccurrenceApplyBreakdown);
|
|
179
199
|
addBreakdown(bindingResolutionTotals, pageBindingResolutionBreakdown);
|
|
@@ -187,7 +207,8 @@ export async function buildPageEnvelopes(input) {
|
|
|
187
207
|
image_materialization: Array.isArray(pageIr.image_materialization)
|
|
188
208
|
? pageIr.image_materialization
|
|
189
209
|
: [],
|
|
190
|
-
router: routerEnabled
|
|
210
|
+
router: routerEnabled,
|
|
211
|
+
requires_js: requiresJs
|
|
191
212
|
});
|
|
192
213
|
recordPageProfile({
|
|
193
214
|
pageProfiles,
|
|
@@ -220,3 +241,26 @@ export async function buildPageEnvelopes(input) {
|
|
|
220
241
|
});
|
|
221
242
|
return { envelopes, expressionRewriteMetrics };
|
|
222
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Detects if a page requires client-side JavaScript based on its IR.
|
|
246
|
+
* This is a conservative pass used for Static Route Omission.
|
|
247
|
+
*
|
|
248
|
+
* @param {object} pageIr
|
|
249
|
+
* @param {boolean} routerEnabled
|
|
250
|
+
* @returns {boolean}
|
|
251
|
+
*/
|
|
252
|
+
function detectRequiresJs(pageIr, routerEnabled) {
|
|
253
|
+
if (routerEnabled === true) {
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
const { signals = [], event_bindings = [], ref_bindings = [], marker_bindings = [], component_instances = [], hoisted = {} } = pageIr;
|
|
257
|
+
const requiresJs = (signals.length > 0 ||
|
|
258
|
+
event_bindings.length > 0 ||
|
|
259
|
+
ref_bindings.length > 0 ||
|
|
260
|
+
(Array.isArray(hoisted.signals) && hoisted.signals.length > 0) ||
|
|
261
|
+
(Array.isArray(hoisted.state) && hoisted.state.length > 0) ||
|
|
262
|
+
(Array.isArray(hoisted.code) && hoisted.code.filter(c => String(c).trim().length > 0).length > 0) ||
|
|
263
|
+
component_instances.some(instance => Array.isArray(instance.props) &&
|
|
264
|
+
instance.props.some(prop => prop.type === 'signal' || prop.type === 'binding' || prop.type === 'callback' || prop.type === 'reactive')));
|
|
265
|
+
return requiresJs;
|
|
266
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function createCompilerTotals(): {
|
|
2
|
+
pageMs: number;
|
|
3
|
+
ownerMs: number;
|
|
4
|
+
componentMs: number;
|
|
5
|
+
pageCalls: number;
|
|
6
|
+
ownerCalls: number;
|
|
7
|
+
componentCalls: number;
|
|
8
|
+
componentCacheHits: number;
|
|
9
|
+
componentCacheMisses: number;
|
|
10
|
+
};
|
|
11
|
+
export function createExpressionRewriteMetrics(): {
|
|
12
|
+
calls: number;
|
|
13
|
+
compilerOwnedBindings: number;
|
|
14
|
+
ambiguousBindings: number;
|
|
15
|
+
};
|
|
16
|
+
export function toManifestEntryMap(manifest: any, pagesDir: any): Map<any, any>;
|
|
17
|
+
export function orderEnvelopes(manifest: any, pagesDir: any, envelopeByFile: any): any[] | null;
|
|
18
|
+
export function isCssOnlyChange(changedFiles: any): any;
|
|
19
|
+
export function buildPageOnlyFastPathSignature(envelope: any): any;
|
|
20
|
+
export function buildGlobalGraphHash(envelopes: any): string;
|
|
21
|
+
export function selectPageOnlyEntries(changedFiles: any, pagesDir: any, manifestEntryByPath: any): any[];
|
|
22
|
+
export function maybeRunVersionCheck({ state, startupProfile, projectRoot, logger, bundlerBin }: {
|
|
23
|
+
state: any;
|
|
24
|
+
startupProfile: any;
|
|
25
|
+
projectRoot: any;
|
|
26
|
+
logger: any;
|
|
27
|
+
bundlerBin: any;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
export function buildCompilerWarningEmitter(logger: any): (line: string) => void;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { createCompilerWarningEmitter } from '../build/compiler-runtime.js';
|
|
5
|
+
import { resolveBundlerBin } from '../toolchain-paths.js';
|
|
6
|
+
import { getActiveToolchainCandidate } from '../toolchain-runner.js';
|
|
7
|
+
import { maybeWarnAboutZenithVersionMismatch } from '../version-check.js';
|
|
8
|
+
export function createCompilerTotals() {
|
|
9
|
+
return {
|
|
10
|
+
pageMs: 0,
|
|
11
|
+
ownerMs: 0,
|
|
12
|
+
componentMs: 0,
|
|
13
|
+
pageCalls: 0,
|
|
14
|
+
ownerCalls: 0,
|
|
15
|
+
componentCalls: 0,
|
|
16
|
+
componentCacheHits: 0,
|
|
17
|
+
componentCacheMisses: 0
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function createExpressionRewriteMetrics() {
|
|
21
|
+
return {
|
|
22
|
+
calls: 0,
|
|
23
|
+
compilerOwnedBindings: 0,
|
|
24
|
+
ambiguousBindings: 0
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function toManifestEntryMap(manifest, pagesDir) {
|
|
28
|
+
const map = new Map();
|
|
29
|
+
for (const entry of manifest) {
|
|
30
|
+
map.set(resolve(pagesDir, entry.file), entry);
|
|
31
|
+
}
|
|
32
|
+
return map;
|
|
33
|
+
}
|
|
34
|
+
export function orderEnvelopes(manifest, pagesDir, envelopeByFile) {
|
|
35
|
+
const ordered = [];
|
|
36
|
+
for (const entry of manifest) {
|
|
37
|
+
const envelope = envelopeByFile.get(resolve(pagesDir, entry.file));
|
|
38
|
+
if (!envelope) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
ordered.push(envelope);
|
|
42
|
+
}
|
|
43
|
+
return ordered;
|
|
44
|
+
}
|
|
45
|
+
export function isCssOnlyChange(changedFiles) {
|
|
46
|
+
return changedFiles.length > 0 && changedFiles.every((filePath) => filePath.endsWith('.css'));
|
|
47
|
+
}
|
|
48
|
+
function stableJson(value) {
|
|
49
|
+
if (value === null || value === undefined) {
|
|
50
|
+
return 'null';
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(value)) {
|
|
53
|
+
return `[${value.map((entry) => stableJson(entry)).join(',')}]`;
|
|
54
|
+
}
|
|
55
|
+
if (typeof value === 'object') {
|
|
56
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(',')}}`;
|
|
57
|
+
}
|
|
58
|
+
return JSON.stringify(value);
|
|
59
|
+
}
|
|
60
|
+
function collectJsImportSpecifiers(source) {
|
|
61
|
+
const values = [];
|
|
62
|
+
const patterns = [
|
|
63
|
+
/\bimport\s+(?:[^'"\n;]*?\s+from\s+)?['"]([^'"]+)['"]/g,
|
|
64
|
+
/\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
65
|
+
/\bexport\s+[^'"\n;]*?\s+from\s+['"]([^'"]+)['"]/g
|
|
66
|
+
];
|
|
67
|
+
for (const pattern of patterns) {
|
|
68
|
+
pattern.lastIndex = 0;
|
|
69
|
+
for (const match of source.matchAll(pattern)) {
|
|
70
|
+
const value = String(match[1] || '').trim();
|
|
71
|
+
if (value.length > 0 && !values.includes(value)) {
|
|
72
|
+
values.push(value);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return values.sort();
|
|
77
|
+
}
|
|
78
|
+
function isExternalRuntimeSpecifier(specifier) {
|
|
79
|
+
return !specifier.startsWith('.')
|
|
80
|
+
&& !specifier.startsWith('/')
|
|
81
|
+
&& !specifier.startsWith('@/')
|
|
82
|
+
&& !specifier.startsWith('\0zenith:')
|
|
83
|
+
&& !specifier.includes('zenith:');
|
|
84
|
+
}
|
|
85
|
+
function collectEnvelopeAssetContract(envelope) {
|
|
86
|
+
const cssImportSpecifiers = new Set();
|
|
87
|
+
const externalImportSpecifiers = new Set();
|
|
88
|
+
for (const entry of envelope.ir.hoisted?.imports || []) {
|
|
89
|
+
for (const specifier of collectJsImportSpecifiers(String(entry || ''))) {
|
|
90
|
+
if (specifier.endsWith('.css')) {
|
|
91
|
+
cssImportSpecifiers.add(specifier);
|
|
92
|
+
}
|
|
93
|
+
if (isExternalRuntimeSpecifier(specifier)) {
|
|
94
|
+
externalImportSpecifiers.add(specifier);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
for (const moduleEntry of envelope.ir.modules || []) {
|
|
99
|
+
for (const specifier of collectJsImportSpecifiers(String(moduleEntry?.source || ''))) {
|
|
100
|
+
if (specifier.endsWith('.css')) {
|
|
101
|
+
cssImportSpecifiers.add(specifier);
|
|
102
|
+
}
|
|
103
|
+
if (isExternalRuntimeSpecifier(specifier)) {
|
|
104
|
+
externalImportSpecifiers.add(specifier);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const importEntry of envelope.ir.imports || []) {
|
|
109
|
+
const specifier = String(importEntry?.spec || '').trim();
|
|
110
|
+
if (!specifier) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (specifier.endsWith('.css')) {
|
|
114
|
+
cssImportSpecifiers.add(specifier);
|
|
115
|
+
}
|
|
116
|
+
if (isExternalRuntimeSpecifier(specifier)) {
|
|
117
|
+
externalImportSpecifiers.add(specifier);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
componentHoistIds: Object.keys(envelope.ir.components_scripts || {}).sort(),
|
|
122
|
+
cssImportSpecifiers: [...cssImportSpecifiers].sort(),
|
|
123
|
+
externalImportSpecifiers: [...externalImportSpecifiers].sort()
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function collectTemplateClassSignature(envelope) {
|
|
127
|
+
const html = typeof envelope?.ir?.html === 'string' ? envelope.ir.html : '';
|
|
128
|
+
if (!html) {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
const classes = new Set();
|
|
132
|
+
const classAttrRe = /\bclass\s*=\s*(?:"([^"]*)"|'([^']*)')/gi;
|
|
133
|
+
let match;
|
|
134
|
+
while ((match = classAttrRe.exec(html)) !== null) {
|
|
135
|
+
const rawValue = String(match[1] || match[2] || '');
|
|
136
|
+
for (const token of rawValue.split(/\s+/)) {
|
|
137
|
+
const value = token.trim();
|
|
138
|
+
if (value.length > 0) {
|
|
139
|
+
classes.add(value);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return [...classes].sort();
|
|
144
|
+
}
|
|
145
|
+
export function buildPageOnlyFastPathSignature(envelope) {
|
|
146
|
+
return stableJson({
|
|
147
|
+
route: envelope.route,
|
|
148
|
+
router: envelope.router === true,
|
|
149
|
+
assetContract: collectEnvelopeAssetContract(envelope),
|
|
150
|
+
templateClassSignature: collectTemplateClassSignature(envelope),
|
|
151
|
+
styleBlocks: envelope.ir.style_blocks || [],
|
|
152
|
+
serverScript: envelope.ir.server_script || null,
|
|
153
|
+
prerender: envelope.ir.prerender === true,
|
|
154
|
+
hasGuard: envelope.ir.has_guard === true,
|
|
155
|
+
hasLoad: envelope.ir.has_load === true,
|
|
156
|
+
guardModuleRef: envelope.ir.guard_module_ref || null,
|
|
157
|
+
loadModuleRef: envelope.ir.load_module_ref || null
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
export function buildGlobalGraphHash(envelopes) {
|
|
161
|
+
const nodesByHoistId = new Map();
|
|
162
|
+
const edgeSet = new Set();
|
|
163
|
+
for (const envelope of envelopes) {
|
|
164
|
+
for (const node of envelope.ir.graph_nodes || []) {
|
|
165
|
+
if (node && typeof node.hoist_id === 'string' && node.hoist_id.length > 0) {
|
|
166
|
+
nodesByHoistId.set(node.hoist_id, true);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
for (const edge of envelope.ir.graph_edges || []) {
|
|
170
|
+
if (typeof edge === 'string' && edge.length > 0) {
|
|
171
|
+
edgeSet.add(edge);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
let seed = '';
|
|
176
|
+
for (const hoistId of [...nodesByHoistId.keys()].sort()) {
|
|
177
|
+
seed += `node:${hoistId}\n`;
|
|
178
|
+
}
|
|
179
|
+
for (const edge of [...edgeSet].sort()) {
|
|
180
|
+
seed += `edge:${edge}\n`;
|
|
181
|
+
}
|
|
182
|
+
return createHash('sha256').update(seed).digest('hex');
|
|
183
|
+
}
|
|
184
|
+
export function selectPageOnlyEntries(changedFiles, pagesDir, manifestEntryByPath) {
|
|
185
|
+
if (changedFiles.length === 0) {
|
|
186
|
+
return [];
|
|
187
|
+
}
|
|
188
|
+
const selected = new Map();
|
|
189
|
+
for (const filePath of changedFiles) {
|
|
190
|
+
const resolvedPath = resolve(filePath);
|
|
191
|
+
if (!resolvedPath.startsWith(pagesDir) || !resolvedPath.endsWith('.zen') || !existsSync(resolvedPath)) {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
const entry = manifestEntryByPath.get(resolvedPath);
|
|
195
|
+
if (!entry) {
|
|
196
|
+
return [];
|
|
197
|
+
}
|
|
198
|
+
selected.set(entry.file, entry);
|
|
199
|
+
}
|
|
200
|
+
return [...selected.values()];
|
|
201
|
+
}
|
|
202
|
+
export async function maybeRunVersionCheck({ state, startupProfile, projectRoot, logger, bundlerBin }) {
|
|
203
|
+
if (state.versionChecked) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const resolvedBundlerCandidate = getActiveToolchainCandidate(bundlerBin);
|
|
207
|
+
await startupProfile.measureAsync('version_mismatch_check', () => maybeWarnAboutZenithVersionMismatch({
|
|
208
|
+
projectRoot,
|
|
209
|
+
logger,
|
|
210
|
+
command: 'dev',
|
|
211
|
+
bundlerBinPath: resolvedBundlerCandidate?.path || resolveBundlerBin(projectRoot)
|
|
212
|
+
}));
|
|
213
|
+
state.versionChecked = true;
|
|
214
|
+
}
|
|
215
|
+
export function buildCompilerWarningEmitter(logger) {
|
|
216
|
+
return createCompilerWarningEmitter((line) => {
|
|
217
|
+
if (logger && typeof logger.warn === 'function') {
|
|
218
|
+
logger.warn(line, { onceKey: `compiler-warning:${line}` });
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
console.warn(line);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function createDevBuildSession(options: any): {
|
|
2
|
+
build(buildOptions?: {}): Promise<{
|
|
3
|
+
pages: number;
|
|
4
|
+
assets: any;
|
|
5
|
+
strategy: string;
|
|
6
|
+
}>;
|
|
7
|
+
getImageRuntimePayload(): {
|
|
8
|
+
mode: string;
|
|
9
|
+
basePath: string;
|
|
10
|
+
config: {
|
|
11
|
+
formats: string[];
|
|
12
|
+
deviceSizes: number[];
|
|
13
|
+
imageSizes: number[];
|
|
14
|
+
remotePatterns: any[];
|
|
15
|
+
quality: number;
|
|
16
|
+
allowSvg: boolean;
|
|
17
|
+
maxRemoteBytes: number;
|
|
18
|
+
maxPixels: number;
|
|
19
|
+
minimumCacheTTL: number;
|
|
20
|
+
dangerouslyAllowLocalNetwork: boolean;
|
|
21
|
+
};
|
|
22
|
+
localImages: any;
|
|
23
|
+
} | null;
|
|
24
|
+
};
|