@zenithbuild/cli 0.7.11 → 0.7.12
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 +10 -1
- package/dist/adapters/adapter-netlify-static.d.ts +2 -5
- package/dist/adapters/adapter-netlify.d.ts +2 -5
- package/dist/adapters/adapter-netlify.js +22 -5
- package/dist/adapters/adapter-types.d.ts +32 -13
- package/dist/adapters/adapter-types.js +0 -59
- package/dist/adapters/adapter-vercel-static.d.ts +2 -5
- package/dist/adapters/adapter-vercel.d.ts +2 -5
- package/dist/adapters/adapter-vercel.js +21 -6
- package/dist/adapters/copy-hosted-page-runtime.d.ts +2 -1
- package/dist/adapters/copy-hosted-page-runtime.js +68 -3
- package/dist/adapters/resolve-adapter.d.ts +6 -4
- package/dist/build/expression-rewrites.d.ts +3 -1
- package/dist/build/expression-rewrites.js +14 -2
- package/dist/build/page-component-loop.d.ts +1 -0
- package/dist/build/page-component-loop.js +66 -6
- package/dist/build/page-ir-normalization.js +7 -0
- package/dist/build/page-loop-state.d.ts +2 -1
- package/dist/build/page-loop-state.js +9 -2
- package/dist/build/page-loop.js +10 -1
- package/dist/build/scoped-expression-context.d.ts +5 -0
- package/dist/build/scoped-expression-context.js +133 -0
- package/dist/build/type-declarations.d.ts +2 -1
- package/dist/build/type-declarations.js +31 -1
- package/dist/build-output-manifest.d.ts +10 -6
- package/dist/build-output-manifest.js +4 -1
- package/dist/build.js +11 -2
- package/dist/component-instance-ir.js +1 -0
- package/dist/component-occurrences.d.ts +9 -0
- package/dist/component-occurrences.js +18 -0
- package/dist/config-plugins.d.ts +12 -0
- package/dist/config-plugins.js +100 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +56 -5
- package/dist/dev-server/request-handler.js +46 -4
- package/dist/dev-server.js +92 -4
- package/dist/global-middleware-runtime-source.d.ts +15 -0
- package/dist/global-middleware-runtime-source.js +62 -0
- package/dist/global-middleware.d.ts +13 -0
- package/dist/global-middleware.js +252 -0
- package/dist/manifest.d.ts +9 -1
- package/dist/manifest.js +66 -26
- package/dist/preview/request-handler.js +78 -5
- package/dist/preview/server-runner.d.ts +7 -2
- package/dist/preview/server-runner.js +19 -6
- package/dist/preview/server-script-runner-template.js +97 -29
- package/dist/route-classification.d.ts +2 -1
- package/dist/route-classification.js +6 -2
- package/dist/scoped-server-data/analyze-owner-file.d.ts +3 -0
- package/dist/scoped-server-data/analyze-owner-file.js +149 -0
- package/dist/scoped-server-data/diagnostics.d.ts +18 -0
- package/dist/scoped-server-data/diagnostics.js +32 -0
- package/dist/scoped-server-data/lowering.d.ts +27 -0
- package/dist/scoped-server-data/lowering.js +242 -0
- package/dist/scoped-server-data/manifest-integration.d.ts +4 -0
- package/dist/scoped-server-data/manifest-integration.js +125 -0
- package/dist/scoped-server-data/owner-scanner.d.ts +6 -0
- package/dist/scoped-server-data/owner-scanner.js +55 -0
- package/dist/scoped-server-data/parse-owner-server-block.d.ts +12 -0
- package/dist/scoped-server-data/parse-owner-server-block.js +35 -0
- package/dist/scoped-server-data/runtime.d.ts +24 -0
- package/dist/scoped-server-data/runtime.js +121 -0
- package/dist/scoped-server-data/serialization-set.d.ts +2 -0
- package/dist/scoped-server-data/serialization-set.js +52 -0
- package/dist/scoped-server-data/static-props.d.ts +12 -0
- package/dist/scoped-server-data/static-props.js +307 -0
- package/dist/scoped-server-data/type-declarations.d.ts +10 -0
- package/dist/scoped-server-data/type-declarations.js +368 -0
- package/dist/scoped-server-data/types.d.ts +74 -0
- package/dist/scoped-server-data/types.js +1 -0
- package/dist/server-contract/auth-control-flow.d.ts +1 -0
- package/dist/server-contract/auth-control-flow.js +10 -0
- package/dist/server-contract/resolve.d.ts +19 -0
- package/dist/server-contract/resolve.js +85 -13
- package/dist/server-contract/resolved-envelope.d.ts +9 -0
- package/dist/server-contract/resolved-envelope.js +14 -0
- package/dist/server-contract/stage.js +1 -10
- package/dist/server-module-output.d.ts +9 -0
- package/dist/server-module-output.js +250 -0
- package/dist/server-output.d.ts +7 -1
- package/dist/server-output.js +138 -179
- package/dist/server-runtime/matched-route-pipeline.d.ts +1 -0
- package/dist/server-runtime/matched-route-pipeline.js +1 -0
- package/dist/server-runtime/node-server.js +21 -1
- package/dist/server-runtime/route-render.d.ts +12 -3
- package/dist/server-runtime/route-render.js +67 -13
- package/package.json +3 -3
package/dist/server-output.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
|
-
import {
|
|
4
|
-
import { basename, dirname, extname, join, relative, resolve } from 'node:path';
|
|
3
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
5
4
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
5
|
import { loadResourceRouteManifest } from './resource-manifest.js';
|
|
7
6
|
import { assignServerRouteNames } from './server-route-names.js';
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import { normalizeGlobalMiddlewareMetadata } from './global-middleware.js';
|
|
8
|
+
import { writeServerModulePackage } from './server-module-output.js';
|
|
9
|
+
const GLOBAL_MIDDLEWARE_MODULE = 'global-middleware/entry.js';
|
|
10
|
+
const SCOPED_SERVER_DATA_LOWERING_HELPER_UNAVAILABLE = '[Zenith:ScopedServerData] Server-output lowering helper is unavailable. Run the CLI build step before packaging scoped server data modules.';
|
|
10
11
|
const SERVER_RUNTIME_FILES = [
|
|
11
12
|
{
|
|
12
13
|
from: new URL('./server-runtime/route-render.js', import.meta.url),
|
|
@@ -21,6 +22,10 @@ const SERVER_RUNTIME_FILES = [
|
|
|
21
22
|
to: 'server-contract',
|
|
22
23
|
recursive: true
|
|
23
24
|
},
|
|
25
|
+
{
|
|
26
|
+
from: new URL('./scoped-server-data/runtime.js', import.meta.url),
|
|
27
|
+
to: 'scoped-server-data/runtime.js'
|
|
28
|
+
},
|
|
24
29
|
{
|
|
25
30
|
from: new URL('./server-middleware.js', import.meta.url),
|
|
26
31
|
to: 'server-middleware.js'
|
|
@@ -70,183 +75,33 @@ const SERVER_RUNTIME_FILES = [
|
|
|
70
75
|
to: 'download-result.js'
|
|
71
76
|
}
|
|
72
77
|
];
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
]);
|
|
77
|
-
function resolveTypeScriptApi(projectRoot) {
|
|
78
|
-
try {
|
|
79
|
-
const projectRequire = createRequire(join(projectRoot, '__zenith_server_output_loader__.js'));
|
|
80
|
-
return projectRequire('typescript');
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
try {
|
|
84
|
-
return PACKAGE_REQUIRE('typescript');
|
|
85
|
-
}
|
|
86
|
-
catch {
|
|
87
|
-
throw new Error('[Zenith:Build] Server-capable targets require the `typescript` package to transpile route modules.');
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
function withJsExtension(specifier) {
|
|
92
|
-
if (specifier.endsWith('.json')) {
|
|
93
|
-
return specifier;
|
|
94
|
-
}
|
|
95
|
-
return specifier.replace(/\.(tsx|ts|mts|cts|jsx|js|mjs|cjs)$/i, '.js');
|
|
96
|
-
}
|
|
97
|
-
function replaceSpecifier(source, original, nextValue) {
|
|
98
|
-
return source.replace(new RegExp(`(['"])${escapeRegex(original)}\\1`, 'g'), (_, quote) => `${quote}${nextValue}${quote}`);
|
|
99
|
-
}
|
|
100
|
-
function escapeRegex(value) {
|
|
101
|
-
return String(value).replace(/[|\\{}()[\]^$+*?.-]/g, '\\$&');
|
|
102
|
-
}
|
|
103
|
-
function isRelativeSpecifier(specifier) {
|
|
104
|
-
return (specifier.startsWith('./') ||
|
|
105
|
-
specifier.startsWith('../') ||
|
|
106
|
-
specifier.startsWith('/') ||
|
|
107
|
-
specifier.startsWith('file:'));
|
|
108
|
-
}
|
|
109
|
-
function resolveModuleCandidates(basePath) {
|
|
110
|
-
if (extname(basePath)) {
|
|
111
|
-
return [basePath];
|
|
112
|
-
}
|
|
78
|
+
let scopedServerDataLoweringPromise = null;
|
|
79
|
+
function resolveScopedServerDataLoweringPath() {
|
|
80
|
+
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
113
81
|
return [
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
`${basePath}.mts`,
|
|
118
|
-
`${basePath}.cts`,
|
|
119
|
-
`${basePath}.js`,
|
|
120
|
-
`${basePath}.mjs`,
|
|
121
|
-
`${basePath}.cjs`,
|
|
122
|
-
`${basePath}.json`,
|
|
123
|
-
join(basePath, 'index.ts'),
|
|
124
|
-
join(basePath, 'index.tsx'),
|
|
125
|
-
join(basePath, 'index.mts'),
|
|
126
|
-
join(basePath, 'index.cts'),
|
|
127
|
-
join(basePath, 'index.js'),
|
|
128
|
-
join(basePath, 'index.mjs'),
|
|
129
|
-
join(basePath, 'index.cjs'),
|
|
130
|
-
join(basePath, 'index.json')
|
|
131
|
-
];
|
|
132
|
-
}
|
|
133
|
-
function resolveImportedModule(specifier, sourceFile) {
|
|
134
|
-
if (!isRelativeSpecifier(specifier)) {
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
const baseDir = dirname(sourceFile);
|
|
138
|
-
const basePath = specifier.startsWith('file:')
|
|
139
|
-
? new URL(specifier)
|
|
140
|
-
: resolve(baseDir, specifier);
|
|
141
|
-
const filePath = basePath instanceof URL ? fileURLToPath(basePath) : basePath;
|
|
142
|
-
const candidates = resolveModuleCandidates(filePath);
|
|
143
|
-
const found = candidates.find((candidate) => existsSync(candidate));
|
|
144
|
-
if (!found) {
|
|
145
|
-
throw new Error(`[Zenith:Build] Cannot resolve server import "${specifier}" from "${sourceFile}"`);
|
|
146
|
-
}
|
|
147
|
-
return found;
|
|
148
|
-
}
|
|
149
|
-
function gatherSpecifiers(source) {
|
|
150
|
-
const results = [];
|
|
151
|
-
for (const match of source.matchAll(RELATIVE_SPECIFIER_RE)) {
|
|
152
|
-
const specifier = String(match[3] || '');
|
|
153
|
-
results.push(specifier);
|
|
154
|
-
}
|
|
155
|
-
return results;
|
|
156
|
-
}
|
|
157
|
-
function transpileSource(ts, source, filePath) {
|
|
158
|
-
return ts.transpileModule(source, {
|
|
159
|
-
compilerOptions: {
|
|
160
|
-
module: ts.ModuleKind.ESNext,
|
|
161
|
-
target: ts.ScriptTarget.ES2022,
|
|
162
|
-
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
163
|
-
esModuleInterop: true,
|
|
164
|
-
allowSyntheticDefaultImports: true
|
|
165
|
-
},
|
|
166
|
-
fileName: filePath
|
|
167
|
-
}).outputText;
|
|
168
|
-
}
|
|
169
|
-
function outputPathForSource(projectRoot, modulesRoot, sourcePath) {
|
|
170
|
-
const relativePath = relative(projectRoot, sourcePath).replaceAll('\\', '/');
|
|
171
|
-
const nextRelative = extname(relativePath) === '.json'
|
|
172
|
-
? relativePath
|
|
173
|
-
: relativePath.replace(/\.(tsx|ts|mts|cts|jsx|js|mjs|cjs)$/i, '.js');
|
|
174
|
-
return join(modulesRoot, nextRelative);
|
|
82
|
+
join(moduleDir, 'scoped-server-data', 'lowering.js'),
|
|
83
|
+
join(moduleDir, '..', 'dist', 'scoped-server-data', 'lowering.js')
|
|
84
|
+
].find((candidate) => existsSync(candidate)) || null;
|
|
175
85
|
}
|
|
176
|
-
async function
|
|
177
|
-
|
|
178
|
-
|
|
86
|
+
async function getScopedServerDataLowering() {
|
|
87
|
+
const helperPath = resolveScopedServerDataLoweringPath();
|
|
88
|
+
if (!helperPath) {
|
|
89
|
+
throw new Error(SCOPED_SERVER_DATA_LOWERING_HELPER_UNAVAILABLE);
|
|
179
90
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
await mkdir(dirname(outPath), { recursive: true });
|
|
183
|
-
if (extname(sourcePath) === '.json') {
|
|
184
|
-
await cp(sourcePath, outPath, { force: true });
|
|
185
|
-
return outPath;
|
|
186
|
-
}
|
|
187
|
-
const source = await readFile(sourcePath, 'utf8');
|
|
188
|
-
let output = transpileSource(ts, source, sourcePath);
|
|
189
|
-
for (const specifier of gatherSpecifiers(output)) {
|
|
190
|
-
const specialSpecifierPath = SPECIAL_SERVER_SPECIFIERS.get(specifier);
|
|
191
|
-
if (specialSpecifierPath) {
|
|
192
|
-
const nextSpecifier = relative(dirname(outPath), join(serverDir, specialSpecifierPath)).replaceAll('\\', '/');
|
|
193
|
-
output = replaceSpecifier(output, specifier, nextSpecifier.startsWith('.') ? nextSpecifier : `./${nextSpecifier}`);
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
if (!isRelativeSpecifier(specifier)) {
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
const resolvedPath = resolveImportedModule(specifier, sourcePath);
|
|
200
|
-
if (!resolvedPath) {
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
const compiledDependencyPath = await compileImportedModule({
|
|
204
|
-
projectRoot,
|
|
205
|
-
modulesRoot,
|
|
206
|
-
serverDir,
|
|
207
|
-
sourcePath: resolvedPath,
|
|
208
|
-
ts,
|
|
209
|
-
seen
|
|
210
|
-
});
|
|
211
|
-
const nextSpecifier = relative(dirname(outPath), compiledDependencyPath).replaceAll('\\', '/');
|
|
212
|
-
output = replaceSpecifier(output, specifier, nextSpecifier.startsWith('.') ? nextSpecifier : `./${nextSpecifier}`);
|
|
91
|
+
if (!scopedServerDataLoweringPromise) {
|
|
92
|
+
scopedServerDataLoweringPromise = import(pathToFileURL(helperPath).href);
|
|
213
93
|
}
|
|
214
|
-
|
|
215
|
-
return outPath;
|
|
94
|
+
return scopedServerDataLoweringPromise;
|
|
216
95
|
}
|
|
217
96
|
async function writeRouteModulePackage({ projectRoot, serverDir, routeDir, route }) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
entryOutput = replaceSpecifier(entryOutput, specifier, nextSpecifier.startsWith('.') ? nextSpecifier : `./${nextSpecifier}`);
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
if (!isRelativeSpecifier(specifier)) {
|
|
230
|
-
continue;
|
|
231
|
-
}
|
|
232
|
-
const resolvedPath = resolveImportedModule(specifier, route.server_script_path || projectRoot);
|
|
233
|
-
if (!resolvedPath) {
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
const compiledDependencyPath = await compileImportedModule({
|
|
237
|
-
projectRoot,
|
|
238
|
-
modulesRoot,
|
|
239
|
-
serverDir,
|
|
240
|
-
sourcePath: resolvedPath,
|
|
241
|
-
ts,
|
|
242
|
-
seen
|
|
243
|
-
});
|
|
244
|
-
const nextSpecifier = relative(join(routeDir, 'route'), compiledDependencyPath).replaceAll('\\', '/');
|
|
245
|
-
entryOutput = replaceSpecifier(entryOutput, specifier, nextSpecifier.startsWith('.') ? nextSpecifier : `./${nextSpecifier}`);
|
|
246
|
-
}
|
|
247
|
-
const routeModulePath = join(routeDir, 'route', 'entry.js');
|
|
248
|
-
await mkdir(dirname(routeModulePath), { recursive: true });
|
|
249
|
-
await writeFile(routeModulePath, entryOutput, 'utf8');
|
|
97
|
+
await writeServerModulePackage({
|
|
98
|
+
projectRoot,
|
|
99
|
+
serverDir,
|
|
100
|
+
entrySource: route.server_script || '',
|
|
101
|
+
entrySourcePath: route.server_script_path || 'route-entry.ts',
|
|
102
|
+
entryOutputPath: join(routeDir, 'route', 'entry.js'),
|
|
103
|
+
modulesRoot: join(routeDir, 'modules')
|
|
104
|
+
});
|
|
250
105
|
}
|
|
251
106
|
async function copyRuntimeFiles(serverDir) {
|
|
252
107
|
for (const file of SERVER_RUNTIME_FILES) {
|
|
@@ -266,7 +121,81 @@ async function copyOptionalFile(sourcePath, targetPath) {
|
|
|
266
121
|
await cp(sourcePath, targetPath, { force: true });
|
|
267
122
|
return true;
|
|
268
123
|
}
|
|
269
|
-
|
|
124
|
+
async function writeGlobalMiddlewarePackage({ projectRoot, serverDir, globalMiddleware }) {
|
|
125
|
+
const metadata = normalizeGlobalMiddlewareMetadata(globalMiddleware);
|
|
126
|
+
if (!metadata) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const sourcePath = resolve(projectRoot, metadata.source_file);
|
|
130
|
+
if (!existsSync(sourcePath)) {
|
|
131
|
+
throw new Error(`[Zenith:Middleware] Cannot emit global middleware because source file "${metadata.source_file}" was not found.`);
|
|
132
|
+
}
|
|
133
|
+
await writeServerModulePackage({
|
|
134
|
+
projectRoot,
|
|
135
|
+
serverDir,
|
|
136
|
+
entrySource: await readFile(sourcePath, 'utf8'),
|
|
137
|
+
entrySourcePath: sourcePath,
|
|
138
|
+
entryOutputPath: join(serverDir, GLOBAL_MIDDLEWARE_MODULE),
|
|
139
|
+
modulesRoot: join(serverDir, 'global-middleware', 'modules'),
|
|
140
|
+
validateMiddlewareImports: true
|
|
141
|
+
});
|
|
142
|
+
return {
|
|
143
|
+
...metadata,
|
|
144
|
+
module: GLOBAL_MIDDLEWARE_MODULE
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function mergePageRouteMetadata(route, manifestEntry) {
|
|
148
|
+
if (!manifestEntry) {
|
|
149
|
+
return route;
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
...route,
|
|
153
|
+
file: manifestEntry.file || route.file || null,
|
|
154
|
+
has_scoped_server_data: manifestEntry.has_scoped_server_data === true || route.has_scoped_server_data === true,
|
|
155
|
+
scoped_server_data: Array.isArray(manifestEntry.scoped_server_data)
|
|
156
|
+
? manifestEntry.scoped_server_data
|
|
157
|
+
: (Array.isArray(route.scoped_server_data) ? route.scoped_server_data : [])
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
async function writeScopedServerDataModules({ projectRoot, serverDir, route, pagesDir, srcDir, registry, compilerOpts }) {
|
|
161
|
+
if (route.route_kind === 'resource' || route.has_scoped_server_data !== true) {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
if (!pagesDir || !srcDir || !registry) {
|
|
165
|
+
throw new Error(`[Zenith:ScopedServerData] Cannot package scoped server data for route "${route.path}" without build context.`);
|
|
166
|
+
}
|
|
167
|
+
if (typeof route.file !== 'string' || route.file.length === 0) {
|
|
168
|
+
throw new Error(`[Zenith:ScopedServerData] Cannot package scoped server data for route "${route.path}" without a source file.`);
|
|
169
|
+
}
|
|
170
|
+
const pageFile = resolve(pagesDir, route.file);
|
|
171
|
+
const pageSource = await readFile(pageFile, 'utf8');
|
|
172
|
+
const lowering = await getScopedServerDataLowering();
|
|
173
|
+
const lowered = lowering.lowerRouteScopedServerData({
|
|
174
|
+
pageSource,
|
|
175
|
+
pageFile,
|
|
176
|
+
registry,
|
|
177
|
+
srcDir,
|
|
178
|
+
projectRoot,
|
|
179
|
+
compilerOpts,
|
|
180
|
+
scopedServerData: Array.isArray(route.scoped_server_data) ? route.scoped_server_data : []
|
|
181
|
+
});
|
|
182
|
+
if (!Array.isArray(lowered.scopedServerData) || lowered.scopedServerData.length === 0) {
|
|
183
|
+
throw new Error(`[Zenith:ScopedServerData] Cannot package scoped server data for route "${route.path}" because no owners were lowered.`);
|
|
184
|
+
}
|
|
185
|
+
for (const module of lowered.modules) {
|
|
186
|
+
const entryOutputPath = lowering.resolveScopedServerModuleOutputPath(serverDir, module.module);
|
|
187
|
+
await writeServerModulePackage({
|
|
188
|
+
projectRoot,
|
|
189
|
+
serverDir,
|
|
190
|
+
entrySource: module.source,
|
|
191
|
+
entrySourcePath: module.sourcePath,
|
|
192
|
+
entryOutputPath,
|
|
193
|
+
modulesRoot: join(serverDir, 'scoped', '_modules')
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return lowered.scopedServerData;
|
|
197
|
+
}
|
|
198
|
+
export async function writeServerOutput({ coreOutputDir, staticDir, projectRoot, config, basePath = '/', globalMiddleware = null, pageManifest = [], pagesDir = null, srcDir = null, registry = null, compilerOpts = {} }) {
|
|
270
199
|
const serverDir = join(coreOutputDir, 'server');
|
|
271
200
|
await rm(serverDir, { recursive: true, force: true });
|
|
272
201
|
let routerManifest = { routes: [] };
|
|
@@ -277,16 +206,28 @@ export async function writeServerOutput({ coreOutputDir, staticDir, projectRoot,
|
|
|
277
206
|
routerManifest = { routes: [] };
|
|
278
207
|
}
|
|
279
208
|
const resourceManifest = await loadResourceRouteManifest(staticDir, basePath);
|
|
280
|
-
const
|
|
209
|
+
const pageManifestByPath = new Map((Array.isArray(pageManifest) ? pageManifest : [])
|
|
210
|
+
.filter((entry) => entry?.route_kind !== 'resource')
|
|
211
|
+
.map((entry) => [entry.path, entry]));
|
|
212
|
+
const pageRoutes = (Array.isArray(routerManifest.routes) ? routerManifest.routes : [])
|
|
213
|
+
.map((route) => mergePageRouteMetadata(route, pageManifestByPath.get(route.path)));
|
|
281
214
|
const serverRoutes = pageRoutes
|
|
282
|
-
.filter((route) => route.
|
|
215
|
+
.filter((route) => route.prerender !== true && (route.server_script || route.has_scoped_server_data === true))
|
|
283
216
|
.map((route) => ({ ...route, route_kind: 'page' }))
|
|
284
217
|
.concat((Array.isArray(resourceManifest.routes) ? resourceManifest.routes : []).map((route) => ({
|
|
285
218
|
...route,
|
|
286
219
|
route_kind: 'resource'
|
|
287
220
|
})));
|
|
221
|
+
if (serverRoutes.some((route) => route.route_kind !== 'resource' && route.has_scoped_server_data === true)) {
|
|
222
|
+
await getScopedServerDataLowering();
|
|
223
|
+
}
|
|
288
224
|
await mkdir(serverDir, { recursive: true });
|
|
289
225
|
await copyRuntimeFiles(serverDir);
|
|
226
|
+
const serverGlobalMiddlewareMetadata = await writeGlobalMiddlewarePackage({
|
|
227
|
+
projectRoot,
|
|
228
|
+
serverDir,
|
|
229
|
+
globalMiddleware
|
|
230
|
+
});
|
|
290
231
|
const imageManifestSource = join(staticDir, '_zenith', 'image', 'manifest.json');
|
|
291
232
|
const emittedRoutes = [];
|
|
292
233
|
for (const { route, name } of assignServerRouteNames(serverRoutes)) {
|
|
@@ -314,6 +255,15 @@ export async function writeServerOutput({ coreOutputDir, staticDir, projectRoot,
|
|
|
314
255
|
routeDir,
|
|
315
256
|
route
|
|
316
257
|
});
|
|
258
|
+
const scopedServerData = await writeScopedServerDataModules({
|
|
259
|
+
projectRoot,
|
|
260
|
+
serverDir,
|
|
261
|
+
route,
|
|
262
|
+
pagesDir,
|
|
263
|
+
srcDir,
|
|
264
|
+
registry,
|
|
265
|
+
compilerOpts
|
|
266
|
+
});
|
|
317
267
|
const meta = {
|
|
318
268
|
name,
|
|
319
269
|
path: route.path,
|
|
@@ -336,13 +286,22 @@ export async function writeServerOutput({ coreOutputDir, staticDir, projectRoot,
|
|
|
336
286
|
image_manifest_file: route.route_kind === 'resource' ? null : imageManifestFile,
|
|
337
287
|
image_config: config?.images || {}
|
|
338
288
|
};
|
|
289
|
+
if (scopedServerData.length > 0) {
|
|
290
|
+
meta.has_scoped_server_data = true;
|
|
291
|
+
meta.scoped_server_data = scopedServerData;
|
|
292
|
+
}
|
|
339
293
|
if (route.route_kind !== 'resource' && Array.isArray(route.image_materialization) && route.image_materialization.length > 0) {
|
|
340
294
|
meta.image_materialization = route.image_materialization;
|
|
341
295
|
}
|
|
342
296
|
await writeFile(join(routeDir, 'route.json'), `${JSON.stringify(meta, null, 2)}\n`, 'utf8');
|
|
343
297
|
emittedRoutes.push(meta);
|
|
344
298
|
}
|
|
345
|
-
|
|
299
|
+
const serverManifest = {
|
|
300
|
+
base_path: basePath,
|
|
301
|
+
...(serverGlobalMiddlewareMetadata ? { global_middleware: serverGlobalMiddlewareMetadata } : {}),
|
|
302
|
+
routes: emittedRoutes
|
|
303
|
+
};
|
|
304
|
+
await writeFile(join(serverDir, 'manifest.json'), `${JSON.stringify(serverManifest, null, 2)}\n`, 'utf8');
|
|
346
305
|
return {
|
|
347
306
|
serverDir,
|
|
348
307
|
routes: emittedRoutes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { executeMatchedRoutePipeline, runGlobalMiddlewareChain } from "../server-contract/resolve.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { executeMatchedRoutePipeline, runGlobalMiddlewareChain } from '../server-contract/resolve.js';
|
|
@@ -61,6 +61,22 @@ function resolveWithinRoot(rootDir, requestPath) {
|
|
|
61
61
|
}
|
|
62
62
|
return null;
|
|
63
63
|
}
|
|
64
|
+
function resolveManifestMiddlewareModulePath(serverDir, serverManifest) {
|
|
65
|
+
const modulePath = serverManifest?.global_middleware?.module;
|
|
66
|
+
if (typeof modulePath !== 'string' || modulePath.trim().length === 0) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const normalized = normalize(modulePath).replace(/\\/g, '/');
|
|
70
|
+
if (normalized === '..' || normalized.startsWith('../') || normalized.startsWith('/')) {
|
|
71
|
+
throw new Error('[Zenith:Middleware] Invalid global middleware module path in server manifest.');
|
|
72
|
+
}
|
|
73
|
+
const root = resolve(serverDir);
|
|
74
|
+
const candidate = resolve(root, normalized);
|
|
75
|
+
if (candidate !== root && candidate.startsWith(`${root}${sep}`)) {
|
|
76
|
+
return candidate;
|
|
77
|
+
}
|
|
78
|
+
throw new Error('[Zenith:Middleware] Invalid global middleware module path in server manifest.');
|
|
79
|
+
}
|
|
64
80
|
function toStaticFilePath(staticDir, pathname) {
|
|
65
81
|
let resolvedPath = pathname;
|
|
66
82
|
if (resolvedPath === '/') {
|
|
@@ -193,6 +209,7 @@ async function loadRuntimeContext(options = {}) {
|
|
|
193
209
|
});
|
|
194
210
|
const serverManifest = await readJson(join(serverDir, 'manifest.json'), { routes: [] });
|
|
195
211
|
const allServerRoutes = Array.isArray(serverManifest.routes) ? serverManifest.routes : [];
|
|
212
|
+
const globalMiddlewareModulePath = resolveManifestMiddlewareModulePath(serverDir, serverManifest);
|
|
196
213
|
return {
|
|
197
214
|
distDir,
|
|
198
215
|
serverDir,
|
|
@@ -202,6 +219,7 @@ async function loadRuntimeContext(options = {}) {
|
|
|
202
219
|
serverRoutes: allServerRoutes,
|
|
203
220
|
pageServerRoutes: allServerRoutes.filter((route) => route?.route_kind !== 'resource'),
|
|
204
221
|
resourceServerRoutes: allServerRoutes.filter((route) => route?.route_kind === 'resource'),
|
|
222
|
+
globalMiddlewareModulePath,
|
|
205
223
|
images: config.images || {},
|
|
206
224
|
basePath: normalizeBasePath(config.base_path || '/')
|
|
207
225
|
};
|
|
@@ -323,7 +341,8 @@ async function handleNodeRequest(req, res, context, serverOrigin) {
|
|
|
323
341
|
request,
|
|
324
342
|
route: resourceResolved.route,
|
|
325
343
|
params: resourceResolved.params,
|
|
326
|
-
routeModulePath: join(routeDir, 'route', 'entry.js')
|
|
344
|
+
routeModulePath: join(routeDir, 'route', 'entry.js'),
|
|
345
|
+
globalMiddlewareModulePath: context.globalMiddlewareModulePath
|
|
327
346
|
});
|
|
328
347
|
await sendFetchResponse(res, response, req.method);
|
|
329
348
|
return;
|
|
@@ -337,6 +356,7 @@ async function handleNodeRequest(req, res, context, serverOrigin) {
|
|
|
337
356
|
route: serverResolved.route,
|
|
338
357
|
params: serverResolved.params,
|
|
339
358
|
routeModulePath: join(routeDir, 'route', 'entry.js'),
|
|
359
|
+
globalMiddlewareModulePath: context.globalMiddlewareModulePath,
|
|
340
360
|
shellHtmlPath: join(routeDir, 'route', 'page.html'),
|
|
341
361
|
imageManifestPath: serverResolved.route.image_manifest_file
|
|
342
362
|
? join(routeDir, 'route', serverResolved.route.image_manifest_file)
|
|
@@ -6,9 +6,10 @@ export function extractInternalParams(requestUrl: any, route: any): {};
|
|
|
6
6
|
* route: { path: string, params?: string[], route_id?: string | null, server_script_path?: string | null, file?: string | null },
|
|
7
7
|
* params: Record<string, string>,
|
|
8
8
|
* routeModulePath: string,
|
|
9
|
+
* globalMiddlewareModulePath?: string | null,
|
|
9
10
|
* guardOnly?: boolean
|
|
10
11
|
* }} options
|
|
11
|
-
* @returns {Promise<{ publicUrl: URL, result: { kind: string, [key: string]: unknown }, trace: { guard: string, action: string, load: string }, status?: number, setCookies?: string[] }>}
|
|
12
|
+
* @returns {Promise<{ publicUrl: URL, ctx: object, result: { kind: string, [key: string]: unknown }, trace: { guard: string, action: string, load: string }, status?: number, setCookies?: string[] }>}
|
|
12
13
|
*/
|
|
13
14
|
export function executeRouteRequest(options: {
|
|
14
15
|
request: Request;
|
|
@@ -21,9 +22,11 @@ export function executeRouteRequest(options: {
|
|
|
21
22
|
};
|
|
22
23
|
params: Record<string, string>;
|
|
23
24
|
routeModulePath: string;
|
|
25
|
+
globalMiddlewareModulePath?: string | null;
|
|
24
26
|
guardOnly?: boolean;
|
|
25
27
|
}): Promise<{
|
|
26
28
|
publicUrl: URL;
|
|
29
|
+
ctx: object;
|
|
27
30
|
result: {
|
|
28
31
|
kind: string;
|
|
29
32
|
[key: string]: unknown;
|
|
@@ -42,9 +45,11 @@ export function executeRouteRequest(options: {
|
|
|
42
45
|
* route: { path: string, params?: string[], route_id?: string | null, server_script_path?: string | null, file?: string | null },
|
|
43
46
|
* params: Record<string, string>,
|
|
44
47
|
* routeModulePath: string,
|
|
48
|
+
* globalMiddlewareModulePath?: string | null,
|
|
45
49
|
* shellHtmlPath: string,
|
|
46
50
|
* imageManifestPath?: string | null,
|
|
47
|
-
* imageConfig?: Record<string, unknown
|
|
51
|
+
* imageConfig?: Record<string, unknown>,
|
|
52
|
+
* scopedModuleBaseDir?: string | null
|
|
48
53
|
* }} options
|
|
49
54
|
* @returns {Promise<Response>}
|
|
50
55
|
*/
|
|
@@ -59,16 +64,19 @@ export function renderRouteRequest(options: {
|
|
|
59
64
|
};
|
|
60
65
|
params: Record<string, string>;
|
|
61
66
|
routeModulePath: string;
|
|
67
|
+
globalMiddlewareModulePath?: string | null;
|
|
62
68
|
shellHtmlPath: string;
|
|
63
69
|
imageManifestPath?: string | null;
|
|
64
70
|
imageConfig?: Record<string, unknown>;
|
|
71
|
+
scopedModuleBaseDir?: string | null;
|
|
65
72
|
}): Promise<Response>;
|
|
66
73
|
/**
|
|
67
74
|
* @param {{
|
|
68
75
|
* request: Request,
|
|
69
76
|
* route: { path: string, params?: string[], route_id?: string | null, server_script_path?: string | null, file?: string | null, route_kind?: string | null, base_path?: string | null },
|
|
70
77
|
* params: Record<string, string>,
|
|
71
|
-
* routeModulePath: string
|
|
78
|
+
* routeModulePath: string,
|
|
79
|
+
* globalMiddlewareModulePath?: string | null
|
|
72
80
|
* }} options
|
|
73
81
|
* @returns {Promise<Response>}
|
|
74
82
|
*/
|
|
@@ -85,4 +93,5 @@ export function renderResourceRouteRequest(options: {
|
|
|
85
93
|
};
|
|
86
94
|
params: Record<string, string>;
|
|
87
95
|
routeModulePath: string;
|
|
96
|
+
globalMiddlewareModulePath?: string | null;
|
|
88
97
|
}): Promise<Response>;
|