@webstir-io/webstir-frontend 0.1.40 → 0.1.41
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 +124 -60
- package/dist/assets/imageOptimizer.js +10 -15
- package/dist/assets/precompression.js +1 -1
- package/dist/builders/contentBuilder.js +102 -90
- package/dist/builders/cssBuilder.js +25 -19
- package/dist/builders/htmlBuilder.js +57 -42
- package/dist/builders/index.js +1 -1
- package/dist/builders/jsBuilder.js +219 -76
- package/dist/builders/staticAssetsBuilder.js +27 -9
- package/dist/builders/types.d.ts +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +6 -30
- package/dist/config/manifest.js +7 -6
- package/dist/config/paths.js +2 -2
- package/dist/config/schema.d.ts +8 -0
- package/dist/config/schema.js +7 -6
- package/dist/config/setup.js +1 -1
- package/dist/config/workspace.js +11 -9
- package/dist/core/constants.d.ts +1 -1
- package/dist/core/constants.js +5 -5
- package/dist/core/diagnostics.js +1 -1
- package/dist/core/pages.js +4 -4
- package/dist/hooks.js +3 -3
- package/dist/html/criticalCss.js +6 -3
- package/dist/html/htmlSecurity.d.ts +6 -1
- package/dist/html/htmlSecurity.js +28 -14
- package/dist/html/lazyLoad.js +1 -1
- package/dist/html/pageScaffold.js +1 -1
- package/dist/html/resourceHints.js +5 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/inspect.d.ts +2 -0
- package/dist/inspect.js +110 -0
- package/dist/modes/ssg/metadata.js +4 -4
- package/dist/modes/ssg/routing.js +2 -5
- package/dist/modes/ssg/seo.js +5 -5
- package/dist/modes/ssg/views.js +17 -11
- package/dist/operations.js +18 -10
- package/dist/pipeline.d.ts +1 -0
- package/dist/pipeline.js +6 -1
- package/dist/provider.js +28 -24
- package/dist/runtime/boundary.d.ts +28 -0
- package/dist/runtime/boundary.js +247 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/types.d.ts +52 -0
- package/dist/utils/fs.d.ts +11 -10
- package/dist/utils/fs.js +48 -20
- package/dist/utils/glob.d.ts +8 -0
- package/dist/utils/glob.js +21 -0
- package/dist/utils/hash.js +1 -2
- package/dist/utils/pagePaths.js +2 -2
- package/package.json +19 -14
- package/scripts/publish.sh +2 -94
- package/scripts/update-contract.sh +12 -10
- package/src/assets/assetManifest.ts +39 -29
- package/src/assets/imageOptimizer.ts +91 -82
- package/src/assets/precompression.ts +22 -16
- package/src/builders/contentBuilder.ts +1224 -1149
- package/src/builders/cssBuilder.ts +466 -417
- package/src/builders/htmlBuilder.ts +511 -448
- package/src/builders/index.ts +7 -7
- package/src/builders/jsBuilder.ts +538 -280
- package/src/builders/staticAssetsBuilder.ts +166 -135
- package/src/builders/types.ts +7 -6
- package/src/cli.ts +66 -90
- package/src/config/manifest.ts +16 -14
- package/src/config/paths.ts +5 -5
- package/src/config/schema.ts +38 -37
- package/src/config/setup.ts +7 -7
- package/src/config/workspace.ts +118 -116
- package/src/config/workspaceManifest.ts +14 -14
- package/src/core/constants.ts +62 -62
- package/src/core/diagnostics.ts +26 -26
- package/src/core/pages.ts +19 -19
- package/src/hooks.ts +128 -118
- package/src/html/criticalCss.ts +84 -77
- package/src/html/htmlSecurity.ts +107 -66
- package/src/html/lazyLoad.ts +22 -19
- package/src/html/pageScaffold.ts +37 -28
- package/src/html/resourceHints.ts +83 -74
- package/src/index.ts +2 -0
- package/src/inspect.ts +158 -0
- package/src/modes/ssg/metadata.ts +53 -51
- package/src/modes/ssg/routing.ts +177 -177
- package/src/modes/ssg/seo.ts +208 -200
- package/src/modes/ssg/validation.ts +31 -25
- package/src/modes/ssg/views.ts +257 -238
- package/src/operations.ts +105 -95
- package/src/pipeline.ts +81 -69
- package/src/provider.ts +184 -176
- package/src/runtime/boundary.ts +325 -0
- package/src/runtime/index.ts +1 -0
- package/src/types.ts +107 -48
- package/src/utils/changedFile.ts +22 -22
- package/src/utils/fs.ts +73 -26
- package/src/utils/glob.ts +38 -0
- package/src/utils/hash.ts +2 -4
- package/src/utils/pagePaths.ts +35 -23
- package/src/utils/pathMatch.ts +26 -23
- package/tests/add-page-defaults.test.js +44 -39
- package/tests/bundlerParity.test.js +252 -0
- package/tests/cli.contract.test.js +13 -0
- package/tests/content-pages.test.js +108 -13
- package/tests/css-app-imports.test.js +22 -11
- package/tests/css-page-imports.test.js +26 -13
- package/tests/diagnostics.test.js +39 -36
- package/tests/features.test.js +48 -43
- package/tests/hooks.test.js +58 -42
- package/tests/htmlSecurity.test.js +66 -0
- package/tests/inspect.test.js +148 -0
- package/tests/provider.integration.test.js +71 -20
- package/tests/runtime.test.js +493 -0
- package/tests/ssg-defaults.test.js +284 -177
- package/tests/ssg-guardrails.test.js +51 -51
- package/tsconfig.json +3 -10
- package/dist/watch/frontendFiles.d.ts +0 -3
- package/dist/watch/frontendFiles.js +0 -25
- package/dist/watch/hotUpdateTracker.d.ts +0 -51
- package/dist/watch/hotUpdateTracker.js +0 -205
- package/dist/watch/pipelineHelpers.d.ts +0 -26
- package/dist/watch/pipelineHelpers.js +0 -177
- package/dist/watch/types.d.ts +0 -27
- package/dist/watch/types.js +0 -1
- package/dist/watch/watchCoordinator.d.ts +0 -36
- package/dist/watch/watchCoordinator.js +0 -551
- package/dist/watch/watchDaemon.d.ts +0 -17
- package/dist/watch/watchDaemon.js +0 -127
- package/dist/watch/watchReporter.d.ts +0 -21
- package/dist/watch/watchReporter.js +0 -64
- package/scripts/smoke.mjs +0 -35
- package/src/watch/frontendFiles.ts +0 -32
- package/src/watch/hotUpdateTracker.ts +0 -285
- package/src/watch/pipelineHelpers.ts +0 -242
- package/src/watch/types.ts +0 -23
- package/src/watch/watchCoordinator.ts +0 -666
- package/src/watch/watchDaemon.ts +0 -144
- package/src/watch/watchReporter.ts +0 -98
package/src/modes/ssg/views.ts
CHANGED
|
@@ -6,304 +6,323 @@ import { FOLDERS } from '../../core/constants.js';
|
|
|
6
6
|
import type { WorkspaceModuleView, WorkspacePackageJson } from '../../config/workspaceManifest.js';
|
|
7
7
|
|
|
8
8
|
interface ViewDefinitionLike {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
readonly name?: string;
|
|
10
|
+
readonly path?: string;
|
|
11
|
+
readonly renderMode?: 'ssg' | 'ssr' | 'spa';
|
|
12
|
+
readonly staticPaths?: readonly string[];
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
interface ViewSpecLike {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
readonly definition?: ViewDefinitionLike;
|
|
17
|
+
readonly load?: (context: unknown) => unknown | Promise<unknown>;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
interface ModuleDefinitionLike {
|
|
21
|
-
|
|
21
|
+
readonly views?: readonly ViewSpecLike[];
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
interface ViewDataEntry {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
readonly data: any;
|
|
25
|
+
readonly viewName: string;
|
|
26
|
+
readonly path: string;
|
|
27
|
+
readonly data: unknown;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
export async function generateSsgViewData(config: FrontendConfig): Promise<void> {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
const workspaceRoot = config.paths.workspace;
|
|
32
|
+
const pkgPath = path.join(workspaceRoot, 'package.json');
|
|
33
|
+
const pkg = await readJson<WorkspacePackageJson>(pkgPath);
|
|
34
|
+
const moduleConfig = pkg?.webstir?.moduleManifest;
|
|
35
|
+
const viewMetadata = moduleConfig?.views ?? [];
|
|
36
|
+
const workspaceMode = pkg?.webstir?.mode;
|
|
37
|
+
const isSsgWorkspace = typeof workspaceMode === 'string' && workspaceMode.toLowerCase() === 'ssg';
|
|
38
|
+
|
|
39
|
+
const moduleDefinition = await loadBackendModuleDefinition(workspaceRoot);
|
|
40
|
+
if (!moduleDefinition?.views || moduleDefinition.views.length === 0) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const perPageData = new Map<string, ViewDataEntry[]>();
|
|
45
|
+
|
|
46
|
+
for (const spec of moduleDefinition.views) {
|
|
47
|
+
const definition = spec.definition ?? {};
|
|
48
|
+
const viewName = definition.name ?? '';
|
|
49
|
+
const viewPathTemplate = definition.path ?? '';
|
|
50
|
+
const meta = findViewMetadata(viewMetadata, viewName, viewPathTemplate);
|
|
51
|
+
const renderMode =
|
|
52
|
+
meta?.renderMode ?? definition.renderMode ?? (isSsgWorkspace ? 'ssg' : undefined);
|
|
53
|
+
if (renderMode !== 'ssg') {
|
|
54
|
+
continue;
|
|
43
55
|
}
|
|
44
56
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const definition = spec.definition ?? {};
|
|
49
|
-
const viewName = definition.name ?? '';
|
|
50
|
-
const viewPathTemplate = definition.path ?? '';
|
|
51
|
-
const meta = findViewMetadata(viewMetadata, viewName, viewPathTemplate);
|
|
52
|
-
const renderMode = meta?.renderMode ?? definition.renderMode ?? (isSsgWorkspace ? 'ssg' : undefined);
|
|
53
|
-
if (renderMode !== 'ssg') {
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const staticPaths = getEffectiveStaticPaths(meta, definition, isSsgWorkspace);
|
|
58
|
-
if (!spec.load || !Array.isArray(staticPaths) || staticPaths.length === 0) {
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
for (const rawPath of staticPaths) {
|
|
63
|
-
if (typeof rawPath !== 'string' || rawPath.length === 0) {
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const normalizedPath = normalizePath(rawPath);
|
|
68
|
-
const params = deriveRouteParams(viewPathTemplate, normalizedPath);
|
|
69
|
-
if (!params) {
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const ssrContext = createMinimalSsrContext(normalizedPath, params);
|
|
74
|
-
|
|
75
|
-
let data: unknown;
|
|
76
|
-
try {
|
|
77
|
-
data = await spec.load(ssrContext);
|
|
78
|
-
} catch {
|
|
79
|
-
// Best-effort only; skip paths that fail to load.
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const pageName = normalizedPath === '/' ? FOLDERS.home : firstPathSegment(normalizedPath) ?? FOLDERS.home;
|
|
84
|
-
const entries = perPageData.get(pageName) ?? [];
|
|
85
|
-
entries.push({
|
|
86
|
-
viewName: viewName || viewPathTemplate || normalizedPath,
|
|
87
|
-
path: normalizedPath,
|
|
88
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
89
|
-
data
|
|
90
|
-
});
|
|
91
|
-
perPageData.set(pageName, entries);
|
|
92
|
-
}
|
|
57
|
+
const staticPaths = getEffectiveStaticPaths(meta, definition, isSsgWorkspace);
|
|
58
|
+
if (!spec.load || !Array.isArray(staticPaths) || staticPaths.length === 0) {
|
|
59
|
+
continue;
|
|
93
60
|
}
|
|
94
61
|
|
|
95
|
-
|
|
96
|
-
|
|
62
|
+
for (const rawPath of staticPaths) {
|
|
63
|
+
if (typeof rawPath !== 'string' || rawPath.length === 0) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const normalizedPath = normalizePath(rawPath);
|
|
68
|
+
const params = deriveRouteParams(viewPathTemplate, normalizedPath);
|
|
69
|
+
if (!params) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const ssrContext = createMinimalSsrContext(normalizedPath, params);
|
|
74
|
+
|
|
75
|
+
let data: unknown;
|
|
76
|
+
try {
|
|
77
|
+
data = await spec.load(ssrContext);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
const viewLabel = viewName || viewPathTemplate || normalizedPath;
|
|
80
|
+
throw new Error(
|
|
81
|
+
`[webstir-frontend] failed to load SSG view data for "${viewLabel}" at ${normalizedPath}: ${formatErrorMessage(error)}`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const pageName =
|
|
86
|
+
normalizedPath === '/' ? FOLDERS.home : (firstPathSegment(normalizedPath) ?? FOLDERS.home);
|
|
87
|
+
const entries = perPageData.get(pageName) ?? [];
|
|
88
|
+
entries.push({
|
|
89
|
+
viewName: viewName || viewPathTemplate || normalizedPath,
|
|
90
|
+
path: normalizedPath,
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
92
|
+
data,
|
|
93
|
+
});
|
|
94
|
+
perPageData.set(pageName, entries);
|
|
97
95
|
}
|
|
96
|
+
}
|
|
98
97
|
|
|
99
|
-
|
|
98
|
+
if (perPageData.size === 0) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
const pageDir = path.join(pagesRoot, pageName);
|
|
103
|
-
if (!(await pathExists(pageDir))) {
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
102
|
+
const pagesRoot = config.paths.dist.pages;
|
|
106
103
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
104
|
+
for (const [pageName, entries] of perPageData.entries()) {
|
|
105
|
+
const pageDir = path.join(pagesRoot, pageName);
|
|
106
|
+
if (!(await pathExists(pageDir))) {
|
|
107
|
+
continue;
|
|
110
108
|
}
|
|
109
|
+
|
|
110
|
+
const dataPath = path.join(pageDir, 'view-data.json');
|
|
111
|
+
await ensureDir(pageDir);
|
|
112
|
+
await writeJson(dataPath, entries);
|
|
113
|
+
}
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
function findViewMetadata(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
views: readonly WorkspaceModuleView[],
|
|
118
|
+
name: string,
|
|
119
|
+
templatePath: string,
|
|
117
120
|
): WorkspaceModuleView | undefined {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)
|
|
121
|
+
return (
|
|
122
|
+
views.find(
|
|
123
|
+
(view) => (view.name && view.name === name) || (view.path && view.path === templatePath),
|
|
124
|
+
) ??
|
|
125
|
+
views.find((view) => view.path === templatePath) ??
|
|
126
|
+
views.find((view) => view.name === name)
|
|
127
|
+
);
|
|
123
128
|
}
|
|
124
129
|
|
|
125
|
-
async function loadBackendModuleDefinition(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
try {
|
|
140
|
-
const url = `${pathToFileURL(fullPath).href}?t=${Date.now()}`;
|
|
141
|
-
const imported = (await import(url)) as Record<string, unknown>;
|
|
142
|
-
const candidate = extractModuleDefinition(imported);
|
|
143
|
-
if (candidate) {
|
|
144
|
-
return candidate;
|
|
145
|
-
}
|
|
146
|
-
} catch {
|
|
147
|
-
// Best-effort only.
|
|
148
|
-
}
|
|
130
|
+
async function loadBackendModuleDefinition(
|
|
131
|
+
workspaceRoot: string,
|
|
132
|
+
): Promise<ModuleDefinitionLike | undefined> {
|
|
133
|
+
const buildRoot = path.join(workspaceRoot, 'build', 'backend');
|
|
134
|
+
const candidates = [
|
|
135
|
+
path.join(buildRoot, 'module.js'),
|
|
136
|
+
path.join(buildRoot, 'module.mjs'),
|
|
137
|
+
path.join(buildRoot, 'module', 'index.js'),
|
|
138
|
+
path.join(buildRoot, 'module', 'index.mjs'),
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
for (const fullPath of candidates) {
|
|
142
|
+
if (!(await pathExists(fullPath))) {
|
|
143
|
+
continue;
|
|
149
144
|
}
|
|
150
145
|
|
|
151
|
-
|
|
146
|
+
try {
|
|
147
|
+
const url = `${pathToFileURL(fullPath).href}?t=${Date.now()}`;
|
|
148
|
+
const imported = (await import(url)) as Record<string, unknown>;
|
|
149
|
+
const candidate = extractModuleDefinition(imported);
|
|
150
|
+
if (candidate) {
|
|
151
|
+
return candidate;
|
|
152
|
+
}
|
|
153
|
+
} catch (error) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`[webstir-frontend] failed to import backend module definition from ${fullPath}: ${formatErrorMessage(error)}`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return undefined;
|
|
152
161
|
}
|
|
153
162
|
|
|
154
|
-
function extractModuleDefinition(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
+
function extractModuleDefinition(
|
|
164
|
+
exports: Record<string, unknown>,
|
|
165
|
+
): ModuleDefinitionLike | undefined {
|
|
166
|
+
const keys = ['module', 'moduleDefinition', 'default', 'backendModule'];
|
|
167
|
+
for (const key of keys) {
|
|
168
|
+
if (key in exports) {
|
|
169
|
+
const value = exports[key as keyof typeof exports];
|
|
170
|
+
if (value && typeof value === 'object') {
|
|
171
|
+
return value as ModuleDefinitionLike;
|
|
172
|
+
}
|
|
163
173
|
}
|
|
164
|
-
|
|
174
|
+
}
|
|
175
|
+
return undefined;
|
|
165
176
|
}
|
|
166
177
|
|
|
167
178
|
function normalizePath(value: string): string {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
179
|
+
let s = value.trim();
|
|
180
|
+
if (!s.startsWith('/')) {
|
|
181
|
+
s = `/${s}`;
|
|
182
|
+
}
|
|
183
|
+
if (s.length > 1 && s.endsWith('/')) {
|
|
184
|
+
s = s.slice(0, -1);
|
|
185
|
+
}
|
|
186
|
+
return s;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function formatErrorMessage(error: unknown): string {
|
|
190
|
+
if (error instanceof Error && error.message) {
|
|
191
|
+
return error.message;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return String(error);
|
|
176
195
|
}
|
|
177
196
|
|
|
178
197
|
function firstPathSegment(pathname: string): string | undefined {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
198
|
+
const [, segment] = pathname.split('/');
|
|
199
|
+
if (!segment) {
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
return segment;
|
|
184
203
|
}
|
|
185
204
|
|
|
186
205
|
function deriveRouteParams(template: string, actual: string): Record<string, string> | null {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
206
|
+
if (!template || !actual) {
|
|
207
|
+
return {};
|
|
208
|
+
}
|
|
190
209
|
|
|
191
|
-
|
|
192
|
-
|
|
210
|
+
const templateSegments = template.split('/').filter(Boolean);
|
|
211
|
+
const actualSegments = actual.split('/').filter(Boolean);
|
|
193
212
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
213
|
+
if (templateSegments.length !== actualSegments.length) {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
197
216
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
217
|
+
const params: Record<string, string> = {};
|
|
218
|
+
|
|
219
|
+
for (let i = 0; i < templateSegments.length; i++) {
|
|
220
|
+
const templateSegment = templateSegments[i];
|
|
221
|
+
const actualSegment = actualSegments[i];
|
|
222
|
+
|
|
223
|
+
if (templateSegment.startsWith(':')) {
|
|
224
|
+
const key = templateSegment.slice(1);
|
|
225
|
+
if (!key) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
params[key] = decodeURIComponent(actualSegment);
|
|
229
|
+
} else if (templateSegment !== actualSegment) {
|
|
230
|
+
return null;
|
|
213
231
|
}
|
|
232
|
+
}
|
|
214
233
|
|
|
215
|
-
|
|
234
|
+
return params;
|
|
216
235
|
}
|
|
217
236
|
|
|
218
237
|
function createMinimalSsrContext(pathname: string, params: Record<string, string>): unknown {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
238
|
+
const url = new URL(`http://localhost${pathname}`);
|
|
239
|
+
|
|
240
|
+
const envAccessor = {
|
|
241
|
+
get(name: string): string | undefined {
|
|
242
|
+
return process.env[name];
|
|
243
|
+
},
|
|
244
|
+
require(name: string): string {
|
|
245
|
+
const value = process.env[name];
|
|
246
|
+
if (value === undefined) {
|
|
247
|
+
throw new Error(`Missing required env variable ${name} for SSG view rendering.`);
|
|
248
|
+
}
|
|
249
|
+
return value;
|
|
250
|
+
},
|
|
251
|
+
entries(): Record<string, string | undefined> {
|
|
252
|
+
return process.env as Record<string, string | undefined>;
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const logger = {
|
|
257
|
+
level: 'info',
|
|
258
|
+
log(_level: string, _message: string, _metadata?: Record<string, unknown>): void {
|
|
259
|
+
// no-op for SSG
|
|
260
|
+
},
|
|
261
|
+
debug(_message: string, _metadata?: Record<string, unknown>): void {
|
|
262
|
+
// no-op for SSG
|
|
263
|
+
},
|
|
264
|
+
info(_message: string, _metadata?: Record<string, unknown>): void {
|
|
265
|
+
// no-op for SSG
|
|
266
|
+
},
|
|
267
|
+
warn(_message: string, _metadata?: Record<string, unknown>): void {
|
|
268
|
+
// no-op for SSG
|
|
269
|
+
},
|
|
270
|
+
error(_message: string, _metadata?: Record<string, unknown>): void {
|
|
271
|
+
// no-op for SSG
|
|
272
|
+
},
|
|
273
|
+
with(_bindings: Record<string, unknown>) {
|
|
274
|
+
return this;
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
url,
|
|
280
|
+
params,
|
|
281
|
+
cookies: {},
|
|
282
|
+
headers: {},
|
|
283
|
+
auth: undefined,
|
|
284
|
+
session: null,
|
|
285
|
+
env: envAccessor,
|
|
286
|
+
logger,
|
|
287
|
+
now: () => new Date(),
|
|
288
|
+
};
|
|
270
289
|
}
|
|
271
290
|
|
|
272
291
|
function getEffectiveStaticPaths(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
292
|
+
meta: WorkspaceModuleView | undefined,
|
|
293
|
+
definition: ViewDefinitionLike,
|
|
294
|
+
isSsgWorkspace: boolean,
|
|
276
295
|
): readonly string[] {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
296
|
+
const explicit = meta?.staticPaths ?? definition.staticPaths ?? [];
|
|
297
|
+
if (Array.isArray(explicit) && explicit.length > 0) {
|
|
298
|
+
return explicit;
|
|
299
|
+
}
|
|
281
300
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
301
|
+
if (!isSsgWorkspace) {
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
285
304
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
305
|
+
const candidate = meta?.path ?? definition.path ?? '';
|
|
306
|
+
if (!isDefaultStaticPathCandidate(candidate)) {
|
|
307
|
+
return [];
|
|
308
|
+
}
|
|
290
309
|
|
|
291
|
-
|
|
310
|
+
return [candidate];
|
|
292
311
|
}
|
|
293
312
|
|
|
294
313
|
function isDefaultStaticPathCandidate(template: string): boolean {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
314
|
+
if (typeof template !== 'string') {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
298
317
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
318
|
+
const trimmed = template.trim();
|
|
319
|
+
if (!trimmed.startsWith('/')) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
303
322
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
323
|
+
if (trimmed.includes(':') || trimmed.includes('*')) {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
307
326
|
|
|
308
|
-
|
|
327
|
+
return true;
|
|
309
328
|
}
|