@ykdz/template 0.0.3 → 0.0.5

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 (95) hide show
  1. package/README.md +1 -1
  2. package/dist/src/cli.d.ts +0 -1
  3. package/dist/src/cli.js +26 -2
  4. package/dist/src/declarations.d.ts +38 -3
  5. package/dist/src/declarations.js +75 -36
  6. package/dist/src/dependency-catalog.d.ts +9 -1
  7. package/dist/src/dependency-catalog.js +69 -0
  8. package/dist/src/devcontainer.d.ts +49 -9
  9. package/dist/src/devcontainer.js +192 -14
  10. package/dist/src/editor-customization.d.ts +0 -1
  11. package/dist/src/editor-customization.js +54 -47
  12. package/dist/src/generation-context.d.ts +0 -1
  13. package/dist/src/generation-context.js +4 -1
  14. package/dist/src/module-graph.d.ts +8 -5
  15. package/dist/src/module-graph.js +20 -2
  16. package/dist/src/next-step-instructions.d.ts +0 -1
  17. package/dist/src/next-step-instructions.js +35 -0
  18. package/dist/src/package-addition-support.d.ts +5 -0
  19. package/dist/src/package-addition-support.js +4 -0
  20. package/dist/src/package-addition.d.ts +2 -1
  21. package/dist/src/package-addition.js +557 -55
  22. package/dist/src/package-linking.d.ts +55 -0
  23. package/dist/src/package-linking.js +116 -0
  24. package/dist/src/preset-projection.d.ts +4 -2
  25. package/dist/src/project-github.d.ts +3 -2
  26. package/dist/src/project-github.js +24 -6
  27. package/dist/src/renderer.d.ts +16 -2
  28. package/dist/src/renderer.js +96 -12
  29. package/dist/src/runtime-paths.d.ts +0 -1
  30. package/dist/src/template-boundary-check.d.ts +34 -0
  31. package/dist/src/template-boundary-check.js +448 -0
  32. package/dist/src/toolchain-resolution.d.ts +0 -1
  33. package/dist/templates/hono-api/projection.d.ts +2 -3
  34. package/dist/templates/hono-api/projection.js +258 -76
  35. package/dist/templates/projection-plans.d.ts +0 -1
  36. package/dist/templates/registry.d.ts +0 -1
  37. package/dist/templates/registry.js +5 -2
  38. package/dist/templates/rust-bin/projection.d.ts +2 -3
  39. package/dist/templates/rust-bin/projection.js +171 -53
  40. package/dist/templates/ts-lib/projection.d.ts +2 -3
  41. package/dist/templates/ts-lib/projection.js +251 -87
  42. package/dist/templates/vue-app/projection.d.ts +2 -3
  43. package/dist/templates/vue-app/projection.js +266 -94
  44. package/dist/templates/vue-hono-app/projection.d.ts +0 -1
  45. package/dist/templates/vue-hono-app/projection.js +232 -108
  46. package/package.json +10 -5
  47. package/pnpm-workspace.yaml +1 -0
  48. package/templates/hono-api/.github/dependabot.yml +13 -0
  49. package/templates/hono-api/src/server.ts +1 -1
  50. package/templates/hono-api/test/app.test.ts +1 -1
  51. package/templates/rust-bin/.github/dependabot.yml +18 -1
  52. package/templates/rust-bin/rust-toolchain.toml +3 -0
  53. package/templates/rust-bin/rustfmt.toml +2 -0
  54. package/templates/shared/devcontainer/browser-test.Dockerfile +2 -0
  55. package/templates/shared/devcontainer/node-pnpm.Dockerfile +6 -0
  56. package/templates/shared/devcontainer/rust.Dockerfile +16 -0
  57. package/templates/shared/editor-customization/capabilities.json +56 -0
  58. package/templates/ts-lib/.github/dependabot.yml +13 -0
  59. package/templates/ts-lib/src/index.ts +5 -1
  60. package/templates/ts-lib/src/name-schema.ts +3 -0
  61. package/templates/vue-app/.github/dependabot.yml +13 -0
  62. package/templates/vue-app/.github/workflows/check.yml +1 -1
  63. package/templates/vue-app/src/App.vue +1 -1
  64. package/templates/vue-app/test/app.test.ts +1 -1
  65. package/templates/vue-hono-app/.github/dependabot.yml +13 -0
  66. package/templates/vue-hono-app/web/src/App.vue +2 -2
  67. package/templates/vue-hono-app/web/test/app.test.ts +1 -1
  68. package/dist/src/cli.d.ts.map +0 -1
  69. package/dist/src/declarations.d.ts.map +0 -1
  70. package/dist/src/dependency-catalog.d.ts.map +0 -1
  71. package/dist/src/devcontainer.d.ts.map +0 -1
  72. package/dist/src/editor-customization.d.ts.map +0 -1
  73. package/dist/src/generation-context.d.ts.map +0 -1
  74. package/dist/src/module-graph.d.ts.map +0 -1
  75. package/dist/src/next-step-instructions.d.ts.map +0 -1
  76. package/dist/src/package-addition.d.ts.map +0 -1
  77. package/dist/src/preset-projection.d.ts.map +0 -1
  78. package/dist/src/project-github.d.ts.map +0 -1
  79. package/dist/src/renderer.d.ts.map +0 -1
  80. package/dist/src/runtime-paths.d.ts.map +0 -1
  81. package/dist/src/toolchain-resolution.d.ts.map +0 -1
  82. package/dist/templates/hono-api/projection.d.ts.map +0 -1
  83. package/dist/templates/projection-plans.d.ts.map +0 -1
  84. package/dist/templates/registry.d.ts.map +0 -1
  85. package/dist/templates/rust-bin/projection.d.ts.map +0 -1
  86. package/dist/templates/ts-lib/projection.d.ts.map +0 -1
  87. package/dist/templates/vue-app/projection.d.ts.map +0 -1
  88. package/dist/templates/vue-hono-app/projection.d.ts.map +0 -1
  89. package/templates/hono-api/projection.ts +0 -453
  90. package/templates/projection-plans.ts +0 -135
  91. package/templates/registry.ts +0 -59
  92. package/templates/rust-bin/projection.ts +0 -346
  93. package/templates/ts-lib/projection.ts +0 -454
  94. package/templates/vue-app/projection.ts +0 -687
  95. package/templates/vue-hono-app/projection.ts +0 -669
package/README.md CHANGED
@@ -12,7 +12,7 @@ Available commands:
12
12
 
13
13
  ```sh
14
14
  template init <dir> --preset <name> --yes
15
- template add package --preset <name> --name <name>
15
+ template add package --preset <name> --name <name> [--path <package-path>]
16
16
  template presets
17
17
  template schema preset
18
18
  template schema blueprint
package/dist/src/cli.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  #!/usr/bin/env node
2
2
  export {};
3
- //# sourceMappingURL=cli.d.ts.map
package/dist/src/cli.js CHANGED
@@ -12,7 +12,7 @@ function usage() {
12
12
  return [
13
13
  "Usage:",
14
14
  " template init <dir> --preset <name> --yes",
15
- " template add package --preset <name> --name <name>",
15
+ " template add package --preset <name> --name <name> [--path <package-path>] [--link-from <package-path>]...",
16
16
  " template presets",
17
17
  " template schema preset",
18
18
  " template schema blueprint",
@@ -22,6 +22,8 @@ function usage() {
22
22
  "Options:",
23
23
  " --preset <name> Project preset to generate",
24
24
  " --name <name> Package name to add",
25
+ " --path <path> Two-segment Package Path to add",
26
+ " --link-from <path> Existing consumer Package Path to link from; repeatable",
25
27
  " --scope <name> Package scope for workspace package names",
26
28
  " --yes Accept defaults for non-interactive generation",
27
29
  " --dry-run Print the planned generation without writing files",
@@ -281,6 +283,8 @@ async function confirmInit(targetDir, blueprint) {
281
283
  function parseAddPackageOptions(args) {
282
284
  let preset = "";
283
285
  let name = "";
286
+ let packagePath;
287
+ const linkFrom = [];
284
288
  for (let index = 2; index < args.length; index += 1) {
285
289
  const arg = args[index];
286
290
  if (arg === "--preset") {
@@ -301,6 +305,24 @@ function parseAddPackageOptions(args) {
301
305
  index += 1;
302
306
  continue;
303
307
  }
308
+ if (arg === "--path") {
309
+ const value = args[index + 1];
310
+ if (!value) {
311
+ throw new Error("--path requires a value");
312
+ }
313
+ packagePath = value;
314
+ index += 1;
315
+ continue;
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
+ }
304
326
  throw new Error(`Unknown option: ${arg}`);
305
327
  }
306
328
  if (!preset) {
@@ -309,7 +331,7 @@ function parseAddPackageOptions(args) {
309
331
  if (!name) {
310
332
  throw new Error("add package requires --name");
311
333
  }
312
- return { preset, name };
334
+ return { preset, name, path: packagePath, linkFrom };
313
335
  }
314
336
  async function main(args) {
315
337
  const command = args[0];
@@ -405,6 +427,8 @@ async function main(args) {
405
427
  cwd: process.cwd(),
406
428
  preset: options.preset,
407
429
  name: options.name,
430
+ path: options.path,
431
+ linkFrom: options.linkFrom,
408
432
  });
409
433
  console.log(`Added ${options.preset} package ${options.name}`);
410
434
  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: {
@@ -67,7 +73,7 @@ export declare const presetFileJsonSchema: {
67
73
  readonly type: "array";
68
74
  readonly minItems: 1;
69
75
  readonly items: {
70
- readonly enum: readonly ["single-package", "multi-package"];
76
+ readonly enum: readonly ["multi-package"];
71
77
  };
72
78
  readonly uniqueItems: true;
73
79
  };
@@ -99,7 +105,7 @@ export declare const blueprintJsonSchema: {
99
105
  readonly enum: readonly ["pnpm"];
100
106
  };
101
107
  readonly projectKind: {
102
- readonly enum: readonly ["single-package", "multi-package"];
108
+ readonly enum: readonly ["multi-package"];
103
109
  };
104
110
  readonly features: {
105
111
  readonly type: "array";
@@ -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 = {
@@ -162,4 +198,3 @@ export type ValidationResult<T> = {
162
198
  export declare function validatePresetFile(input: unknown): ValidationResult<PresetFile>;
163
199
  export declare function findBuiltInPreset(name: string): BuiltInPreset | undefined;
164
200
  export declare function validateProjectBlueprint(input: unknown): ValidationResult<ProjectBlueprint>;
165
- //# sourceMappingURL=declarations.d.ts.map
@@ -12,7 +12,7 @@ const featureNames = [
12
12
  "dependabot",
13
13
  "rustfmt-clippy",
14
14
  "cargo-test",
15
- "native-binary-release"
15
+ "native-binary-release",
16
16
  ];
17
17
  export const presetFileJsonSchema = {
18
18
  $schema: "https://json-schema.org/draft/2020-12/schema",
@@ -27,7 +27,7 @@ export const presetFileJsonSchema = {
27
27
  "description",
28
28
  "supportedPackageManagers",
29
29
  "supportedProjectKinds",
30
- "features"
30
+ "features",
31
31
  ],
32
32
  properties: {
33
33
  schemaVersion: { const: 1 },
@@ -37,20 +37,20 @@ export const presetFileJsonSchema = {
37
37
  supportedPackageManagers: {
38
38
  type: "array",
39
39
  items: { enum: ["pnpm"] },
40
- uniqueItems: true
40
+ uniqueItems: true,
41
41
  },
42
42
  supportedProjectKinds: {
43
43
  type: "array",
44
44
  minItems: 1,
45
- items: { enum: ["single-package", "multi-package"] },
46
- uniqueItems: true
45
+ items: { enum: ["multi-package"] },
46
+ uniqueItems: true,
47
47
  },
48
48
  features: {
49
49
  type: "array",
50
50
  items: { enum: featureNames },
51
- uniqueItems: true
52
- }
53
- }
51
+ uniqueItems: true,
52
+ },
53
+ },
54
54
  };
55
55
  export const blueprintJsonSchema = {
56
56
  $schema: "https://json-schema.org/draft/2020-12/schema",
@@ -63,11 +63,11 @@ export const blueprintJsonSchema = {
63
63
  schemaVersion: { const: 1 },
64
64
  preset: { type: "string", minLength: 1 },
65
65
  packageManager: { enum: ["pnpm"] },
66
- projectKind: { enum: ["single-package", "multi-package"] },
66
+ projectKind: { enum: ["multi-package"] },
67
67
  features: {
68
68
  type: "array",
69
69
  items: { enum: featureNames },
70
- uniqueItems: true
70
+ uniqueItems: true,
71
71
  },
72
72
  packages: {
73
73
  type: "array",
@@ -77,16 +77,42 @@ export const blueprintJsonSchema = {
77
77
  required: ["name", "path"],
78
78
  properties: {
79
79
  name: { type: "string", minLength: 1 },
80
- path: { type: "string", minLength: 1 }
81
- }
82
- }
83
- }
84
- }
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 },
95
+ },
96
+ },
97
+ },
98
+ },
85
99
  };
86
100
  const nonEmptyString = v.pipe(v.string(), v.minLength(1));
87
101
  const packageManagerSchema = v.picklist(["pnpm"]);
88
- const projectKindSchema = v.picklist(["single-package", "multi-package"]);
102
+ const projectKindSchema = v.picklist([
103
+ "single-package",
104
+ "multi-package",
105
+ ]);
89
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
+ ]);
90
116
  export const presetFileSchema = v.strictObject({
91
117
  schemaVersion: v.literal(1),
92
118
  name: nonEmptyString,
@@ -94,7 +120,7 @@ export const presetFileSchema = v.strictObject({
94
120
  description: nonEmptyString,
95
121
  supportedPackageManagers: v.array(packageManagerSchema),
96
122
  supportedProjectKinds: v.pipe(v.array(projectKindSchema), v.minLength(1)),
97
- features: v.array(featureNameSchema)
123
+ features: v.array(featureNameSchema),
98
124
  });
99
125
  export const projectBlueprintSchema = v.strictObject({
100
126
  schemaVersion: v.literal(1),
@@ -104,8 +130,14 @@ export const projectBlueprintSchema = v.strictObject({
104
130
  features: v.array(featureNameSchema),
105
131
  packages: v.optional(v.array(v.strictObject({
106
132
  name: nonEmptyString,
107
- path: nonEmptyString
108
- })))
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,
140
+ }))),
109
141
  });
110
142
  function formatIssuePath(issue) {
111
143
  if (!issue.path || issue.path.length === 0) {
@@ -116,7 +148,7 @@ function formatIssuePath(issue) {
116
148
  function shapeIssues(issues) {
117
149
  return issues.map((issue) => ({
118
150
  path: formatIssuePath(issue),
119
- message: issue.message
151
+ message: issue.message,
120
152
  }));
121
153
  }
122
154
  function duplicateIssues(values, path) {
@@ -130,7 +162,7 @@ function duplicateIssues(values, path) {
130
162
  }
131
163
  return [...duplicates].map((value) => ({
132
164
  path,
133
- message: `Duplicate value: ${value}`
165
+ message: `Duplicate value: ${value}`,
134
166
  }));
135
167
  }
136
168
  function generationSupportIssue(preset, path) {
@@ -139,7 +171,13 @@ function generationSupportIssue(preset, path) {
139
171
  }
140
172
  return {
141
173
  path,
142
- message: `Preset ${preset.name} is not supported for generation in this version`
174
+ message: `Preset ${preset.name} is not supported for generation in this version`,
175
+ };
176
+ }
177
+ function unsupportedSinglePackageProjectShapeIssue(path) {
178
+ return {
179
+ path,
180
+ message: "single-package Project Shape is unsupported in V1; use the workspace monorepo Project Shape",
143
181
  };
144
182
  }
145
183
  export function validatePresetFile(input) {
@@ -150,8 +188,11 @@ export function validatePresetFile(input) {
150
188
  const semanticIssues = [
151
189
  ...duplicateIssues(result.output.supportedPackageManagers, "$.supportedPackageManagers"),
152
190
  ...duplicateIssues(result.output.supportedProjectKinds, "$.supportedProjectKinds"),
153
- ...duplicateIssues(result.output.features, "$.features")
191
+ ...duplicateIssues(result.output.features, "$.features"),
154
192
  ];
193
+ if (result.output.supportedProjectKinds.includes("single-package")) {
194
+ semanticIssues.push(unsupportedSinglePackageProjectShapeIssue("$.supportedProjectKinds"));
195
+ }
155
196
  const builtInPreset = findBuiltInPreset(result.output.name);
156
197
  const unsupportedGeneration = builtInPreset
157
198
  ? generationSupportIssue(builtInPreset, "$.name")
@@ -175,12 +216,15 @@ export function validateProjectBlueprint(input) {
175
216
  const blueprint = result.output;
176
217
  const preset = findBuiltInPreset(blueprint.preset);
177
218
  const semanticIssues = [
178
- ...duplicateIssues(blueprint.features, "$.features")
219
+ ...duplicateIssues(blueprint.features, "$.features"),
179
220
  ];
221
+ if (blueprint.projectKind === "single-package") {
222
+ semanticIssues.push(unsupportedSinglePackageProjectShapeIssue("$.projectKind"));
223
+ }
180
224
  if (!preset) {
181
225
  semanticIssues.push({
182
226
  path: "$.preset",
183
- message: `Unknown built-in preset: ${blueprint.preset}`
227
+ message: `Unknown built-in preset: ${blueprint.preset}`,
184
228
  });
185
229
  }
186
230
  else {
@@ -192,19 +236,20 @@ export function validateProjectBlueprint(input) {
192
236
  !preset.supportedPackageManagers.includes(blueprint.packageManager)) {
193
237
  semanticIssues.push({
194
238
  path: "$.packageManager",
195
- message: `${blueprint.packageManager} is not supported by preset ${preset.name}`
239
+ message: `${blueprint.packageManager} is not supported by preset ${preset.name}`,
196
240
  });
197
241
  }
198
- if (!blueprint.packageManager && preset.supportedPackageManagers.length > 0) {
242
+ if (!blueprint.packageManager &&
243
+ preset.supportedPackageManagers.length > 0) {
199
244
  semanticIssues.push({
200
245
  path: "$.packageManager",
201
- message: `packageManager is required by preset ${preset.name}`
246
+ message: `packageManager is required by preset ${preset.name}`,
202
247
  });
203
248
  }
204
249
  if (!preset.supportedProjectKinds.includes(blueprint.projectKind)) {
205
250
  semanticIssues.push({
206
251
  path: "$.projectKind",
207
- message: `${blueprint.projectKind} is not supported by preset ${preset.name}`
252
+ message: `${blueprint.projectKind} is not supported by preset ${preset.name}`,
208
253
  });
209
254
  }
210
255
  const supportedFeatures = new Set(preset.features);
@@ -212,18 +257,12 @@ export function validateProjectBlueprint(input) {
212
257
  if (!supportedFeatures.has(feature)) {
213
258
  semanticIssues.push({
214
259
  path: "$.features",
215
- message: `${feature} is not supported by preset ${preset.name}`
260
+ message: `${feature} is not supported by preset ${preset.name}`,
216
261
  });
217
262
  }
218
263
  }
219
264
  }
220
265
  if (blueprint.packages) {
221
- if (blueprint.projectKind === "single-package" && blueprint.packages.length > 1) {
222
- semanticIssues.push({
223
- path: "$.packages",
224
- message: "single-package blueprints support exactly one package"
225
- });
226
- }
227
266
  semanticIssues.push(...duplicateIssues(blueprint.packages.map((projectPackage) => projectPackage.name), "$.packages.name"), ...duplicateIssues(blueprint.packages.map((projectPackage) => projectPackage.path), "$.packages.path"));
228
267
  }
229
268
  if (semanticIssues.length > 0) {
@@ -4,7 +4,15 @@ export type GeneratedDependencyCatalogOptions = {
4
4
  readonly packages?: readonly string[];
5
5
  readonly allowBuilds?: Record<string, boolean>;
6
6
  };
7
+ export type GeneratedPackageManifestDependencies = {
8
+ readonly dependencies?: Record<string, string>;
9
+ readonly devDependencies?: Record<string, string>;
10
+ readonly optionalDependencies?: Record<string, string>;
11
+ readonly peerDependencies?: Record<string, string>;
12
+ };
7
13
  export declare function loadTemplateDependencyCatalog(): TemplateDependencyCatalog;
8
14
  export declare function selectTemplateDependencyCatalogEntries(dependencies: readonly string[], catalog?: TemplateDependencyCatalog): TemplateDependencyCatalog;
15
+ export declare function collectGeneratedManifestCatalogDependencies(manifests: readonly GeneratedPackageManifestDependencies[]): string[];
16
+ export declare function collectGeneratedManifestCatalogReferences(manifests: readonly GeneratedPackageManifestDependencies[]): string[];
17
+ export declare function pnpmWorkspaceYamlWithCatalogDependencies(workspaceYaml: string, dependencies: readonly string[], templateCatalog?: TemplateDependencyCatalog): string;
9
18
  export declare function renderGeneratedPnpmWorkspaceYaml(options: GeneratedDependencyCatalogOptions): string;
10
- //# sourceMappingURL=dependency-catalog.d.ts.map
@@ -42,9 +42,78 @@ export function selectTemplateDependencyCatalogEntries(dependencies, catalog = l
42
42
  }
43
43
  return selected;
44
44
  }
45
+ export function collectGeneratedManifestCatalogDependencies(manifests) {
46
+ const dependencies = new Set();
47
+ for (const manifest of manifests) {
48
+ for (const dependencyMap of [
49
+ manifest.dependencies,
50
+ manifest.devDependencies,
51
+ manifest.optionalDependencies,
52
+ manifest.peerDependencies,
53
+ ]) {
54
+ for (const [dependency, specifier] of Object.entries(dependencyMap ?? {})) {
55
+ if (specifier !== "catalog:") {
56
+ throw new Error(`Generated manifest dependency ${dependency} must use catalog:, got ${specifier}`);
57
+ }
58
+ dependencies.add(dependency);
59
+ }
60
+ }
61
+ }
62
+ return [...dependencies].sort();
63
+ }
64
+ export function collectGeneratedManifestCatalogReferences(manifests) {
65
+ const dependencies = new Set();
66
+ for (const manifest of manifests) {
67
+ for (const dependencyMap of [
68
+ manifest.dependencies,
69
+ manifest.devDependencies,
70
+ manifest.optionalDependencies,
71
+ manifest.peerDependencies,
72
+ ]) {
73
+ for (const [dependency, specifier] of Object.entries(dependencyMap ?? {})) {
74
+ if (specifier.startsWith("catalog:")) {
75
+ dependencies.add(dependency);
76
+ }
77
+ }
78
+ }
79
+ }
80
+ return [...dependencies].sort();
81
+ }
45
82
  function renderCatalogKey(key) {
46
83
  return key.startsWith("@") ? JSON.stringify(key) : key;
47
84
  }
85
+ export function pnpmWorkspaceYamlWithCatalogDependencies(workspaceYaml, dependencies, templateCatalog = loadTemplateDependencyCatalog()) {
86
+ const catalogStart = workspaceYaml
87
+ .split(/\r?\n/)
88
+ .findIndex((line) => line === "catalog:");
89
+ if (catalogStart === -1) {
90
+ throw new Error("Cannot update pnpm Dependency Catalog: missing catalog section");
91
+ }
92
+ const existingCatalog = parseTemplateDependencyCatalog(workspaceYaml);
93
+ const missingDependencies = dependencies.filter((dependency) => existingCatalog[dependency] === undefined);
94
+ if (missingDependencies.length === 0) {
95
+ return workspaceYaml;
96
+ }
97
+ const nextCatalog = {
98
+ ...existingCatalog,
99
+ ...selectTemplateDependencyCatalogEntries(missingDependencies, templateCatalog),
100
+ };
101
+ const lines = workspaceYaml.split(/\r?\n/);
102
+ const catalogEnd = lines.findIndex((line, index) => index > catalogStart && line.length > 0 && !line.startsWith(" "));
103
+ const replacementEnd = catalogEnd === -1 ? lines.length : catalogEnd;
104
+ const catalogLines = [
105
+ "catalog:",
106
+ ...Object.entries(nextCatalog)
107
+ .sort(([left], [right]) => left.localeCompare(right))
108
+ .map(([dependency, version]) => ` ${renderCatalogKey(dependency)}: ${version}`),
109
+ "",
110
+ ];
111
+ return [
112
+ ...lines.slice(0, catalogStart),
113
+ ...catalogLines,
114
+ ...lines.slice(replacementEnd),
115
+ ].join("\n");
116
+ }
48
117
  export function renderGeneratedPnpmWorkspaceYaml(options) {
49
118
  const catalog = selectTemplateDependencyCatalogEntries(options.dependencies);
50
119
  const packages = options.packages ?? ["."];
@@ -1,18 +1,58 @@
1
- export type DevcontainerNodeFeatureOptions = {
1
+ import type { WriteTextFromFragmentsOperation } from "./renderer.js";
2
+ export type DevelopmentContainerNodePnpmLayer = {
3
+ readonly kind: "node-pnpm";
2
4
  readonly nodeVersion: string;
3
5
  readonly packageManagerPin: string;
4
6
  };
5
- export type DevcontainerNodeFeature = {
6
- readonly version: string;
7
- readonly pnpmVersion: string;
7
+ export type DevelopmentContainerBrowserTestLayer = {
8
+ readonly kind: "browser-test";
9
+ readonly playwrightCliPackage: string;
8
10
  };
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: {
11
+ export type DevelopmentContainerRustLayer = {
12
+ readonly kind: "rust";
13
+ readonly toolchain: string;
14
+ };
15
+ type DevelopmentContainerCapabilityLayer = DevelopmentContainerBrowserTestLayer | DevelopmentContainerRustLayer;
16
+ export type DevelopmentContainerPlan = {
17
+ readonly devcontainer: Record<string, unknown>;
18
+ readonly dockerfile: string;
19
+ readonly dockerfileOperation?: WriteTextFromFragmentsOperation;
20
+ };
21
+ export type DevelopmentContainerDockerfileLayer = {
22
+ readonly kind: "base" | "capability";
12
23
  readonly name: string;
24
+ readonly text: string;
25
+ };
26
+ export declare function composeDevelopmentContainerDockerfile(options: {
27
+ readonly layers: readonly DevelopmentContainerDockerfileLayer[];
28
+ }): string;
29
+ export declare function nodePnpmToolLayer(options: {
13
30
  readonly nodeVersion: string;
14
31
  readonly packageManagerPin: string;
32
+ }): DevelopmentContainerNodePnpmLayer;
33
+ export declare function browserTestToolLayer(): DevelopmentContainerBrowserTestLayer;
34
+ export declare function rustToolLayer(options?: {
35
+ readonly toolchain?: string;
36
+ }): DevelopmentContainerRustLayer;
37
+ export declare function dockerfileFirstNodePnpmDevcontainer(options: {
38
+ readonly name: string;
39
+ readonly layer: DevelopmentContainerNodePnpmLayer;
40
+ readonly extensions: readonly string[];
41
+ readonly settings?: Record<string, unknown>;
42
+ }): DevelopmentContainerPlan;
43
+ export declare function checkedDockerfileFirstNodePnpmDevcontainer(options: {
44
+ readonly name: string;
45
+ readonly layer: DevelopmentContainerNodePnpmLayer;
46
+ readonly additionalLayers?: readonly DevelopmentContainerCapabilityLayer[];
47
+ readonly extensions: readonly string[];
48
+ readonly settings?: Record<string, unknown>;
49
+ readonly mounts?: readonly string[];
50
+ }): DevelopmentContainerPlan;
51
+ export declare function dockerfileFirstRustPnpmDevcontainer(options: {
52
+ readonly name: string;
53
+ readonly nodeLayer: DevelopmentContainerNodePnpmLayer;
54
+ readonly rustLayer: DevelopmentContainerRustLayer;
15
55
  readonly extensions: readonly string[];
16
56
  readonly settings?: Record<string, unknown>;
17
- }): Record<string, unknown>;
18
- //# sourceMappingURL=devcontainer.d.ts.map
57
+ }): DevelopmentContainerPlan;
58
+ export {};