@zenithbuild/cli 0.7.10 → 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 +14 -2
- 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/compiler-runtime.js +3 -0
- 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 -4
- package/dist/build/page-loop-state.js +17 -9
- package/dist/build/page-loop.js +18 -8
- package/dist/build/scoped-expression-context.d.ts +5 -0
- package/dist/build/scoped-expression-context.js +133 -0
- package/dist/build/server-script.js +13 -36
- package/dist/build/type-declarations.d.ts +2 -1
- package/dist/build/type-declarations.js +29 -52
- 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-build-session/helpers.js +27 -7
- package/dist/dev-build-session/session.js +19 -10
- package/dist/dev-server/build-error-response.d.ts +21 -0
- package/dist/dev-server/build-error-response.js +48 -0
- package/dist/dev-server/port-fallback.d.ts +15 -0
- package/dist/dev-server/port-fallback.js +61 -0
- package/dist/dev-server/request-handler.js +58 -5
- package/dist/dev-server/watcher.js +15 -0
- package/dist/dev-server.d.ts +5 -2
- package/dist/dev-server.js +129 -49
- 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/images/remote-fetch.d.ts +12 -0
- package/dist/images/remote-fetch.js +257 -0
- package/dist/images/service.d.ts +10 -0
- package/dist/images/service.js +9 -46
- package/dist/index.js +12 -2
- package/dist/manifest.d.ts +9 -1
- package/dist/manifest.js +70 -25
- 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/resource-response.js +25 -8
- package/dist/resource-route-module.js +5 -22
- package/dist/route-classification.d.ts +11 -0
- package/dist/route-classification.js +21 -0
- package/dist/route-handler-export-analysis.d.ts +22 -0
- package/dist/route-handler-export-analysis.js +41 -0
- 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 +144 -195
- package/dist/server-route-names.d.ts +2 -0
- package/dist/server-route-names.js +38 -0
- 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 +26 -3
- package/dist/server-runtime/route-render.d.ts +12 -3
- package/dist/server-runtime/route-render.js +67 -13
- package/dist/types/generate-env-dts.js +2 -44
- package/dist/types/zenith-env-dts.d.ts +4 -0
- package/dist/types/zenith-env-dts.js +96 -0
- package/package.json +3 -6
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export type ScopedServerDiagnosticSeverity = 'error' | 'warning';
|
|
2
|
+
export interface ScopedServerDiagnostic {
|
|
3
|
+
code: string;
|
|
4
|
+
severity: ScopedServerDiagnosticSeverity;
|
|
5
|
+
message: string;
|
|
6
|
+
filePath: string;
|
|
7
|
+
}
|
|
8
|
+
export type ScopedServerOwnerKind = 'layout' | 'component';
|
|
9
|
+
export type ScopedServerOwnerSyntax = 'variables' | 'explicit-data';
|
|
10
|
+
export interface ScopedServerDataOwnerBase {
|
|
11
|
+
ownerKind: ScopedServerOwnerKind;
|
|
12
|
+
ownerPath: string;
|
|
13
|
+
syntax: ScopedServerOwnerSyntax;
|
|
14
|
+
serializedVariableNames: string[];
|
|
15
|
+
level1VariableNames?: string[];
|
|
16
|
+
exportName: 'data';
|
|
17
|
+
}
|
|
18
|
+
export interface ScopedServerDataOwner extends ScopedServerDataOwnerBase {
|
|
19
|
+
ownerKey: string;
|
|
20
|
+
}
|
|
21
|
+
export interface CompilerOptsLike {
|
|
22
|
+
typescriptDefault?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface ScanRouteScopedServerOwnersOptions {
|
|
25
|
+
pageSource: string;
|
|
26
|
+
pageFile: string;
|
|
27
|
+
registry: Map<string, string>;
|
|
28
|
+
srcDir: string;
|
|
29
|
+
compilerOpts?: CompilerOptsLike;
|
|
30
|
+
}
|
|
31
|
+
export interface ScanRouteScopedServerOwnersResult {
|
|
32
|
+
owners: ScopedServerDataOwner[];
|
|
33
|
+
diagnostics: ScopedServerDiagnostic[];
|
|
34
|
+
}
|
|
35
|
+
export interface OwnerFileAnalysisResult {
|
|
36
|
+
owner: ScopedServerDataOwnerBase | null;
|
|
37
|
+
diagnostics: ScopedServerDiagnostic[];
|
|
38
|
+
}
|
|
39
|
+
export interface ScriptBlockPartition {
|
|
40
|
+
attrs: string;
|
|
41
|
+
body: string;
|
|
42
|
+
}
|
|
43
|
+
export type ScopedServerInstanceStrategy = 'singleton' | 'per-instance';
|
|
44
|
+
export type ScopedServerStaticPropValue = string | number | boolean | null | ScopedServerStaticPropValue[] | {
|
|
45
|
+
[key: string]: ScopedServerStaticPropValue;
|
|
46
|
+
};
|
|
47
|
+
export type ScopedServerStaticProps = Record<string, ScopedServerStaticPropValue>;
|
|
48
|
+
export interface ManifestScopedServerDataInstance {
|
|
49
|
+
key: string;
|
|
50
|
+
occurrenceId: string;
|
|
51
|
+
props: ScopedServerStaticProps;
|
|
52
|
+
}
|
|
53
|
+
export interface ManifestScopedServerDataEntry {
|
|
54
|
+
ownerKind: ScopedServerOwnerKind;
|
|
55
|
+
ownerKey: string;
|
|
56
|
+
syntax: ScopedServerOwnerSyntax;
|
|
57
|
+
exportName: 'data';
|
|
58
|
+
instanceStrategy: ScopedServerInstanceStrategy;
|
|
59
|
+
serializedVariableNames?: string[];
|
|
60
|
+
props?: ScopedServerStaticProps;
|
|
61
|
+
instances?: ManifestScopedServerDataInstance[];
|
|
62
|
+
}
|
|
63
|
+
export interface AnalyzeRouteScopedServerMetadataOptions {
|
|
64
|
+
pageSource: string;
|
|
65
|
+
pageFile: string;
|
|
66
|
+
registry: Map<string, string>;
|
|
67
|
+
srcDir: string;
|
|
68
|
+
compilerOpts?: CompilerOptsLike;
|
|
69
|
+
}
|
|
70
|
+
export interface AnalyzeRouteScopedServerMetadataResult {
|
|
71
|
+
hasScopedServerData: boolean;
|
|
72
|
+
scopedServerData: ManifestScopedServerDataEntry[];
|
|
73
|
+
diagnostics: ScopedServerDiagnostic[];
|
|
74
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function unwrapAuthControlFlow(error: any, where: any, allowedKinds: any): any;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AUTH_CONTROL_FLOW_FLAG } from './constants.js';
|
|
2
|
+
import { assertValidRouteResultShape } from './route-result-validation.js';
|
|
3
|
+
export function unwrapAuthControlFlow(error, where, allowedKinds) {
|
|
4
|
+
if (!error || typeof error !== 'object' || error[AUTH_CONTROL_FLOW_FLAG] !== true) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const result = error.result;
|
|
8
|
+
assertValidRouteResultShape(result, where, allowedKinds);
|
|
9
|
+
return result;
|
|
10
|
+
}
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
export function runGlobalMiddlewareChain({ middlewareFn, ctx, runRoute }: {
|
|
2
|
+
middlewareFn: any;
|
|
3
|
+
ctx: any;
|
|
4
|
+
runRoute: any;
|
|
5
|
+
}): Promise<{
|
|
6
|
+
result: any;
|
|
7
|
+
trace: any;
|
|
8
|
+
} | undefined>;
|
|
9
|
+
export function executeMatchedRoutePipeline({ exports, ctx, filePath, guardOnly, routeKind, globalMiddleware }: {
|
|
10
|
+
exports: any;
|
|
11
|
+
ctx: any;
|
|
12
|
+
filePath: any;
|
|
13
|
+
guardOnly?: boolean | undefined;
|
|
14
|
+
routeKind?: string | undefined;
|
|
15
|
+
globalMiddleware?: null | undefined;
|
|
16
|
+
}): Promise<{
|
|
17
|
+
result: any;
|
|
18
|
+
trace: any;
|
|
19
|
+
} | undefined>;
|
|
1
20
|
export function resolveRouteResult({ exports, ctx, filePath, guardOnly, routeKind }: {
|
|
2
21
|
exports: any;
|
|
3
22
|
ctx: any;
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import { STAGED_SET_COOKIES_KEY } from './constants.js';
|
|
2
1
|
import { validateServerExports } from './export-validation.js';
|
|
2
|
+
import { unwrapAuthControlFlow } from './auth-control-flow.js';
|
|
3
3
|
import { assertJsonSerializable } from './json-serializable.js';
|
|
4
4
|
import { assertValidRouteResultShape, isRouteResultLike } from './route-result-validation.js';
|
|
5
|
+
import { buildResolvedEnvelope } from './resolved-envelope.js';
|
|
5
6
|
import { allow, data, text } from './result-helpers.js';
|
|
6
7
|
import { invokeRouteStage } from './stage.js';
|
|
8
|
+
const GLOBAL_MIDDLEWARE_RESULT_ERROR = 'Global middleware may only continue with next() or short-circuit with redirect() / deny() in V1.';
|
|
9
|
+
const GLOBAL_MIDDLEWARE_DOUBLE_NEXT_ERROR = 'Global middleware called next() more than once.';
|
|
10
|
+
const GLOBAL_MIDDLEWARE_POST_NEXT_ERROR = 'Global middleware cannot override the route result after next() in V1.';
|
|
11
|
+
const GLOBAL_MIDDLEWARE_ALLOWED_KINDS = new Set(['redirect', 'deny']);
|
|
7
12
|
function buildActionState(result) {
|
|
8
13
|
if (!result || typeof result !== 'object') {
|
|
9
14
|
return null;
|
|
@@ -24,18 +29,85 @@ function buildActionState(result) {
|
|
|
24
29
|
}
|
|
25
30
|
return null;
|
|
26
31
|
}
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return
|
|
32
|
+
function createEmptyRouteTrace() {
|
|
33
|
+
return {
|
|
34
|
+
guard: 'none',
|
|
35
|
+
action: 'none',
|
|
36
|
+
load: 'none'
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function createUnsupportedMiddlewareResultError() {
|
|
40
|
+
return new Error(GLOBAL_MIDDLEWARE_RESULT_ERROR);
|
|
41
|
+
}
|
|
42
|
+
function buildMiddlewareShortCircuitEnvelope(result, ctx) {
|
|
43
|
+
assertValidRouteResultShape(result, 'global middleware return', GLOBAL_MIDDLEWARE_ALLOWED_KINDS);
|
|
44
|
+
return buildResolvedEnvelope({
|
|
45
|
+
result,
|
|
46
|
+
trace: createEmptyRouteTrace(),
|
|
47
|
+
ctx
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function normalizePreNextResult(result, ctx) {
|
|
51
|
+
if (!isRouteResultLike(result) || !GLOBAL_MIDDLEWARE_ALLOWED_KINDS.has(result.kind)) {
|
|
52
|
+
throw createUnsupportedMiddlewareResultError();
|
|
53
|
+
}
|
|
54
|
+
return buildMiddlewareShortCircuitEnvelope(result, ctx);
|
|
55
|
+
}
|
|
56
|
+
export async function runGlobalMiddlewareChain({ middlewareFn, ctx, runRoute }) {
|
|
57
|
+
if (typeof middlewareFn !== 'function' || typeof runRoute !== 'function') {
|
|
58
|
+
throw createUnsupportedMiddlewareResultError();
|
|
59
|
+
}
|
|
60
|
+
let nextCalled = false;
|
|
61
|
+
let capturedEnvelope;
|
|
62
|
+
const next = async () => {
|
|
63
|
+
if (nextCalled) {
|
|
64
|
+
throw new Error(GLOBAL_MIDDLEWARE_DOUBLE_NEXT_ERROR);
|
|
65
|
+
}
|
|
66
|
+
nextCalled = true;
|
|
67
|
+
capturedEnvelope = await runRoute();
|
|
68
|
+
return capturedEnvelope;
|
|
69
|
+
};
|
|
70
|
+
let middlewareResult;
|
|
71
|
+
try {
|
|
72
|
+
middlewareResult = await middlewareFn(ctx, next);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
const authResult = unwrapAuthControlFlow(error, 'global middleware return', GLOBAL_MIDDLEWARE_ALLOWED_KINDS);
|
|
76
|
+
if (authResult) {
|
|
77
|
+
return buildMiddlewareShortCircuitEnvelope(authResult, ctx);
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
if (nextCalled) {
|
|
82
|
+
if (middlewareResult === undefined || middlewareResult === capturedEnvelope) {
|
|
83
|
+
return capturedEnvelope;
|
|
84
|
+
}
|
|
85
|
+
throw new Error(GLOBAL_MIDDLEWARE_POST_NEXT_ERROR);
|
|
86
|
+
}
|
|
87
|
+
if (middlewareResult === undefined) {
|
|
88
|
+
throw createUnsupportedMiddlewareResultError();
|
|
89
|
+
}
|
|
90
|
+
return normalizePreNextResult(middlewareResult, ctx);
|
|
91
|
+
}
|
|
92
|
+
export async function executeMatchedRoutePipeline({ exports, ctx, filePath, guardOnly = false, routeKind = 'page', globalMiddleware = null }) {
|
|
93
|
+
const runRoute = () => resolveRouteResult({
|
|
94
|
+
exports,
|
|
95
|
+
ctx,
|
|
96
|
+
filePath,
|
|
97
|
+
guardOnly,
|
|
98
|
+
routeKind
|
|
99
|
+
});
|
|
100
|
+
if (globalMiddleware == null) {
|
|
101
|
+
return runRoute();
|
|
102
|
+
}
|
|
103
|
+
if (typeof globalMiddleware !== 'function') {
|
|
104
|
+
throw createUnsupportedMiddlewareResultError();
|
|
105
|
+
}
|
|
106
|
+
return runGlobalMiddlewareChain({
|
|
107
|
+
middlewareFn: globalMiddleware,
|
|
108
|
+
ctx,
|
|
109
|
+
runRoute
|
|
110
|
+
});
|
|
39
111
|
}
|
|
40
112
|
export async function resolveRouteResult({ exports, ctx, filePath, guardOnly = false, routeKind = 'page' }) {
|
|
41
113
|
validateServerExports({ exports, filePath, routeKind });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { STAGED_SET_COOKIES_KEY } from './constants.js';
|
|
2
|
+
export function buildResolvedEnvelope({ result, trace, status, ctx }) {
|
|
3
|
+
const envelope = { result, trace };
|
|
4
|
+
if (status !== undefined) {
|
|
5
|
+
envelope.status = status;
|
|
6
|
+
}
|
|
7
|
+
const setCookies = Array.isArray(ctx?.[STAGED_SET_COOKIES_KEY])
|
|
8
|
+
? ctx[STAGED_SET_COOKIES_KEY].slice()
|
|
9
|
+
: [];
|
|
10
|
+
if (setCookies.length > 0) {
|
|
11
|
+
envelope.setCookies = setCookies;
|
|
12
|
+
}
|
|
13
|
+
return envelope;
|
|
14
|
+
}
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { assertValidRouteResultShape } from './route-result-validation.js';
|
|
3
|
-
function unwrapAuthControlFlow(error, where, allowedKinds) {
|
|
4
|
-
if (!error || typeof error !== 'object' || error[AUTH_CONTROL_FLOW_FLAG] !== true) {
|
|
5
|
-
return null;
|
|
6
|
-
}
|
|
7
|
-
const result = error.result;
|
|
8
|
-
assertValidRouteResultShape(result, where, allowedKinds);
|
|
9
|
-
return result;
|
|
10
|
-
}
|
|
1
|
+
import { unwrapAuthControlFlow } from './auth-control-flow.js';
|
|
11
2
|
export async function invokeRouteStage({ fn, ctx, where, allowedKinds }) {
|
|
12
3
|
try {
|
|
13
4
|
return await fn(ctx);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function writeServerModulePackage({ projectRoot, serverDir, entrySource, entrySourcePath, entryOutputPath, modulesRoot, validateMiddlewareImports }: {
|
|
2
|
+
projectRoot: any;
|
|
3
|
+
serverDir: any;
|
|
4
|
+
entrySource: any;
|
|
5
|
+
entrySourcePath: any;
|
|
6
|
+
entryOutputPath: any;
|
|
7
|
+
modulesRoot: any;
|
|
8
|
+
validateMiddlewareImports?: boolean | undefined;
|
|
9
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { cp, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { dirname, extname, join, relative, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const PACKAGE_REQUIRE = createRequire(import.meta.url);
|
|
7
|
+
const RELATIVE_SPECIFIER_RE = /((?:import|export)\s+(?:[^'"]*?\s+from\s+)?|import\s*\()\s*(['"])([^'"]+)\2/g;
|
|
8
|
+
const SPECIAL_SERVER_SPECIFIERS = new Map([
|
|
9
|
+
['zenith:server-contract', 'server-contract.js'],
|
|
10
|
+
['zenith:route-auth', 'auth/route-auth.js']
|
|
11
|
+
]);
|
|
12
|
+
const SERVER_MODULE_EXTENSIONS = new Set([
|
|
13
|
+
'.ts',
|
|
14
|
+
'.tsx',
|
|
15
|
+
'.mts',
|
|
16
|
+
'.cts',
|
|
17
|
+
'.js',
|
|
18
|
+
'.jsx',
|
|
19
|
+
'.mjs',
|
|
20
|
+
'.cjs',
|
|
21
|
+
'.json'
|
|
22
|
+
]);
|
|
23
|
+
function resolveTypeScriptApi(projectRoot) {
|
|
24
|
+
try {
|
|
25
|
+
const projectRequire = createRequire(join(projectRoot, '__zenith_server_output_loader__.js'));
|
|
26
|
+
return projectRequire('typescript');
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
try {
|
|
30
|
+
return PACKAGE_REQUIRE('typescript');
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
throw new Error('[Zenith:Build] Server-capable targets require the `typescript` package to transpile route modules.');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function replaceSpecifier(source, original, nextValue) {
|
|
38
|
+
return source.replace(new RegExp(`(['"])${escapeRegex(original)}\\1`, 'g'), (_, quote) => `${quote}${nextValue}${quote}`);
|
|
39
|
+
}
|
|
40
|
+
function escapeRegex(value) {
|
|
41
|
+
return String(value).replace(/[|\\{}()[\]^$+*?.-]/g, '\\$&');
|
|
42
|
+
}
|
|
43
|
+
function isRelativeSpecifier(specifier) {
|
|
44
|
+
return (specifier.startsWith('./') ||
|
|
45
|
+
specifier.startsWith('../') ||
|
|
46
|
+
specifier.startsWith('/') ||
|
|
47
|
+
specifier.startsWith('file:'));
|
|
48
|
+
}
|
|
49
|
+
function resolveModuleCandidates(basePath) {
|
|
50
|
+
if (extname(basePath)) {
|
|
51
|
+
return [basePath];
|
|
52
|
+
}
|
|
53
|
+
return [
|
|
54
|
+
basePath,
|
|
55
|
+
`${basePath}.ts`,
|
|
56
|
+
`${basePath}.tsx`,
|
|
57
|
+
`${basePath}.mts`,
|
|
58
|
+
`${basePath}.cts`,
|
|
59
|
+
`${basePath}.js`,
|
|
60
|
+
`${basePath}.mjs`,
|
|
61
|
+
`${basePath}.cjs`,
|
|
62
|
+
`${basePath}.json`,
|
|
63
|
+
join(basePath, 'index.ts'),
|
|
64
|
+
join(basePath, 'index.tsx'),
|
|
65
|
+
join(basePath, 'index.mts'),
|
|
66
|
+
join(basePath, 'index.cts'),
|
|
67
|
+
join(basePath, 'index.js'),
|
|
68
|
+
join(basePath, 'index.mjs'),
|
|
69
|
+
join(basePath, 'index.cjs'),
|
|
70
|
+
join(basePath, 'index.json')
|
|
71
|
+
];
|
|
72
|
+
}
|
|
73
|
+
function resolveImportedModule(specifier, sourceFile) {
|
|
74
|
+
if (!isRelativeSpecifier(specifier)) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const baseDir = dirname(sourceFile);
|
|
78
|
+
const basePath = specifier.startsWith('file:')
|
|
79
|
+
? new URL(specifier)
|
|
80
|
+
: resolve(baseDir, specifier);
|
|
81
|
+
const filePath = basePath instanceof URL ? fileURLToPath(basePath) : basePath;
|
|
82
|
+
const candidates = resolveModuleCandidates(filePath);
|
|
83
|
+
const found = candidates.find((candidate) => existsSync(candidate));
|
|
84
|
+
if (!found) {
|
|
85
|
+
throw new Error(`[Zenith:Build] Cannot resolve server import "${specifier}" from "${sourceFile}"`);
|
|
86
|
+
}
|
|
87
|
+
return found;
|
|
88
|
+
}
|
|
89
|
+
function gatherSpecifiers(source) {
|
|
90
|
+
const results = [];
|
|
91
|
+
for (const match of source.matchAll(RELATIVE_SPECIFIER_RE)) {
|
|
92
|
+
const specifier = String(match[3] || '');
|
|
93
|
+
results.push(specifier);
|
|
94
|
+
}
|
|
95
|
+
return results;
|
|
96
|
+
}
|
|
97
|
+
function transpileSource(ts, source, filePath) {
|
|
98
|
+
return ts.transpileModule(source, {
|
|
99
|
+
compilerOptions: {
|
|
100
|
+
module: ts.ModuleKind.ESNext,
|
|
101
|
+
target: ts.ScriptTarget.ES2022,
|
|
102
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
103
|
+
esModuleInterop: true,
|
|
104
|
+
allowSyntheticDefaultImports: true
|
|
105
|
+
},
|
|
106
|
+
fileName: filePath
|
|
107
|
+
}).outputText;
|
|
108
|
+
}
|
|
109
|
+
function outputPathForSource(projectRoot, modulesRoot, sourcePath) {
|
|
110
|
+
const relativePath = relative(projectRoot, sourcePath).replaceAll('\\', '/');
|
|
111
|
+
const nextRelative = extname(relativePath) === '.json'
|
|
112
|
+
? relativePath
|
|
113
|
+
: relativePath.replace(/\.(tsx|ts|mts|cts|jsx|js|mjs|cjs)$/i, '.js');
|
|
114
|
+
return join(modulesRoot, nextRelative);
|
|
115
|
+
}
|
|
116
|
+
function assertSupportedMiddlewareImport(specifier, sourceFile) {
|
|
117
|
+
if (!isRelativeSpecifier(specifier)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const extension = extname(specifier.startsWith('file:') ? fileURLToPath(new URL(specifier)) : specifier).toLowerCase();
|
|
121
|
+
if (!extension || SERVER_MODULE_EXTENSIONS.has(extension)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
throw new Error(`[Zenith:Middleware] Unsupported middleware import "${specifier}" from "${sourceFile}". ` +
|
|
125
|
+
'Global middleware may only import JavaScript, TypeScript, or JSON modules.');
|
|
126
|
+
}
|
|
127
|
+
function assertSupportedMiddlewareSourcePath(sourcePath, specifier, sourceFile) {
|
|
128
|
+
const extension = extname(sourcePath).toLowerCase();
|
|
129
|
+
if (SERVER_MODULE_EXTENSIONS.has(extension)) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
throw new Error(`[Zenith:Middleware] Unsupported middleware import "${specifier}" from "${sourceFile}". ` +
|
|
133
|
+
'Global middleware may only import JavaScript, TypeScript, or JSON modules.');
|
|
134
|
+
}
|
|
135
|
+
function assertLiteralMiddlewareDynamicImports(ts, source, sourceFile) {
|
|
136
|
+
const parsed = ts.createSourceFile(sourceFile, String(source || ''), ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
137
|
+
let invalid = false;
|
|
138
|
+
function visit(node) {
|
|
139
|
+
if (invalid) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (ts.isCallExpression(node) &&
|
|
143
|
+
node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
144
|
+
const firstArg = node.arguments?.[0];
|
|
145
|
+
if (!firstArg || !ts.isStringLiteralLike(firstArg)) {
|
|
146
|
+
invalid = true;
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
ts.forEachChild(node, visit);
|
|
151
|
+
}
|
|
152
|
+
ts.forEachChild(parsed, visit);
|
|
153
|
+
if (invalid) {
|
|
154
|
+
throw new Error('[Zenith:Middleware] Dynamic middleware imports must use a string literal specifier.');
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function compileImportedModule({ projectRoot, modulesRoot, serverDir, sourcePath, sourceSpecifier, importerPath, ts, seen, validateMiddlewareImports = false }) {
|
|
158
|
+
if (seen.has(sourcePath)) {
|
|
159
|
+
return outputPathForSource(projectRoot, modulesRoot, sourcePath);
|
|
160
|
+
}
|
|
161
|
+
seen.add(sourcePath);
|
|
162
|
+
if (validateMiddlewareImports) {
|
|
163
|
+
assertSupportedMiddlewareSourcePath(sourcePath, sourceSpecifier, importerPath);
|
|
164
|
+
}
|
|
165
|
+
const outPath = outputPathForSource(projectRoot, modulesRoot, sourcePath);
|
|
166
|
+
await mkdir(dirname(outPath), { recursive: true });
|
|
167
|
+
if (extname(sourcePath) === '.json') {
|
|
168
|
+
await cp(sourcePath, outPath, { force: true });
|
|
169
|
+
return outPath;
|
|
170
|
+
}
|
|
171
|
+
const source = await readFile(sourcePath, 'utf8');
|
|
172
|
+
if (validateMiddlewareImports) {
|
|
173
|
+
assertLiteralMiddlewareDynamicImports(ts, source, sourcePath);
|
|
174
|
+
}
|
|
175
|
+
let output = transpileSource(ts, source, sourcePath);
|
|
176
|
+
for (const specifier of gatherSpecifiers(output)) {
|
|
177
|
+
const specialSpecifierPath = SPECIAL_SERVER_SPECIFIERS.get(specifier);
|
|
178
|
+
if (specialSpecifierPath) {
|
|
179
|
+
const nextSpecifier = relative(dirname(outPath), join(serverDir, specialSpecifierPath)).replaceAll('\\', '/');
|
|
180
|
+
output = replaceSpecifier(output, specifier, nextSpecifier.startsWith('.') ? nextSpecifier : `./${nextSpecifier}`);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (!isRelativeSpecifier(specifier)) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (validateMiddlewareImports) {
|
|
187
|
+
assertSupportedMiddlewareImport(specifier, sourcePath);
|
|
188
|
+
}
|
|
189
|
+
const resolvedPath = resolveImportedModule(specifier, sourcePath);
|
|
190
|
+
if (!resolvedPath) {
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const compiledDependencyPath = await compileImportedModule({
|
|
194
|
+
projectRoot,
|
|
195
|
+
modulesRoot,
|
|
196
|
+
serverDir,
|
|
197
|
+
sourcePath: resolvedPath,
|
|
198
|
+
sourceSpecifier: specifier,
|
|
199
|
+
importerPath: sourcePath,
|
|
200
|
+
ts,
|
|
201
|
+
seen,
|
|
202
|
+
validateMiddlewareImports
|
|
203
|
+
});
|
|
204
|
+
const nextSpecifier = relative(dirname(outPath), compiledDependencyPath).replaceAll('\\', '/');
|
|
205
|
+
output = replaceSpecifier(output, specifier, nextSpecifier.startsWith('.') ? nextSpecifier : `./${nextSpecifier}`);
|
|
206
|
+
}
|
|
207
|
+
await writeFile(outPath, output, 'utf8');
|
|
208
|
+
return outPath;
|
|
209
|
+
}
|
|
210
|
+
export async function writeServerModulePackage({ projectRoot, serverDir, entrySource, entrySourcePath, entryOutputPath, modulesRoot, validateMiddlewareImports = false }) {
|
|
211
|
+
const ts = resolveTypeScriptApi(projectRoot);
|
|
212
|
+
const seen = new Set();
|
|
213
|
+
if (validateMiddlewareImports) {
|
|
214
|
+
assertLiteralMiddlewareDynamicImports(ts, entrySource, entrySourcePath);
|
|
215
|
+
}
|
|
216
|
+
let entryOutput = transpileSource(ts, entrySource || '', entrySourcePath || 'route-entry.ts');
|
|
217
|
+
for (const specifier of gatherSpecifiers(entryOutput)) {
|
|
218
|
+
const specialSpecifierPath = SPECIAL_SERVER_SPECIFIERS.get(specifier);
|
|
219
|
+
if (specialSpecifierPath) {
|
|
220
|
+
const nextSpecifier = relative(dirname(entryOutputPath), join(serverDir, specialSpecifierPath)).replaceAll('\\', '/');
|
|
221
|
+
entryOutput = replaceSpecifier(entryOutput, specifier, nextSpecifier.startsWith('.') ? nextSpecifier : `./${nextSpecifier}`);
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
if (!isRelativeSpecifier(specifier)) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (validateMiddlewareImports) {
|
|
228
|
+
assertSupportedMiddlewareImport(specifier, entrySourcePath);
|
|
229
|
+
}
|
|
230
|
+
const resolvedPath = resolveImportedModule(specifier, entrySourcePath || projectRoot);
|
|
231
|
+
if (!resolvedPath) {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const compiledDependencyPath = await compileImportedModule({
|
|
235
|
+
projectRoot,
|
|
236
|
+
modulesRoot,
|
|
237
|
+
serverDir,
|
|
238
|
+
sourcePath: resolvedPath,
|
|
239
|
+
sourceSpecifier: specifier,
|
|
240
|
+
importerPath: entrySourcePath,
|
|
241
|
+
ts,
|
|
242
|
+
seen,
|
|
243
|
+
validateMiddlewareImports
|
|
244
|
+
});
|
|
245
|
+
const nextSpecifier = relative(dirname(entryOutputPath), compiledDependencyPath).replaceAll('\\', '/');
|
|
246
|
+
entryOutput = replaceSpecifier(entryOutput, specifier, nextSpecifier.startsWith('.') ? nextSpecifier : `./${nextSpecifier}`);
|
|
247
|
+
}
|
|
248
|
+
await mkdir(dirname(entryOutputPath), { recursive: true });
|
|
249
|
+
await writeFile(entryOutputPath, entryOutput, 'utf8');
|
|
250
|
+
}
|
package/dist/server-output.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
export function writeServerOutput({ coreOutputDir, staticDir, projectRoot, config, basePath }: {
|
|
1
|
+
export function writeServerOutput({ coreOutputDir, staticDir, projectRoot, config, basePath, globalMiddleware, pageManifest, pagesDir, srcDir, registry, compilerOpts }: {
|
|
2
2
|
coreOutputDir: any;
|
|
3
3
|
staticDir: any;
|
|
4
4
|
projectRoot: any;
|
|
5
5
|
config: any;
|
|
6
6
|
basePath?: string | undefined;
|
|
7
|
+
globalMiddleware?: null | undefined;
|
|
8
|
+
pageManifest?: never[] | undefined;
|
|
9
|
+
pagesDir?: null | undefined;
|
|
10
|
+
srcDir?: null | undefined;
|
|
11
|
+
registry?: null | undefined;
|
|
12
|
+
compilerOpts?: {} | undefined;
|
|
7
13
|
}): Promise<{
|
|
8
14
|
serverDir: string;
|
|
9
15
|
routes: {
|