@ykdz/template 0.0.35 → 0.0.36
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/application.d.ts +2 -1
- package/dist/application.js +64 -75
- package/dist/main.js +6 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.d.ts +50 -36
- package/node_modules/@ykdz/template-builtin-presets/dist/src/foundation.js +968 -364
- package/node_modules/@ykdz/template-builtin-presets/dist/src/generation-record.d.ts +24 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/generation-record.js +154 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/index.d.ts +2 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/index.js +1 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/rust-bin/definition.d.ts +31 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/rust-bin/definition.js +29 -33
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/typescript.d.ts +4 -3
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/typescript.js +9 -5
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.d.ts +1 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/vue.js +0 -1
- package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-cli/definition.d.ts +31 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-cli/definition.js +79 -58
- package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-lib/definition.d.ts +31 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/ts-lib/definition.js +31 -23
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vike-app/definition.d.ts +22 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vike-app/definition.js +53 -25
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.d.ts +26 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-app/definition.js +19 -7
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.d.ts +22 -2
- package/node_modules/@ykdz/template-builtin-presets/dist/src/vue-hono-app/definition.js +31 -20
- package/node_modules/@ykdz/template-builtin-presets/package.json +12 -11
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment-diagnostics.yml +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.github/workflows/check-deployment.yml +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/.pnpmfile.mjs +19 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/tsconfig.packing-hook.json +8 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/CHANGELOG.md.template +17 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/RELEASING.md +17 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/artifact.ts +1271 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/changelog.ts +283 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/check-artifact.ts +99 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/check-readiness.ts +66 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/cli-command-identity.ts +4 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/handoff.ts +450 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/publish.ts +483 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/readiness.ts +1125 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/release.ts +819 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/release.yml +119 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/root-tsconfig.json +13 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/tsconfig.json +4 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication/turbo.json +6 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/README.md +48 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/assets/CHANGELOG.md.template +17 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/assets/LICENSE-APACHE-2.0.txt +202 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/assets/LICENSE-MIT.txt +21 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/assets/README.md.template +5 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/bridge.ts +3336 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/publication-setup/setup.sh +221 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/src/cli-command-identity.ts +133 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/src/cli.ts +4 -3
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/src/main.ts +5 -4
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/test/integration/command.test.ts +14 -13
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/test/unit/cli-command-identity.test.ts +76 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/ts-cli/tsconfig.build.json +2 -0
- package/node_modules/@ykdz/template-core/dist/module-graph.d.ts +15 -53
- package/node_modules/@ykdz/template-core/dist/module-graph.js +134 -101
- package/node_modules/@ykdz/template-core/dist/package-contribution.d.ts +13 -1
- package/node_modules/@ykdz/template-core/dist/package-contribution.js +92 -0
- package/node_modules/@ykdz/template-core/dist/pnpm-workspace.yaml +14 -10
- package/node_modules/@ykdz/template-core/dist/preset-definition.d.ts +71 -13
- package/node_modules/@ykdz/template-core/dist/preset-definition.js +16 -1
- package/node_modules/@ykdz/template-core/dist/project-blueprint.d.ts +56 -0
- package/node_modules/@ykdz/template-core/dist/project-blueprint.js +271 -0
- package/node_modules/@ykdz/template-core/dist/project-linking-v2.d.ts +2 -2
- package/node_modules/@ykdz/template-core/dist/project-linking-v2.js +2 -2
- package/node_modules/@ykdz/template-core/dist/project-projection.d.ts +2 -1
- package/node_modules/@ykdz/template-core/dist/project-projection.js +107 -20
- package/node_modules/@ykdz/template-core/dist/renderer.d.ts +0 -2
- package/node_modules/@ykdz/template-core/dist/renderer.js +17 -38
- package/node_modules/@ykdz/template-core/dist/template-boundary-check.d.ts +2 -2
- package/node_modules/@ykdz/template-core/dist/template-boundary-check.js +6 -0
- package/node_modules/@ykdz/template-core/dist/toolchain-resolution.d.ts +2 -0
- package/node_modules/@ykdz/template-core/dist/toolchain-resolution.js +9 -3
- package/package.json +5 -5
- package/node_modules/@ykdz/template-core/dist/project-blueprint-v2.d.ts +0 -33
- package/node_modules/@ykdz/template-core/dist/project-blueprint-v2.js +0 -182
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
@@ -5,15 +6,17 @@ import { composeCiDiagnosticArtifacts } from "#template-core/ci-diagnostic-artif
|
|
|
5
6
|
import { collectGeneratedManifestCatalogReferences, selectTemplateDependencyCatalogEntries, } from "#template-core/dependency-catalog";
|
|
6
7
|
import { planDevelopmentContainerToolLayersSync, } from "#template-core/development-container-tool-layer";
|
|
7
8
|
import { editorCustomizationForCapabilities, loadEditorCustomizationDeclarations, } from "#template-core/editor-customization";
|
|
8
|
-
import {
|
|
9
|
-
import { assertPackageContribution, } from "#template-core/package-contribution";
|
|
10
|
-
import {
|
|
9
|
+
import { normalizeEnvironmentNeeds, parseEnvironmentNeedsMetadata, renderDeploymentCheckCommand, renderFixCommand, renderRootCheckCommand, renderTurboRunCommand, } from "#template-core/module-graph";
|
|
10
|
+
import { assertPackageContribution, assertPackageContributionCommandNames, } from "#template-core/package-contribution";
|
|
11
|
+
import { assertProjectBlueprint, assertProjectBlueprintDraft, isValidNewNpmPackageName, validateNewPackagePath, validateProjectBlueprint as validateCoreProjectBlueprint, } from "#template-core/project-blueprint";
|
|
11
12
|
import { projectCheckWorkflowTemplateSource, projectCheckWorkflowTemplateReplacements, projectDependabotTemplateReplacements, } from "#template-core/project-github";
|
|
12
13
|
import { planExplicitProjectLinks } from "#template-core/project-linking-v2";
|
|
14
|
+
import { validateProjectProjectionPlan } from "#template-core/project-projection";
|
|
13
15
|
import { resolveTemplateSource, } from "#template-core/renderer";
|
|
16
|
+
import { isValidDefaultPackageScope, parseGenerationRecord, } from "./generation-record.js";
|
|
14
17
|
import { rustBinDefinition } from "./rust-bin/definition.js";
|
|
15
18
|
import { githubCliDevelopmentContainerToolLayer } from "./shared/development-container.js";
|
|
16
|
-
import { typescriptConfigContribution, typescriptConfigPackageDefinition,
|
|
19
|
+
import { typescriptConfigContribution, typescriptConfigPackageDefinition, typescriptConfigReplayAdapter, } from "./shared/typescript.js";
|
|
17
20
|
import { vuePnpmDependencyOverrides } from "./shared/vue.js";
|
|
18
21
|
import { templateSources } from "./template-sources.js";
|
|
19
22
|
import { tsCliDefinition } from "./ts-cli/definition.js";
|
|
@@ -21,6 +24,7 @@ import { tsLibDefinition } from "./ts-lib/definition.js";
|
|
|
21
24
|
import { vikeAppDefinition } from "./vike-app/definition.js";
|
|
22
25
|
import { vueAppDefinition } from "./vue-app/definition.js";
|
|
23
26
|
import { vueHonoAppDefinition } from "./vue-hono-app/definition.js";
|
|
27
|
+
const localTemplateMetadataStateKey = Symbol("localTemplateMetadataState");
|
|
24
28
|
const environmentNeedsPath = ".template/environment-needs.json";
|
|
25
29
|
const packageManifestKeyOrder = [
|
|
26
30
|
"name",
|
|
@@ -53,8 +57,8 @@ const packageConditionKeyOrder = ["source", "types", "default"];
|
|
|
53
57
|
export function resolveBuiltInTemplateSource(source, relativePath) {
|
|
54
58
|
return resolveTemplateSource(source, relativePath);
|
|
55
59
|
}
|
|
56
|
-
export function
|
|
57
|
-
return
|
|
60
|
+
export function validateProjectBlueprint(value) {
|
|
61
|
+
return validateCoreProjectBlueprint(value);
|
|
58
62
|
}
|
|
59
63
|
class PresetRegistry {
|
|
60
64
|
#definitions;
|
|
@@ -64,6 +68,14 @@ class PresetRegistry {
|
|
|
64
68
|
new Set(names).size !== names.length) {
|
|
65
69
|
throw new Error("Preset Registry requires unique non-empty Definition names");
|
|
66
70
|
}
|
|
71
|
+
for (const definition of definitions) {
|
|
72
|
+
const adapterIdentities = definition.packageContributionReplayAdapters.map((adapter) => adapter.identity);
|
|
73
|
+
if (adapterIdentities.length === 0 ||
|
|
74
|
+
adapterIdentities.some((identity) => identity.length === 0) ||
|
|
75
|
+
new Set(adapterIdentities).size !== adapterIdentities.length) {
|
|
76
|
+
throw new Error(`Built-in Preset ${definition.metadata.name} requires unique non-empty Package Contribution replay adapters`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
67
79
|
this.#definitions = [...definitions].toSorted((left, right) => left.metadata.name.localeCompare(right.metadata.name));
|
|
68
80
|
}
|
|
69
81
|
all() {
|
|
@@ -84,245 +96,304 @@ export const builtInPresetRegistry = new PresetRegistry([
|
|
|
84
96
|
vueHonoAppDefinition,
|
|
85
97
|
vikeAppDefinition,
|
|
86
98
|
]);
|
|
87
|
-
/** Registry-derived Template Source roots checked independently of render plans. */
|
|
88
|
-
export function builtInPresetTemplateSourceContexts() {
|
|
89
|
-
return [
|
|
90
|
-
...builtInPresetRegistry.all().map((definition) => ({
|
|
91
|
-
name: definition.metadata.name,
|
|
92
|
-
root: resolveBuiltInTemplateSource(definition.source, "."),
|
|
93
|
-
})),
|
|
94
|
-
{
|
|
95
|
-
name: "foundation",
|
|
96
|
-
root: resolveBuiltInTemplateSource(templateSources.foundation, "."),
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
name: "shared-devcontainer",
|
|
100
|
-
root: resolveBuiltInTemplateSource(templateSources.sharedDevcontainer, "."),
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
name: "shared-oxc",
|
|
104
|
-
root: resolveBuiltInTemplateSource(templateSources.sharedOxc, "."),
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
name: "shared-vue",
|
|
108
|
-
root: resolveBuiltInTemplateSource(templateSources.vue, "."),
|
|
109
|
-
},
|
|
110
|
-
];
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Derives direct Template Source checks from every registered Definition's
|
|
114
|
-
* actual initial contributions, without maintaining a second Preset catalog.
|
|
115
|
-
*/
|
|
116
|
-
export function builtInPresetTemplateSourceCheckContexts() {
|
|
117
|
-
return builtInPresetRegistry.all().flatMap((definition) => {
|
|
118
|
-
const context = createGenerationContext({
|
|
119
|
-
targetDir: path.join("generated-repository", "template-source", definition.metadata.name),
|
|
120
|
-
toolchain: { nodeLtsMajor: "24", packageManagerPin: "pnpm@11.11.0" },
|
|
121
|
-
});
|
|
122
|
-
const plan = planGeneratedRepositoryInitialization({ definition, context });
|
|
123
|
-
const contributions = definition.planInitializationContributions?.(context) ?? [definition.planInitialization(context)];
|
|
124
|
-
return contributions.map((contribution) => ({
|
|
125
|
-
definition,
|
|
126
|
-
contribution,
|
|
127
|
-
plan,
|
|
128
|
-
}));
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
99
|
export function createGenerationContext(options) {
|
|
132
|
-
const
|
|
100
|
+
const repositoryName = path.basename(path.resolve(options.targetDir));
|
|
101
|
+
const defaultPackageScope = options.defaultPackageScope ?? repositoryName;
|
|
133
102
|
return {
|
|
134
103
|
targetDir: options.targetDir,
|
|
135
|
-
|
|
136
|
-
|
|
104
|
+
repositoryName,
|
|
105
|
+
defaultPackageScope,
|
|
106
|
+
foundationPackages: {
|
|
107
|
+
typescriptConfiguration: {
|
|
108
|
+
name: `@${defaultPackageScope}/typescript-config`,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
137
111
|
toolchain: options.toolchain,
|
|
138
112
|
};
|
|
139
113
|
}
|
|
140
114
|
function isRecord(value) {
|
|
141
115
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
142
116
|
}
|
|
117
|
+
const packageDefinitionIdDomain = "ykdz.template.package-definition-id.v1";
|
|
118
|
+
function packageDefinitionIdDigest(value) {
|
|
119
|
+
return `package-${createHash("sha256")
|
|
120
|
+
.update(JSON.stringify(value))
|
|
121
|
+
.digest("hex")}`;
|
|
122
|
+
}
|
|
123
|
+
function allocatePackageDefinitionId(options) {
|
|
124
|
+
const creationFacts = {
|
|
125
|
+
domain: packageDefinitionIdDomain,
|
|
126
|
+
definition: {
|
|
127
|
+
name: options.definition.name,
|
|
128
|
+
path: options.definition.path,
|
|
129
|
+
role: options.definition.role,
|
|
130
|
+
},
|
|
131
|
+
provenance: {
|
|
132
|
+
definitionName: options.provenance.definitionName,
|
|
133
|
+
planningContribution: options.provenance.planningContribution,
|
|
134
|
+
contributionIdentity: options.provenance.contributionIdentity,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
let nonce = 0;
|
|
138
|
+
while (true) {
|
|
139
|
+
const candidate = packageDefinitionIdDigest(nonce === 0
|
|
140
|
+
? creationFacts
|
|
141
|
+
: {
|
|
142
|
+
creationFacts,
|
|
143
|
+
occupiedIds: [...options.occupiedIds].toSorted(),
|
|
144
|
+
nonce,
|
|
145
|
+
});
|
|
146
|
+
if (!options.occupiedIds.has(candidate))
|
|
147
|
+
return candidate;
|
|
148
|
+
nonce += 1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function persistPackageDefinition(options) {
|
|
152
|
+
const packageDefinitionId = allocatePackageDefinitionId(options);
|
|
153
|
+
options.occupiedIds.add(packageDefinitionId);
|
|
154
|
+
return { ...options.definition, packageDefinitionId };
|
|
155
|
+
}
|
|
143
156
|
function readGenerationRecord(options) {
|
|
144
|
-
const generationPath = path.join(options.
|
|
145
|
-
|
|
157
|
+
const generationPath = path.join(options.repositoryRoot, ".template/generation.json");
|
|
158
|
+
return parseGenerationRecord(readJsonFile(generationPath, "Generation Record facts"));
|
|
159
|
+
}
|
|
160
|
+
function readJsonFile(filePath, label) {
|
|
146
161
|
try {
|
|
147
|
-
|
|
162
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
148
163
|
}
|
|
149
164
|
catch (error) {
|
|
150
|
-
throw new Error(`Package Addition requires valid
|
|
151
|
-
}
|
|
152
|
-
if (isRecord(value)) {
|
|
153
|
-
for (const key of Object.keys(value)) {
|
|
154
|
-
if (![
|
|
155
|
-
"schemaVersion",
|
|
156
|
-
"preset",
|
|
157
|
-
"templateVersion",
|
|
158
|
-
"toolchain",
|
|
159
|
-
"packages",
|
|
160
|
-
].includes(key)) {
|
|
161
|
-
throw new Error(`Package Addition Generation Record contains unknown field: ${key}`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
if (isRecord(value.toolchain)) {
|
|
165
|
-
for (const key of Object.keys(value.toolchain)) {
|
|
166
|
-
if (!["nodeLtsMajor", "packageManagerPin"].includes(key)) {
|
|
167
|
-
throw new Error(`Package Addition Generation Record toolchain contains unknown field: ${key}`);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
165
|
+
throw new Error(`Package Addition requires valid ${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
171
166
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
167
|
+
}
|
|
168
|
+
function barePackageDefinition(definition) {
|
|
169
|
+
return {
|
|
170
|
+
name: definition.name,
|
|
171
|
+
path: definition.path,
|
|
172
|
+
role: definition.role,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function preflightLocalTemplateMetadata(options) {
|
|
176
|
+
const definitionsById = new Map(options.blueprint.packages.map((definition) => [
|
|
177
|
+
definition.packageDefinitionId,
|
|
178
|
+
definition,
|
|
179
|
+
]));
|
|
180
|
+
const joinedPackages = options.generationRecord.packages.map((record) => {
|
|
181
|
+
const definition = definitionsById.get(record.packageDefinitionId);
|
|
182
|
+
if (definition === undefined) {
|
|
183
|
+
throw new Error(`Generation Record Package Definition ID ${record.packageDefinitionId} has no matching Project Blueprint Package Definition`);
|
|
190
184
|
}
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
typeof item.definitionName !== "string" ||
|
|
194
|
-
!["foundationPlan", "planInitialization", "planPackageAddition"].includes(String(item.planningContribution))) {
|
|
195
|
-
throw new Error(`Package Addition requires valid package planning facts at .template/generation.json packages[${index}]`);
|
|
185
|
+
if (record.path !== definition.path) {
|
|
186
|
+
throw new Error(`Generation Record path witness ${record.path} for Package Definition ID ${record.packageDefinitionId} conflicts with Project Blueprint path ${definition.path}`);
|
|
196
187
|
}
|
|
197
|
-
|
|
188
|
+
return { record, definition: barePackageDefinition(definition) };
|
|
189
|
+
});
|
|
190
|
+
const recordedIds = new Set(options.generationRecord.packages.map((record) => record.packageDefinitionId));
|
|
191
|
+
const unrecordedDefinition = options.blueprint.packages.find((definition) => !recordedIds.has(definition.packageDefinitionId));
|
|
192
|
+
if (unrecordedDefinition !== undefined) {
|
|
193
|
+
throw new Error(`Project Blueprint Package Definition ID ${unrecordedDefinition.packageDefinitionId} has no matching Generation Record provenance`);
|
|
198
194
|
}
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
.
|
|
202
|
-
const recordedPaths = packages.map((item) => item.path).toSorted();
|
|
203
|
-
if (JSON.stringify(blueprintPaths) !== JSON.stringify(recordedPaths) ||
|
|
204
|
-
new Set(recordedPaths).size !== recordedPaths.length) {
|
|
205
|
-
throw new Error("Package Addition requires Generation Record packages to match the current Project Blueprint");
|
|
195
|
+
const records = options.generationRecord.packages.filter((record) => record.planningContribution === "foundationPlan");
|
|
196
|
+
if (records.length !== 1) {
|
|
197
|
+
throw new Error(`Package Addition requires exactly one Foundation Package Planning Provenance record; found ${records.length}`);
|
|
206
198
|
}
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
context: options.context,
|
|
219
|
-
blueprint: options.blueprint,
|
|
220
|
-
generationRecord,
|
|
221
|
-
});
|
|
222
|
-
return generationRecord;
|
|
223
|
-
}
|
|
224
|
-
function assertGenerationRecordFoundationConsistency(options) {
|
|
225
|
-
const configDefinition = typescriptConfigPackageDefinition(options.context);
|
|
226
|
-
const foundationRecords = options.generationRecord.packages.filter((record) => record.planningContribution === "foundationPlan");
|
|
227
|
-
if (foundationRecords.length !== 1 ||
|
|
228
|
-
foundationRecords[0].definitionName !== "foundation" ||
|
|
229
|
-
foundationRecords[0].path !== configDefinition.path) {
|
|
230
|
-
throw new Error("Package Addition Generation Record must contain exactly one Foundation TypeScript configuration provenance record");
|
|
199
|
+
const record = records[0];
|
|
200
|
+
if (record.definitionName !== "foundation") {
|
|
201
|
+
throw new Error(`Foundation Package Planning Provenance at ${record.path} must use definitionName foundation; received ${record.definitionName}`);
|
|
202
|
+
}
|
|
203
|
+
if (record.contributionIdentity !== typescriptConfigReplayAdapter.identity) {
|
|
204
|
+
throw new Error(`Foundation TypeScript Configuration Package provenance must use contribution identity ${typescriptConfigReplayAdapter.identity}`);
|
|
205
|
+
}
|
|
206
|
+
const foundationPackage = joinedPackages.find((candidate) => candidate.record === record);
|
|
207
|
+
let initialDefinition;
|
|
208
|
+
try {
|
|
209
|
+
initialDefinition = builtInPresetRegistry.require(options.generationRecord.preset);
|
|
231
210
|
}
|
|
232
|
-
|
|
211
|
+
catch {
|
|
212
|
+
throw new Error(`Package Addition Generation Record preset ${options.generationRecord.preset} is not a registered Built-in Preset`);
|
|
213
|
+
}
|
|
214
|
+
const initialRecords = options.generationRecord.packages.filter((candidate) => candidate.planningContribution === "planInitialization");
|
|
233
215
|
if (initialRecords.length === 0) {
|
|
234
216
|
throw new Error(`Package Addition Generation Record preset ${options.generationRecord.preset} has no initial Package provenance`);
|
|
235
217
|
}
|
|
236
|
-
const conflictingRecord = initialRecords.find((
|
|
218
|
+
const conflictingRecord = initialRecords.find((candidate) => candidate.definitionName !== options.generationRecord.preset);
|
|
237
219
|
if (conflictingRecord !== undefined) {
|
|
238
220
|
throw new Error(`Package Addition Generation Record preset ${options.generationRecord.preset} conflicts with initial Package provenance ${conflictingRecord.definitionName} for Blueprint package ${conflictingRecord.path}`);
|
|
239
221
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
222
|
+
const preparedWithoutInitialLookup = joinedPackages.map((candidate) => {
|
|
223
|
+
if (candidate.record === record) {
|
|
224
|
+
return {
|
|
225
|
+
...candidate,
|
|
226
|
+
owner: "foundation",
|
|
227
|
+
adapter: typescriptConfigReplayAdapter,
|
|
228
|
+
kind: "foundation",
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
let definition;
|
|
232
|
+
try {
|
|
233
|
+
definition = builtInPresetRegistry.require(candidate.record.definitionName);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
throw new Error(`Package Addition Generation Record Package Planning Provenance references unknown Built-in Preset ${candidate.record.definitionName} for Blueprint package ${candidate.record.path}`);
|
|
237
|
+
}
|
|
238
|
+
if (candidate.record.planningContribution === "planPackageAddition" &&
|
|
239
|
+
definition.planPackageAddition === undefined) {
|
|
240
|
+
throw new Error(`Package Addition Generation Record Package Planning Provenance references unsupported Package Addition for Built-in Preset ${candidate.record.definitionName} at ${candidate.record.path}`);
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
...candidate,
|
|
244
|
+
owner: definition.metadata.name,
|
|
245
|
+
adapter: requireRecordReplayAdapter({
|
|
246
|
+
owner: definition.metadata.name,
|
|
247
|
+
adapters: definition.packageContributionReplayAdapters,
|
|
248
|
+
record: candidate.record,
|
|
249
|
+
}),
|
|
250
|
+
kind: "package",
|
|
251
|
+
};
|
|
252
|
+
});
|
|
253
|
+
const initialPlanningKeys = initialRecords.map((candidate) => `${candidate.definitionName}:${candidate.contributionIdentity}`);
|
|
254
|
+
if (new Set(initialPlanningKeys).size !== initialPlanningKeys.length) {
|
|
255
|
+
throw new Error("Package Addition Generation Record contains duplicate initialization contribution identity");
|
|
246
256
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
.map((definition) => definition.path)
|
|
251
|
-
.toSorted();
|
|
252
|
-
const recordedInitialPaths = initialRecords
|
|
253
|
-
.map((record) => record.path)
|
|
254
|
-
.toSorted();
|
|
255
|
-
if (JSON.stringify(recordedInitialPaths) !== JSON.stringify(expectedPaths)) {
|
|
256
|
-
throw new Error(`Package Addition Generation Record preset ${options.generationRecord.preset} expects initial Blueprint packages ${expectedPaths.join(", ")}, but initial provenance records ${recordedInitialPaths.join(", ")}`);
|
|
257
|
-
}
|
|
258
|
-
for (const expectedDefinition of initialBlueprint.packages) {
|
|
259
|
-
const currentDefinition = options.blueprint.packages.find((definition) => definition.path === expectedDefinition.path);
|
|
260
|
-
if (currentDefinition === undefined ||
|
|
261
|
-
!packageDefinitionsEqual(currentDefinition, expectedDefinition)) {
|
|
262
|
-
throw new Error(`Package Addition Generation Record preset ${options.generationRecord.preset} cannot reproduce initial Blueprint Package Definition ${expectedDefinition.name} at ${expectedDefinition.path} (${expectedDefinition.role})`);
|
|
257
|
+
if (initialDefinition.initialPrimaryPackage !== undefined) {
|
|
258
|
+
if (initialRecords.length !== 1) {
|
|
259
|
+
throw new Error(`${initialDefinition.metadata.name} requires exactly one initial Primary Package Contribution provenance record`);
|
|
263
260
|
}
|
|
264
261
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
262
|
+
else {
|
|
263
|
+
for (const adapter of initialDefinition.packageContributionReplayAdapters) {
|
|
264
|
+
const matchingRecords = initialRecords.filter((candidate) => candidate.contributionIdentity === adapter.identity);
|
|
265
|
+
if (matchingRecords.length !== 1) {
|
|
266
|
+
throw new Error(`${initialDefinition.metadata.name} requires exactly one initial ${adapter.identity} Package Contribution provenance record`);
|
|
267
|
+
}
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
|
+
const initialDefinitions = new Map(preparedWithoutInitialLookup.flatMap((candidate) => candidate.record.planningContribution === "planInitialization"
|
|
271
|
+
? [[candidate.record.contributionIdentity, candidate.definition]]
|
|
272
|
+
: []));
|
|
273
|
+
const initialPackages = {
|
|
274
|
+
require(identity) {
|
|
275
|
+
const definition = initialDefinitions.get(identity);
|
|
276
|
+
if (definition === undefined) {
|
|
277
|
+
throw new Error(`${initialDefinition.metadata.name} has no preflighted initial ${identity} Package Definition`);
|
|
278
|
+
}
|
|
279
|
+
return definition;
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
const noInitialPackages = {
|
|
283
|
+
require(identity) {
|
|
284
|
+
throw new Error(`Foundation replay adapter does not declare initial Package ${identity}`);
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
return {
|
|
288
|
+
foundationPackage,
|
|
289
|
+
replays: preparedWithoutInitialLookup.map((candidate) => ({
|
|
290
|
+
...candidate,
|
|
291
|
+
initialPackages: candidate.kind === "foundation" ? noInitialPackages : initialPackages,
|
|
292
|
+
})),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
export function loadLocalTemplateMetadata(repositoryRoot) {
|
|
296
|
+
const resolvedRoot = path.resolve(repositoryRoot);
|
|
297
|
+
const blueprintPath = path.join(resolvedRoot, ".template/blueprint.json");
|
|
298
|
+
let blueprint;
|
|
299
|
+
try {
|
|
300
|
+
blueprint = assertProjectBlueprint(readJsonFile(blueprintPath, "Project Blueprint facts"));
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
throw new Error(`Package Addition requires a supported Project Blueprint in .template/blueprint.json: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
304
|
+
}
|
|
305
|
+
const generationRecord = readGenerationRecord({
|
|
306
|
+
repositoryRoot: resolvedRoot,
|
|
307
|
+
});
|
|
308
|
+
const preflight = preflightLocalTemplateMetadata({
|
|
309
|
+
blueprint,
|
|
310
|
+
generationRecord,
|
|
311
|
+
});
|
|
312
|
+
const context = {
|
|
313
|
+
targetDir: resolvedRoot,
|
|
314
|
+
repositoryName: generationRecord.repositoryName,
|
|
315
|
+
defaultPackageScope: generationRecord.defaultPackageScope,
|
|
316
|
+
foundationPackages: {
|
|
317
|
+
typescriptConfiguration: {
|
|
318
|
+
name: preflight.foundationPackage.definition.name,
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
toolchain: generationRecord.toolchain,
|
|
322
|
+
};
|
|
323
|
+
const planningState = replayLocalTemplateMetadata({
|
|
324
|
+
context,
|
|
325
|
+
preflight,
|
|
326
|
+
});
|
|
327
|
+
return {
|
|
328
|
+
blueprint,
|
|
329
|
+
context,
|
|
330
|
+
[localTemplateMetadataStateKey]: { generationRecord, ...planningState },
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
function requireReplayAdapter(options) {
|
|
334
|
+
const adapter = options.adapters.find((candidate) => candidate.identity === options.identity);
|
|
335
|
+
if (adapter === undefined) {
|
|
336
|
+
throw new Error(`unknown Package Contribution replay adapter ${options.identity}; expected ${options.adapters.map((candidate) => candidate.identity).join(", ")} for ${options.owner}`);
|
|
337
|
+
}
|
|
338
|
+
return adapter;
|
|
270
339
|
}
|
|
271
|
-
function
|
|
272
|
-
|
|
273
|
-
return
|
|
274
|
-
|
|
275
|
-
|
|
340
|
+
function requireRecordReplayAdapter(options) {
|
|
341
|
+
try {
|
|
342
|
+
return requireReplayAdapter({
|
|
343
|
+
owner: options.owner,
|
|
344
|
+
adapters: options.adapters,
|
|
345
|
+
identity: options.record.contributionIdentity,
|
|
346
|
+
});
|
|
276
347
|
}
|
|
277
|
-
|
|
278
|
-
|
|
348
|
+
catch (error) {
|
|
349
|
+
throw new Error(`Package Planning Provenance ${options.owner}:${options.record.contributionIdentity} (${options.record.planningContribution}) at ${options.record.path} failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
279
350
|
}
|
|
280
|
-
return undefined;
|
|
281
351
|
}
|
|
282
|
-
function
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
352
|
+
function packageLeafName(packageDefinition) {
|
|
353
|
+
return packageDefinition.name.slice(packageDefinition.name.lastIndexOf("/") + 1);
|
|
354
|
+
}
|
|
355
|
+
function replayPersistedPackageContribution(options) {
|
|
356
|
+
let contribution;
|
|
357
|
+
try {
|
|
358
|
+
contribution = options.adapter.replay({
|
|
359
|
+
context: options.context,
|
|
360
|
+
planningContribution: options.record.planningContribution,
|
|
361
|
+
packageDefinition: options.packageDefinition,
|
|
362
|
+
packageLeafName: packageLeafName(options.packageDefinition),
|
|
363
|
+
initialPackages: options.initialPackages,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
throw new Error(`Package Planning Provenance ${options.owner}:${options.record.contributionIdentity} (${options.record.planningContribution}) at ${options.record.path} failed: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
368
|
+
}
|
|
369
|
+
if (!packageDefinitionsEqual(contribution.definition, options.packageDefinition)) {
|
|
370
|
+
throw new Error(`Package Planning Provenance ${options.owner}:${options.record.contributionIdentity} at ${options.record.path} expected ${options.packageDefinition.name} / ${options.packageDefinition.path} / ${options.packageDefinition.role}, received ${contribution.definition.name} / ${contribution.definition.path} / ${contribution.definition.role}`);
|
|
287
371
|
}
|
|
372
|
+
return options.adapter.identify(contribution);
|
|
288
373
|
}
|
|
289
|
-
function
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
assertEnvironmentNeedFactKeys(value.owner, ["kind", "path"], `${context}.owner`);
|
|
308
|
-
}
|
|
309
|
-
const owner = environmentNeedOwner(value.owner);
|
|
310
|
-
if (owner === undefined)
|
|
311
|
-
return undefined;
|
|
312
|
-
switch (value.kind) {
|
|
313
|
-
case "playwright-browser-assets":
|
|
314
|
-
return value.browser === "chromium"
|
|
315
|
-
? { kind: value.kind, browser: value.browser, owner }
|
|
316
|
-
: undefined;
|
|
317
|
-
case "shellcheck-command":
|
|
318
|
-
return { kind: value.kind, owner };
|
|
319
|
-
case "rust-toolchain":
|
|
320
|
-
return value.toolchain === "stable"
|
|
321
|
-
? { kind: value.kind, toolchain: value.toolchain, owner }
|
|
322
|
-
: undefined;
|
|
323
|
-
default:
|
|
324
|
-
return undefined;
|
|
374
|
+
function replayLocalTemplateMetadata(options) {
|
|
375
|
+
let foundationContribution;
|
|
376
|
+
const packageContributions = [];
|
|
377
|
+
for (const replay of options.preflight.replays) {
|
|
378
|
+
const contribution = replayPersistedPackageContribution({
|
|
379
|
+
context: options.context,
|
|
380
|
+
owner: replay.owner,
|
|
381
|
+
adapter: replay.adapter,
|
|
382
|
+
record: replay.record,
|
|
383
|
+
packageDefinition: replay.definition,
|
|
384
|
+
initialPackages: replay.initialPackages,
|
|
385
|
+
});
|
|
386
|
+
if (replay.kind === "foundation") {
|
|
387
|
+
foundationContribution = contribution;
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
packageContributions.push(contribution);
|
|
391
|
+
}
|
|
325
392
|
}
|
|
393
|
+
if (foundationContribution === undefined) {
|
|
394
|
+
throw new Error("Package Addition Generation Record is missing its validated Foundation contribution");
|
|
395
|
+
}
|
|
396
|
+
return { foundationContribution, packageContributions };
|
|
326
397
|
}
|
|
327
398
|
function readPersistedEnvironmentNeeds(targetDir) {
|
|
328
399
|
const filePath = path.join(targetDir, environmentNeedsPath);
|
|
@@ -336,88 +407,31 @@ function readPersistedEnvironmentNeeds(targetDir) {
|
|
|
336
407
|
catch (error) {
|
|
337
408
|
throw new Error(`Package Addition requires valid Check Environment Need facts: ${error instanceof Error ? error.message : String(error)}`);
|
|
338
409
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
if (!["schemaVersion", "check", "deployment"].includes(key)) {
|
|
342
|
-
throw new Error(`Package Addition Environment Need facts contain unknown field: ${key}`);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
if (!isRecord(value) ||
|
|
347
|
-
value.schemaVersion !== 1 ||
|
|
348
|
-
!Array.isArray(value.check) ||
|
|
349
|
-
!Array.isArray(value.deployment)) {
|
|
350
|
-
throw new Error(`Package Addition requires valid Check Environment Need facts in ${environmentNeedsPath}`);
|
|
351
|
-
}
|
|
352
|
-
const check = value.check.map(persistedCheckEnvironmentNeedFact);
|
|
353
|
-
if (check.some((fact) => fact === undefined)) {
|
|
354
|
-
throw new Error(`Package Addition requires supported Check Environment Need facts in ${environmentNeedsPath}`);
|
|
410
|
+
try {
|
|
411
|
+
return parseEnvironmentNeedsMetadata(value);
|
|
355
412
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
return true;
|
|
359
|
-
assertEnvironmentNeedFactKeys(fact, ["kind"], `deployment[${index}]`);
|
|
360
|
-
return false;
|
|
361
|
-
})) {
|
|
362
|
-
throw new Error(`Package Addition requires supported deployment Environment Need facts in ${environmentNeedsPath}`);
|
|
413
|
+
catch (error) {
|
|
414
|
+
throw new Error(`Package Addition requires valid Environment Need metadata in ${environmentNeedsPath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
|
|
363
415
|
}
|
|
364
|
-
return {
|
|
365
|
-
check: check.map(checkEnvironmentNeedFromFact),
|
|
366
|
-
deployment: value.deployment.map((fact) => deploymentEnvironmentNeedFromFact(fact)),
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
function uniqueEnvironmentNeeds(needs) {
|
|
370
|
-
const seen = new Set();
|
|
371
|
-
return needs.filter((need) => {
|
|
372
|
-
const key = JSON.stringify(need);
|
|
373
|
-
if (seen.has(key))
|
|
374
|
-
return false;
|
|
375
|
-
seen.add(key);
|
|
376
|
-
return true;
|
|
377
|
-
});
|
|
378
416
|
}
|
|
379
417
|
function readExistingPackageAdditionState(options) {
|
|
380
|
-
const
|
|
381
|
-
const
|
|
418
|
+
const { blueprint, context } = options.localTemplateMetadata;
|
|
419
|
+
const state = options.localTemplateMetadata[localTemplateMetadataStateKey];
|
|
420
|
+
const { foundationContribution, generationRecord, packageContributions: contributions, } = state;
|
|
421
|
+
const persistedEnvironmentNeeds = readPersistedEnvironmentNeeds(context.targetDir);
|
|
382
422
|
const manifestTruthByPackagePath = new Map();
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}
|
|
387
|
-
const expectedDefinition = options.blueprint.packages.find((definition) => definition.path === record.path);
|
|
388
|
-
const definition = builtInPresetRegistry.require(record.definitionName);
|
|
389
|
-
let candidates;
|
|
390
|
-
if (record.planningContribution === "planInitialization") {
|
|
391
|
-
candidates = definition.planInitializationContributions?.(options.context) ?? [definition.planInitialization(options.context)];
|
|
392
|
-
}
|
|
393
|
-
else {
|
|
394
|
-
if (definition.planPackageAddition === undefined) {
|
|
395
|
-
throw new Error(`Generation Record Definition ${record.definitionName} no longer supports Package Addition`);
|
|
396
|
-
}
|
|
397
|
-
const packageLeafName = expectedDefinition.name.split("/")[1];
|
|
398
|
-
if (!packageLeafName) {
|
|
399
|
-
throw new Error(`Generation Record package has an invalid name: ${expectedDefinition.name}`);
|
|
400
|
-
}
|
|
401
|
-
candidates = [
|
|
402
|
-
definition.planPackageAddition({
|
|
403
|
-
context: options.context,
|
|
404
|
-
packageLeafName,
|
|
405
|
-
packagePath: expectedDefinition.path,
|
|
406
|
-
}),
|
|
407
|
-
];
|
|
408
|
-
}
|
|
409
|
-
const contribution = candidates.find((candidate) => packageDefinitionsEqual(candidate.definition, expectedDefinition));
|
|
410
|
-
if (contribution === undefined) {
|
|
411
|
-
throw new Error(`Generation Record cannot reproduce Package Definition ${expectedDefinition.name} at ${expectedDefinition.path}`);
|
|
412
|
-
}
|
|
413
|
-
return [contribution];
|
|
414
|
-
});
|
|
415
|
-
for (const packagePath of new Set(options.manifestTruthPackagePaths ?? [])) {
|
|
416
|
-
const expectedDefinition = options.blueprint.packages.find((definition) => definition.path === packagePath);
|
|
417
|
-
if (expectedDefinition === undefined) {
|
|
423
|
+
const requiredManifestTruthPackagePaths = new Set(options.requiredManifestTruthPackagePaths ?? []);
|
|
424
|
+
for (const packagePath of requiredManifestTruthPackagePaths) {
|
|
425
|
+
if (!blueprint.packages.some((definition) => definition.path === packagePath)) {
|
|
418
426
|
throw new Error(`Package Addition requires manifest truth for unknown Package Path ${packagePath}`);
|
|
419
427
|
}
|
|
420
|
-
|
|
428
|
+
}
|
|
429
|
+
for (const expectedDefinition of blueprint.packages) {
|
|
430
|
+
const manifestPath = path.join(context.targetDir, expectedDefinition.path, "package.json");
|
|
431
|
+
if (!existsSync(manifestPath) &&
|
|
432
|
+
!requiredManifestTruthPackagePaths.has(expectedDefinition.path)) {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
421
435
|
let manifest;
|
|
422
436
|
try {
|
|
423
437
|
manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
@@ -430,17 +444,16 @@ function readExistingPackageAdditionState(options) {
|
|
|
430
444
|
}
|
|
431
445
|
manifestTruthByPackagePath.set(expectedDefinition.path, manifest);
|
|
432
446
|
}
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
if (JSON.stringify(
|
|
438
|
-
JSON.stringify(
|
|
439
|
-
|
|
440
|
-
JSON.stringify(persistedDeploymentFacts)) {
|
|
441
|
-
throw new Error("Package Addition requires persisted Environment Need facts to match the reproducible Project Projection");
|
|
447
|
+
const reconstructedEnvironmentNeeds = normalizeEnvironmentNeeds({
|
|
448
|
+
check: [foundationContribution, ...contributions].flatMap((contribution) => contribution.environmentNeeds),
|
|
449
|
+
deployment: [foundationContribution, ...contributions].flatMap((contribution) => contribution.deploymentEnvironmentNeeds ?? []),
|
|
450
|
+
});
|
|
451
|
+
if (JSON.stringify(reconstructedEnvironmentNeeds) !==
|
|
452
|
+
JSON.stringify(persistedEnvironmentNeeds)) {
|
|
453
|
+
throw new Error("Package Addition requires persisted Environment Needs to match the reproducible Project Projection");
|
|
442
454
|
}
|
|
443
455
|
return {
|
|
456
|
+
foundationContribution,
|
|
444
457
|
contributions,
|
|
445
458
|
manifestTruthByPackagePath,
|
|
446
459
|
deploymentEnvironmentNeeds: persistedEnvironmentNeeds.deployment,
|
|
@@ -452,6 +465,13 @@ function packageDefinitionsEqual(left, right) {
|
|
|
452
465
|
left.path === right.path &&
|
|
453
466
|
left.role === right.role);
|
|
454
467
|
}
|
|
468
|
+
function requirePersistedPackageDefinition(blueprint, definition) {
|
|
469
|
+
const persisted = blueprint.packages.find((candidate) => packageDefinitionsEqual(candidate, definition));
|
|
470
|
+
if (persisted === undefined) {
|
|
471
|
+
throw new Error(`Project Blueprint has no persisted Package Definition for ${definition.name} at ${definition.path} (${definition.role})`);
|
|
472
|
+
}
|
|
473
|
+
return persisted;
|
|
474
|
+
}
|
|
455
475
|
function packageLinkIntentsEqual(left, right) {
|
|
456
476
|
return (left.consumerPackagePath === right.consumerPackagePath &&
|
|
457
477
|
left.providerPackagePath === right.providerPackagePath);
|
|
@@ -510,7 +530,7 @@ function contributedDevcontainerComposition(options) {
|
|
|
510
530
|
from: "devcontainer.json",
|
|
511
531
|
to: ".devcontainer/devcontainer.json",
|
|
512
532
|
replacements: {
|
|
513
|
-
PROJECT_NAME: options.context.
|
|
533
|
+
PROJECT_NAME: options.context.repositoryName,
|
|
514
534
|
NODE_LTS_MAJOR: options.context.toolchain.nodeLtsMajor,
|
|
515
535
|
PACKAGE_MANAGER_PIN: options.context.toolchain.packageManagerPin,
|
|
516
536
|
},
|
|
@@ -642,13 +662,36 @@ function composeDependencyMaintenancePolicy(contributions) {
|
|
|
642
662
|
interval: "weekly",
|
|
643
663
|
};
|
|
644
664
|
}
|
|
665
|
+
function localTypescriptConfigurationSpecifier(options) {
|
|
666
|
+
const relativePackagePath = path.posix.relative(options.consumerPackagePath, options.configurationPackagePath);
|
|
667
|
+
if (relativePackagePath.length === 0) {
|
|
668
|
+
throw new Error("TypeScript Configuration Package must have a distinct Package Path");
|
|
669
|
+
}
|
|
670
|
+
return `link:${relativePackagePath}`;
|
|
671
|
+
}
|
|
672
|
+
function publicCliCandidate(contributions) {
|
|
673
|
+
const candidates = contributions.filter((contribution) => contribution.foundation.npmPublication?.kind === "public-cli-candidate");
|
|
674
|
+
if (candidates.length > 1) {
|
|
675
|
+
throw new Error(`Generated Repository Plan supports at most one public CLI candidate; found ${candidates.map((candidate) => `${candidate.definition.name} at ${candidate.definition.path}`).join(", ")}`);
|
|
676
|
+
}
|
|
677
|
+
const candidate = candidates[0];
|
|
678
|
+
if (candidate !== undefined && candidate.definition.role !== "cli-tool") {
|
|
679
|
+
throw new Error(`Public CLI candidate ${candidate.definition.name} must use the cli-tool Package role`);
|
|
680
|
+
}
|
|
681
|
+
return candidate;
|
|
682
|
+
}
|
|
683
|
+
function renderPublicationRootCheckCommand(candidatePackagePath) {
|
|
684
|
+
return [
|
|
685
|
+
renderTurboRunCommand(["boundaries", "format:check", "lint", "typecheck", "test"], [], { continueAfterFailure: true, taskPrefix: true }),
|
|
686
|
+
renderTurboRunCommand(["build", "test:e2e"], [`--filter=!./${candidatePackagePath}`], { continueAfterFailure: true, taskPrefix: true }),
|
|
687
|
+
"pnpm run publication:artifact",
|
|
688
|
+
].join(" && ");
|
|
689
|
+
}
|
|
645
690
|
function foundationPlan(options) {
|
|
646
|
-
|
|
647
|
-
const
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
if (persistedConfigDefinition === undefined ||
|
|
651
|
-
!packageDefinitionsEqual(persistedConfigDefinition, configDefinition)) {
|
|
691
|
+
assertProjectBlueprint(options.blueprint);
|
|
692
|
+
const configContribution = options.foundationContribution;
|
|
693
|
+
const configDefinition = configContribution.definition;
|
|
694
|
+
if (!options.blueprint.packages.some((definition) => packageDefinitionsEqual(definition, configDefinition))) {
|
|
652
695
|
throw new Error("Project Blueprint must contain the Foundation TypeScript configuration Package Definition");
|
|
653
696
|
}
|
|
654
697
|
const initialProjectLinkPlan = planExplicitProjectLinks({
|
|
@@ -663,15 +706,26 @@ function foundationPlan(options) {
|
|
|
663
706
|
const injectedProviderNames = new Set([...initialProjectLinkPlan.manifestPatchesByPackagePath.values()].flatMap((patch) => Object.entries(patch.dependenciesMeta)
|
|
664
707
|
.filter(([, metadata]) => metadata.injected)
|
|
665
708
|
.map(([name]) => name)));
|
|
666
|
-
const configPackageName =
|
|
709
|
+
const configPackageName = options.context.foundationPackages.typescriptConfiguration.name;
|
|
710
|
+
if (configDefinition.name !== configPackageName) {
|
|
711
|
+
throw new Error(`Generation Context TypeScript Configuration Package ${configPackageName} conflicts with Blueprint Package Definition ${configDefinition.name}`);
|
|
712
|
+
}
|
|
713
|
+
const publicationCandidate = publicCliCandidate(options.contributions);
|
|
667
714
|
const packageContributions = options.contributions.map((contribution) => {
|
|
668
|
-
|
|
669
|
-
operation.to === `${contribution.definition.path}/tsconfig.json`);
|
|
670
|
-
if (!ownsTypeScriptConfig)
|
|
715
|
+
if (contribution.foundation.typescriptConfigurationPackage === undefined) {
|
|
671
716
|
return contribution;
|
|
717
|
+
}
|
|
672
718
|
const dependencyField = injectedProviderNames.has(contribution.definition.name)
|
|
673
719
|
? "dependencies"
|
|
674
720
|
: "devDependencies";
|
|
721
|
+
const dependencySpecifier = dependencyField === "devDependencies" &&
|
|
722
|
+
(contribution.manifest.private !== true ||
|
|
723
|
+
publicationCandidate?.definition.path === contribution.definition.path)
|
|
724
|
+
? localTypescriptConfigurationSpecifier({
|
|
725
|
+
consumerPackagePath: contribution.definition.path,
|
|
726
|
+
configurationPackagePath: configDefinition.path,
|
|
727
|
+
})
|
|
728
|
+
: "workspace:*";
|
|
675
729
|
const existingDependencies = contribution.manifest[dependencyField];
|
|
676
730
|
return {
|
|
677
731
|
...contribution,
|
|
@@ -679,43 +733,67 @@ function foundationPlan(options) {
|
|
|
679
733
|
...contribution.manifest,
|
|
680
734
|
[dependencyField]: {
|
|
681
735
|
...(isRecord(existingDependencies) ? existingDependencies : {}),
|
|
682
|
-
[configPackageName]:
|
|
736
|
+
[configPackageName]: dependencySpecifier,
|
|
683
737
|
},
|
|
684
738
|
},
|
|
685
739
|
};
|
|
686
740
|
});
|
|
687
|
-
const
|
|
741
|
+
const requiresPackingHook = packageContributions.some((contribution) => {
|
|
742
|
+
const developmentDependencies = contribution.manifest.devDependencies;
|
|
743
|
+
return (isRecord(developmentDependencies) &&
|
|
744
|
+
typeof developmentDependencies[configPackageName] === "string" &&
|
|
745
|
+
developmentDependencies[configPackageName].startsWith("link:"));
|
|
746
|
+
});
|
|
688
747
|
const contributions = [configContribution, ...packageContributions];
|
|
689
|
-
|
|
690
|
-
|
|
748
|
+
assertPackageContributionCommandNames(contributions.map((contribution) => {
|
|
749
|
+
const manifest = options.manifestTruthByPackagePath?.get(contribution.definition.path);
|
|
750
|
+
return manifest === undefined
|
|
751
|
+
? contribution
|
|
752
|
+
: { ...contribution, manifest };
|
|
753
|
+
}));
|
|
754
|
+
const candidateRecord = options.generationRecord ?? {
|
|
755
|
+
schemaVersion: 2,
|
|
756
|
+
repositoryName: options.context.repositoryName,
|
|
757
|
+
defaultPackageScope: options.context.defaultPackageScope,
|
|
691
758
|
preset: options.definition.metadata.name,
|
|
692
759
|
templateVersion: "0.0.0",
|
|
693
760
|
toolchain: options.context.toolchain,
|
|
694
761
|
packages: [
|
|
695
|
-
...options.contributions.map((contribution) =>
|
|
696
|
-
|
|
697
|
-
definitionName: options.definition.metadata.name,
|
|
698
|
-
planningContribution: options.mode === "initialization"
|
|
762
|
+
...options.contributions.map((contribution) => {
|
|
763
|
+
const planningContribution = options.mode === "initialization"
|
|
699
764
|
? "planInitialization"
|
|
700
|
-
: "planPackageAddition"
|
|
701
|
-
|
|
765
|
+
: "planPackageAddition";
|
|
766
|
+
return {
|
|
767
|
+
packageDefinitionId: requirePersistedPackageDefinition(options.blueprint, contribution.definition).packageDefinitionId,
|
|
768
|
+
path: contribution.definition.path,
|
|
769
|
+
definitionName: options.definition.metadata.name,
|
|
770
|
+
planningContribution,
|
|
771
|
+
contributionIdentity: requireReplayAdapter({
|
|
772
|
+
owner: options.definition.metadata.name,
|
|
773
|
+
adapters: options.definition.packageContributionReplayAdapters,
|
|
774
|
+
identity: contribution.planningIdentity,
|
|
775
|
+
}).identity,
|
|
776
|
+
};
|
|
777
|
+
}),
|
|
702
778
|
{
|
|
779
|
+
packageDefinitionId: requirePersistedPackageDefinition(options.blueprint, configDefinition).packageDefinitionId,
|
|
703
780
|
path: configDefinition.path,
|
|
704
781
|
definitionName: "foundation",
|
|
705
782
|
planningContribution: "foundationPlan",
|
|
783
|
+
contributionIdentity: typescriptConfigReplayAdapter.identity,
|
|
706
784
|
},
|
|
707
785
|
],
|
|
708
786
|
};
|
|
709
|
-
const
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
787
|
+
const generationRecord = parseGenerationRecord(candidateRecord);
|
|
788
|
+
const persistedEnvironmentNeeds = normalizeEnvironmentNeeds({
|
|
789
|
+
check: contributions.flatMap((item) => item.environmentNeeds),
|
|
790
|
+
deployment: [
|
|
791
|
+
...(options.existingDeploymentEnvironmentNeeds ?? []),
|
|
792
|
+
...contributions.flatMap((item) => item.deploymentEnvironmentNeeds ?? []),
|
|
793
|
+
],
|
|
794
|
+
});
|
|
795
|
+
const environmentNeeds = persistedEnvironmentNeeds.check;
|
|
796
|
+
const deploymentEnvironmentNeeds = persistedEnvironmentNeeds.deployment;
|
|
719
797
|
const hasDeploymentTask = contributions.some((contribution) => {
|
|
720
798
|
const scripts = contribution.manifest.scripts;
|
|
721
799
|
return (typeof scripts === "object" &&
|
|
@@ -759,13 +837,27 @@ function foundationPlan(options) {
|
|
|
759
837
|
const editorCustomization = editorCustomizationForCapabilities(contributions.flatMap((contribution) => contribution.foundation.editorCapabilities), loadEditorCustomizationDeclarations(resolveTemplateSource(templateSources.editorCustomization, "capabilities.json")));
|
|
760
838
|
const dependencyMaintenancePolicy = composeDependencyMaintenancePolicy(contributions);
|
|
761
839
|
const rootManifest = {
|
|
762
|
-
name: options.context.
|
|
763
|
-
version: "0.0.0",
|
|
840
|
+
name: options.context.repositoryName,
|
|
764
841
|
private: true,
|
|
765
842
|
type: "module",
|
|
843
|
+
...(publicationCandidate === undefined
|
|
844
|
+
? {}
|
|
845
|
+
: {
|
|
846
|
+
imports: {
|
|
847
|
+
"#npm-publication/*": "./scripts/npm-publication/*.ts",
|
|
848
|
+
},
|
|
849
|
+
}),
|
|
766
850
|
scripts: {
|
|
767
|
-
check:
|
|
851
|
+
check: publicationCandidate === undefined
|
|
852
|
+
? renderRootCheckCommand()
|
|
853
|
+
: renderPublicationRootCheckCommand(publicationCandidate.definition.path),
|
|
768
854
|
boundaries: "node --conditions=source scripts/check-boundaries.ts",
|
|
855
|
+
...(publicationCandidate === undefined
|
|
856
|
+
? {}
|
|
857
|
+
: {
|
|
858
|
+
"publication:readiness": "node --conditions=source scripts/npm-publication/check-readiness.ts",
|
|
859
|
+
"publication:artifact": "node --conditions=source scripts/npm-publication/check-artifact.ts",
|
|
860
|
+
}),
|
|
769
861
|
...(hasDeploymentTask
|
|
770
862
|
? { "check:deployment": renderDeploymentCheckCommand() }
|
|
771
863
|
: {}),
|
|
@@ -778,9 +870,23 @@ function foundationPlan(options) {
|
|
|
778
870
|
},
|
|
779
871
|
devDependencies: {
|
|
780
872
|
"@types/node": "catalog:",
|
|
873
|
+
...(publicationCandidate === undefined
|
|
874
|
+
? {}
|
|
875
|
+
: {
|
|
876
|
+
"@types/semver": "catalog:",
|
|
877
|
+
"@types/spdx-expression-parse": "catalog:",
|
|
878
|
+
}),
|
|
781
879
|
oxfmt: "catalog:",
|
|
782
880
|
oxlint: "catalog:",
|
|
783
881
|
"oxlint-tsgolint": "catalog:",
|
|
882
|
+
...(publicationCandidate === undefined
|
|
883
|
+
? {}
|
|
884
|
+
: {
|
|
885
|
+
semver: "catalog:",
|
|
886
|
+
"spdx-expression-parse": "catalog:",
|
|
887
|
+
npm: "catalog:",
|
|
888
|
+
tar: "catalog:",
|
|
889
|
+
}),
|
|
784
890
|
turbo: "catalog:",
|
|
785
891
|
"typescript-7": "catalog:",
|
|
786
892
|
},
|
|
@@ -847,6 +953,16 @@ function foundationPlan(options) {
|
|
|
847
953
|
};
|
|
848
954
|
const workflowOperations = [
|
|
849
955
|
workflowOperation,
|
|
956
|
+
...(publicationCandidate === undefined
|
|
957
|
+
? []
|
|
958
|
+
: [
|
|
959
|
+
{
|
|
960
|
+
kind: "copyFile",
|
|
961
|
+
source: templateSources.tsCli,
|
|
962
|
+
from: "publication/release.yml",
|
|
963
|
+
to: ".github/workflows/release.yml",
|
|
964
|
+
},
|
|
965
|
+
]),
|
|
850
966
|
{
|
|
851
967
|
kind: "writeTextTemplate",
|
|
852
968
|
source: templateSources.foundation,
|
|
@@ -866,6 +982,16 @@ function foundationPlan(options) {
|
|
|
866
982
|
nestedKeyOrder: packageConditionKeyOrder,
|
|
867
983
|
},
|
|
868
984
|
workspaceOperation,
|
|
985
|
+
...(requiresPackingHook
|
|
986
|
+
? [
|
|
987
|
+
{
|
|
988
|
+
kind: "copyFile",
|
|
989
|
+
source: templateSources.foundation,
|
|
990
|
+
from: ".pnpmfile.mjs",
|
|
991
|
+
to: ".pnpmfile.mjs",
|
|
992
|
+
},
|
|
993
|
+
]
|
|
994
|
+
: []),
|
|
869
995
|
{
|
|
870
996
|
kind: "copyFile",
|
|
871
997
|
source: templateSources.foundation,
|
|
@@ -878,6 +1004,16 @@ function foundationPlan(options) {
|
|
|
878
1004
|
from: "turbo.json",
|
|
879
1005
|
to: "turbo.json",
|
|
880
1006
|
},
|
|
1007
|
+
...(publicationCandidate === undefined
|
|
1008
|
+
? []
|
|
1009
|
+
: [
|
|
1010
|
+
{
|
|
1011
|
+
kind: "mergeJsonTemplate",
|
|
1012
|
+
source: templateSources.tsCli,
|
|
1013
|
+
from: "publication/turbo.json",
|
|
1014
|
+
to: "turbo.json",
|
|
1015
|
+
},
|
|
1016
|
+
]),
|
|
881
1017
|
...turboBoundaryTags.map((tag) => ({
|
|
882
1018
|
kind: "mergeJsonTemplate",
|
|
883
1019
|
source: templateSources.foundation,
|
|
@@ -902,6 +1038,27 @@ function foundationPlan(options) {
|
|
|
902
1038
|
from: "tsconfig.json",
|
|
903
1039
|
to: "tsconfig.json",
|
|
904
1040
|
},
|
|
1041
|
+
...(publicationCandidate === undefined
|
|
1042
|
+
? []
|
|
1043
|
+
: [
|
|
1044
|
+
{
|
|
1045
|
+
kind: "mergeJsonTemplate",
|
|
1046
|
+
source: templateSources.tsCli,
|
|
1047
|
+
from: "publication/root-tsconfig.json",
|
|
1048
|
+
to: "tsconfig.json",
|
|
1049
|
+
multilineArrays: ["include"],
|
|
1050
|
+
},
|
|
1051
|
+
]),
|
|
1052
|
+
...(requiresPackingHook && publicationCandidate === undefined
|
|
1053
|
+
? [
|
|
1054
|
+
{
|
|
1055
|
+
kind: "mergeJsonTemplate",
|
|
1056
|
+
source: templateSources.foundation,
|
|
1057
|
+
from: "tsconfig.packing-hook.json",
|
|
1058
|
+
to: "tsconfig.json",
|
|
1059
|
+
},
|
|
1060
|
+
]
|
|
1061
|
+
: []),
|
|
905
1062
|
{
|
|
906
1063
|
kind: "copyFile",
|
|
907
1064
|
source: templateSources.sharedOxc,
|
|
@@ -920,6 +1077,135 @@ function foundationPlan(options) {
|
|
|
920
1077
|
from: "oxfmt.config.ts",
|
|
921
1078
|
to: "oxfmt.config.ts",
|
|
922
1079
|
},
|
|
1080
|
+
...(publicationCandidate === undefined
|
|
1081
|
+
? []
|
|
1082
|
+
: [
|
|
1083
|
+
{
|
|
1084
|
+
kind: "copyFile",
|
|
1085
|
+
source: templateSources.tsCli,
|
|
1086
|
+
from: "publication/readiness.ts",
|
|
1087
|
+
to: "scripts/npm-publication/readiness.ts",
|
|
1088
|
+
},
|
|
1089
|
+
{
|
|
1090
|
+
kind: "copyFile",
|
|
1091
|
+
source: templateSources.tsCli,
|
|
1092
|
+
from: "publication/artifact.ts",
|
|
1093
|
+
to: "scripts/npm-publication/artifact.ts",
|
|
1094
|
+
},
|
|
1095
|
+
{
|
|
1096
|
+
kind: "copyFile",
|
|
1097
|
+
source: templateSources.tsCli,
|
|
1098
|
+
from: "publication/handoff.ts",
|
|
1099
|
+
to: "scripts/npm-publication/handoff.ts",
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
kind: "writeTextTemplate",
|
|
1103
|
+
source: templateSources.tsCli,
|
|
1104
|
+
from: "publication/check-artifact.ts",
|
|
1105
|
+
to: "scripts/npm-publication/check-artifact.ts",
|
|
1106
|
+
replacements: {
|
|
1107
|
+
PUBLIC_CLI_PACKAGE_PATH: publicationCandidate.definition.path,
|
|
1108
|
+
},
|
|
1109
|
+
},
|
|
1110
|
+
{
|
|
1111
|
+
kind: "writeTextTemplate",
|
|
1112
|
+
source: templateSources.tsCli,
|
|
1113
|
+
from: "publication/publish.ts",
|
|
1114
|
+
to: "scripts/npm-publication/publish.ts",
|
|
1115
|
+
replacements: {
|
|
1116
|
+
PUBLIC_CLI_PACKAGE_PATH: publicationCandidate.definition.path,
|
|
1117
|
+
},
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
kind: "writeTextTemplate",
|
|
1121
|
+
source: templateSources.tsCli,
|
|
1122
|
+
from: "publication/release.ts",
|
|
1123
|
+
to: "scripts/npm-publication/release.ts",
|
|
1124
|
+
replacements: {
|
|
1125
|
+
PUBLIC_CLI_PACKAGE_PATH: publicationCandidate.definition.path,
|
|
1126
|
+
},
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
kind: "writeTextTemplate",
|
|
1130
|
+
source: templateSources.tsCli,
|
|
1131
|
+
from: "publication/check-readiness.ts",
|
|
1132
|
+
to: "scripts/npm-publication/check-readiness.ts",
|
|
1133
|
+
replacements: {
|
|
1134
|
+
PUBLIC_CLI_PACKAGE_PATH: publicationCandidate.definition.path,
|
|
1135
|
+
},
|
|
1136
|
+
},
|
|
1137
|
+
{
|
|
1138
|
+
kind: "copyFile",
|
|
1139
|
+
source: templateSources.tsCli,
|
|
1140
|
+
from: "publication/changelog.ts",
|
|
1141
|
+
to: "scripts/npm-publication/changelog.ts",
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
kind: "copyFile",
|
|
1145
|
+
source: templateSources.tsCli,
|
|
1146
|
+
from: "publication/RELEASING.md",
|
|
1147
|
+
to: "RELEASING.md",
|
|
1148
|
+
},
|
|
1149
|
+
{
|
|
1150
|
+
kind: "copyFile",
|
|
1151
|
+
source: templateSources.tsCli,
|
|
1152
|
+
from: "src/cli-command-identity.ts",
|
|
1153
|
+
to: "scripts/npm-publication/cli-command-identity.ts",
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
kind: "writeTextTemplate",
|
|
1157
|
+
source: templateSources.tsCli,
|
|
1158
|
+
from: "publication-setup/setup.sh",
|
|
1159
|
+
to: "scripts/npm-publication-setup/setup.sh",
|
|
1160
|
+
replacements: {
|
|
1161
|
+
PUBLIC_CLI_PACKAGE_PATH: publicationCandidate.definition.path,
|
|
1162
|
+
},
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
kind: "setExecutable",
|
|
1166
|
+
path: "scripts/npm-publication-setup/setup.sh",
|
|
1167
|
+
executable: true,
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
kind: "copyFile",
|
|
1171
|
+
source: templateSources.tsCli,
|
|
1172
|
+
from: "publication-setup/README.md",
|
|
1173
|
+
to: "scripts/npm-publication-setup/README.md",
|
|
1174
|
+
},
|
|
1175
|
+
{
|
|
1176
|
+
kind: "writeTextTemplate",
|
|
1177
|
+
source: templateSources.tsCli,
|
|
1178
|
+
from: "publication-setup/bridge.ts",
|
|
1179
|
+
to: "scripts/npm-publication-setup/bridge.ts",
|
|
1180
|
+
replacements: {
|
|
1181
|
+
PUBLIC_CLI_PACKAGE_PATH: publicationCandidate.definition.path,
|
|
1182
|
+
},
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
kind: "copyFile",
|
|
1186
|
+
source: templateSources.tsCli,
|
|
1187
|
+
from: "publication-setup/assets/LICENSE-MIT.txt",
|
|
1188
|
+
to: "scripts/npm-publication-setup/assets/LICENSE-MIT.txt",
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
kind: "copyFile",
|
|
1192
|
+
source: templateSources.tsCli,
|
|
1193
|
+
from: "publication-setup/assets/LICENSE-APACHE-2.0.txt",
|
|
1194
|
+
to: "scripts/npm-publication-setup/assets/LICENSE-APACHE-2.0.txt",
|
|
1195
|
+
},
|
|
1196
|
+
{
|
|
1197
|
+
kind: "copyFile",
|
|
1198
|
+
source: templateSources.tsCli,
|
|
1199
|
+
from: "publication-setup/assets/README.md.template",
|
|
1200
|
+
to: "scripts/npm-publication-setup/assets/README.md.template",
|
|
1201
|
+
},
|
|
1202
|
+
{
|
|
1203
|
+
kind: "copyFile",
|
|
1204
|
+
source: templateSources.tsCli,
|
|
1205
|
+
from: "publication-setup/assets/CHANGELOG.md.template",
|
|
1206
|
+
to: "scripts/npm-publication-setup/assets/CHANGELOG.md.template",
|
|
1207
|
+
},
|
|
1208
|
+
]),
|
|
923
1209
|
{
|
|
924
1210
|
kind: "writeJson",
|
|
925
1211
|
to: ".vscode/extensions.json",
|
|
@@ -1008,6 +1294,7 @@ function foundationPlan(options) {
|
|
|
1008
1294
|
...linkOperations.map((operation) => withProvenance(operation, foundationProvenance)),
|
|
1009
1295
|
];
|
|
1010
1296
|
const reconciliation = [
|
|
1297
|
+
{ path: "tsconfig.json", driver: "structured" },
|
|
1011
1298
|
{ path: "turbo.json", driver: "structured" },
|
|
1012
1299
|
{
|
|
1013
1300
|
path: ".devcontainer/devcontainer.json",
|
|
@@ -1036,6 +1323,7 @@ function foundationPlan(options) {
|
|
|
1036
1323
|
: "planInitialization",
|
|
1037
1324
|
blueprint: options.blueprint,
|
|
1038
1325
|
generationRecord,
|
|
1326
|
+
packageContributions,
|
|
1039
1327
|
operations,
|
|
1040
1328
|
reconciliation,
|
|
1041
1329
|
developmentContainer: {
|
|
@@ -1057,40 +1345,342 @@ function foundationPlan(options) {
|
|
|
1057
1345
|
],
|
|
1058
1346
|
};
|
|
1059
1347
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1348
|
+
function hasConfigurablePrimaryPackage(definition) {
|
|
1349
|
+
return definition.initialPrimaryPackage !== undefined;
|
|
1350
|
+
}
|
|
1351
|
+
function errorDiagnostics(error) {
|
|
1352
|
+
return (error instanceof Error ? error.message : String(error))
|
|
1353
|
+
.split("\n")
|
|
1354
|
+
.filter((diagnostic) => diagnostic.length > 0);
|
|
1355
|
+
}
|
|
1356
|
+
function preparePresetInitialization(options) {
|
|
1357
|
+
if (!hasConfigurablePrimaryPackage(options.definition)) {
|
|
1358
|
+
const diagnostics = [];
|
|
1359
|
+
let presetBlueprint;
|
|
1360
|
+
let contributions;
|
|
1361
|
+
try {
|
|
1362
|
+
presetBlueprint = assertProjectBlueprintDraft(options.definition.blueprint(options.context));
|
|
1363
|
+
}
|
|
1364
|
+
catch (error) {
|
|
1365
|
+
diagnostics.push(...errorDiagnostics(error));
|
|
1366
|
+
}
|
|
1367
|
+
try {
|
|
1368
|
+
contributions = options.definition.planInitializationContributions?.(options.context) ?? [options.definition.planInitialization(options.context)];
|
|
1369
|
+
}
|
|
1370
|
+
catch (error) {
|
|
1371
|
+
diagnostics.push(...errorDiagnostics(error));
|
|
1372
|
+
}
|
|
1373
|
+
if (diagnostics.length > 0)
|
|
1374
|
+
throw new Error(diagnostics.join("\n"));
|
|
1375
|
+
return {
|
|
1376
|
+
presetBlueprint: presetBlueprint,
|
|
1377
|
+
contributions: contributions,
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
const capability = options.definition.initialPrimaryPackage;
|
|
1381
|
+
const leafName = options.overrides?.name ?? capability.defaultLeafName;
|
|
1382
|
+
const resolvedPackageIdentity = {
|
|
1383
|
+
leafName,
|
|
1384
|
+
definition: {
|
|
1385
|
+
name: `@${options.context.defaultPackageScope}/${leafName}`,
|
|
1386
|
+
path: options.overrides?.path ??
|
|
1387
|
+
capability.defaultPackagePath({ packageLeafName: leafName }),
|
|
1388
|
+
role: capability.role,
|
|
1389
|
+
},
|
|
1390
|
+
};
|
|
1391
|
+
const diagnostics = [];
|
|
1392
|
+
let presetBlueprint;
|
|
1393
|
+
let contribution;
|
|
1394
|
+
try {
|
|
1395
|
+
presetBlueprint = assertProjectBlueprintDraft({
|
|
1396
|
+
schemaVersion: 3,
|
|
1397
|
+
packages: [resolvedPackageIdentity.definition],
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
catch (error) {
|
|
1401
|
+
diagnostics.push(...errorDiagnostics(error));
|
|
1402
|
+
}
|
|
1403
|
+
try {
|
|
1404
|
+
contribution = capability.planInitialContribution({
|
|
1405
|
+
context: options.context,
|
|
1406
|
+
resolvedPackageIdentity,
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
catch (error) {
|
|
1410
|
+
diagnostics.push(...errorDiagnostics(error));
|
|
1411
|
+
}
|
|
1412
|
+
if (diagnostics.length > 0)
|
|
1413
|
+
throw new Error(diagnostics.join("\n"));
|
|
1414
|
+
return {
|
|
1415
|
+
presetBlueprint: presetBlueprint,
|
|
1416
|
+
contributions: [contribution],
|
|
1417
|
+
resolvedPackageIdentity,
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
function planPreparedRepositoryInitialization(options) {
|
|
1421
|
+
const { presetBlueprint, contributions } = options;
|
|
1062
1422
|
const configDefinition = typescriptConfigPackageDefinition(options.context);
|
|
1063
|
-
|
|
1064
|
-
definition.
|
|
1065
|
-
|
|
1423
|
+
const foundationIdentityDiagnostics = presetBlueprint.packages.flatMap((definition) => [
|
|
1424
|
+
...(definition.name === configDefinition.name
|
|
1425
|
+
? [
|
|
1426
|
+
`Initial package name ${definition.name} conflicts with Foundation package ${configDefinition.name}`,
|
|
1427
|
+
]
|
|
1428
|
+
: []),
|
|
1429
|
+
...(definition.path === configDefinition.path
|
|
1430
|
+
? [
|
|
1431
|
+
`Initial Package Path ${definition.path} conflicts with Foundation Package Path ${configDefinition.path}`,
|
|
1432
|
+
]
|
|
1433
|
+
: []),
|
|
1434
|
+
]);
|
|
1435
|
+
if (foundationIdentityDiagnostics.length > 0) {
|
|
1436
|
+
throw new Error(foundationIdentityDiagnostics.join("\n"));
|
|
1066
1437
|
}
|
|
1438
|
+
const occupiedIds = new Set();
|
|
1067
1439
|
const blueprint = {
|
|
1068
1440
|
...presetBlueprint,
|
|
1069
|
-
packages: [
|
|
1441
|
+
packages: [
|
|
1442
|
+
...presetBlueprint.packages.map((definition) => {
|
|
1443
|
+
const contribution = contributions.find((candidate) => packageDefinitionsEqual(candidate.definition, definition));
|
|
1444
|
+
if (contribution === undefined) {
|
|
1445
|
+
throw new Error(`Preset Blueprint Package Definition ${definition.path} has no initialization Package Contribution`);
|
|
1446
|
+
}
|
|
1447
|
+
return persistPackageDefinition({
|
|
1448
|
+
definition,
|
|
1449
|
+
provenance: {
|
|
1450
|
+
definitionName: options.definition.metadata.name,
|
|
1451
|
+
planningContribution: "planInitialization",
|
|
1452
|
+
contributionIdentity: requireReplayAdapter({
|
|
1453
|
+
owner: options.definition.metadata.name,
|
|
1454
|
+
adapters: options.definition.packageContributionReplayAdapters,
|
|
1455
|
+
identity: contribution.planningIdentity,
|
|
1456
|
+
}).identity,
|
|
1457
|
+
},
|
|
1458
|
+
occupiedIds,
|
|
1459
|
+
});
|
|
1460
|
+
}),
|
|
1461
|
+
persistPackageDefinition({
|
|
1462
|
+
definition: configDefinition,
|
|
1463
|
+
provenance: {
|
|
1464
|
+
definitionName: "foundation",
|
|
1465
|
+
planningContribution: "foundationPlan",
|
|
1466
|
+
contributionIdentity: typescriptConfigReplayAdapter.identity,
|
|
1467
|
+
},
|
|
1468
|
+
occupiedIds,
|
|
1469
|
+
}),
|
|
1470
|
+
],
|
|
1070
1471
|
};
|
|
1071
|
-
const contributions = options.definition.planInitializationContributions?.(options.context) ?? [options.definition.planInitialization(options.context)];
|
|
1072
1472
|
return foundationPlan({
|
|
1073
1473
|
definition: options.definition,
|
|
1074
1474
|
context: options.context,
|
|
1075
1475
|
blueprint,
|
|
1476
|
+
foundationContribution: typescriptConfigReplayAdapter.identify(typescriptConfigContribution(options.context, configDefinition)),
|
|
1076
1477
|
contributions,
|
|
1077
1478
|
mode: "initialization",
|
|
1078
1479
|
});
|
|
1079
1480
|
}
|
|
1481
|
+
function assertPreflightedInitializationPlan(plan) {
|
|
1482
|
+
const projectionDiagnostics = validateProjectProjectionPlan({
|
|
1483
|
+
operations: plan.operations,
|
|
1484
|
+
reconciliation: plan.reconciliation,
|
|
1485
|
+
});
|
|
1486
|
+
if (projectionDiagnostics.length > 0) {
|
|
1487
|
+
throw new Error(projectionDiagnostics.join("\n"));
|
|
1488
|
+
}
|
|
1489
|
+
return plan;
|
|
1490
|
+
}
|
|
1491
|
+
export function planGeneratedRepositoryInitialization(options) {
|
|
1492
|
+
const prepared = preparePresetInitialization(options);
|
|
1493
|
+
return assertPreflightedInitializationPlan(planPreparedRepositoryInitialization({
|
|
1494
|
+
definition: options.definition,
|
|
1495
|
+
context: options.context,
|
|
1496
|
+
presetBlueprint: prepared.presetBlueprint,
|
|
1497
|
+
contributions: prepared.contributions,
|
|
1498
|
+
}));
|
|
1499
|
+
}
|
|
1500
|
+
export function prepareGeneratedRepositoryInitialization(options) {
|
|
1501
|
+
const diagnostics = [];
|
|
1502
|
+
const appendDiagnostics = (error) => {
|
|
1503
|
+
diagnostics.push(...errorDiagnostics(error));
|
|
1504
|
+
};
|
|
1505
|
+
const scopeOverride = options.overrides?.scope;
|
|
1506
|
+
const normalizedScope = scopeOverride?.startsWith("@") === true
|
|
1507
|
+
? scopeOverride.slice(1)
|
|
1508
|
+
: scopeOverride;
|
|
1509
|
+
const nameOverride = options.overrides?.name;
|
|
1510
|
+
const pathOverride = options.overrides?.path;
|
|
1511
|
+
const repositoryName = path.basename(path.resolve(options.targetDir));
|
|
1512
|
+
const configurablePrimaryPackage = options.definition.initialPrimaryPackage !== undefined;
|
|
1513
|
+
if (!configurablePrimaryPackage &&
|
|
1514
|
+
(nameOverride !== undefined || pathOverride !== undefined)) {
|
|
1515
|
+
diagnostics.push(`Built-in Preset ${options.definition.metadata.name} has fixed initial package topology and does not accept --name or --path`);
|
|
1516
|
+
}
|
|
1517
|
+
let validNameOverride = nameOverride !== undefined &&
|
|
1518
|
+
isValidNewNpmPackageName(`@x/${nameOverride}`);
|
|
1519
|
+
if (nameOverride !== undefined && !validNameOverride) {
|
|
1520
|
+
diagnostics.push("--name must be an unscoped package leaf name");
|
|
1521
|
+
}
|
|
1522
|
+
let validPathOverride = pathOverride !== undefined;
|
|
1523
|
+
if (pathOverride !== undefined) {
|
|
1524
|
+
const pathValidation = validateNewPackagePath(pathOverride);
|
|
1525
|
+
if (!pathValidation.hasValidShape) {
|
|
1526
|
+
diagnostics.push("--path must be exactly two safe path segments");
|
|
1527
|
+
validPathOverride = false;
|
|
1528
|
+
}
|
|
1529
|
+
if (pathValidation.reservedWorkspaceCollection !== undefined) {
|
|
1530
|
+
diagnostics.push(`--path ${pathOverride} uses reserved workspace collection ${pathValidation.reservedWorkspaceCollection}`);
|
|
1531
|
+
validPathOverride = false;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
let validScopeOverride = normalizedScope !== undefined &&
|
|
1535
|
+
scopeOverride === scopeOverride?.trim() &&
|
|
1536
|
+
isValidDefaultPackageScope(normalizedScope) &&
|
|
1537
|
+
isValidNewNpmPackageName(`@${normalizedScope}/typescript-config`);
|
|
1538
|
+
if (normalizedScope !== undefined && !validScopeOverride) {
|
|
1539
|
+
diagnostics.push("--scope must be a valid npm scope without whitespace");
|
|
1540
|
+
}
|
|
1541
|
+
let validRepositoryScope = isValidDefaultPackageScope(repositoryName) &&
|
|
1542
|
+
isValidNewNpmPackageName(`@${repositoryName}/typescript-config`);
|
|
1543
|
+
if (normalizedScope === undefined && !validRepositoryScope) {
|
|
1544
|
+
diagnostics.push(`Repository Identity ${repositoryName} is not a valid default package scope; pass --scope with a valid npm scope`);
|
|
1545
|
+
}
|
|
1546
|
+
const resolvedScope = validScopeOverride
|
|
1547
|
+
? (normalizedScope ?? "repository")
|
|
1548
|
+
: validRepositoryScope
|
|
1549
|
+
? repositoryName
|
|
1550
|
+
: "repository";
|
|
1551
|
+
const resolvedLeaf = configurablePrimaryPackage &&
|
|
1552
|
+
nameOverride !== undefined &&
|
|
1553
|
+
validNameOverride
|
|
1554
|
+
? nameOverride
|
|
1555
|
+
: configurablePrimaryPackage
|
|
1556
|
+
? options.definition.initialPrimaryPackage?.defaultLeafName
|
|
1557
|
+
: undefined;
|
|
1558
|
+
if (resolvedLeaf !== undefined &&
|
|
1559
|
+
!isValidNewNpmPackageName(`@${resolvedScope}/${resolvedLeaf}`)) {
|
|
1560
|
+
if (nameOverride !== undefined) {
|
|
1561
|
+
validNameOverride = false;
|
|
1562
|
+
diagnostics.push("--name must be an unscoped package leaf name");
|
|
1563
|
+
}
|
|
1564
|
+
else if (scopeOverride !== undefined) {
|
|
1565
|
+
validScopeOverride = false;
|
|
1566
|
+
diagnostics.push("--scope must be a valid npm scope without whitespace");
|
|
1567
|
+
}
|
|
1568
|
+
else {
|
|
1569
|
+
validRepositoryScope = false;
|
|
1570
|
+
diagnostics.push(`Repository Identity ${repositoryName} is not a valid default package scope; pass --scope with a valid npm scope`);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
const context = createGenerationContext({
|
|
1574
|
+
targetDir: options.targetDir,
|
|
1575
|
+
defaultPackageScope: validScopeOverride
|
|
1576
|
+
? (normalizedScope ?? "repository")
|
|
1577
|
+
: validRepositoryScope
|
|
1578
|
+
? repositoryName
|
|
1579
|
+
: "repository",
|
|
1580
|
+
toolchain: options.toolchain,
|
|
1581
|
+
});
|
|
1582
|
+
const safeNameOverride = configurablePrimaryPackage && nameOverride !== undefined
|
|
1583
|
+
? validNameOverride
|
|
1584
|
+
? nameOverride
|
|
1585
|
+
: "x"
|
|
1586
|
+
: undefined;
|
|
1587
|
+
let safeDerivedPathOverride;
|
|
1588
|
+
const initialPrimaryPackage = options.definition.initialPrimaryPackage;
|
|
1589
|
+
if (initialPrimaryPackage !== undefined && pathOverride === undefined) {
|
|
1590
|
+
const safeLeafName = safeNameOverride ?? initialPrimaryPackage.defaultLeafName;
|
|
1591
|
+
const derivedPackagePath = initialPrimaryPackage.defaultPackagePath({
|
|
1592
|
+
packageLeafName: safeLeafName,
|
|
1593
|
+
});
|
|
1594
|
+
const derivedPathValidation = validateNewPackagePath(derivedPackagePath);
|
|
1595
|
+
if (!derivedPathValidation.hasValidShape ||
|
|
1596
|
+
derivedPathValidation.reservedWorkspaceCollection !== undefined) {
|
|
1597
|
+
diagnostics.push(`Preset-derived Package Path ${derivedPackagePath} is unsafe; pass --path with exactly two safe path segments`);
|
|
1598
|
+
safeDerivedPathOverride = "packages/invalid";
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
const safeIdentityOverrides = {
|
|
1602
|
+
...(safeNameOverride === undefined ? {} : { name: safeNameOverride }),
|
|
1603
|
+
...(configurablePrimaryPackage && pathOverride !== undefined
|
|
1604
|
+
? { path: validPathOverride ? pathOverride : "packages/invalid" }
|
|
1605
|
+
: safeDerivedPathOverride === undefined
|
|
1606
|
+
? {}
|
|
1607
|
+
: { path: safeDerivedPathOverride }),
|
|
1608
|
+
};
|
|
1609
|
+
let prepared;
|
|
1610
|
+
try {
|
|
1611
|
+
prepared = preparePresetInitialization({
|
|
1612
|
+
definition: options.definition,
|
|
1613
|
+
context,
|
|
1614
|
+
...(Object.keys(safeIdentityOverrides).length === 0
|
|
1615
|
+
? {}
|
|
1616
|
+
: { overrides: safeIdentityOverrides }),
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
catch (error) {
|
|
1620
|
+
appendDiagnostics(error);
|
|
1621
|
+
}
|
|
1622
|
+
let plan;
|
|
1623
|
+
if (prepared !== undefined) {
|
|
1624
|
+
try {
|
|
1625
|
+
plan = planPreparedRepositoryInitialization({
|
|
1626
|
+
definition: options.definition,
|
|
1627
|
+
context,
|
|
1628
|
+
presetBlueprint: prepared.presetBlueprint,
|
|
1629
|
+
contributions: prepared.contributions,
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
catch (error) {
|
|
1633
|
+
appendDiagnostics(error);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
if (plan !== undefined) {
|
|
1637
|
+
diagnostics.push(...validateProjectProjectionPlan({
|
|
1638
|
+
operations: plan.operations,
|
|
1639
|
+
reconciliation: plan.reconciliation,
|
|
1640
|
+
}));
|
|
1641
|
+
}
|
|
1642
|
+
if (diagnostics.length > 0)
|
|
1643
|
+
throw new Error(diagnostics.join("\n"));
|
|
1644
|
+
if (prepared === undefined || plan === undefined) {
|
|
1645
|
+
throw new Error("Initialization preparation did not produce a plan");
|
|
1646
|
+
}
|
|
1647
|
+
return {
|
|
1648
|
+
context,
|
|
1649
|
+
resolved: {
|
|
1650
|
+
preset: options.definition.metadata.name,
|
|
1651
|
+
topology: prepared.resolvedPackageIdentity === undefined
|
|
1652
|
+
? "fixed"
|
|
1653
|
+
: "configurable-primary-package",
|
|
1654
|
+
packages: prepared.presetBlueprint.packages.map(({ name, path }) => ({
|
|
1655
|
+
name,
|
|
1656
|
+
path,
|
|
1657
|
+
})),
|
|
1658
|
+
scope: context.defaultPackageScope,
|
|
1659
|
+
},
|
|
1660
|
+
...(prepared.resolvedPackageIdentity === undefined
|
|
1661
|
+
? {}
|
|
1662
|
+
: { resolvedPackageIdentity: prepared.resolvedPackageIdentity }),
|
|
1663
|
+
plan,
|
|
1664
|
+
publicationSetup: publicCliCandidate(prepared.contributions) === undefined
|
|
1665
|
+
? null
|
|
1666
|
+
: { command: "./scripts/npm-publication-setup/setup.sh" },
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1080
1669
|
export function planGeneratedRepositoryPackageAddition(options) {
|
|
1081
|
-
|
|
1670
|
+
const { blueprint: persistedBlueprint, context } = options.localTemplateMetadata;
|
|
1671
|
+
assertProjectBlueprint(persistedBlueprint);
|
|
1082
1672
|
if (!options.definition.planPackageAddition)
|
|
1083
1673
|
throw new Error(`Built-in Preset ${options.definition.metadata.name} does not support Package Addition`);
|
|
1084
1674
|
const packagePath = options.packagePath ??
|
|
1085
1675
|
options.definition.defaultPackagePath?.({
|
|
1086
|
-
context
|
|
1676
|
+
context,
|
|
1087
1677
|
packageLeafName: options.packageLeafName,
|
|
1088
1678
|
});
|
|
1089
1679
|
if (packagePath === undefined) {
|
|
1090
1680
|
throw new Error(`Built-in Preset ${options.definition.metadata.name} must own a default Package Path or receive an explicit Package Path`);
|
|
1091
1681
|
}
|
|
1092
1682
|
const contribution = options.definition.planPackageAddition({
|
|
1093
|
-
context
|
|
1683
|
+
context,
|
|
1094
1684
|
packageLeafName: options.packageLeafName,
|
|
1095
1685
|
packagePath,
|
|
1096
1686
|
});
|
|
@@ -1098,28 +1688,31 @@ export function planGeneratedRepositoryPackageAddition(options) {
|
|
|
1098
1688
|
consumerPackagePath,
|
|
1099
1689
|
providerPackagePath: contribution.definition.path,
|
|
1100
1690
|
}));
|
|
1101
|
-
const conflictingPackage =
|
|
1691
|
+
const conflictingPackage = persistedBlueprint.packages.find((existing) => existing.name === contribution.definition.name ||
|
|
1102
1692
|
existing.path === contribution.definition.path);
|
|
1103
1693
|
if (conflictingPackage !== undefined) {
|
|
1104
1694
|
const isExactPackageDefinition = packageDefinitionsEqual(conflictingPackage, contribution.definition);
|
|
1105
|
-
const existingPackageLinkIntents =
|
|
1695
|
+
const existingPackageLinkIntents = persistedBlueprint.packageLinkIntents ?? [];
|
|
1106
1696
|
const missingPackageLinkIntent = requestedPackageLinkIntents.find((requested) => !existingPackageLinkIntents.some((existing) => packageLinkIntentsEqual(existing, requested)));
|
|
1107
1697
|
if (isExactPackageDefinition && missingPackageLinkIntent === undefined) {
|
|
1108
1698
|
const existing = readExistingPackageAdditionState(options);
|
|
1109
1699
|
const plan = foundationPlan({
|
|
1110
1700
|
definition: options.definition,
|
|
1111
|
-
context
|
|
1112
|
-
blueprint:
|
|
1701
|
+
context,
|
|
1702
|
+
blueprint: persistedBlueprint,
|
|
1703
|
+
foundationContribution: existing.foundationContribution,
|
|
1113
1704
|
contributions: existing.contributions,
|
|
1114
1705
|
renderContributions: [],
|
|
1115
1706
|
existingDeploymentEnvironmentNeeds: existing.deploymentEnvironmentNeeds,
|
|
1707
|
+
manifestTruthByPackagePath: existing.manifestTruthByPackagePath,
|
|
1116
1708
|
generationRecord: existing.generationRecord,
|
|
1117
1709
|
mode: "addition",
|
|
1118
1710
|
});
|
|
1119
1711
|
const currentProjection = foundationPlan({
|
|
1120
1712
|
definition: options.definition,
|
|
1121
|
-
context
|
|
1122
|
-
blueprint:
|
|
1713
|
+
context,
|
|
1714
|
+
blueprint: persistedBlueprint,
|
|
1715
|
+
foundationContribution: existing.foundationContribution,
|
|
1123
1716
|
contributions: existing.contributions,
|
|
1124
1717
|
existingDeploymentEnvironmentNeeds: existing.deploymentEnvironmentNeeds,
|
|
1125
1718
|
generationRecord: existing.generationRecord,
|
|
@@ -1146,47 +1739,64 @@ export function planGeneratedRepositoryPackageAddition(options) {
|
|
|
1146
1739
|
}
|
|
1147
1740
|
throw new Error(`Package Addition conflicts with existing Package Definition ${conflictingPackage.name} at ${conflictingPackage.path} (${conflictingPackage.role}); requested ${contribution.definition.name} at ${contribution.definition.path} (${contribution.definition.role})`);
|
|
1148
1741
|
}
|
|
1742
|
+
const contributionIdentity = requireReplayAdapter({
|
|
1743
|
+
owner: options.definition.metadata.name,
|
|
1744
|
+
adapters: options.definition.packageContributionReplayAdapters,
|
|
1745
|
+
identity: contribution.planningIdentity,
|
|
1746
|
+
}).identity;
|
|
1747
|
+
const persistedContributionDefinition = persistPackageDefinition({
|
|
1748
|
+
definition: contribution.definition,
|
|
1749
|
+
provenance: {
|
|
1750
|
+
definitionName: options.definition.metadata.name,
|
|
1751
|
+
planningContribution: "planPackageAddition",
|
|
1752
|
+
contributionIdentity,
|
|
1753
|
+
},
|
|
1754
|
+
occupiedIds: new Set(persistedBlueprint.packages.map((definition) => definition.packageDefinitionId)),
|
|
1755
|
+
});
|
|
1149
1756
|
const blueprint = {
|
|
1150
|
-
...
|
|
1151
|
-
packages: [...
|
|
1757
|
+
...persistedBlueprint,
|
|
1758
|
+
packages: [...persistedBlueprint.packages, persistedContributionDefinition],
|
|
1152
1759
|
...(requestedPackageLinkIntents.length > 0
|
|
1153
1760
|
? {
|
|
1154
1761
|
packageLinkIntents: [
|
|
1155
|
-
...(
|
|
1762
|
+
...(persistedBlueprint.packageLinkIntents ?? []),
|
|
1156
1763
|
...requestedPackageLinkIntents,
|
|
1157
1764
|
],
|
|
1158
1765
|
}
|
|
1159
1766
|
: {}),
|
|
1160
1767
|
};
|
|
1161
|
-
|
|
1162
|
-
const
|
|
1768
|
+
assertProjectBlueprint(blueprint);
|
|
1769
|
+
const requiredManifestTruthPackagePaths = requestedPackageLinkIntents.length === 0
|
|
1163
1770
|
? []
|
|
1164
1771
|
: [
|
|
1165
|
-
...(
|
|
1772
|
+
...(persistedBlueprint.packageLinkIntents ?? []).flatMap((intent) => [
|
|
1166
1773
|
intent.consumerPackagePath,
|
|
1167
1774
|
intent.providerPackagePath,
|
|
1168
1775
|
]),
|
|
1169
1776
|
...requestedPackageLinkIntents.map((intent) => intent.consumerPackagePath),
|
|
1170
1777
|
];
|
|
1171
1778
|
const existing = readExistingPackageAdditionState({
|
|
1172
|
-
|
|
1173
|
-
|
|
1779
|
+
localTemplateMetadata: options.localTemplateMetadata,
|
|
1780
|
+
requiredManifestTruthPackagePaths,
|
|
1174
1781
|
});
|
|
1175
1782
|
const generationRecord = {
|
|
1176
1783
|
...existing.generationRecord,
|
|
1177
1784
|
packages: [
|
|
1178
1785
|
...existing.generationRecord.packages,
|
|
1179
1786
|
{
|
|
1787
|
+
packageDefinitionId: persistedContributionDefinition.packageDefinitionId,
|
|
1180
1788
|
path: contribution.definition.path,
|
|
1181
1789
|
definitionName: options.definition.metadata.name,
|
|
1182
1790
|
planningContribution: "planPackageAddition",
|
|
1791
|
+
contributionIdentity,
|
|
1183
1792
|
},
|
|
1184
1793
|
],
|
|
1185
1794
|
};
|
|
1186
1795
|
const beforeProjection = foundationPlan({
|
|
1187
1796
|
definition: options.definition,
|
|
1188
|
-
context
|
|
1189
|
-
blueprint:
|
|
1797
|
+
context,
|
|
1798
|
+
blueprint: persistedBlueprint,
|
|
1799
|
+
foundationContribution: existing.foundationContribution,
|
|
1190
1800
|
contributions: existing.contributions,
|
|
1191
1801
|
existingDeploymentEnvironmentNeeds: existing.deploymentEnvironmentNeeds,
|
|
1192
1802
|
generationRecord: existing.generationRecord,
|
|
@@ -1194,30 +1804,24 @@ export function planGeneratedRepositoryPackageAddition(options) {
|
|
|
1194
1804
|
});
|
|
1195
1805
|
const afterProjection = foundationPlan({
|
|
1196
1806
|
definition: options.definition,
|
|
1197
|
-
context
|
|
1807
|
+
context,
|
|
1198
1808
|
blueprint,
|
|
1809
|
+
foundationContribution: existing.foundationContribution,
|
|
1199
1810
|
contributions: [...existing.contributions, contribution],
|
|
1200
1811
|
existingDeploymentEnvironmentNeeds: existing.deploymentEnvironmentNeeds,
|
|
1201
|
-
|
|
1202
|
-
? {}
|
|
1203
|
-
: {
|
|
1204
|
-
manifestTruthByPackagePath: existing.manifestTruthByPackagePath,
|
|
1205
|
-
}),
|
|
1812
|
+
manifestTruthByPackagePath: existing.manifestTruthByPackagePath,
|
|
1206
1813
|
generationRecord,
|
|
1207
1814
|
mode: "initialization",
|
|
1208
1815
|
});
|
|
1209
1816
|
const plan = foundationPlan({
|
|
1210
1817
|
definition: options.definition,
|
|
1211
|
-
context
|
|
1818
|
+
context,
|
|
1212
1819
|
blueprint,
|
|
1820
|
+
foundationContribution: existing.foundationContribution,
|
|
1213
1821
|
contributions: [...existing.contributions, contribution],
|
|
1214
1822
|
renderContributions: [contribution],
|
|
1215
1823
|
existingDeploymentEnvironmentNeeds: existing.deploymentEnvironmentNeeds,
|
|
1216
|
-
|
|
1217
|
-
? {}
|
|
1218
|
-
: {
|
|
1219
|
-
manifestTruthByPackagePath: existing.manifestTruthByPackagePath,
|
|
1220
|
-
}),
|
|
1824
|
+
manifestTruthByPackagePath: existing.manifestTruthByPackagePath,
|
|
1221
1825
|
generationRecord,
|
|
1222
1826
|
mode: "addition",
|
|
1223
1827
|
});
|