@ykdz/template 0.0.5 → 0.0.7
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/src/cli.js +140 -62
- package/dist/src/declarations.js +2 -2
- package/dist/src/generated-scenarios.d.ts +62 -0
- package/dist/src/generated-scenarios.js +361 -0
- package/dist/src/package-addition.js +22 -29
- package/dist/src/preset-projection.d.ts +1 -0
- package/dist/src/preset-source.d.ts +430 -0
- package/dist/src/preset-source.js +940 -0
- package/dist/src/projection-capabilities.d.ts +67 -0
- package/dist/src/projection-capabilities.js +2207 -0
- package/dist/src/renderer.js +19 -1
- package/dist/src/template-boundary-check.d.ts +7 -3
- package/dist/src/template-boundary-check.js +104 -10
- package/package.json +3 -2
- package/templates/preset-source.json +499 -0
- package/templates/vue-app/playwright.package-addition.config.ts +22 -0
- package/dist/templates/hono-api/projection.d.ts +0 -6
- package/dist/templates/hono-api/projection.js +0 -559
- package/dist/templates/projection-plans.d.ts +0 -7
- package/dist/templates/projection-plans.js +0 -94
- package/dist/templates/registry.d.ts +0 -7
- package/dist/templates/registry.js +0 -45
- package/dist/templates/rust-bin/projection.d.ts +0 -9
- package/dist/templates/rust-bin/projection.js +0 -400
- package/dist/templates/ts-lib/projection.d.ts +0 -6
- package/dist/templates/ts-lib/projection.js +0 -536
- package/dist/templates/vue-app/projection.d.ts +0 -6
- package/dist/templates/vue-app/projection.js +0 -766
- package/dist/templates/vue-hono-app/projection.d.ts +0 -8
- package/dist/templates/vue-hono-app/projection.js +0 -713
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { execa } from "execa";
|
|
4
|
+
import { assembleGenerationContext } from "./generation-context.js";
|
|
5
|
+
import { renderPlaywrightBrowserInstallCommand, } from "./module-graph.js";
|
|
6
|
+
import { planNextStepInstructions } from "./next-step-instructions.js";
|
|
7
|
+
import { findPresetSourceManifestPreset } from "./preset-source.js";
|
|
8
|
+
import { blueprintForPresetSourcePreset, projectPresetSourcePreset, } from "./projection-capabilities.js";
|
|
9
|
+
const defaultDeterministicToolchainEnv = {
|
|
10
|
+
TEMPLATE_TOOLCHAIN_RESOLUTION: "bundled-fallback",
|
|
11
|
+
};
|
|
12
|
+
const defaultFixtureConcurrency = 2;
|
|
13
|
+
export function createExclusiveLock() {
|
|
14
|
+
let tail = Promise.resolve();
|
|
15
|
+
return {
|
|
16
|
+
async run(callback) {
|
|
17
|
+
const previous = tail;
|
|
18
|
+
let release;
|
|
19
|
+
tail = new Promise((resolve) => {
|
|
20
|
+
release = resolve;
|
|
21
|
+
});
|
|
22
|
+
await previous;
|
|
23
|
+
try {
|
|
24
|
+
return await callback();
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
release();
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function generatedScenarioId(scenario) {
|
|
33
|
+
if (!scenario.addedPreset) {
|
|
34
|
+
return scenario.basePreset;
|
|
35
|
+
}
|
|
36
|
+
if (scenario.linkFrom && scenario.linkFrom.length > 0) {
|
|
37
|
+
const linkFromId = scenario.linkFrom
|
|
38
|
+
.map((packagePath) => packagePath.replaceAll("/", "-"))
|
|
39
|
+
.join("-");
|
|
40
|
+
return `${scenario.basePreset}-add-${scenario.addedPreset}-link-from-${linkFromId}`;
|
|
41
|
+
}
|
|
42
|
+
return `${scenario.basePreset}-add-${scenario.addedPreset}`;
|
|
43
|
+
}
|
|
44
|
+
export function generatedScenarioLabel(scenario) {
|
|
45
|
+
if (!scenario.addedPreset) {
|
|
46
|
+
return scenario.basePreset;
|
|
47
|
+
}
|
|
48
|
+
if (scenario.linkFrom && scenario.linkFrom.length > 0) {
|
|
49
|
+
return `${scenario.basePreset} + ${scenario.addedPreset} linked from ${scenario.linkFrom.join(", ")}`;
|
|
50
|
+
}
|
|
51
|
+
return `${scenario.basePreset} + ${scenario.addedPreset}`;
|
|
52
|
+
}
|
|
53
|
+
function scenario(set, input) {
|
|
54
|
+
return {
|
|
55
|
+
set,
|
|
56
|
+
basePreset: input.basePreset,
|
|
57
|
+
addedPreset: input.addedPreset,
|
|
58
|
+
linkFrom: input.linkFrom ? [...input.linkFrom] : undefined,
|
|
59
|
+
id: generatedScenarioId(input),
|
|
60
|
+
label: generatedScenarioLabel(input),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export function selectGeneratedScenarios(manifest, set) {
|
|
64
|
+
const contract = manifest.fixtureMatrix;
|
|
65
|
+
if (!contract) {
|
|
66
|
+
throw new Error(`Preset Source ${manifest.name} does not declare a Fixture Matrix Contract`);
|
|
67
|
+
}
|
|
68
|
+
const initScenarios = contract.initSupport.map((support) => scenario(set, { basePreset: support.preset }));
|
|
69
|
+
if (set === "init") {
|
|
70
|
+
return { runnable: initScenarios, skipped: [] };
|
|
71
|
+
}
|
|
72
|
+
const matrixScenarios = contract.supportedCombinations.map((combination) => scenario(set, {
|
|
73
|
+
basePreset: combination.basePreset,
|
|
74
|
+
addedPreset: combination.addedPreset,
|
|
75
|
+
linkFrom: combination.linkFrom,
|
|
76
|
+
}));
|
|
77
|
+
const skipped = contract.semanticSkips.map((skip) => ({
|
|
78
|
+
set,
|
|
79
|
+
basePreset: skip.basePreset,
|
|
80
|
+
addedPreset: skip.addedPreset,
|
|
81
|
+
id: generatedScenarioId(skip),
|
|
82
|
+
label: generatedScenarioLabel(skip),
|
|
83
|
+
reason: skip.reason,
|
|
84
|
+
}));
|
|
85
|
+
return {
|
|
86
|
+
runnable: matrixScenarios,
|
|
87
|
+
skipped,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export function packageLeafNameForAddedPreset(manifest, presetName) {
|
|
91
|
+
const support = manifest.fixtureMatrix?.packageAdditionSupport.find((entry) => entry.preset === presetName);
|
|
92
|
+
if (!support) {
|
|
93
|
+
throw new Error(`Missing fixture Package Addition leaf name for preset: ${presetName}`);
|
|
94
|
+
}
|
|
95
|
+
return support.packageLeafName;
|
|
96
|
+
}
|
|
97
|
+
export async function defaultGeneratedScenarioCommandRunner(command, args, cwd, options = {}) {
|
|
98
|
+
const prefix = options.logPrefix ? `[${options.logPrefix}] ` : "";
|
|
99
|
+
console.log(`${prefix}$ ${[command, ...args].join(" ")}`);
|
|
100
|
+
await execa(command, [...args], {
|
|
101
|
+
cwd,
|
|
102
|
+
env: options.env,
|
|
103
|
+
stdio: "inherit",
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async function generateScenario(scenario, workspace, options) {
|
|
107
|
+
const projectDir = path.join(workspace, `fixture-${scenario.id}`);
|
|
108
|
+
await options.runCommand("pnpm", [
|
|
109
|
+
"exec",
|
|
110
|
+
"tsx",
|
|
111
|
+
options.cliPath,
|
|
112
|
+
"init",
|
|
113
|
+
projectDir,
|
|
114
|
+
"--preset",
|
|
115
|
+
scenario.basePreset,
|
|
116
|
+
"--yes",
|
|
117
|
+
], options.repoRoot, {
|
|
118
|
+
env: options.deterministicToolchainEnv,
|
|
119
|
+
logPrefix: scenario.label,
|
|
120
|
+
});
|
|
121
|
+
return projectDir;
|
|
122
|
+
}
|
|
123
|
+
async function applyPackageAddition(manifest, scenario, projectDir, options) {
|
|
124
|
+
if (!scenario.addedPreset) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
const packageLeafName = packageLeafNameForAddedPreset(manifest, scenario.addedPreset);
|
|
128
|
+
const beforeConsumerSources = await linkedConsumerSourceSnapshots(projectDir, scenario.linkFrom ?? []);
|
|
129
|
+
await options.runCommand("pnpm", [
|
|
130
|
+
"exec",
|
|
131
|
+
"tsx",
|
|
132
|
+
options.cliPath,
|
|
133
|
+
"add",
|
|
134
|
+
"package",
|
|
135
|
+
"--preset",
|
|
136
|
+
scenario.addedPreset,
|
|
137
|
+
"--name",
|
|
138
|
+
packageLeafName,
|
|
139
|
+
...linkFromArgs(scenario.linkFrom ?? []),
|
|
140
|
+
], projectDir, { logPrefix: scenario.label });
|
|
141
|
+
await assertLinkedConsumerSourcesUnchanged(projectDir, beforeConsumerSources);
|
|
142
|
+
return readAddedPackagePath(projectDir, packageLeafName);
|
|
143
|
+
}
|
|
144
|
+
function linkFromArgs(packagePaths) {
|
|
145
|
+
return packagePaths.flatMap((packagePath) => ["--link-from", packagePath]);
|
|
146
|
+
}
|
|
147
|
+
async function linkedConsumerSourceSnapshots(projectDir, packagePaths) {
|
|
148
|
+
const snapshots = new Map();
|
|
149
|
+
for (const packagePath of packagePaths) {
|
|
150
|
+
snapshots.set(packagePath, await sourceFileSnapshot(path.join(projectDir, packagePath, "src")));
|
|
151
|
+
}
|
|
152
|
+
return snapshots;
|
|
153
|
+
}
|
|
154
|
+
async function sourceFileSnapshot(sourceDir, currentDir = sourceDir) {
|
|
155
|
+
const snapshot = {};
|
|
156
|
+
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
157
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
158
|
+
const entryPath = path.join(currentDir, entry.name);
|
|
159
|
+
if (entry.isDirectory()) {
|
|
160
|
+
Object.assign(snapshot, await sourceFileSnapshot(sourceDir, entryPath));
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (entry.isFile()) {
|
|
164
|
+
snapshot[path.relative(sourceDir, entryPath)] = await readFile(entryPath, "utf8");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return snapshot;
|
|
168
|
+
}
|
|
169
|
+
async function assertLinkedConsumerSourcesUnchanged(projectDir, beforeSnapshots) {
|
|
170
|
+
for (const [packagePath, beforeSnapshot] of beforeSnapshots) {
|
|
171
|
+
const afterSnapshot = await sourceFileSnapshot(path.join(projectDir, packagePath, "src"));
|
|
172
|
+
if (JSON.stringify(afterSnapshot) !== JSON.stringify(beforeSnapshot)) {
|
|
173
|
+
throw new Error(`Package Link Intent fixture modified consumer source files for ${packagePath}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function readAddedPackagePath(projectDir, packageLeafName) {
|
|
178
|
+
const blueprint = JSON.parse(await readFile(path.join(projectDir, ".template", "blueprint.json"), "utf8"));
|
|
179
|
+
const packageDefinition = blueprint.packages?.find((pkg) => path.basename(pkg.path) === packageLeafName);
|
|
180
|
+
if (!packageDefinition) {
|
|
181
|
+
throw new Error(`Package Addition fixture did not record package leaf ${packageLeafName}`);
|
|
182
|
+
}
|
|
183
|
+
return packageDefinition.path;
|
|
184
|
+
}
|
|
185
|
+
function projectionPlanForPreset(presetName, projectDir, manifest, options, packageScope) {
|
|
186
|
+
const preset = findPresetSourceManifestPreset(manifest, presetName);
|
|
187
|
+
if (!preset?.projection) {
|
|
188
|
+
throw new Error(`Missing Preset Projection for fixture preset ${presetName}`);
|
|
189
|
+
}
|
|
190
|
+
const blueprint = blueprintForPresetSourcePreset(preset, {
|
|
191
|
+
targetDir: projectDir,
|
|
192
|
+
scope: packageScope,
|
|
193
|
+
});
|
|
194
|
+
return projectPresetSourcePreset({
|
|
195
|
+
preset,
|
|
196
|
+
context: assembleGenerationContext({
|
|
197
|
+
targetDir: projectDir,
|
|
198
|
+
blueprint,
|
|
199
|
+
toolchain: {
|
|
200
|
+
nodeLtsMajor: { kind: "NodeLtsMajor", value: "24" },
|
|
201
|
+
packageManagerPin: { kind: "PackageManagerPin", value: "pnpm@10.0.0" },
|
|
202
|
+
source: "bundled-fallback",
|
|
203
|
+
diagnostics: [],
|
|
204
|
+
},
|
|
205
|
+
}),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
function machineVerifiableNextStepsForPreset(presetName, projectDir, manifest, options) {
|
|
209
|
+
const projectionPlan = projectionPlanForPreset(presetName, projectDir, manifest, options);
|
|
210
|
+
const plan = planNextStepInstructions({
|
|
211
|
+
targetDir: projectDir,
|
|
212
|
+
projectionPlan,
|
|
213
|
+
});
|
|
214
|
+
return plan.steps
|
|
215
|
+
.filter((step) => step.machineVerifiable && step.kind === "command")
|
|
216
|
+
.map((step) => ({
|
|
217
|
+
id: step.id,
|
|
218
|
+
command: step.command,
|
|
219
|
+
args: step.args,
|
|
220
|
+
cwd: step.cwd,
|
|
221
|
+
display: step.display,
|
|
222
|
+
}));
|
|
223
|
+
}
|
|
224
|
+
function addedPresetEnvironmentNeeds(manifest, scenario, projectDir, addedPackagePath, options) {
|
|
225
|
+
if (!scenario.addedPreset || !addedPackagePath) {
|
|
226
|
+
return [];
|
|
227
|
+
}
|
|
228
|
+
return projectionPlanForPreset(scenario.addedPreset, projectDir, manifest, options, path.basename(projectDir)).checkPlan.environmentNeeds.map((need) => {
|
|
229
|
+
if (need.owner.kind !== "package-boundary") {
|
|
230
|
+
return need;
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
...need,
|
|
234
|
+
owner: { kind: "package-boundary", path: addedPackagePath },
|
|
235
|
+
};
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function environmentNeedStep(need, projectDir) {
|
|
239
|
+
const display = renderPlaywrightBrowserInstallCommand(need);
|
|
240
|
+
const [command, ...args] = display.split(" ");
|
|
241
|
+
return {
|
|
242
|
+
id: `install-${need.owner.path}-playwright-browsers`,
|
|
243
|
+
command,
|
|
244
|
+
args,
|
|
245
|
+
cwd: projectDir,
|
|
246
|
+
display,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
export function generatedScenarioQualityGateSteps(manifest, scenario, projectDir, addedPackagePath, options) {
|
|
250
|
+
const normalizedOptions = normalizeRunnerOptions(options);
|
|
251
|
+
const steps = machineVerifiableNextStepsForPreset(scenario.basePreset, projectDir, manifest, normalizedOptions);
|
|
252
|
+
const addedEnvironmentSteps = addedPresetEnvironmentNeeds(manifest, scenario, projectDir, addedPackagePath, normalizedOptions).map((need) => environmentNeedStep(need, projectDir));
|
|
253
|
+
const existingDisplays = new Set(steps.map((step) => step.display));
|
|
254
|
+
const extraEnvironmentSteps = addedEnvironmentSteps.filter((step) => !existingDisplays.has(step.display));
|
|
255
|
+
if (extraEnvironmentSteps.length === 0) {
|
|
256
|
+
return steps;
|
|
257
|
+
}
|
|
258
|
+
const rootCheckIndex = steps.findIndex((step) => step.id === "run-root-check");
|
|
259
|
+
if (rootCheckIndex === -1) {
|
|
260
|
+
return [...steps, ...extraEnvironmentSteps];
|
|
261
|
+
}
|
|
262
|
+
return [
|
|
263
|
+
...steps.slice(0, rootCheckIndex),
|
|
264
|
+
...extraEnvironmentSteps,
|
|
265
|
+
...steps.slice(rootCheckIndex),
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
async function runGeneratedScenarioQualityGate(manifest, scenario, projectDir, addedPackagePath, options) {
|
|
269
|
+
const steps = generatedScenarioQualityGateSteps(manifest, scenario, projectDir, addedPackagePath, options);
|
|
270
|
+
const requiresSerializedPlaywrightRootCheck = steps.some((step) => step.id.endsWith("-playwright-browsers"));
|
|
271
|
+
for (const step of steps) {
|
|
272
|
+
const env = step.id === "run-root-check" ? { CI: "1" } : undefined;
|
|
273
|
+
const runStep = async () => {
|
|
274
|
+
await options.runCommand(step.command, [...step.args], step.cwd, {
|
|
275
|
+
env,
|
|
276
|
+
logPrefix: scenario.label,
|
|
277
|
+
});
|
|
278
|
+
};
|
|
279
|
+
if (step.id === "run-root-check" && requiresSerializedPlaywrightRootCheck) {
|
|
280
|
+
await options.rootCheckLock.run(runStep);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
await runStep();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async function checkGeneratedScenario(manifest, scenario, workspace, options) {
|
|
287
|
+
options.reporter.info?.(`\n== ${scenario.label} ==`);
|
|
288
|
+
const projectDir = await generateScenario(scenario, workspace, options);
|
|
289
|
+
const addedPackagePath = await applyPackageAddition(manifest, scenario, projectDir, options);
|
|
290
|
+
await runGeneratedScenarioQualityGate(manifest, scenario, projectDir, addedPackagePath, options);
|
|
291
|
+
}
|
|
292
|
+
export function fixtureConcurrency(scenarioCount) {
|
|
293
|
+
const rawValue = process.env.TEMPLATE_FIXTURE_CONCURRENCY;
|
|
294
|
+
if (rawValue === undefined || rawValue === "") {
|
|
295
|
+
return Math.min(defaultFixtureConcurrency, scenarioCount);
|
|
296
|
+
}
|
|
297
|
+
const value = Number(rawValue);
|
|
298
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
299
|
+
throw new Error("TEMPLATE_FIXTURE_CONCURRENCY must be a positive integer");
|
|
300
|
+
}
|
|
301
|
+
return Math.min(value, scenarioCount);
|
|
302
|
+
}
|
|
303
|
+
export function errorForFailedGeneratedScenario(scenario, error) {
|
|
304
|
+
if (error instanceof Error) {
|
|
305
|
+
return new Error(`Fixture scenario failed: ${scenario.label}`, {
|
|
306
|
+
cause: error,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
return new Error(`Fixture scenario failed: ${scenario.label}: ${String(error)}`);
|
|
310
|
+
}
|
|
311
|
+
function normalizeRunnerOptions(options) {
|
|
312
|
+
return {
|
|
313
|
+
repoRoot: options.repoRoot,
|
|
314
|
+
cliPath: options.cliPath,
|
|
315
|
+
runCommand: options.runCommand ?? defaultGeneratedScenarioCommandRunner,
|
|
316
|
+
reporter: options.reporter ?? {
|
|
317
|
+
info: console.log,
|
|
318
|
+
error: console.error,
|
|
319
|
+
},
|
|
320
|
+
deterministicToolchainEnv: options.deterministicToolchainEnv ?? defaultDeterministicToolchainEnv,
|
|
321
|
+
rootCheckLock: options.rootCheckLock ?? createExclusiveLock(),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
export async function runGeneratedScenariosConcurrently(manifest, scenarios, workspace, concurrency, options) {
|
|
325
|
+
const normalizedOptions = normalizeRunnerOptions(options);
|
|
326
|
+
let nextScenarioIndex = 0;
|
|
327
|
+
let firstFailure;
|
|
328
|
+
async function worker() {
|
|
329
|
+
while (firstFailure === undefined) {
|
|
330
|
+
const scenario = scenarios[nextScenarioIndex];
|
|
331
|
+
nextScenarioIndex += 1;
|
|
332
|
+
if (!scenario) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
await checkGeneratedScenario(manifest, scenario, workspace, normalizedOptions);
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
firstFailure ??= errorForFailedGeneratedScenario(scenario, error);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
await Promise.all(Array.from({ length: concurrency }, async () => {
|
|
345
|
+
await worker();
|
|
346
|
+
}));
|
|
347
|
+
if (firstFailure) {
|
|
348
|
+
throw firstFailure;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
export async function runGeneratedScenarioSet(manifest, set, workspace, options) {
|
|
352
|
+
const normalizedOptions = normalizeRunnerOptions(options);
|
|
353
|
+
const selection = selectGeneratedScenarios(manifest, set);
|
|
354
|
+
const concurrency = fixtureConcurrency(selection.runnable.length);
|
|
355
|
+
for (const skippedScenario of selection.skipped) {
|
|
356
|
+
normalizedOptions.reporter.info?.(`-- Skipping ${skippedScenario.label}: ${skippedScenario.reason}`);
|
|
357
|
+
}
|
|
358
|
+
normalizedOptions.reporter.info?.(`Checking ${selection.runnable.length} fixture scenarios with concurrency ${concurrency}.`);
|
|
359
|
+
await runGeneratedScenariosConcurrently(manifest, selection.runnable, workspace, concurrency, normalizedOptions);
|
|
360
|
+
return selection;
|
|
361
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { builtInPresetProjections, findBuiltInPresetProjection, } from "../templates/registry.js";
|
|
4
3
|
import { validateProjectBlueprint, } from "./declarations.js";
|
|
5
4
|
import { collectGeneratedManifestCatalogReferences, pnpmWorkspaceYamlWithCatalogDependencies, } from "./dependency-catalog.js";
|
|
6
5
|
import { PackageAdditionSupport } from "./package-addition-support.js";
|
|
7
6
|
import { assertTypeScriptPackageBoundaryForLinkIntent, packageTurboTasks, planPackageLinks, } from "./package-linking.js";
|
|
7
|
+
import { findBuiltInPresetSourceManifestPreset, loadBuiltInPresetSourceManifest, } from "./preset-source.js";
|
|
8
|
+
import { defaultPackagePathForPresetSourcePackageAddition, planPresetSourcePackageAddition, } from "./projection-capabilities.js";
|
|
8
9
|
import { renderProject } from "./renderer.js";
|
|
9
10
|
function projectNameFromBlueprint(blueprint, fallbackProjectName) {
|
|
10
11
|
const firstPackage = blueprint.packages?.[0];
|
|
@@ -74,16 +75,10 @@ function validateExplicitPackagePath(packagePath) {
|
|
|
74
75
|
}
|
|
75
76
|
return packagePath;
|
|
76
77
|
}
|
|
77
|
-
function defaultPackagePathForPreset(preset, packageLeafName) {
|
|
78
|
-
return preset === "ts-lib"
|
|
79
|
-
? `packages/${packageLeafName}`
|
|
80
|
-
: `apps/${packageLeafName}`;
|
|
81
|
-
}
|
|
82
78
|
function supportedPackageAdditionPresetNames() {
|
|
83
|
-
return
|
|
84
|
-
.filter((
|
|
85
|
-
|
|
86
|
-
.map((projection) => projection.metadata.name);
|
|
79
|
+
return loadBuiltInPresetSourceManifest()
|
|
80
|
+
.presets.filter((preset) => preset.packageAdditionSupport === PackageAdditionSupport.Supported)
|
|
81
|
+
.map((preset) => preset.name);
|
|
87
82
|
}
|
|
88
83
|
function formatUnsupportedPackageAdditionPresetError(preset) {
|
|
89
84
|
return [
|
|
@@ -627,28 +622,26 @@ export async function addPackage(options) {
|
|
|
627
622
|
const repositoryMetadata = await readGeneratedRepositoryPackageMetadata(root);
|
|
628
623
|
const projectName = projectNameFromBlueprint(blueprint, repositoryMetadata.projectName);
|
|
629
624
|
const packageName = `@${projectName}/${options.name}`;
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
: defaultPackagePathForPreset(options.preset, options.name);
|
|
633
|
-
const projection = findBuiltInPresetProjection(options.preset);
|
|
634
|
-
if (!projection) {
|
|
625
|
+
const preset = findBuiltInPresetSourceManifestPreset(options.preset);
|
|
626
|
+
if (!preset) {
|
|
635
627
|
throw new Error(`Unknown preset for Package Addition: ${options.preset}`);
|
|
636
628
|
}
|
|
637
|
-
if (
|
|
638
|
-
PackageAdditionSupport.Supported) {
|
|
629
|
+
if (preset.packageAdditionSupport !== PackageAdditionSupport.Supported) {
|
|
639
630
|
throw new Error(formatUnsupportedPackageAdditionPresetError(options.preset));
|
|
640
631
|
}
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
632
|
+
const packagePath = options.path
|
|
633
|
+
? validateExplicitPackagePath(options.path)
|
|
634
|
+
: defaultPackagePathForPresetSourcePackageAddition(preset, options.name);
|
|
635
|
+
const additionPlan = await planPresetSourcePackageAddition({
|
|
636
|
+
preset,
|
|
637
|
+
addition: {
|
|
638
|
+
root,
|
|
639
|
+
blueprint,
|
|
640
|
+
packageLeafName: options.name,
|
|
641
|
+
packageName,
|
|
642
|
+
packagePath,
|
|
643
|
+
nodeVersion: repositoryMetadata.nodeVersion,
|
|
644
|
+
},
|
|
652
645
|
});
|
|
653
646
|
const requiresSharedOxcConfiguration = additionPlan.sourceRoots?.sharedOxc !== undefined;
|
|
654
647
|
assertNoPackageConflict(blueprint, packageName, additionPlan.packagePath);
|
|
@@ -663,7 +656,7 @@ export async function addPackage(options) {
|
|
|
663
656
|
packageLinkIntents,
|
|
664
657
|
});
|
|
665
658
|
await assertMissingPackagePath(additionPlan.packagePath, path.join(root, additionPlan.packagePath));
|
|
666
|
-
const rootUpdatePlan = await planRootUpdates(root, blueprint, packageName, additionPlan.packagePath, additionPlan.packageRole, additionPlan.packageSourcePreset, packageLinkIntents, additionPlan.workspacePackageGlob, packageAdditionCatalogDependencies(additionPlan.operations), requiresSharedOxcConfiguration, additionPlan.operations);
|
|
659
|
+
const rootUpdatePlan = await planRootUpdates(root, blueprint, packageName, additionPlan.packagePath, additionPlan.packageRole, additionPlan.packageSourcePreset, packageLinkIntents, additionPlan.workspaceMembershipGlob ?? additionPlan.workspacePackageGlob, packageAdditionCatalogDependencies(additionPlan.operations), requiresSharedOxcConfiguration, additionPlan.operations);
|
|
667
660
|
await mkdir(path.join(root, additionPlan.packagePath), { recursive: true });
|
|
668
661
|
await ensureRootOxcConfiguration({
|
|
669
662
|
root,
|
|
@@ -16,6 +16,7 @@ export type PresetPackageAdditionOptions = {
|
|
|
16
16
|
export type PresetPackageAdditionPlan = {
|
|
17
17
|
readonly packagePath: string;
|
|
18
18
|
readonly workspacePackageGlob: string;
|
|
19
|
+
readonly workspaceMembershipGlob?: string;
|
|
19
20
|
readonly packageRole: PackageRole;
|
|
20
21
|
readonly packageSourcePreset: PackageSourcePreset;
|
|
21
22
|
readonly sourceRoot: string;
|