@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.
- package/dist/cli.js +7 -7
- package/node_modules/@ykdz/template-builtin-source/dist/src/index.js +10 -6
- package/node_modules/@ykdz/template-builtin-source/dist/templates/hono-api/projection.js +15 -5
- package/node_modules/@ykdz/template-builtin-source/dist/templates/ts-lib/projection.js +15 -5
- package/node_modules/@ykdz/template-builtin-source/dist/templates/vue-app/projection.js +31 -7
- package/node_modules/@ykdz/template-builtin-source/dist/templates/vue-hono-app/projection.js +7 -8
- package/node_modules/@ykdz/template-builtin-source/package.json +15 -2
- package/node_modules/@ykdz/template-builtin-source/templates/preset-source.json +18 -4
- package/node_modules/@ykdz/template-builtin-source/templates/shared/oxc/node/oxlint.config.ts +25 -3
- package/node_modules/@ykdz/template-builtin-source/templates/shared/oxc/tsconfig.config.json +20 -0
- package/node_modules/@ykdz/template-builtin-source/templates/shared/oxc/vue/oxlint.config.ts +27 -3
- package/node_modules/@ykdz/template-builtin-source/templates/vue-app/env.d.ts +7 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vue-app/scripts/run-playwright.ts +25 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vue-app/src/App.vue +18 -4
- package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/env.d.ts +7 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/scripts/run-playwright.ts +25 -0
- package/node_modules/@ykdz/template-builtin-source/templates/vue-hono-app/web/src/App.vue +18 -4
- package/node_modules/@ykdz/template-core/dist/declarations.js +34 -1
- package/node_modules/@ykdz/template-core/dist/dependency-catalog.js +11 -6
- package/node_modules/@ykdz/template-core/dist/devcontainer.d.ts +5 -5
- package/node_modules/@ykdz/template-core/dist/editor-customization.js +20 -29
- package/node_modules/@ykdz/template-core/dist/generated-scenarios.d.ts +5 -4
- package/node_modules/@ykdz/template-core/dist/generated-scenarios.js +38 -10
- package/node_modules/@ykdz/template-core/dist/generation-context.js +8 -3
- package/node_modules/@ykdz/template-core/dist/next-step-instructions.js +3 -0
- package/node_modules/@ykdz/template-core/dist/package-addition.d.ts +2 -2
- package/node_modules/@ykdz/template-core/dist/package-addition.js +24 -9
- package/node_modules/@ykdz/template-core/dist/pnpm-workspace.yaml +4 -5
- package/node_modules/@ykdz/template-core/dist/preset-projection.d.ts +3 -3
- package/node_modules/@ykdz/template-core/dist/preset-projection.js +1 -0
- package/node_modules/@ykdz/template-core/dist/preset-source.js +60 -45
- package/node_modules/@ykdz/template-core/dist/project-github.d.ts +3 -3
- package/node_modules/@ykdz/template-core/dist/project-github.js +1 -0
- package/node_modules/@ykdz/template-core/dist/projection-capabilities.d.ts +7 -3
- package/node_modules/@ykdz/template-core/dist/projection-capabilities.js +196 -130
- package/node_modules/@ykdz/template-core/dist/renderer.d.ts +2 -2
- package/node_modules/@ykdz/template-core/dist/renderer.js +2 -2
- package/node_modules/@ykdz/template-core/dist/template-boundary-check.d.ts +1 -1
- package/node_modules/@ykdz/template-core/dist/template-boundary-check.js +8 -10
- package/node_modules/@ykdz/template-core/dist/toolchain-resolution.d.ts +5 -5
- package/node_modules/@ykdz/template-core/dist/toolchain-resolution.js +10 -11
- package/node_modules/@ykdz/template-core/package.json +9 -2
- 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 {
|
|
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
|
|
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
|
-
|
|
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
|
|
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].
|
|
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].
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
188
|
-
const
|
|
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.
|
|
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
|
-
|
|
10
|
-
? {
|
|
11
|
-
|
|
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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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).
|
|
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:
|
|
434
|
-
taskNames,
|
|
447
|
+
script: checkScript,
|
|
448
|
+
taskNames: rootCheckTaskNames,
|
|
435
449
|
workspacePackageGlobs,
|
|
436
450
|
}),
|
|
437
|
-
fix: rootScriptWithTurboPackageTask(
|
|
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:
|
|
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;
|