@ykdz/template 0.0.9 → 0.0.11

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 (51) hide show
  1. package/dist/cli.js +2 -2
  2. package/node_modules/@ykdz/template-builtin-source/dist/src/index.d.ts +4 -4
  3. package/node_modules/@ykdz/template-builtin-source/dist/src/index.js +12 -5
  4. package/node_modules/@ykdz/template-builtin-source/dist/templates/hono-api/projection.d.ts +1 -1
  5. package/node_modules/@ykdz/template-builtin-source/dist/templates/hono-api/projection.js +1 -1
  6. package/node_modules/@ykdz/template-builtin-source/dist/templates/projection-plans.js +9 -6
  7. package/node_modules/@ykdz/template-builtin-source/dist/templates/rust-bin/projection.d.ts +1 -1
  8. package/node_modules/@ykdz/template-builtin-source/dist/templates/rust-bin/projection.js +1 -1
  9. package/node_modules/@ykdz/template-builtin-source/dist/templates/ts-lib/projection.d.ts +1 -1
  10. package/node_modules/@ykdz/template-builtin-source/dist/templates/ts-lib/projection.js +1 -1
  11. package/node_modules/@ykdz/template-builtin-source/dist/templates/vue-app/projection.d.ts +1 -1
  12. package/node_modules/@ykdz/template-builtin-source/dist/templates/vue-app/projection.js +9 -4
  13. package/node_modules/@ykdz/template-builtin-source/dist/templates/vue-hono-app/projection.d.ts +1 -1
  14. package/node_modules/@ykdz/template-builtin-source/dist/templates/vue-hono-app/projection.js +7 -7
  15. package/node_modules/@ykdz/template-builtin-source/package.json +4 -3
  16. package/node_modules/@ykdz/template-builtin-source/templates/preset-source.json +39 -11
  17. package/node_modules/@ykdz/template-builtin-source/templates/vue-app/scripts/run-playwright.ts +25 -0
  18. package/node_modules/@ykdz/template-builtin-source/templates/vue-app/src/App.vue +18 -4
  19. package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/scripts/run-playwright.ts +25 -0
  20. package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/src/App.vue +18 -4
  21. package/node_modules/@ykdz/template-core/dist/declarations.d.ts +2 -203
  22. package/node_modules/@ykdz/template-core/dist/declarations.js +1 -303
  23. package/node_modules/@ykdz/template-core/dist/devcontainer.d.ts +17 -0
  24. package/node_modules/@ykdz/template-core/dist/devcontainer.js +22 -21
  25. package/node_modules/@ykdz/template-core/dist/editor-customization.d.ts +10 -1
  26. package/node_modules/@ykdz/template-core/dist/editor-customization.js +36 -26
  27. package/node_modules/@ykdz/template-core/dist/generated-scenarios.d.ts +5 -0
  28. package/node_modules/@ykdz/template-core/dist/generated-scenarios.js +42 -24
  29. package/node_modules/@ykdz/template-core/dist/generation-context.d.ts +1 -1
  30. package/node_modules/@ykdz/template-core/dist/module-graph.d.ts +19 -1
  31. package/node_modules/@ykdz/template-core/dist/module-graph.js +42 -3
  32. package/node_modules/@ykdz/template-core/dist/next-step-instructions.d.ts +2 -0
  33. package/node_modules/@ykdz/template-core/dist/next-step-instructions.js +11 -22
  34. package/node_modules/@ykdz/template-core/dist/package-addition-support.d.ts +2 -1
  35. package/node_modules/@ykdz/template-core/dist/package-addition-support.js +2 -4
  36. package/node_modules/@ykdz/template-core/dist/package-addition.js +4 -3
  37. package/node_modules/@ykdz/template-core/dist/package-linking.d.ts +2 -6
  38. package/node_modules/@ykdz/template-core/dist/package-linking.js +1 -1
  39. package/node_modules/@ykdz/template-core/dist/pnpm-workspace.yaml +3 -5
  40. package/node_modules/@ykdz/template-core/dist/preset-projection.d.ts +1 -2
  41. package/node_modules/@ykdz/template-core/dist/preset-source.d.ts +4 -418
  42. package/node_modules/@ykdz/template-core/dist/preset-source.js +81 -726
  43. package/node_modules/@ykdz/template-core/dist/project-github.js +2 -6
  44. package/node_modules/@ykdz/template-core/dist/projection-capabilities.d.ts +5 -53
  45. package/node_modules/@ykdz/template-core/dist/projection-capabilities.js +108 -507
  46. package/node_modules/@ykdz/template-core/dist/toolchain-resolution.js +1 -1
  47. package/node_modules/@ykdz/template-core/package.json +4 -3
  48. package/node_modules/@ykdz/template-shared/dist/index.d.ts +794 -0
  49. package/node_modules/@ykdz/template-shared/dist/index.js +1637 -0
  50. package/node_modules/@ykdz/template-shared/package.json +32 -0
  51. package/package.json +9 -6
@@ -1,651 +1,18 @@
1
1
  import { existsSync, readdirSync, readFileSync, realpathSync, statSync, } from "node:fs";
2
2
  import path from "node:path";
3
- import * as v from "valibot";
3
+ import { presetSourceManifestJsonSchema, validatePresetSourceManifestDeclaration, } from "@ykdz/template-shared";
4
4
  import { loadTemplateDependencyCatalog, } from "./dependency-catalog.js";
5
- import { PackageAdditionSupport } from "./package-addition-support.js";
6
- import { normalizePresetProjectionDeclaration, projectionCapabilityIssues, } from "./projection-capabilities.js";
7
- const featureNames = [
8
- "pnpm-catalog",
9
- "oxc-format-lint",
10
- "strict-typescript",
11
- "root-check",
12
- "fix-command",
13
- "devcontainer",
14
- "github-actions",
15
- "dependabot",
16
- "rustfmt-clippy",
17
- "cargo-test",
18
- "native-binary-release",
19
- ];
20
- const packageLeafNamePattern = "^[a-z0-9][a-z0-9-]*$";
21
- const packageLeafNameRegExp = new RegExp(packageLeafNamePattern);
22
- const requiredFixtureMatrixCheckRequirements = [
23
- "machine-verifiable-next-steps",
24
- "root-check-ci",
25
- ];
26
- const requiredFixtureMatrixEnvironmentPreparation = [
27
- "playwright-browser-assets",
28
- ];
29
- export const presetSourceManifestJsonSchema = {
30
- $schema: "https://json-schema.org/draft/2020-12/schema",
31
- $id: "https://ykdz.dev/schemas/template/preset-source-manifest.schema.json",
32
- title: "Preset Source Manifest",
33
- type: "object",
34
- additionalProperties: false,
35
- required: ["schemaVersion", "name", "presets"],
36
- properties: {
37
- schemaVersion: { const: 1 },
38
- name: { type: "string", minLength: 1 },
39
- sharedResources: {
40
- type: "array",
41
- items: {
42
- type: "object",
43
- additionalProperties: false,
44
- required: ["id", "path"],
45
- properties: {
46
- id: { type: "string", minLength: 1 },
47
- path: { type: "string", minLength: 1 },
48
- },
49
- },
50
- },
51
- fixtureMatrix: {
52
- type: "object",
53
- additionalProperties: false,
54
- required: [
55
- "initSupport",
56
- "packageAdditionSupport",
57
- "supportedCombinations",
58
- "semanticSkips",
59
- "checkRequirements",
60
- "environmentPreparation",
61
- ],
62
- properties: {
63
- initSupport: {
64
- type: "array",
65
- items: {
66
- type: "object",
67
- additionalProperties: false,
68
- required: ["preset"],
69
- properties: {
70
- preset: { type: "string", minLength: 1 },
71
- },
72
- },
73
- },
74
- packageAdditionSupport: {
75
- type: "array",
76
- items: {
77
- type: "object",
78
- additionalProperties: false,
79
- required: ["preset", "packageLeafName"],
80
- properties: {
81
- preset: { type: "string", minLength: 1 },
82
- packageLeafName: {
83
- type: "string",
84
- minLength: 1,
85
- pattern: packageLeafNamePattern,
86
- },
87
- },
88
- },
89
- },
90
- supportedCombinations: {
91
- type: "array",
92
- items: {
93
- type: "object",
94
- additionalProperties: false,
95
- required: ["basePreset", "addedPreset"],
96
- properties: {
97
- basePreset: { type: "string", minLength: 1 },
98
- addedPreset: { type: "string", minLength: 1 },
99
- linkFrom: {
100
- type: "array",
101
- items: { type: "string", minLength: 1 },
102
- },
103
- },
104
- },
105
- },
106
- semanticSkips: {
107
- type: "array",
108
- items: {
109
- type: "object",
110
- additionalProperties: false,
111
- required: ["basePreset", "addedPreset", "reason"],
112
- properties: {
113
- basePreset: { type: "string", minLength: 1 },
114
- addedPreset: { type: "string", minLength: 1 },
115
- reason: { type: "string", minLength: 1 },
116
- },
117
- },
118
- },
119
- checkRequirements: {
120
- type: "array",
121
- items: {
122
- enum: [...requiredFixtureMatrixCheckRequirements],
123
- },
124
- uniqueItems: true,
125
- },
126
- environmentPreparation: {
127
- type: "array",
128
- items: {
129
- enum: [...requiredFixtureMatrixEnvironmentPreparation],
130
- },
131
- uniqueItems: true,
132
- },
133
- },
134
- },
135
- presets: {
136
- type: "array",
137
- minItems: 1,
138
- items: {
139
- type: "object",
140
- additionalProperties: false,
141
- required: [
142
- "name",
143
- "title",
144
- "description",
145
- "generation",
146
- "supportedPackageManagers",
147
- "supportedProjectKinds",
148
- "packageAdditionSupport",
149
- "features",
150
- ],
151
- properties: {
152
- name: { type: "string", minLength: 1 },
153
- title: { type: "string", minLength: 1 },
154
- description: { type: "string", minLength: 1 },
155
- generation: { enum: ["supported", "future"] },
156
- supportedPackageManagers: {
157
- type: "array",
158
- items: { enum: ["pnpm"] },
159
- uniqueItems: true,
160
- },
161
- supportedProjectKinds: {
162
- type: "array",
163
- minItems: 1,
164
- items: { enum: ["multi-package"] },
165
- uniqueItems: true,
166
- },
167
- packageAdditionSupport: {
168
- enum: [
169
- PackageAdditionSupport.Supported,
170
- PackageAdditionSupport.Unsupported,
171
- ],
172
- },
173
- features: {
174
- type: "array",
175
- items: { enum: featureNames },
176
- uniqueItems: true,
177
- },
178
- dependencyCatalog: {
179
- type: "array",
180
- items: { type: "string", minLength: 1 },
181
- uniqueItems: true,
182
- },
183
- projection: {
184
- type: "object",
185
- additionalProperties: false,
186
- required: ["capabilities"],
187
- properties: {
188
- capabilities: {
189
- type: "array",
190
- items: {
191
- oneOf: [
192
- {
193
- type: "object",
194
- additionalProperties: false,
195
- required: [
196
- "kind",
197
- "workspacePackageGlob",
198
- "packageRole",
199
- "packageSourcePreset",
200
- "sourceFiles",
201
- ],
202
- properties: {
203
- kind: { const: "workspace-library-package" },
204
- workspacePackageGlob: { const: "packages/*" },
205
- packageRole: { const: "shared-library" },
206
- packageSourcePreset: { const: "ts-lib" },
207
- sourceFiles: {
208
- type: "array",
209
- minItems: 1,
210
- items: { type: "string", minLength: 1 },
211
- },
212
- },
213
- },
214
- {
215
- type: "object",
216
- additionalProperties: false,
217
- required: ["kind", "workspacePackageGlob", "packages"],
218
- properties: {
219
- kind: { const: "workspace-node-packages" },
220
- workspacePackageGlob: { const: "apps/*" },
221
- packages: {
222
- type: "array",
223
- minItems: 1,
224
- items: {
225
- type: "object",
226
- additionalProperties: false,
227
- required: ["kind", "path", "sourceFiles"],
228
- properties: {
229
- kind: { enum: ["hono-api", "vue-app"] },
230
- path: { enum: ["apps/api", "apps/web"] },
231
- sourceFiles: {
232
- type: "array",
233
- minItems: 1,
234
- items: { type: "string", minLength: 1 },
235
- },
236
- },
237
- },
238
- },
239
- packageLinks: {
240
- type: "array",
241
- items: {
242
- type: "object",
243
- additionalProperties: false,
244
- required: [
245
- "consumerPackagePath",
246
- "providerPackagePath",
247
- ],
248
- properties: {
249
- consumerPackagePath: { const: "apps/web" },
250
- providerPackagePath: { const: "apps/api" },
251
- },
252
- },
253
- },
254
- },
255
- },
256
- {
257
- type: "object",
258
- additionalProperties: false,
259
- required: ["kind", "workspacePackageGlob", "sourceFiles"],
260
- properties: {
261
- kind: { const: "rust-binary-workspace" },
262
- workspacePackageGlob: { const: "packages/*" },
263
- sourceFiles: {
264
- type: "array",
265
- minItems: 1,
266
- items: { type: "string", minLength: 1 },
267
- },
268
- },
269
- },
270
- {
271
- type: "object",
272
- additionalProperties: false,
273
- required: ["kind"],
274
- properties: {
275
- kind: { const: "strict-typescript-root" },
276
- },
277
- },
278
- {
279
- type: "object",
280
- additionalProperties: false,
281
- required: ["kind"],
282
- properties: {
283
- kind: { const: "oxc-format-lint" },
284
- },
285
- },
286
- {
287
- type: "object",
288
- additionalProperties: false,
289
- required: ["kind"],
290
- properties: {
291
- kind: { const: "node-pnpm-devcontainer" },
292
- },
293
- },
294
- {
295
- type: "object",
296
- additionalProperties: false,
297
- required: ["kind"],
298
- properties: {
299
- kind: { const: "github-maintenance" },
300
- },
301
- },
302
- ],
303
- },
304
- },
305
- },
306
- },
307
- source: {
308
- type: "object",
309
- additionalProperties: false,
310
- properties: {
311
- roots: {
312
- type: "array",
313
- items: { type: "string", minLength: 1 },
314
- },
315
- files: {
316
- type: "array",
317
- items: { type: "string", minLength: 1 },
318
- },
319
- sharedResources: {
320
- type: "array",
321
- items: { type: "string", minLength: 1 },
322
- },
323
- },
324
- },
325
- },
326
- },
327
- },
328
- },
329
- };
330
- const nonEmptyString = v.pipe(v.string(), v.minLength(1));
331
- const packageManagerSchema = v.picklist(["pnpm"]);
332
- const projectKindSchema = v.picklist([
333
- "single-package",
334
- "multi-package",
335
- ]);
336
- const packageAdditionSupportSchema = v.picklist([
337
- PackageAdditionSupport.Supported,
338
- PackageAdditionSupport.Unsupported,
339
- ]);
340
- const featureNameSchema = v.picklist(featureNames);
341
- const presetSourceManifestPresetSourceSchema = v.strictObject({
342
- roots: v.optional(v.array(nonEmptyString), []),
343
- files: v.optional(v.array(nonEmptyString), []),
344
- sharedResources: v.optional(v.array(nonEmptyString), []),
345
- });
346
- const fixtureMatrixPresetSupportSchema = v.strictObject({
347
- preset: nonEmptyString,
348
- });
349
- const fixtureMatrixPackageAdditionSupportSchema = v.strictObject({
350
- preset: nonEmptyString,
351
- packageLeafName: v.pipe(v.string(), v.regex(packageLeafNameRegExp, "Fixture Matrix Package Addition packageLeafName must be a lowercase package leaf name using letters, numbers, and hyphens")),
352
- });
353
- const fixtureMatrixCombinationSchema = v.strictObject({
354
- basePreset: nonEmptyString,
355
- addedPreset: nonEmptyString,
356
- linkFrom: v.optional(v.array(nonEmptyString), []),
357
- });
358
- const fixtureMatrixSemanticSkipSchema = v.strictObject({
359
- basePreset: nonEmptyString,
360
- addedPreset: nonEmptyString,
361
- reason: nonEmptyString,
362
- });
363
- const fixtureMatrixContractSchema = v.strictObject({
364
- initSupport: v.array(fixtureMatrixPresetSupportSchema),
365
- packageAdditionSupport: v.array(fixtureMatrixPackageAdditionSupportSchema),
366
- supportedCombinations: v.array(fixtureMatrixCombinationSchema),
367
- semanticSkips: v.array(fixtureMatrixSemanticSkipSchema),
368
- checkRequirements: v.array(v.picklist(requiredFixtureMatrixCheckRequirements)),
369
- environmentPreparation: v.array(v.picklist(requiredFixtureMatrixEnvironmentPreparation)),
370
- });
371
- const projectionCapabilityKindSchema = v.string();
372
- const presetProjectionDeclarationSchema = v.strictObject({
373
- capabilities: v.array(v.looseObject({
374
- kind: projectionCapabilityKindSchema,
375
- })),
376
- });
377
- const presetSourceManifestSchema = v.strictObject({
378
- schemaVersion: v.literal(1),
379
- name: nonEmptyString,
380
- sharedResources: v.optional(v.array(v.strictObject({
381
- id: nonEmptyString,
382
- path: nonEmptyString,
383
- })), []),
384
- fixtureMatrix: v.optional(fixtureMatrixContractSchema),
385
- presets: v.pipe(v.array(v.strictObject({
386
- name: nonEmptyString,
387
- title: nonEmptyString,
388
- description: nonEmptyString,
389
- generation: v.picklist(["supported", "future"]),
390
- supportedPackageManagers: v.array(packageManagerSchema),
391
- supportedProjectKinds: v.pipe(v.array(projectKindSchema), v.minLength(1)),
392
- packageAdditionSupport: packageAdditionSupportSchema,
393
- features: v.array(featureNameSchema),
394
- dependencyCatalog: v.optional(v.array(nonEmptyString), []),
395
- projection: v.optional(presetProjectionDeclarationSchema),
396
- source: v.optional(presetSourceManifestPresetSourceSchema),
397
- })), v.minLength(1)),
398
- });
399
- function formatIssuePath(issue) {
400
- if (!issue.path || issue.path.length === 0) {
401
- return "$";
402
- }
403
- return issue.path.reduce((issuePath, item) => {
404
- if (typeof item.key === "number") {
405
- return `${issuePath}[${item.key}]`;
406
- }
407
- return `${issuePath}.${String(item.key)}`;
408
- }, "$");
409
- }
410
- function shapeIssues(issues) {
411
- return issues.map((issue) => {
412
- const validationPath = formatIssuePath(issue);
413
- const missingPresetMetadata = validationPath.match(/^\$\.presets\[\d+\]\.(.+)$/);
414
- if (missingPresetMetadata && issue.message.includes("received undefined")) {
415
- return {
416
- path: validationPath,
417
- message: `Preset metadata is missing required field: ${missingPresetMetadata[1]}`,
418
- };
419
- }
420
- if (issue.message.startsWith("Invalid key:") &&
421
- /\.(body|content|text)$/.test(validationPath)) {
422
- const key = validationPath.split(".").at(-1);
423
- return {
424
- path: validationPath,
425
- message: `Preset Source Manifests must reference Generated Repository file bodies by path, not inline ${key}`,
426
- };
427
- }
428
- return {
429
- path: validationPath,
430
- message: validationPath.match(/^\$\.presets\[\d+\]\.packageAdditionSupport$/)
431
- ? "Package Addition Support must be one of: supported, unsupported"
432
- : issue.message,
433
- };
434
- });
435
- }
436
- function duplicatePresetNameIssues(presets) {
437
- const seen = new Set();
438
- const duplicates = new Set();
439
- for (const preset of presets) {
440
- if (seen.has(preset.name)) {
441
- duplicates.add(preset.name);
442
- }
443
- seen.add(preset.name);
444
- }
445
- return [...duplicates].map((name) => ({
446
- path: "$.presets.name",
447
- message: `Duplicate Preset name: ${name}`,
448
- }));
449
- }
450
- function duplicateValueIssues(values, validationPath) {
451
- const seen = new Set();
452
- const duplicates = new Set();
453
- for (const value of values) {
454
- if (seen.has(value)) {
455
- duplicates.add(value);
456
- }
457
- seen.add(value);
458
- }
459
- return [...duplicates].map((value) => ({
460
- path: validationPath,
461
- message: `Duplicate value: ${value}`,
462
- }));
463
- }
464
- function duplicatePresetMetadataArrayIssues(presets) {
465
- return presets.flatMap((preset, index) => [
466
- ...duplicateValueIssues(preset.supportedPackageManagers, `$.presets[${index}].supportedPackageManagers`),
467
- ...duplicateValueIssues(preset.supportedProjectKinds, `$.presets[${index}].supportedProjectKinds`),
468
- ...duplicateValueIssues(preset.features, `$.presets[${index}].features`),
469
- ...duplicateValueIssues(preset.dependencyCatalog ?? [], `$.presets[${index}].dependencyCatalog`),
470
- ]);
471
- }
472
- function fixtureCombinationKey(combination) {
473
- return [
474
- combination.basePreset,
475
- combination.addedPreset,
476
- ...(combination.linkFrom ?? []),
477
- ].join("\0");
478
- }
479
- function fixtureCombinationPairKey(combination) {
480
- return [combination.basePreset, combination.addedPreset].join("\0");
481
- }
482
- function fixtureMatrixContractIssues(manifest) {
483
- if (!manifest.fixtureMatrix) {
484
- return [];
485
- }
486
- const issues = [];
487
- const presetsByName = new Map(manifest.presets.map((preset) => [preset.name, preset]));
488
- const initPresets = new Set(manifest.fixtureMatrix.initSupport.map((support) => support.preset));
489
- const addablePresets = new Set(manifest.fixtureMatrix.packageAdditionSupport.map((support) => support.preset));
490
- const validInitPresets = new Set();
491
- const validAddablePresets = new Set();
492
- const supportedCombinationKeys = new Set();
493
- const supportedCombinationPairKeys = new Set();
494
- const semanticSkipKeys = new Set();
495
- const semanticSkipPairKeys = new Set();
496
- manifest.fixtureMatrix.initSupport.forEach((support, index) => {
497
- const preset = presetsByName.get(support.preset);
498
- if (!preset) {
499
- issues.push({
500
- path: `$.fixtureMatrix.initSupport[${index}].preset`,
501
- message: `Fixture Matrix init support references unknown Preset: ${support.preset}`,
502
- });
503
- return;
504
- }
505
- if (preset.generation !== "supported") {
506
- issues.push({
507
- path: `$.fixtureMatrix.initSupport[${index}].preset`,
508
- message: `Fixture Matrix init support must reference supported Presets: ${support.preset}`,
509
- });
510
- return;
511
- }
512
- validInitPresets.add(support.preset);
513
- });
514
- manifest.presets.forEach((preset, index) => {
515
- if (preset.generation === "supported" &&
516
- preset.packageAdditionSupport === PackageAdditionSupport.Supported &&
517
- !addablePresets.has(preset.name)) {
518
- issues.push({
519
- path: `$.presets[${index}].packageAdditionSupport`,
520
- message: `Fixture Matrix Package Addition support must declare supported Preset: ${preset.name}`,
521
- });
522
- }
523
- });
524
- manifest.fixtureMatrix.packageAdditionSupport.forEach((support, index) => {
525
- const preset = presetsByName.get(support.preset);
526
- if (!preset) {
527
- issues.push({
528
- path: `$.fixtureMatrix.packageAdditionSupport[${index}].preset`,
529
- message: `Fixture Matrix Package Addition support references unknown Preset: ${support.preset}`,
530
- });
531
- return;
532
- }
533
- if (preset.packageAdditionSupport !== PackageAdditionSupport.Supported) {
534
- issues.push({
535
- path: `$.fixtureMatrix.packageAdditionSupport[${index}].preset`,
536
- message: `Fixture Matrix Package Addition support must match Preset metadata: ${support.preset} is ${preset.packageAdditionSupport}`,
537
- });
538
- return;
539
- }
540
- validAddablePresets.add(support.preset);
541
- });
542
- manifest.fixtureMatrix.supportedCombinations.forEach((combination, index) => {
543
- const key = fixtureCombinationKey(combination);
544
- supportedCombinationPairKeys.add(fixtureCombinationPairKey(combination));
545
- if (supportedCombinationKeys.has(key)) {
546
- issues.push({
547
- path: `$.fixtureMatrix.supportedCombinations[${index}]`,
548
- message: `Duplicate Fixture Matrix supported combination: ${combination.basePreset} + ${combination.addedPreset}`,
549
- });
550
- }
551
- supportedCombinationKeys.add(key);
552
- if (!initPresets.has(combination.basePreset)) {
553
- issues.push({
554
- path: `$.fixtureMatrix.supportedCombinations[${index}].basePreset`,
555
- message: `Fixture Matrix supported combination base Preset is not init-supported: ${combination.basePreset}`,
556
- });
557
- }
558
- if (!addablePresets.has(combination.addedPreset)) {
559
- issues.push({
560
- path: `$.fixtureMatrix.supportedCombinations[${index}].addedPreset`,
561
- message: `Fixture Matrix supported combination added Preset is not Package Addition-supported: ${combination.addedPreset}`,
562
- });
563
- }
564
- });
565
- manifest.fixtureMatrix.semanticSkips.forEach((skip, index) => {
566
- const key = fixtureCombinationKey(skip);
567
- semanticSkipPairKeys.add(fixtureCombinationPairKey(skip));
568
- if (semanticSkipKeys.has(key)) {
569
- issues.push({
570
- path: `$.fixtureMatrix.semanticSkips[${index}]`,
571
- message: `Duplicate Fixture Matrix semantic skip: ${skip.basePreset} + ${skip.addedPreset}`,
572
- });
573
- }
574
- semanticSkipKeys.add(key);
575
- if (!initPresets.has(skip.basePreset)) {
576
- issues.push({
577
- path: `$.fixtureMatrix.semanticSkips[${index}].basePreset`,
578
- message: `Fixture Matrix semantic skip base Preset is not init-supported: ${skip.basePreset}`,
579
- });
580
- }
581
- if (!presetsByName.has(skip.addedPreset)) {
582
- issues.push({
583
- path: `$.fixtureMatrix.semanticSkips[${index}].addedPreset`,
584
- message: `Fixture Matrix semantic skip references unknown added Preset: ${skip.addedPreset}`,
585
- });
586
- }
587
- });
588
- for (const basePreset of validInitPresets) {
589
- for (const addedPreset of validAddablePresets) {
590
- const key = fixtureCombinationPairKey({ basePreset, addedPreset });
591
- if (!supportedCombinationPairKeys.has(key) &&
592
- !semanticSkipPairKeys.has(key)) {
593
- issues.push({
594
- path: "$.fixtureMatrix.supportedCombinations",
595
- message: `Fixture Matrix must explicitly cover supported combination or semantic skip: ${basePreset} + ${addedPreset}`,
596
- });
597
- }
598
- }
599
- }
600
- for (const checkRequirement of requiredFixtureMatrixCheckRequirements) {
601
- if (!manifest.fixtureMatrix.checkRequirements.includes(checkRequirement)) {
602
- issues.push({
603
- path: "$.fixtureMatrix.checkRequirements",
604
- message: `Fixture Matrix check requirements must include ${checkRequirement}`,
605
- });
606
- }
607
- }
608
- for (const environmentPreparation of requiredFixtureMatrixEnvironmentPreparation) {
609
- if (!manifest.fixtureMatrix.environmentPreparation.includes(environmentPreparation)) {
610
- issues.push({
611
- path: "$.fixtureMatrix.environmentPreparation",
612
- message: `Fixture Matrix environment preparation must include ${environmentPreparation}`,
613
- });
614
- }
615
- }
616
- for (const key of semanticSkipPairKeys) {
617
- if (supportedCombinationPairKeys.has(key)) {
618
- issues.push({
619
- path: "$.fixtureMatrix.semanticSkips",
620
- message: "Fixture Matrix semantic skips must not duplicate supported combinations",
621
- });
622
- }
623
- }
624
- return [
625
- ...duplicateValueIssues(manifest.fixtureMatrix.initSupport.map((support) => support.preset), "$.fixtureMatrix.initSupport.preset"),
626
- ...duplicateValueIssues(manifest.fixtureMatrix.packageAdditionSupport.map((support) => support.preset), "$.fixtureMatrix.packageAdditionSupport.preset"),
627
- ...duplicateValueIssues(manifest.fixtureMatrix.checkRequirements, "$.fixtureMatrix.checkRequirements"),
628
- ...duplicateValueIssues(manifest.fixtureMatrix.environmentPreparation, "$.fixtureMatrix.environmentPreparation"),
629
- ...issues,
630
- ];
631
- }
5
+ export { presetSourceManifestJsonSchema };
632
6
  function dependencyCatalogReferenceIssues(presets, dependencyCatalog) {
633
- return presets.flatMap((preset, presetIndex) => (preset.dependencyCatalog ?? [])
634
- .filter((dependency) => dependencyCatalog[dependency] === undefined)
635
- .map((dependency) => ({
636
- path: `$.presets[${presetIndex}].dependencyCatalog`,
637
- message: `Preset ${preset.name} references missing Template Dependency Catalog entry: ${dependency}`,
638
- })));
639
- }
640
- function unsupportedProjectShapeIssues(presets) {
641
- return presets.flatMap((preset, index) => preset.supportedProjectKinds.includes("single-package")
642
- ? [
643
- {
644
- path: `$.presets[${index}].supportedProjectKinds`,
645
- message: "single-package Project Shape is unsupported in V1; use the workspace monorepo Project Shape",
646
- },
647
- ]
648
- : []);
7
+ return presets.flatMap((preset, fallbackPresetIndex) => {
8
+ const presetIndex = preset.manifestIndex ?? fallbackPresetIndex;
9
+ return (preset.dependencyCatalog ?? [])
10
+ .filter((dependency) => dependencyCatalog[dependency] === undefined)
11
+ .map((dependency) => ({
12
+ path: `$.presets[${presetIndex}].dependencyCatalog`,
13
+ message: `Preset ${preset.name} references missing Template Dependency Catalog entry: ${dependency}`,
14
+ }));
15
+ });
649
16
  }
650
17
  function resolvePresetSourcePath(sourceRoot, referencePath) {
651
18
  if (path.isAbsolute(referencePath)) {
@@ -674,12 +41,13 @@ function sharedResourcePathIssues(manifest, sourceRoot) {
674
41
  if (sourceRoot === undefined) {
675
42
  return [];
676
43
  }
677
- return manifest.sharedResources.flatMap((resource, index) => {
44
+ return manifest.sharedResources.flatMap((resource, fallbackResourceIndex) => {
45
+ const resourceIndex = resource.manifestIndex ?? fallbackResourceIndex;
678
46
  const resolvedPath = resolvePresetSourcePath(sourceRoot, resource.path);
679
47
  if (typeof resolvedPath !== "string") {
680
48
  return [
681
49
  {
682
- path: `$.sharedResources[${index}].path`,
50
+ path: `$.sharedResources[${resourceIndex}].path`,
683
51
  message: resolvedPath.issue,
684
52
  },
685
53
  ];
@@ -687,7 +55,7 @@ function sharedResourcePathIssues(manifest, sourceRoot) {
687
55
  if (!existsSync(resolvedPath)) {
688
56
  return [
689
57
  {
690
- path: `$.sharedResources[${index}].path`,
58
+ path: `$.sharedResources[${resourceIndex}].path`,
691
59
  message: `Shared Resource ${resource.id} path does not exist: ${resource.path}`,
692
60
  },
693
61
  ];
@@ -696,7 +64,7 @@ function sharedResourcePathIssues(manifest, sourceRoot) {
696
64
  if (realPathIssue) {
697
65
  return [
698
66
  {
699
- path: `$.sharedResources[${index}].path`,
67
+ path: `$.sharedResources[${resourceIndex}].path`,
700
68
  message: realPathIssue,
701
69
  },
702
70
  ];
@@ -739,9 +107,55 @@ function inlineDependencyCatalogSpecifierIssues(input) {
739
107
  }));
740
108
  });
741
109
  }
110
+ function stringArray(value) {
111
+ return Array.isArray(value) &&
112
+ value.every((item) => typeof item === "string")
113
+ ? value
114
+ : undefined;
115
+ }
116
+ function corePresetSourceFromUnknown(source) {
117
+ if (!isRecord(source)) {
118
+ return undefined;
119
+ }
120
+ return {
121
+ roots: stringArray(source.roots) ?? [],
122
+ files: stringArray(source.files) ?? [],
123
+ sharedResources: stringArray(source.sharedResources) ?? [],
124
+ };
125
+ }
126
+ function coreSemanticManifestFromUnknown(input) {
127
+ if (!isRecord(input)) {
128
+ return undefined;
129
+ }
130
+ const sharedResources = Array.isArray(input.sharedResources)
131
+ ? input.sharedResources.flatMap((resource, manifestIndex) => isRecord(resource) &&
132
+ typeof resource.id === "string" &&
133
+ typeof resource.path === "string"
134
+ ? [{ id: resource.id, path: resource.path, manifestIndex }]
135
+ : [])
136
+ : [];
137
+ const presets = Array.isArray(input.presets)
138
+ ? input.presets.flatMap((preset, manifestIndex) => {
139
+ if (!isRecord(preset) || typeof preset.name !== "string") {
140
+ return [];
141
+ }
142
+ const source = corePresetSourceFromUnknown(preset.source);
143
+ return [
144
+ {
145
+ manifestIndex,
146
+ name: preset.name,
147
+ dependencyCatalog: stringArray(preset.dependencyCatalog) ?? [],
148
+ ...(source === undefined ? {} : { source }),
149
+ },
150
+ ];
151
+ })
152
+ : [];
153
+ return { sharedResources, presets };
154
+ }
742
155
  function presetSourceReferenceIssues(manifest, sourceRoot) {
743
156
  const sharedResourceIds = new Set(manifest.sharedResources.map((resource) => resource.id));
744
- return manifest.presets.flatMap((preset, presetIndex) => {
157
+ return manifest.presets.flatMap((preset, fallbackPresetIndex) => {
158
+ const presetIndex = preset.manifestIndex ?? fallbackPresetIndex;
745
159
  const source = preset.source;
746
160
  if (!source) {
747
161
  return [];
@@ -787,6 +201,13 @@ function presetSourceReferenceIssues(manifest, sourceRoot) {
787
201
  return [...sharedResourceIssues, ...pathIssues];
788
202
  });
789
203
  }
204
+ function coreSemanticIssues(manifest, options) {
205
+ return [
206
+ ...dependencyCatalogReferenceIssues(manifest.presets, options.dependencyCatalog ?? loadTemplateDependencyCatalog()),
207
+ ...sharedResourcePathIssues(manifest, options.sourceRoot),
208
+ ...presetSourceReferenceIssues(manifest, options.sourceRoot),
209
+ ];
210
+ }
790
211
  function supportedProjectionDeclarationIssues(presets) {
791
212
  return presets.flatMap((preset, index) => preset.generation === "supported" && preset.projection === undefined
792
213
  ? [
@@ -797,91 +218,25 @@ function supportedProjectionDeclarationIssues(presets) {
797
218
  ]
798
219
  : []);
799
220
  }
800
- function normalizePresetSourceManifestOutput(manifest) {
801
- return {
802
- schemaVersion: manifest.schemaVersion,
803
- name: manifest.name,
804
- sharedResources: manifest.sharedResources.map((resource) => ({
805
- ...resource,
806
- })),
807
- ...(manifest.fixtureMatrix
808
- ? {
809
- fixtureMatrix: {
810
- initSupport: manifest.fixtureMatrix.initSupport.map((support) => ({
811
- ...support,
812
- })),
813
- packageAdditionSupport: manifest.fixtureMatrix.packageAdditionSupport.map((support) => ({
814
- ...support,
815
- })),
816
- supportedCombinations: manifest.fixtureMatrix.supportedCombinations.map((combination) => ({
817
- basePreset: combination.basePreset,
818
- addedPreset: combination.addedPreset,
819
- linkFrom: [...combination.linkFrom],
820
- })),
821
- semanticSkips: manifest.fixtureMatrix.semanticSkips.map((skip) => ({
822
- ...skip,
823
- })),
824
- checkRequirements: [...manifest.fixtureMatrix.checkRequirements],
825
- environmentPreparation: [
826
- ...manifest.fixtureMatrix.environmentPreparation,
827
- ],
828
- },
829
- }
830
- : {}),
831
- presets: manifest.presets.map((preset) => ({
832
- name: preset.name,
833
- title: preset.title,
834
- description: preset.description,
835
- generation: preset.generation,
836
- packageAdditionSupport: preset.packageAdditionSupport,
837
- supportedPackageManagers: [...preset.supportedPackageManagers],
838
- supportedProjectKinds: [...preset.supportedProjectKinds],
839
- features: [...preset.features],
840
- dependencyCatalog: [...preset.dependencyCatalog],
841
- ...(preset.projection
842
- ? {
843
- projection: normalizePresetProjectionDeclaration(preset.projection),
844
- }
845
- : {}),
846
- ...(preset.source
847
- ? {
848
- source: {
849
- roots: [...preset.source.roots],
850
- files: [...preset.source.files],
851
- sharedResources: [...preset.source.sharedResources],
852
- },
853
- }
854
- : {}),
855
- })),
856
- };
857
- }
858
221
  export function validatePresetSourceManifest(input, options = {}) {
859
222
  const inlineDependencyIssues = inlineDependencyCatalogSpecifierIssues(input);
860
223
  if (inlineDependencyIssues.length > 0) {
861
224
  return { ok: false, issues: inlineDependencyIssues };
862
225
  }
863
- const result = v.safeParse(presetSourceManifestSchema, input);
864
- if (!result.success) {
865
- return { ok: false, issues: shapeIssues(result.issues) };
866
- }
867
- const parsedManifest = result.output;
868
- const semanticIssues = [
869
- ...duplicateValueIssues(parsedManifest.sharedResources.map((resource) => resource.id), "$.sharedResources.id"),
870
- ...duplicatePresetNameIssues(parsedManifest.presets),
871
- ...duplicatePresetMetadataArrayIssues(parsedManifest.presets),
872
- ...unsupportedProjectShapeIssues(parsedManifest.presets),
873
- ...fixtureMatrixContractIssues(parsedManifest),
874
- ...dependencyCatalogReferenceIssues(parsedManifest.presets, options.dependencyCatalog ?? loadTemplateDependencyCatalog()),
875
- ...projectionCapabilityIssues(parsedManifest.presets),
876
- ...sharedResourcePathIssues(parsedManifest, options.sourceRoot),
877
- ...presetSourceReferenceIssues(parsedManifest, options.sourceRoot),
878
- ];
879
- if (semanticIssues.length > 0) {
880
- return { ok: false, issues: semanticIssues };
226
+ const result = validatePresetSourceManifestDeclaration(input);
227
+ const manifest = result.ok
228
+ ? result.value
229
+ : coreSemanticManifestFromUnknown(input);
230
+ const semanticIssues = manifest ? coreSemanticIssues(manifest, options) : [];
231
+ if (!result.ok || semanticIssues.length > 0) {
232
+ return {
233
+ ok: false,
234
+ issues: [...(result.ok ? [] : result.issues), ...semanticIssues],
235
+ };
881
236
  }
882
237
  return {
883
238
  ok: true,
884
- value: normalizePresetSourceManifestOutput(parsedManifest),
239
+ value: result.value,
885
240
  };
886
241
  }
887
242
  export function validateBuiltInPresetSourceManifest(input, options = {}) {