@ykdz/template 0.0.1 → 0.0.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.
Files changed (101) hide show
  1. package/dist/src/cli.d.ts.map +1 -0
  2. package/dist/{cli.js → src/cli.js} +126 -128
  3. package/dist/{declarations.d.ts → src/declarations.d.ts} +1 -2
  4. package/dist/src/declarations.d.ts.map +1 -0
  5. package/dist/{declarations.js → src/declarations.js} +2 -109
  6. package/dist/src/dependency-catalog.d.ts +10 -0
  7. package/dist/src/dependency-catalog.d.ts.map +1 -0
  8. package/dist/src/dependency-catalog.js +63 -0
  9. package/dist/src/devcontainer.d.ts +18 -0
  10. package/dist/src/devcontainer.d.ts.map +1 -0
  11. package/dist/src/devcontainer.js +24 -0
  12. package/dist/src/editor-customization.d.ts +13 -0
  13. package/dist/src/editor-customization.d.ts.map +1 -0
  14. package/dist/src/editor-customization.js +101 -0
  15. package/dist/src/generation-context.d.ts +24 -0
  16. package/dist/src/generation-context.d.ts.map +1 -0
  17. package/dist/src/generation-context.js +12 -0
  18. package/dist/src/module-graph.d.ts +31 -0
  19. package/dist/src/module-graph.d.ts.map +1 -0
  20. package/dist/src/module-graph.js +48 -0
  21. package/dist/src/next-step-instructions.d.ts +21 -0
  22. package/dist/src/next-step-instructions.d.ts.map +1 -0
  23. package/dist/src/next-step-instructions.js +89 -0
  24. package/dist/src/package-addition.d.ts.map +1 -0
  25. package/dist/src/package-addition.js +188 -0
  26. package/dist/src/preset-projection.d.ts +66 -0
  27. package/dist/src/preset-projection.d.ts.map +1 -0
  28. package/dist/src/preset-projection.js +7 -0
  29. package/dist/src/project-github.d.ts +29 -0
  30. package/dist/src/project-github.d.ts.map +1 -0
  31. package/dist/src/project-github.js +63 -0
  32. package/dist/src/renderer.d.ts.map +1 -0
  33. package/dist/{renderer.js → src/renderer.js} +0 -3
  34. package/dist/src/runtime-paths.d.ts +2 -0
  35. package/dist/src/runtime-paths.d.ts.map +1 -0
  36. package/dist/src/runtime-paths.js +11 -0
  37. package/dist/src/toolchain-resolution.d.ts +35 -0
  38. package/dist/src/toolchain-resolution.d.ts.map +1 -0
  39. package/dist/src/toolchain-resolution.js +192 -0
  40. package/dist/templates/hono-api/projection.d.ts +7 -0
  41. package/dist/templates/hono-api/projection.d.ts.map +1 -0
  42. package/dist/templates/hono-api/projection.js +377 -0
  43. package/dist/templates/projection-plans.d.ts +8 -0
  44. package/dist/templates/projection-plans.d.ts.map +1 -0
  45. package/dist/templates/projection-plans.js +94 -0
  46. package/dist/templates/registry.d.ts +8 -0
  47. package/dist/templates/registry.d.ts.map +1 -0
  48. package/dist/templates/registry.js +42 -0
  49. package/dist/templates/rust-bin/projection.d.ts +10 -0
  50. package/dist/templates/rust-bin/projection.d.ts.map +1 -0
  51. package/dist/templates/rust-bin/projection.js +282 -0
  52. package/dist/templates/ts-lib/projection.d.ts +7 -0
  53. package/dist/templates/ts-lib/projection.d.ts.map +1 -0
  54. package/dist/templates/ts-lib/projection.js +372 -0
  55. package/dist/templates/vue-app/projection.d.ts +7 -0
  56. package/dist/templates/vue-app/projection.d.ts.map +1 -0
  57. package/dist/templates/vue-app/projection.js +594 -0
  58. package/dist/templates/vue-hono-app/projection.d.ts +9 -0
  59. package/dist/templates/vue-hono-app/projection.d.ts.map +1 -0
  60. package/dist/templates/vue-hono-app/projection.js +589 -0
  61. package/package.json +30 -24
  62. package/pnpm-workspace.yaml +34 -0
  63. package/templates/hono-api/projection.ts +453 -0
  64. package/templates/projection-plans.ts +135 -0
  65. package/templates/registry.ts +59 -0
  66. package/templates/rust-bin/.github/dependabot.yml +4 -0
  67. package/templates/rust-bin/.github/workflows/check.yml +6 -1
  68. package/templates/rust-bin/projection.ts +346 -0
  69. package/templates/ts-lib/projection.ts +454 -0
  70. package/templates/vue-app/projection.ts +687 -0
  71. package/templates/vue-app/test/e2e/app.spec.ts +4 -1
  72. package/templates/vue-hono-app/projection.ts +669 -0
  73. package/templates/vue-hono-app/web/test/e2e/app.spec.ts +4 -1
  74. package/dist/cli.d.ts.map +0 -1
  75. package/dist/declarations.d.ts.map +0 -1
  76. package/dist/hono-api.d.ts +0 -2
  77. package/dist/hono-api.d.ts.map +0 -1
  78. package/dist/hono-api.js +0 -208
  79. package/dist/package-addition.d.ts.map +0 -1
  80. package/dist/package-addition.js +0 -560
  81. package/dist/post-commands.d.ts +0 -40
  82. package/dist/post-commands.d.ts.map +0 -1
  83. package/dist/post-commands.js +0 -154
  84. package/dist/renderer.d.ts.map +0 -1
  85. package/dist/rust-bin.d.ts +0 -2
  86. package/dist/rust-bin.d.ts.map +0 -1
  87. package/dist/rust-bin.js +0 -174
  88. package/dist/ts-lib.d.ts +0 -2
  89. package/dist/ts-lib.d.ts.map +0 -1
  90. package/dist/ts-lib.js +0 -181
  91. package/dist/vue-app.d.ts +0 -2
  92. package/dist/vue-app.d.ts.map +0 -1
  93. package/dist/vue-app.js +0 -299
  94. package/dist/vue-hono-app.d.ts +0 -4
  95. package/dist/vue-hono-app.d.ts.map +0 -1
  96. package/dist/vue-hono-app.js +0 -436
  97. package/templates/shared/oxc/package.json +0 -18
  98. package/templates/shared/oxc/tsconfig.json +0 -11
  99. /package/dist/{cli.d.ts → src/cli.d.ts} +0 -0
  100. /package/dist/{package-addition.d.ts → src/package-addition.d.ts} +0 -0
  101. /package/dist/{renderer.d.ts → src/renderer.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":""}
@@ -2,14 +2,12 @@
2
2
  import { readFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { createInterface } from "node:readline/promises";
5
- import { initHonoApiProject } from "./hono-api.js";
6
- import { initRustBinProject } from "./rust-bin.js";
7
- import { initTsLibProject } from "./ts-lib.js";
8
- import { initVueHonoAppProject } from "./vue-hono-app.js";
9
- import { initVueAppProject } from "./vue-app.js";
5
+ import { findBuiltInPresetProjection, projectPresetThroughRegistry, } from "../templates/registry.js";
6
+ import { blueprintJsonSchema, builtInPresets, presetFileJsonSchema, validatePresetFile, validateProjectBlueprint, } from "./declarations.js";
7
+ import { assembleGenerationContext, } from "./generation-context.js";
10
8
  import { addPackage } from "./package-addition.js";
11
- import { planPostCommands, runPostCommands } from "./post-commands.js";
12
- import { blueprintJsonSchema, builtInPresets, presetFileJsonSchema, validatePresetFile, validateProjectBlueprint } from "./declarations.js";
9
+ import { planNextStepInstructionsForProjection, } from "./preset-projection.js";
10
+ import { resolveToolchainVersions, } from "./toolchain-resolution.js";
13
11
  function usage() {
14
12
  return [
15
13
  "Usage:",
@@ -26,9 +24,8 @@ function usage() {
26
24
  " --name <name> Package name to add",
27
25
  " --scope <name> Package scope for workspace package names",
28
26
  " --yes Accept defaults for non-interactive generation",
29
- " --ready Run template-maintained Post Commands after writing files",
30
27
  " --dry-run Print the planned generation without writing files",
31
- " --json Print machine-readable output"
28
+ " --json Print machine-readable output",
32
29
  ].join("\n");
33
30
  }
34
31
  function printJson(value) {
@@ -49,12 +46,14 @@ async function readJsonDeclaration(filePath) {
49
46
  }
50
47
  }
51
48
  function formatValidationIssues(issues) {
52
- return issues.map((issue) => ` - ${issue.path}: ${issue.message}`).join("\n");
49
+ return issues
50
+ .map((issue) => ` - ${issue.path}: ${issue.message}`)
51
+ .join("\n");
53
52
  }
54
53
  function formatPresetCatalog() {
55
54
  return [
56
55
  "Built-in presets",
57
- ...builtInPresets.map((preset) => ` ${preset.name.padEnd(8)} ${preset.title} (${preset.generation}) - ${preset.description}`)
56
+ ...builtInPresets.map((preset) => ` ${preset.name.padEnd(8)} ${preset.title} (${preset.generation}) - ${preset.description}`),
58
57
  ].join("\n");
59
58
  }
60
59
  function parseInitOptions(args) {
@@ -63,7 +62,6 @@ function parseInitOptions(args) {
63
62
  let yes = false;
64
63
  let dryRun = false;
65
64
  let json = false;
66
- let ready = false;
67
65
  let scope;
68
66
  for (let index = 2; index < args.length; index += 1) {
69
67
  const arg = args[index];
@@ -75,10 +73,6 @@ function parseInitOptions(args) {
75
73
  dryRun = true;
76
74
  continue;
77
75
  }
78
- if (arg === "--ready") {
79
- ready = true;
80
- continue;
81
- }
82
76
  if (arg === "--json") {
83
77
  json = true;
84
78
  continue;
@@ -106,7 +100,7 @@ function parseInitOptions(args) {
106
100
  if (!dir) {
107
101
  throw new Error("init requires a target directory");
108
102
  }
109
- return { dir, preset, yes, dryRun, json, ready, scope };
103
+ return { dir, preset, yes, dryRun, json, scope };
110
104
  }
111
105
  function normalizeNpmScope(value) {
112
106
  if (value !== value.trim() || /\s/.test(value)) {
@@ -121,35 +115,40 @@ function normalizeNpmScope(value) {
121
115
  function supportedPreset(name) {
122
116
  const preset = builtInPresets.find((candidate) => candidate.name === name && candidate.generation === "supported");
123
117
  if (!preset) {
124
- throw new Error("Only the ts-lib, hono-api, vue-app, vue-hono-app, and rust-bin presets are supported in this version");
118
+ throw new Error(formatSupportedPresetError());
125
119
  }
126
120
  return preset;
127
121
  }
128
- function projectNameFromDir(targetDir) {
129
- return path.basename(path.resolve(targetDir));
122
+ function formatSupportedPresetError() {
123
+ const supportedPresetNames = builtInPresets
124
+ .filter((preset) => preset.generation === "supported")
125
+ .map((preset) => preset.name);
126
+ return `Only the ${formatList(supportedPresetNames)} presets are supported in this version`;
130
127
  }
131
- function scopeFromOptions(projectName, scope) {
132
- return scope ?? projectName;
128
+ function formatList(values) {
129
+ if (values.length <= 1) {
130
+ return values[0] ?? "";
131
+ }
132
+ return `${values.slice(0, -1).join(", ")}, and ${values.at(-1)}`;
133
133
  }
134
134
  function blueprintForInit(options) {
135
135
  const preset = supportedPreset(options.preset);
136
- const projectName = projectNameFromDir(options.dir);
137
- const packageScope = scopeFromOptions(projectName, options.scope);
136
+ const projection = findBuiltInPresetProjection(preset.name);
137
+ if (projection) {
138
+ return projection.blueprint({
139
+ targetDir: options.dir,
140
+ scope: options.scope,
141
+ });
142
+ }
138
143
  const blueprint = {
139
144
  schemaVersion: 1,
140
145
  preset: preset.name,
141
146
  projectKind: preset.supportedProjectKinds[0],
142
- features: [...preset.features]
147
+ features: [...preset.features],
143
148
  };
144
149
  if (preset.supportedPackageManagers[0]) {
145
150
  blueprint.packageManager = preset.supportedPackageManagers[0];
146
151
  }
147
- if (preset.name === "vue-hono-app") {
148
- blueprint.packages = [
149
- { name: `@${packageScope}/web`, path: "apps/web" },
150
- { name: `@${packageScope}/api`, path: "apps/api" }
151
- ];
152
- }
153
152
  return blueprint;
154
153
  }
155
154
  function formatBlueprintSummary(targetDir, blueprint) {
@@ -157,7 +156,7 @@ function formatBlueprintSummary(targetDir, blueprint) {
157
156
  "Project Blueprint",
158
157
  ` Target: ${targetDir}`,
159
158
  ` Preset: ${blueprint.preset}`,
160
- ` Project kind: ${blueprint.projectKind}`
159
+ ` Project kind: ${blueprint.projectKind}`,
161
160
  ];
162
161
  if (blueprint.packageManager) {
163
162
  lines.push(` Package manager: ${blueprint.packageManager}`);
@@ -171,103 +170,95 @@ function formatBlueprintSummary(targetDir, blueprint) {
171
170
  lines.push(` Features: ${blueprint.features.join(", ")}`);
172
171
  return lines.join("\n");
173
172
  }
174
- function formatPostCommand(command) {
175
- return [command.command, ...command.args].join(" ");
176
- }
177
- function formatPostCommandPlan(commands) {
178
- if (commands.length === 0) {
179
- return ["Post Commands:", " (none)"].join("\n");
180
- }
173
+ function formatProjectionNextSteps(targetDir, projectionPlan) {
174
+ const steps = planNextStepInstructionsForProjection({
175
+ targetDir,
176
+ plan: projectionPlan,
177
+ });
181
178
  return [
182
- "Post Commands:",
183
- ...commands.map((command) => ` ${command.label}: ${formatPostCommand(command)}`)
179
+ "Next Step Instructions:",
180
+ ...steps.map((step) => ` ${step.label}: ${step.display}`),
184
181
  ].join("\n");
185
182
  }
186
- function nextStepsForPreset(preset) {
187
- if (preset === "rust-bin") {
188
- return ["cd <target>", "./scripts/check"];
189
- }
190
- return ["cd <target>", "pnpm install", "pnpm run check"];
183
+ function toolchainReport(toolchain) {
184
+ return {
185
+ nodeLtsMajor: toolchain.nodeLtsMajor.value,
186
+ packageManagerPin: toolchain.packageManagerPin.value,
187
+ source: toolchain.source,
188
+ diagnostics: [...toolchain.diagnostics],
189
+ };
191
190
  }
192
- function formatNextSteps(targetDir, preset) {
191
+ function formatToolchainReport(toolchain) {
193
192
  return [
194
- "Next steps:",
195
- ...nextStepsForPreset(preset).map((step) => ` ${step.replace("<target>", targetDir)}`)
193
+ "Toolchain Resolution:",
194
+ ` Source: ${toolchain.source}`,
195
+ ` Node LTS major: ${toolchain.nodeLtsMajor.value}`,
196
+ ` Package Manager Pin: ${toolchain.packageManagerPin.value}`,
197
+ ...toolchain.diagnostics.map((diagnostic) => ` ${diagnostic}`),
196
198
  ].join("\n");
197
199
  }
198
- function postCommandReport(planned, executions, skipped) {
199
- return {
200
- planned: planned.map((command) => ({
201
- id: command.id,
202
- label: command.label,
203
- command: command.command,
204
- args: [...command.args],
205
- cwd: command.cwd
206
- })),
207
- run: executions
208
- .filter((execution) => execution.status === "run")
209
- .map((execution) => ({
210
- id: execution.command.id,
211
- exitCode: execution.exitCode
212
- })),
213
- failed: executions
214
- .filter((execution) => execution.status === "failed")
215
- .map((execution) => ({
216
- id: execution.command.id,
217
- exitCode: execution.exitCode,
218
- error: execution.error
219
- })),
220
- skipped
221
- };
222
- }
223
- function failedPostCommand(executions) {
224
- return executions.find((execution) => execution.status === "failed");
200
+ function toolchainResolutionSourceFromEnv() {
201
+ if (process.env.TEMPLATE_TOOLCHAIN_RESOLUTION === "online" ||
202
+ process.env.TEMPLATE_TOOLCHAIN_RESOLUTION === "bundled-fallback") {
203
+ return process.env.TEMPLATE_TOOLCHAIN_RESOLUTION;
204
+ }
205
+ return undefined;
225
206
  }
226
- function skippedPostCommands(commands, reason) {
227
- return commands.map((command) => ({ id: command.id, reason }));
207
+ async function generationContextForInit(options, blueprint) {
208
+ if (!findBuiltInPresetProjection(options.preset)) {
209
+ return undefined;
210
+ }
211
+ return assembleGenerationContext({
212
+ targetDir: options.dir,
213
+ blueprint,
214
+ toolchain: await resolveToolchainVersions({
215
+ source: toolchainResolutionSourceFromEnv(),
216
+ nodeReleaseIndexUrl: process.env.TEMPLATE_TOOLCHAIN_NODE_RELEASE_INDEX_URL,
217
+ pnpmRegistryUrl: process.env.TEMPLATE_TOOLCHAIN_PNPM_REGISTRY_URL,
218
+ }),
219
+ });
228
220
  }
229
- async function generateInitProject(options) {
230
- if (options.preset === "ts-lib") {
231
- await initTsLibProject(options.dir);
232
- return;
233
- }
234
- if (options.preset === "hono-api") {
235
- await initHonoApiProject(options.dir);
236
- return;
237
- }
238
- if (options.preset === "vue-app") {
239
- await initVueAppProject(options.dir);
240
- return;
241
- }
242
- if (options.preset === "vue-hono-app") {
243
- await initVueHonoAppProject(options.dir, { scope: options.scope });
244
- return;
245
- }
246
- if (options.preset === "rust-bin") {
247
- await initRustBinProject(options.dir);
221
+ async function generateInitProject(options, generationContext) {
222
+ const projection = findBuiltInPresetProjection(options.preset);
223
+ if (projection) {
224
+ if (!generationContext) {
225
+ throw new Error(`Missing Generation Context for Preset Projection: ${options.preset}`);
226
+ }
227
+ const plan = projection.project(generationContext);
228
+ await projection.render({ targetDir: options.dir, plan });
248
229
  return;
249
230
  }
250
- throw new Error("Only the ts-lib, hono-api, vue-app, vue-hono-app, and rust-bin presets are supported in this version");
231
+ throw new Error(formatSupportedPresetError());
251
232
  }
252
- function printInitComplete(options, blueprint, report) {
253
- const preset = blueprint.preset;
233
+ function printInitComplete(options, blueprint, generationContext) {
234
+ const projectionPlan = generationContext
235
+ ? projectPresetThroughRegistry(generationContext)
236
+ : undefined;
237
+ if (!projectionPlan) {
238
+ throw new Error(`Missing Preset Projection plan: ${blueprint.preset}`);
239
+ }
240
+ const nextSteps = planNextStepInstructionsForProjection({
241
+ targetDir: options.dir,
242
+ plan: projectionPlan,
243
+ });
254
244
  if (options.json) {
255
245
  printJson({
256
246
  command: "init",
257
247
  dryRun: false,
258
- ready: options.ready,
259
248
  targetDir: options.dir,
260
249
  blueprint,
261
- postCommands: report,
262
- nextSteps: nextStepsForPreset(preset).map((step) => step.replace("<target>", options.dir))
250
+ toolchain: generationContext
251
+ ? toolchainReport(generationContext.toolchain)
252
+ : undefined,
253
+ nextSteps,
263
254
  });
264
255
  return;
265
256
  }
266
257
  console.log(`Initialized ${options.preset} project in ${options.dir}`);
267
- if (options.ready) {
268
- console.log(formatPostCommandPlan(report.planned));
258
+ if (generationContext) {
259
+ console.log(formatToolchainReport(generationContext.toolchain));
269
260
  }
270
- console.log(formatNextSteps(options.dir, preset));
261
+ console.log(formatProjectionNextSteps(options.dir, projectionPlan));
271
262
  }
272
263
  function isInteractiveTerminal() {
273
264
  return Boolean(process.stdin.isTTY && process.stdout.isTTY);
@@ -276,11 +267,12 @@ async function confirmInit(targetDir, blueprint) {
276
267
  console.log(formatBlueprintSummary(targetDir, blueprint));
277
268
  const readline = createInterface({
278
269
  input: process.stdin,
279
- output: process.stdout
270
+ output: process.stdout,
280
271
  });
281
272
  try {
282
273
  const answer = await readline.question("Generate this project? [y/N] ");
283
- return answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes";
274
+ return (answer.trim().toLowerCase() === "y" ||
275
+ answer.trim().toLowerCase() === "yes");
284
276
  }
285
277
  finally {
286
278
  readline.close();
@@ -365,23 +357,35 @@ async function main(args) {
365
357
  const options = parseInitOptions(args);
366
358
  const blueprint = blueprintForInit(options);
367
359
  if (options.dryRun) {
368
- const postCommandPlan = planPostCommands({
369
- preset: blueprint.preset,
370
- targetDir: options.dir
360
+ const generationContext = await generationContextForInit(options, blueprint);
361
+ const projectionPlan = generationContext
362
+ ? projectPresetThroughRegistry(generationContext)
363
+ : undefined;
364
+ if (!projectionPlan) {
365
+ throw new Error(`Missing Preset Projection plan: ${blueprint.preset}`);
366
+ }
367
+ const nextSteps = planNextStepInstructionsForProjection({
368
+ targetDir: options.dir,
369
+ plan: projectionPlan,
371
370
  });
372
371
  if (options.json) {
373
372
  printJson({
374
373
  command: "init",
375
374
  dryRun: true,
376
- ready: options.ready,
377
375
  targetDir: options.dir,
378
376
  blueprint,
379
- postCommands: postCommandReport(postCommandPlan.commands, [], skippedPostCommands(postCommandPlan.commands, "dry-run"))
377
+ toolchain: generationContext
378
+ ? toolchainReport(generationContext.toolchain)
379
+ : undefined,
380
+ nextSteps,
380
381
  });
381
382
  return;
382
383
  }
383
384
  console.log(formatBlueprintSummary(options.dir, blueprint));
384
- console.log(formatPostCommandPlan(postCommandPlan.commands));
385
+ if (generationContext) {
386
+ console.log(formatToolchainReport(generationContext.toolchain));
387
+ }
388
+ console.log(formatProjectionNextSteps(options.dir, projectionPlan));
385
389
  return;
386
390
  }
387
391
  if (!options.yes && (options.json || !isInteractiveTerminal())) {
@@ -390,24 +394,18 @@ async function main(args) {
390
394
  if (!options.yes && !(await confirmInit(options.dir, blueprint))) {
391
395
  throw new Error("Init cancelled");
392
396
  }
393
- const postCommandPlan = planPostCommands({
394
- preset: blueprint.preset,
395
- targetDir: options.dir
396
- });
397
- await generateInitProject(options);
398
- const executions = options.ready ? await runPostCommands({ plan: postCommandPlan }) : [];
399
- const failedExecution = failedPostCommand(executions);
400
- printInitComplete(options, blueprint, postCommandReport(postCommandPlan.commands, executions, options.ready
401
- ? []
402
- : skippedPostCommands(postCommandPlan.commands, "ready-mode-not-requested")));
403
- if (failedExecution) {
404
- throw new Error(failedExecution.error);
405
- }
397
+ const generationContext = await generationContextForInit(options, blueprint);
398
+ await generateInitProject(options, generationContext);
399
+ printInitComplete(options, blueprint, generationContext);
406
400
  return;
407
401
  }
408
402
  if (command === "add" && args[1] === "package") {
409
403
  const options = parseAddPackageOptions(args);
410
- await addPackage({ cwd: process.cwd(), preset: options.preset, name: options.name });
404
+ await addPackage({
405
+ cwd: process.cwd(),
406
+ preset: options.preset,
407
+ name: options.name,
408
+ });
411
409
  console.log(`Added ${options.preset} package ${options.name}`);
412
410
  return;
413
411
  }
@@ -1,7 +1,6 @@
1
1
  import * as v from "valibot";
2
- export type PresetName = "ts-lib" | "hono-api" | "vue-app" | "vue-hono-app" | "rust-bin" | "ts-app" | "node-cli";
3
2
  export type BuiltInPreset = {
4
- name: PresetName;
3
+ name: string;
5
4
  title: string;
6
5
  description: string;
7
6
  generation: "supported" | "future";
@@ -0,0 +1 @@
1
+ {"version":3,"file":"declarations.d.ts","sourceRoot":"","sources":["../../src/declarations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAC;AAG7B,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,WAAW,GAAG,QAAQ,CAAC;IACnC,wBAAwB,EAAE,SAAS,cAAc,EAAE,CAAC;IACpD,qBAAqB,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9C,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,eAAe,CAAC;AAE7D,MAAM,MAAM,WAAW,GACnB,cAAc,GACd,iBAAiB,GACjB,mBAAmB,GACnB,YAAY,GACZ,aAAa,GACb,cAAc,GACd,gBAAgB,GAChB,YAAY,GACZ,gBAAgB,GAChB,YAAY,GACZ,uBAAuB,CAAC;AAE5B,MAAM,MAAM,UAAU,GAAG;IACvB,aAAa,EAAE,CAAC,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,wBAAwB,EAAE,cAAc,EAAE,CAAC;IAC3C,qBAAqB,EAAE,WAAW,EAAE,CAAC;IACrC,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,aAAa,EAAE,CAAC,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC;IACzB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;CAC7B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,SAAS,aAAa,EAA0B,CAAC;AAgB9E,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCvB,CAAC;AAEX,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BtB,CAAC;AAOX,eAAO,MAAM,gBAAgB;;;;;;;;aAQ3B,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;aAcjC,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACtB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,eAAe,EAAE,CAAA;CAAE,CAAC;AAgD7C,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAAC,UAAU,CAAC,CA2B/E;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAEzE;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,GACb,gBAAgB,CAAC,gBAAgB,CAAC,CAqFpC"}
@@ -1,113 +1,6 @@
1
1
  import * as v from "valibot";
2
- export const builtInPresets = [
3
- {
4
- name: "ts-lib",
5
- title: "TypeScript library",
6
- description: "Strict TypeScript package with pnpm catalog tooling.",
7
- generation: "supported",
8
- supportedPackageManagers: ["pnpm"],
9
- supportedProjectKinds: ["single-package"],
10
- features: [
11
- "pnpm-catalog",
12
- "oxc-format-lint",
13
- "strict-typescript",
14
- "root-check",
15
- "fix-command",
16
- "devcontainer",
17
- "github-actions",
18
- "dependabot"
19
- ]
20
- },
21
- {
22
- name: "hono-api",
23
- title: "Hono API",
24
- description: "Single-package Hono Node API with strict TypeScript tooling.",
25
- generation: "supported",
26
- supportedPackageManagers: ["pnpm"],
27
- supportedProjectKinds: ["single-package"],
28
- features: [
29
- "pnpm-catalog",
30
- "oxc-format-lint",
31
- "strict-typescript",
32
- "root-check",
33
- "fix-command",
34
- "devcontainer",
35
- "github-actions",
36
- "dependabot"
37
- ]
38
- },
39
- {
40
- name: "vue-app",
41
- title: "Vue app",
42
- description: "Single-package Vue app with Vite, Tailwind, Pinia, and test tooling.",
43
- generation: "supported",
44
- supportedPackageManagers: ["pnpm"],
45
- supportedProjectKinds: ["single-package"],
46
- features: [
47
- "pnpm-catalog",
48
- "oxc-format-lint",
49
- "strict-typescript",
50
- "root-check",
51
- "fix-command",
52
- "devcontainer",
53
- "github-actions",
54
- "dependabot"
55
- ]
56
- },
57
- {
58
- name: "vue-hono-app",
59
- title: "Vue Hono app",
60
- description: "Full-stack Vue and Hono workspace with Hono RPC typing.",
61
- generation: "supported",
62
- supportedPackageManagers: ["pnpm"],
63
- supportedProjectKinds: ["multi-package"],
64
- features: [
65
- "pnpm-catalog",
66
- "oxc-format-lint",
67
- "strict-typescript",
68
- "root-check",
69
- "fix-command",
70
- "devcontainer",
71
- "github-actions",
72
- "dependabot"
73
- ]
74
- },
75
- {
76
- name: "rust-bin",
77
- title: "Rust binary",
78
- description: "Single-package Rust native binary with rustfmt, clippy, and cargo tests.",
79
- generation: "supported",
80
- supportedPackageManagers: [],
81
- supportedProjectKinds: ["single-package"],
82
- features: [
83
- "root-check",
84
- "fix-command",
85
- "devcontainer",
86
- "github-actions",
87
- "dependabot",
88
- "rustfmt-clippy",
89
- "cargo-test"
90
- ]
91
- },
92
- {
93
- name: "ts-app",
94
- title: "TypeScript application",
95
- description: "Future application preset metadata.",
96
- generation: "future",
97
- supportedPackageManagers: ["pnpm"],
98
- supportedProjectKinds: ["single-package"],
99
- features: []
100
- },
101
- {
102
- name: "node-cli",
103
- title: "Node.js CLI",
104
- description: "Future command-line preset metadata.",
105
- generation: "future",
106
- supportedPackageManagers: ["pnpm"],
107
- supportedProjectKinds: ["single-package"],
108
- features: []
109
- }
110
- ];
2
+ import { builtInPresetMetadata } from "../templates/registry.js";
3
+ export const builtInPresets = builtInPresetMetadata;
111
4
  const featureNames = [
112
5
  "pnpm-catalog",
113
6
  "oxc-format-lint",
@@ -0,0 +1,10 @@
1
+ export type TemplateDependencyCatalog = Record<string, string>;
2
+ export type GeneratedDependencyCatalogOptions = {
3
+ readonly dependencies: readonly string[];
4
+ readonly packages?: readonly string[];
5
+ readonly allowBuilds?: Record<string, boolean>;
6
+ };
7
+ export declare function loadTemplateDependencyCatalog(): TemplateDependencyCatalog;
8
+ export declare function selectTemplateDependencyCatalogEntries(dependencies: readonly string[], catalog?: TemplateDependencyCatalog): TemplateDependencyCatalog;
9
+ export declare function renderGeneratedPnpmWorkspaceYaml(options: GeneratedDependencyCatalogOptions): string;
10
+ //# sourceMappingURL=dependency-catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependency-catalog.d.ts","sourceRoot":"","sources":["../../src/dependency-catalog.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE/D,MAAM,MAAM,iCAAiC,GAAG;IAC9C,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChD,CAAC;AA2CF,wBAAgB,6BAA6B,IAAI,yBAAyB,CAOzE;AAED,wBAAgB,sCAAsC,CACpD,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,OAAO,GAAE,yBAA2D,GACnE,yBAAyB,CAe3B;AAMD,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,iCAAiC,GACzC,MAAM,CA4BR"}
@@ -0,0 +1,63 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ function templateRepositoryRoot() {
5
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
6
+ const candidates = [
7
+ path.resolve(moduleDir, ".."),
8
+ path.resolve(moduleDir, "..", ".."),
9
+ ];
10
+ return (candidates.find((candidate) => existsSync(path.join(candidate, "pnpm-workspace.yaml"))) ?? candidates[0]);
11
+ }
12
+ function parseTemplateDependencyCatalog(workspaceYaml) {
13
+ const catalog = {};
14
+ const lines = workspaceYaml.split(/\r?\n/);
15
+ const catalogStart = lines.findIndex((line) => line === "catalog:");
16
+ if (catalogStart === -1) {
17
+ return catalog;
18
+ }
19
+ for (const line of lines.slice(catalogStart + 1)) {
20
+ if (line.length > 0 && !line.startsWith(" ")) {
21
+ break;
22
+ }
23
+ const match = /^ (?:"([^"]+)"|([^:]+)): (.+)$/.exec(line);
24
+ if (!match) {
25
+ continue;
26
+ }
27
+ catalog[match[1] ?? match[2]] = match[3];
28
+ }
29
+ return catalog;
30
+ }
31
+ export function loadTemplateDependencyCatalog() {
32
+ return parseTemplateDependencyCatalog(readFileSync(path.join(templateRepositoryRoot(), "pnpm-workspace.yaml"), "utf8"));
33
+ }
34
+ export function selectTemplateDependencyCatalogEntries(dependencies, catalog = loadTemplateDependencyCatalog()) {
35
+ const selected = {};
36
+ for (const dependency of [...dependencies].sort()) {
37
+ const version = catalog[dependency];
38
+ if (version === undefined) {
39
+ throw new Error(`Template Dependency Catalog is missing dependency: ${dependency}`);
40
+ }
41
+ selected[dependency] = version;
42
+ }
43
+ return selected;
44
+ }
45
+ function renderCatalogKey(key) {
46
+ return key.startsWith("@") ? JSON.stringify(key) : key;
47
+ }
48
+ export function renderGeneratedPnpmWorkspaceYaml(options) {
49
+ const catalog = selectTemplateDependencyCatalogEntries(options.dependencies);
50
+ const packages = options.packages ?? ["."];
51
+ const lines = [
52
+ "packages:",
53
+ ...packages.map((workspacePackage) => ` - ${workspacePackage}`),
54
+ "",
55
+ ];
56
+ if (options.allowBuilds) {
57
+ lines.push("allowBuilds:", ...Object.entries(options.allowBuilds)
58
+ .sort(([left], [right]) => left.localeCompare(right))
59
+ .map(([dependency, allowed]) => ` ${dependency}: ${allowed}`), "");
60
+ }
61
+ lines.push("catalog:", ...Object.entries(catalog).map(([dependency, version]) => ` ${renderCatalogKey(dependency)}: ${version}`), "");
62
+ return lines.join("\n");
63
+ }
@@ -0,0 +1,18 @@
1
+ export type DevcontainerNodeFeatureOptions = {
2
+ readonly nodeVersion: string;
3
+ readonly packageManagerPin: string;
4
+ };
5
+ export type DevcontainerNodeFeature = {
6
+ readonly version: string;
7
+ readonly pnpmVersion: string;
8
+ };
9
+ export declare function pnpmVersionFromPackageManagerPin(packageManagerPin: string): string;
10
+ export declare function devcontainerNodeFeature(options: DevcontainerNodeFeatureOptions): Record<"ghcr.io/devcontainers/features/node:1", DevcontainerNodeFeature>;
11
+ export declare function nodePnpmDevcontainer(options: {
12
+ readonly name: string;
13
+ readonly nodeVersion: string;
14
+ readonly packageManagerPin: string;
15
+ readonly extensions: readonly string[];
16
+ readonly settings?: Record<string, unknown>;
17
+ }): Record<string, unknown>;
18
+ //# sourceMappingURL=devcontainer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devcontainer.d.ts","sourceRoot":"","sources":["../../src/devcontainer.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,8BAA8B,GAAG;IAC3C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,wBAAgB,gCAAgC,CAC9C,iBAAiB,EAAE,MAAM,GACxB,MAAM,CAER;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,8BAA8B,GACtC,MAAM,CAAC,uCAAuC,EAAE,uBAAuB,CAAC,CAO1E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7C,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAY1B"}
@@ -0,0 +1,24 @@
1
+ export function pnpmVersionFromPackageManagerPin(packageManagerPin) {
2
+ return packageManagerPin.replace(/^pnpm@/, "");
3
+ }
4
+ export function devcontainerNodeFeature(options) {
5
+ return {
6
+ "ghcr.io/devcontainers/features/node:1": {
7
+ version: options.nodeVersion,
8
+ pnpmVersion: pnpmVersionFromPackageManagerPin(options.packageManagerPin),
9
+ },
10
+ };
11
+ }
12
+ export function nodePnpmDevcontainer(options) {
13
+ return {
14
+ name: options.name,
15
+ image: `mcr.microsoft.com/devcontainers/typescript-node:${options.nodeVersion}`,
16
+ features: devcontainerNodeFeature(options),
17
+ customizations: {
18
+ vscode: {
19
+ extensions: options.extensions,
20
+ ...(options.settings ? { settings: options.settings } : {}),
21
+ },
22
+ },
23
+ };
24
+ }