@pracht/cli 1.1.5 → 1.2.1

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.
@@ -9,6 +9,7 @@ import {
9
9
  listFilesRecursively,
10
10
  readProjectConfig,
11
11
  resolveProjectPath,
12
+ type ProjectConfig,
12
13
  } from "./project.js";
13
14
 
14
15
  const CONFIG_FILE_NAMES = new Set([
@@ -23,7 +24,30 @@ const CONFIG_FILE_NAMES = new Set([
23
24
  const MODULE_SOURCE_RE = /\.(ts|tsx|js|jsx)$/;
24
25
  const PAGE_SOURCE_RE = /\.(ts|tsx|js|jsx|md|mdx)$/;
25
26
 
26
- export function runDoctor(root) {
27
+ export interface Check {
28
+ message: string;
29
+ status: "ok" | "warning" | "error";
30
+ }
31
+
32
+ export interface DoctorReport {
33
+ checks: Check[];
34
+ configFile: string | null;
35
+ mode: "manifest" | "pages";
36
+ ok: boolean;
37
+ }
38
+
39
+ export interface VerificationReport {
40
+ changedFiles: string[];
41
+ checks: Check[];
42
+ configFile: string | null;
43
+ frameworkFiles: string[];
44
+ mode: "manifest" | "pages";
45
+ ok: boolean;
46
+ requestedScope: string;
47
+ scope: string;
48
+ }
49
+
50
+ export function runDoctor(root: string): DoctorReport {
27
51
  const report = runVerification(root);
28
52
 
29
53
  return {
@@ -34,9 +58,12 @@ export function runDoctor(root) {
34
58
  };
35
59
  }
36
60
 
37
- export function runVerification(root, options = {}) {
61
+ export function runVerification(
62
+ root: string,
63
+ options: { changed?: boolean } = {},
64
+ ): VerificationReport {
38
65
  const project = readProjectConfig(root);
39
- const checks = [];
66
+ const checks: Check[] = [];
40
67
  const packageJsonPath = resolve(project.root, "package.json");
41
68
  const configDisplayPath = project.configFile
42
69
  ? displayPath(root, project.configFile)
@@ -45,7 +72,7 @@ export function runVerification(root, options = {}) {
45
72
 
46
73
  collectConfigChecks(project, checks, configDisplayPath);
47
74
 
48
- let changedInfo = {
75
+ let changedInfo: { files: string[]; warning: string | null } = {
49
76
  files: [],
50
77
  warning: null,
51
78
  };
@@ -92,7 +119,11 @@ export function runVerification(root, options = {}) {
92
119
  };
93
120
  }
94
121
 
95
- function collectConfigChecks(project, checks, configDisplayPath) {
122
+ function collectConfigChecks(
123
+ project: ProjectConfig,
124
+ checks: Check[],
125
+ configDisplayPath: string,
126
+ ): void {
96
127
  if (!project.configFile) {
97
128
  checks.push(createCheck("error", "Missing vite config."));
98
129
  } else {
@@ -106,7 +137,11 @@ function collectConfigChecks(project, checks, configDisplayPath) {
106
137
  }
107
138
  }
108
139
 
109
- function collectManifestVerification(project, checks, { changedFiles, scope }) {
140
+ function collectManifestVerification(
141
+ project: ProjectConfig,
142
+ checks: Check[],
143
+ { changedFiles, scope }: { changedFiles: string[]; scope: string },
144
+ ): void {
110
145
  const manifestPath = resolveProjectPath(project.root, project.appFile);
111
146
  if (!existsSync(manifestPath)) {
112
147
  checks.push(createCheck("error", `App manifest is missing at ${project.appFile}.`));
@@ -187,12 +222,12 @@ function collectManifestVerification(project, checks, { changedFiles, scope }) {
187
222
  }
188
223
 
189
224
  function collectChangedManifestModuleChecks(
190
- project,
191
- checks,
192
- manifestPath,
193
- relativeModules,
194
- changedFiles,
195
- ) {
225
+ project: ProjectConfig,
226
+ checks: Check[],
227
+ manifestPath: string,
228
+ relativeModules: string[],
229
+ changedFiles: string[],
230
+ ): void {
196
231
  const manifestDir = dirname(manifestPath);
197
232
  const referencedModules = new Set(relativeModules.map(normalizePath));
198
233
  const moduleDirectories = [
@@ -241,7 +276,11 @@ function collectChangedManifestModuleChecks(
241
276
  }
242
277
  }
243
278
 
244
- function collectPagesVerification(project, checks, { changedFiles, scope }) {
279
+ function collectPagesVerification(
280
+ project: ProjectConfig,
281
+ checks: Check[],
282
+ { changedFiles, scope }: { changedFiles: string[]; scope: string },
283
+ ): void {
245
284
  const pagesDir = resolveProjectPath(project.root, project.pagesDir);
246
285
  if (!existsSync(pagesDir)) {
247
286
  checks.push(createCheck("error", `Pages directory is missing at ${project.pagesDir}.`));
@@ -250,7 +289,7 @@ function collectPagesVerification(project, checks, { changedFiles, scope }) {
250
289
 
251
290
  const pages = scanPagesDirectory(pagesDir);
252
291
  const routes = pages.filter((page) => page.kind === "route");
253
- const duplicates = collectDuplicateRoutePaths(routes).map((entry) => ({
292
+ const duplicates = collectDuplicateRoutePaths(routes as PagesRoute[]).map((entry) => ({
254
293
  ...entry,
255
294
  files: entry.files.map((file) => displayPath(project.root, file)),
256
295
  }));
@@ -298,7 +337,12 @@ function collectPagesVerification(project, checks, { changedFiles, scope }) {
298
337
  }
299
338
  }
300
339
 
301
- function collectChangedPagesChecks(project, checks, pagesDir, changedFiles) {
340
+ function collectChangedPagesChecks(
341
+ project: ProjectConfig,
342
+ checks: Check[],
343
+ pagesDir: string,
344
+ changedFiles: string[],
345
+ ): void {
302
346
  for (const file of changedFiles) {
303
347
  if (!isWithinDirectory(file, pagesDir)) continue;
304
348
  if (!PAGE_SOURCE_RE.test(file)) continue;
@@ -344,7 +388,11 @@ function collectChangedPagesChecks(project, checks, pagesDir, changedFiles) {
344
388
  }
345
389
  }
346
390
 
347
- function collectApiVerification(project, checks, { changedFiles, scope }) {
391
+ function collectApiVerification(
392
+ project: ProjectConfig,
393
+ checks: Check[],
394
+ { changedFiles, scope }: { changedFiles: string[]; scope: string },
395
+ ): void {
348
396
  const apiDir = resolveProjectPath(project.root, project.apiDir);
349
397
  const changedApiFiles = changedFiles.filter((file) => isWithinDirectory(file, apiDir));
350
398
  if (scope === "changed" && changedApiFiles.length === 0) {
@@ -364,7 +412,7 @@ function collectApiVerification(project, checks, { changedFiles, scope }) {
364
412
  }
365
413
 
366
414
  const apiFiles = listFilesRecursively(apiDir).filter((file) => MODULE_SOURCE_RE.test(file));
367
- const routeMap = new Map();
415
+ const routeMap = new Map<string, string[]>();
368
416
 
369
417
  for (const file of apiFiles) {
370
418
  const routePath = resolveApiRoutePath(apiDir, file);
@@ -422,14 +470,18 @@ function collectApiVerification(project, checks, { changedFiles, scope }) {
422
470
  }
423
471
  }
424
472
 
425
- function collectPackageChecks(project, checks, packageJsonPath) {
473
+ function collectPackageChecks(
474
+ project: ProjectConfig,
475
+ checks: Check[],
476
+ packageJsonPath: string,
477
+ ): void {
426
478
  if (!existsSync(packageJsonPath)) {
427
479
  checks.push(createCheck("warning", "No package.json found in the current app root."));
428
480
  return;
429
481
  }
430
482
 
431
483
  const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
432
- const deps = {
484
+ const deps: Record<string, string> = {
433
485
  ...packageJson.dependencies,
434
486
  ...packageJson.devDependencies,
435
487
  };
@@ -455,7 +507,7 @@ function collectPackageChecks(project, checks, packageJsonPath) {
455
507
  }
456
508
  }
457
509
 
458
- function collectChangedFiles(root) {
510
+ function collectChangedFiles(root: string): { files: string[]; warning: string | null } {
459
511
  try {
460
512
  const repoRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], {
461
513
  cwd: root,
@@ -469,7 +521,7 @@ function collectChangedFiles(root) {
469
521
  cwd: repoRoot,
470
522
  encoding: "utf-8",
471
523
  });
472
- const files = new Set();
524
+ const files = new Set<string>();
473
525
 
474
526
  for (const line of output.split(/\r?\n/).filter(Boolean)) {
475
527
  const record = line.slice(3);
@@ -496,7 +548,12 @@ function collectChangedFiles(root) {
496
548
  }
497
549
  }
498
550
 
499
- function addChangedFile(files, repoRoot, prefix, repoRelativePath) {
551
+ function addChangedFile(
552
+ files: Set<string>,
553
+ repoRoot: string,
554
+ prefix: string,
555
+ repoRelativePath: string,
556
+ ): void {
500
557
  if (prefix && !repoRelativePath.startsWith(prefix)) {
501
558
  return;
502
559
  }
@@ -509,7 +566,11 @@ function addChangedFile(files, repoRoot, prefix, repoRelativePath) {
509
566
  files.add(resolve(repoRoot, projectRelativePath));
510
567
  }
511
568
 
512
- function filterFrameworkFiles(project, files, packageJsonPath) {
569
+ function filterFrameworkFiles(
570
+ project: ProjectConfig,
571
+ files: string[],
572
+ packageJsonPath: string,
573
+ ): string[] {
513
574
  const appFile = resolveProjectPath(project.root, project.appFile);
514
575
  const routesDir = resolveProjectPath(project.root, project.routesDir);
515
576
  const shellsDir = resolveProjectPath(project.root, project.shellsDir);
@@ -532,7 +593,7 @@ function filterFrameworkFiles(project, files, packageJsonPath) {
532
593
  });
533
594
  }
534
595
 
535
- function requiresFullVerification(project, changedFiles) {
596
+ function requiresFullVerification(project: ProjectConfig, changedFiles: string[]): boolean {
536
597
  const packageJsonPath = resolve(project.root, "package.json");
537
598
  const appFile = resolveProjectPath(project.root, project.appFile);
538
599
 
@@ -545,13 +606,21 @@ function requiresFullVerification(project, changedFiles) {
545
606
  });
546
607
  }
547
608
 
548
- function scanPagesDirectory(pagesDir) {
609
+ type PagesFile = { file: string; kind: "shell" } | { file: string; kind: "ignored" } | PagesRoute;
610
+
611
+ interface PagesRoute {
612
+ file: string;
613
+ kind: "route";
614
+ routePath: string;
615
+ }
616
+
617
+ function scanPagesDirectory(pagesDir: string): PagesFile[] {
549
618
  return listFilesRecursively(pagesDir)
550
619
  .filter((file) => PAGE_SOURCE_RE.test(file))
551
620
  .map((file) => describePagesFile(pagesDir, file));
552
621
  }
553
622
 
554
- function describePagesFile(pagesDir, file) {
623
+ function describePagesFile(pagesDir: string, file: string): PagesFile {
555
624
  const relativePath = relative(pagesDir, file).replace(/\\/g, "/");
556
625
  const routePath = relativePath.replace(/\.(tsx?|jsx?|mdx?)$/, "");
557
626
  const name = basename(routePath);
@@ -580,8 +649,8 @@ function describePagesFile(pagesDir, file) {
580
649
  };
581
650
  }
582
651
 
583
- function collectDuplicateRoutePaths(routes) {
584
- const routeMap = new Map();
652
+ function collectDuplicateRoutePaths(routes: PagesRoute[]): { files: string[]; path: string }[] {
653
+ const routeMap = new Map<string, string[]>();
585
654
 
586
655
  for (const route of routes) {
587
656
  const files = routeMap.get(route.routePath) ?? [];
@@ -594,7 +663,7 @@ function collectDuplicateRoutePaths(routes) {
594
663
  .map(([path, files]) => ({ files, path }));
595
664
  }
596
665
 
597
- function resolveApiRoutePath(apiDir, file) {
666
+ function resolveApiRoutePath(apiDir: string, file: string): string {
598
667
  let relativePath = relative(apiDir, file).replace(/\\/g, "/");
599
668
  relativePath = relativePath.replace(/\.(ts|tsx|js|jsx)$/, "");
600
669
 
@@ -609,7 +678,7 @@ function resolveApiRoutePath(apiDir, file) {
609
678
  return normalizeRoutePath(relativePath ? `/api/${relativePath}` : "/api");
610
679
  }
611
680
 
612
- function toModuleSpecifier(fromDir, filePath) {
681
+ function toModuleSpecifier(fromDir: string, filePath: string): string {
613
682
  const relativePath = relative(fromDir, filePath).replace(/\\/g, "/");
614
683
  if (relativePath.startsWith(".")) {
615
684
  return relativePath;
@@ -617,7 +686,7 @@ function toModuleSpecifier(fromDir, filePath) {
617
686
  return `./${relativePath}`;
618
687
  }
619
688
 
620
- function normalizeRoutePath(path) {
689
+ function normalizeRoutePath(path: string): string {
621
690
  if (!path || path === "/") {
622
691
  return "/";
623
692
  }
@@ -627,15 +696,15 @@ function normalizeRoutePath(path) {
627
696
  return collapsed.length > 1 && collapsed.endsWith("/") ? collapsed.slice(0, -1) : collapsed;
628
697
  }
629
698
 
630
- function isWithinDirectory(filePath, directoryPath) {
699
+ function isWithinDirectory(filePath: string, directoryPath: string): boolean {
631
700
  const relativePath = relative(directoryPath, filePath);
632
701
  return relativePath === "" || (!relativePath.startsWith("..") && !relativePath.startsWith("../"));
633
702
  }
634
703
 
635
- function normalizePath(value) {
704
+ function normalizePath(value: string): string {
636
705
  return value.replace(/\\/g, "/");
637
706
  }
638
707
 
639
- function createCheck(status, message) {
708
+ function createCheck(status: Check["status"], message: string): Check {
640
709
  return { message, status };
641
710
  }
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from "tsdown";
2
+
3
+ export default defineConfig({
4
+ clean: true,
5
+ entry: ["src/index.ts"],
6
+ format: "esm",
7
+ external: [/^@pracht\//, /^node:/, "vite", "citty"],
8
+ });
@@ -1,73 +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
-
6
- export function readClientBuildAssets(root = process.cwd()) {
7
- const manifestPath = MANIFEST_PATHS.map((candidate) => resolve(root, candidate)).find((path) =>
8
- existsSync(path),
9
- );
10
-
11
- if (!manifestPath) {
12
- return {
13
- clientEntryUrl: null,
14
- cssManifest: {},
15
- jsManifest: {},
16
- };
17
- }
18
-
19
- const rawManifest = readFileSync(manifestPath, "utf-8");
20
- const manifest = JSON.parse(rawManifest);
21
- const clientEntry = manifest["virtual:pracht/client"];
22
-
23
- function collectTransitiveDeps(key) {
24
- const css = new Set();
25
- const js = new Set();
26
- const visited = new Set();
27
-
28
- function collect(currentKey) {
29
- if (visited.has(currentKey)) return;
30
- visited.add(currentKey);
31
-
32
- const entry = manifest[currentKey];
33
- if (!entry) return;
34
-
35
- for (const cssFile of entry.css ?? []) {
36
- css.add(cssFile);
37
- }
38
-
39
- js.add(entry.file);
40
-
41
- for (const importedKey of entry.imports ?? []) {
42
- collect(importedKey);
43
- }
44
- }
45
-
46
- collect(key);
47
- return {
48
- css: [...css],
49
- js: [...js],
50
- };
51
- }
52
-
53
- const cssManifest = {};
54
- const jsManifest = {};
55
-
56
- for (const [key, entry] of Object.entries(manifest)) {
57
- if (!entry.src) continue;
58
-
59
- const deps = collectTransitiveDeps(key);
60
- if (deps.css.length > 0) {
61
- cssManifest[key] = deps.css.map((file) => `/${file}`);
62
- }
63
- if (deps.js.length > 0) {
64
- jsManifest[key] = deps.js.map((file) => `/${file}`);
65
- }
66
- }
67
-
68
- return {
69
- clientEntryUrl: clientEntry ? `/${clientEntry.file}` : null,
70
- cssManifest,
71
- jsManifest,
72
- };
73
- }
package/lib/cli.js DELETED
@@ -1,164 +0,0 @@
1
- import { HTTP_METHODS, VERSION } from "./constants.js";
2
-
3
- export function printHelp() {
4
- console.log(`pracht ${VERSION}
5
-
6
- Usage:
7
- pracht dev [port] Start development server with HMR
8
- pracht build Production build (client + server)
9
- pracht generate <kind> [flags] Scaffold framework files
10
- pracht verify [--changed] [--json] Fast framework-aware verification
11
- pracht inspect [target] [--json] Inspect resolved app graph
12
- pracht doctor [--json] Validate app wiring
13
-
14
- Generate kinds:
15
- route --path /dashboard [--render ssr|spa|ssg|isg] [--shell app] [--middleware auth] [--loader]
16
- shell --name app
17
- middleware --name auth
18
- api --path /health [--methods GET,POST]
19
- `);
20
- }
21
-
22
- export function printInspectHelp() {
23
- console.log(`Usage:
24
- pracht inspect [routes|api|build] [--json]
25
- pracht inspect --json
26
- `);
27
- }
28
-
29
- export function printGenerateHelp() {
30
- console.log(`Usage:
31
- pracht generate route --path /dashboard [--render ssr|spa|ssg|isg] [--shell app] [--middleware auth] [--loader] [--json]
32
- pracht generate shell --name app [--json]
33
- pracht generate middleware --name auth [--json]
34
- pracht generate api --path /health [--methods GET,POST] [--json]
35
- `);
36
- }
37
-
38
- export function parseFlags(args) {
39
- const options = { _: [] };
40
-
41
- for (let index = 0; index < args.length; index += 1) {
42
- const token = args[index];
43
- if (!token.startsWith("--")) {
44
- options._.push(token);
45
- continue;
46
- }
47
-
48
- if (token.startsWith("--no-")) {
49
- options[token.slice(5)] = false;
50
- continue;
51
- }
52
-
53
- const equalsIndex = token.indexOf("=");
54
- if (equalsIndex !== -1) {
55
- const key = token.slice(2, equalsIndex);
56
- const value = token.slice(equalsIndex + 1);
57
- assignOption(options, key, value);
58
- continue;
59
- }
60
-
61
- const key = token.slice(2);
62
- const next = args[index + 1];
63
- if (next && !next.startsWith("--")) {
64
- assignOption(options, key, next);
65
- index += 1;
66
- continue;
67
- }
68
-
69
- assignOption(options, key, true);
70
- }
71
-
72
- return options;
73
- }
74
-
75
- export function requireStringOption(options, key) {
76
- const value = requireOptionalString(options, key);
77
- if (!value) {
78
- throw new Error(`Missing required flag --${key}.`);
79
- }
80
- return value;
81
- }
82
-
83
- export function requireOptionalString(options, key) {
84
- const value = options[key];
85
- if (Array.isArray(value)) {
86
- return String(value[value.length - 1]);
87
- }
88
- if (typeof value === "string") {
89
- return value;
90
- }
91
- return null;
92
- }
93
-
94
- export function requireEnumOption(options, key, allowed, fallback) {
95
- const value = requireOptionalString(options, key) ?? fallback;
96
- if (!allowed.includes(value)) {
97
- throw new Error(`Invalid value for --${key}. Expected one of ${allowed.join(", ")}.`);
98
- }
99
- return value;
100
- }
101
-
102
- export function requirePositiveIntegerOption(options, key, fallback) {
103
- const raw = requireOptionalString(options, key);
104
- const value = raw == null ? fallback : Number.parseInt(raw, 10);
105
- if (!Number.isInteger(value) || value <= 0) {
106
- throw new Error(`--${key} must be a positive integer.`);
107
- }
108
- return value;
109
- }
110
-
111
- export function parseCommaList(value) {
112
- if (!value) return [];
113
- const values = Array.isArray(value) ? value : [value];
114
- return values
115
- .flatMap((entry) => String(entry).split(","))
116
- .map((entry) => entry.trim())
117
- .filter(Boolean);
118
- }
119
-
120
- export function parseApiMethods(value) {
121
- const methods = parseCommaList(value);
122
- const normalized = methods.length === 0 ? ["GET"] : methods.map((entry) => entry.toUpperCase());
123
-
124
- for (const method of normalized) {
125
- if (!HTTP_METHODS.has(method)) {
126
- throw new Error(`Unsupported HTTP method "${method}".`);
127
- }
128
- }
129
-
130
- return [...new Set(normalized)];
131
- }
132
-
133
- export function quote(value) {
134
- return JSON.stringify(value);
135
- }
136
-
137
- export function ensureTrailingNewline(value) {
138
- return value.endsWith("\n") ? value : `${value}\n`;
139
- }
140
-
141
- export function handleCliError(error, { json }) {
142
- const message = error instanceof Error ? error.message : String(error);
143
- if (json) {
144
- console.error(JSON.stringify({ ok: false, error: message }, null, 2));
145
- } else {
146
- console.error(message);
147
- if (error instanceof Error && error.stack && process.env.DEBUG) {
148
- console.error(error.stack);
149
- }
150
- }
151
- process.exit(1);
152
- }
153
-
154
- function assignOption(options, key, value) {
155
- if (!(key in options)) {
156
- options[key] = value;
157
- return;
158
- }
159
-
160
- if (!Array.isArray(options[key])) {
161
- options[key] = [options[key]];
162
- }
163
- options[key].push(value);
164
- }
@@ -1,120 +0,0 @@
1
- import { cpSync, existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
2
- import { dirname, join, resolve } from "node:path";
3
-
4
- import { build as viteBuild } from "vite";
5
-
6
- import { readClientBuildAssets } from "../build-metadata.js";
7
- import { writeVercelBuildOutput } from "../build-shared.js";
8
-
9
- export async function buildCommand() {
10
- const root = process.cwd();
11
-
12
- console.log("\n Building client...\n");
13
- await viteBuild({
14
- root,
15
- build: {
16
- outDir: "dist",
17
- manifest: true,
18
- rollupOptions: {
19
- input: "virtual:pracht/client",
20
- },
21
- },
22
- });
23
-
24
- console.log("\n Building server...\n");
25
- await viteBuild({
26
- root,
27
- build: {
28
- outDir: "dist/server",
29
- ssr: "virtual:pracht/server",
30
- },
31
- });
32
-
33
- const serverEntry = resolve(root, "dist/server/server.js");
34
- let clientDir;
35
- if (existsSync(resolve(root, "dist/client/.vite/manifest.json"))) {
36
- clientDir = resolve(root, "dist/client");
37
- } else {
38
- clientDir = resolve(root, "dist/client");
39
- const distRoot = resolve(root, "dist");
40
- mkdirSync(clientDir, { recursive: true });
41
- for (const entry of readdirSync(distRoot)) {
42
- if (entry === "server" || entry === "client") continue;
43
- const sourcePath = join(distRoot, entry);
44
- const destinationPath = join(clientDir, entry);
45
- cpSync(sourcePath, destinationPath, { recursive: true });
46
- rmSync(sourcePath, { force: true, recursive: true });
47
- }
48
- }
49
-
50
- if (existsSync(serverEntry)) {
51
- const serverMod = await import(serverEntry);
52
- const { prerenderApp } = serverMod;
53
- const { clientEntryUrl, cssManifest, jsManifest } = readClientBuildAssets(root);
54
-
55
- const { pages, isgManifest } = await prerenderApp({
56
- app: serverMod.resolvedApp,
57
- clientEntryUrl: clientEntryUrl ?? undefined,
58
- cssManifest,
59
- jsManifest,
60
- registry: serverMod.registry,
61
- withISGManifest: true,
62
- });
63
- const headersManifest = Object.fromEntries(
64
- pages.map((page) => [page.path, page.headers ?? {}]),
65
- );
66
-
67
- if (pages.length > 0) {
68
- console.log(`\n Prerendering ${pages.length} SSG/ISG route(s)...\n`);
69
- for (const page of pages) {
70
- const filePath =
71
- page.path === "/"
72
- ? join(clientDir, "index.html")
73
- : join(clientDir, page.path, "index.html");
74
-
75
- mkdirSync(dirname(filePath), { recursive: true });
76
- writeFileSync(filePath, page.html, "utf-8");
77
- console.log(` ${page.path} → ${filePath.replace(root + "/", "")}`);
78
- }
79
- }
80
-
81
- if (Object.keys(headersManifest).length > 0) {
82
- const headersManifestJson = `${JSON.stringify(headersManifest, null, 2)}\n`;
83
- writeFileSync(
84
- resolve(root, "dist/server/headers-manifest.json"),
85
- headersManifestJson,
86
- "utf-8",
87
- );
88
- mkdirSync(resolve(clientDir, "_pracht"), { recursive: true });
89
- writeFileSync(resolve(clientDir, "_pracht/headers.json"), headersManifestJson, "utf-8");
90
- }
91
-
92
- if (Object.keys(isgManifest).length > 0) {
93
- const isgManifestPath = resolve(root, "dist/server/isg-manifest.json");
94
- writeFileSync(isgManifestPath, JSON.stringify(isgManifest, null, 2), "utf-8");
95
- console.log(
96
- `\n ISG manifest → dist/server/isg-manifest.json (${Object.keys(isgManifest).length} route(s))\n`,
97
- );
98
- }
99
-
100
- if (serverMod.buildTarget === "cloudflare") {
101
- console.log("\n Cloudflare worker → dist/server/server.js\n");
102
- console.log(" Deploy with: wrangler deploy\n");
103
- }
104
-
105
- if (serverMod.buildTarget === "vercel") {
106
- const outputPath = writeVercelBuildOutput({
107
- functionName: serverMod.vercelFunctionName,
108
- isgRoutes: Object.keys(isgManifest),
109
- headersManifest,
110
- regions: serverMod.vercelRegions,
111
- root,
112
- staticRoutes: pages.map((page) => page.path).filter((path) => !(path in isgManifest)),
113
- });
114
-
115
- console.log(`\n Vercel build output → ${outputPath}\n`);
116
- }
117
- }
118
-
119
- console.log("\n Build complete.\n");
120
- }
@@ -1,16 +0,0 @@
1
- import { createServer } from "vite";
2
-
3
- import { parseFlags } from "../cli.js";
4
-
5
- export async function devCommand(args) {
6
- const options = parseFlags(args);
7
- const port = parseInt(process.env.PORT || options._[0] || "3000", 10);
8
-
9
- const server = await createServer({
10
- root: process.cwd(),
11
- server: { port },
12
- });
13
-
14
- await server.listen();
15
- server.printUrls();
16
- }