@ykdz/template 0.0.31 → 0.0.32
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 +8 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.js +283 -149
- 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 +81 -11
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.js +3 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.js +21 -10
- package/node_modules/@ykdz/template-builtin-presets/package.json +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check.dynamic.template +2 -2
- 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/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-hono-app/api/tsconfig.json +2 -12
- 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 +12 -1
- package/node_modules/@ykdz/template-core/dist/pnpm-workspace.yaml +1 -0
- package/node_modules/@ykdz/template-core/dist/project-github.d.ts +1 -9
- package/node_modules/@ykdz/template-core/dist/project-github.js +0 -49
- 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.js +21 -0
- package/package.json +3 -3
- 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,4 @@
|
|
|
1
|
+
import { type DevelopmentContainerToolLayerBuildArgument, type DevelopmentContainerToolLayerMount, type DevelopmentContainerToolLayerProbe, type PlannedDevelopmentContainerToolLayer } from "#template-core/development-container-tool-layer";
|
|
1
2
|
import type { CheckEnvironmentNeed, DeploymentEnvironmentNeed } from "#template-core/module-graph";
|
|
2
3
|
import { type PackageContribution } from "#template-core/package-contribution";
|
|
3
4
|
import type { BuiltInPresetDefinition, GenerationContext } from "#template-core/preset-definition";
|
|
@@ -16,7 +17,7 @@ export type NextStepInstruction = {
|
|
|
16
17
|
type GeneratedPackagePlanningRecord = {
|
|
17
18
|
readonly path: string;
|
|
18
19
|
readonly definitionName: string;
|
|
19
|
-
readonly planningContribution: "planInitialization" | "planPackageAddition";
|
|
20
|
+
readonly planningContribution: "foundationPlan" | "planInitialization" | "planPackageAddition";
|
|
20
21
|
};
|
|
21
22
|
type GenerationRecord = {
|
|
22
23
|
readonly schemaVersion: 1;
|
|
@@ -33,6 +34,12 @@ export type GeneratedRepositoryPlan = {
|
|
|
33
34
|
readonly generationRecord: GenerationRecord;
|
|
34
35
|
readonly operations: readonly RenderOperation[];
|
|
35
36
|
readonly reconciliation: readonly ProjectProjectionReconciliation[];
|
|
37
|
+
readonly developmentContainer: {
|
|
38
|
+
readonly toolLayers: readonly PlannedDevelopmentContainerToolLayer[];
|
|
39
|
+
readonly buildArguments: readonly DevelopmentContainerToolLayerBuildArgument[];
|
|
40
|
+
readonly mounts: readonly DevelopmentContainerToolLayerMount[];
|
|
41
|
+
readonly probes: readonly DevelopmentContainerToolLayerProbe[];
|
|
42
|
+
};
|
|
36
43
|
readonly environmentNeeds: readonly CheckEnvironmentNeed[];
|
|
37
44
|
readonly deploymentEnvironmentNeeds: readonly DeploymentEnvironmentNeed[];
|
|
38
45
|
/** Structured manifests used to derive the generated Dependency Catalog. */
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { collectGeneratedManifestCatalogReferences, selectTemplateDependencyCatalogEntries, } from "#template-core/dependency-catalog";
|
|
5
|
-
import {
|
|
5
|
+
import { planDevelopmentContainerToolLayersSync, } from "#template-core/development-container-tool-layer";
|
|
6
6
|
import { editorCustomizationForCapabilities, loadEditorCustomizationDeclarations, } from "#template-core/editor-customization";
|
|
7
7
|
import { checkEnvironmentNeedFact, checkEnvironmentNeedFromFact, deploymentEnvironmentNeedFact, deploymentEnvironmentNeedFromFact, renderDeploymentCheckCommand, renderFixCommand, renderRootCheckCommand, } from "#template-core/module-graph";
|
|
8
8
|
import { assertPackageContribution, } from "#template-core/package-contribution";
|
|
@@ -11,6 +11,7 @@ import { projectCheckWorkflowTemplateReplacements, projectDependabotTemplateRepl
|
|
|
11
11
|
import { planExplicitProjectLinks } from "#template-core/project-linking-v2";
|
|
12
12
|
import { resolveTemplateSource, } from "#template-core/renderer";
|
|
13
13
|
import { rustBinDefinition } from "./rust-bin/definition.js";
|
|
14
|
+
import { typescriptConfigContribution, typescriptConfigPackageDefinition, typescriptConfigPackageName, } from "./shared/typescript.js";
|
|
14
15
|
import { vuePnpmDependencyOverrides } from "./shared/vue.js";
|
|
15
16
|
import { templateSources } from "./template-sources.js";
|
|
16
17
|
import { tsCliDefinition } from "./ts-cli/definition.js";
|
|
@@ -188,7 +189,7 @@ function readGenerationRecord(options) {
|
|
|
188
189
|
if (!isRecord(item) ||
|
|
189
190
|
typeof item.path !== "string" ||
|
|
190
191
|
typeof item.definitionName !== "string" ||
|
|
191
|
-
!["planInitialization", "planPackageAddition"].includes(String(item.planningContribution))) {
|
|
192
|
+
!["foundationPlan", "planInitialization", "planPackageAddition"].includes(String(item.planningContribution))) {
|
|
192
193
|
throw new Error(`Package Addition requires valid package planning facts at .template/generation.json packages[${index}]`);
|
|
193
194
|
}
|
|
194
195
|
packages.push(item);
|
|
@@ -219,6 +220,13 @@ function readGenerationRecord(options) {
|
|
|
219
220
|
return generationRecord;
|
|
220
221
|
}
|
|
221
222
|
function assertGenerationRecordFoundationConsistency(options) {
|
|
223
|
+
const configDefinition = typescriptConfigPackageDefinition(options.context);
|
|
224
|
+
const foundationRecords = options.generationRecord.packages.filter((record) => record.planningContribution === "foundationPlan");
|
|
225
|
+
if (foundationRecords.length !== 1 ||
|
|
226
|
+
foundationRecords[0].definitionName !== "foundation" ||
|
|
227
|
+
foundationRecords[0].path !== configDefinition.path) {
|
|
228
|
+
throw new Error("Package Addition Generation Record must contain exactly one Foundation TypeScript configuration provenance record");
|
|
229
|
+
}
|
|
222
230
|
const initialRecords = options.generationRecord.packages.filter((record) => record.planningContribution === "planInitialization");
|
|
223
231
|
if (initialRecords.length === 0) {
|
|
224
232
|
throw new Error(`Package Addition Generation Record preset ${options.generationRecord.preset} has no initial Package provenance`);
|
|
@@ -370,7 +378,10 @@ function readExistingPackageAdditionState(options) {
|
|
|
370
378
|
const generationRecord = readGenerationRecord(options);
|
|
371
379
|
const persistedEnvironmentNeeds = readPersistedEnvironmentNeeds(options.context.targetDir);
|
|
372
380
|
const manifestTruthByPackagePath = new Map();
|
|
373
|
-
const contributions = generationRecord.packages.
|
|
381
|
+
const contributions = generationRecord.packages.flatMap((record) => {
|
|
382
|
+
if (record.planningContribution === "foundationPlan") {
|
|
383
|
+
return [];
|
|
384
|
+
}
|
|
374
385
|
const expectedDefinition = options.blueprint.packages.find((definition) => definition.path === record.path);
|
|
375
386
|
const definition = builtInPresetRegistry.require(record.definitionName);
|
|
376
387
|
let candidates;
|
|
@@ -397,7 +408,7 @@ function readExistingPackageAdditionState(options) {
|
|
|
397
408
|
if (contribution === undefined) {
|
|
398
409
|
throw new Error(`Generation Record cannot reproduce Package Definition ${expectedDefinition.name} at ${expectedDefinition.path}`);
|
|
399
410
|
}
|
|
400
|
-
return contribution;
|
|
411
|
+
return [contribution];
|
|
401
412
|
});
|
|
402
413
|
for (const packagePath of new Set(options.manifestTruthPackagePaths ?? [])) {
|
|
403
414
|
const expectedDefinition = options.blueprint.packages.find((definition) => definition.path === packagePath);
|
|
@@ -460,95 +471,267 @@ function turboBoundaryTagsForContributions(contributions) {
|
|
|
460
471
|
}
|
|
461
472
|
return [...selectedTags];
|
|
462
473
|
}
|
|
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",
|
|
474
|
+
function contributedDevcontainerComposition(options) {
|
|
475
|
+
const layerPlan = planDevelopmentContainerToolLayersSync({
|
|
476
|
+
baseLayer: {
|
|
477
|
+
identity: "node-pnpm",
|
|
478
|
+
dockerfile: {
|
|
479
|
+
source: templateSources.sharedDevcontainer,
|
|
480
|
+
from: "node-pnpm.Dockerfile",
|
|
478
481
|
},
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
482
|
+
buildArguments: [
|
|
483
|
+
{
|
|
484
|
+
name: "NODE_VERSION",
|
|
485
|
+
value: options.context.toolchain.nodeLtsMajor,
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
name: "PACKAGE_MANAGER_PIN",
|
|
489
|
+
value: options.context.toolchain.packageManagerPin,
|
|
490
|
+
},
|
|
491
|
+
],
|
|
492
|
+
mounts: [
|
|
484
493
|
{
|
|
485
|
-
|
|
486
|
-
|
|
494
|
+
identity: "pnpm-store",
|
|
495
|
+
type: "volume",
|
|
496
|
+
source: "${devcontainerId}-pnpm-store",
|
|
497
|
+
target: "/pnpm/store",
|
|
487
498
|
},
|
|
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
499
|
],
|
|
505
500
|
},
|
|
501
|
+
layers: options.layers,
|
|
502
|
+
});
|
|
503
|
+
return {
|
|
504
|
+
operations: [
|
|
505
|
+
{
|
|
506
|
+
kind: "writeTextTemplate",
|
|
507
|
+
source: templateSources.foundation,
|
|
508
|
+
from: "devcontainer.json",
|
|
509
|
+
to: ".devcontainer/devcontainer.json",
|
|
510
|
+
replacements: {
|
|
511
|
+
PROJECT_NAME: options.context.projectName,
|
|
512
|
+
NODE_LTS_MAJOR: options.context.toolchain.nodeLtsMajor,
|
|
513
|
+
PACKAGE_MANAGER_PIN: options.context.toolchain.packageManagerPin,
|
|
514
|
+
},
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
kind: "mergeJson",
|
|
518
|
+
to: ".devcontainer/devcontainer.json",
|
|
519
|
+
value: {
|
|
520
|
+
build: {
|
|
521
|
+
args: Object.fromEntries(layerPlan.buildArguments.map((argument) => [
|
|
522
|
+
argument.name,
|
|
523
|
+
argument.value,
|
|
524
|
+
])),
|
|
525
|
+
},
|
|
526
|
+
...(layerPlan.mounts.length === 0
|
|
527
|
+
? {}
|
|
528
|
+
: {
|
|
529
|
+
mounts: layerPlan.mounts.map(({ identity: _identity, ...mount }) => mount),
|
|
530
|
+
}),
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
kind: "writeTextFromFragments",
|
|
535
|
+
to: ".devcontainer/Dockerfile",
|
|
536
|
+
validation: "development-container-dockerfile",
|
|
537
|
+
fragments: layerPlan.layers.map((layer) => layer.dockerfile),
|
|
538
|
+
},
|
|
539
|
+
],
|
|
540
|
+
mountIdentitySet: {
|
|
541
|
+
location: "/mounts",
|
|
542
|
+
identity: {
|
|
543
|
+
kind: "projection",
|
|
544
|
+
members: layerPlan.mounts.map(({ identity, target, }) => ({
|
|
545
|
+
identity,
|
|
546
|
+
match: { target },
|
|
547
|
+
})),
|
|
548
|
+
fallback: { fields: ["target"] },
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
toolLayers: layerPlan.layers,
|
|
552
|
+
buildArguments: layerPlan.buildArguments,
|
|
553
|
+
mounts: layerPlan.mounts,
|
|
554
|
+
probes: layerPlan.probes,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
function contributedFoundationTemplateFileOperations(contributions) {
|
|
558
|
+
const byIdentity = new Map();
|
|
559
|
+
const outputOwners = new Map();
|
|
560
|
+
for (const file of contributions.flatMap((contribution) => contribution.foundation.templateFiles ?? [])) {
|
|
561
|
+
const previous = byIdentity.get(file.identity);
|
|
562
|
+
if (previous !== undefined) {
|
|
563
|
+
const previousFingerprint = JSON.stringify({
|
|
564
|
+
source: resolveTemplateSource(previous.source, previous.from),
|
|
565
|
+
from: previous.from,
|
|
566
|
+
to: previous.to,
|
|
567
|
+
replacements: previous.replacements ?? {},
|
|
568
|
+
});
|
|
569
|
+
const fingerprint = JSON.stringify({
|
|
570
|
+
source: resolveTemplateSource(file.source, file.from),
|
|
571
|
+
from: file.from,
|
|
572
|
+
to: file.to,
|
|
573
|
+
replacements: file.replacements ?? {},
|
|
574
|
+
});
|
|
575
|
+
if (fingerprint !== previousFingerprint) {
|
|
576
|
+
throw new Error(`Foundation Template File identity ${file.identity} has conflicting descriptors`);
|
|
577
|
+
}
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
const outputOwner = outputOwners.get(file.to);
|
|
581
|
+
if (outputOwner !== undefined) {
|
|
582
|
+
throw new Error(`Foundation Template File output ${file.to} is declared by both ${outputOwner} and ${file.identity}`);
|
|
583
|
+
}
|
|
584
|
+
byIdentity.set(file.identity, file);
|
|
585
|
+
outputOwners.set(file.to, file.identity);
|
|
586
|
+
}
|
|
587
|
+
return [...byIdentity.values()]
|
|
588
|
+
.toSorted((left, right) => left.identity.localeCompare(right.identity))
|
|
589
|
+
.map((file) => ({
|
|
590
|
+
kind: "writeTextTemplate",
|
|
591
|
+
source: file.source,
|
|
592
|
+
from: file.from,
|
|
593
|
+
to: file.to,
|
|
594
|
+
replacements: { ...file.replacements },
|
|
595
|
+
}));
|
|
596
|
+
}
|
|
597
|
+
function assertCompatibleRustToolchainFacts(contributions) {
|
|
598
|
+
const facts = contributions.flatMap((contribution) => {
|
|
599
|
+
const rust = contribution.foundation.toolchains.rust;
|
|
600
|
+
return rust === undefined
|
|
601
|
+
? []
|
|
602
|
+
: [
|
|
603
|
+
JSON.stringify({
|
|
604
|
+
toolchain: rust.toolchain,
|
|
605
|
+
components: [...new Set(rust.components)].toSorted(),
|
|
606
|
+
}),
|
|
607
|
+
];
|
|
608
|
+
});
|
|
609
|
+
if (new Set(facts).size > 1) {
|
|
610
|
+
throw new Error("Foundation requires compatible Rust toolchain facts");
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
function composeDependencyMaintenancePolicy(contributions) {
|
|
614
|
+
const ecosystems = [
|
|
615
|
+
...new Set(contributions.flatMap((contribution) => contribution.foundation.dependencyMaintenance.ecosystems)),
|
|
506
616
|
];
|
|
617
|
+
const directories = {};
|
|
618
|
+
const extraDirectories = {};
|
|
619
|
+
for (const ecosystem of ecosystems) {
|
|
620
|
+
const candidates = [
|
|
621
|
+
...new Set(contributions.flatMap((contribution) => {
|
|
622
|
+
const policy = contribution.foundation.dependencyMaintenance;
|
|
623
|
+
const primary = policy.directories?.[ecosystem];
|
|
624
|
+
return [
|
|
625
|
+
...(primary === undefined ? [] : [primary]),
|
|
626
|
+
...(policy.extraDirectories?.[ecosystem] ?? []),
|
|
627
|
+
];
|
|
628
|
+
})),
|
|
629
|
+
];
|
|
630
|
+
const [primary, ...extra] = candidates;
|
|
631
|
+
if (primary !== undefined)
|
|
632
|
+
directories[ecosystem] = primary;
|
|
633
|
+
if (extra.length > 0)
|
|
634
|
+
extraDirectories[ecosystem] = extra;
|
|
635
|
+
}
|
|
636
|
+
return {
|
|
637
|
+
ecosystems,
|
|
638
|
+
...(Object.keys(directories).length === 0 ? {} : { directories }),
|
|
639
|
+
...(Object.keys(extraDirectories).length === 0 ? {} : { extraDirectories }),
|
|
640
|
+
interval: "weekly",
|
|
641
|
+
};
|
|
507
642
|
}
|
|
508
643
|
function foundationPlan(options) {
|
|
509
644
|
assertProjectBlueprintV2(options.blueprint);
|
|
645
|
+
const configDefinition = typescriptConfigPackageDefinition(options.context);
|
|
646
|
+
const persistedConfigDefinition = options.blueprint.packages.find((definition) => definition.name === configDefinition.name ||
|
|
647
|
+
definition.path === configDefinition.path);
|
|
648
|
+
if (persistedConfigDefinition === undefined ||
|
|
649
|
+
!packageDefinitionsEqual(persistedConfigDefinition, configDefinition)) {
|
|
650
|
+
throw new Error("Project Blueprint must contain the Foundation TypeScript configuration Package Definition");
|
|
651
|
+
}
|
|
652
|
+
const initialProjectLinkPlan = planExplicitProjectLinks({
|
|
653
|
+
blueprint: options.blueprint,
|
|
654
|
+
contributions: options.contributions,
|
|
655
|
+
...(options.manifestTruthByPackagePath === undefined
|
|
656
|
+
? {}
|
|
657
|
+
: {
|
|
658
|
+
manifestTruthByPackagePath: options.manifestTruthByPackagePath,
|
|
659
|
+
}),
|
|
660
|
+
});
|
|
661
|
+
const injectedProviderNames = new Set([...initialProjectLinkPlan.manifestPatchesByPackagePath.values()].flatMap((patch) => Object.entries(patch.dependenciesMeta)
|
|
662
|
+
.filter(([, metadata]) => metadata.injected)
|
|
663
|
+
.map(([name]) => name)));
|
|
664
|
+
const configPackageName = typescriptConfigPackageName(options.context);
|
|
665
|
+
const packageContributions = options.contributions.map((contribution) => {
|
|
666
|
+
const ownsTypeScriptConfig = contribution.operations.some((operation) => "to" in operation &&
|
|
667
|
+
operation.to === `${contribution.definition.path}/tsconfig.json`);
|
|
668
|
+
if (!ownsTypeScriptConfig)
|
|
669
|
+
return contribution;
|
|
670
|
+
const dependencyField = injectedProviderNames.has(contribution.definition.name)
|
|
671
|
+
? "dependencies"
|
|
672
|
+
: "devDependencies";
|
|
673
|
+
const existingDependencies = contribution.manifest[dependencyField];
|
|
674
|
+
return {
|
|
675
|
+
...contribution,
|
|
676
|
+
manifest: {
|
|
677
|
+
...contribution.manifest,
|
|
678
|
+
[dependencyField]: {
|
|
679
|
+
...(isRecord(existingDependencies) ? existingDependencies : {}),
|
|
680
|
+
[configPackageName]: "workspace:*",
|
|
681
|
+
},
|
|
682
|
+
},
|
|
683
|
+
};
|
|
684
|
+
});
|
|
685
|
+
const configContribution = typescriptConfigContribution(options.context);
|
|
686
|
+
const contributions = [configContribution, ...packageContributions];
|
|
510
687
|
const generationRecord = options.generationRecord ?? {
|
|
511
688
|
schemaVersion: 1,
|
|
512
689
|
preset: options.definition.metadata.name,
|
|
513
690
|
templateVersion: "0.0.0",
|
|
514
691
|
toolchain: options.context.toolchain,
|
|
515
|
-
packages:
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
692
|
+
packages: [
|
|
693
|
+
...options.contributions.map((contribution) => ({
|
|
694
|
+
path: contribution.definition.path,
|
|
695
|
+
definitionName: options.definition.metadata.name,
|
|
696
|
+
planningContribution: options.mode === "initialization"
|
|
697
|
+
? "planInitialization"
|
|
698
|
+
: "planPackageAddition",
|
|
699
|
+
})),
|
|
700
|
+
{
|
|
701
|
+
path: configDefinition.path,
|
|
702
|
+
definitionName: "foundation",
|
|
703
|
+
planningContribution: "foundationPlan",
|
|
704
|
+
},
|
|
705
|
+
],
|
|
522
706
|
};
|
|
523
|
-
const environmentNeeds = uniqueEnvironmentNeeds(
|
|
707
|
+
const environmentNeeds = uniqueEnvironmentNeeds(contributions.flatMap((item) => item.environmentNeeds));
|
|
524
708
|
const deploymentEnvironmentNeeds = uniqueEnvironmentNeeds([
|
|
525
709
|
...(options.existingDeploymentEnvironmentNeeds ?? []),
|
|
526
|
-
...
|
|
710
|
+
...contributions.flatMap((item) => item.deploymentEnvironmentNeeds ?? []),
|
|
527
711
|
]);
|
|
528
712
|
const persistedEnvironmentNeeds = {
|
|
529
713
|
schemaVersion: 1,
|
|
530
714
|
check: environmentNeeds.map(checkEnvironmentNeedFact),
|
|
531
715
|
deployment: deploymentEnvironmentNeeds.map(deploymentEnvironmentNeedFact),
|
|
532
716
|
};
|
|
533
|
-
const hasDeploymentTask =
|
|
717
|
+
const hasDeploymentTask = contributions.some((contribution) => {
|
|
534
718
|
const scripts = contribution.manifest.scripts;
|
|
535
719
|
return (typeof scripts === "object" &&
|
|
536
720
|
scripts !== null &&
|
|
537
721
|
typeof scripts.deployment === "string");
|
|
538
722
|
});
|
|
539
|
-
const packagePaths =
|
|
723
|
+
const packagePaths = contributions.map((contribution) => contribution.definition.path);
|
|
540
724
|
if (new Set(packagePaths).size !== packagePaths.length)
|
|
541
725
|
throw new Error("Package Contributions must have unique Package Paths");
|
|
542
|
-
const packageNames =
|
|
726
|
+
const packageNames = contributions.map((contribution) => contribution.definition.name);
|
|
543
727
|
if (new Set(packageNames).size !== packageNames.length)
|
|
544
728
|
throw new Error("Package Contributions must have unique package names");
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
}
|
|
729
|
+
assertCompatibleRustToolchainFacts(contributions);
|
|
730
|
+
const contributedToolLayers = contributions.flatMap((contribution) => contribution.foundation.developmentContainerToolLayers ?? []);
|
|
731
|
+
const developmentContainer = contributedDevcontainerComposition({
|
|
732
|
+
context: options.context,
|
|
733
|
+
layers: contributedToolLayers,
|
|
734
|
+
});
|
|
552
735
|
const workspacePackageGlobs = [
|
|
553
736
|
"apps/*",
|
|
554
737
|
"packages/*",
|
|
@@ -556,20 +739,13 @@ function foundationPlan(options) {
|
|
|
556
739
|
...options.blueprint.packages
|
|
557
740
|
.map((definition) => `${definition.path.split("/")[0]}/*`)
|
|
558
741
|
.filter((glob) => glob !== "apps/*" && glob !== "packages/*"),
|
|
559
|
-
...
|
|
742
|
+
...contributions
|
|
560
743
|
.flatMap((contribution) => contribution.foundation.workspacePackageGlobs ?? [])
|
|
561
744
|
.filter((glob) => glob !== "apps/*" && glob !== "packages/*"),
|
|
562
745
|
]),
|
|
563
746
|
];
|
|
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
|
-
};
|
|
747
|
+
const editorCustomization = editorCustomizationForCapabilities(contributions.flatMap((contribution) => contribution.foundation.editorCapabilities), loadEditorCustomizationDeclarations(resolveTemplateSource(templateSources.editorCustomization, "capabilities.json")));
|
|
748
|
+
const dependencyMaintenancePolicy = composeDependencyMaintenancePolicy(contributions);
|
|
573
749
|
const rootManifest = {
|
|
574
750
|
name: options.context.projectName,
|
|
575
751
|
version: "0.0.0",
|
|
@@ -600,7 +776,7 @@ function foundationPlan(options) {
|
|
|
600
776
|
packageManager: options.context.toolchain.packageManagerPin,
|
|
601
777
|
};
|
|
602
778
|
const dependencyCatalog = selectTemplateDependencyCatalogEntries(collectGeneratedManifestCatalogReferences([
|
|
603
|
-
...
|
|
779
|
+
...contributions.map((contribution) => contribution.manifest),
|
|
604
780
|
rootManifest,
|
|
605
781
|
]));
|
|
606
782
|
const dependencyOverrides = {
|
|
@@ -640,7 +816,6 @@ function foundationPlan(options) {
|
|
|
640
816
|
from: ".github/workflows/check.dynamic.template",
|
|
641
817
|
to: ".github/workflows/check.yml",
|
|
642
818
|
replacements: projectCheckWorkflowTemplateReplacements({
|
|
643
|
-
environment: { needs: environmentNeeds },
|
|
644
819
|
deploymentEnvironmentNeeds,
|
|
645
820
|
hasDeploymentTask,
|
|
646
821
|
}),
|
|
@@ -655,16 +830,8 @@ function foundationPlan(options) {
|
|
|
655
830
|
replacements: projectDependabotTemplateReplacements(dependencyMaintenancePolicy),
|
|
656
831
|
},
|
|
657
832
|
];
|
|
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);
|
|
833
|
+
const projectLinkPlan = initialProjectLinkPlan;
|
|
834
|
+
const turboBoundaryTags = turboBoundaryTagsForContributions(contributions);
|
|
668
835
|
const initializationFoundationOperations = [
|
|
669
836
|
{
|
|
670
837
|
kind: "writeJson",
|
|
@@ -741,64 +908,8 @@ function foundationPlan(options) {
|
|
|
741
908
|
to: ".vscode/settings.json",
|
|
742
909
|
value: editorCustomization.settings,
|
|
743
910
|
},
|
|
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
|
-
]),
|
|
911
|
+
...developmentContainer.operations,
|
|
912
|
+
...contributedFoundationTemplateFileOperations(contributions),
|
|
802
913
|
...workflowOperations,
|
|
803
914
|
{
|
|
804
915
|
kind: "writeJson",
|
|
@@ -843,7 +954,15 @@ function foundationPlan(options) {
|
|
|
843
954
|
};
|
|
844
955
|
const withProvenance = (operation, provenance) => ({ ...operation, provenance });
|
|
845
956
|
const operations = [
|
|
846
|
-
...(options.renderContributions
|
|
957
|
+
...(options.renderContributions === undefined
|
|
958
|
+
? contributions
|
|
959
|
+
: options.renderContributions.map((renderContribution) => {
|
|
960
|
+
const enriched = packageContributions.find((candidate) => candidate.definition.path === renderContribution.definition.path);
|
|
961
|
+
if (enriched === undefined) {
|
|
962
|
+
throw new Error(`Foundation cannot render unknown Package Contribution ${renderContribution.definition.path}`);
|
|
963
|
+
}
|
|
964
|
+
return enriched;
|
|
965
|
+
}))
|
|
847
966
|
.map((item) => assertPackageContribution(item, {
|
|
848
967
|
definitionName: options.definition.metadata.name,
|
|
849
968
|
planner: options.mode === "addition"
|
|
@@ -865,7 +984,11 @@ function foundationPlan(options) {
|
|
|
865
984
|
];
|
|
866
985
|
const reconciliation = [
|
|
867
986
|
{ path: "turbo.json", driver: "structured" },
|
|
868
|
-
{
|
|
987
|
+
{
|
|
988
|
+
path: ".devcontainer/devcontainer.json",
|
|
989
|
+
driver: "structured",
|
|
990
|
+
identitySets: [developmentContainer.mountIdentitySet],
|
|
991
|
+
},
|
|
869
992
|
{
|
|
870
993
|
path: ".vscode/extensions.json",
|
|
871
994
|
driver: "structured",
|
|
@@ -890,24 +1013,35 @@ function foundationPlan(options) {
|
|
|
890
1013
|
generationRecord,
|
|
891
1014
|
operations,
|
|
892
1015
|
reconciliation,
|
|
1016
|
+
developmentContainer: {
|
|
1017
|
+
toolLayers: developmentContainer.toolLayers,
|
|
1018
|
+
buildArguments: developmentContainer.buildArguments,
|
|
1019
|
+
mounts: developmentContainer.mounts,
|
|
1020
|
+
probes: developmentContainer.probes,
|
|
1021
|
+
},
|
|
893
1022
|
environmentNeeds,
|
|
894
1023
|
deploymentEnvironmentNeeds,
|
|
895
|
-
manifests: [
|
|
896
|
-
...options.contributions.map((item) => item.manifest),
|
|
897
|
-
rootManifest,
|
|
898
|
-
],
|
|
1024
|
+
manifests: [...contributions.map((item) => item.manifest), rootManifest],
|
|
899
1025
|
dependencyCatalog,
|
|
900
1026
|
dependencyMaintenancePolicy,
|
|
901
1027
|
nextStepInstructions: [
|
|
902
1028
|
{ display: "pnpm install" },
|
|
903
|
-
...environmentNeeds.map((need) => ({ display: need.nextStep.display })),
|
|
904
1029
|
{ display: "pnpm run fix" },
|
|
905
1030
|
{ display: "pnpm run check" },
|
|
906
1031
|
],
|
|
907
1032
|
};
|
|
908
1033
|
}
|
|
909
1034
|
export function planGeneratedRepositoryInitialization(options) {
|
|
910
|
-
const
|
|
1035
|
+
const presetBlueprint = options.definition.blueprint(options.context);
|
|
1036
|
+
const configDefinition = typescriptConfigPackageDefinition(options.context);
|
|
1037
|
+
if (presetBlueprint.packages.some((definition) => definition.name === configDefinition.name ||
|
|
1038
|
+
definition.path === configDefinition.path)) {
|
|
1039
|
+
throw new Error("Preset Blueprint must not redefine the Foundation TypeScript configuration Package Definition");
|
|
1040
|
+
}
|
|
1041
|
+
const blueprint = {
|
|
1042
|
+
...presetBlueprint,
|
|
1043
|
+
packages: [...presetBlueprint.packages, configDefinition],
|
|
1044
|
+
};
|
|
911
1045
|
const contributions = options.definition.planInitializationContributions?.(options.context) ?? [options.definition.planInitialization(options.context)];
|
|
912
1046
|
return foundationPlan({
|
|
913
1047
|
definition: options.definition,
|