@pracht/cli 1.2.1 → 1.2.2
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/CHANGELOG.md +74 -0
- package/dist/{build-DlABFPbG.mjs → build-DkP2ffu9.mjs} +8 -0
- package/dist/{doctor-CQq64tfW.mjs → doctor-CwQJjJ83.mjs} +1 -1
- package/dist/{generate-sHtRHRAH.mjs → generate-C9AMSGsv.mjs} +141 -138
- package/dist/index.mjs +5 -5
- package/dist/{inspect-DP-h3vLw.mjs → inspect-MseHPU9s.mjs} +1 -1
- package/dist/{verification-CId4crSF.mjs → verification-Dwf1ZuNn.mjs} +116 -108
- package/dist/{verify-D7Bg1Su6.mjs → verify-o0G5Xuff.mjs} +1 -1
- package/package.json +9 -2
- package/src/build-metadata.ts +0 -101
- package/src/build-shared.ts +0 -163
- package/src/commands/build.ts +0 -137
- package/src/commands/dev.ts +0 -27
- package/src/commands/doctor.ts +0 -33
- package/src/commands/generate.ts +0 -616
- package/src/commands/inspect.ts +0 -212
- package/src/commands/verify.ts +0 -37
- package/src/constants.ts +0 -26
- package/src/index.ts +0 -21
- package/src/manifest.ts +0 -166
- package/src/project.ts +0 -160
- package/src/utils.ts +0 -72
- package/src/verification.ts +0 -710
- package/test/pracht-cli.test.js +0 -633
- package/tsdown.config.ts +0 -8
- /package/dist/{manifest-DGq1n5LT.mjs → manifest-Bs5hp3gA.mjs} +0 -0
- /package/dist/{project-DUyH_7eJ.mjs → project-BdMiN3s7.mjs} +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import {
|
|
1
|
+
import { a as readProjectConfig, c as resolveProjectPath, i as listFilesRecursively, n as displayPath, r as hasPagesAppShell } from "./project-BdMiN3s7.mjs";
|
|
2
|
+
import { n as extractRegistryEntries, r as extractRelativeModulePaths } from "./manifest-Bs5hp3gA.mjs";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { basename, dirname, relative, resolve } from "node:path";
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
|
-
//#region src/verification.ts
|
|
6
|
+
//#region src/verification-helpers.ts
|
|
7
7
|
const CONFIG_FILE_NAMES = new Set([
|
|
8
8
|
"vite.config.ts",
|
|
9
9
|
"vite.config.mts",
|
|
@@ -14,57 +14,78 @@ const CONFIG_FILE_NAMES = new Set([
|
|
|
14
14
|
]);
|
|
15
15
|
const MODULE_SOURCE_RE = /\.(ts|tsx|js|jsx)$/;
|
|
16
16
|
const PAGE_SOURCE_RE = /\.(ts|tsx|js|jsx|md|mdx)$/;
|
|
17
|
-
function
|
|
18
|
-
const report = runVerification(root);
|
|
17
|
+
function createCheck(status, message) {
|
|
19
18
|
return {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
mode: report.mode,
|
|
23
|
-
ok: report.ok
|
|
19
|
+
message,
|
|
20
|
+
status
|
|
24
21
|
};
|
|
25
22
|
}
|
|
26
|
-
function
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
function isWithinDirectory(filePath, directoryPath) {
|
|
24
|
+
const relativePath = relative(directoryPath, filePath);
|
|
25
|
+
return relativePath === "" || !relativePath.startsWith("..") && !relativePath.startsWith("../");
|
|
26
|
+
}
|
|
27
|
+
function normalizePath(value) {
|
|
28
|
+
return value.replace(/\\/g, "/");
|
|
29
|
+
}
|
|
30
|
+
function toModuleSpecifier(fromDir, filePath) {
|
|
31
|
+
const relativePath = relative(fromDir, filePath).replace(/\\/g, "/");
|
|
32
|
+
if (relativePath.startsWith(".")) return relativePath;
|
|
33
|
+
return `./${relativePath}`;
|
|
34
|
+
}
|
|
35
|
+
function normalizeRoutePath(path) {
|
|
36
|
+
if (!path || path === "/") return "/";
|
|
37
|
+
const collapsed = (path.startsWith("/") ? path : `/${path}`).replace(/\/{2,}/g, "/");
|
|
38
|
+
return collapsed.length > 1 && collapsed.endsWith("/") ? collapsed.slice(0, -1) : collapsed;
|
|
39
|
+
}
|
|
40
|
+
function resolveApiRoutePath(apiDir, file) {
|
|
41
|
+
let relativePath = relative(apiDir, file).replace(/\\/g, "/");
|
|
42
|
+
relativePath = relativePath.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
43
|
+
if (relativePath === "index") relativePath = "";
|
|
44
|
+
else relativePath = relativePath.replace(/\/index$/, "");
|
|
45
|
+
relativePath = relativePath.replace(/\[([^\]]+)\]/g, ":$1");
|
|
46
|
+
return normalizeRoutePath(relativePath ? `/api/${relativePath}` : "/api");
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/verification-pages.ts
|
|
50
|
+
function scanPagesDirectory(pagesDir) {
|
|
51
|
+
return listFilesRecursively(pagesDir).filter((file) => PAGE_SOURCE_RE.test(file)).map((file) => describePagesFile(pagesDir, file));
|
|
52
|
+
}
|
|
53
|
+
function describePagesFile(pagesDir, file) {
|
|
54
|
+
const routePath = relative(pagesDir, file).replace(/\\/g, "/").replace(/\.(tsx?|jsx?|mdx?)$/, "");
|
|
55
|
+
const name = basename(routePath);
|
|
56
|
+
if (hasPagesAppShell(file)) return {
|
|
57
|
+
file,
|
|
58
|
+
kind: "shell"
|
|
59
|
+
};
|
|
60
|
+
if (name.startsWith("_")) return {
|
|
61
|
+
file,
|
|
62
|
+
kind: "ignored"
|
|
63
|
+
};
|
|
64
|
+
if (routePath === "index") return {
|
|
65
|
+
file,
|
|
66
|
+
kind: "route",
|
|
67
|
+
routePath: "/"
|
|
36
68
|
};
|
|
37
|
-
if (options.changed) {
|
|
38
|
-
changedInfo = collectChangedFiles(project.root);
|
|
39
|
-
if (changedInfo.warning) checks.push(createCheck("warning", changedInfo.warning));
|
|
40
|
-
}
|
|
41
|
-
const frameworkFiles = options.changed ? filterFrameworkFiles(project, changedInfo.files, packageJsonPath) : [];
|
|
42
|
-
const scope = options.changed && !changedInfo.warning && !requiresFullVerification(project, frameworkFiles) ? "changed" : "full";
|
|
43
|
-
if (project.mode === "pages") collectPagesVerification(project, checks, {
|
|
44
|
-
changedFiles: frameworkFiles,
|
|
45
|
-
scope
|
|
46
|
-
});
|
|
47
|
-
else collectManifestVerification(project, checks, {
|
|
48
|
-
changedFiles: frameworkFiles,
|
|
49
|
-
scope
|
|
50
|
-
});
|
|
51
|
-
collectApiVerification(project, checks, {
|
|
52
|
-
changedFiles: frameworkFiles,
|
|
53
|
-
scope
|
|
54
|
-
});
|
|
55
|
-
collectPackageChecks(project, checks, packageJsonPath);
|
|
56
|
-
if (options.changed && frameworkFiles.length === 0 && !changedInfo.warning) checks.push(createCheck("ok", "No changed framework files were detected in the current project scope."));
|
|
57
69
|
return {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
ok: !checks.some((check) => check.status === "error"),
|
|
62
|
-
requestedScope,
|
|
63
|
-
scope,
|
|
64
|
-
changedFiles: changedInfo.files.map((file) => displayPath(project.root, file)),
|
|
65
|
-
frameworkFiles: frameworkFiles.map((file) => displayPath(project.root, file))
|
|
70
|
+
file,
|
|
71
|
+
kind: "route",
|
|
72
|
+
routePath: normalizeRoutePath(`/${routePath.replace(/\/index$/, "").replace(/\[\.\.\.([^\]]+)\]/g, "*").replace(/\[([^\].]+)\]/g, ":$1")}`)
|
|
66
73
|
};
|
|
67
74
|
}
|
|
75
|
+
function collectDuplicateRoutePaths(routes) {
|
|
76
|
+
const routeMap = /* @__PURE__ */ new Map();
|
|
77
|
+
for (const route of routes) {
|
|
78
|
+
const files = routeMap.get(route.routePath) ?? [];
|
|
79
|
+
files.push(route.file);
|
|
80
|
+
routeMap.set(route.routePath, files);
|
|
81
|
+
}
|
|
82
|
+
return [...routeMap.entries()].filter(([, files]) => files.length > 1).map(([path, files]) => ({
|
|
83
|
+
files,
|
|
84
|
+
path
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/verification-checks.ts
|
|
68
89
|
function collectConfigChecks(project, checks, configDisplayPath) {
|
|
69
90
|
if (!project.configFile) checks.push(createCheck("error", "Missing vite config."));
|
|
70
91
|
else checks.push(createCheck("ok", `Found ${configDisplayPath}.`));
|
|
@@ -223,6 +244,8 @@ function collectPackageChecks(project, checks, packageJsonPath) {
|
|
|
223
244
|
if (adapterPackages.length === 0) checks.push(createCheck("warning", "No built-in pracht adapter dependency was found in package.json."));
|
|
224
245
|
else checks.push(createCheck("ok", `Found adapter dependency ${adapterPackages.map((name) => JSON.stringify(name)).join(", ")}.`));
|
|
225
246
|
}
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region src/verification-scope.ts
|
|
226
249
|
function collectChangedFiles(root) {
|
|
227
250
|
try {
|
|
228
251
|
const repoRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
@@ -300,72 +323,57 @@ function requiresFullVerification(project, changedFiles) {
|
|
|
300
323
|
return false;
|
|
301
324
|
});
|
|
302
325
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const routePath = relative(pagesDir, file).replace(/\\/g, "/").replace(/\.(tsx?|jsx?|mdx?)$/, "");
|
|
308
|
-
const name = basename(routePath);
|
|
309
|
-
if (hasPagesAppShell(file)) return {
|
|
310
|
-
file,
|
|
311
|
-
kind: "shell"
|
|
312
|
-
};
|
|
313
|
-
if (name.startsWith("_")) return {
|
|
314
|
-
file,
|
|
315
|
-
kind: "ignored"
|
|
316
|
-
};
|
|
317
|
-
if (routePath === "index") return {
|
|
318
|
-
file,
|
|
319
|
-
kind: "route",
|
|
320
|
-
routePath: "/"
|
|
321
|
-
};
|
|
326
|
+
//#endregion
|
|
327
|
+
//#region src/verification.ts
|
|
328
|
+
function runDoctor(root) {
|
|
329
|
+
const report = runVerification(root);
|
|
322
330
|
return {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
331
|
+
checks: report.checks,
|
|
332
|
+
configFile: report.configFile,
|
|
333
|
+
mode: report.mode,
|
|
334
|
+
ok: report.ok
|
|
326
335
|
};
|
|
327
336
|
}
|
|
328
|
-
function
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
337
|
+
function runVerification(root, options = {}) {
|
|
338
|
+
const project = readProjectConfig(root);
|
|
339
|
+
const checks = [];
|
|
340
|
+
const packageJsonPath = resolve(project.root, "package.json");
|
|
341
|
+
const configDisplayPath = project.configFile ? displayPath(root, project.configFile) : "vite.config.*";
|
|
342
|
+
const requestedScope = options.changed ? "changed" : "full";
|
|
343
|
+
collectConfigChecks(project, checks, configDisplayPath);
|
|
344
|
+
let changedInfo = {
|
|
345
|
+
files: [],
|
|
346
|
+
warning: null
|
|
347
|
+
};
|
|
348
|
+
if (options.changed) {
|
|
349
|
+
changedInfo = collectChangedFiles(project.root);
|
|
350
|
+
if (changedInfo.warning) checks.push(createCheck("warning", changedInfo.warning));
|
|
334
351
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
if (
|
|
351
|
-
return `./${relativePath}`;
|
|
352
|
-
}
|
|
353
|
-
function normalizeRoutePath(path) {
|
|
354
|
-
if (!path || path === "/") return "/";
|
|
355
|
-
const collapsed = (path.startsWith("/") ? path : `/${path}`).replace(/\/{2,}/g, "/");
|
|
356
|
-
return collapsed.length > 1 && collapsed.endsWith("/") ? collapsed.slice(0, -1) : collapsed;
|
|
357
|
-
}
|
|
358
|
-
function isWithinDirectory(filePath, directoryPath) {
|
|
359
|
-
const relativePath = relative(directoryPath, filePath);
|
|
360
|
-
return relativePath === "" || !relativePath.startsWith("..") && !relativePath.startsWith("../");
|
|
361
|
-
}
|
|
362
|
-
function normalizePath(value) {
|
|
363
|
-
return value.replace(/\\/g, "/");
|
|
364
|
-
}
|
|
365
|
-
function createCheck(status, message) {
|
|
352
|
+
const frameworkFiles = options.changed ? filterFrameworkFiles(project, changedInfo.files, packageJsonPath) : [];
|
|
353
|
+
const scope = options.changed && !changedInfo.warning && !requiresFullVerification(project, frameworkFiles) ? "changed" : "full";
|
|
354
|
+
if (project.mode === "pages") collectPagesVerification(project, checks, {
|
|
355
|
+
changedFiles: frameworkFiles,
|
|
356
|
+
scope
|
|
357
|
+
});
|
|
358
|
+
else collectManifestVerification(project, checks, {
|
|
359
|
+
changedFiles: frameworkFiles,
|
|
360
|
+
scope
|
|
361
|
+
});
|
|
362
|
+
collectApiVerification(project, checks, {
|
|
363
|
+
changedFiles: frameworkFiles,
|
|
364
|
+
scope
|
|
365
|
+
});
|
|
366
|
+
collectPackageChecks(project, checks, packageJsonPath);
|
|
367
|
+
if (options.changed && frameworkFiles.length === 0 && !changedInfo.warning) checks.push(createCheck("ok", "No changed framework files were detected in the current project scope."));
|
|
366
368
|
return {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
+
checks,
|
|
370
|
+
configFile: project.configFile ? displayPath(root, project.configFile) : null,
|
|
371
|
+
mode: project.mode,
|
|
372
|
+
ok: !checks.some((check) => check.status === "error"),
|
|
373
|
+
requestedScope,
|
|
374
|
+
scope,
|
|
375
|
+
changedFiles: changedInfo.files.map((file) => displayPath(project.root, file)),
|
|
376
|
+
frameworkFiles: frameworkFiles.map((file) => displayPath(project.root, file))
|
|
369
377
|
};
|
|
370
378
|
}
|
|
371
379
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pracht/cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"homepage": "https://github.com/JoviDeCroock/pracht/tree/main/packages/cli",
|
|
6
6
|
"bugs": {
|
|
@@ -14,13 +14,20 @@
|
|
|
14
14
|
"bin": {
|
|
15
15
|
"pracht": "./bin/pracht.js"
|
|
16
16
|
},
|
|
17
|
+
"files": [
|
|
18
|
+
"bin",
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
"CHANGELOG.md"
|
|
23
|
+
],
|
|
17
24
|
"type": "module",
|
|
18
25
|
"publishConfig": {
|
|
19
26
|
"provenance": true
|
|
20
27
|
},
|
|
21
28
|
"dependencies": {
|
|
22
29
|
"citty": "^0.1.6",
|
|
23
|
-
"@pracht/core": "0.
|
|
30
|
+
"@pracht/core": "0.3.0"
|
|
24
31
|
},
|
|
25
32
|
"scripts": {
|
|
26
33
|
"build": "tsdown"
|
package/src/build-metadata.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
|
|
4
|
-
const MANIFEST_PATHS = ["dist/client/.vite/manifest.json", "dist/.vite/manifest.json"];
|
|
5
|
-
const PRACHT_CLIENT_MODULE_QUERY = "pracht-client";
|
|
6
|
-
|
|
7
|
-
interface ViteManifestEntry {
|
|
8
|
-
css?: string[];
|
|
9
|
-
file: string;
|
|
10
|
-
imports?: string[];
|
|
11
|
-
src?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface ClientBuildAssets {
|
|
15
|
-
clientEntryUrl: string | null;
|
|
16
|
-
cssManifest: Record<string, string[]>;
|
|
17
|
-
jsManifest: Record<string, string[]>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function readClientBuildAssets(root: string = process.cwd()): ClientBuildAssets {
|
|
21
|
-
const manifestPath = MANIFEST_PATHS.map((candidate) => resolve(root, candidate)).find((path) =>
|
|
22
|
-
existsSync(path),
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
if (!manifestPath) {
|
|
26
|
-
return {
|
|
27
|
-
clientEntryUrl: null,
|
|
28
|
-
cssManifest: {},
|
|
29
|
-
jsManifest: {},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const rawManifest = readFileSync(manifestPath, "utf-8");
|
|
34
|
-
const manifest: Record<string, ViteManifestEntry> = JSON.parse(rawManifest);
|
|
35
|
-
const clientEntry = manifest["virtual:pracht/client"];
|
|
36
|
-
|
|
37
|
-
function collectTransitiveDeps(key: string): { css: string[]; js: string[] } {
|
|
38
|
-
const css = new Set<string>();
|
|
39
|
-
const js = new Set<string>();
|
|
40
|
-
const visited = new Set<string>();
|
|
41
|
-
|
|
42
|
-
function collect(currentKey: string): void {
|
|
43
|
-
if (visited.has(currentKey)) return;
|
|
44
|
-
visited.add(currentKey);
|
|
45
|
-
|
|
46
|
-
const entry = manifest[currentKey];
|
|
47
|
-
if (!entry) return;
|
|
48
|
-
|
|
49
|
-
for (const cssFile of entry.css ?? []) {
|
|
50
|
-
css.add(cssFile);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
js.add(entry.file);
|
|
54
|
-
|
|
55
|
-
for (const importedKey of entry.imports ?? []) {
|
|
56
|
-
collect(importedKey);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
collect(key);
|
|
61
|
-
return {
|
|
62
|
-
css: [...css],
|
|
63
|
-
js: [...js],
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const cssManifest: Record<string, string[]> = {};
|
|
68
|
-
const jsManifest: Record<string, string[]> = {};
|
|
69
|
-
|
|
70
|
-
for (const [key, entry] of Object.entries(manifest)) {
|
|
71
|
-
if (!entry.src) continue;
|
|
72
|
-
|
|
73
|
-
const deps = collectTransitiveDeps(key);
|
|
74
|
-
const manifestKey = stripPrachtClientModuleQuery(entry.src);
|
|
75
|
-
if (deps.css.length > 0) {
|
|
76
|
-
cssManifest[manifestKey] = deps.css.map((file) => `/${file}`);
|
|
77
|
-
}
|
|
78
|
-
if (deps.js.length > 0) {
|
|
79
|
-
jsManifest[manifestKey] = deps.js.map((file) => `/${file}`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
clientEntryUrl: clientEntry ? `/${clientEntry.file}` : null,
|
|
85
|
-
cssManifest,
|
|
86
|
-
jsManifest,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function stripPrachtClientModuleQuery(id: string): string {
|
|
91
|
-
const queryStart = id.indexOf("?");
|
|
92
|
-
if (queryStart === -1) return id;
|
|
93
|
-
|
|
94
|
-
const path = id.slice(0, queryStart);
|
|
95
|
-
const query = id
|
|
96
|
-
.slice(queryStart + 1)
|
|
97
|
-
.split("&")
|
|
98
|
-
.filter((part) => part !== PRACHT_CLIENT_MODULE_QUERY);
|
|
99
|
-
|
|
100
|
-
return query.length > 0 ? `${path}?${query.join("&")}` : path;
|
|
101
|
-
}
|
package/src/build-shared.ts
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { cpSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
import { VERSION } from "./constants.js";
|
|
5
|
-
|
|
6
|
-
const ROUTE_STATE_REQUEST_HEADER = "x-pracht-route-state-request";
|
|
7
|
-
|
|
8
|
-
interface VercelBuildOutputOptions {
|
|
9
|
-
functionName?: string;
|
|
10
|
-
headersManifest?: Record<string, Record<string, string>>;
|
|
11
|
-
isgRoutes: string[];
|
|
12
|
-
regions?: string[];
|
|
13
|
-
root: string;
|
|
14
|
-
staticRoutes: string[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function writeVercelBuildOutput({
|
|
18
|
-
functionName,
|
|
19
|
-
headersManifest = {},
|
|
20
|
-
isgRoutes,
|
|
21
|
-
regions,
|
|
22
|
-
root,
|
|
23
|
-
staticRoutes,
|
|
24
|
-
}: VercelBuildOutputOptions): string {
|
|
25
|
-
const outputDir = join(root, ".vercel/output");
|
|
26
|
-
const staticDir = join(outputDir, "static");
|
|
27
|
-
const functionDir = join(outputDir, "functions", `${functionName || "render"}.func`);
|
|
28
|
-
|
|
29
|
-
rmSync(outputDir, { force: true, recursive: true });
|
|
30
|
-
mkdirSync(outputDir, { recursive: true });
|
|
31
|
-
cpSync(join(root, "dist/client"), staticDir, { recursive: true });
|
|
32
|
-
cpSync(join(root, "dist/server"), functionDir, { recursive: true });
|
|
33
|
-
|
|
34
|
-
writeFileSync(
|
|
35
|
-
join(outputDir, "config.json"),
|
|
36
|
-
`${JSON.stringify(
|
|
37
|
-
createVercelOutputConfig({ functionName, headersManifest, staticRoutes, isgRoutes }),
|
|
38
|
-
null,
|
|
39
|
-
2,
|
|
40
|
-
)}\n`,
|
|
41
|
-
"utf-8",
|
|
42
|
-
);
|
|
43
|
-
writeFileSync(
|
|
44
|
-
join(functionDir, ".vc-config.json"),
|
|
45
|
-
`${JSON.stringify(createVercelFunctionConfig({ regions }), null, 2)}\n`,
|
|
46
|
-
"utf-8",
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
return ".vercel/output";
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function createVercelOutputConfig({
|
|
53
|
-
functionName,
|
|
54
|
-
headersManifest,
|
|
55
|
-
staticRoutes,
|
|
56
|
-
isgRoutes,
|
|
57
|
-
}: {
|
|
58
|
-
functionName?: string;
|
|
59
|
-
headersManifest: Record<string, Record<string, string>>;
|
|
60
|
-
isgRoutes: string[];
|
|
61
|
-
staticRoutes: string[];
|
|
62
|
-
}): Record<string, unknown> {
|
|
63
|
-
const target = `/${functionName || "render"}`;
|
|
64
|
-
const routes: Record<string, unknown>[] = [
|
|
65
|
-
{
|
|
66
|
-
dest: target,
|
|
67
|
-
has: [{ type: "header", key: ROUTE_STATE_REQUEST_HEADER, value: "1" }],
|
|
68
|
-
src: "/(.*)",
|
|
69
|
-
},
|
|
70
|
-
];
|
|
71
|
-
|
|
72
|
-
for (const route of sortStaticRoutes(staticRoutes)) {
|
|
73
|
-
routes.push({
|
|
74
|
-
dest: routeToStaticHtmlPath(route),
|
|
75
|
-
src: routeToRouteExpression(route),
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
for (const route of isgRoutes) {
|
|
80
|
-
routes.push({
|
|
81
|
-
dest: target,
|
|
82
|
-
src: routeToRouteExpression(route),
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
routes.push({ handle: "filesystem" });
|
|
87
|
-
routes.push({ dest: target, src: "/(.*)" });
|
|
88
|
-
|
|
89
|
-
const headers: Record<string, unknown>[] = [
|
|
90
|
-
{
|
|
91
|
-
headers: [
|
|
92
|
-
{
|
|
93
|
-
key: "permissions-policy",
|
|
94
|
-
value:
|
|
95
|
-
"accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()",
|
|
96
|
-
},
|
|
97
|
-
{ key: "referrer-policy", value: "strict-origin-when-cross-origin" },
|
|
98
|
-
{ key: "x-content-type-options", value: "nosniff" },
|
|
99
|
-
{ key: "x-frame-options", value: "SAMEORIGIN" },
|
|
100
|
-
],
|
|
101
|
-
source: "/(.*)",
|
|
102
|
-
},
|
|
103
|
-
];
|
|
104
|
-
|
|
105
|
-
for (const route of sortStaticRoutes(staticRoutes)) {
|
|
106
|
-
const routeHeaders = headersManifest[route];
|
|
107
|
-
if (!routeHeaders) continue;
|
|
108
|
-
headers.push({
|
|
109
|
-
headers: Object.entries(routeHeaders).map(([key, value]) => ({ key, value })),
|
|
110
|
-
source: routeToHeaderSource(route),
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
headers,
|
|
116
|
-
framework: {
|
|
117
|
-
version: VERSION,
|
|
118
|
-
},
|
|
119
|
-
routes,
|
|
120
|
-
version: 3,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function createVercelFunctionConfig({ regions }: { regions?: string[] }): Record<string, unknown> {
|
|
125
|
-
const config: Record<string, unknown> = {
|
|
126
|
-
entrypoint: "server.js",
|
|
127
|
-
runtime: "edge",
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
if (regions) {
|
|
131
|
-
config.regions = regions;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
return config;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function sortStaticRoutes(routes: string[]): string[] {
|
|
138
|
-
return [...new Set(routes)].sort((left, right) => right.length - left.length);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function routeToRouteExpression(route: string): string {
|
|
142
|
-
if (route === "/") {
|
|
143
|
-
return "^/$";
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return `^${escapeRegex(route)}/?$`;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function routeToStaticHtmlPath(route: string): string {
|
|
150
|
-
if (route === "/") {
|
|
151
|
-
return "/index.html";
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return `${route}/index.html`;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function routeToHeaderSource(route: string): string {
|
|
158
|
-
return route === "/" ? "/" : route;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function escapeRegex(value: string): string {
|
|
162
|
-
return value.replace(/[|\\{}()[\]^$+*?.-]/g, "\\$&");
|
|
163
|
-
}
|