create-daloy 0.1.1 → 0.1.2

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 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:
@@ -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 TEMPLATES = ["node-basic", "vercel-edge", "cloudflare-worker"];
18
- const PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"];
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")} scaffold a DaloyJS project
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((c, i) => ` ${color(COLORS.dim, `${i + 1})`)} ${c}${c === defaultChoice ? color(COLORS.dim, " (default)") : ""}`)
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.includes(raw)) return raw;
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, "\n create-daloy"));
213
- console.log(color(COLORS.dim, ` https://daloyjs.dev\n`));
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, "Pick a template:", TEMPLATES, "node-basic") : "node-basic";
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 ?? detectedPm;
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.dim, `\n Scaffolding ${color(COLORS.bold, projectName)} from template ${color(COLORS.bold, template)}...`));
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
- console.log(color(COLORS.green, " git repository initialized"));
389
+ logStep("Git repository initialized");
301
390
  } else {
302
- console.warn(color(COLORS.yellow, " ! git init failed; continuing"));
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
- console.warn(
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
- console.log(color(COLORS.green, " dependencies installed"));
401
+ logStep("Dependencies installed", packageManager);
315
402
  }
316
403
  }
317
404
 
318
- console.log(color(COLORS.green, `\n Done.`));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-daloy",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Scaffold a new DaloyJS project. Run with `pnpm create daloy`, `npm create daloy@latest`, `yarn create daloy`, or `bun create daloy`.",
5
5
  "type": "module",
6
6
  "license": "MIT",