@ykdz/template 0.0.10 → 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 (45) 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 +1 -1
  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 +6 -5
  15. package/node_modules/@ykdz/template-builtin-source/package.json +3 -2
  16. package/node_modules/@ykdz/template-builtin-source/templates/preset-source.json +35 -9
  17. package/node_modules/@ykdz/template-core/dist/declarations.d.ts +2 -203
  18. package/node_modules/@ykdz/template-core/dist/declarations.js +1 -303
  19. package/node_modules/@ykdz/template-core/dist/devcontainer.d.ts +17 -0
  20. package/node_modules/@ykdz/template-core/dist/devcontainer.js +22 -21
  21. package/node_modules/@ykdz/template-core/dist/editor-customization.d.ts +10 -1
  22. package/node_modules/@ykdz/template-core/dist/editor-customization.js +36 -26
  23. package/node_modules/@ykdz/template-core/dist/generated-scenarios.d.ts +4 -0
  24. package/node_modules/@ykdz/template-core/dist/generated-scenarios.js +32 -22
  25. package/node_modules/@ykdz/template-core/dist/generation-context.d.ts +1 -1
  26. package/node_modules/@ykdz/template-core/dist/module-graph.d.ts +19 -1
  27. package/node_modules/@ykdz/template-core/dist/module-graph.js +42 -3
  28. package/node_modules/@ykdz/template-core/dist/next-step-instructions.d.ts +2 -0
  29. package/node_modules/@ykdz/template-core/dist/next-step-instructions.js +11 -22
  30. package/node_modules/@ykdz/template-core/dist/package-addition-support.d.ts +2 -1
  31. package/node_modules/@ykdz/template-core/dist/package-addition-support.js +2 -4
  32. package/node_modules/@ykdz/template-core/dist/package-addition.js +2 -2
  33. package/node_modules/@ykdz/template-core/dist/package-linking.d.ts +2 -6
  34. package/node_modules/@ykdz/template-core/dist/package-linking.js +1 -1
  35. package/node_modules/@ykdz/template-core/dist/preset-projection.d.ts +1 -2
  36. package/node_modules/@ykdz/template-core/dist/preset-source.d.ts +4 -418
  37. package/node_modules/@ykdz/template-core/dist/preset-source.js +81 -726
  38. package/node_modules/@ykdz/template-core/dist/project-github.js +2 -6
  39. package/node_modules/@ykdz/template-core/dist/projection-capabilities.d.ts +5 -53
  40. package/node_modules/@ykdz/template-core/dist/projection-capabilities.js +99 -497
  41. package/node_modules/@ykdz/template-core/package.json +3 -2
  42. package/node_modules/@ykdz/template-shared/dist/index.d.ts +794 -0
  43. package/node_modules/@ykdz/template-shared/dist/index.js +1637 -0
  44. package/node_modules/@ykdz/template-shared/package.json +32 -0
  45. package/package.json +9 -6
@@ -1,303 +1 @@
1
- import * as v from "valibot";
2
- const featureNames = [
3
- "pnpm-catalog",
4
- "oxc-format-lint",
5
- "strict-typescript",
6
- "root-check",
7
- "fix-command",
8
- "devcontainer",
9
- "github-actions",
10
- "dependabot",
11
- "rustfmt-clippy",
12
- "cargo-test",
13
- "native-binary-release",
14
- ];
15
- export const presetFileJsonSchema = {
16
- $schema: "https://json-schema.org/draft/2020-12/schema",
17
- $id: "https://ykdz.dev/schemas/project-kit/preset-file.schema.json",
18
- title: "Project Kit Preset File",
19
- type: "object",
20
- additionalProperties: false,
21
- required: [
22
- "schemaVersion",
23
- "name",
24
- "title",
25
- "description",
26
- "supportedPackageManagers",
27
- "supportedProjectKinds",
28
- "features",
29
- ],
30
- properties: {
31
- schemaVersion: { const: 1 },
32
- name: { type: "string", minLength: 1 },
33
- title: { type: "string", minLength: 1 },
34
- description: { type: "string", minLength: 1 },
35
- supportedPackageManagers: {
36
- type: "array",
37
- items: { enum: ["pnpm"] },
38
- uniqueItems: true,
39
- },
40
- supportedProjectKinds: {
41
- type: "array",
42
- minItems: 1,
43
- items: { enum: ["multi-package"] },
44
- uniqueItems: true,
45
- },
46
- features: {
47
- type: "array",
48
- items: { enum: featureNames },
49
- uniqueItems: true,
50
- },
51
- },
52
- };
53
- export const blueprintJsonSchema = {
54
- $schema: "https://json-schema.org/draft/2020-12/schema",
55
- $id: "https://ykdz.dev/schemas/project-kit/blueprint.schema.json",
56
- title: "Project Kit Blueprint",
57
- type: "object",
58
- additionalProperties: false,
59
- required: ["schemaVersion", "preset", "projectKind", "features"],
60
- properties: {
61
- schemaVersion: { const: 1 },
62
- preset: { type: "string", minLength: 1 },
63
- packageManager: { enum: ["pnpm"] },
64
- projectKind: { enum: ["multi-package"] },
65
- features: {
66
- type: "array",
67
- items: { enum: featureNames },
68
- uniqueItems: true,
69
- },
70
- packages: {
71
- type: "array",
72
- items: {
73
- type: "object",
74
- additionalProperties: false,
75
- required: ["name", "path"],
76
- properties: {
77
- name: { type: "string", minLength: 1 },
78
- path: { type: "string", minLength: 1 },
79
- role: { enum: ["runtime-service", "shared-library"] },
80
- sourcePreset: { enum: ["hono-api", "ts-lib", "vue-app"] },
81
- },
82
- },
83
- },
84
- packageLinkIntents: {
85
- type: "array",
86
- items: {
87
- type: "object",
88
- additionalProperties: false,
89
- required: ["consumerPackagePath", "providerPackagePath"],
90
- properties: {
91
- consumerPackagePath: { type: "string", minLength: 1 },
92
- providerPackagePath: { type: "string", minLength: 1 },
93
- },
94
- },
95
- },
96
- },
97
- };
98
- const nonEmptyString = v.pipe(v.string(), v.minLength(1));
99
- const packageManagerSchema = v.picklist(["pnpm"]);
100
- const projectKindSchema = v.picklist([
101
- "single-package",
102
- "multi-package",
103
- ]);
104
- const featureNameSchema = v.picklist(featureNames);
105
- const packageRoleSchema = v.picklist([
106
- "runtime-service",
107
- "shared-library",
108
- ]);
109
- const packageSourcePresetSchema = v.picklist([
110
- "hono-api",
111
- "ts-lib",
112
- "vue-app",
113
- ]);
114
- export const presetFileSchema = v.strictObject({
115
- schemaVersion: v.literal(1),
116
- name: nonEmptyString,
117
- title: nonEmptyString,
118
- description: nonEmptyString,
119
- supportedPackageManagers: v.array(packageManagerSchema),
120
- supportedProjectKinds: v.pipe(v.array(projectKindSchema), v.minLength(1)),
121
- features: v.array(featureNameSchema),
122
- });
123
- export const projectBlueprintSchema = v.strictObject({
124
- schemaVersion: v.literal(1),
125
- preset: nonEmptyString,
126
- packageManager: v.optional(packageManagerSchema),
127
- projectKind: projectKindSchema,
128
- features: v.array(featureNameSchema),
129
- packages: v.optional(v.array(v.strictObject({
130
- name: nonEmptyString,
131
- path: nonEmptyString,
132
- role: v.optional(packageRoleSchema),
133
- sourcePreset: v.optional(packageSourcePresetSchema),
134
- }))),
135
- packageLinkIntents: v.optional(v.array(v.strictObject({
136
- consumerPackagePath: nonEmptyString,
137
- providerPackagePath: nonEmptyString,
138
- }))),
139
- });
140
- function formatIssuePath(issue) {
141
- if (!issue.path || issue.path.length === 0) {
142
- return "$";
143
- }
144
- return issue.path.reduce((path, item) => `${path}.${String(item.key)}`, "$");
145
- }
146
- function shapeIssues(issues) {
147
- return issues.map((issue) => ({
148
- path: formatIssuePath(issue),
149
- message: issue.message,
150
- }));
151
- }
152
- function duplicateIssues(values, path) {
153
- const seen = new Set();
154
- const duplicates = new Set();
155
- for (const value of values) {
156
- if (seen.has(value)) {
157
- duplicates.add(value);
158
- }
159
- seen.add(value);
160
- }
161
- return [...duplicates].map((value) => ({
162
- path,
163
- message: `Duplicate value: ${value}`,
164
- }));
165
- }
166
- function generationSupportIssue(preset, path) {
167
- if (preset.generation === "supported") {
168
- return undefined;
169
- }
170
- return {
171
- path,
172
- message: `Preset ${preset.name} is not supported for generation in this version`,
173
- };
174
- }
175
- function unsupportedSinglePackageProjectShapeIssue(path) {
176
- return {
177
- path,
178
- message: "single-package Project Shape is unsupported in V1; use the workspace monorepo Project Shape",
179
- };
180
- }
181
- export function validatePresetFile(input, options = {}) {
182
- const result = v.safeParse(presetFileSchema, input);
183
- if (!result.success) {
184
- return { ok: false, issues: shapeIssues(result.issues) };
185
- }
186
- const semanticIssues = [
187
- ...duplicateIssues(result.output.supportedPackageManagers, "$.supportedPackageManagers"),
188
- ...duplicateIssues(result.output.supportedProjectKinds, "$.supportedProjectKinds"),
189
- ...duplicateIssues(result.output.features, "$.features"),
190
- ];
191
- if (result.output.supportedProjectKinds.includes("single-package")) {
192
- semanticIssues.push(unsupportedSinglePackageProjectShapeIssue("$.supportedProjectKinds"));
193
- }
194
- const catalogPreset = findPreset(options.presets ?? [], result.output.name);
195
- const unsupportedGeneration = catalogPreset
196
- ? generationSupportIssue(catalogPreset, "$.name")
197
- : undefined;
198
- if (unsupportedGeneration) {
199
- semanticIssues.push(unsupportedGeneration);
200
- }
201
- if (semanticIssues.length > 0) {
202
- return { ok: false, issues: semanticIssues };
203
- }
204
- return { ok: true, value: result.output };
205
- }
206
- export function findPreset(presets, name) {
207
- return presets.find((preset) => preset.name === name);
208
- }
209
- function normalizeProjectBlueprint(blueprint) {
210
- return {
211
- schemaVersion: blueprint.schemaVersion,
212
- preset: blueprint.preset,
213
- ...(blueprint.packageManager === undefined
214
- ? {}
215
- : { packageManager: blueprint.packageManager }),
216
- projectKind: blueprint.projectKind,
217
- features: [...blueprint.features],
218
- ...(blueprint.packages === undefined
219
- ? {}
220
- : {
221
- packages: blueprint.packages.map((projectPackage) => ({
222
- name: projectPackage.name,
223
- path: projectPackage.path,
224
- ...(projectPackage.role === undefined
225
- ? {}
226
- : { role: projectPackage.role }),
227
- ...(projectPackage.sourcePreset === undefined
228
- ? {}
229
- : { sourcePreset: projectPackage.sourcePreset }),
230
- })),
231
- }),
232
- ...(blueprint.packageLinkIntents === undefined
233
- ? {}
234
- : {
235
- packageLinkIntents: blueprint.packageLinkIntents.map((intent) => ({
236
- consumerPackagePath: intent.consumerPackagePath,
237
- providerPackagePath: intent.providerPackagePath,
238
- })),
239
- }),
240
- };
241
- }
242
- export function validateProjectBlueprint(input, options = {}) {
243
- const result = v.safeParse(projectBlueprintSchema, input);
244
- if (!result.success) {
245
- return { ok: false, issues: shapeIssues(result.issues) };
246
- }
247
- const blueprint = normalizeProjectBlueprint(result.output);
248
- const preset = findPreset(options.presets ?? [], blueprint.preset);
249
- const semanticIssues = [
250
- ...duplicateIssues(blueprint.features, "$.features"),
251
- ];
252
- if (blueprint.projectKind === "single-package") {
253
- semanticIssues.push(unsupportedSinglePackageProjectShapeIssue("$.projectKind"));
254
- }
255
- if (options.presets && !preset) {
256
- semanticIssues.push({
257
- path: "$.preset",
258
- message: `Unknown ${options.presetLabel ?? "built-in"} preset: ${blueprint.preset}`,
259
- });
260
- }
261
- else if (preset) {
262
- const unsupportedGeneration = generationSupportIssue(preset, "$.preset");
263
- if (unsupportedGeneration) {
264
- semanticIssues.push(unsupportedGeneration);
265
- }
266
- if (blueprint.packageManager &&
267
- !preset.supportedPackageManagers.includes(blueprint.packageManager)) {
268
- semanticIssues.push({
269
- path: "$.packageManager",
270
- message: `${blueprint.packageManager} is not supported by preset ${preset.name}`,
271
- });
272
- }
273
- if (!blueprint.packageManager &&
274
- preset.supportedPackageManagers.length > 0) {
275
- semanticIssues.push({
276
- path: "$.packageManager",
277
- message: `packageManager is required by preset ${preset.name}`,
278
- });
279
- }
280
- if (!preset.supportedProjectKinds.includes(blueprint.projectKind)) {
281
- semanticIssues.push({
282
- path: "$.projectKind",
283
- message: `${blueprint.projectKind} is not supported by preset ${preset.name}`,
284
- });
285
- }
286
- const supportedFeatures = new Set(preset.features);
287
- for (const feature of blueprint.features) {
288
- if (!supportedFeatures.has(feature)) {
289
- semanticIssues.push({
290
- path: "$.features",
291
- message: `${feature} is not supported by preset ${preset.name}`,
292
- });
293
- }
294
- }
295
- }
296
- if (blueprint.packages) {
297
- semanticIssues.push(...duplicateIssues(blueprint.packages.map((projectPackage) => projectPackage.name), "$.packages.name"), ...duplicateIssues(blueprint.packages.map((projectPackage) => projectPackage.path), "$.packages.path"));
298
- }
299
- if (semanticIssues.length > 0) {
300
- return { ok: false, issues: semanticIssues };
301
- }
302
- return { ok: true, value: blueprint };
303
- }
1
+ export { blueprintJsonSchema, findPreset, presetFileJsonSchema, presetFileSchema, projectBlueprintSchema, validatePresetFile, validateProjectBlueprint, } from "@ykdz/template-shared";
@@ -13,6 +13,21 @@ export type DevelopmentContainerRustLayer = {
13
13
  readonly toolchain: string;
14
14
  };
15
15
  type DevelopmentContainerCapabilityLayer = DevelopmentContainerBrowserTestLayer | DevelopmentContainerRustLayer;
16
+ export type DevelopmentContainerDockerfileFragments = {
17
+ readonly sourceRoot: string;
18
+ readonly nodePnpm: {
19
+ readonly from: string;
20
+ readonly text: string;
21
+ };
22
+ readonly browserTest?: {
23
+ readonly from: string;
24
+ readonly text: string;
25
+ } | undefined;
26
+ readonly rust?: {
27
+ readonly from: string;
28
+ readonly text: string;
29
+ } | undefined;
30
+ };
16
31
  export type DevelopmentContainerPlan = {
17
32
  readonly devcontainer: Record<string, unknown>;
18
33
  readonly dockerfile: string;
@@ -43,6 +58,7 @@ export declare function dockerfileFirstNodePnpmDevcontainer(options: {
43
58
  export declare function checkedDockerfileFirstNodePnpmDevcontainer(options: {
44
59
  readonly name: string;
45
60
  readonly layer: DevelopmentContainerNodePnpmLayer;
61
+ readonly dockerfileFragments: DevelopmentContainerDockerfileFragments;
46
62
  readonly additionalLayers?: readonly DevelopmentContainerCapabilityLayer[] | undefined;
47
63
  readonly extensions: readonly string[];
48
64
  readonly settings?: Record<string, unknown> | undefined;
@@ -52,6 +68,7 @@ export declare function dockerfileFirstRustPnpmDevcontainer(options: {
52
68
  readonly name: string;
53
69
  readonly nodeLayer: DevelopmentContainerNodePnpmLayer;
54
70
  readonly rustLayer: DevelopmentContainerRustLayer;
71
+ readonly dockerfileFragments: DevelopmentContainerDockerfileFragments;
55
72
  readonly extensions: readonly string[];
56
73
  readonly settings?: Record<string, unknown> | undefined;
57
74
  }): DevelopmentContainerPlan;
@@ -1,8 +1,11 @@
1
- import { readFileSync } from "node:fs";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
1
  import { loadTemplateDependencyCatalog } from "./dependency-catalog.js";
5
- import { packageTemplateRoot } from "./runtime-paths.js";
2
+ function requireDockerfileFragment(fragments, name) {
3
+ const fragment = fragments[name];
4
+ if (fragment === undefined) {
5
+ throw new Error(`Development Container Dockerfile fragment is missing: ${name}`);
6
+ }
7
+ return fragment;
8
+ }
6
9
  const runtimeDockerfileInstructions = new Set([
7
10
  "CMD",
8
11
  "ENTRYPOINT",
@@ -61,19 +64,13 @@ export function browserTestToolLayer() {
61
64
  export function rustToolLayer(options = {}) {
62
65
  return { kind: "rust", toolchain: options.toolchain ?? "stable" };
63
66
  }
64
- function sharedDevcontainerSourceRoot() {
65
- return packageTemplateRoot(path.dirname(fileURLToPath(import.meta.url)), "shared", "devcontainer");
66
- }
67
- function checkedDockerfileFragment(name) {
68
- return readFileSync(path.join(sharedDevcontainerSourceRoot(), name), "utf8");
69
- }
70
- function checkedNodePnpmDockerfile(options = {}) {
67
+ function checkedNodePnpmDockerfile(options) {
71
68
  return composeDevelopmentContainerDockerfile({
72
69
  layers: [
73
70
  {
74
71
  kind: "base",
75
72
  name: "node-pnpm",
76
- text: checkedDockerfileFragment("node-pnpm.Dockerfile"),
73
+ text: options.dockerfileFragments.nodePnpm.text,
77
74
  },
78
75
  ...(options.additionalLayers ?? []).map((layer) => {
79
76
  switch (layer.kind) {
@@ -81,36 +78,37 @@ function checkedNodePnpmDockerfile(options = {}) {
81
78
  return {
82
79
  kind: "capability",
83
80
  name: "browser-test",
84
- text: checkedDockerfileFragment("browser-test.Dockerfile"),
81
+ text: requireDockerfileFragment(options.dockerfileFragments, "browserTest").text,
85
82
  };
86
83
  case "rust":
87
84
  return {
88
85
  kind: "capability",
89
86
  name: "rust",
90
- text: checkedDockerfileFragment("rust.Dockerfile"),
87
+ text: requireDockerfileFragment(options.dockerfileFragments, "rust").text,
91
88
  };
92
89
  }
93
90
  }),
94
91
  ],
95
92
  });
96
93
  }
97
- function checkedNodePnpmDockerfileFragments(options = {}) {
94
+ function checkedNodePnpmDockerfileFragments(options) {
98
95
  return [
99
96
  {
100
- sourceRoot: "sharedDevcontainer",
101
- from: "node-pnpm.Dockerfile",
97
+ sourceRoot: options.dockerfileFragments.sourceRoot,
98
+ from: options.dockerfileFragments.nodePnpm.from,
102
99
  },
103
100
  ...(options.additionalLayers ?? []).map((layer) => {
104
101
  switch (layer.kind) {
105
102
  case "browser-test":
106
103
  return {
107
- sourceRoot: "sharedDevcontainer",
108
- from: "browser-test.Dockerfile",
104
+ sourceRoot: options.dockerfileFragments.sourceRoot,
105
+ from: requireDockerfileFragment(options.dockerfileFragments, "browserTest").from,
109
106
  };
110
107
  case "rust":
111
108
  return {
112
- sourceRoot: "sharedDevcontainer",
113
- from: "rust.Dockerfile",
109
+ sourceRoot: options.dockerfileFragments.sourceRoot,
110
+ from: requireDockerfileFragment(options.dockerfileFragments, "rust")
111
+ .from,
114
112
  };
115
113
  }
116
114
  }),
@@ -175,12 +173,14 @@ export function checkedDockerfileFirstNodePnpmDevcontainer(options) {
175
173
  ...(options.mounts ? { mounts: options.mounts } : {}),
176
174
  },
177
175
  dockerfile: checkedNodePnpmDockerfile({
176
+ dockerfileFragments: options.dockerfileFragments,
178
177
  additionalLayers: options.additionalLayers,
179
178
  }),
180
179
  dockerfileOperation: {
181
180
  kind: "writeTextFromFragments",
182
181
  to: ".devcontainer/Dockerfile",
183
182
  fragments: checkedNodePnpmDockerfileFragments({
183
+ dockerfileFragments: options.dockerfileFragments,
184
184
  additionalLayers: options.additionalLayers,
185
185
  }),
186
186
  },
@@ -190,6 +190,7 @@ export function dockerfileFirstRustPnpmDevcontainer(options) {
190
190
  return checkedDockerfileFirstNodePnpmDevcontainer({
191
191
  name: options.name,
192
192
  layer: options.nodeLayer,
193
+ dockerfileFragments: options.dockerfileFragments,
193
194
  additionalLayers: [options.rustLayer],
194
195
  extensions: options.extensions,
195
196
  settings: options.settings,
@@ -9,4 +9,13 @@ export type EditorCustomizationOptions = {
9
9
  readonly formatter?: string;
10
10
  };
11
11
  };
12
- export declare function editorCustomizationForCapabilities(capabilities: readonly EditorCustomizationCapability[], options?: EditorCustomizationOptions): EditorCustomization;
12
+ type CapabilityProjection = {
13
+ readonly extensions: readonly string[];
14
+ readonly settings: Record<string, unknown>;
15
+ };
16
+ export type EditorCustomizationDeclarations = {
17
+ readonly capabilities: Record<EditorCustomizationCapability, CapabilityProjection>;
18
+ };
19
+ export declare function loadEditorCustomizationDeclarations(resourcePath: string): EditorCustomizationDeclarations;
20
+ export declare function editorCustomizationForCapabilities(capabilities: readonly EditorCustomizationCapability[], declarations: EditorCustomizationDeclarations, options?: EditorCustomizationOptions): EditorCustomization;
21
+ export {};
@@ -1,6 +1,4 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
1
+ import { readFileSync } from "node:fs";
4
2
  import * as v from "valibot";
5
3
  const capabilityOrder = [
6
4
  "oxc-format-lint",
@@ -26,28 +24,40 @@ const editorCustomizationDeclarationsSchema = v.object({
26
24
  vitest: capabilityProjectionSchema,
27
25
  }),
28
26
  });
29
- const declarations = loadEditorCustomizationDeclarations();
30
- function editorCustomizationDeclarationPath() {
31
- const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));
32
- const relativeDeclarationPath = "builtin-source/templates/shared/editor-customization/capabilities.json";
33
- const candidates = [
34
- path.resolve(moduleDirectory, "..", relativeDeclarationPath),
35
- path.resolve(moduleDirectory, "..", "..", relativeDeclarationPath),
36
- path.resolve(moduleDirectory, "..", "..", "template-builtin-source", "templates", "shared", "editor-customization", "capabilities.json"),
37
- path.resolve(moduleDirectory, "..", "..", "..", "packages", relativeDeclarationPath),
38
- ];
39
- const declarationPath = candidates.find((candidate) => existsSync(candidate));
40
- if (!declarationPath) {
41
- throw new Error(`Unable to find editor customization declarations at ${relativeDeclarationPath}`);
42
- }
43
- return declarationPath;
27
+ function editorCustomizationDeclarationIssuePath(issue) {
28
+ const segments = issue.path?.flatMap((item) => typeof item.key === "string" || typeof item.key === "number"
29
+ ? [String(item.key)]
30
+ : []) ?? [];
31
+ return segments.length === 0 ? "$" : `$.${segments.join(".")}`;
32
+ }
33
+ function errorMessage(error) {
34
+ return error instanceof Error ? error.message : String(error);
44
35
  }
45
- function loadEditorCustomizationDeclarations() {
46
- const source = JSON.parse(readFileSync(editorCustomizationDeclarationPath(), "utf8"));
36
+ function editorCustomizationSharedResourceLabel(resourcePath) {
37
+ return `Editor Customization Shared Resource ${resourcePath}`;
38
+ }
39
+ export function loadEditorCustomizationDeclarations(resourcePath) {
40
+ const resourceLabel = editorCustomizationSharedResourceLabel(resourcePath);
41
+ let resourceText;
42
+ try {
43
+ resourceText = readFileSync(resourcePath, "utf8");
44
+ }
45
+ catch (error) {
46
+ throw new Error(`${resourceLabel} is unreadable: ${errorMessage(error)}`, {
47
+ cause: error,
48
+ });
49
+ }
50
+ let source;
51
+ try {
52
+ source = JSON.parse(resourceText);
53
+ }
54
+ catch (error) {
55
+ throw new Error(`${resourceLabel} is invalid: invalid JSON: ${errorMessage(error)}`, { cause: error });
56
+ }
47
57
  const result = v.safeParse(editorCustomizationDeclarationsSchema, source);
48
58
  if (!result.success) {
49
- throw new Error(`Editor customization declarations are invalid: ${result.issues
50
- .map((issue) => issue.message)
59
+ throw new Error(`${resourceLabel} is invalid: ${result.issues
60
+ .map((issue) => `${editorCustomizationDeclarationIssuePath(issue)}: ${issue.message}`)
51
61
  .join("; ")}`);
52
62
  }
53
63
  return result.output;
@@ -66,19 +76,19 @@ function oxcConfigPathSettings(configPaths) {
66
76
  };
67
77
  }
68
78
  function oxcProjection(options) {
69
- const baseProjection = declarations.capabilities["oxc-format-lint"];
79
+ const baseProjection = options.declarations.capabilities["oxc-format-lint"];
70
80
  return {
71
81
  extensions: baseProjection.extensions,
72
82
  settings: {
73
83
  ...baseProjection.settings,
74
- ...oxcConfigPathSettings(options?.oxcConfigPaths),
84
+ ...oxcConfigPathSettings(options.editorOptions?.oxcConfigPaths),
75
85
  },
76
86
  };
77
87
  }
78
88
  function mergeSettings(base, patch) {
79
89
  return { ...base, ...patch };
80
90
  }
81
- export function editorCustomizationForCapabilities(capabilities, options) {
91
+ export function editorCustomizationForCapabilities(capabilities, declarations, options) {
82
92
  const selected = new Set(capabilities);
83
93
  const extensions = new Set();
84
94
  let settings = {};
@@ -87,7 +97,7 @@ export function editorCustomizationForCapabilities(capabilities, options) {
87
97
  continue;
88
98
  }
89
99
  const projection = capability === "oxc-format-lint"
90
- ? oxcProjection(options)
100
+ ? oxcProjection({ declarations, editorOptions: options })
91
101
  : declarations.capabilities[capability];
92
102
  for (const extension of projection.extensions) {
93
103
  extensions.add(extension);
@@ -1,3 +1,4 @@
1
+ import { type CheckEnvironmentNeed } from "./module-graph.js";
1
2
  import type { PresetSourceManifest } from "./preset-source.js";
2
3
  import { type PresetProjectionSourceRoots } from "./projection-capabilities.js";
3
4
  export type GeneratedScenarioSet = "init" | "package-addition-matrix";
@@ -27,6 +28,7 @@ export type GeneratedScenarioCommandStep = {
27
28
  readonly args: readonly string[];
28
29
  readonly cwd: string;
29
30
  readonly display: string;
31
+ readonly environmentNeedKind?: CheckEnvironmentNeed["kind"];
30
32
  };
31
33
  export type GeneratedScenarioCommandRunner = (command: string, args: readonly string[], cwd: string, options?: {
32
34
  readonly env?: Record<string, string> | undefined;
@@ -58,6 +60,8 @@ export declare function defaultGeneratedScenarioCommandRunner(command: string, a
58
60
  readonly env?: Record<string, string> | undefined;
59
61
  readonly logPrefix?: string | undefined;
60
62
  }): Promise<void>;
63
+ export declare function generatedScenarioEnvironmentNeedSteps(needs: readonly CheckEnvironmentNeed[], projectDir: string): GeneratedScenarioCommandStep[];
64
+ export declare function generatedScenarioRequiresSerializedRootCheck(steps: readonly GeneratedScenarioCommandStep[]): boolean;
61
65
  export declare function generatedScenarioQualityGateSteps(manifest: PresetSourceManifest, scenario: GeneratedScenario, projectDir: string, addedPackagePath: string | undefined, options: GeneratedScenarioRunnerOptions): GeneratedScenarioCommandStep[];
62
66
  export declare function fixtureConcurrency(scenarioCount: number): number;
63
67
  export declare function errorForFailedGeneratedScenario(scenario: GeneratedScenario, error: unknown): Error;