@ykdz/template 0.0.31 → 0.0.33
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/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.d.ts +10 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.js +320 -161
- package/node_modules/@ykdz/template-builtin-presets/dist/src/rust-bin/definition.js +66 -9
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.d.ts +2 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.js +35 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/typescript.d.ts +13 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/typescript.js +70 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.d.ts +2 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.js +22 -7
- package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-cli/definition.js +4 -3
- package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-lib/definition.js +4 -3
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vike-app/definition.js +82 -11
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.js +9 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.js +27 -10
- package/node_modules/@ykdz/template-builtin-presets/package.json +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment-diagnostics.yml +81 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment.yml +55 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-diagnostics.yml +64 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check.yml +24 -5
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/devcontainer.json +3 -3
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/gitignore +1 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/turbo.json +1 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/typescript-config/base.json +15 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/typescript-config/turbo.json +4 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/rust-bin/devcontainer/rust.Dockerfile +3 -3
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/devcontainer/browser-test.Dockerfile +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/oxc/tsconfig.config.json +2 -12
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/vue/tsconfig.app.json +6 -13
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/vue/tsconfig.node.json +2 -12
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/tsconfig.json +2 -6
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-lib/tsconfig.json +3 -3
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/tsconfig.json +2 -11
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/tsconfig.json +2 -11
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/devcontainer/docker-client.Dockerfile +3 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/playwright.config.ts +1 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/scripts/check-standalone-deployment.ts +67 -27
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/tsconfig.app.json +6 -12
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/tsconfig.node.json +2 -11
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/tsconfig.test.json +0 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/playwright.config.ts +2 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/api/tsconfig.json +2 -12
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/playwright.config.ts +2 -0
- package/node_modules/@ykdz/template-core/dist/ci-diagnostic-artifact.d.ts +19 -0
- package/node_modules/@ykdz/template-core/dist/ci-diagnostic-artifact.js +68 -0
- package/node_modules/@ykdz/template-core/dist/development-container-tool-layer.d.ts +56 -0
- package/node_modules/@ykdz/template-core/dist/development-container-tool-layer.js +361 -0
- package/node_modules/@ykdz/template-core/dist/package-contribution.d.ts +15 -1
- package/node_modules/@ykdz/template-core/dist/package-contribution.js +7 -0
- package/node_modules/@ykdz/template-core/dist/pnpm-workspace.yaml +1 -0
- package/node_modules/@ykdz/template-core/dist/project-github.d.ts +51 -15
- package/node_modules/@ykdz/template-core/dist/project-github.js +83 -96
- package/node_modules/@ykdz/template-core/dist/project-projection.d.ts +9 -0
- package/node_modules/@ykdz/template-core/dist/project-projection.js +84 -5
- package/node_modules/@ykdz/template-core/dist/renderer.d.ts +1 -0
- package/node_modules/@ykdz/template-core/dist/renderer.js +47 -7
- package/node_modules/@ykdz/template-core/dist/template-boundary-check.d.ts +15 -1
- package/node_modules/@ykdz/template-core/dist/template-boundary-check.js +183 -1
- package/package.json +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-playwright.yml +0 -20
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-rust.yml +0 -23
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check.dynamic.template +0 -19
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/rust/.vscode/extensions.json +0 -3
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/rust/.vscode/settings.json +0 -5
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/rust/devcontainer/devcontainer.json +0 -21
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/rust/devcontainer/rust.Dockerfile +0 -18
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/rust/rust-toolchain.toml +0 -3
- package/node_modules/@ykdz/template-builtin-presets/templates/rust-bin/devcontainer/devcontainer.json +0 -21
- package/node_modules/@ykdz/template-core/dist/devcontainer.d.ts +0 -83
- package/node_modules/@ykdz/template-core/dist/devcontainer.js +0 -217
- /package/node_modules/@ykdz/template-builtin-presets/templates/{shared → vike-app}/devcontainer/shellcheck.Dockerfile +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { CiDiagnosticArtifactDeclaration } from "#template-core/ci-diagnostic-artifact";
|
|
2
|
+
import { type DevelopmentContainerToolLayerBuildArgument, type DevelopmentContainerToolLayerMount, type DevelopmentContainerToolLayerProbe, type PlannedDevelopmentContainerToolLayer } from "#template-core/development-container-tool-layer";
|
|
1
3
|
import type { CheckEnvironmentNeed, DeploymentEnvironmentNeed } from "#template-core/module-graph";
|
|
2
4
|
import { type PackageContribution } from "#template-core/package-contribution";
|
|
3
5
|
import type { BuiltInPresetDefinition, GenerationContext } from "#template-core/preset-definition";
|
|
@@ -16,7 +18,7 @@ export type NextStepInstruction = {
|
|
|
16
18
|
type GeneratedPackagePlanningRecord = {
|
|
17
19
|
readonly path: string;
|
|
18
20
|
readonly definitionName: string;
|
|
19
|
-
readonly planningContribution: "planInitialization" | "planPackageAddition";
|
|
21
|
+
readonly planningContribution: "foundationPlan" | "planInitialization" | "planPackageAddition";
|
|
20
22
|
};
|
|
21
23
|
type GenerationRecord = {
|
|
22
24
|
readonly schemaVersion: 1;
|
|
@@ -33,8 +35,15 @@ export type GeneratedRepositoryPlan = {
|
|
|
33
35
|
readonly generationRecord: GenerationRecord;
|
|
34
36
|
readonly operations: readonly RenderOperation[];
|
|
35
37
|
readonly reconciliation: readonly ProjectProjectionReconciliation[];
|
|
38
|
+
readonly developmentContainer: {
|
|
39
|
+
readonly toolLayers: readonly PlannedDevelopmentContainerToolLayer[];
|
|
40
|
+
readonly buildArguments: readonly DevelopmentContainerToolLayerBuildArgument[];
|
|
41
|
+
readonly mounts: readonly DevelopmentContainerToolLayerMount[];
|
|
42
|
+
readonly probes: readonly DevelopmentContainerToolLayerProbe[];
|
|
43
|
+
};
|
|
36
44
|
readonly environmentNeeds: readonly CheckEnvironmentNeed[];
|
|
37
45
|
readonly deploymentEnvironmentNeeds: readonly DeploymentEnvironmentNeed[];
|
|
46
|
+
readonly ciDiagnosticArtifacts: readonly CiDiagnosticArtifactDeclaration[];
|
|
38
47
|
/** Structured manifests used to derive the generated Dependency Catalog. */
|
|
39
48
|
readonly manifests: readonly Readonly<Record<string, unknown>>[];
|
|
40
49
|
readonly dependencyCatalog: Readonly<Record<string, string>>;
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { composeCiDiagnosticArtifacts } from "#template-core/ci-diagnostic-artifact";
|
|
4
5
|
import { collectGeneratedManifestCatalogReferences, selectTemplateDependencyCatalogEntries, } from "#template-core/dependency-catalog";
|
|
5
|
-
import {
|
|
6
|
+
import { planDevelopmentContainerToolLayersSync, } from "#template-core/development-container-tool-layer";
|
|
6
7
|
import { editorCustomizationForCapabilities, loadEditorCustomizationDeclarations, } from "#template-core/editor-customization";
|
|
7
8
|
import { checkEnvironmentNeedFact, checkEnvironmentNeedFromFact, deploymentEnvironmentNeedFact, deploymentEnvironmentNeedFromFact, renderDeploymentCheckCommand, renderFixCommand, renderRootCheckCommand, } from "#template-core/module-graph";
|
|
8
9
|
import { assertPackageContribution, } from "#template-core/package-contribution";
|
|
9
10
|
import { assertProjectBlueprintV2, validateProjectBlueprintV2 as validateCoreProjectBlueprintV2, } from "#template-core/project-blueprint-v2";
|
|
10
|
-
import { projectCheckWorkflowTemplateReplacements, projectDependabotTemplateReplacements, } from "#template-core/project-github";
|
|
11
|
+
import { projectCheckWorkflowTemplateSource, projectCheckWorkflowTemplateReplacements, projectDependabotTemplateReplacements, } from "#template-core/project-github";
|
|
11
12
|
import { planExplicitProjectLinks } from "#template-core/project-linking-v2";
|
|
12
13
|
import { resolveTemplateSource, } from "#template-core/renderer";
|
|
13
14
|
import { rustBinDefinition } from "./rust-bin/definition.js";
|
|
15
|
+
import { typescriptConfigContribution, typescriptConfigPackageDefinition, typescriptConfigPackageName, } from "./shared/typescript.js";
|
|
14
16
|
import { vuePnpmDependencyOverrides } from "./shared/vue.js";
|
|
15
17
|
import { templateSources } from "./template-sources.js";
|
|
16
18
|
import { tsCliDefinition } from "./ts-cli/definition.js";
|
|
@@ -188,7 +190,7 @@ function readGenerationRecord(options) {
|
|
|
188
190
|
if (!isRecord(item) ||
|
|
189
191
|
typeof item.path !== "string" ||
|
|
190
192
|
typeof item.definitionName !== "string" ||
|
|
191
|
-
!["planInitialization", "planPackageAddition"].includes(String(item.planningContribution))) {
|
|
193
|
+
!["foundationPlan", "planInitialization", "planPackageAddition"].includes(String(item.planningContribution))) {
|
|
192
194
|
throw new Error(`Package Addition requires valid package planning facts at .template/generation.json packages[${index}]`);
|
|
193
195
|
}
|
|
194
196
|
packages.push(item);
|
|
@@ -219,6 +221,13 @@ function readGenerationRecord(options) {
|
|
|
219
221
|
return generationRecord;
|
|
220
222
|
}
|
|
221
223
|
function assertGenerationRecordFoundationConsistency(options) {
|
|
224
|
+
const configDefinition = typescriptConfigPackageDefinition(options.context);
|
|
225
|
+
const foundationRecords = options.generationRecord.packages.filter((record) => record.planningContribution === "foundationPlan");
|
|
226
|
+
if (foundationRecords.length !== 1 ||
|
|
227
|
+
foundationRecords[0].definitionName !== "foundation" ||
|
|
228
|
+
foundationRecords[0].path !== configDefinition.path) {
|
|
229
|
+
throw new Error("Package Addition Generation Record must contain exactly one Foundation TypeScript configuration provenance record");
|
|
230
|
+
}
|
|
222
231
|
const initialRecords = options.generationRecord.packages.filter((record) => record.planningContribution === "planInitialization");
|
|
223
232
|
if (initialRecords.length === 0) {
|
|
224
233
|
throw new Error(`Package Addition Generation Record preset ${options.generationRecord.preset} has no initial Package provenance`);
|
|
@@ -370,7 +379,10 @@ function readExistingPackageAdditionState(options) {
|
|
|
370
379
|
const generationRecord = readGenerationRecord(options);
|
|
371
380
|
const persistedEnvironmentNeeds = readPersistedEnvironmentNeeds(options.context.targetDir);
|
|
372
381
|
const manifestTruthByPackagePath = new Map();
|
|
373
|
-
const contributions = generationRecord.packages.
|
|
382
|
+
const contributions = generationRecord.packages.flatMap((record) => {
|
|
383
|
+
if (record.planningContribution === "foundationPlan") {
|
|
384
|
+
return [];
|
|
385
|
+
}
|
|
374
386
|
const expectedDefinition = options.blueprint.packages.find((definition) => definition.path === record.path);
|
|
375
387
|
const definition = builtInPresetRegistry.require(record.definitionName);
|
|
376
388
|
let candidates;
|
|
@@ -397,7 +409,7 @@ function readExistingPackageAdditionState(options) {
|
|
|
397
409
|
if (contribution === undefined) {
|
|
398
410
|
throw new Error(`Generation Record cannot reproduce Package Definition ${expectedDefinition.name} at ${expectedDefinition.path}`);
|
|
399
411
|
}
|
|
400
|
-
return contribution;
|
|
412
|
+
return [contribution];
|
|
401
413
|
});
|
|
402
414
|
for (const packagePath of new Set(options.manifestTruthPackagePaths ?? [])) {
|
|
403
415
|
const expectedDefinition = options.blueprint.packages.find((definition) => definition.path === packagePath);
|
|
@@ -460,95 +472,277 @@ function turboBoundaryTagsForContributions(contributions) {
|
|
|
460
472
|
}
|
|
461
473
|
return [...selectedTags];
|
|
462
474
|
}
|
|
463
|
-
function
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
source: templateSources.foundation,
|
|
471
|
-
from: "devcontainer.json",
|
|
472
|
-
to: ".devcontainer/devcontainer.json",
|
|
473
|
-
replacements: {
|
|
474
|
-
PROJECT_NAME: options.context.projectName,
|
|
475
|
-
NODE_LTS_MAJOR: options.context.toolchain.nodeLtsMajor,
|
|
476
|
-
PACKAGE_MANAGER_PIN: options.context.toolchain.packageManagerPin,
|
|
477
|
-
PLAYWRIGHT_CLI_PACKAGE: browserLayer?.playwrightCliPackage ?? "unused",
|
|
475
|
+
function contributedDevcontainerComposition(options) {
|
|
476
|
+
const layerPlan = planDevelopmentContainerToolLayersSync({
|
|
477
|
+
baseLayer: {
|
|
478
|
+
identity: "node-pnpm",
|
|
479
|
+
dockerfile: {
|
|
480
|
+
source: templateSources.sharedDevcontainer,
|
|
481
|
+
from: "node-pnpm.Dockerfile",
|
|
478
482
|
},
|
|
479
|
-
|
|
480
|
-
{
|
|
481
|
-
kind: "writeTextFromFragments",
|
|
482
|
-
to: ".devcontainer/Dockerfile",
|
|
483
|
-
fragments: [
|
|
483
|
+
buildArguments: [
|
|
484
484
|
{
|
|
485
|
-
|
|
486
|
-
|
|
485
|
+
name: "NODE_VERSION",
|
|
486
|
+
value: options.context.toolchain.nodeLtsMajor,
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
name: "PACKAGE_MANAGER_PIN",
|
|
490
|
+
value: options.context.toolchain.packageManagerPin,
|
|
491
|
+
},
|
|
492
|
+
],
|
|
493
|
+
mounts: [
|
|
494
|
+
{
|
|
495
|
+
identity: "pnpm-store",
|
|
496
|
+
type: "volume",
|
|
497
|
+
source: "${devcontainerId}-pnpm-store",
|
|
498
|
+
target: "/pnpm/store",
|
|
487
499
|
},
|
|
488
|
-
...(browserLayer === undefined
|
|
489
|
-
? []
|
|
490
|
-
: [
|
|
491
|
-
{
|
|
492
|
-
source: templateSources.sharedDevcontainer,
|
|
493
|
-
from: "browser-test.Dockerfile",
|
|
494
|
-
},
|
|
495
|
-
]),
|
|
496
|
-
...(hasShellChecks
|
|
497
|
-
? [
|
|
498
|
-
{
|
|
499
|
-
source: templateSources.sharedDevcontainer,
|
|
500
|
-
from: "shellcheck.Dockerfile",
|
|
501
|
-
},
|
|
502
|
-
]
|
|
503
|
-
: []),
|
|
504
500
|
],
|
|
505
501
|
},
|
|
502
|
+
layers: options.layers,
|
|
503
|
+
});
|
|
504
|
+
return {
|
|
505
|
+
operations: [
|
|
506
|
+
{
|
|
507
|
+
kind: "writeTextTemplate",
|
|
508
|
+
source: templateSources.foundation,
|
|
509
|
+
from: "devcontainer.json",
|
|
510
|
+
to: ".devcontainer/devcontainer.json",
|
|
511
|
+
replacements: {
|
|
512
|
+
PROJECT_NAME: options.context.projectName,
|
|
513
|
+
NODE_LTS_MAJOR: options.context.toolchain.nodeLtsMajor,
|
|
514
|
+
PACKAGE_MANAGER_PIN: options.context.toolchain.packageManagerPin,
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
kind: "mergeJson",
|
|
519
|
+
to: ".devcontainer/devcontainer.json",
|
|
520
|
+
value: {
|
|
521
|
+
build: {
|
|
522
|
+
args: Object.fromEntries(layerPlan.buildArguments.map((argument) => [
|
|
523
|
+
argument.name,
|
|
524
|
+
argument.value,
|
|
525
|
+
])),
|
|
526
|
+
},
|
|
527
|
+
...(layerPlan.mounts.length === 0
|
|
528
|
+
? {}
|
|
529
|
+
: {
|
|
530
|
+
mounts: layerPlan.mounts.map(({ identity: _identity, ...mount }) => mount),
|
|
531
|
+
}),
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
kind: "writeTextFromFragments",
|
|
536
|
+
to: ".devcontainer/Dockerfile",
|
|
537
|
+
validation: "development-container-dockerfile",
|
|
538
|
+
fragments: layerPlan.layers.map((layer) => layer.dockerfile),
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
mountIdentitySet: {
|
|
542
|
+
location: "/mounts",
|
|
543
|
+
identity: {
|
|
544
|
+
kind: "projection",
|
|
545
|
+
members: layerPlan.mounts.map(({ identity, target, }) => ({
|
|
546
|
+
identity,
|
|
547
|
+
match: { target },
|
|
548
|
+
})),
|
|
549
|
+
fallback: { fields: ["target"] },
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
toolLayers: layerPlan.layers,
|
|
553
|
+
buildArguments: layerPlan.buildArguments,
|
|
554
|
+
mounts: layerPlan.mounts,
|
|
555
|
+
probes: layerPlan.probes,
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
function contributedFoundationTemplateFileOperations(contributions) {
|
|
559
|
+
const byIdentity = new Map();
|
|
560
|
+
const outputOwners = new Map();
|
|
561
|
+
for (const file of contributions.flatMap((contribution) => contribution.foundation.templateFiles ?? [])) {
|
|
562
|
+
const previous = byIdentity.get(file.identity);
|
|
563
|
+
if (previous !== undefined) {
|
|
564
|
+
const previousFingerprint = JSON.stringify({
|
|
565
|
+
source: resolveTemplateSource(previous.source, previous.from),
|
|
566
|
+
from: previous.from,
|
|
567
|
+
to: previous.to,
|
|
568
|
+
replacements: previous.replacements ?? {},
|
|
569
|
+
});
|
|
570
|
+
const fingerprint = JSON.stringify({
|
|
571
|
+
source: resolveTemplateSource(file.source, file.from),
|
|
572
|
+
from: file.from,
|
|
573
|
+
to: file.to,
|
|
574
|
+
replacements: file.replacements ?? {},
|
|
575
|
+
});
|
|
576
|
+
if (fingerprint !== previousFingerprint) {
|
|
577
|
+
throw new Error(`Foundation Template File identity ${file.identity} has conflicting descriptors`);
|
|
578
|
+
}
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
const outputOwner = outputOwners.get(file.to);
|
|
582
|
+
if (outputOwner !== undefined) {
|
|
583
|
+
throw new Error(`Foundation Template File output ${file.to} is declared by both ${outputOwner} and ${file.identity}`);
|
|
584
|
+
}
|
|
585
|
+
byIdentity.set(file.identity, file);
|
|
586
|
+
outputOwners.set(file.to, file.identity);
|
|
587
|
+
}
|
|
588
|
+
return [...byIdentity.values()]
|
|
589
|
+
.toSorted((left, right) => left.identity.localeCompare(right.identity))
|
|
590
|
+
.map((file) => ({
|
|
591
|
+
kind: "writeTextTemplate",
|
|
592
|
+
source: file.source,
|
|
593
|
+
from: file.from,
|
|
594
|
+
to: file.to,
|
|
595
|
+
replacements: { ...file.replacements },
|
|
596
|
+
}));
|
|
597
|
+
}
|
|
598
|
+
function assertCompatibleRustToolchainFacts(contributions) {
|
|
599
|
+
const facts = contributions.flatMap((contribution) => {
|
|
600
|
+
const rust = contribution.foundation.toolchains.rust;
|
|
601
|
+
return rust === undefined
|
|
602
|
+
? []
|
|
603
|
+
: [
|
|
604
|
+
JSON.stringify({
|
|
605
|
+
toolchain: rust.toolchain,
|
|
606
|
+
components: [...new Set(rust.components)].toSorted(),
|
|
607
|
+
}),
|
|
608
|
+
];
|
|
609
|
+
});
|
|
610
|
+
if (new Set(facts).size > 1) {
|
|
611
|
+
throw new Error("Foundation requires compatible Rust toolchain facts");
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
function composeDependencyMaintenancePolicy(contributions) {
|
|
615
|
+
const ecosystems = [
|
|
616
|
+
...new Set(contributions.flatMap((contribution) => contribution.foundation.dependencyMaintenance.ecosystems)),
|
|
506
617
|
];
|
|
618
|
+
const directories = {};
|
|
619
|
+
const extraDirectories = {};
|
|
620
|
+
for (const ecosystem of ecosystems) {
|
|
621
|
+
const candidates = [
|
|
622
|
+
...new Set(contributions.flatMap((contribution) => {
|
|
623
|
+
const policy = contribution.foundation.dependencyMaintenance;
|
|
624
|
+
const primary = policy.directories?.[ecosystem];
|
|
625
|
+
return [
|
|
626
|
+
...(primary === undefined ? [] : [primary]),
|
|
627
|
+
...(policy.extraDirectories?.[ecosystem] ?? []),
|
|
628
|
+
];
|
|
629
|
+
})),
|
|
630
|
+
];
|
|
631
|
+
const [primary, ...extra] = candidates;
|
|
632
|
+
if (primary !== undefined)
|
|
633
|
+
directories[ecosystem] = primary;
|
|
634
|
+
if (extra.length > 0)
|
|
635
|
+
extraDirectories[ecosystem] = extra;
|
|
636
|
+
}
|
|
637
|
+
return {
|
|
638
|
+
ecosystems,
|
|
639
|
+
...(Object.keys(directories).length === 0 ? {} : { directories }),
|
|
640
|
+
...(Object.keys(extraDirectories).length === 0 ? {} : { extraDirectories }),
|
|
641
|
+
interval: "weekly",
|
|
642
|
+
};
|
|
507
643
|
}
|
|
508
644
|
function foundationPlan(options) {
|
|
509
645
|
assertProjectBlueprintV2(options.blueprint);
|
|
646
|
+
const configDefinition = typescriptConfigPackageDefinition(options.context);
|
|
647
|
+
const persistedConfigDefinition = options.blueprint.packages.find((definition) => definition.name === configDefinition.name ||
|
|
648
|
+
definition.path === configDefinition.path);
|
|
649
|
+
if (persistedConfigDefinition === undefined ||
|
|
650
|
+
!packageDefinitionsEqual(persistedConfigDefinition, configDefinition)) {
|
|
651
|
+
throw new Error("Project Blueprint must contain the Foundation TypeScript configuration Package Definition");
|
|
652
|
+
}
|
|
653
|
+
const initialProjectLinkPlan = planExplicitProjectLinks({
|
|
654
|
+
blueprint: options.blueprint,
|
|
655
|
+
contributions: options.contributions,
|
|
656
|
+
...(options.manifestTruthByPackagePath === undefined
|
|
657
|
+
? {}
|
|
658
|
+
: {
|
|
659
|
+
manifestTruthByPackagePath: options.manifestTruthByPackagePath,
|
|
660
|
+
}),
|
|
661
|
+
});
|
|
662
|
+
const injectedProviderNames = new Set([...initialProjectLinkPlan.manifestPatchesByPackagePath.values()].flatMap((patch) => Object.entries(patch.dependenciesMeta)
|
|
663
|
+
.filter(([, metadata]) => metadata.injected)
|
|
664
|
+
.map(([name]) => name)));
|
|
665
|
+
const configPackageName = typescriptConfigPackageName(options.context);
|
|
666
|
+
const packageContributions = options.contributions.map((contribution) => {
|
|
667
|
+
const ownsTypeScriptConfig = contribution.operations.some((operation) => "to" in operation &&
|
|
668
|
+
operation.to === `${contribution.definition.path}/tsconfig.json`);
|
|
669
|
+
if (!ownsTypeScriptConfig)
|
|
670
|
+
return contribution;
|
|
671
|
+
const dependencyField = injectedProviderNames.has(contribution.definition.name)
|
|
672
|
+
? "dependencies"
|
|
673
|
+
: "devDependencies";
|
|
674
|
+
const existingDependencies = contribution.manifest[dependencyField];
|
|
675
|
+
return {
|
|
676
|
+
...contribution,
|
|
677
|
+
manifest: {
|
|
678
|
+
...contribution.manifest,
|
|
679
|
+
[dependencyField]: {
|
|
680
|
+
...(isRecord(existingDependencies) ? existingDependencies : {}),
|
|
681
|
+
[configPackageName]: "workspace:*",
|
|
682
|
+
},
|
|
683
|
+
},
|
|
684
|
+
};
|
|
685
|
+
});
|
|
686
|
+
const configContribution = typescriptConfigContribution(options.context);
|
|
687
|
+
const contributions = [configContribution, ...packageContributions];
|
|
510
688
|
const generationRecord = options.generationRecord ?? {
|
|
511
689
|
schemaVersion: 1,
|
|
512
690
|
preset: options.definition.metadata.name,
|
|
513
691
|
templateVersion: "0.0.0",
|
|
514
692
|
toolchain: options.context.toolchain,
|
|
515
|
-
packages:
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
693
|
+
packages: [
|
|
694
|
+
...options.contributions.map((contribution) => ({
|
|
695
|
+
path: contribution.definition.path,
|
|
696
|
+
definitionName: options.definition.metadata.name,
|
|
697
|
+
planningContribution: options.mode === "initialization"
|
|
698
|
+
? "planInitialization"
|
|
699
|
+
: "planPackageAddition",
|
|
700
|
+
})),
|
|
701
|
+
{
|
|
702
|
+
path: configDefinition.path,
|
|
703
|
+
definitionName: "foundation",
|
|
704
|
+
planningContribution: "foundationPlan",
|
|
705
|
+
},
|
|
706
|
+
],
|
|
522
707
|
};
|
|
523
|
-
const environmentNeeds = uniqueEnvironmentNeeds(
|
|
708
|
+
const environmentNeeds = uniqueEnvironmentNeeds(contributions.flatMap((item) => item.environmentNeeds));
|
|
524
709
|
const deploymentEnvironmentNeeds = uniqueEnvironmentNeeds([
|
|
525
710
|
...(options.existingDeploymentEnvironmentNeeds ?? []),
|
|
526
|
-
...
|
|
711
|
+
...contributions.flatMap((item) => item.deploymentEnvironmentNeeds ?? []),
|
|
527
712
|
]);
|
|
528
713
|
const persistedEnvironmentNeeds = {
|
|
529
714
|
schemaVersion: 1,
|
|
530
715
|
check: environmentNeeds.map(checkEnvironmentNeedFact),
|
|
531
716
|
deployment: deploymentEnvironmentNeeds.map(deploymentEnvironmentNeedFact),
|
|
532
717
|
};
|
|
533
|
-
const hasDeploymentTask =
|
|
718
|
+
const hasDeploymentTask = contributions.some((contribution) => {
|
|
534
719
|
const scripts = contribution.manifest.scripts;
|
|
535
720
|
return (typeof scripts === "object" &&
|
|
536
721
|
scripts !== null &&
|
|
537
722
|
typeof scripts.deployment === "string");
|
|
538
723
|
});
|
|
539
|
-
const
|
|
540
|
-
if (new Set(
|
|
724
|
+
const contributionPackagePaths = contributions.map((contribution) => contribution.definition.path);
|
|
725
|
+
if (new Set(contributionPackagePaths).size !== contributionPackagePaths.length)
|
|
541
726
|
throw new Error("Package Contributions must have unique Package Paths");
|
|
542
|
-
const
|
|
727
|
+
const blueprintPackagePaths = options.blueprint.packages.map((definition) => definition.path);
|
|
728
|
+
if (contributionPackagePaths.length !== blueprintPackagePaths.length ||
|
|
729
|
+
contributionPackagePaths.some((packagePath) => !blueprintPackagePaths.includes(packagePath))) {
|
|
730
|
+
throw new Error("Package Contributions must exactly match Project Blueprint Package Paths");
|
|
731
|
+
}
|
|
732
|
+
const packageNames = contributions.map((contribution) => contribution.definition.name);
|
|
543
733
|
if (new Set(packageNames).size !== packageNames.length)
|
|
544
734
|
throw new Error("Package Contributions must have unique package names");
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
735
|
+
const ciDiagnosticArtifactDeclarations = contributions.flatMap((contribution) => contribution.ciDiagnosticArtifacts ?? []);
|
|
736
|
+
const ciDiagnosticArtifacts = composeCiDiagnosticArtifacts({
|
|
737
|
+
packagePaths: blueprintPackagePaths,
|
|
738
|
+
declarations: ciDiagnosticArtifactDeclarations,
|
|
739
|
+
});
|
|
740
|
+
assertCompatibleRustToolchainFacts(contributions);
|
|
741
|
+
const contributedToolLayers = contributions.flatMap((contribution) => contribution.foundation.developmentContainerToolLayers ?? []);
|
|
742
|
+
const developmentContainer = contributedDevcontainerComposition({
|
|
743
|
+
context: options.context,
|
|
744
|
+
layers: contributedToolLayers,
|
|
745
|
+
});
|
|
552
746
|
const workspacePackageGlobs = [
|
|
553
747
|
"apps/*",
|
|
554
748
|
"packages/*",
|
|
@@ -556,20 +750,13 @@ function foundationPlan(options) {
|
|
|
556
750
|
...options.blueprint.packages
|
|
557
751
|
.map((definition) => `${definition.path.split("/")[0]}/*`)
|
|
558
752
|
.filter((glob) => glob !== "apps/*" && glob !== "packages/*"),
|
|
559
|
-
...
|
|
753
|
+
...contributions
|
|
560
754
|
.flatMap((contribution) => contribution.foundation.workspacePackageGlobs ?? [])
|
|
561
755
|
.filter((glob) => glob !== "apps/*" && glob !== "packages/*"),
|
|
562
756
|
]),
|
|
563
757
|
];
|
|
564
|
-
const editorCustomization = editorCustomizationForCapabilities(
|
|
565
|
-
const dependencyMaintenancePolicy =
|
|
566
|
-
ecosystems: [
|
|
567
|
-
...new Set(options.contributions.flatMap((contribution) => contribution.foundation.dependencyMaintenance.ecosystems)),
|
|
568
|
-
],
|
|
569
|
-
directories: Object.assign({}, ...options.contributions.map((contribution) => contribution.foundation.dependencyMaintenance.directories ?? {})),
|
|
570
|
-
extraDirectories: Object.assign({}, ...options.contributions.map((contribution) => contribution.foundation.dependencyMaintenance.extraDirectories ?? {})),
|
|
571
|
-
interval: "weekly",
|
|
572
|
-
};
|
|
758
|
+
const editorCustomization = editorCustomizationForCapabilities(contributions.flatMap((contribution) => contribution.foundation.editorCapabilities), loadEditorCustomizationDeclarations(resolveTemplateSource(templateSources.editorCustomization, "capabilities.json")));
|
|
759
|
+
const dependencyMaintenancePolicy = composeDependencyMaintenancePolicy(contributions);
|
|
573
760
|
const rootManifest = {
|
|
574
761
|
name: options.context.projectName,
|
|
575
762
|
version: "0.0.0",
|
|
@@ -600,7 +787,7 @@ function foundationPlan(options) {
|
|
|
600
787
|
packageManager: options.context.toolchain.packageManagerPin,
|
|
601
788
|
};
|
|
602
789
|
const dependencyCatalog = selectTemplateDependencyCatalogEntries(collectGeneratedManifestCatalogReferences([
|
|
603
|
-
...
|
|
790
|
+
...contributions.map((contribution) => contribution.manifest),
|
|
604
791
|
rootManifest,
|
|
605
792
|
]));
|
|
606
793
|
const dependencyOverrides = {
|
|
@@ -634,17 +821,29 @@ function foundationPlan(options) {
|
|
|
634
821
|
].join("\n"),
|
|
635
822
|
},
|
|
636
823
|
};
|
|
637
|
-
const
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
824
|
+
const workflowTemplateSource = projectCheckWorkflowTemplateSource({
|
|
825
|
+
packagePaths: blueprintPackagePaths,
|
|
826
|
+
deploymentEnvironmentNeeds,
|
|
827
|
+
hasDeploymentTask,
|
|
828
|
+
diagnosticArtifacts: ciDiagnosticArtifacts,
|
|
829
|
+
});
|
|
830
|
+
const workflowOperation = ciDiagnosticArtifacts.length === 0
|
|
831
|
+
? {
|
|
832
|
+
kind: "copyFile",
|
|
833
|
+
source: templateSources.foundation,
|
|
834
|
+
from: workflowTemplateSource,
|
|
835
|
+
to: ".github/workflows/check.yml",
|
|
836
|
+
}
|
|
837
|
+
: {
|
|
838
|
+
kind: "writeTextTemplate",
|
|
839
|
+
source: templateSources.foundation,
|
|
840
|
+
from: workflowTemplateSource,
|
|
841
|
+
to: ".github/workflows/check.yml",
|
|
842
|
+
replacements: projectCheckWorkflowTemplateReplacements({
|
|
843
|
+
packagePaths: blueprintPackagePaths,
|
|
844
|
+
diagnosticArtifacts: ciDiagnosticArtifacts,
|
|
845
|
+
}),
|
|
846
|
+
};
|
|
648
847
|
const workflowOperations = [
|
|
649
848
|
workflowOperation,
|
|
650
849
|
{
|
|
@@ -655,16 +854,8 @@ function foundationPlan(options) {
|
|
|
655
854
|
replacements: projectDependabotTemplateReplacements(dependencyMaintenancePolicy),
|
|
656
855
|
},
|
|
657
856
|
];
|
|
658
|
-
const projectLinkPlan =
|
|
659
|
-
|
|
660
|
-
contributions: options.contributions,
|
|
661
|
-
...(options.manifestTruthByPackagePath === undefined
|
|
662
|
-
? {}
|
|
663
|
-
: {
|
|
664
|
-
manifestTruthByPackagePath: options.manifestTruthByPackagePath,
|
|
665
|
-
}),
|
|
666
|
-
});
|
|
667
|
-
const turboBoundaryTags = turboBoundaryTagsForContributions(options.contributions);
|
|
857
|
+
const projectLinkPlan = initialProjectLinkPlan;
|
|
858
|
+
const turboBoundaryTags = turboBoundaryTagsForContributions(contributions);
|
|
668
859
|
const initializationFoundationOperations = [
|
|
669
860
|
{
|
|
670
861
|
kind: "writeJson",
|
|
@@ -741,64 +932,8 @@ function foundationPlan(options) {
|
|
|
741
932
|
to: ".vscode/settings.json",
|
|
742
933
|
value: editorCustomization.settings,
|
|
743
934
|
},
|
|
744
|
-
...
|
|
745
|
-
|
|
746
|
-
...devcontainerDockerfileOperations({
|
|
747
|
-
context: options.context,
|
|
748
|
-
environmentNeeds,
|
|
749
|
-
}),
|
|
750
|
-
]
|
|
751
|
-
: [
|
|
752
|
-
{
|
|
753
|
-
kind: "writeTextTemplate",
|
|
754
|
-
source: templateSources.foundation,
|
|
755
|
-
from: "rust/rust-toolchain.toml",
|
|
756
|
-
to: "rust-toolchain.toml",
|
|
757
|
-
replacements: { RUST_TOOLCHAIN: rustToolchain.toolchain },
|
|
758
|
-
},
|
|
759
|
-
{
|
|
760
|
-
kind: "writeTextTemplate",
|
|
761
|
-
source: templateSources.foundation,
|
|
762
|
-
from: "rust/devcontainer/devcontainer.json",
|
|
763
|
-
to: ".devcontainer/devcontainer.json",
|
|
764
|
-
replacements: {
|
|
765
|
-
PROJECT_NAME: options.context.projectName,
|
|
766
|
-
NODE_LTS_MAJOR: options.context.toolchain.nodeLtsMajor,
|
|
767
|
-
PACKAGE_MANAGER_PIN: options.context.toolchain.packageManagerPin,
|
|
768
|
-
RUST_TOOLCHAIN: rustToolchain.toolchain,
|
|
769
|
-
},
|
|
770
|
-
},
|
|
771
|
-
{
|
|
772
|
-
kind: "writeTextFromFragments",
|
|
773
|
-
to: ".devcontainer/Dockerfile",
|
|
774
|
-
fragments: [
|
|
775
|
-
{
|
|
776
|
-
source: templateSources.sharedDevcontainer,
|
|
777
|
-
from: "node-pnpm.Dockerfile",
|
|
778
|
-
},
|
|
779
|
-
{
|
|
780
|
-
source: templateSources.foundation,
|
|
781
|
-
from: "rust/devcontainer/rust.Dockerfile",
|
|
782
|
-
},
|
|
783
|
-
...(environmentNeeds.some((need) => need.kind === "playwright-browser-assets")
|
|
784
|
-
? [
|
|
785
|
-
{
|
|
786
|
-
source: templateSources.sharedDevcontainer,
|
|
787
|
-
from: "browser-test.Dockerfile",
|
|
788
|
-
},
|
|
789
|
-
]
|
|
790
|
-
: []),
|
|
791
|
-
...(environmentNeeds.some((need) => need.kind === "shellcheck-command")
|
|
792
|
-
? [
|
|
793
|
-
{
|
|
794
|
-
source: templateSources.sharedDevcontainer,
|
|
795
|
-
from: "shellcheck.Dockerfile",
|
|
796
|
-
},
|
|
797
|
-
]
|
|
798
|
-
: []),
|
|
799
|
-
],
|
|
800
|
-
},
|
|
801
|
-
]),
|
|
935
|
+
...developmentContainer.operations,
|
|
936
|
+
...contributedFoundationTemplateFileOperations(contributions),
|
|
802
937
|
...workflowOperations,
|
|
803
938
|
{
|
|
804
939
|
kind: "writeJson",
|
|
@@ -843,7 +978,15 @@ function foundationPlan(options) {
|
|
|
843
978
|
};
|
|
844
979
|
const withProvenance = (operation, provenance) => ({ ...operation, provenance });
|
|
845
980
|
const operations = [
|
|
846
|
-
...(options.renderContributions
|
|
981
|
+
...(options.renderContributions === undefined
|
|
982
|
+
? contributions
|
|
983
|
+
: options.renderContributions.map((renderContribution) => {
|
|
984
|
+
const enriched = packageContributions.find((candidate) => candidate.definition.path === renderContribution.definition.path);
|
|
985
|
+
if (enriched === undefined) {
|
|
986
|
+
throw new Error(`Foundation cannot render unknown Package Contribution ${renderContribution.definition.path}`);
|
|
987
|
+
}
|
|
988
|
+
return enriched;
|
|
989
|
+
}))
|
|
847
990
|
.map((item) => assertPackageContribution(item, {
|
|
848
991
|
definitionName: options.definition.metadata.name,
|
|
849
992
|
planner: options.mode === "addition"
|
|
@@ -865,7 +1008,11 @@ function foundationPlan(options) {
|
|
|
865
1008
|
];
|
|
866
1009
|
const reconciliation = [
|
|
867
1010
|
{ path: "turbo.json", driver: "structured" },
|
|
868
|
-
{
|
|
1011
|
+
{
|
|
1012
|
+
path: ".devcontainer/devcontainer.json",
|
|
1013
|
+
driver: "structured",
|
|
1014
|
+
identitySets: [developmentContainer.mountIdentitySet],
|
|
1015
|
+
},
|
|
869
1016
|
{
|
|
870
1017
|
path: ".vscode/extensions.json",
|
|
871
1018
|
driver: "structured",
|
|
@@ -890,24 +1037,36 @@ function foundationPlan(options) {
|
|
|
890
1037
|
generationRecord,
|
|
891
1038
|
operations,
|
|
892
1039
|
reconciliation,
|
|
1040
|
+
developmentContainer: {
|
|
1041
|
+
toolLayers: developmentContainer.toolLayers,
|
|
1042
|
+
buildArguments: developmentContainer.buildArguments,
|
|
1043
|
+
mounts: developmentContainer.mounts,
|
|
1044
|
+
probes: developmentContainer.probes,
|
|
1045
|
+
},
|
|
893
1046
|
environmentNeeds,
|
|
894
1047
|
deploymentEnvironmentNeeds,
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
rootManifest,
|
|
898
|
-
],
|
|
1048
|
+
ciDiagnosticArtifacts,
|
|
1049
|
+
manifests: [...contributions.map((item) => item.manifest), rootManifest],
|
|
899
1050
|
dependencyCatalog,
|
|
900
1051
|
dependencyMaintenancePolicy,
|
|
901
1052
|
nextStepInstructions: [
|
|
902
1053
|
{ display: "pnpm install" },
|
|
903
|
-
...environmentNeeds.map((need) => ({ display: need.nextStep.display })),
|
|
904
1054
|
{ display: "pnpm run fix" },
|
|
905
1055
|
{ display: "pnpm run check" },
|
|
906
1056
|
],
|
|
907
1057
|
};
|
|
908
1058
|
}
|
|
909
1059
|
export function planGeneratedRepositoryInitialization(options) {
|
|
910
|
-
const
|
|
1060
|
+
const presetBlueprint = options.definition.blueprint(options.context);
|
|
1061
|
+
const configDefinition = typescriptConfigPackageDefinition(options.context);
|
|
1062
|
+
if (presetBlueprint.packages.some((definition) => definition.name === configDefinition.name ||
|
|
1063
|
+
definition.path === configDefinition.path)) {
|
|
1064
|
+
throw new Error("Preset Blueprint must not redefine the Foundation TypeScript configuration Package Definition");
|
|
1065
|
+
}
|
|
1066
|
+
const blueprint = {
|
|
1067
|
+
...presetBlueprint,
|
|
1068
|
+
packages: [...presetBlueprint.packages, configDefinition],
|
|
1069
|
+
};
|
|
911
1070
|
const contributions = options.definition.planInitializationContributions?.(options.context) ?? [options.definition.planInitialization(options.context)];
|
|
912
1071
|
return foundationPlan({
|
|
913
1072
|
definition: options.definition,
|