create-pracht 0.0.1 → 0.1.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/package.json +13 -1
- package/src/index.js +155 -21
package/package.json
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pracht",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "https://github.com/JoviDeCroock/pracht",
|
|
7
|
+
"directory": "packages/start"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/JoviDeCroock/pracht/tree/main/packages/start",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/JoviDeCroock/pracht/issues"
|
|
12
|
+
},
|
|
4
13
|
"bin": {
|
|
5
14
|
"create-pracht": "./bin/create-pracht.js"
|
|
6
15
|
},
|
|
@@ -10,6 +19,9 @@
|
|
|
10
19
|
"README.md"
|
|
11
20
|
],
|
|
12
21
|
"type": "module",
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"provenance": true
|
|
24
|
+
},
|
|
13
25
|
"scripts": {
|
|
14
26
|
"build": "node -e \"\""
|
|
15
27
|
}
|
package/src/index.js
CHANGED
|
@@ -4,6 +4,19 @@ import { mkdir, readdir, stat, writeFile } from "node:fs/promises";
|
|
|
4
4
|
import { basename, dirname, resolve } from "node:path";
|
|
5
5
|
import { createInterface } from "node:readline/promises";
|
|
6
6
|
|
|
7
|
+
async function fetchLatestVersion(packageName) {
|
|
8
|
+
const res = await fetch(
|
|
9
|
+
`https://registry.npmjs.org/${packageName}/latest`,
|
|
10
|
+
);
|
|
11
|
+
if (!res.ok) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`Failed to fetch version for ${packageName}: ${res.statusText}`,
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
const data = await res.json();
|
|
17
|
+
return data.version;
|
|
18
|
+
}
|
|
19
|
+
|
|
7
20
|
const ADAPTERS = {
|
|
8
21
|
node: {
|
|
9
22
|
description: "Node.js server with pracht preview",
|
|
@@ -46,6 +59,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
46
59
|
try {
|
|
47
60
|
const dir = options.dir ?? (await promptForDirectory(readline));
|
|
48
61
|
const adapterId = options.adapter ?? (await promptForAdapter(readline));
|
|
62
|
+
const router = options.router ?? (await promptForRouter(readline));
|
|
49
63
|
const targetDir = resolve(process.cwd(), dir);
|
|
50
64
|
|
|
51
65
|
await ensureTargetDirectory(targetDir);
|
|
@@ -53,6 +67,7 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
53
67
|
await scaffoldProject({
|
|
54
68
|
adapter: ADAPTERS[adapterId],
|
|
55
69
|
packageManager,
|
|
70
|
+
router,
|
|
56
71
|
targetDir,
|
|
57
72
|
});
|
|
58
73
|
|
|
@@ -75,12 +90,13 @@ export async function run(argv = process.argv.slice(2)) {
|
|
|
75
90
|
}
|
|
76
91
|
}
|
|
77
92
|
|
|
78
|
-
export async function scaffoldProject({ adapter, packageManager, targetDir }) {
|
|
93
|
+
export async function scaffoldProject({ adapter, packageManager, router = "manifest", targetDir }) {
|
|
79
94
|
const packageName = toPackageName(basename(targetDir));
|
|
80
|
-
const files = buildProjectFiles({
|
|
95
|
+
const files = await buildProjectFiles({
|
|
81
96
|
adapter,
|
|
82
97
|
packageManager,
|
|
83
98
|
projectName: packageName,
|
|
99
|
+
router,
|
|
84
100
|
});
|
|
85
101
|
|
|
86
102
|
await mkdir(targetDir, { recursive: true });
|
|
@@ -103,6 +119,7 @@ export function parseArgs(argv) {
|
|
|
103
119
|
const options = {
|
|
104
120
|
adapter: undefined,
|
|
105
121
|
dir: undefined,
|
|
122
|
+
router: undefined,
|
|
106
123
|
skipInstall: false,
|
|
107
124
|
};
|
|
108
125
|
|
|
@@ -117,6 +134,11 @@ export function parseArgs(argv) {
|
|
|
117
134
|
continue;
|
|
118
135
|
}
|
|
119
136
|
|
|
137
|
+
if (arg.startsWith("--router=")) {
|
|
138
|
+
options.router = normalizeRouter(arg.slice("--router=".length));
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
120
142
|
if (arg === "--help" || arg === "-h") {
|
|
121
143
|
printHelp();
|
|
122
144
|
process.exit(0);
|
|
@@ -166,6 +188,23 @@ async function promptForAdapter(readline) {
|
|
|
166
188
|
}
|
|
167
189
|
}
|
|
168
190
|
|
|
191
|
+
async function promptForRouter(readline) {
|
|
192
|
+
console.log("Router:");
|
|
193
|
+
console.log(" 1. Manifest (explicit routes.ts)");
|
|
194
|
+
console.log(" 2. Pages (file-system routing)");
|
|
195
|
+
|
|
196
|
+
while (true) {
|
|
197
|
+
const answer = await readline.question("Router (1): ");
|
|
198
|
+
const normalized = normalizeRouter(answer.trim() || "1");
|
|
199
|
+
|
|
200
|
+
if (normalized) {
|
|
201
|
+
return normalized;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
console.log("Choose 1/2 or manifest/pages.");
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
169
208
|
async function ensureTargetDirectory(targetDir) {
|
|
170
209
|
const error = await validateTargetDirectory(targetDir);
|
|
171
210
|
|
|
@@ -192,6 +231,20 @@ async function validateTargetDirectory(targetDir) {
|
|
|
192
231
|
return null;
|
|
193
232
|
}
|
|
194
233
|
|
|
234
|
+
function normalizeRouter(value) {
|
|
235
|
+
const normalized = value.toLowerCase();
|
|
236
|
+
|
|
237
|
+
if (normalized === "1" || normalized === "manifest") {
|
|
238
|
+
return "manifest";
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (normalized === "2" || normalized === "pages") {
|
|
242
|
+
return "pages";
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
|
|
195
248
|
function normalizeAdapter(value) {
|
|
196
249
|
const normalized = value.toLowerCase();
|
|
197
250
|
|
|
@@ -215,18 +268,46 @@ function normalizeAdapter(value) {
|
|
|
215
268
|
return null;
|
|
216
269
|
}
|
|
217
270
|
|
|
218
|
-
function
|
|
271
|
+
async function resolveVersions(packageNames) {
|
|
272
|
+
const entries = await Promise.all(
|
|
273
|
+
packageNames.map(async (name) => [
|
|
274
|
+
name,
|
|
275
|
+
`^${await fetchLatestVersion(name)}`,
|
|
276
|
+
]),
|
|
277
|
+
);
|
|
278
|
+
return Object.fromEntries(entries);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async function buildProjectFiles({ adapter, packageManager, projectName, router }) {
|
|
282
|
+
const packagesToResolve = [
|
|
283
|
+
"@pracht/cli",
|
|
284
|
+
"@pracht/vite-plugin",
|
|
285
|
+
"@pracht/core",
|
|
286
|
+
adapter.packageName,
|
|
287
|
+
];
|
|
288
|
+
if (adapter.id === "vercel") {
|
|
289
|
+
packagesToResolve.push("vercel");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const versions = await resolveVersions(packagesToResolve);
|
|
293
|
+
|
|
219
294
|
const files = {
|
|
220
295
|
".gitignore": "dist\nnode_modules\n.wrangler\n.vercel\n",
|
|
221
|
-
"README.md": createReadme({ adapter, packageManager, projectName }),
|
|
222
|
-
"package.json": createPackageJson({ adapter, projectName }),
|
|
296
|
+
"README.md": createReadme({ adapter, packageManager, projectName, router }),
|
|
297
|
+
"package.json": createPackageJson({ adapter, projectName, versions }),
|
|
223
298
|
"src/api/health.ts": createHealthRoute(adapter),
|
|
224
|
-
"
|
|
225
|
-
"src/routes/home.tsx": createHomeRoute(adapter),
|
|
226
|
-
"src/shells/public.tsx": createShellFile(projectName),
|
|
227
|
-
"vite.config.ts": createViteConfig(adapter),
|
|
299
|
+
"vite.config.ts": createViteConfig(adapter, router),
|
|
228
300
|
};
|
|
229
301
|
|
|
302
|
+
if (router === "pages") {
|
|
303
|
+
files["src/pages/_app.tsx"] = createShellFile(projectName);
|
|
304
|
+
files["src/pages/index.tsx"] = createPagesHomeRoute(adapter);
|
|
305
|
+
} else {
|
|
306
|
+
files["src/routes.ts"] = createRoutesFile();
|
|
307
|
+
files["src/routes/home.tsx"] = createHomeRoute(adapter);
|
|
308
|
+
files["src/shells/public.tsx"] = createShellFile(projectName);
|
|
309
|
+
}
|
|
310
|
+
|
|
230
311
|
if (adapter.id === "cloudflare") {
|
|
231
312
|
files["wrangler.jsonc"] = createWranglerConfig(projectName);
|
|
232
313
|
files["src/env.d.ts"] = createCloudflareEnvDeclaration();
|
|
@@ -235,7 +316,7 @@ function buildProjectFiles({ adapter, packageManager, projectName }) {
|
|
|
235
316
|
return files;
|
|
236
317
|
}
|
|
237
318
|
|
|
238
|
-
function createPackageJson({ adapter, projectName }) {
|
|
319
|
+
function createPackageJson({ adapter, projectName, versions }) {
|
|
239
320
|
const scripts = {
|
|
240
321
|
build: "pracht build",
|
|
241
322
|
dev: "pracht dev",
|
|
@@ -243,8 +324,8 @@ function createPackageJson({ adapter, projectName }) {
|
|
|
243
324
|
};
|
|
244
325
|
|
|
245
326
|
const devDependencies = {
|
|
246
|
-
"@pracht/cli": "
|
|
247
|
-
"@pracht/vite-plugin": "
|
|
327
|
+
"@pracht/cli": versions["@pracht/cli"],
|
|
328
|
+
"@pracht/vite-plugin": versions["@pracht/vite-plugin"],
|
|
248
329
|
preact: "^10.26.9",
|
|
249
330
|
"preact-render-to-string": "^6.5.13",
|
|
250
331
|
vite: "^8.0.0",
|
|
@@ -257,14 +338,14 @@ function createPackageJson({ adapter, projectName }) {
|
|
|
257
338
|
|
|
258
339
|
if (adapter.id === "vercel") {
|
|
259
340
|
scripts.deploy = "pracht build && vercel deploy --prebuilt";
|
|
260
|
-
devDependencies.vercel = "
|
|
341
|
+
devDependencies.vercel = versions["vercel"];
|
|
261
342
|
}
|
|
262
343
|
|
|
263
344
|
return `${JSON.stringify(
|
|
264
345
|
{
|
|
265
346
|
dependencies: {
|
|
266
|
-
[adapter.packageName]:
|
|
267
|
-
pracht: "
|
|
347
|
+
[adapter.packageName]: versions[adapter.packageName],
|
|
348
|
+
"@pracht/core": versions["@pracht/core"],
|
|
268
349
|
},
|
|
269
350
|
devDependencies,
|
|
270
351
|
name: projectName,
|
|
@@ -278,7 +359,7 @@ function createPackageJson({ adapter, projectName }) {
|
|
|
278
359
|
)}\n`;
|
|
279
360
|
}
|
|
280
361
|
|
|
281
|
-
function createViteConfig(adapter) {
|
|
362
|
+
function createViteConfig(adapter, router) {
|
|
282
363
|
const ADAPTER_IMPORTS = {
|
|
283
364
|
node: { fn: "nodeAdapter", pkg: "@pracht/adapter-node" },
|
|
284
365
|
cloudflare: { fn: "cloudflareAdapter", pkg: "@pracht/adapter-cloudflare" },
|
|
@@ -287,13 +368,18 @@ function createViteConfig(adapter) {
|
|
|
287
368
|
|
|
288
369
|
const info = ADAPTER_IMPORTS[adapter.id] ?? ADAPTER_IMPORTS.node;
|
|
289
370
|
|
|
371
|
+
const prachtOptions =
|
|
372
|
+
router === "pages"
|
|
373
|
+
? `{ pagesDir: "/src/pages", adapter: ${info.fn}() }`
|
|
374
|
+
: `{ adapter: ${info.fn}() }`;
|
|
375
|
+
|
|
290
376
|
return [
|
|
291
377
|
'import { defineConfig } from "vite";',
|
|
292
378
|
'import { pracht } from "@pracht/vite-plugin";',
|
|
293
379
|
`import { ${info.fn} } from "${info.pkg}";`,
|
|
294
380
|
"",
|
|
295
381
|
"export default defineConfig({",
|
|
296
|
-
` plugins: [pracht(
|
|
382
|
+
` plugins: [pracht(${prachtOptions})],`,
|
|
297
383
|
"});",
|
|
298
384
|
"",
|
|
299
385
|
].join("\n");
|
|
@@ -379,6 +465,46 @@ function createHomeRoute(adapter) {
|
|
|
379
465
|
].join("\n");
|
|
380
466
|
}
|
|
381
467
|
|
|
468
|
+
function createPagesHomeRoute(adapter) {
|
|
469
|
+
return [
|
|
470
|
+
'import type { LoaderArgs, RouteComponentProps } from "@pracht/core";',
|
|
471
|
+
"",
|
|
472
|
+
'export const RENDER_MODE = "ssg";',
|
|
473
|
+
"",
|
|
474
|
+
"export async function loader(_args: LoaderArgs) {",
|
|
475
|
+
" return {",
|
|
476
|
+
` adapter: ${JSON.stringify(adapter.label)},`,
|
|
477
|
+
" steps: [",
|
|
478
|
+
' "Edit src/pages/index.tsx to change this page.",',
|
|
479
|
+
' "Add more pages in src/pages/.",',
|
|
480
|
+
' "Add API handlers in src/api/*.ts.",',
|
|
481
|
+
" ],",
|
|
482
|
+
" };",
|
|
483
|
+
"}",
|
|
484
|
+
"",
|
|
485
|
+
"export function Component({ data }: RouteComponentProps<typeof loader>) {",
|
|
486
|
+
" return (",
|
|
487
|
+
" <section>",
|
|
488
|
+
' <p style={{ color: "#555", marginBottom: "8px" }}>Starter ready.</p>',
|
|
489
|
+
' <h1 style={{ fontSize: "2.5rem", lineHeight: 1.1, margin: "0 0 16px" }}>Your pracht app is up and running.</h1>',
|
|
490
|
+
' <p style={{ fontSize: "1.1rem", lineHeight: 1.6, marginBottom: "24px" }}>',
|
|
491
|
+
" This starter is configured for <strong>{data.adapter}</strong>.",
|
|
492
|
+
" </p>",
|
|
493
|
+
' <ul style={{ lineHeight: 1.8, paddingLeft: "20px" }}>',
|
|
494
|
+
" {data.steps.map((step) => (",
|
|
495
|
+
" <li key={step}>{step}</li>",
|
|
496
|
+
" ))}",
|
|
497
|
+
" </ul>",
|
|
498
|
+
' <p style={{ marginTop: "24px" }}>',
|
|
499
|
+
" Check <code>/api/health</code> for a simple API route.",
|
|
500
|
+
" </p>",
|
|
501
|
+
" </section>",
|
|
502
|
+
" );",
|
|
503
|
+
"}",
|
|
504
|
+
"",
|
|
505
|
+
].join("\n");
|
|
506
|
+
}
|
|
507
|
+
|
|
382
508
|
function createHealthRoute(adapter) {
|
|
383
509
|
return [
|
|
384
510
|
"export function GET() {",
|
|
@@ -426,7 +552,7 @@ function createCloudflareEnvDeclaration() {
|
|
|
426
552
|
].join("\n");
|
|
427
553
|
}
|
|
428
554
|
|
|
429
|
-
function createReadme({ adapter, packageManager, projectName }) {
|
|
555
|
+
function createReadme({ adapter, packageManager, projectName, router }) {
|
|
430
556
|
const installCommand = packageManager === "npm" ? "npm install" : `${packageManager} install`;
|
|
431
557
|
const devCommand = packageManager === "npm" ? "npm run dev" : `${packageManager} dev`;
|
|
432
558
|
const previewCommand = packageManager === "npm" ? "npm run preview" : `${packageManager} preview`;
|
|
@@ -461,8 +587,16 @@ function createReadme({ adapter, packageManager, projectName }) {
|
|
|
461
587
|
lines.push("");
|
|
462
588
|
lines.push("## Files");
|
|
463
589
|
lines.push("");
|
|
464
|
-
|
|
465
|
-
|
|
590
|
+
|
|
591
|
+
if (router === "pages") {
|
|
592
|
+
lines.push("- `src/pages/` contains your file-system routes.");
|
|
593
|
+
lines.push("- `src/pages/_app.tsx` is the app shell.");
|
|
594
|
+
lines.push("- `src/pages/index.tsx` is the home page.");
|
|
595
|
+
} else {
|
|
596
|
+
lines.push("- `src/routes.ts` defines your app manifest.");
|
|
597
|
+
lines.push("- `src/routes/home.tsx` is the first page.");
|
|
598
|
+
}
|
|
599
|
+
|
|
466
600
|
lines.push("- `src/api/health.ts` is a sample API route.");
|
|
467
601
|
|
|
468
602
|
return `${lines.join("\n")}\n`;
|
|
@@ -514,7 +648,7 @@ function printHelp() {
|
|
|
514
648
|
console.log(`create-pracht
|
|
515
649
|
|
|
516
650
|
Usage:
|
|
517
|
-
create-pracht [directory] [--adapter=node|cf|vercel] [--skip-install]
|
|
651
|
+
create-pracht [directory] [--adapter=node|cf|vercel] [--router=manifest|pages] [--skip-install]
|
|
518
652
|
`);
|
|
519
653
|
}
|
|
520
654
|
|