create-pracht 0.5.0 → 0.6.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.
- package/README.md +7 -4
- package/package.json +1 -1
- package/skills/add-i18n/SKILL.md +308 -151
- package/skills/audit-bundles/SKILL.md +8 -0
- package/skills/audit-headers/SKILL.md +14 -5
- package/skills/audit-secrets/SKILL.md +7 -4
- package/skills/configure-isg/SKILL.md +7 -3
- package/skills/migrate-nextjs/SKILL.md +15 -5
- package/skills/pracht-debug/SKILL.md +9 -1
- package/skills/pracht-deploy/SKILL.md +118 -12
- package/skills/pracht-scaffold/SKILL.md +11 -1
- package/skills/pre-deploy/SKILL.md +82 -11
- package/skills/typed-routes/SKILL.md +6 -2
- package/skills/upgrade-pracht/SKILL.md +5 -2
- package/src/index.js +81 -17
package/src/index.js
CHANGED
|
@@ -16,10 +16,11 @@ const FALLBACK_VERSION_RANGES = {
|
|
|
16
16
|
"@pracht/adapter-cloudflare": "^0.5.8",
|
|
17
17
|
"@pracht/adapter-netlify": "^0.1.0",
|
|
18
18
|
"@pracht/adapter-node": "^0.3.8",
|
|
19
|
+
"@pracht/adapter-static": "^0.1.0",
|
|
19
20
|
"@pracht/adapter-vercel": "^0.2.8",
|
|
20
|
-
"@pracht/cli": "^1.
|
|
21
|
-
"@pracht/core": "^0.
|
|
22
|
-
"@pracht/vite-plugin": "^0.
|
|
21
|
+
"@pracht/cli": "^1.11.0",
|
|
22
|
+
"@pracht/core": "^0.14.0",
|
|
23
|
+
"@pracht/vite-plugin": "^0.9.0",
|
|
23
24
|
"@tailwindcss/vite": "^4.1.0",
|
|
24
25
|
"netlify-cli": "^21.6.0",
|
|
25
26
|
tailwindcss: "^4.1.0",
|
|
@@ -87,6 +88,13 @@ const ADAPTERS = {
|
|
|
87
88
|
packageName: "@pracht/adapter-vercel",
|
|
88
89
|
short: "vercel",
|
|
89
90
|
},
|
|
91
|
+
static: {
|
|
92
|
+
description: "Pure static export — deploy dist/client to any static host",
|
|
93
|
+
id: "static",
|
|
94
|
+
label: "Static export",
|
|
95
|
+
packageName: "@pracht/adapter-static",
|
|
96
|
+
short: "static",
|
|
97
|
+
},
|
|
90
98
|
};
|
|
91
99
|
|
|
92
100
|
const DEFAULT_DIRECTORY = "pracht-app";
|
|
@@ -407,7 +415,7 @@ export function parseArgs(argv) {
|
|
|
407
415
|
const value = normalizeAdapter(arg.slice("--adapter=".length));
|
|
408
416
|
if (!value) {
|
|
409
417
|
throw new ValidationError(
|
|
410
|
-
`Invalid adapter: ${arg.slice("--adapter=".length)}. Use node, cf, netlify, or
|
|
418
|
+
`Invalid adapter: ${arg.slice("--adapter=".length)}. Use node, cf, netlify, vercel, or static.`,
|
|
411
419
|
);
|
|
412
420
|
}
|
|
413
421
|
options.adapter = value;
|
|
@@ -462,6 +470,7 @@ async function promptForAdapter(readline) {
|
|
|
462
470
|
console.log(" 2. Cloudflare Workers");
|
|
463
471
|
console.log(" 3. Vercel");
|
|
464
472
|
console.log(" 4. Netlify");
|
|
473
|
+
console.log(" 5. Static export (no server)");
|
|
465
474
|
|
|
466
475
|
while (true) {
|
|
467
476
|
const answer = await readline.question("Adapter (1): ");
|
|
@@ -471,7 +480,7 @@ async function promptForAdapter(readline) {
|
|
|
471
480
|
return normalized;
|
|
472
481
|
}
|
|
473
482
|
|
|
474
|
-
console.log("Choose 1/2/3/4 or node/cf/vercel/netlify.");
|
|
483
|
+
console.log("Choose 1/2/3/4/5 or node/cf/vercel/netlify/static.");
|
|
475
484
|
}
|
|
476
485
|
}
|
|
477
486
|
|
|
@@ -614,6 +623,10 @@ function normalizeAdapter(value) {
|
|
|
614
623
|
return "netlify";
|
|
615
624
|
}
|
|
616
625
|
|
|
626
|
+
if (normalized === "5" || normalized === "static" || normalized === "export") {
|
|
627
|
+
return "static";
|
|
628
|
+
}
|
|
629
|
+
|
|
617
630
|
return null;
|
|
618
631
|
}
|
|
619
632
|
|
|
@@ -690,11 +703,16 @@ async function buildProjectFiles({
|
|
|
690
703
|
tailwind,
|
|
691
704
|
versions,
|
|
692
705
|
}),
|
|
693
|
-
"src/api/health.ts": createHealthRoute(adapter),
|
|
694
706
|
"vite.config.ts": createViteConfig(adapter, router, tailwind),
|
|
695
707
|
"tsconfig.json": createBaseTSConfig(adapter),
|
|
696
708
|
};
|
|
697
709
|
|
|
710
|
+
// A static export has no server, so an API route would be a hard build
|
|
711
|
+
// error — the starter must not scaffold one it cannot build.
|
|
712
|
+
if (adapter.id !== "static") {
|
|
713
|
+
files["src/api/health.ts"] = createHealthRoute(adapter);
|
|
714
|
+
}
|
|
715
|
+
|
|
698
716
|
if (agentTools) {
|
|
699
717
|
files["AGENTS.md"] = createAgentInstructions({
|
|
700
718
|
adapter,
|
|
@@ -803,6 +821,10 @@ function createPackageJson({ adapter, projectName, tailwind, versions }) {
|
|
|
803
821
|
scripts.start = "node dist/server/server.js";
|
|
804
822
|
}
|
|
805
823
|
|
|
824
|
+
if (adapter.id === "static") {
|
|
825
|
+
scripts.preview = "pracht preview";
|
|
826
|
+
}
|
|
827
|
+
|
|
806
828
|
const devDependencies = {
|
|
807
829
|
"@pracht/cli": versions["@pracht/cli"],
|
|
808
830
|
"@pracht/vite-plugin": versions["@pracht/vite-plugin"],
|
|
@@ -858,6 +880,7 @@ function createViteConfig(adapter, router, tailwind) {
|
|
|
858
880
|
cloudflare: { fn: "cloudflareAdapter", pkg: "@pracht/adapter-cloudflare" },
|
|
859
881
|
netlify: { fn: "netlifyAdapter", pkg: "@pracht/adapter-netlify" },
|
|
860
882
|
vercel: { fn: "vercelAdapter", pkg: "@pracht/adapter-vercel" },
|
|
883
|
+
static: { fn: "staticAdapter", pkg: "@pracht/adapter-static" },
|
|
861
884
|
};
|
|
862
885
|
|
|
863
886
|
const info = ADAPTER_IMPORTS[adapter.id] ?? ADAPTER_IMPORTS.node;
|
|
@@ -955,7 +978,9 @@ function createHomeRoute(adapter) {
|
|
|
955
978
|
" steps: [",
|
|
956
979
|
' "Edit src/routes/home.tsx to change this page.",',
|
|
957
980
|
' "Add more routes in src/routes.ts.",',
|
|
958
|
-
|
|
981
|
+
adapter.id === "static"
|
|
982
|
+
? ' "Fetch live data from the browser — a static export runs no server.",'
|
|
983
|
+
: ' "Add API handlers in src/api/*.ts.",',
|
|
959
984
|
" ],",
|
|
960
985
|
" };",
|
|
961
986
|
"}",
|
|
@@ -974,7 +999,9 @@ function createHomeRoute(adapter) {
|
|
|
974
999
|
" ))}",
|
|
975
1000
|
" </ul>",
|
|
976
1001
|
' <p style={{ marginTop: "24px" }}>',
|
|
977
|
-
|
|
1002
|
+
adapter.id === "static"
|
|
1003
|
+
? " Run <code>pracht build</code>, then deploy <code>dist/client</code> anywhere."
|
|
1004
|
+
: " Check <code>/api/health</code> for a simple API route.",
|
|
978
1005
|
" </p>",
|
|
979
1006
|
" </section>",
|
|
980
1007
|
" );",
|
|
@@ -1022,7 +1049,9 @@ function createPagesHomeRoute(adapter) {
|
|
|
1022
1049
|
" steps: [",
|
|
1023
1050
|
' "Edit src/pages/index.tsx to change this page.",',
|
|
1024
1051
|
' "Add more pages in src/pages/.",',
|
|
1025
|
-
|
|
1052
|
+
adapter.id === "static"
|
|
1053
|
+
? ' "Fetch live data from the browser — a static export runs no server.",'
|
|
1054
|
+
: ' "Add API handlers in src/api/*.ts.",',
|
|
1026
1055
|
" ],",
|
|
1027
1056
|
" };",
|
|
1028
1057
|
"}",
|
|
@@ -1041,7 +1070,9 @@ function createPagesHomeRoute(adapter) {
|
|
|
1041
1070
|
" ))}",
|
|
1042
1071
|
" </ul>",
|
|
1043
1072
|
' <p style={{ marginTop: "24px" }}>',
|
|
1044
|
-
|
|
1073
|
+
adapter.id === "static"
|
|
1074
|
+
? " Run <code>pracht build</code>, then deploy <code>dist/client</code> anywhere."
|
|
1075
|
+
: " Check <code>/api/health</code> for a simple API route.",
|
|
1045
1076
|
" </p>",
|
|
1046
1077
|
" </section>",
|
|
1047
1078
|
" );",
|
|
@@ -1267,6 +1298,11 @@ function createWranglerConfig(projectName) {
|
|
|
1267
1298
|
// validates every named export of the deployed entry module and rejects the
|
|
1268
1299
|
// build metadata (buildTarget, manifests, ...) server.js also exports.
|
|
1269
1300
|
' "main": "dist/server/worker.js",',
|
|
1301
|
+
// Pracht's Vite build is the authoritative bundle and may contain lazy
|
|
1302
|
+
// server chunks. A second Wrangler bundle would inline them again; the
|
|
1303
|
+
// module rule makes Wrangler upload those chunks next to the entry.
|
|
1304
|
+
' "no_bundle": true,',
|
|
1305
|
+
' "rules": [{ "type": "ESModule", "globs": ["**/*.js", "**/*.mjs"] }],',
|
|
1270
1306
|
` "compatibility_date": ${JSON.stringify(compatibilityDate)},`,
|
|
1271
1307
|
' "assets": {',
|
|
1272
1308
|
' "binding": "ASSETS",',
|
|
@@ -1410,7 +1446,12 @@ function createAgentInstructions({ adapter, agentTools, packageManager, router,
|
|
|
1410
1446
|
`- \`${runCmd} build\` — production build`,
|
|
1411
1447
|
];
|
|
1412
1448
|
|
|
1413
|
-
if (
|
|
1449
|
+
if (
|
|
1450
|
+
adapter.id === "node" ||
|
|
1451
|
+
adapter.id === "cloudflare" ||
|
|
1452
|
+
adapter.id === "netlify" ||
|
|
1453
|
+
adapter.id === "static"
|
|
1454
|
+
) {
|
|
1414
1455
|
lines.push(`- \`${runCmd} preview\` — build and serve the production build locally`);
|
|
1415
1456
|
}
|
|
1416
1457
|
|
|
@@ -1430,10 +1471,14 @@ function createAgentInstructions({ adapter, agentTools, packageManager, router,
|
|
|
1430
1471
|
lines.push("- `pracht generate route --path /about` — add a route");
|
|
1431
1472
|
if (router !== "pages") {
|
|
1432
1473
|
lines.push("- `pracht generate shell --name app` — add a shell");
|
|
1433
|
-
|
|
1474
|
+
if (adapter.id !== "static") {
|
|
1475
|
+
lines.push("- `pracht generate middleware --name auth` — add middleware");
|
|
1476
|
+
}
|
|
1434
1477
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
1478
|
+
if (adapter.id !== "static") {
|
|
1479
|
+
lines.push("- `pracht generate api --path /health --methods GET` — add an API route");
|
|
1480
|
+
}
|
|
1481
|
+
if (router !== "pages" && adapter.id !== "static") {
|
|
1437
1482
|
lines.push(
|
|
1438
1483
|
"- `pracht generate capability --name notes.search --effect read --expose http` — add a capability (agent-callable operation)",
|
|
1439
1484
|
);
|
|
@@ -1473,7 +1518,9 @@ function createAgentInstructions({ adapter, agentTools, packageManager, router,
|
|
|
1473
1518
|
lines.push("- `src/shells/` — shell components (layouts)");
|
|
1474
1519
|
}
|
|
1475
1520
|
|
|
1476
|
-
|
|
1521
|
+
if (adapter.id !== "static") {
|
|
1522
|
+
lines.push("- `src/api/` — API route handlers");
|
|
1523
|
+
}
|
|
1477
1524
|
lines.push(`- \`vite.config.ts\` — Vite config with the ${adapter.label} adapter`);
|
|
1478
1525
|
|
|
1479
1526
|
if (tailwind) {
|
|
@@ -1576,6 +1623,21 @@ function createReadme({
|
|
|
1576
1623
|
lines.push("Run the deploy command after linking or logging into your Vercel account.");
|
|
1577
1624
|
}
|
|
1578
1625
|
|
|
1626
|
+
if (adapter.id === "static") {
|
|
1627
|
+
lines.push(`- \`${previewCommand}\``);
|
|
1628
|
+
lines.push("");
|
|
1629
|
+
lines.push(
|
|
1630
|
+
"`pracht build` writes the whole site to `dist/client`. Upload that directory to any " +
|
|
1631
|
+
"static host — there is no server to run. Configure the host to serve `index.html` " +
|
|
1632
|
+
"for directory URLs and to use `404.html` as its error document.",
|
|
1633
|
+
);
|
|
1634
|
+
lines.push("");
|
|
1635
|
+
lines.push(
|
|
1636
|
+
"A static export runs no server, so API routes, middleware, and `ssr`/`isg` routes are " +
|
|
1637
|
+
"build errors. Fetch live data from the browser instead, or switch to a serverful adapter.",
|
|
1638
|
+
);
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1579
1641
|
lines.push("");
|
|
1580
1642
|
lines.push("## Files");
|
|
1581
1643
|
lines.push("");
|
|
@@ -1597,7 +1659,9 @@ function createReadme({
|
|
|
1597
1659
|
lines.push("- `src/routes/not-found.tsx` is the not-found page, wired via `notFound`.");
|
|
1598
1660
|
}
|
|
1599
1661
|
|
|
1600
|
-
|
|
1662
|
+
if (adapter.id !== "static") {
|
|
1663
|
+
lines.push("- `src/api/health.ts` is a sample API route.");
|
|
1664
|
+
}
|
|
1601
1665
|
|
|
1602
1666
|
if (packageManager === "pnpm") {
|
|
1603
1667
|
lines.push(
|
|
@@ -1791,7 +1855,7 @@ Usage:
|
|
|
1791
1855
|
create-pracht [directory] [options]
|
|
1792
1856
|
|
|
1793
1857
|
Options:
|
|
1794
|
-
--adapter=node|cf|netlify|vercel
|
|
1858
|
+
--adapter=node|cf|netlify|vercel|static
|
|
1795
1859
|
Choose hosting adapter (default: node)
|
|
1796
1860
|
--router=manifest|pages Choose routing system (default: manifest)
|
|
1797
1861
|
--template=minimal|tailwind Choose starter template (minimal, or minimal + Tailwind CSS)
|