@zenithbuild/cli 0.7.4 → 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/README.md +5 -3
- package/dist/adapters/adapter-netlify.d.ts +1 -1
- package/dist/adapters/adapter-netlify.js +48 -14
- package/dist/adapters/adapter-static-export.d.ts +5 -0
- package/dist/adapters/adapter-static-export.js +115 -0
- package/dist/adapters/adapter-types.d.ts +3 -1
- package/dist/adapters/adapter-types.js +5 -2
- package/dist/adapters/adapter-vercel.d.ts +1 -1
- package/dist/adapters/adapter-vercel.js +67 -19
- package/dist/adapters/copy-hosted-page-runtime.d.ts +1 -0
- package/dist/adapters/copy-hosted-page-runtime.js +50 -0
- package/dist/adapters/resolve-adapter.js +4 -0
- package/dist/adapters/route-rules.d.ts +5 -0
- package/dist/adapters/route-rules.js +9 -0
- package/dist/adapters/validate-hosted-resource-routes.d.ts +1 -0
- package/dist/adapters/validate-hosted-resource-routes.js +13 -0
- package/dist/auth/route-auth.d.ts +6 -0
- package/dist/auth/route-auth.js +236 -0
- package/dist/build/compiler-runtime.d.ts +1 -1
- package/dist/build/compiler-runtime.js +8 -2
- 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-state.js +1 -1
- package/dist/build/page-loop.js +46 -2
- package/dist/build/server-script.d.ts +2 -1
- package/dist/build/server-script.js +7 -3
- package/dist/build-output-manifest.d.ts +3 -2
- package/dist/build-output-manifest.js +3 -0
- package/dist/build.js +29 -17
- 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 +136 -883
- package/dist/download-result.d.ts +14 -0
- package/dist/download-result.js +148 -0
- package/dist/images/payload.js +4 -0
- package/dist/images/service.d.ts +13 -1
- package/dist/images/service.js +45 -15
- package/dist/manifest.d.ts +15 -1
- package/dist/manifest.js +70 -6
- 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 -104
- package/dist/preview.js +7 -993
- package/dist/request-body.d.ts +0 -1
- package/dist/request-body.js +0 -6
- package/dist/resource-manifest.d.ts +16 -0
- package/dist/resource-manifest.js +53 -0
- package/dist/resource-response.d.ts +49 -0
- package/dist/resource-response.js +160 -0
- package/dist/resource-route-module.d.ts +15 -0
- package/dist/resource-route-module.js +129 -0
- package/dist/route-check-support.js +1 -1
- 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 -54
- package/dist/server-contract.js +9 -301
- package/dist/server-error.d.ts +1 -1
- package/dist/server-error.js +2 -0
- package/dist/server-middleware.d.ts +10 -0
- package/dist/server-middleware.js +30 -0
- package/dist/server-output.d.ts +2 -1
- package/dist/server-output.js +72 -12
- package/dist/server-runtime/node-server.js +59 -7
- package/dist/server-runtime/route-render.d.ts +25 -1
- package/dist/server-runtime/route-render.js +81 -29
- package/dist/server-script-composition.d.ts +4 -2
- package/dist/server-script-composition.js +6 -3
- package/dist/static-export-paths.d.ts +3 -0
- package/dist/static-export-paths.js +160 -0
- package/package.json +3 -3
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
import { assertJsonSerializable } from '../server-contract.js';
|
|
3
|
+
export const SESSION_COOKIE_NAME = 'zenith_session';
|
|
4
|
+
export const SESSION_SECRET_ENV = 'ZENITH_SESSION_SECRET';
|
|
5
|
+
export const STAGED_SET_COOKIES_KEY = '__zenith_staged_set_cookies';
|
|
6
|
+
export const AUTH_CONTROL_FLOW_FLAG = '__zenith_auth_control_flow';
|
|
7
|
+
const SESSION_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 7;
|
|
8
|
+
const SESSION_COOKIE_MAX_BYTES = 3800;
|
|
9
|
+
const SESSION_SCHEMA_VERSION = 1;
|
|
10
|
+
function createAuthError(message) {
|
|
11
|
+
return new Error(`[Zenith] ${message}`);
|
|
12
|
+
}
|
|
13
|
+
function base64urlEncode(input) {
|
|
14
|
+
return Buffer.from(input)
|
|
15
|
+
.toString('base64')
|
|
16
|
+
.replace(/\+/g, '-')
|
|
17
|
+
.replace(/\//g, '_')
|
|
18
|
+
.replace(/=+$/g, '');
|
|
19
|
+
}
|
|
20
|
+
function base64urlDecode(input) {
|
|
21
|
+
const normalized = String(input || '')
|
|
22
|
+
.replace(/-/g, '+')
|
|
23
|
+
.replace(/_/g, '/');
|
|
24
|
+
const padded = normalized + '='.repeat((4 - (normalized.length % 4 || 4)) % 4);
|
|
25
|
+
return Buffer.from(padded, 'base64').toString('utf8');
|
|
26
|
+
}
|
|
27
|
+
function isPlainObject(value) {
|
|
28
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const proto = Object.getPrototypeOf(value);
|
|
32
|
+
return proto === null || proto === Object.prototype || proto?.constructor?.name === 'Object';
|
|
33
|
+
}
|
|
34
|
+
function readSessionSecret() {
|
|
35
|
+
const secret = typeof process?.env?.[SESSION_SECRET_ENV] === 'string'
|
|
36
|
+
? process.env[SESSION_SECRET_ENV].trim()
|
|
37
|
+
: '';
|
|
38
|
+
if (!secret) {
|
|
39
|
+
throw createAuthError(`ctx.auth requires ${SESSION_SECRET_ENV} to be set`);
|
|
40
|
+
}
|
|
41
|
+
return secret;
|
|
42
|
+
}
|
|
43
|
+
function signPayload(payload, secret) {
|
|
44
|
+
return createHmac('sha256', secret).update(payload).digest('base64url');
|
|
45
|
+
}
|
|
46
|
+
function createSignedSessionValue(session, secret) {
|
|
47
|
+
if (!isPlainObject(session)) {
|
|
48
|
+
throw createAuthError('ctx.auth.signIn(sessionObject) requires a JSON-safe plain object');
|
|
49
|
+
}
|
|
50
|
+
assertJsonSerializable(session, 'ctx.auth.signIn(sessionObject)');
|
|
51
|
+
const envelope = {
|
|
52
|
+
v: SESSION_SCHEMA_VERSION,
|
|
53
|
+
exp: Date.now() + SESSION_COOKIE_MAX_AGE_SECONDS * 1000,
|
|
54
|
+
session
|
|
55
|
+
};
|
|
56
|
+
const json = JSON.stringify(envelope);
|
|
57
|
+
const encodedPayload = base64urlEncode(json);
|
|
58
|
+
const signature = signPayload(encodedPayload, secret);
|
|
59
|
+
const token = `${encodedPayload}.${signature}`;
|
|
60
|
+
if (Buffer.byteLength(token, 'utf8') > SESSION_COOKIE_MAX_BYTES) {
|
|
61
|
+
throw createAuthError('ctx.auth.signIn(sessionObject) produced an oversized session cookie');
|
|
62
|
+
}
|
|
63
|
+
return token;
|
|
64
|
+
}
|
|
65
|
+
function parseSessionValue(rawValue, secret) {
|
|
66
|
+
if (typeof rawValue !== 'string' || rawValue.length === 0) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const dot = rawValue.lastIndexOf('.');
|
|
70
|
+
if (dot <= 0 || dot === rawValue.length - 1) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const encodedPayload = rawValue.slice(0, dot);
|
|
74
|
+
const receivedSignature = rawValue.slice(dot + 1);
|
|
75
|
+
const expectedSignature = signPayload(encodedPayload, secret);
|
|
76
|
+
const received = Buffer.from(receivedSignature);
|
|
77
|
+
const expected = Buffer.from(expectedSignature);
|
|
78
|
+
if (received.length !== expected.length || !timingSafeEqual(received, expected)) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const envelope = JSON.parse(base64urlDecode(encodedPayload));
|
|
83
|
+
if (!envelope || typeof envelope !== 'object' || envelope.v !== SESSION_SCHEMA_VERSION) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
if (!Number.isFinite(envelope.exp) || envelope.exp <= Date.now()) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
if (!isPlainObject(envelope.session)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
assertJsonSerializable(envelope.session, 'ctx.auth session payload');
|
|
93
|
+
return envelope.session;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function shouldUseSecureCookies(requestUrl) {
|
|
100
|
+
try {
|
|
101
|
+
const url = requestUrl instanceof URL ? requestUrl : new URL(String(requestUrl || 'http://localhost/'));
|
|
102
|
+
return url.protocol === 'https:';
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function buildCookieAttributes(requestUrl) {
|
|
109
|
+
const attributes = ['Path=/', 'HttpOnly', 'SameSite=Lax'];
|
|
110
|
+
if (shouldUseSecureCookies(requestUrl)) {
|
|
111
|
+
attributes.push('Secure');
|
|
112
|
+
}
|
|
113
|
+
return attributes;
|
|
114
|
+
}
|
|
115
|
+
function buildSessionSetCookie(value, requestUrl) {
|
|
116
|
+
const attributes = buildCookieAttributes(requestUrl);
|
|
117
|
+
attributes.push(`Max-Age=${SESSION_COOKIE_MAX_AGE_SECONDS}`);
|
|
118
|
+
attributes.push(`Expires=${new Date(Date.now() + SESSION_COOKIE_MAX_AGE_SECONDS * 1000).toUTCString()}`);
|
|
119
|
+
return `${SESSION_COOKIE_NAME}=${encodeURIComponent(value)}; ${attributes.join('; ')}`;
|
|
120
|
+
}
|
|
121
|
+
function buildSessionClearCookie(requestUrl) {
|
|
122
|
+
const attributes = buildCookieAttributes(requestUrl);
|
|
123
|
+
attributes.push('Max-Age=0');
|
|
124
|
+
attributes.push('Expires=Thu, 01 Jan 1970 00:00:00 GMT');
|
|
125
|
+
return `${SESSION_COOKIE_NAME}=; ${attributes.join('; ')}`;
|
|
126
|
+
}
|
|
127
|
+
function stageSetCookie(ctx, value) {
|
|
128
|
+
if (!Array.isArray(ctx[STAGED_SET_COOKIES_KEY])) {
|
|
129
|
+
Object.defineProperty(ctx, STAGED_SET_COOKIES_KEY, {
|
|
130
|
+
value: [],
|
|
131
|
+
enumerable: false,
|
|
132
|
+
configurable: true
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
ctx[STAGED_SET_COOKIES_KEY].push(value);
|
|
136
|
+
}
|
|
137
|
+
function toAuthControlFlow(result) {
|
|
138
|
+
const error = new Error('auth control flow');
|
|
139
|
+
error[AUTH_CONTROL_FLOW_FLAG] = true;
|
|
140
|
+
error.result = result;
|
|
141
|
+
return error;
|
|
142
|
+
}
|
|
143
|
+
function normalizeRequirePolicy(options, redirect, deny) {
|
|
144
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
145
|
+
throw createAuthError('ctx.auth.requireSession(...) requires an explicit redirect or deny policy object');
|
|
146
|
+
}
|
|
147
|
+
const hasRedirect = typeof options.redirectTo === 'string' && options.redirectTo.trim().length > 0;
|
|
148
|
+
const hasDeny = Number.isInteger(options.deny);
|
|
149
|
+
if (hasRedirect === hasDeny) {
|
|
150
|
+
throw createAuthError('ctx.auth.requireSession(...) requires exactly one of redirectTo or deny');
|
|
151
|
+
}
|
|
152
|
+
if (hasRedirect) {
|
|
153
|
+
const status = options.status === undefined ? 302 : options.status;
|
|
154
|
+
if (status !== 302 && status !== 303 && status !== 307) {
|
|
155
|
+
throw createAuthError('ctx.auth.requireSession({ redirectTo, status }) only supports 302, 303, or 307');
|
|
156
|
+
}
|
|
157
|
+
return redirect(options.redirectTo, status);
|
|
158
|
+
}
|
|
159
|
+
if (options.deny !== 401 && options.deny !== 403 && options.deny !== 404) {
|
|
160
|
+
throw createAuthError('ctx.auth.requireSession({ deny, message }) only supports 401, 403, or 404');
|
|
161
|
+
}
|
|
162
|
+
if (options.message !== undefined && typeof options.message !== 'string') {
|
|
163
|
+
throw createAuthError('ctx.auth.requireSession({ deny, message }) requires message to be a string when provided');
|
|
164
|
+
}
|
|
165
|
+
return deny(options.deny, options.message);
|
|
166
|
+
}
|
|
167
|
+
export function consumeStagedSetCookies(ctx) {
|
|
168
|
+
if (!ctx || typeof ctx !== 'object' || !Array.isArray(ctx[STAGED_SET_COOKIES_KEY])) {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
return ctx[STAGED_SET_COOKIES_KEY].slice();
|
|
172
|
+
}
|
|
173
|
+
export function attachRouteAuth(ctx, options = {}) {
|
|
174
|
+
if (!ctx || typeof ctx !== 'object') {
|
|
175
|
+
throw createAuthError('attachRouteAuth(ctx) requires a route context object');
|
|
176
|
+
}
|
|
177
|
+
const requestUrl = options.requestUrl instanceof URL
|
|
178
|
+
? options.requestUrl
|
|
179
|
+
: new URL(String(options.requestUrl || ctx.url || 'http://localhost/'));
|
|
180
|
+
const guardOnly = options.guardOnly === true;
|
|
181
|
+
const redirect = options.redirect;
|
|
182
|
+
const deny = options.deny;
|
|
183
|
+
if (typeof redirect !== 'function' || typeof deny !== 'function') {
|
|
184
|
+
throw createAuthError('attachRouteAuth(ctx) requires redirect() and deny() constructors');
|
|
185
|
+
}
|
|
186
|
+
let activeSession;
|
|
187
|
+
let activeSessionInitialized = false;
|
|
188
|
+
function readActiveSession() {
|
|
189
|
+
if (activeSessionInitialized) {
|
|
190
|
+
return activeSession;
|
|
191
|
+
}
|
|
192
|
+
const secret = readSessionSecret();
|
|
193
|
+
const rawCookieValue = typeof ctx.cookies?.[SESSION_COOKIE_NAME] === 'string'
|
|
194
|
+
? ctx.cookies[SESSION_COOKIE_NAME]
|
|
195
|
+
: '';
|
|
196
|
+
activeSession = parseSessionValue(rawCookieValue, secret);
|
|
197
|
+
activeSessionInitialized = true;
|
|
198
|
+
return activeSession;
|
|
199
|
+
}
|
|
200
|
+
Object.defineProperty(ctx, STAGED_SET_COOKIES_KEY, {
|
|
201
|
+
value: [],
|
|
202
|
+
enumerable: false,
|
|
203
|
+
configurable: true
|
|
204
|
+
});
|
|
205
|
+
ctx.auth = {
|
|
206
|
+
async getSession() {
|
|
207
|
+
return readActiveSession();
|
|
208
|
+
},
|
|
209
|
+
async requireSession(policy) {
|
|
210
|
+
const session = await this.getSession();
|
|
211
|
+
if (session) {
|
|
212
|
+
return session;
|
|
213
|
+
}
|
|
214
|
+
throw toAuthControlFlow(normalizeRequirePolicy(policy, redirect, deny));
|
|
215
|
+
},
|
|
216
|
+
async signIn(sessionObject) {
|
|
217
|
+
if (guardOnly) {
|
|
218
|
+
throw createAuthError('ctx.auth.signIn(...) is unavailable during advisory route-check execution');
|
|
219
|
+
}
|
|
220
|
+
const secret = readSessionSecret();
|
|
221
|
+
const cookieValue = createSignedSessionValue(sessionObject, secret);
|
|
222
|
+
stageSetCookie(ctx, buildSessionSetCookie(cookieValue, requestUrl));
|
|
223
|
+
activeSession = sessionObject;
|
|
224
|
+
activeSessionInitialized = true;
|
|
225
|
+
},
|
|
226
|
+
async signOut() {
|
|
227
|
+
if (guardOnly) {
|
|
228
|
+
throw createAuthError('ctx.auth.signOut() is unavailable during advisory route-check execution');
|
|
229
|
+
}
|
|
230
|
+
stageSetCookie(ctx, buildSessionClearCookie(requestUrl));
|
|
231
|
+
activeSession = null;
|
|
232
|
+
activeSessionInitialized = true;
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
return ctx.auth;
|
|
236
|
+
}
|
|
@@ -42,7 +42,7 @@ export function createTimedCompilerRunner(startupProfile: ReturnType<typeof impo
|
|
|
42
42
|
* @param {object | null} [logger]
|
|
43
43
|
* @param {boolean} [showInfo]
|
|
44
44
|
* @param {string|object} [bundlerBin]
|
|
45
|
-
* @param {{ devStableAssets?: boolean, rebuildStrategy?: 'full'|'bundle-only'|'page-only', changedRoutes?: string[], fastPath?: boolean, globalGraphHash?: string, basePath?: string, routeCheck?: boolean }} [bundlerOptions]
|
|
45
|
+
* @param {{ devStableAssets?: boolean, rebuildStrategy?: 'full'|'bundle-only'|'page-only', changedRoutes?: string[], fastPath?: boolean, globalGraphHash?: string, basePath?: string, routeCheck?: boolean, imageRuntimePayload?: object }} [bundlerOptions]
|
|
46
46
|
* @returns {Promise<void>}
|
|
47
47
|
*/
|
|
48
48
|
/**
|
|
@@ -171,7 +171,7 @@ export function createTimedCompilerRunner(startupProfile, compilerTotals) {
|
|
|
171
171
|
* @param {object | null} [logger]
|
|
172
172
|
* @param {boolean} [showInfo]
|
|
173
173
|
* @param {string|object} [bundlerBin]
|
|
174
|
-
* @param {{ devStableAssets?: boolean, rebuildStrategy?: 'full'|'bundle-only'|'page-only', changedRoutes?: string[], fastPath?: boolean, globalGraphHash?: string, basePath?: string, routeCheck?: boolean }} [bundlerOptions]
|
|
174
|
+
* @param {{ devStableAssets?: boolean, rebuildStrategy?: 'full'|'bundle-only'|'page-only', changedRoutes?: string[], fastPath?: boolean, globalGraphHash?: string, basePath?: string, routeCheck?: boolean, imageRuntimePayload?: object }} [bundlerOptions]
|
|
175
175
|
* @returns {Promise<void>}
|
|
176
176
|
*/
|
|
177
177
|
/**
|
|
@@ -284,7 +284,13 @@ export function runBundler(envelope, outDir, projectRoot, logger = null, showInf
|
|
|
284
284
|
}
|
|
285
285
|
rejectPromise(new Error(`Bundler failed with exit code ${code}`));
|
|
286
286
|
});
|
|
287
|
-
|
|
287
|
+
const bundlerPayload = bundlerOptions.imageRuntimePayload
|
|
288
|
+
? {
|
|
289
|
+
inputs: Array.isArray(envelope) ? envelope : [envelope],
|
|
290
|
+
image_runtime_payload: bundlerOptions.imageRuntimePayload
|
|
291
|
+
}
|
|
292
|
+
: envelope;
|
|
293
|
+
child.stdin.write(JSON.stringify(bundlerPayload));
|
|
288
294
|
child.stdin.end();
|
|
289
295
|
});
|
|
290
296
|
}
|
|
@@ -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
|
}
|
|
@@ -48,7 +48,7 @@ export function preparePageIrForMerge(pageIr) {
|
|
|
48
48
|
}
|
|
49
49
|
export function applyServerEnvelopeToPageIr({ pageIr, composedServer, hasGuard, hasLoad, hasAction, entry, srcDir, sourceFile }) {
|
|
50
50
|
if (composedServer.serverScript) {
|
|
51
|
-
const { has_action: _unusedHasAction, ...serverScript } = composedServer.serverScript;
|
|
51
|
+
const { has_action: _unusedHasAction, export_paths: _unusedExportPaths, ...serverScript } = composedServer.serverScript;
|
|
52
52
|
pageIr.server_script = serverScript;
|
|
53
53
|
pageIr.prerender = composedServer.serverScript.prerender === true;
|
|
54
54
|
if (pageIr.ssr_data === undefined) {
|
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
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @param {string} source
|
|
3
3
|
* @param {string} sourceFile
|
|
4
4
|
* @param {object} [compilerOpts]
|
|
5
|
-
* @returns {{ source: string, serverScript: { source: string, prerender: boolean, has_guard: boolean, has_load: boolean, has_action: boolean, source_path: string } | null }}
|
|
5
|
+
* @returns {{ source: string, serverScript: { source: string, prerender: boolean, has_guard: boolean, has_load: boolean, has_action: boolean, source_path: string, export_paths?: string[] } | null }}
|
|
6
6
|
*/
|
|
7
7
|
export function extractServerScript(source: string, sourceFile: string, compilerOpts?: object): {
|
|
8
8
|
source: string;
|
|
@@ -13,6 +13,7 @@ export function extractServerScript(source: string, sourceFile: string, compiler
|
|
|
13
13
|
has_load: boolean;
|
|
14
14
|
has_action: boolean;
|
|
15
15
|
source_path: string;
|
|
16
|
+
export_paths?: string[];
|
|
16
17
|
} | null;
|
|
17
18
|
};
|
|
18
19
|
/**
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { findNextKnownComponentTag } from '../component-tag-parser.js';
|
|
3
|
+
import { extractStaticExportPaths } from '../static-export-paths.js';
|
|
3
4
|
/**
|
|
4
5
|
* @param {string} source
|
|
5
6
|
* @param {string} sourceFile
|
|
6
7
|
* @param {object} [compilerOpts]
|
|
7
|
-
* @returns {{ source: string, serverScript: { source: string, prerender: boolean, has_guard: boolean, has_load: boolean, has_action: boolean, source_path: string } | null }}
|
|
8
|
+
* @returns {{ source: string, serverScript: { source: string, prerender: boolean, has_guard: boolean, has_load: boolean, has_action: boolean, source_path: string, export_paths?: string[] } | null }}
|
|
8
9
|
*/
|
|
9
10
|
export function extractServerScript(source, sourceFile, compilerOpts = {}) {
|
|
10
11
|
const scriptRe = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
|
|
@@ -155,6 +156,7 @@ export function extractServerScript(source, sourceFile, compilerOpts = {}) {
|
|
|
155
156
|
}
|
|
156
157
|
prerender = rawValue.startsWith('true');
|
|
157
158
|
}
|
|
159
|
+
const exportPaths = extractStaticExportPaths(serverSource, sourceFile) || [];
|
|
158
160
|
const start = match.index ?? -1;
|
|
159
161
|
if (start < 0) {
|
|
160
162
|
return {
|
|
@@ -165,7 +167,8 @@ export function extractServerScript(source, sourceFile, compilerOpts = {}) {
|
|
|
165
167
|
has_guard: hasGuard,
|
|
166
168
|
has_load: hasLoad,
|
|
167
169
|
has_action: hasAction,
|
|
168
|
-
source_path: sourceFile
|
|
170
|
+
source_path: sourceFile,
|
|
171
|
+
export_paths: exportPaths
|
|
169
172
|
}
|
|
170
173
|
};
|
|
171
174
|
}
|
|
@@ -179,7 +182,8 @@ export function extractServerScript(source, sourceFile, compilerOpts = {}) {
|
|
|
179
182
|
has_guard: hasGuard,
|
|
180
183
|
has_load: hasLoad,
|
|
181
184
|
has_action: hasAction,
|
|
182
|
-
source_path: sourceFile
|
|
185
|
+
source_path: sourceFile,
|
|
186
|
+
export_paths: exportPaths
|
|
183
187
|
}
|
|
184
188
|
};
|
|
185
189
|
}
|
|
@@ -11,14 +11,15 @@ export function writeBuildOutputManifest({ coreOutputDir, staticDir, target, rou
|
|
|
11
11
|
base_path: string;
|
|
12
12
|
content_hash: any;
|
|
13
13
|
routes: {
|
|
14
|
+
html: any;
|
|
15
|
+
assets: any[];
|
|
16
|
+
export_paths?: any[] | undefined;
|
|
14
17
|
path: any;
|
|
15
18
|
file: any;
|
|
16
19
|
path_kind: any;
|
|
17
20
|
render_mode: any;
|
|
18
21
|
requires_hydration: boolean;
|
|
19
22
|
params: any[];
|
|
20
|
-
html: any;
|
|
21
|
-
assets: any[];
|
|
22
23
|
}[];
|
|
23
24
|
assets: {
|
|
24
25
|
js: any[];
|
|
@@ -67,6 +67,9 @@ export async function writeBuildOutputManifest({ coreOutputDir, staticDir, targe
|
|
|
67
67
|
render_mode: entry.render_mode,
|
|
68
68
|
requires_hydration: /<script\b[^>]*type="module"/i.test(html),
|
|
69
69
|
params: [...entry.params],
|
|
70
|
+
...(Array.isArray(entry.export_paths) && entry.export_paths.length > 0
|
|
71
|
+
? { export_paths: [...entry.export_paths] }
|
|
72
|
+
: {}),
|
|
70
73
|
html: htmlPath,
|
|
71
74
|
assets
|
|
72
75
|
});
|
package/dist/build.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { mkdir, rm } from 'node:fs/promises';
|
|
1
|
+
import { cp, mkdir, rm } from 'node:fs/promises';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { resolveBuildAdapter } from './adapters/resolve-adapter.js';
|
|
4
4
|
import { normalizeBasePath } from './base-path.js';
|
|
5
5
|
import { rewriteSoftNavigationHrefBasePathInHtmlFiles } from './base-path-html.js';
|
|
6
6
|
import { writeBuildOutputManifest } from './build-output-manifest.js';
|
|
7
7
|
import { generateManifest } from './manifest.js';
|
|
8
|
+
import { writeResourceRouteManifest } from './resource-manifest.js';
|
|
8
9
|
import { buildComponentRegistry } from './resolve-components.js';
|
|
9
10
|
import { collectAssets, createCompilerWarningEmitter, runBundler } from './build/compiler-runtime.js';
|
|
10
11
|
import { buildPageEnvelopes } from './build/page-loop.js';
|
|
11
12
|
import { deriveProjectRootFromPagesDir, ensureZenithTypeDeclarations } from './build/type-declarations.js';
|
|
12
|
-
import { materializeImageMarkupInHtmlFiles } from './images/materialize.js';
|
|
13
13
|
import { buildImageArtifacts } from './images/service.js';
|
|
14
14
|
import { injectImageMaterializationIntoRouterManifest } from './images/router-manifest.js';
|
|
15
15
|
import { createImageRuntimePayload, injectImageRuntimePayloadIntoHtmlFiles } from './images/payload.js';
|
|
@@ -50,6 +50,7 @@ export async function build(options) {
|
|
|
50
50
|
const projectRoot = deriveProjectRootFromPagesDir(pagesDir);
|
|
51
51
|
const coreOutputDir = join(projectRoot, '.zenith-output');
|
|
52
52
|
const staticOutputDir = join(coreOutputDir, 'static');
|
|
53
|
+
const imageStageDir = join(coreOutputDir, 'image-materialization-stage');
|
|
53
54
|
const srcDir = resolve(pagesDir, '..');
|
|
54
55
|
const compilerBin = createCompilerToolchain({ projectRoot, logger });
|
|
55
56
|
const bundlerBin = createBundlerToolchain({ projectRoot, logger });
|
|
@@ -75,11 +76,12 @@ export async function build(options) {
|
|
|
75
76
|
}
|
|
76
77
|
const registry = startupProfile.measureSync('build_component_registry', () => buildComponentRegistry(srcDir));
|
|
77
78
|
const manifest = await startupProfile.measureAsync('generate_manifest', () => generateManifest(pagesDir, '.zen', { compilerOpts }));
|
|
79
|
+
const pageManifest = manifest.filter((entry) => entry?.route_kind !== 'resource');
|
|
78
80
|
if (mode !== 'legacy') {
|
|
79
81
|
adapter.validateRoutes(manifest);
|
|
80
82
|
}
|
|
81
83
|
await startupProfile.measureAsync('ensure_zenith_type_declarations', () => ensureZenithTypeDeclarations({
|
|
82
|
-
manifest,
|
|
84
|
+
manifest: pageManifest,
|
|
83
85
|
pagesDir
|
|
84
86
|
}));
|
|
85
87
|
await startupProfile.measureAsync('reset_core_output', () => rm(coreOutputDir, { recursive: true, force: true }));
|
|
@@ -92,7 +94,7 @@ export async function build(options) {
|
|
|
92
94
|
console.warn(line);
|
|
93
95
|
});
|
|
94
96
|
const { envelopes, expressionRewriteMetrics } = await buildPageEnvelopes({
|
|
95
|
-
manifest,
|
|
97
|
+
manifest: pageManifest,
|
|
96
98
|
pagesDir,
|
|
97
99
|
srcDir,
|
|
98
100
|
registry,
|
|
@@ -103,27 +105,37 @@ export async function build(options) {
|
|
|
103
105
|
compilerTotals,
|
|
104
106
|
emitCompilerWarning
|
|
105
107
|
});
|
|
106
|
-
if (envelopes.length > 0) {
|
|
107
|
-
await startupProfile.measureAsync('run_bundler', () => runBundler(envelopes, staticOutputDir, projectRoot, logger, showBundlerInfo, bundlerBin, { basePath, routeCheck: routeCheckEnabled }), { envelopes: envelopes.length });
|
|
108
|
-
await startupProfile.measureAsync('inject_image_materialization_manifest', () => injectImageMaterializationIntoRouterManifest(staticOutputDir, envelopes), { envelopes: envelopes.length });
|
|
109
|
-
}
|
|
110
|
-
await startupProfile.measureAsync('rewrite_soft_navigation_base_path', () => rewriteSoftNavigationHrefBasePathInHtmlFiles(staticOutputDir, basePath));
|
|
111
108
|
const { manifest: imageManifest } = await startupProfile.measureAsync('build_image_artifacts', () => buildImageArtifacts({
|
|
112
109
|
projectRoot,
|
|
113
|
-
outDir:
|
|
110
|
+
outDir: imageStageDir,
|
|
114
111
|
config: config.images
|
|
115
112
|
}));
|
|
116
113
|
const imageRuntimePayload = createImageRuntimePayload(config.images, imageManifest, 'passthrough', basePath);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
if (envelopes.length > 0) {
|
|
115
|
+
await startupProfile.measureAsync('run_bundler', () => runBundler(envelopes, staticOutputDir, projectRoot, logger, showBundlerInfo, bundlerBin, {
|
|
116
|
+
basePath,
|
|
117
|
+
routeCheck: routeCheckEnabled,
|
|
118
|
+
imageRuntimePayload
|
|
119
|
+
}), { envelopes: envelopes.length });
|
|
120
|
+
await startupProfile.measureAsync('inject_image_materialization_manifest', () => injectImageMaterializationIntoRouterManifest(staticOutputDir, envelopes), { envelopes: envelopes.length });
|
|
121
|
+
}
|
|
122
|
+
await startupProfile.measureAsync('write_resource_manifest', () => writeResourceRouteManifest(staticOutputDir, manifest, basePath));
|
|
123
|
+
await startupProfile.measureAsync('rewrite_soft_navigation_base_path', () => rewriteSoftNavigationHrefBasePathInHtmlFiles(staticOutputDir, basePath));
|
|
124
|
+
await startupProfile.measureAsync('stage_image_artifacts_into_static_output', async () => {
|
|
125
|
+
if (Object.keys(imageManifest).length === 0) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
await cp(join(imageStageDir, '_zenith'), join(staticOutputDir, '_zenith'), {
|
|
129
|
+
recursive: true,
|
|
130
|
+
force: true
|
|
131
|
+
});
|
|
132
|
+
});
|
|
121
133
|
await startupProfile.measureAsync('inject_image_runtime_payload', () => injectImageRuntimePayloadIntoHtmlFiles(staticOutputDir, imageRuntimePayload));
|
|
122
134
|
const buildManifest = await startupProfile.measureAsync('write_core_manifest', () => writeBuildOutputManifest({
|
|
123
135
|
coreOutputDir,
|
|
124
136
|
staticDir: staticOutputDir,
|
|
125
137
|
target,
|
|
126
|
-
routeManifest:
|
|
138
|
+
routeManifest: pageManifest,
|
|
127
139
|
basePath
|
|
128
140
|
}));
|
|
129
141
|
await startupProfile.measureAsync('write_server_output', () => writeServerOutput({
|
|
@@ -136,11 +148,11 @@ export async function build(options) {
|
|
|
136
148
|
await startupProfile.measureAsync('adapt_output', () => adapter.adapt({ coreOutput: coreOutputDir, outDir, manifest: buildManifest, config }));
|
|
137
149
|
const assets = await startupProfile.measureAsync('collect_assets', () => collectAssets(outDir));
|
|
138
150
|
startupProfile.emit('build_complete', {
|
|
139
|
-
pages:
|
|
151
|
+
pages: pageManifest.length,
|
|
140
152
|
assets: assets.length,
|
|
141
153
|
target,
|
|
142
154
|
compilerTotals,
|
|
143
155
|
expressionRewriteMetrics
|
|
144
156
|
});
|
|
145
|
-
return { pages:
|
|
157
|
+
return { pages: pageManifest.length, assets };
|
|
146
158
|
}
|