@ykdz/template 0.0.8 → 0.0.10

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 (43) hide show
  1. package/dist/cli.js +7 -7
  2. package/node_modules/@ykdz/template-builtin-source/dist/src/index.js +10 -6
  3. package/node_modules/@ykdz/template-builtin-source/dist/templates/hono-api/projection.js +15 -5
  4. package/node_modules/@ykdz/template-builtin-source/dist/templates/ts-lib/projection.js +15 -5
  5. package/node_modules/@ykdz/template-builtin-source/dist/templates/vue-app/projection.js +31 -7
  6. package/node_modules/@ykdz/template-builtin-source/dist/templates/vue-hono-app/projection.js +7 -8
  7. package/node_modules/@ykdz/template-builtin-source/package.json +15 -2
  8. package/node_modules/@ykdz/template-builtin-source/templates/preset-source.json +18 -4
  9. package/node_modules/@ykdz/template-builtin-source/templates/shared/oxc/node/oxlint.config.ts +25 -3
  10. package/node_modules/@ykdz/template-builtin-source/templates/shared/oxc/tsconfig.config.json +20 -0
  11. package/node_modules/@ykdz/template-builtin-source/templates/shared/oxc/vue/oxlint.config.ts +27 -3
  12. package/node_modules/@ykdz/template-builtin-source/templates/vue-app/env.d.ts +7 -0
  13. package/node_modules/@ykdz/template-builtin-source/templates/vue-app/scripts/run-playwright.ts +25 -0
  14. package/node_modules/@ykdz/template-builtin-source/templates/vue-app/src/App.vue +18 -4
  15. package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/env.d.ts +7 -0
  16. package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/scripts/run-playwright.ts +25 -0
  17. package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/src/App.vue +18 -4
  18. package/node_modules/@ykdz/template-core/dist/declarations.js +34 -1
  19. package/node_modules/@ykdz/template-core/dist/dependency-catalog.js +11 -6
  20. package/node_modules/@ykdz/template-core/dist/devcontainer.d.ts +5 -5
  21. package/node_modules/@ykdz/template-core/dist/editor-customization.js +20 -29
  22. package/node_modules/@ykdz/template-core/dist/generated-scenarios.d.ts +5 -4
  23. package/node_modules/@ykdz/template-core/dist/generated-scenarios.js +38 -10
  24. package/node_modules/@ykdz/template-core/dist/generation-context.js +8 -3
  25. package/node_modules/@ykdz/template-core/dist/next-step-instructions.js +3 -0
  26. package/node_modules/@ykdz/template-core/dist/package-addition.d.ts +2 -2
  27. package/node_modules/@ykdz/template-core/dist/package-addition.js +24 -9
  28. package/node_modules/@ykdz/template-core/dist/pnpm-workspace.yaml +4 -5
  29. package/node_modules/@ykdz/template-core/dist/preset-projection.d.ts +3 -3
  30. package/node_modules/@ykdz/template-core/dist/preset-projection.js +1 -0
  31. package/node_modules/@ykdz/template-core/dist/preset-source.js +60 -45
  32. package/node_modules/@ykdz/template-core/dist/project-github.d.ts +3 -3
  33. package/node_modules/@ykdz/template-core/dist/project-github.js +1 -0
  34. package/node_modules/@ykdz/template-core/dist/projection-capabilities.d.ts +7 -3
  35. package/node_modules/@ykdz/template-core/dist/projection-capabilities.js +196 -130
  36. package/node_modules/@ykdz/template-core/dist/renderer.d.ts +2 -2
  37. package/node_modules/@ykdz/template-core/dist/renderer.js +2 -2
  38. package/node_modules/@ykdz/template-core/dist/template-boundary-check.d.ts +1 -1
  39. package/node_modules/@ykdz/template-core/dist/template-boundary-check.js +8 -10
  40. package/node_modules/@ykdz/template-core/dist/toolchain-resolution.d.ts +5 -5
  41. package/node_modules/@ykdz/template-core/dist/toolchain-resolution.js +10 -11
  42. package/node_modules/@ykdz/template-core/package.json +9 -2
  43. package/package.json +12 -5
@@ -1,17 +1,31 @@
1
1
  <script setup lang="ts">
2
- import { usePreferredDark } from "@vueuse/core";
3
2
  import { storeToRefs } from "pinia";
4
- import { computed, onMounted, ref } from "vue";
3
+ import { onMounted, onUnmounted, ref } from "vue";
5
4
 
6
5
  import { api } from "#/api";
7
6
  import { useCounterStore } from "#/stores/counter";
8
7
 
9
8
  const counter = useCounterStore();
10
9
  const { count } = storeToRefs(counter);
11
- const prefersDark = usePreferredDark();
12
- const themeLabel = computed(() => (prefersDark.value ? "dark" : "light"));
10
+ const themeLabel = ref<"dark" | "light">("light");
13
11
  const apiStatus = ref("checking");
14
12
 
13
+ let preferredThemeQuery: MediaQueryList | undefined;
14
+
15
+ function updateThemeLabel(event: MediaQueryList | MediaQueryListEvent): void {
16
+ themeLabel.value = event.matches ? "dark" : "light";
17
+ }
18
+
19
+ onMounted(() => {
20
+ preferredThemeQuery = window.matchMedia("(prefers-color-scheme: dark)");
21
+ updateThemeLabel(preferredThemeQuery);
22
+ preferredThemeQuery.addEventListener("change", updateThemeLabel);
23
+ });
24
+
25
+ onUnmounted(() => {
26
+ preferredThemeQuery?.removeEventListener("change", updateThemeLabel);
27
+ });
28
+
15
29
  onMounted(async () => {
16
30
  const response = await api.api.health.$get();
17
31
  const body = await response.json();
@@ -206,12 +206,45 @@ export function validatePresetFile(input, options = {}) {
206
206
  export function findPreset(presets, name) {
207
207
  return presets.find((preset) => preset.name === name);
208
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
+ }
209
242
  export function validateProjectBlueprint(input, options = {}) {
210
243
  const result = v.safeParse(projectBlueprintSchema, input);
211
244
  if (!result.success) {
212
245
  return { ok: false, issues: shapeIssues(result.issues) };
213
246
  }
214
- const blueprint = result.output;
247
+ const blueprint = normalizeProjectBlueprint(result.output);
215
248
  const preset = findPreset(options.presets ?? [], blueprint.preset);
216
249
  const semanticIssues = [
217
250
  ...duplicateIssues(blueprint.features, "$.features"),
@@ -26,7 +26,12 @@ function parseTemplateDependencyCatalog(workspaceYaml) {
26
26
  if (!match) {
27
27
  continue;
28
28
  }
29
- catalog[match[1] ?? match[2]] = match[3];
29
+ const key = match[1] ?? match[2];
30
+ const version = match[3];
31
+ if (key === undefined || version === undefined) {
32
+ continue;
33
+ }
34
+ catalog[key] = version;
30
35
  }
31
36
  return catalog;
32
37
  }
@@ -35,7 +40,7 @@ export function loadTemplateDependencyCatalog() {
35
40
  }
36
41
  export function selectTemplateDependencyCatalogEntries(dependencies, catalog = loadTemplateDependencyCatalog()) {
37
42
  const selected = {};
38
- for (const dependency of [...dependencies].sort()) {
43
+ for (const dependency of dependencies.toSorted()) {
39
44
  const version = catalog[dependency];
40
45
  if (version === undefined) {
41
46
  throw new Error(`Template Dependency Catalog is missing dependency: ${dependency}`);
@@ -61,7 +66,7 @@ export function collectGeneratedManifestCatalogDependencies(manifests) {
61
66
  }
62
67
  }
63
68
  }
64
- return [...dependencies].sort();
69
+ return [...dependencies].toSorted();
65
70
  }
66
71
  export function collectGeneratedManifestCatalogReferences(manifests) {
67
72
  const dependencies = new Set();
@@ -79,7 +84,7 @@ export function collectGeneratedManifestCatalogReferences(manifests) {
79
84
  }
80
85
  }
81
86
  }
82
- return [...dependencies].sort();
87
+ return [...dependencies].toSorted();
83
88
  }
84
89
  function renderCatalogKey(key) {
85
90
  return key.startsWith("@") ? JSON.stringify(key) : key;
@@ -106,7 +111,7 @@ export function pnpmWorkspaceYamlWithCatalogDependencies(workspaceYaml, dependen
106
111
  const catalogLines = [
107
112
  "catalog:",
108
113
  ...Object.entries(nextCatalog)
109
- .sort(([left], [right]) => left.localeCompare(right))
114
+ .toSorted(([left], [right]) => left.localeCompare(right))
110
115
  .map(([dependency, version]) => ` ${renderCatalogKey(dependency)}: ${version}`),
111
116
  "",
112
117
  ];
@@ -126,7 +131,7 @@ export function renderGeneratedPnpmWorkspaceYaml(options) {
126
131
  ];
127
132
  if (options.allowBuilds) {
128
133
  lines.push("allowBuilds:", ...Object.entries(options.allowBuilds)
129
- .sort(([left], [right]) => left.localeCompare(right))
134
+ .toSorted(([left], [right]) => left.localeCompare(right))
130
135
  .map(([dependency, allowed]) => ` ${dependency}: ${allowed}`), "");
131
136
  }
132
137
  lines.push("catalog:", ...Object.entries(catalog).map(([dependency, version]) => ` ${renderCatalogKey(dependency)}: ${version}`), "");
@@ -38,21 +38,21 @@ export declare function dockerfileFirstNodePnpmDevcontainer(options: {
38
38
  readonly name: string;
39
39
  readonly layer: DevelopmentContainerNodePnpmLayer;
40
40
  readonly extensions: readonly string[];
41
- readonly settings?: Record<string, unknown>;
41
+ readonly settings?: Record<string, unknown> | undefined;
42
42
  }): DevelopmentContainerPlan;
43
43
  export declare function checkedDockerfileFirstNodePnpmDevcontainer(options: {
44
44
  readonly name: string;
45
45
  readonly layer: DevelopmentContainerNodePnpmLayer;
46
- readonly additionalLayers?: readonly DevelopmentContainerCapabilityLayer[];
46
+ readonly additionalLayers?: readonly DevelopmentContainerCapabilityLayer[] | undefined;
47
47
  readonly extensions: readonly string[];
48
- readonly settings?: Record<string, unknown>;
49
- readonly mounts?: readonly string[];
48
+ readonly settings?: Record<string, unknown> | undefined;
49
+ readonly mounts?: readonly string[] | undefined;
50
50
  }): DevelopmentContainerPlan;
51
51
  export declare function dockerfileFirstRustPnpmDevcontainer(options: {
52
52
  readonly name: string;
53
53
  readonly nodeLayer: DevelopmentContainerNodePnpmLayer;
54
54
  readonly rustLayer: DevelopmentContainerRustLayer;
55
55
  readonly extensions: readonly string[];
56
- readonly settings?: Record<string, unknown>;
56
+ readonly settings?: Record<string, unknown> | undefined;
57
57
  }): DevelopmentContainerPlan;
58
58
  export {};
@@ -1,6 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import * as v from "valibot";
4
5
  const capabilityOrder = [
5
6
  "oxc-format-lint",
6
7
  "vue",
@@ -12,6 +13,19 @@ const defaultOxcConfigPaths = {
12
13
  formatter: "./oxfmt.config.ts",
13
14
  lint: "./oxlint.config.ts",
14
15
  };
16
+ const capabilityProjectionSchema = v.object({
17
+ extensions: v.array(v.string()),
18
+ settings: v.record(v.string(), v.unknown()),
19
+ });
20
+ const editorCustomizationDeclarationsSchema = v.object({
21
+ capabilities: v.object({
22
+ "oxc-format-lint": capabilityProjectionSchema,
23
+ vue: capabilityProjectionSchema,
24
+ tailwind: capabilityProjectionSchema,
25
+ "rust-tooling": capabilityProjectionSchema,
26
+ vitest: capabilityProjectionSchema,
27
+ }),
28
+ });
15
29
  const declarations = loadEditorCustomizationDeclarations();
16
30
  function editorCustomizationDeclarationPath() {
17
31
  const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));
@@ -28,38 +42,15 @@ function editorCustomizationDeclarationPath() {
28
42
  }
29
43
  return declarationPath;
30
44
  }
31
- function isRecord(value) {
32
- return typeof value === "object" && value !== null && !Array.isArray(value);
33
- }
34
- function assertCapabilityProjection(capability, value) {
35
- if (!isRecord(value)) {
36
- throw new Error(`Editor customization ${capability} must be an object`);
37
- }
38
- const extensions = value.extensions;
39
- const settings = value.settings;
40
- if (!Array.isArray(extensions) ||
41
- extensions.some((extension) => typeof extension !== "string")) {
42
- throw new Error(`Editor customization ${capability} must declare string extensions`);
43
- }
44
- if (!isRecord(settings)) {
45
- throw new Error(`Editor customization ${capability} must declare object settings`);
46
- }
47
- }
48
45
  function loadEditorCustomizationDeclarations() {
49
46
  const source = JSON.parse(readFileSync(editorCustomizationDeclarationPath(), "utf8"));
50
- if (!isRecord(source) || !isRecord(source.capabilities)) {
51
- throw new Error("Editor customization declarations must contain capabilities");
47
+ const result = v.safeParse(editorCustomizationDeclarationsSchema, source);
48
+ if (!result.success) {
49
+ throw new Error(`Editor customization declarations are invalid: ${result.issues
50
+ .map((issue) => issue.message)
51
+ .join("; ")}`);
52
52
  }
53
- const capabilities = source.capabilities;
54
- for (const capability of capabilityOrder) {
55
- assertCapabilityProjection(capability, capabilities[capability]);
56
- }
57
- return {
58
- capabilities: Object.fromEntries(capabilityOrder.map((capability) => [
59
- capability,
60
- capabilities[capability],
61
- ])),
62
- };
53
+ return result.output;
63
54
  }
64
55
  function oxcConfigPathSettings(configPaths) {
65
56
  if (configPaths === "nested") {
@@ -29,8 +29,8 @@ export type GeneratedScenarioCommandStep = {
29
29
  readonly display: string;
30
30
  };
31
31
  export type GeneratedScenarioCommandRunner = (command: string, args: readonly string[], cwd: string, options?: {
32
- readonly env?: Record<string, string>;
33
- readonly logPrefix?: string;
32
+ readonly env?: Record<string, string> | undefined;
33
+ readonly logPrefix?: string | undefined;
34
34
  }) => Promise<void>;
35
35
  export type GeneratedScenarioReporter = {
36
36
  readonly info?: (message: string) => void;
@@ -48,14 +48,15 @@ export type GeneratedScenarioRunnerOptions = {
48
48
  export type ExclusiveLock = {
49
49
  readonly run: <T>(callback: () => Promise<T>) => Promise<T>;
50
50
  };
51
+ export declare function generatedScenarioChildProcessEnv(env?: Record<string, string>): NodeJS.ProcessEnv;
51
52
  export declare function createExclusiveLock(): ExclusiveLock;
52
53
  export declare function generatedScenarioId(scenario: Pick<GeneratedScenario, "basePreset" | "addedPreset" | "linkFrom">): string;
53
54
  export declare function generatedScenarioLabel(scenario: Pick<GeneratedScenario, "basePreset" | "addedPreset" | "linkFrom">): string;
54
55
  export declare function selectGeneratedScenarios(manifest: PresetSourceManifest, set: GeneratedScenarioSet): GeneratedScenarioSelection;
55
56
  export declare function packageLeafNameForAddedPreset(manifest: PresetSourceManifest, presetName: string): string;
56
57
  export declare function defaultGeneratedScenarioCommandRunner(command: string, args: readonly string[], cwd: string, options?: {
57
- readonly env?: Record<string, string>;
58
- readonly logPrefix?: string;
58
+ readonly env?: Record<string, string> | undefined;
59
+ readonly logPrefix?: string | undefined;
59
60
  }): Promise<void>;
60
61
  export declare function generatedScenarioQualityGateSteps(manifest: PresetSourceManifest, scenario: GeneratedScenario, projectDir: string, addedPackagePath: string | undefined, options: GeneratedScenarioRunnerOptions): GeneratedScenarioCommandStep[];
61
62
  export declare function fixtureConcurrency(scenarioCount: number): number;
@@ -1,6 +1,7 @@
1
1
  import { readdir, readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { execa } from "execa";
4
+ import * as v from "valibot";
4
5
  import { assembleGenerationContext } from "./generation-context.js";
5
6
  import { renderPlaywrightBrowserInstallCommand, } from "./module-graph.js";
6
7
  import { planNextStepInstructions } from "./next-step-instructions.js";
@@ -10,6 +11,19 @@ const defaultDeterministicToolchainEnv = {
10
11
  TEMPLATE_TOOLCHAIN_RESOLUTION: "bundled-fallback",
11
12
  };
12
13
  const defaultFixtureConcurrency = 2;
14
+ const storedBlueprintSchema = v.object({
15
+ packages: v.optional(v.array(v.object({
16
+ name: v.string(),
17
+ path: v.string(),
18
+ }))),
19
+ });
20
+ export function generatedScenarioChildProcessEnv(env = {}) {
21
+ const childEnv = { ...process.env, ...env };
22
+ if (childEnv.NO_COLOR !== undefined && childEnv.FORCE_COLOR !== undefined) {
23
+ delete childEnv.FORCE_COLOR;
24
+ }
25
+ return childEnv;
26
+ }
13
27
  function cliSourceExecutionEnv(options, env = {}) {
14
28
  return {
15
29
  ...env,
@@ -60,8 +74,10 @@ function createGeneratedScenario(set, input) {
60
74
  return {
61
75
  set,
62
76
  basePreset: input.basePreset,
63
- addedPreset: input.addedPreset,
64
- linkFrom: input.linkFrom ? [...input.linkFrom] : undefined,
77
+ ...(input.addedPreset === undefined
78
+ ? {}
79
+ : { addedPreset: input.addedPreset }),
80
+ ...(input.linkFrom === undefined ? {} : { linkFrom: [...input.linkFrom] }),
65
81
  id: generatedScenarioId(input),
66
82
  label: generatedScenarioLabel(input),
67
83
  };
@@ -78,7 +94,9 @@ export function selectGeneratedScenarios(manifest, set) {
78
94
  const matrixScenarios = contract.supportedCombinations.map((combination) => createGeneratedScenario(set, {
79
95
  basePreset: combination.basePreset,
80
96
  addedPreset: combination.addedPreset,
81
- linkFrom: combination.linkFrom,
97
+ ...(combination.linkFrom === undefined
98
+ ? {}
99
+ : { linkFrom: combination.linkFrom }),
82
100
  }));
83
101
  const skipped = contract.semanticSkips.map((skip) => ({
84
102
  set,
@@ -105,7 +123,8 @@ export async function defaultGeneratedScenarioCommandRunner(command, args, cwd,
105
123
  console.log(`${prefix}$ ${[command, ...args].join(" ")}`);
106
124
  await execa(command, [...args], {
107
125
  cwd,
108
- env: options.env,
126
+ env: generatedScenarioChildProcessEnv(options.env),
127
+ extendEnv: false,
109
128
  stdio: "inherit",
110
129
  });
111
130
  }
@@ -163,7 +182,7 @@ async function linkedConsumerSourceSnapshots(projectDir, packagePaths) {
163
182
  async function sourceFileSnapshot(sourceDir, currentDir = sourceDir) {
164
183
  const snapshot = {};
165
184
  const entries = await readdir(currentDir, { withFileTypes: true });
166
- for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
185
+ for (const entry of entries.toSorted((left, right) => left.name.localeCompare(right.name))) {
167
186
  const entryPath = path.join(currentDir, entry.name);
168
187
  if (entry.isDirectory()) {
169
188
  Object.assign(snapshot, await sourceFileSnapshot(sourceDir, entryPath));
@@ -184,8 +203,14 @@ async function assertLinkedConsumerSourcesUnchanged(projectDir, beforeSnapshots)
184
203
  }
185
204
  }
186
205
  async function readAddedPackagePath(projectDir, packageLeafName) {
187
- const blueprint = JSON.parse(await readFile(path.join(projectDir, ".template", "blueprint.json"), "utf8"));
188
- const packageDefinition = blueprint.packages?.find((pkg) => path.basename(pkg.path) === packageLeafName);
206
+ const input = JSON.parse(await readFile(path.join(projectDir, ".template", "blueprint.json"), "utf8"));
207
+ const result = v.safeParse(storedBlueprintSchema, input);
208
+ if (!result.success) {
209
+ throw new Error(`Generated Repository blueprint is invalid: ${result.issues
210
+ .map((issue) => issue.message)
211
+ .join("; ")}`);
212
+ }
213
+ const packageDefinition = result.output.packages?.find((pkg) => path.basename(pkg.path) === packageLeafName);
189
214
  if (!packageDefinition) {
190
215
  throw new Error(`Package Addition fixture did not record package leaf ${packageLeafName}`);
191
216
  }
@@ -198,7 +223,7 @@ function projectionPlanForPreset(presetName, projectDir, manifest, options, pack
198
223
  }
199
224
  const blueprint = blueprintForPresetSourcePreset(preset, {
200
225
  targetDir: projectDir,
201
- scope: packageScope,
226
+ ...(packageScope === undefined ? {} : { scope: packageScope }),
202
227
  });
203
228
  return projectPresetSourcePreset({
204
229
  preset,
@@ -208,7 +233,7 @@ function projectionPlanForPreset(presetName, projectDir, manifest, options, pack
208
233
  blueprint,
209
234
  toolchain: {
210
235
  nodeLtsMajor: { kind: "NodeLtsMajor", value: "24" },
211
- packageManagerPin: { kind: "PackageManagerPin", value: "pnpm@10.0.0" },
236
+ packageManagerPin: { kind: "PackageManagerPin", value: "pnpm@10.34.4" },
212
237
  source: "bundled-fallback",
213
238
  diagnostics: [],
214
239
  },
@@ -248,6 +273,9 @@ function addedPresetEnvironmentNeeds(manifest, scenario, projectDir, addedPackag
248
273
  function environmentNeedStep(need, projectDir) {
249
274
  const display = renderPlaywrightBrowserInstallCommand(need);
250
275
  const [command, ...args] = display.split(" ");
276
+ if (command === undefined) {
277
+ throw new Error(`Cannot parse environment setup command: ${display}`);
278
+ }
251
279
  return {
252
280
  id: `install-${need.owner.path}-playwright-browsers`,
253
281
  command,
@@ -282,7 +310,7 @@ async function runGeneratedScenarioQualityGate(manifest, scenario, projectDir, a
282
310
  const env = step.id === "run-root-check" ? { CI: "1" } : undefined;
283
311
  const runStep = async () => {
284
312
  await options.runCommand(step.command, [...step.args], step.cwd, {
285
- env,
313
+ ...(env === undefined ? {} : { env }),
286
314
  logPrefix: scenario.label,
287
315
  });
288
316
  };
@@ -6,9 +6,14 @@ export function assembleGenerationContext(options) {
6
6
  value: path.basename(path.resolve(options.targetDir)),
7
7
  },
8
8
  preset: options.blueprint.preset,
9
- packageManager: options.blueprint.packageManager
10
- ? { kind: "PackageManager", value: options.blueprint.packageManager }
11
- : undefined,
9
+ ...(options.blueprint.packageManager
10
+ ? {
11
+ packageManager: {
12
+ kind: "PackageManager",
13
+ value: options.blueprint.packageManager,
14
+ },
15
+ }
16
+ : {}),
12
17
  blueprint: options.blueprint,
13
18
  toolchain: options.toolchain,
14
19
  };
@@ -73,6 +73,9 @@ function checkEnvironmentInstructions(plan) {
73
73
  return plan.checkPlan.environmentNeeds.map((need) => {
74
74
  const display = renderPlaywrightBrowserInstallCommand(need);
75
75
  const [command, ...args] = display.split(" ");
76
+ if (command === undefined) {
77
+ throw new Error(`Cannot parse environment setup command: ${display}`);
78
+ }
76
79
  return {
77
80
  id: need.owner.path === "apps/web"
78
81
  ? "install-web-playwright-browsers"
@@ -4,8 +4,8 @@ export type AddPackageOptions = {
4
4
  cwd: string;
5
5
  preset: string;
6
6
  name: string;
7
- path?: string;
8
- linkFrom?: readonly string[];
7
+ path?: string | undefined;
8
+ linkFrom?: readonly string[] | undefined;
9
9
  presetSourceManifest: PresetSourceManifest;
10
10
  projectionSourceRoots: PresetProjectionSourceRoots;
11
11
  };
@@ -10,7 +10,7 @@ import { renderProject } from "./renderer.js";
10
10
  function projectNameFromBlueprint(blueprint, fallbackProjectName) {
11
11
  const firstPackage = blueprint.packages?.[0];
12
12
  const match = firstPackage?.name.match(/^@([^/]+)\//);
13
- if (match) {
13
+ if (match?.[1]) {
14
14
  return match[1];
15
15
  }
16
16
  if (/^[a-z0-9][a-z0-9._-]*$/.test(fallbackProjectName)) {
@@ -314,9 +314,17 @@ async function packageManifestsForTaskGraph(options) {
314
314
  for (const packageDefinition of options.blueprint.packages ?? []) {
315
315
  const manifestOverride = options.manifestOverrides?.get(packageDefinition.path);
316
316
  const manifestFromOperations = generatedPackageManifestFromOperations(options.operations, packageDefinition.path);
317
- manifests.push(manifestOverride ??
318
- manifestFromOperations ??
319
- (await readJson(path.join(options.root, packageDefinition.path, "package.json"))));
317
+ if (manifestOverride !== undefined) {
318
+ manifests.push(manifestOverride);
319
+ continue;
320
+ }
321
+ if (manifestFromOperations !== undefined) {
322
+ manifests.push(manifestFromOperations);
323
+ continue;
324
+ }
325
+ const manifest = await readJson(path.join(options.root, packageDefinition.path, "package.json"));
326
+ assertObjectManifest(manifest, packageDefinition.path);
327
+ manifests.push(manifest);
320
328
  }
321
329
  return manifests;
322
330
  }
@@ -348,7 +356,7 @@ function assertObjectManifest(input, packagePath) {
348
356
  }
349
357
  }
350
358
  function recordSortedByKey(record) {
351
- return Object.fromEntries(Object.entries(record).sort(([left], [right]) => left.localeCompare(right)));
359
+ return Object.fromEntries(Object.entries(record).toSorted(([left], [right]) => left.localeCompare(right)));
352
360
  }
353
361
  async function consumerManifestUpdatesForPackageLinkIntents(options) {
354
362
  const updates = [];
@@ -425,16 +433,22 @@ function rootPackageJsonWithPackageTaskFilters(input, blueprint, packageManifest
425
433
  assertRootPackageJson(input);
426
434
  const workspacePackageGlobs = workspacePackageGlobsFromBlueprint(blueprint);
427
435
  const taskNames = turboTaskNamesForPackageManifests(packageManifests);
436
+ const rootCheckTaskNames = taskNames.filter((taskName) => taskName !== "test:e2e");
437
+ const checkScript = input.scripts.check;
438
+ const fixScript = input.scripts.fix;
439
+ if (checkScript === undefined || fixScript === undefined) {
440
+ throw new Error("Cannot update root Package Addition scripts: check and fix scripts are required");
441
+ }
428
442
  return {
429
443
  ...input,
430
444
  scripts: {
431
445
  ...input.scripts,
432
446
  check: rootScriptWithTurboPackageTasks({
433
- script: input.scripts.check,
434
- taskNames,
447
+ script: checkScript,
448
+ taskNames: rootCheckTaskNames,
435
449
  workspacePackageGlobs,
436
450
  }),
437
- fix: rootScriptWithTurboPackageTask(input.scripts.fix, "fix", workspacePackageGlobs),
451
+ fix: rootScriptWithTurboPackageTask(fixScript, "fix", workspacePackageGlobs),
438
452
  },
439
453
  };
440
454
  }
@@ -483,6 +497,7 @@ function rootPackageJsonWithSharedOxcRuntimeDependencies(input, requiresSharedOx
483
497
  ...input.devDependencies,
484
498
  oxfmt: input.devDependencies?.oxfmt ?? "catalog:",
485
499
  oxlint: input.devDependencies?.oxlint ?? "catalog:",
500
+ "oxlint-tsgolint": input.devDependencies?.["oxlint-tsgolint"] ?? "catalog:",
486
501
  },
487
502
  };
488
503
  }
@@ -491,7 +506,7 @@ function catalogDependenciesWithSharedOxcRuntimeDependencies(catalogDependencies
491
506
  if (!requiresSharedOxcConfiguration) {
492
507
  return dependencies;
493
508
  }
494
- for (const dependency of ["oxfmt", "oxlint"]) {
509
+ for (const dependency of ["oxfmt", "oxlint", "oxlint-tsgolint"]) {
495
510
  if (!dependencies.includes(dependency)) {
496
511
  dependencies.push(dependency);
497
512
  }
@@ -1,6 +1,8 @@
1
1
  packages:
2
2
  - packages/*
3
3
 
4
+ nodeLinker: hoisted
5
+
4
6
  catalog:
5
7
  "@hono/node-server": ^2.0.6
6
8
  "@playwright/test": ^1.61.1
@@ -9,16 +11,16 @@ catalog:
9
11
  "@types/web-bluetooth": ^0.0.21
10
12
  "@vitejs/plugin-vue": ^6.0.7
11
13
  "@vue/tsconfig": ^0.9.1
12
- "@vueuse/core": ^14.3.0
13
14
  execa: ^9.6.1
14
15
  hono: ^4.12.27
15
16
  oxfmt: ^0.57.0
16
17
  oxlint: ^1.72.0
18
+ oxlint-tsgolint: ^0.24.0
17
19
  pinia: ^3.0.4
18
20
  tailwindcss: ^4.3.2
19
21
  tsc-alias: ^1.8.17
20
22
  tsx: ^4.22.4
21
- turbo: ^2.10.2
23
+ turbo: 2.10.2
22
24
  typescript: ^6.0.3
23
25
  valibot: ^1.4.2
24
26
  vite: ^8.1.2
@@ -30,8 +32,5 @@ catalog:
30
32
  allowBuilds:
31
33
  esbuild: true
32
34
 
33
- minimumReleaseAgeExclude:
34
- - valibot@1.4.2
35
-
36
35
  onlyBuiltDependencies:
37
36
  - esbuild
@@ -20,7 +20,7 @@ export type PresetPackageAdditionPlan = {
20
20
  readonly packageRole: PackageRole;
21
21
  readonly packageSourcePreset: PackageSourcePreset;
22
22
  readonly sourceRoot: string;
23
- readonly sourceRoots?: Record<string, string>;
23
+ readonly sourceRoots?: Record<string, string> | undefined;
24
24
  readonly operations: readonly RenderOperation[];
25
25
  readonly textFiles?: readonly {
26
26
  readonly path: string;
@@ -32,7 +32,7 @@ export type PresetPackageAdditionCapability = {
32
32
  };
33
33
  export type PresetProjectionPlan = {
34
34
  readonly sourceRoot: string;
35
- readonly sourceRoots?: Record<string, string>;
35
+ readonly sourceRoots?: Record<string, string> | undefined;
36
36
  readonly operations: readonly RenderOperation[];
37
37
  readonly checkPlan: CheckPlan;
38
38
  readonly fixPlan: FixPlan;
@@ -48,7 +48,7 @@ export type PresetProjectionPlan = {
48
48
  };
49
49
  export type PresetBlueprintOptions = {
50
50
  readonly targetDir: string;
51
- readonly scope?: string;
51
+ readonly scope?: string | undefined;
52
52
  };
53
53
  export type RenderPresetProjectionOptions = {
54
54
  readonly targetDir: string;
@@ -1,3 +1,4 @@
1
+ import {} from "./module-graph.js";
1
2
  import { planNextStepInstructions, } from "./next-step-instructions.js";
2
3
  export function planNextStepInstructionsForProjection(options) {
3
4
  return planNextStepInstructions({