@ykdz/template 0.0.4 → 0.0.6

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 (46) hide show
  1. package/dist/src/cli.js +79 -34
  2. package/dist/src/declarations.d.ts +36 -0
  3. package/dist/src/declarations.js +31 -2
  4. package/dist/src/generated-scenarios.d.ts +62 -0
  5. package/dist/src/generated-scenarios.js +361 -0
  6. package/dist/src/module-graph.d.ts +1 -1
  7. package/dist/src/module-graph.js +10 -2
  8. package/dist/src/package-addition-support.d.ts +5 -0
  9. package/dist/src/package-addition-support.js +4 -0
  10. package/dist/src/package-addition.d.ts +1 -0
  11. package/dist/src/package-addition.js +409 -69
  12. package/dist/src/package-linking.d.ts +55 -0
  13. package/dist/src/package-linking.js +116 -0
  14. package/dist/src/preset-projection.d.ts +4 -1
  15. package/dist/src/preset-source.d.ts +430 -0
  16. package/dist/src/preset-source.js +940 -0
  17. package/dist/src/projection-capabilities.d.ts +67 -0
  18. package/dist/src/projection-capabilities.js +2207 -0
  19. package/dist/src/renderer.js +20 -1
  20. package/dist/src/template-boundary-check.d.ts +7 -3
  21. package/dist/src/template-boundary-check.js +104 -10
  22. package/package.json +3 -2
  23. package/templates/hono-api/src/server.ts +1 -1
  24. package/templates/hono-api/test/app.test.ts +1 -1
  25. package/templates/preset-source.json +499 -0
  26. package/templates/ts-lib/src/index.ts +2 -2
  27. package/templates/ts-lib/src/name-schema.ts +3 -0
  28. package/templates/vue-app/playwright.package-addition.config.ts +22 -0
  29. package/templates/vue-app/src/App.vue +1 -1
  30. package/templates/vue-app/test/app.test.ts +1 -1
  31. package/templates/vue-hono-app/web/src/App.vue +2 -2
  32. package/templates/vue-hono-app/web/test/app.test.ts +1 -1
  33. package/dist/templates/hono-api/projection.d.ts +0 -6
  34. package/dist/templates/hono-api/projection.js +0 -538
  35. package/dist/templates/projection-plans.d.ts +0 -7
  36. package/dist/templates/projection-plans.js +0 -94
  37. package/dist/templates/registry.d.ts +0 -7
  38. package/dist/templates/registry.js +0 -42
  39. package/dist/templates/rust-bin/projection.d.ts +0 -9
  40. package/dist/templates/rust-bin/projection.js +0 -390
  41. package/dist/templates/ts-lib/projection.d.ts +0 -6
  42. package/dist/templates/ts-lib/projection.js +0 -523
  43. package/dist/templates/vue-app/projection.d.ts +0 -6
  44. package/dist/templates/vue-app/projection.js +0 -757
  45. package/dist/templates/vue-hono-app/projection.d.ts +0 -8
  46. package/dist/templates/vue-hono-app/projection.js +0 -693
package/dist/src/cli.js CHANGED
@@ -2,27 +2,32 @@
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 { findBuiltInPresetProjection, projectPresetThroughRegistry, } from "../templates/registry.js";
6
5
  import { blueprintJsonSchema, builtInPresets, presetFileJsonSchema, validatePresetFile, validateProjectBlueprint, } from "./declarations.js";
7
6
  import { assembleGenerationContext, } from "./generation-context.js";
8
7
  import { addPackage } from "./package-addition.js";
9
8
  import { planNextStepInstructionsForProjection, } from "./preset-projection.js";
9
+ import { findBuiltInPresetSourceManifestPreset, presetSourceManifestJsonSchema, validateBuiltInPresetSourceManifest, validatePresetSourceManifest, } from "./preset-source.js";
10
+ import { blueprintForPresetSourcePreset, projectPresetSourcePreset, } from "./projection-capabilities.js";
11
+ import { renderNewProject } from "./renderer.js";
10
12
  import { resolveToolchainVersions, } from "./toolchain-resolution.js";
11
13
  function usage() {
12
14
  return [
13
15
  "Usage:",
14
16
  " template init <dir> --preset <name> --yes",
15
- " template add package --preset <name> --name <name> [--path <package-path>]",
17
+ " template add package --preset <name> --name <name> [--path <package-path>] [--link-from <package-path>]...",
16
18
  " template presets",
17
19
  " template schema preset",
20
+ " template schema preset-source",
18
21
  " template schema blueprint",
19
22
  " template preset validate <path>",
23
+ " template preset-source validate <path>",
20
24
  " template blueprint validate <path>",
21
25
  "",
22
26
  "Options:",
23
27
  " --preset <name> Project preset to generate",
24
28
  " --name <name> Package name to add",
25
29
  " --path <path> Two-segment Package Path to add",
30
+ " --link-from <path> Existing consumer Package Path to link from; repeatable",
26
31
  " --scope <name> Package scope for workspace package names",
27
32
  " --yes Accept defaults for non-interactive generation",
28
33
  " --dry-run Print the planned generation without writing files",
@@ -134,23 +139,10 @@ function formatList(values) {
134
139
  }
135
140
  function blueprintForInit(options) {
136
141
  const preset = supportedPreset(options.preset);
137
- const projection = findBuiltInPresetProjection(preset.name);
138
- if (projection) {
139
- return projection.blueprint({
140
- targetDir: options.dir,
141
- scope: options.scope,
142
- });
143
- }
144
- const blueprint = {
145
- schemaVersion: 1,
146
- preset: preset.name,
147
- projectKind: preset.supportedProjectKinds[0],
148
- features: [...preset.features],
149
- };
150
- if (preset.supportedPackageManagers[0]) {
151
- blueprint.packageManager = preset.supportedPackageManagers[0];
152
- }
153
- return blueprint;
142
+ return blueprintForPresetSourcePreset(preset, {
143
+ targetDir: options.dir,
144
+ scope: options.scope,
145
+ });
154
146
  }
155
147
  function formatBlueprintSummary(targetDir, blueprint) {
156
148
  const lines = [
@@ -206,7 +198,8 @@ function toolchainResolutionSourceFromEnv() {
206
198
  return undefined;
207
199
  }
208
200
  async function generationContextForInit(options, blueprint) {
209
- if (!findBuiltInPresetProjection(options.preset)) {
201
+ if (findBuiltInPresetSourceManifestPreset(options.preset)?.projection ===
202
+ undefined) {
210
203
  return undefined;
211
204
  }
212
205
  return assembleGenerationContext({
@@ -220,20 +213,28 @@ async function generationContextForInit(options, blueprint) {
220
213
  });
221
214
  }
222
215
  async function generateInitProject(options, generationContext) {
223
- const projection = findBuiltInPresetProjection(options.preset);
224
- if (projection) {
225
- if (!generationContext) {
226
- throw new Error(`Missing Generation Context for Preset Projection: ${options.preset}`);
227
- }
228
- const plan = projection.project(generationContext);
229
- await projection.render({ targetDir: options.dir, plan });
230
- return;
216
+ const preset = findBuiltInPresetSourceManifestPreset(options.preset);
217
+ if (!preset?.projection) {
218
+ throw new Error(formatSupportedPresetError());
231
219
  }
232
- throw new Error(formatSupportedPresetError());
220
+ if (!generationContext) {
221
+ throw new Error(`Missing Generation Context for Preset Projection: ${options.preset}`);
222
+ }
223
+ const plan = projectPresetSourcePreset({
224
+ preset,
225
+ context: generationContext,
226
+ });
227
+ await renderNewProject({
228
+ sourceRoot: plan.sourceRoot,
229
+ sourceRoots: plan.sourceRoots,
230
+ targetRoot: options.dir,
231
+ operations: [...plan.operations],
232
+ });
233
233
  }
234
234
  function printInitComplete(options, blueprint, generationContext) {
235
- const projectionPlan = generationContext
236
- ? projectPresetThroughRegistry(generationContext)
235
+ const preset = findBuiltInPresetSourceManifestPreset(blueprint.preset);
236
+ const projectionPlan = generationContext && preset?.projection
237
+ ? projectPresetSourcePreset({ preset, context: generationContext })
237
238
  : undefined;
238
239
  if (!projectionPlan) {
239
240
  throw new Error(`Missing Preset Projection plan: ${blueprint.preset}`);
@@ -283,6 +284,7 @@ function parseAddPackageOptions(args) {
283
284
  let preset = "";
284
285
  let name = "";
285
286
  let packagePath;
287
+ const linkFrom = [];
286
288
  for (let index = 2; index < args.length; index += 1) {
287
289
  const arg = args[index];
288
290
  if (arg === "--preset") {
@@ -312,6 +314,15 @@ function parseAddPackageOptions(args) {
312
314
  index += 1;
313
315
  continue;
314
316
  }
317
+ if (arg === "--link-from") {
318
+ const value = args[index + 1];
319
+ if (!value) {
320
+ throw new Error("--link-from requires a value");
321
+ }
322
+ linkFrom.push(value);
323
+ index += 1;
324
+ continue;
325
+ }
315
326
  throw new Error(`Unknown option: ${arg}`);
316
327
  }
317
328
  if (!preset) {
@@ -320,7 +331,7 @@ function parseAddPackageOptions(args) {
320
331
  if (!name) {
321
332
  throw new Error("add package requires --name");
322
333
  }
323
- return { preset, name, path: packagePath };
334
+ return { preset, name, path: packagePath, linkFrom };
324
335
  }
325
336
  async function main(args) {
326
337
  const command = args[0];
@@ -338,7 +349,11 @@ async function main(args) {
338
349
  printJson(blueprintJsonSchema);
339
350
  return;
340
351
  }
341
- throw new Error("schema requires preset or blueprint");
352
+ if (schemaName === "preset-source") {
353
+ printJson(presetSourceManifestJsonSchema);
354
+ return;
355
+ }
356
+ throw new Error("schema requires preset, preset-source, or blueprint");
342
357
  }
343
358
  if (command === "preset" && args[1] === "validate") {
344
359
  const filePath = args[2];
@@ -352,6 +367,32 @@ async function main(args) {
352
367
  console.log(`Preset file is valid: ${result.value.name}`);
353
368
  return;
354
369
  }
370
+ if (command === "preset-source" && args[1] === "validate") {
371
+ const filePath = args[2];
372
+ if (!filePath) {
373
+ throw new Error("preset-source validate requires a path");
374
+ }
375
+ const declaration = await readJsonDeclaration(filePath);
376
+ const sourceName = typeof declaration === "object" &&
377
+ declaration !== null &&
378
+ !Array.isArray(declaration) &&
379
+ "name" in declaration &&
380
+ typeof declaration.name === "string"
381
+ ? declaration.name
382
+ : undefined;
383
+ const result = sourceName === "built-in"
384
+ ? validateBuiltInPresetSourceManifest(declaration, {
385
+ sourceRoot: path.dirname(filePath),
386
+ })
387
+ : validatePresetSourceManifest(declaration, {
388
+ sourceRoot: path.dirname(filePath),
389
+ });
390
+ if (!result.ok) {
391
+ throw new Error(`Preset Source Manifest is invalid:\n${formatValidationIssues(result.issues)}`);
392
+ }
393
+ console.log(`Preset Source Manifest is valid: ${result.value.name} (${result.value.presets.map((preset) => preset.name).join(", ")})`);
394
+ return;
395
+ }
355
396
  if (command === "blueprint" && args[1] === "validate") {
356
397
  const filePath = args[2];
357
398
  if (!filePath) {
@@ -369,8 +410,11 @@ async function main(args) {
369
410
  const blueprint = blueprintForInit(options);
370
411
  if (options.dryRun) {
371
412
  const generationContext = await generationContextForInit(options, blueprint);
413
+ const preset = findBuiltInPresetSourceManifestPreset(options.preset);
372
414
  const projectionPlan = generationContext
373
- ? projectPresetThroughRegistry(generationContext)
415
+ ? preset?.projection
416
+ ? projectPresetSourcePreset({ preset, context: generationContext })
417
+ : undefined
374
418
  : undefined;
375
419
  if (!projectionPlan) {
376
420
  throw new Error(`Missing Preset Projection plan: ${blueprint.preset}`);
@@ -417,6 +461,7 @@ async function main(args) {
417
461
  preset: options.preset,
418
462
  name: options.name,
419
463
  path: options.path,
464
+ linkFrom: options.linkFrom,
420
465
  });
421
466
  console.log(`Added ${options.preset} package ${options.name}`);
422
467
  return;
@@ -1,4 +1,6 @@
1
1
  import * as v from "valibot";
2
+ import type { PackageAdditionSupport } from "./package-addition-support.js";
3
+ import type { PackageLinkIntent, PackageRole, PackageSourcePreset } from "./package-linking.js";
2
4
  export type BuiltInPreset = {
3
5
  name: string;
4
6
  title: string;
@@ -6,6 +8,7 @@ export type BuiltInPreset = {
6
8
  generation: "supported" | "future";
7
9
  supportedPackageManagers: readonly PackageManager[];
8
10
  supportedProjectKinds: readonly ProjectKind[];
11
+ packageAdditionSupport: PackageAdditionSupport;
9
12
  features: readonly FeatureName[];
10
13
  };
11
14
  export type PackageManager = "pnpm";
@@ -23,6 +26,8 @@ export type PresetFile = {
23
26
  export type ProjectPackage = {
24
27
  name: string;
25
28
  path: string;
29
+ role?: PackageRole;
30
+ sourcePreset?: PackageSourcePreset;
26
31
  };
27
32
  export type ProjectBlueprint = {
28
33
  schemaVersion: 1;
@@ -31,6 +36,7 @@ export type ProjectBlueprint = {
31
36
  projectKind: ProjectKind;
32
37
  features: FeatureName[];
33
38
  packages?: ProjectPackage[];
39
+ packageLinkIntents?: PackageLinkIntent[];
34
40
  };
35
41
  export declare const builtInPresets: readonly BuiltInPreset[];
36
42
  export declare const presetFileJsonSchema: {
@@ -123,6 +129,30 @@ export declare const blueprintJsonSchema: {
123
129
  readonly type: "string";
124
130
  readonly minLength: 1;
125
131
  };
132
+ readonly role: {
133
+ readonly enum: readonly ["runtime-service", "shared-library"];
134
+ };
135
+ readonly sourcePreset: {
136
+ readonly enum: readonly ["hono-api", "ts-lib", "vue-app"];
137
+ };
138
+ };
139
+ };
140
+ };
141
+ readonly packageLinkIntents: {
142
+ readonly type: "array";
143
+ readonly items: {
144
+ readonly type: "object";
145
+ readonly additionalProperties: false;
146
+ readonly required: readonly ["consumerPackagePath", "providerPackagePath"];
147
+ readonly properties: {
148
+ readonly consumerPackagePath: {
149
+ readonly type: "string";
150
+ readonly minLength: 1;
151
+ };
152
+ readonly providerPackagePath: {
153
+ readonly type: "string";
154
+ readonly minLength: 1;
155
+ };
126
156
  };
127
157
  };
128
158
  };
@@ -146,6 +176,12 @@ export declare const projectBlueprintSchema: v.StrictObjectSchema<{
146
176
  readonly packages: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
147
177
  readonly name: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
148
178
  readonly path: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
179
+ readonly role: v.OptionalSchema<v.PicklistSchema<["runtime-service", "shared-library"], undefined>, undefined>;
180
+ readonly sourcePreset: v.OptionalSchema<v.PicklistSchema<["hono-api", "ts-lib", "vue-app"], undefined>, undefined>;
181
+ }, undefined>, undefined>, undefined>;
182
+ readonly packageLinkIntents: v.OptionalSchema<v.ArraySchema<v.StrictObjectSchema<{
183
+ readonly consumerPackagePath: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
184
+ readonly providerPackagePath: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
149
185
  }, undefined>, undefined>, undefined>;
150
186
  }, undefined>;
151
187
  export type ValidationIssue = {
@@ -1,6 +1,6 @@
1
1
  import * as v from "valibot";
2
- import { builtInPresetMetadata } from "../templates/registry.js";
3
- export const builtInPresets = builtInPresetMetadata;
2
+ import { loadBuiltInPresetSourceManifest } from "./preset-source.js";
3
+ export const builtInPresets = loadBuiltInPresetSourceManifest().presets;
4
4
  const featureNames = [
5
5
  "pnpm-catalog",
6
6
  "oxc-format-lint",
@@ -78,6 +78,20 @@ export const blueprintJsonSchema = {
78
78
  properties: {
79
79
  name: { type: "string", minLength: 1 },
80
80
  path: { type: "string", minLength: 1 },
81
+ role: { enum: ["runtime-service", "shared-library"] },
82
+ sourcePreset: { enum: ["hono-api", "ts-lib", "vue-app"] },
83
+ },
84
+ },
85
+ },
86
+ packageLinkIntents: {
87
+ type: "array",
88
+ items: {
89
+ type: "object",
90
+ additionalProperties: false,
91
+ required: ["consumerPackagePath", "providerPackagePath"],
92
+ properties: {
93
+ consumerPackagePath: { type: "string", minLength: 1 },
94
+ providerPackagePath: { type: "string", minLength: 1 },
81
95
  },
82
96
  },
83
97
  },
@@ -90,6 +104,15 @@ const projectKindSchema = v.picklist([
90
104
  "multi-package",
91
105
  ]);
92
106
  const featureNameSchema = v.picklist(featureNames);
107
+ const packageRoleSchema = v.picklist([
108
+ "runtime-service",
109
+ "shared-library",
110
+ ]);
111
+ const packageSourcePresetSchema = v.picklist([
112
+ "hono-api",
113
+ "ts-lib",
114
+ "vue-app",
115
+ ]);
93
116
  export const presetFileSchema = v.strictObject({
94
117
  schemaVersion: v.literal(1),
95
118
  name: nonEmptyString,
@@ -108,6 +131,12 @@ export const projectBlueprintSchema = v.strictObject({
108
131
  packages: v.optional(v.array(v.strictObject({
109
132
  name: nonEmptyString,
110
133
  path: nonEmptyString,
134
+ role: v.optional(packageRoleSchema),
135
+ sourcePreset: v.optional(packageSourcePresetSchema),
136
+ }))),
137
+ packageLinkIntents: v.optional(v.array(v.strictObject({
138
+ consumerPackagePath: nonEmptyString,
139
+ providerPackagePath: nonEmptyString,
111
140
  }))),
112
141
  });
113
142
  function formatIssuePath(issue) {
@@ -0,0 +1,62 @@
1
+ import type { PresetSourceManifest } from "./preset-source.js";
2
+ export type GeneratedScenarioSet = "init" | "package-addition-matrix";
3
+ export type GeneratedScenario = {
4
+ readonly set: GeneratedScenarioSet;
5
+ readonly basePreset: string;
6
+ readonly addedPreset?: string;
7
+ readonly linkFrom?: readonly string[];
8
+ readonly id: string;
9
+ readonly label: string;
10
+ };
11
+ export type SkippedGeneratedScenario = {
12
+ readonly set: GeneratedScenarioSet;
13
+ readonly basePreset: string;
14
+ readonly addedPreset: string;
15
+ readonly id: string;
16
+ readonly label: string;
17
+ readonly reason: string;
18
+ };
19
+ export type GeneratedScenarioSelection = {
20
+ readonly runnable: readonly GeneratedScenario[];
21
+ readonly skipped: readonly SkippedGeneratedScenario[];
22
+ };
23
+ export type GeneratedScenarioCommandStep = {
24
+ readonly id: string;
25
+ readonly command: string;
26
+ readonly args: readonly string[];
27
+ readonly cwd: string;
28
+ readonly display: string;
29
+ };
30
+ export type GeneratedScenarioCommandRunner = (command: string, args: readonly string[], cwd: string, options?: {
31
+ readonly env?: Record<string, string>;
32
+ readonly logPrefix?: string;
33
+ }) => Promise<void>;
34
+ export type GeneratedScenarioReporter = {
35
+ readonly info?: (message: string) => void;
36
+ readonly error?: (message: string) => void;
37
+ };
38
+ export type GeneratedScenarioRunnerOptions = {
39
+ readonly repoRoot: string;
40
+ readonly cliPath: string;
41
+ readonly runCommand?: GeneratedScenarioCommandRunner;
42
+ readonly reporter?: GeneratedScenarioReporter;
43
+ readonly deterministicToolchainEnv?: Record<string, string>;
44
+ readonly rootCheckLock?: ExclusiveLock;
45
+ };
46
+ export type ExclusiveLock = {
47
+ readonly run: <T>(callback: () => Promise<T>) => Promise<T>;
48
+ };
49
+ export declare function createExclusiveLock(): ExclusiveLock;
50
+ export declare function generatedScenarioId(scenario: Pick<GeneratedScenario, "basePreset" | "addedPreset" | "linkFrom">): string;
51
+ export declare function generatedScenarioLabel(scenario: Pick<GeneratedScenario, "basePreset" | "addedPreset" | "linkFrom">): string;
52
+ export declare function selectGeneratedScenarios(manifest: PresetSourceManifest, set: GeneratedScenarioSet): GeneratedScenarioSelection;
53
+ export declare function packageLeafNameForAddedPreset(manifest: PresetSourceManifest, presetName: string): string;
54
+ export declare function defaultGeneratedScenarioCommandRunner(command: string, args: readonly string[], cwd: string, options?: {
55
+ readonly env?: Record<string, string>;
56
+ readonly logPrefix?: string;
57
+ }): Promise<void>;
58
+ export declare function generatedScenarioQualityGateSteps(manifest: PresetSourceManifest, scenario: GeneratedScenario, projectDir: string, addedPackagePath: string | undefined, options: GeneratedScenarioRunnerOptions): GeneratedScenarioCommandStep[];
59
+ export declare function fixtureConcurrency(scenarioCount: number): number;
60
+ export declare function errorForFailedGeneratedScenario(scenario: GeneratedScenario, error: unknown): Error;
61
+ export declare function runGeneratedScenariosConcurrently(manifest: PresetSourceManifest, scenarios: readonly GeneratedScenario[], workspace: string, concurrency: number, options: GeneratedScenarioRunnerOptions): Promise<void>;
62
+ export declare function runGeneratedScenarioSet(manifest: PresetSourceManifest, set: GeneratedScenarioSet, workspace: string, options: GeneratedScenarioRunnerOptions): Promise<GeneratedScenarioSelection>;