create-daloy 0.1.1 → 0.1.3
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 +4 -0
- package/bin/create-daloy.mjs +106 -26
- package/package.json +1 -1
- package/templates/node-basic/src/index.ts +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ The CLI is interactive when arguments are missing. It will ask you for:
|
|
|
14
14
|
|
|
15
15
|
- A project directory name (defaults to `my-daloy-app`)
|
|
16
16
|
- A template (`node-basic`, `vercel-edge`, or `cloudflare-worker`)
|
|
17
|
+
- A package manager (`pnpm`, `npm`, `yarn`, or `bun`)
|
|
17
18
|
- Whether to install dependencies
|
|
18
19
|
- Whether to initialize a git repository
|
|
19
20
|
|
|
@@ -33,6 +34,7 @@ pnpm create daloy@latest my-api \
|
|
|
33
34
|
| --- | --- |
|
|
34
35
|
| `--template <name>` | `node-basic` (default), `vercel-edge`, or `cloudflare-worker`. |
|
|
35
36
|
| `--package-manager <pm>` | `pnpm` (default), `npm`, `yarn`, or `bun`. |
|
|
37
|
+
| `--list-templates` | Print available templates with descriptions. |
|
|
36
38
|
| `--install` / `--no-install` | Install dependencies after scaffolding. Defaults to interactive. |
|
|
37
39
|
| `--git` / `--no-git` | Initialize a git repository. Defaults to interactive. |
|
|
38
40
|
| `--force` | Overwrite an existing non-empty directory. |
|
|
@@ -42,6 +44,8 @@ pnpm create daloy@latest my-api \
|
|
|
42
44
|
|
|
43
45
|
## Templates
|
|
44
46
|
|
|
47
|
+
Use `create-daloy --list-templates` to inspect available templates without creating a project.
|
|
48
|
+
|
|
45
49
|
### `node-basic`
|
|
46
50
|
|
|
47
51
|
A production-ready Node.js HTTP server using `@daloyjs/core` with:
|
package/bin/create-daloy.mjs
CHANGED
|
@@ -14,8 +14,33 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
14
14
|
const PKG_ROOT = path.resolve(__dirname, "..");
|
|
15
15
|
const TEMPLATES_DIR = path.join(PKG_ROOT, "templates");
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
|
|
17
|
+
const TEMPLATE_OPTIONS = [
|
|
18
|
+
{
|
|
19
|
+
value: "node-basic",
|
|
20
|
+
title: "Node API",
|
|
21
|
+
description: "Traditional REST API with secure defaults and Hey API codegen",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
value: "vercel-edge",
|
|
25
|
+
title: "Vercel Edge",
|
|
26
|
+
description: "Catch-all Edge API route using @daloyjs/core/vercel",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
value: "cloudflare-worker",
|
|
30
|
+
title: "Cloudflare Workers",
|
|
31
|
+
description: "Worker entrypoint with wrangler dev/deploy scripts",
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const PACKAGE_MANAGER_OPTIONS = [
|
|
36
|
+
{ value: "pnpm", title: "pnpm", description: "Recommended for DaloyJS projects" },
|
|
37
|
+
{ value: "npm", title: "npm", description: "Use the npm client you already have" },
|
|
38
|
+
{ value: "yarn", title: "Yarn", description: "Classic create workflow" },
|
|
39
|
+
{ value: "bun", title: "Bun", description: "Fast install and runtime tooling" },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const TEMPLATES = TEMPLATE_OPTIONS.map((option) => option.value);
|
|
43
|
+
const PACKAGE_MANAGERS = PACKAGE_MANAGER_OPTIONS.map((option) => option.value);
|
|
19
44
|
|
|
20
45
|
const RENAME_ON_COPY = new Map([
|
|
21
46
|
["_gitignore", ".gitignore"],
|
|
@@ -40,7 +65,7 @@ function color(code, s) {
|
|
|
40
65
|
}
|
|
41
66
|
|
|
42
67
|
function printHelp() {
|
|
43
|
-
console.log(`${color(COLORS.bold, "create-daloy")}
|
|
68
|
+
console.log(`${color(COLORS.bold, "create-daloy")} - scaffold a DaloyJS project
|
|
44
69
|
|
|
45
70
|
Usage:
|
|
46
71
|
pnpm create daloy@latest [project-name] [options]
|
|
@@ -49,6 +74,7 @@ Usage:
|
|
|
49
74
|
Options:
|
|
50
75
|
--template <name> ${TEMPLATES.join(" | ")} (default: node-basic)
|
|
51
76
|
--package-manager <pm> ${PACKAGE_MANAGERS.join(" | ")} (default: pnpm)
|
|
77
|
+
--list-templates Print available templates and exit.
|
|
52
78
|
--install / --no-install Install dependencies after scaffolding.
|
|
53
79
|
--git / --no-git Initialize a git repository.
|
|
54
80
|
--force Overwrite an existing non-empty directory.
|
|
@@ -58,6 +84,14 @@ Options:
|
|
|
58
84
|
`);
|
|
59
85
|
}
|
|
60
86
|
|
|
87
|
+
function printTemplates() {
|
|
88
|
+
console.log(color(COLORS.bold, "Available DaloyJS templates\n"));
|
|
89
|
+
for (const option of TEMPLATE_OPTIONS) {
|
|
90
|
+
console.log(` ${color(COLORS.cyan, option.value.padEnd(18))} ${option.title}`);
|
|
91
|
+
console.log(color(COLORS.dim, ` ${" ".repeat(18)} ${option.description}\n`));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
61
95
|
async function readPkgVersion() {
|
|
62
96
|
try {
|
|
63
97
|
const raw = await readFile(path.join(PKG_ROOT, "package.json"), "utf8");
|
|
@@ -78,12 +112,14 @@ function parseArgs(argv) {
|
|
|
78
112
|
yes: false,
|
|
79
113
|
help: false,
|
|
80
114
|
version: false,
|
|
115
|
+
listTemplates: false,
|
|
81
116
|
};
|
|
82
117
|
const args = [...argv];
|
|
83
118
|
while (args.length) {
|
|
84
119
|
const a = args.shift();
|
|
85
120
|
if (a === "--help" || a === "-h") out.help = true;
|
|
86
121
|
else if (a === "--version" || a === "-v") out.version = true;
|
|
122
|
+
else if (a === "--list-templates") out.listTemplates = true;
|
|
87
123
|
else if (a === "--yes" || a === "-y") out.yes = true;
|
|
88
124
|
else if (a === "--force") out.force = true;
|
|
89
125
|
else if (a === "--install") out.install = true;
|
|
@@ -181,22 +217,63 @@ async function askYesNo(rl, question, defaultYes) {
|
|
|
181
217
|
return answer === "y" || answer === "yes";
|
|
182
218
|
}
|
|
183
219
|
|
|
220
|
+
function optionValue(option) {
|
|
221
|
+
return typeof option === "string" ? option : option.value;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function optionLabel(option) {
|
|
225
|
+
return typeof option === "string" ? option : `${option.title} ${color(COLORS.dim, `(${option.value})`)}`;
|
|
226
|
+
}
|
|
227
|
+
|
|
184
228
|
async function askChoice(rl, question, choices, defaultChoice) {
|
|
229
|
+
const nameWidth = Math.max(...choices.map((choice) => optionLabel(choice).replace(/\x1b\[[0-9;]*m/g, "").length));
|
|
185
230
|
const list = choices
|
|
186
|
-
.map((
|
|
231
|
+
.map((choice, i) => {
|
|
232
|
+
const value = optionValue(choice);
|
|
233
|
+
const label = optionLabel(choice).padEnd(nameWidth);
|
|
234
|
+
const description = typeof choice === "string" ? "" : color(COLORS.dim, ` ${choice.description}`);
|
|
235
|
+
const defaultMarker = value === defaultChoice ? color(COLORS.green, " recommended") : "";
|
|
236
|
+
return ` ${color(COLORS.dim, `${i + 1})`)} ${label}${description}${defaultMarker}`;
|
|
237
|
+
})
|
|
187
238
|
.join("\n");
|
|
188
239
|
console.log(`${color(COLORS.cyan, "?")} ${question}\n${list}`);
|
|
189
240
|
const raw = (await rl.question(` > `)).trim();
|
|
190
241
|
if (raw.length === 0) return defaultChoice;
|
|
191
242
|
const asNumber = Number.parseInt(raw, 10);
|
|
192
243
|
if (Number.isInteger(asNumber) && asNumber >= 1 && asNumber <= choices.length) {
|
|
193
|
-
return choices[asNumber - 1];
|
|
244
|
+
return optionValue(choices[asNumber - 1]);
|
|
194
245
|
}
|
|
195
|
-
if (choices.
|
|
196
|
-
console.error(color(COLORS.red, `Invalid choice. Pick one of: ${choices.join(", ")}`));
|
|
246
|
+
if (choices.some((choice) => optionValue(choice) === raw)) return raw;
|
|
247
|
+
console.error(color(COLORS.red, `Invalid choice. Pick one of: ${choices.map(optionValue).join(", ")}`));
|
|
197
248
|
return askChoice(rl, question, choices, defaultChoice);
|
|
198
249
|
}
|
|
199
250
|
|
|
251
|
+
function logStep(message, detail) {
|
|
252
|
+
const suffix = detail ? color(COLORS.dim, ` ${detail}`) : "";
|
|
253
|
+
console.log(`${color(COLORS.green, " [ok]")} ${message}${suffix}`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function logWarn(message) {
|
|
257
|
+
console.warn(`${color(COLORS.yellow, " [warn]")} ${message}`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function printSummary({ projectName, template, packageManager, installDeps }) {
|
|
261
|
+
console.log(color(COLORS.green, "\nCreated a new DaloyJS project."));
|
|
262
|
+
console.log(`\n ${color(COLORS.bold, "Project")} ${projectName}`);
|
|
263
|
+
console.log(` ${color(COLORS.bold, "Template")} ${template}`);
|
|
264
|
+
console.log(` ${color(COLORS.bold, "Manager")} ${packageManager}`);
|
|
265
|
+
console.log(`\n ${color(COLORS.bold, "Next steps")}`);
|
|
266
|
+
console.log(` cd ${projectName}`);
|
|
267
|
+
if (!installDeps) console.log(` ${packageManager} install`);
|
|
268
|
+
console.log(` ${packageManager} run dev`);
|
|
269
|
+
console.log(`\n ${color(COLORS.bold, "Useful commands")}`);
|
|
270
|
+
console.log(` ${packageManager} run typecheck`);
|
|
271
|
+
console.log(` ${packageManager} test`);
|
|
272
|
+
if (template === "node-basic") console.log(` ${packageManager} run gen`);
|
|
273
|
+
console.log(`\n ${color(COLORS.dim, "Docs: https://daloyjs.dev/docs")}`);
|
|
274
|
+
console.log(color(COLORS.dim, " Issues: https://github.com/daloyjs/daloy/issues\n"));
|
|
275
|
+
}
|
|
276
|
+
|
|
200
277
|
async function main() {
|
|
201
278
|
const opts = parseArgs(process.argv.slice(2));
|
|
202
279
|
|
|
@@ -208,9 +285,14 @@ async function main() {
|
|
|
208
285
|
console.log(await readPkgVersion());
|
|
209
286
|
process.exit(0);
|
|
210
287
|
}
|
|
288
|
+
if (opts.listTemplates) {
|
|
289
|
+
printTemplates();
|
|
290
|
+
process.exit(0);
|
|
291
|
+
}
|
|
211
292
|
|
|
212
|
-
console.log(color(COLORS.bold, "\
|
|
213
|
-
console.log(color(COLORS.dim,
|
|
293
|
+
console.log(color(COLORS.bold, "\ncreate-daloy"));
|
|
294
|
+
console.log(color(COLORS.dim, "Contract-first REST APIs for Node, Vercel Edge, and Workers"));
|
|
295
|
+
console.log(color(COLORS.dim, "https://daloyjs.dev\n"));
|
|
214
296
|
|
|
215
297
|
const detectedPm = detectPackageManager();
|
|
216
298
|
const interactive = !opts.yes && process.stdin.isTTY && process.stdout.isTTY;
|
|
@@ -241,7 +323,7 @@ async function main() {
|
|
|
241
323
|
|
|
242
324
|
let template = opts.template;
|
|
243
325
|
if (!template) {
|
|
244
|
-
template = rl ? await askChoice(rl, "
|
|
326
|
+
template = rl ? await askChoice(rl, "Choose a starter template:", TEMPLATE_OPTIONS, "node-basic") : "node-basic";
|
|
245
327
|
}
|
|
246
328
|
if (!TEMPLATES.includes(template)) {
|
|
247
329
|
console.error(color(COLORS.red, `Unknown template "${template}". Available: ${TEMPLATES.join(", ")}`));
|
|
@@ -268,7 +350,12 @@ async function main() {
|
|
|
268
350
|
}
|
|
269
351
|
}
|
|
270
352
|
|
|
271
|
-
let packageManager = opts.packageManager
|
|
353
|
+
let packageManager = opts.packageManager;
|
|
354
|
+
if (!packageManager) {
|
|
355
|
+
packageManager = rl
|
|
356
|
+
? await askChoice(rl, "Choose a package manager:", PACKAGE_MANAGER_OPTIONS, detectedPm)
|
|
357
|
+
: detectedPm;
|
|
358
|
+
}
|
|
272
359
|
if (!PACKAGE_MANAGERS.includes(packageManager)) {
|
|
273
360
|
console.error(
|
|
274
361
|
color(COLORS.red, `Unknown --package-manager "${packageManager}". Use one of: ${PACKAGE_MANAGERS.join(", ")}`),
|
|
@@ -288,18 +375,20 @@ async function main() {
|
|
|
288
375
|
|
|
289
376
|
rl?.close();
|
|
290
377
|
|
|
291
|
-
console.log(color(COLORS.
|
|
378
|
+
console.log(color(COLORS.bold, "\nScaffolding"));
|
|
292
379
|
|
|
293
380
|
await mkdir(targetDir, { recursive: true });
|
|
294
381
|
await copyTemplate(templateDir, targetDir);
|
|
382
|
+
logStep("Template copied", template);
|
|
295
383
|
await patchPackageJson(targetDir, projectName);
|
|
384
|
+
logStep("Package metadata written", projectName);
|
|
296
385
|
|
|
297
386
|
if (initGit) {
|
|
298
387
|
const code = await run("git", ["init", "--quiet"], targetDir);
|
|
299
388
|
if (code === 0) {
|
|
300
|
-
|
|
389
|
+
logStep("Git repository initialized");
|
|
301
390
|
} else {
|
|
302
|
-
|
|
391
|
+
logWarn("git init failed; continuing");
|
|
303
392
|
}
|
|
304
393
|
}
|
|
305
394
|
|
|
@@ -307,22 +396,13 @@ async function main() {
|
|
|
307
396
|
console.log(color(COLORS.dim, ` Installing dependencies with ${packageManager}...`));
|
|
308
397
|
const code = await run(packageManager, ["install"], targetDir);
|
|
309
398
|
if (code !== 0) {
|
|
310
|
-
|
|
311
|
-
color(COLORS.yellow, ` ! ${packageManager} install exited with code ${code}; you can retry inside ${projectName}.`),
|
|
312
|
-
);
|
|
399
|
+
logWarn(`${packageManager} install exited with code ${code}; you can retry inside ${projectName}.`);
|
|
313
400
|
} else {
|
|
314
|
-
|
|
401
|
+
logStep("Dependencies installed", packageManager);
|
|
315
402
|
}
|
|
316
403
|
}
|
|
317
404
|
|
|
318
|
-
|
|
319
|
-
console.log(`\n Next steps:\n`);
|
|
320
|
-
console.log(` cd ${projectName}`);
|
|
321
|
-
if (!installDeps) console.log(` ${packageManager} install`);
|
|
322
|
-
console.log(` ${packageManager} run dev`);
|
|
323
|
-
console.log("");
|
|
324
|
-
console.log(color(COLORS.dim, " Docs: https://daloyjs.dev/docs"));
|
|
325
|
-
console.log(color(COLORS.dim, " Issues: https://github.com/daloyjs/daloy/issues\n"));
|
|
405
|
+
printSummary({ projectName, template, packageManager, installDeps });
|
|
326
406
|
} catch (err) {
|
|
327
407
|
rl?.close();
|
|
328
408
|
console.error(color(COLORS.red, `\n Failed: ${(err && err.message) || err}`));
|
package/package.json
CHANGED