@pracht/cli 1.1.5 → 1.2.0
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 +11 -0
- package/bin/pracht.js +1 -40
- package/dist/build-BzQAQjuy.mjs +212 -0
- package/dist/build-metadata-IABPFFKk.mjs +49 -0
- package/dist/dev-BCFe3g38.mjs +25 -0
- package/dist/doctor-BcoqyBk-.mjs +25 -0
- package/dist/generate-BXkePCIx.mjs +404 -0
- package/dist/index.mjs +41 -0
- package/dist/inspect-DIjjCfs3.mjs +128 -0
- package/dist/manifest-DGq1n5LT.mjs +99 -0
- package/dist/project-DydjqBoS.mjs +155 -0
- package/dist/verification-Dfl3X4Zo.mjs +372 -0
- package/dist/verify-CObGxWtW.mjs +31 -0
- package/package.json +6 -2
- package/{lib/build-metadata.js → src/build-metadata.ts} +22 -9
- package/{lib/build-shared.js → src/build-shared.ts} +39 -13
- package/src/commands/build.ts +132 -0
- package/src/commands/dev.ts +27 -0
- package/src/commands/doctor.ts +33 -0
- package/{lib/commands/generate.js → src/commands/generate.ts} +178 -72
- package/{lib/commands/inspect.js → src/commands/inspect.ts} +81 -30
- package/src/commands/verify.ts +37 -0
- package/{lib/constants.js → src/constants.ts} +12 -2
- package/src/index.ts +21 -0
- package/{lib/manifest.js → src/manifest.ts} +32 -13
- package/{lib/project.js → src/project.ts} +48 -20
- package/src/utils.ts +72 -0
- package/{lib/verification.js → src/verification.ts} +103 -34
- package/tsdown.config.ts +8 -0
- package/lib/cli.js +0 -164
- package/lib/commands/build.js +0 -120
- package/lib/commands/dev.js +0 -16
- package/lib/commands/doctor.js +0 -21
- package/lib/commands/verify.js +0 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @pracht/cli
|
|
2
2
|
|
|
3
|
+
## 1.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#96](https://github.com/JoviDeCroock/pracht/pull/96) [`755dc1f`](https://github.com/JoviDeCroock/pracht/commit/755dc1fd80e0c0457f29e85abf59b2f2ff3f1bdc) Thanks [@JoviDeCroock](https://github.com/JoviDeCroock)! - Convert CLI codebase from JavaScript to TypeScript and replace custom flag parsing with citty
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`f7b5366`](https://github.com/JoviDeCroock/pracht/commit/f7b5366cead40f2237d55e6027dc4bfb7f8b324f), [`d284596`](https://github.com/JoviDeCroock/pracht/commit/d284596fe00c3c74d56e7dc040ea1e8c9961eb99), [`2c95189`](https://github.com/JoviDeCroock/pracht/commit/2c95189209b4b09f862194078f7d2ced15f22dde)]:
|
|
12
|
+
- @pracht/core@0.2.6
|
|
13
|
+
|
|
3
14
|
## 1.1.5
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
package/bin/pracht.js
CHANGED
|
@@ -1,41 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { doctorCommand } from "../lib/commands/doctor.js";
|
|
4
|
-
import { buildCommand } from "../lib/commands/build.js";
|
|
5
|
-
import { devCommand } from "../lib/commands/dev.js";
|
|
6
|
-
import { generateCommand } from "../lib/commands/generate.js";
|
|
7
|
-
import { verifyCommand } from "../lib/commands/verify.js";
|
|
8
|
-
import { inspectCommand } from "../lib/commands/inspect.js";
|
|
9
|
-
import { handleCliError, printHelp } from "../lib/cli.js";
|
|
10
|
-
import { VERSION } from "../lib/constants.js";
|
|
11
|
-
|
|
12
|
-
const argv = process.argv.slice(2);
|
|
13
|
-
const command = argv[0];
|
|
14
|
-
const jsonOutput = argv.includes("--json");
|
|
15
|
-
|
|
16
|
-
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
17
|
-
printHelp();
|
|
18
|
-
process.exit(0);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (command === "--version" || command === "-v") {
|
|
22
|
-
console.log(VERSION);
|
|
23
|
-
process.exit(0);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const handlers = {
|
|
27
|
-
build: buildCommand,
|
|
28
|
-
dev: devCommand,
|
|
29
|
-
doctor: doctorCommand,
|
|
30
|
-
generate: generateCommand,
|
|
31
|
-
verify: verifyCommand,
|
|
32
|
-
inspect: inspectCommand,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
if (!(command in handlers)) {
|
|
36
|
-
handleCliError(new Error(`Unknown pracht command: ${command}`), { json: false });
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
handlers[command](argv.slice(1)).catch((error) => {
|
|
40
|
-
handleCliError(error, { json: jsonOutput });
|
|
41
|
-
});
|
|
2
|
+
import "../dist/index.mjs";
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { r as VERSION } from "./index.mjs";
|
|
2
|
+
import { t as readClientBuildAssets } from "./build-metadata-IABPFFKk.mjs";
|
|
3
|
+
import { defineCommand } from "citty";
|
|
4
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { dirname, join, resolve } from "node:path";
|
|
6
|
+
import { build } from "vite";
|
|
7
|
+
//#region src/build-shared.ts
|
|
8
|
+
const ROUTE_STATE_REQUEST_HEADER = "x-pracht-route-state-request";
|
|
9
|
+
function writeVercelBuildOutput({ functionName, headersManifest = {}, isgRoutes, regions, root, staticRoutes }) {
|
|
10
|
+
const outputDir = join(root, ".vercel/output");
|
|
11
|
+
const staticDir = join(outputDir, "static");
|
|
12
|
+
const functionDir = join(outputDir, "functions", `${functionName || "render"}.func`);
|
|
13
|
+
rmSync(outputDir, {
|
|
14
|
+
force: true,
|
|
15
|
+
recursive: true
|
|
16
|
+
});
|
|
17
|
+
mkdirSync(outputDir, { recursive: true });
|
|
18
|
+
cpSync(join(root, "dist/client"), staticDir, { recursive: true });
|
|
19
|
+
cpSync(join(root, "dist/server"), functionDir, { recursive: true });
|
|
20
|
+
writeFileSync(join(outputDir, "config.json"), `${JSON.stringify(createVercelOutputConfig({
|
|
21
|
+
functionName,
|
|
22
|
+
headersManifest,
|
|
23
|
+
staticRoutes,
|
|
24
|
+
isgRoutes
|
|
25
|
+
}), null, 2)}\n`, "utf-8");
|
|
26
|
+
writeFileSync(join(functionDir, ".vc-config.json"), `${JSON.stringify(createVercelFunctionConfig({ regions }), null, 2)}\n`, "utf-8");
|
|
27
|
+
return ".vercel/output";
|
|
28
|
+
}
|
|
29
|
+
function createVercelOutputConfig({ functionName, headersManifest, staticRoutes, isgRoutes }) {
|
|
30
|
+
const target = `/${functionName || "render"}`;
|
|
31
|
+
const routes = [{
|
|
32
|
+
dest: target,
|
|
33
|
+
has: [{
|
|
34
|
+
type: "header",
|
|
35
|
+
key: ROUTE_STATE_REQUEST_HEADER,
|
|
36
|
+
value: "1"
|
|
37
|
+
}],
|
|
38
|
+
src: "/(.*)"
|
|
39
|
+
}];
|
|
40
|
+
for (const route of sortStaticRoutes(staticRoutes)) routes.push({
|
|
41
|
+
dest: routeToStaticHtmlPath(route),
|
|
42
|
+
src: routeToRouteExpression(route)
|
|
43
|
+
});
|
|
44
|
+
for (const route of isgRoutes) routes.push({
|
|
45
|
+
dest: target,
|
|
46
|
+
src: routeToRouteExpression(route)
|
|
47
|
+
});
|
|
48
|
+
routes.push({ handle: "filesystem" });
|
|
49
|
+
routes.push({
|
|
50
|
+
dest: target,
|
|
51
|
+
src: "/(.*)"
|
|
52
|
+
});
|
|
53
|
+
const headers = [{
|
|
54
|
+
headers: [
|
|
55
|
+
{
|
|
56
|
+
key: "permissions-policy",
|
|
57
|
+
value: "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
key: "referrer-policy",
|
|
61
|
+
value: "strict-origin-when-cross-origin"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
key: "x-content-type-options",
|
|
65
|
+
value: "nosniff"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
key: "x-frame-options",
|
|
69
|
+
value: "SAMEORIGIN"
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
source: "/(.*)"
|
|
73
|
+
}];
|
|
74
|
+
for (const route of sortStaticRoutes(staticRoutes)) {
|
|
75
|
+
const routeHeaders = headersManifest[route];
|
|
76
|
+
if (!routeHeaders) continue;
|
|
77
|
+
headers.push({
|
|
78
|
+
headers: Object.entries(routeHeaders).map(([key, value]) => ({
|
|
79
|
+
key,
|
|
80
|
+
value
|
|
81
|
+
})),
|
|
82
|
+
source: routeToHeaderSource(route)
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
headers,
|
|
87
|
+
framework: { version: VERSION },
|
|
88
|
+
routes,
|
|
89
|
+
version: 3
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function createVercelFunctionConfig({ regions }) {
|
|
93
|
+
const config = {
|
|
94
|
+
entrypoint: "server.js",
|
|
95
|
+
runtime: "edge"
|
|
96
|
+
};
|
|
97
|
+
if (regions) config.regions = regions;
|
|
98
|
+
return config;
|
|
99
|
+
}
|
|
100
|
+
function sortStaticRoutes(routes) {
|
|
101
|
+
return [...new Set(routes)].sort((left, right) => right.length - left.length);
|
|
102
|
+
}
|
|
103
|
+
function routeToRouteExpression(route) {
|
|
104
|
+
if (route === "/") return "^/$";
|
|
105
|
+
return `^${escapeRegex(route)}/?$`;
|
|
106
|
+
}
|
|
107
|
+
function routeToStaticHtmlPath(route) {
|
|
108
|
+
if (route === "/") return "/index.html";
|
|
109
|
+
return `${route}/index.html`;
|
|
110
|
+
}
|
|
111
|
+
function routeToHeaderSource(route) {
|
|
112
|
+
return route === "/" ? "/" : route;
|
|
113
|
+
}
|
|
114
|
+
function escapeRegex(value) {
|
|
115
|
+
return value.replace(/[|\\{}()[\]^$+*?.-]/g, "\\$&");
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/commands/build.ts
|
|
119
|
+
var build_default = defineCommand({
|
|
120
|
+
meta: {
|
|
121
|
+
name: "build",
|
|
122
|
+
description: "Production build (client + server)"
|
|
123
|
+
},
|
|
124
|
+
async run() {
|
|
125
|
+
const root = process.cwd();
|
|
126
|
+
console.log("\n Building client...\n");
|
|
127
|
+
await build({
|
|
128
|
+
root,
|
|
129
|
+
build: {
|
|
130
|
+
outDir: "dist",
|
|
131
|
+
manifest: true,
|
|
132
|
+
rollupOptions: { input: "virtual:pracht/client" }
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
console.log("\n Building server...\n");
|
|
136
|
+
await build({
|
|
137
|
+
root,
|
|
138
|
+
build: {
|
|
139
|
+
outDir: "dist/server",
|
|
140
|
+
ssr: "virtual:pracht/server"
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
const serverEntry = resolve(root, "dist/server/server.js");
|
|
144
|
+
let clientDir;
|
|
145
|
+
if (existsSync(resolve(root, "dist/client/.vite/manifest.json"))) clientDir = resolve(root, "dist/client");
|
|
146
|
+
else {
|
|
147
|
+
clientDir = resolve(root, "dist/client");
|
|
148
|
+
const distRoot = resolve(root, "dist");
|
|
149
|
+
mkdirSync(clientDir, { recursive: true });
|
|
150
|
+
for (const entry of readdirSync(distRoot)) {
|
|
151
|
+
if (entry === "server" || entry === "client") continue;
|
|
152
|
+
const sourcePath = join(distRoot, entry);
|
|
153
|
+
cpSync(sourcePath, join(clientDir, entry), { recursive: true });
|
|
154
|
+
rmSync(sourcePath, {
|
|
155
|
+
force: true,
|
|
156
|
+
recursive: true
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (existsSync(serverEntry)) {
|
|
161
|
+
const serverMod = await import(serverEntry);
|
|
162
|
+
const { prerenderApp } = serverMod;
|
|
163
|
+
const { clientEntryUrl, cssManifest, jsManifest } = readClientBuildAssets(root);
|
|
164
|
+
const { pages, isgManifest } = await prerenderApp({
|
|
165
|
+
app: serverMod.resolvedApp,
|
|
166
|
+
clientEntryUrl: clientEntryUrl ?? void 0,
|
|
167
|
+
cssManifest,
|
|
168
|
+
jsManifest,
|
|
169
|
+
registry: serverMod.registry,
|
|
170
|
+
withISGManifest: true
|
|
171
|
+
});
|
|
172
|
+
const headersManifest = Object.fromEntries(pages.map((page) => [page.path, page.headers ?? {}]));
|
|
173
|
+
if (pages.length > 0) {
|
|
174
|
+
console.log(`\n Prerendering ${pages.length} SSG/ISG route(s)...\n`);
|
|
175
|
+
for (const page of pages) {
|
|
176
|
+
const filePath = page.path === "/" ? join(clientDir, "index.html") : join(clientDir, page.path, "index.html");
|
|
177
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
178
|
+
writeFileSync(filePath, page.html, "utf-8");
|
|
179
|
+
console.log(` ${page.path} → ${filePath.replace(root + "/", "")}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (Object.keys(headersManifest).length > 0) {
|
|
183
|
+
const headersManifestJson = `${JSON.stringify(headersManifest, null, 2)}\n`;
|
|
184
|
+
writeFileSync(resolve(root, "dist/server/headers-manifest.json"), headersManifestJson, "utf-8");
|
|
185
|
+
mkdirSync(resolve(clientDir, "_pracht"), { recursive: true });
|
|
186
|
+
writeFileSync(resolve(clientDir, "_pracht/headers.json"), headersManifestJson, "utf-8");
|
|
187
|
+
}
|
|
188
|
+
if (Object.keys(isgManifest).length > 0) {
|
|
189
|
+
writeFileSync(resolve(root, "dist/server/isg-manifest.json"), JSON.stringify(isgManifest, null, 2), "utf-8");
|
|
190
|
+
console.log(`\n ISG manifest → dist/server/isg-manifest.json (${Object.keys(isgManifest).length} route(s))\n`);
|
|
191
|
+
}
|
|
192
|
+
if (serverMod.buildTarget === "cloudflare") {
|
|
193
|
+
console.log("\n Cloudflare worker → dist/server/server.js\n");
|
|
194
|
+
console.log(" Deploy with: wrangler deploy\n");
|
|
195
|
+
}
|
|
196
|
+
if (serverMod.buildTarget === "vercel") {
|
|
197
|
+
const outputPath = writeVercelBuildOutput({
|
|
198
|
+
functionName: serverMod.vercelFunctionName,
|
|
199
|
+
isgRoutes: Object.keys(isgManifest),
|
|
200
|
+
headersManifest,
|
|
201
|
+
regions: serverMod.vercelRegions,
|
|
202
|
+
root,
|
|
203
|
+
staticRoutes: pages.map((page) => page.path).filter((path) => !(path in isgManifest))
|
|
204
|
+
});
|
|
205
|
+
console.log(`\n Vercel build output → ${outputPath}\n`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
console.log("\n Build complete.\n");
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
//#endregion
|
|
212
|
+
export { build_default as default };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
//#region src/build-metadata.ts
|
|
4
|
+
const MANIFEST_PATHS = ["dist/client/.vite/manifest.json", "dist/.vite/manifest.json"];
|
|
5
|
+
function readClientBuildAssets(root = process.cwd()) {
|
|
6
|
+
const manifestPath = MANIFEST_PATHS.map((candidate) => resolve(root, candidate)).find((path) => existsSync(path));
|
|
7
|
+
if (!manifestPath) return {
|
|
8
|
+
clientEntryUrl: null,
|
|
9
|
+
cssManifest: {},
|
|
10
|
+
jsManifest: {}
|
|
11
|
+
};
|
|
12
|
+
const rawManifest = readFileSync(manifestPath, "utf-8");
|
|
13
|
+
const manifest = JSON.parse(rawManifest);
|
|
14
|
+
const clientEntry = manifest["virtual:pracht/client"];
|
|
15
|
+
function collectTransitiveDeps(key) {
|
|
16
|
+
const css = /* @__PURE__ */ new Set();
|
|
17
|
+
const js = /* @__PURE__ */ new Set();
|
|
18
|
+
const visited = /* @__PURE__ */ new Set();
|
|
19
|
+
function collect(currentKey) {
|
|
20
|
+
if (visited.has(currentKey)) return;
|
|
21
|
+
visited.add(currentKey);
|
|
22
|
+
const entry = manifest[currentKey];
|
|
23
|
+
if (!entry) return;
|
|
24
|
+
for (const cssFile of entry.css ?? []) css.add(cssFile);
|
|
25
|
+
js.add(entry.file);
|
|
26
|
+
for (const importedKey of entry.imports ?? []) collect(importedKey);
|
|
27
|
+
}
|
|
28
|
+
collect(key);
|
|
29
|
+
return {
|
|
30
|
+
css: [...css],
|
|
31
|
+
js: [...js]
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const cssManifest = {};
|
|
35
|
+
const jsManifest = {};
|
|
36
|
+
for (const [key, entry] of Object.entries(manifest)) {
|
|
37
|
+
if (!entry.src) continue;
|
|
38
|
+
const deps = collectTransitiveDeps(key);
|
|
39
|
+
if (deps.css.length > 0) cssManifest[key] = deps.css.map((file) => `/${file}`);
|
|
40
|
+
if (deps.js.length > 0) jsManifest[key] = deps.js.map((file) => `/${file}`);
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
clientEntryUrl: clientEntry ? `/${clientEntry.file}` : null,
|
|
44
|
+
cssManifest,
|
|
45
|
+
jsManifest
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { readClientBuildAssets as t };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { createServer } from "vite";
|
|
3
|
+
//#region src/commands/dev.ts
|
|
4
|
+
var dev_default = defineCommand({
|
|
5
|
+
meta: {
|
|
6
|
+
name: "dev",
|
|
7
|
+
description: "Start development server with HMR"
|
|
8
|
+
},
|
|
9
|
+
args: { port: {
|
|
10
|
+
type: "positional",
|
|
11
|
+
description: "Port number",
|
|
12
|
+
required: false
|
|
13
|
+
} },
|
|
14
|
+
async run({ args }) {
|
|
15
|
+
const port = parseInt(process.env.PORT || args.port || "3000", 10);
|
|
16
|
+
const server = await createServer({
|
|
17
|
+
root: process.cwd(),
|
|
18
|
+
server: { port }
|
|
19
|
+
});
|
|
20
|
+
await server.listen();
|
|
21
|
+
server.printUrls();
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
//#endregion
|
|
25
|
+
export { dev_default as default };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { t as runDoctor } from "./verification-Dfl3X4Zo.mjs";
|
|
2
|
+
import { defineCommand } from "citty";
|
|
3
|
+
//#region src/commands/doctor.ts
|
|
4
|
+
var doctor_default = defineCommand({
|
|
5
|
+
meta: {
|
|
6
|
+
name: "doctor",
|
|
7
|
+
description: "Validate app wiring"
|
|
8
|
+
},
|
|
9
|
+
args: { json: {
|
|
10
|
+
type: "boolean",
|
|
11
|
+
description: "Output as JSON"
|
|
12
|
+
} },
|
|
13
|
+
async run({ args }) {
|
|
14
|
+
const report = runDoctor(process.cwd());
|
|
15
|
+
if (args.json) console.log(JSON.stringify(report, null, 2));
|
|
16
|
+
else {
|
|
17
|
+
console.log(`Pracht doctor (${report.mode} mode)`);
|
|
18
|
+
for (const check of report.checks) console.log(`${check.status.toUpperCase().padEnd(5)} ${check.message}`);
|
|
19
|
+
console.log(report.ok ? "\nNo blocking issues found." : "\nBlocking issues found.");
|
|
20
|
+
}
|
|
21
|
+
if (!report.ok) process.exitCode = 1;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
//#endregion
|
|
25
|
+
export { doctor_default as default };
|