@ykdz/template 0.0.33 → 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 +970 -365
- 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/development-container.d.ts +1 -0
- package/node_modules/@ykdz/template-builtin-presets/dist/src/shared/development-container.js +17 -0
- 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 +1 -2
- 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 +61 -35
- 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 -8
- 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 -21
- 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/gitignore +1 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/foundation/tsconfig.packing-hook.json +8 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/shared/devcontainer/github-cli.Dockerfile +9 -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-builtin-presets/templates/vike-app/db/src/readiness.ts +1 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/test/global-setup.ts +21 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/tsconfig.json +6 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db/vitest.config.ts +7 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/drizzle.config.ts +5 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/playwright.config.ts +102 -22
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/test/playwright-teardown.ts +12 -0
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/vite.config.ts +39 -1
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/playwright.config.ts +48 -7
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/playwright.config.ts +59 -8
- 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 +3 -3
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/db-migrations/scripts/prepare-database.ts +0 -45
- package/node_modules/@ykdz/template-builtin-presets/templates/vike-app/web/scripts/run-playwright.ts +0 -151
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-app/scripts/run-playwright.ts +0 -62
- package/node_modules/@ykdz/template-builtin-presets/templates/vue-hono-app/web/scripts/run-playwright.ts +0 -103
- 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,14 +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
|
-
import {
|
|
18
|
+
import { githubCliDevelopmentContainerToolLayer } from "./shared/development-container.js";
|
|
19
|
+
import { typescriptConfigContribution, typescriptConfigPackageDefinition, typescriptConfigReplayAdapter, } from "./shared/typescript.js";
|
|
16
20
|
import { vuePnpmDependencyOverrides } from "./shared/vue.js";
|
|
17
21
|
import { templateSources } from "./template-sources.js";
|
|
18
22
|
import { tsCliDefinition } from "./ts-cli/definition.js";
|
|
@@ -20,6 +24,7 @@ import { tsLibDefinition } from "./ts-lib/definition.js";
|
|
|
20
24
|
import { vikeAppDefinition } from "./vike-app/definition.js";
|
|
21
25
|
import { vueAppDefinition } from "./vue-app/definition.js";
|
|
22
26
|
import { vueHonoAppDefinition } from "./vue-hono-app/definition.js";
|
|
27
|
+
const localTemplateMetadataStateKey = Symbol("localTemplateMetadataState");
|
|
23
28
|
const environmentNeedsPath = ".template/environment-needs.json";
|
|
24
29
|
const packageManifestKeyOrder = [
|
|
25
30
|
"name",
|
|
@@ -52,8 +57,8 @@ const packageConditionKeyOrder = ["source", "types", "default"];
|
|
|
52
57
|
export function resolveBuiltInTemplateSource(source, relativePath) {
|
|
53
58
|
return resolveTemplateSource(source, relativePath);
|
|
54
59
|
}
|
|
55
|
-
export function
|
|
56
|
-
return
|
|
60
|
+
export function validateProjectBlueprint(value) {
|
|
61
|
+
return validateCoreProjectBlueprint(value);
|
|
57
62
|
}
|
|
58
63
|
class PresetRegistry {
|
|
59
64
|
#definitions;
|
|
@@ -63,6 +68,14 @@ class PresetRegistry {
|
|
|
63
68
|
new Set(names).size !== names.length) {
|
|
64
69
|
throw new Error("Preset Registry requires unique non-empty Definition names");
|
|
65
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
|
+
}
|
|
66
79
|
this.#definitions = [...definitions].toSorted((left, right) => left.metadata.name.localeCompare(right.metadata.name));
|
|
67
80
|
}
|
|
68
81
|
all() {
|
|
@@ -83,245 +96,304 @@ export const builtInPresetRegistry = new PresetRegistry([
|
|
|
83
96
|
vueHonoAppDefinition,
|
|
84
97
|
vikeAppDefinition,
|
|
85
98
|
]);
|
|
86
|
-
/** Registry-derived Template Source roots checked independently of render plans. */
|
|
87
|
-
export function builtInPresetTemplateSourceContexts() {
|
|
88
|
-
return [
|
|
89
|
-
...builtInPresetRegistry.all().map((definition) => ({
|
|
90
|
-
name: definition.metadata.name,
|
|
91
|
-
root: resolveBuiltInTemplateSource(definition.source, "."),
|
|
92
|
-
})),
|
|
93
|
-
{
|
|
94
|
-
name: "foundation",
|
|
95
|
-
root: resolveBuiltInTemplateSource(templateSources.foundation, "."),
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
name: "shared-devcontainer",
|
|
99
|
-
root: resolveBuiltInTemplateSource(templateSources.sharedDevcontainer, "."),
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
name: "shared-oxc",
|
|
103
|
-
root: resolveBuiltInTemplateSource(templateSources.sharedOxc, "."),
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
name: "shared-vue",
|
|
107
|
-
root: resolveBuiltInTemplateSource(templateSources.vue, "."),
|
|
108
|
-
},
|
|
109
|
-
];
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Derives direct Template Source checks from every registered Definition's
|
|
113
|
-
* actual initial contributions, without maintaining a second Preset catalog.
|
|
114
|
-
*/
|
|
115
|
-
export function builtInPresetTemplateSourceCheckContexts() {
|
|
116
|
-
return builtInPresetRegistry.all().flatMap((definition) => {
|
|
117
|
-
const context = createGenerationContext({
|
|
118
|
-
targetDir: path.join("generated-repository", "template-source", definition.metadata.name),
|
|
119
|
-
toolchain: { nodeLtsMajor: "24", packageManagerPin: "pnpm@11.11.0" },
|
|
120
|
-
});
|
|
121
|
-
const plan = planGeneratedRepositoryInitialization({ definition, context });
|
|
122
|
-
const contributions = definition.planInitializationContributions?.(context) ?? [definition.planInitialization(context)];
|
|
123
|
-
return contributions.map((contribution) => ({
|
|
124
|
-
definition,
|
|
125
|
-
contribution,
|
|
126
|
-
plan,
|
|
127
|
-
}));
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
99
|
export function createGenerationContext(options) {
|
|
131
|
-
const
|
|
100
|
+
const repositoryName = path.basename(path.resolve(options.targetDir));
|
|
101
|
+
const defaultPackageScope = options.defaultPackageScope ?? repositoryName;
|
|
132
102
|
return {
|
|
133
103
|
targetDir: options.targetDir,
|
|
134
|
-
|
|
135
|
-
|
|
104
|
+
repositoryName,
|
|
105
|
+
defaultPackageScope,
|
|
106
|
+
foundationPackages: {
|
|
107
|
+
typescriptConfiguration: {
|
|
108
|
+
name: `@${defaultPackageScope}/typescript-config`,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
136
111
|
toolchain: options.toolchain,
|
|
137
112
|
};
|
|
138
113
|
}
|
|
139
114
|
function isRecord(value) {
|
|
140
115
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
141
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
|
+
}
|
|
142
156
|
function readGenerationRecord(options) {
|
|
143
|
-
const generationPath = path.join(options.
|
|
144
|
-
|
|
157
|
+
const generationPath = path.join(options.repositoryRoot, ".template/generation.json");
|
|
158
|
+
return parseGenerationRecord(readJsonFile(generationPath, "Generation Record facts"));
|
|
159
|
+
}
|
|
160
|
+
function readJsonFile(filePath, label) {
|
|
145
161
|
try {
|
|
146
|
-
|
|
162
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
147
163
|
}
|
|
148
164
|
catch (error) {
|
|
149
|
-
throw new Error(`Package Addition requires valid
|
|
150
|
-
}
|
|
151
|
-
if (isRecord(value)) {
|
|
152
|
-
for (const key of Object.keys(value)) {
|
|
153
|
-
if (![
|
|
154
|
-
"schemaVersion",
|
|
155
|
-
"preset",
|
|
156
|
-
"templateVersion",
|
|
157
|
-
"toolchain",
|
|
158
|
-
"packages",
|
|
159
|
-
].includes(key)) {
|
|
160
|
-
throw new Error(`Package Addition Generation Record contains unknown field: ${key}`);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
if (isRecord(value.toolchain)) {
|
|
164
|
-
for (const key of Object.keys(value.toolchain)) {
|
|
165
|
-
if (!["nodeLtsMajor", "packageManagerPin"].includes(key)) {
|
|
166
|
-
throw new Error(`Package Addition Generation Record toolchain contains unknown field: ${key}`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
165
|
+
throw new Error(`Package Addition requires valid ${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
170
166
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
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`);
|
|
189
184
|
}
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
typeof item.definitionName !== "string" ||
|
|
193
|
-
!["foundationPlan", "planInitialization", "planPackageAddition"].includes(String(item.planningContribution))) {
|
|
194
|
-
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}`);
|
|
195
187
|
}
|
|
196
|
-
|
|
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`);
|
|
197
194
|
}
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
.
|
|
201
|
-
const recordedPaths = packages.map((item) => item.path).toSorted();
|
|
202
|
-
if (JSON.stringify(blueprintPaths) !== JSON.stringify(recordedPaths) ||
|
|
203
|
-
new Set(recordedPaths).size !== recordedPaths.length) {
|
|
204
|
-
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}`);
|
|
205
198
|
}
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
context: options.context,
|
|
218
|
-
blueprint: options.blueprint,
|
|
219
|
-
generationRecord,
|
|
220
|
-
});
|
|
221
|
-
return generationRecord;
|
|
222
|
-
}
|
|
223
|
-
function assertGenerationRecordFoundationConsistency(options) {
|
|
224
|
-
const configDefinition = typescriptConfigPackageDefinition(options.context);
|
|
225
|
-
const foundationRecords = options.generationRecord.packages.filter((record) => record.planningContribution === "foundationPlan");
|
|
226
|
-
if (foundationRecords.length !== 1 ||
|
|
227
|
-
foundationRecords[0].definitionName !== "foundation" ||
|
|
228
|
-
foundationRecords[0].path !== configDefinition.path) {
|
|
229
|
-
throw new Error("Package Addition Generation Record must contain exactly one Foundation TypeScript configuration provenance record");
|
|
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);
|
|
230
210
|
}
|
|
231
|
-
|
|
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");
|
|
232
215
|
if (initialRecords.length === 0) {
|
|
233
216
|
throw new Error(`Package Addition Generation Record preset ${options.generationRecord.preset} has no initial Package provenance`);
|
|
234
217
|
}
|
|
235
|
-
const conflictingRecord = initialRecords.find((
|
|
218
|
+
const conflictingRecord = initialRecords.find((candidate) => candidate.definitionName !== options.generationRecord.preset);
|
|
236
219
|
if (conflictingRecord !== undefined) {
|
|
237
220
|
throw new Error(`Package Addition Generation Record preset ${options.generationRecord.preset} conflicts with initial Package provenance ${conflictingRecord.definitionName} for Blueprint package ${conflictingRecord.path}`);
|
|
238
221
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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");
|
|
245
256
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
.map((definition) => definition.path)
|
|
250
|
-
.toSorted();
|
|
251
|
-
const recordedInitialPaths = initialRecords
|
|
252
|
-
.map((record) => record.path)
|
|
253
|
-
.toSorted();
|
|
254
|
-
if (JSON.stringify(recordedInitialPaths) !== JSON.stringify(expectedPaths)) {
|
|
255
|
-
throw new Error(`Package Addition Generation Record preset ${options.generationRecord.preset} expects initial Blueprint packages ${expectedPaths.join(", ")}, but initial provenance records ${recordedInitialPaths.join(", ")}`);
|
|
256
|
-
}
|
|
257
|
-
for (const expectedDefinition of initialBlueprint.packages) {
|
|
258
|
-
const currentDefinition = options.blueprint.packages.find((definition) => definition.path === expectedDefinition.path);
|
|
259
|
-
if (currentDefinition === undefined ||
|
|
260
|
-
!packageDefinitionsEqual(currentDefinition, expectedDefinition)) {
|
|
261
|
-
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`);
|
|
262
260
|
}
|
|
263
261
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
+
}
|
|
267
268
|
}
|
|
268
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;
|
|
269
339
|
}
|
|
270
|
-
function
|
|
271
|
-
|
|
272
|
-
return
|
|
273
|
-
|
|
274
|
-
|
|
340
|
+
function requireRecordReplayAdapter(options) {
|
|
341
|
+
try {
|
|
342
|
+
return requireReplayAdapter({
|
|
343
|
+
owner: options.owner,
|
|
344
|
+
adapters: options.adapters,
|
|
345
|
+
identity: options.record.contributionIdentity,
|
|
346
|
+
});
|
|
275
347
|
}
|
|
276
|
-
|
|
277
|
-
|
|
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 });
|
|
278
350
|
}
|
|
279
|
-
return undefined;
|
|
280
351
|
}
|
|
281
|
-
function
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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}`);
|
|
286
371
|
}
|
|
372
|
+
return options.adapter.identify(contribution);
|
|
287
373
|
}
|
|
288
|
-
function
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
assertEnvironmentNeedFactKeys(value.owner, ["kind", "path"], `${context}.owner`);
|
|
307
|
-
}
|
|
308
|
-
const owner = environmentNeedOwner(value.owner);
|
|
309
|
-
if (owner === undefined)
|
|
310
|
-
return undefined;
|
|
311
|
-
switch (value.kind) {
|
|
312
|
-
case "playwright-browser-assets":
|
|
313
|
-
return value.browser === "chromium"
|
|
314
|
-
? { kind: value.kind, browser: value.browser, owner }
|
|
315
|
-
: undefined;
|
|
316
|
-
case "shellcheck-command":
|
|
317
|
-
return { kind: value.kind, owner };
|
|
318
|
-
case "rust-toolchain":
|
|
319
|
-
return value.toolchain === "stable"
|
|
320
|
-
? { kind: value.kind, toolchain: value.toolchain, owner }
|
|
321
|
-
: undefined;
|
|
322
|
-
default:
|
|
323
|
-
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
|
+
}
|
|
324
392
|
}
|
|
393
|
+
if (foundationContribution === undefined) {
|
|
394
|
+
throw new Error("Package Addition Generation Record is missing its validated Foundation contribution");
|
|
395
|
+
}
|
|
396
|
+
return { foundationContribution, packageContributions };
|
|
325
397
|
}
|
|
326
398
|
function readPersistedEnvironmentNeeds(targetDir) {
|
|
327
399
|
const filePath = path.join(targetDir, environmentNeedsPath);
|
|
@@ -335,88 +407,31 @@ function readPersistedEnvironmentNeeds(targetDir) {
|
|
|
335
407
|
catch (error) {
|
|
336
408
|
throw new Error(`Package Addition requires valid Check Environment Need facts: ${error instanceof Error ? error.message : String(error)}`);
|
|
337
409
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
if (!["schemaVersion", "check", "deployment"].includes(key)) {
|
|
341
|
-
throw new Error(`Package Addition Environment Need facts contain unknown field: ${key}`);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
if (!isRecord(value) ||
|
|
346
|
-
value.schemaVersion !== 1 ||
|
|
347
|
-
!Array.isArray(value.check) ||
|
|
348
|
-
!Array.isArray(value.deployment)) {
|
|
349
|
-
throw new Error(`Package Addition requires valid Check Environment Need facts in ${environmentNeedsPath}`);
|
|
350
|
-
}
|
|
351
|
-
const check = value.check.map(persistedCheckEnvironmentNeedFact);
|
|
352
|
-
if (check.some((fact) => fact === undefined)) {
|
|
353
|
-
throw new Error(`Package Addition requires supported Check Environment Need facts in ${environmentNeedsPath}`);
|
|
410
|
+
try {
|
|
411
|
+
return parseEnvironmentNeedsMetadata(value);
|
|
354
412
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
return true;
|
|
358
|
-
assertEnvironmentNeedFactKeys(fact, ["kind"], `deployment[${index}]`);
|
|
359
|
-
return false;
|
|
360
|
-
})) {
|
|
361
|
-
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 });
|
|
362
415
|
}
|
|
363
|
-
return {
|
|
364
|
-
check: check.map(checkEnvironmentNeedFromFact),
|
|
365
|
-
deployment: value.deployment.map((fact) => deploymentEnvironmentNeedFromFact(fact)),
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
function uniqueEnvironmentNeeds(needs) {
|
|
369
|
-
const seen = new Set();
|
|
370
|
-
return needs.filter((need) => {
|
|
371
|
-
const key = JSON.stringify(need);
|
|
372
|
-
if (seen.has(key))
|
|
373
|
-
return false;
|
|
374
|
-
seen.add(key);
|
|
375
|
-
return true;
|
|
376
|
-
});
|
|
377
416
|
}
|
|
378
417
|
function readExistingPackageAdditionState(options) {
|
|
379
|
-
const
|
|
380
|
-
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);
|
|
381
422
|
const manifestTruthByPackagePath = new Map();
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
}
|
|
386
|
-
const expectedDefinition = options.blueprint.packages.find((definition) => definition.path === record.path);
|
|
387
|
-
const definition = builtInPresetRegistry.require(record.definitionName);
|
|
388
|
-
let candidates;
|
|
389
|
-
if (record.planningContribution === "planInitialization") {
|
|
390
|
-
candidates = definition.planInitializationContributions?.(options.context) ?? [definition.planInitialization(options.context)];
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
if (definition.planPackageAddition === undefined) {
|
|
394
|
-
throw new Error(`Generation Record Definition ${record.definitionName} no longer supports Package Addition`);
|
|
395
|
-
}
|
|
396
|
-
const packageLeafName = expectedDefinition.name.split("/")[1];
|
|
397
|
-
if (!packageLeafName) {
|
|
398
|
-
throw new Error(`Generation Record package has an invalid name: ${expectedDefinition.name}`);
|
|
399
|
-
}
|
|
400
|
-
candidates = [
|
|
401
|
-
definition.planPackageAddition({
|
|
402
|
-
context: options.context,
|
|
403
|
-
packageLeafName,
|
|
404
|
-
packagePath: expectedDefinition.path,
|
|
405
|
-
}),
|
|
406
|
-
];
|
|
407
|
-
}
|
|
408
|
-
const contribution = candidates.find((candidate) => packageDefinitionsEqual(candidate.definition, expectedDefinition));
|
|
409
|
-
if (contribution === undefined) {
|
|
410
|
-
throw new Error(`Generation Record cannot reproduce Package Definition ${expectedDefinition.name} at ${expectedDefinition.path}`);
|
|
411
|
-
}
|
|
412
|
-
return [contribution];
|
|
413
|
-
});
|
|
414
|
-
for (const packagePath of new Set(options.manifestTruthPackagePaths ?? [])) {
|
|
415
|
-
const expectedDefinition = options.blueprint.packages.find((definition) => definition.path === packagePath);
|
|
416
|
-
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)) {
|
|
417
426
|
throw new Error(`Package Addition requires manifest truth for unknown Package Path ${packagePath}`);
|
|
418
427
|
}
|
|
419
|
-
|
|
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
|
+
}
|
|
420
435
|
let manifest;
|
|
421
436
|
try {
|
|
422
437
|
manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
@@ -429,17 +444,16 @@ function readExistingPackageAdditionState(options) {
|
|
|
429
444
|
}
|
|
430
445
|
manifestTruthByPackagePath.set(expectedDefinition.path, manifest);
|
|
431
446
|
}
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (JSON.stringify(
|
|
437
|
-
JSON.stringify(
|
|
438
|
-
|
|
439
|
-
JSON.stringify(persistedDeploymentFacts)) {
|
|
440
|
-
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");
|
|
441
454
|
}
|
|
442
455
|
return {
|
|
456
|
+
foundationContribution,
|
|
443
457
|
contributions,
|
|
444
458
|
manifestTruthByPackagePath,
|
|
445
459
|
deploymentEnvironmentNeeds: persistedEnvironmentNeeds.deployment,
|
|
@@ -451,6 +465,13 @@ function packageDefinitionsEqual(left, right) {
|
|
|
451
465
|
left.path === right.path &&
|
|
452
466
|
left.role === right.role);
|
|
453
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
|
+
}
|
|
454
475
|
function packageLinkIntentsEqual(left, right) {
|
|
455
476
|
return (left.consumerPackagePath === right.consumerPackagePath &&
|
|
456
477
|
left.providerPackagePath === right.providerPackagePath);
|
|
@@ -499,7 +520,7 @@ function contributedDevcontainerComposition(options) {
|
|
|
499
520
|
},
|
|
500
521
|
],
|
|
501
522
|
},
|
|
502
|
-
layers: options.layers,
|
|
523
|
+
layers: [githubCliDevelopmentContainerToolLayer(), ...options.layers],
|
|
503
524
|
});
|
|
504
525
|
return {
|
|
505
526
|
operations: [
|
|
@@ -509,7 +530,7 @@ function contributedDevcontainerComposition(options) {
|
|
|
509
530
|
from: "devcontainer.json",
|
|
510
531
|
to: ".devcontainer/devcontainer.json",
|
|
511
532
|
replacements: {
|
|
512
|
-
PROJECT_NAME: options.context.
|
|
533
|
+
PROJECT_NAME: options.context.repositoryName,
|
|
513
534
|
NODE_LTS_MAJOR: options.context.toolchain.nodeLtsMajor,
|
|
514
535
|
PACKAGE_MANAGER_PIN: options.context.toolchain.packageManagerPin,
|
|
515
536
|
},
|
|
@@ -641,13 +662,36 @@ function composeDependencyMaintenancePolicy(contributions) {
|
|
|
641
662
|
interval: "weekly",
|
|
642
663
|
};
|
|
643
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
|
+
}
|
|
644
690
|
function foundationPlan(options) {
|
|
645
|
-
|
|
646
|
-
const
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
if (persistedConfigDefinition === undefined ||
|
|
650
|
-
!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))) {
|
|
651
695
|
throw new Error("Project Blueprint must contain the Foundation TypeScript configuration Package Definition");
|
|
652
696
|
}
|
|
653
697
|
const initialProjectLinkPlan = planExplicitProjectLinks({
|
|
@@ -662,15 +706,26 @@ function foundationPlan(options) {
|
|
|
662
706
|
const injectedProviderNames = new Set([...initialProjectLinkPlan.manifestPatchesByPackagePath.values()].flatMap((patch) => Object.entries(patch.dependenciesMeta)
|
|
663
707
|
.filter(([, metadata]) => metadata.injected)
|
|
664
708
|
.map(([name]) => name)));
|
|
665
|
-
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);
|
|
666
714
|
const packageContributions = options.contributions.map((contribution) => {
|
|
667
|
-
|
|
668
|
-
operation.to === `${contribution.definition.path}/tsconfig.json`);
|
|
669
|
-
if (!ownsTypeScriptConfig)
|
|
715
|
+
if (contribution.foundation.typescriptConfigurationPackage === undefined) {
|
|
670
716
|
return contribution;
|
|
717
|
+
}
|
|
671
718
|
const dependencyField = injectedProviderNames.has(contribution.definition.name)
|
|
672
719
|
? "dependencies"
|
|
673
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:*";
|
|
674
729
|
const existingDependencies = contribution.manifest[dependencyField];
|
|
675
730
|
return {
|
|
676
731
|
...contribution,
|
|
@@ -678,43 +733,67 @@ function foundationPlan(options) {
|
|
|
678
733
|
...contribution.manifest,
|
|
679
734
|
[dependencyField]: {
|
|
680
735
|
...(isRecord(existingDependencies) ? existingDependencies : {}),
|
|
681
|
-
[configPackageName]:
|
|
736
|
+
[configPackageName]: dependencySpecifier,
|
|
682
737
|
},
|
|
683
738
|
},
|
|
684
739
|
};
|
|
685
740
|
});
|
|
686
|
-
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
|
+
});
|
|
687
747
|
const contributions = [configContribution, ...packageContributions];
|
|
688
|
-
|
|
689
|
-
|
|
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,
|
|
690
758
|
preset: options.definition.metadata.name,
|
|
691
759
|
templateVersion: "0.0.0",
|
|
692
760
|
toolchain: options.context.toolchain,
|
|
693
761
|
packages: [
|
|
694
|
-
...options.contributions.map((contribution) =>
|
|
695
|
-
|
|
696
|
-
definitionName: options.definition.metadata.name,
|
|
697
|
-
planningContribution: options.mode === "initialization"
|
|
762
|
+
...options.contributions.map((contribution) => {
|
|
763
|
+
const planningContribution = options.mode === "initialization"
|
|
698
764
|
? "planInitialization"
|
|
699
|
-
: "planPackageAddition"
|
|
700
|
-
|
|
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
|
+
}),
|
|
701
778
|
{
|
|
779
|
+
packageDefinitionId: requirePersistedPackageDefinition(options.blueprint, configDefinition).packageDefinitionId,
|
|
702
780
|
path: configDefinition.path,
|
|
703
781
|
definitionName: "foundation",
|
|
704
782
|
planningContribution: "foundationPlan",
|
|
783
|
+
contributionIdentity: typescriptConfigReplayAdapter.identity,
|
|
705
784
|
},
|
|
706
785
|
],
|
|
707
786
|
};
|
|
708
|
-
const
|
|
709
|
-
const
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
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;
|
|
718
797
|
const hasDeploymentTask = contributions.some((contribution) => {
|
|
719
798
|
const scripts = contribution.manifest.scripts;
|
|
720
799
|
return (typeof scripts === "object" &&
|
|
@@ -758,13 +837,27 @@ function foundationPlan(options) {
|
|
|
758
837
|
const editorCustomization = editorCustomizationForCapabilities(contributions.flatMap((contribution) => contribution.foundation.editorCapabilities), loadEditorCustomizationDeclarations(resolveTemplateSource(templateSources.editorCustomization, "capabilities.json")));
|
|
759
838
|
const dependencyMaintenancePolicy = composeDependencyMaintenancePolicy(contributions);
|
|
760
839
|
const rootManifest = {
|
|
761
|
-
name: options.context.
|
|
762
|
-
version: "0.0.0",
|
|
840
|
+
name: options.context.repositoryName,
|
|
763
841
|
private: true,
|
|
764
842
|
type: "module",
|
|
843
|
+
...(publicationCandidate === undefined
|
|
844
|
+
? {}
|
|
845
|
+
: {
|
|
846
|
+
imports: {
|
|
847
|
+
"#npm-publication/*": "./scripts/npm-publication/*.ts",
|
|
848
|
+
},
|
|
849
|
+
}),
|
|
765
850
|
scripts: {
|
|
766
|
-
check:
|
|
851
|
+
check: publicationCandidate === undefined
|
|
852
|
+
? renderRootCheckCommand()
|
|
853
|
+
: renderPublicationRootCheckCommand(publicationCandidate.definition.path),
|
|
767
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
|
+
}),
|
|
768
861
|
...(hasDeploymentTask
|
|
769
862
|
? { "check:deployment": renderDeploymentCheckCommand() }
|
|
770
863
|
: {}),
|
|
@@ -777,9 +870,23 @@ function foundationPlan(options) {
|
|
|
777
870
|
},
|
|
778
871
|
devDependencies: {
|
|
779
872
|
"@types/node": "catalog:",
|
|
873
|
+
...(publicationCandidate === undefined
|
|
874
|
+
? {}
|
|
875
|
+
: {
|
|
876
|
+
"@types/semver": "catalog:",
|
|
877
|
+
"@types/spdx-expression-parse": "catalog:",
|
|
878
|
+
}),
|
|
780
879
|
oxfmt: "catalog:",
|
|
781
880
|
oxlint: "catalog:",
|
|
782
881
|
"oxlint-tsgolint": "catalog:",
|
|
882
|
+
...(publicationCandidate === undefined
|
|
883
|
+
? {}
|
|
884
|
+
: {
|
|
885
|
+
semver: "catalog:",
|
|
886
|
+
"spdx-expression-parse": "catalog:",
|
|
887
|
+
npm: "catalog:",
|
|
888
|
+
tar: "catalog:",
|
|
889
|
+
}),
|
|
783
890
|
turbo: "catalog:",
|
|
784
891
|
"typescript-7": "catalog:",
|
|
785
892
|
},
|
|
@@ -846,6 +953,16 @@ function foundationPlan(options) {
|
|
|
846
953
|
};
|
|
847
954
|
const workflowOperations = [
|
|
848
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
|
+
]),
|
|
849
966
|
{
|
|
850
967
|
kind: "writeTextTemplate",
|
|
851
968
|
source: templateSources.foundation,
|
|
@@ -865,6 +982,16 @@ function foundationPlan(options) {
|
|
|
865
982
|
nestedKeyOrder: packageConditionKeyOrder,
|
|
866
983
|
},
|
|
867
984
|
workspaceOperation,
|
|
985
|
+
...(requiresPackingHook
|
|
986
|
+
? [
|
|
987
|
+
{
|
|
988
|
+
kind: "copyFile",
|
|
989
|
+
source: templateSources.foundation,
|
|
990
|
+
from: ".pnpmfile.mjs",
|
|
991
|
+
to: ".pnpmfile.mjs",
|
|
992
|
+
},
|
|
993
|
+
]
|
|
994
|
+
: []),
|
|
868
995
|
{
|
|
869
996
|
kind: "copyFile",
|
|
870
997
|
source: templateSources.foundation,
|
|
@@ -877,6 +1004,16 @@ function foundationPlan(options) {
|
|
|
877
1004
|
from: "turbo.json",
|
|
878
1005
|
to: "turbo.json",
|
|
879
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
|
+
]),
|
|
880
1017
|
...turboBoundaryTags.map((tag) => ({
|
|
881
1018
|
kind: "mergeJsonTemplate",
|
|
882
1019
|
source: templateSources.foundation,
|
|
@@ -901,6 +1038,27 @@ function foundationPlan(options) {
|
|
|
901
1038
|
from: "tsconfig.json",
|
|
902
1039
|
to: "tsconfig.json",
|
|
903
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
|
+
: []),
|
|
904
1062
|
{
|
|
905
1063
|
kind: "copyFile",
|
|
906
1064
|
source: templateSources.sharedOxc,
|
|
@@ -919,6 +1077,135 @@ function foundationPlan(options) {
|
|
|
919
1077
|
from: "oxfmt.config.ts",
|
|
920
1078
|
to: "oxfmt.config.ts",
|
|
921
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
|
+
]),
|
|
922
1209
|
{
|
|
923
1210
|
kind: "writeJson",
|
|
924
1211
|
to: ".vscode/extensions.json",
|
|
@@ -1007,6 +1294,7 @@ function foundationPlan(options) {
|
|
|
1007
1294
|
...linkOperations.map((operation) => withProvenance(operation, foundationProvenance)),
|
|
1008
1295
|
];
|
|
1009
1296
|
const reconciliation = [
|
|
1297
|
+
{ path: "tsconfig.json", driver: "structured" },
|
|
1010
1298
|
{ path: "turbo.json", driver: "structured" },
|
|
1011
1299
|
{
|
|
1012
1300
|
path: ".devcontainer/devcontainer.json",
|
|
@@ -1035,6 +1323,7 @@ function foundationPlan(options) {
|
|
|
1035
1323
|
: "planInitialization",
|
|
1036
1324
|
blueprint: options.blueprint,
|
|
1037
1325
|
generationRecord,
|
|
1326
|
+
packageContributions,
|
|
1038
1327
|
operations,
|
|
1039
1328
|
reconciliation,
|
|
1040
1329
|
developmentContainer: {
|
|
@@ -1056,40 +1345,342 @@ function foundationPlan(options) {
|
|
|
1056
1345
|
],
|
|
1057
1346
|
};
|
|
1058
1347
|
}
|
|
1059
|
-
|
|
1060
|
-
|
|
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;
|
|
1061
1422
|
const configDefinition = typescriptConfigPackageDefinition(options.context);
|
|
1062
|
-
|
|
1063
|
-
definition.
|
|
1064
|
-
|
|
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"));
|
|
1065
1437
|
}
|
|
1438
|
+
const occupiedIds = new Set();
|
|
1066
1439
|
const blueprint = {
|
|
1067
1440
|
...presetBlueprint,
|
|
1068
|
-
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
|
+
],
|
|
1069
1471
|
};
|
|
1070
|
-
const contributions = options.definition.planInitializationContributions?.(options.context) ?? [options.definition.planInitialization(options.context)];
|
|
1071
1472
|
return foundationPlan({
|
|
1072
1473
|
definition: options.definition,
|
|
1073
1474
|
context: options.context,
|
|
1074
1475
|
blueprint,
|
|
1476
|
+
foundationContribution: typescriptConfigReplayAdapter.identify(typescriptConfigContribution(options.context, configDefinition)),
|
|
1075
1477
|
contributions,
|
|
1076
1478
|
mode: "initialization",
|
|
1077
1479
|
});
|
|
1078
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
|
+
}
|
|
1079
1669
|
export function planGeneratedRepositoryPackageAddition(options) {
|
|
1080
|
-
|
|
1670
|
+
const { blueprint: persistedBlueprint, context } = options.localTemplateMetadata;
|
|
1671
|
+
assertProjectBlueprint(persistedBlueprint);
|
|
1081
1672
|
if (!options.definition.planPackageAddition)
|
|
1082
1673
|
throw new Error(`Built-in Preset ${options.definition.metadata.name} does not support Package Addition`);
|
|
1083
1674
|
const packagePath = options.packagePath ??
|
|
1084
1675
|
options.definition.defaultPackagePath?.({
|
|
1085
|
-
context
|
|
1676
|
+
context,
|
|
1086
1677
|
packageLeafName: options.packageLeafName,
|
|
1087
1678
|
});
|
|
1088
1679
|
if (packagePath === undefined) {
|
|
1089
1680
|
throw new Error(`Built-in Preset ${options.definition.metadata.name} must own a default Package Path or receive an explicit Package Path`);
|
|
1090
1681
|
}
|
|
1091
1682
|
const contribution = options.definition.planPackageAddition({
|
|
1092
|
-
context
|
|
1683
|
+
context,
|
|
1093
1684
|
packageLeafName: options.packageLeafName,
|
|
1094
1685
|
packagePath,
|
|
1095
1686
|
});
|
|
@@ -1097,28 +1688,31 @@ export function planGeneratedRepositoryPackageAddition(options) {
|
|
|
1097
1688
|
consumerPackagePath,
|
|
1098
1689
|
providerPackagePath: contribution.definition.path,
|
|
1099
1690
|
}));
|
|
1100
|
-
const conflictingPackage =
|
|
1691
|
+
const conflictingPackage = persistedBlueprint.packages.find((existing) => existing.name === contribution.definition.name ||
|
|
1101
1692
|
existing.path === contribution.definition.path);
|
|
1102
1693
|
if (conflictingPackage !== undefined) {
|
|
1103
1694
|
const isExactPackageDefinition = packageDefinitionsEqual(conflictingPackage, contribution.definition);
|
|
1104
|
-
const existingPackageLinkIntents =
|
|
1695
|
+
const existingPackageLinkIntents = persistedBlueprint.packageLinkIntents ?? [];
|
|
1105
1696
|
const missingPackageLinkIntent = requestedPackageLinkIntents.find((requested) => !existingPackageLinkIntents.some((existing) => packageLinkIntentsEqual(existing, requested)));
|
|
1106
1697
|
if (isExactPackageDefinition && missingPackageLinkIntent === undefined) {
|
|
1107
1698
|
const existing = readExistingPackageAdditionState(options);
|
|
1108
1699
|
const plan = foundationPlan({
|
|
1109
1700
|
definition: options.definition,
|
|
1110
|
-
context
|
|
1111
|
-
blueprint:
|
|
1701
|
+
context,
|
|
1702
|
+
blueprint: persistedBlueprint,
|
|
1703
|
+
foundationContribution: existing.foundationContribution,
|
|
1112
1704
|
contributions: existing.contributions,
|
|
1113
1705
|
renderContributions: [],
|
|
1114
1706
|
existingDeploymentEnvironmentNeeds: existing.deploymentEnvironmentNeeds,
|
|
1707
|
+
manifestTruthByPackagePath: existing.manifestTruthByPackagePath,
|
|
1115
1708
|
generationRecord: existing.generationRecord,
|
|
1116
1709
|
mode: "addition",
|
|
1117
1710
|
});
|
|
1118
1711
|
const currentProjection = foundationPlan({
|
|
1119
1712
|
definition: options.definition,
|
|
1120
|
-
context
|
|
1121
|
-
blueprint:
|
|
1713
|
+
context,
|
|
1714
|
+
blueprint: persistedBlueprint,
|
|
1715
|
+
foundationContribution: existing.foundationContribution,
|
|
1122
1716
|
contributions: existing.contributions,
|
|
1123
1717
|
existingDeploymentEnvironmentNeeds: existing.deploymentEnvironmentNeeds,
|
|
1124
1718
|
generationRecord: existing.generationRecord,
|
|
@@ -1145,47 +1739,64 @@ export function planGeneratedRepositoryPackageAddition(options) {
|
|
|
1145
1739
|
}
|
|
1146
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})`);
|
|
1147
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
|
+
});
|
|
1148
1756
|
const blueprint = {
|
|
1149
|
-
...
|
|
1150
|
-
packages: [...
|
|
1757
|
+
...persistedBlueprint,
|
|
1758
|
+
packages: [...persistedBlueprint.packages, persistedContributionDefinition],
|
|
1151
1759
|
...(requestedPackageLinkIntents.length > 0
|
|
1152
1760
|
? {
|
|
1153
1761
|
packageLinkIntents: [
|
|
1154
|
-
...(
|
|
1762
|
+
...(persistedBlueprint.packageLinkIntents ?? []),
|
|
1155
1763
|
...requestedPackageLinkIntents,
|
|
1156
1764
|
],
|
|
1157
1765
|
}
|
|
1158
1766
|
: {}),
|
|
1159
1767
|
};
|
|
1160
|
-
|
|
1161
|
-
const
|
|
1768
|
+
assertProjectBlueprint(blueprint);
|
|
1769
|
+
const requiredManifestTruthPackagePaths = requestedPackageLinkIntents.length === 0
|
|
1162
1770
|
? []
|
|
1163
1771
|
: [
|
|
1164
|
-
...(
|
|
1772
|
+
...(persistedBlueprint.packageLinkIntents ?? []).flatMap((intent) => [
|
|
1165
1773
|
intent.consumerPackagePath,
|
|
1166
1774
|
intent.providerPackagePath,
|
|
1167
1775
|
]),
|
|
1168
1776
|
...requestedPackageLinkIntents.map((intent) => intent.consumerPackagePath),
|
|
1169
1777
|
];
|
|
1170
1778
|
const existing = readExistingPackageAdditionState({
|
|
1171
|
-
|
|
1172
|
-
|
|
1779
|
+
localTemplateMetadata: options.localTemplateMetadata,
|
|
1780
|
+
requiredManifestTruthPackagePaths,
|
|
1173
1781
|
});
|
|
1174
1782
|
const generationRecord = {
|
|
1175
1783
|
...existing.generationRecord,
|
|
1176
1784
|
packages: [
|
|
1177
1785
|
...existing.generationRecord.packages,
|
|
1178
1786
|
{
|
|
1787
|
+
packageDefinitionId: persistedContributionDefinition.packageDefinitionId,
|
|
1179
1788
|
path: contribution.definition.path,
|
|
1180
1789
|
definitionName: options.definition.metadata.name,
|
|
1181
1790
|
planningContribution: "planPackageAddition",
|
|
1791
|
+
contributionIdentity,
|
|
1182
1792
|
},
|
|
1183
1793
|
],
|
|
1184
1794
|
};
|
|
1185
1795
|
const beforeProjection = foundationPlan({
|
|
1186
1796
|
definition: options.definition,
|
|
1187
|
-
context
|
|
1188
|
-
blueprint:
|
|
1797
|
+
context,
|
|
1798
|
+
blueprint: persistedBlueprint,
|
|
1799
|
+
foundationContribution: existing.foundationContribution,
|
|
1189
1800
|
contributions: existing.contributions,
|
|
1190
1801
|
existingDeploymentEnvironmentNeeds: existing.deploymentEnvironmentNeeds,
|
|
1191
1802
|
generationRecord: existing.generationRecord,
|
|
@@ -1193,30 +1804,24 @@ export function planGeneratedRepositoryPackageAddition(options) {
|
|
|
1193
1804
|
});
|
|
1194
1805
|
const afterProjection = foundationPlan({
|
|
1195
1806
|
definition: options.definition,
|
|
1196
|
-
context
|
|
1807
|
+
context,
|
|
1197
1808
|
blueprint,
|
|
1809
|
+
foundationContribution: existing.foundationContribution,
|
|
1198
1810
|
contributions: [...existing.contributions, contribution],
|
|
1199
1811
|
existingDeploymentEnvironmentNeeds: existing.deploymentEnvironmentNeeds,
|
|
1200
|
-
|
|
1201
|
-
? {}
|
|
1202
|
-
: {
|
|
1203
|
-
manifestTruthByPackagePath: existing.manifestTruthByPackagePath,
|
|
1204
|
-
}),
|
|
1812
|
+
manifestTruthByPackagePath: existing.manifestTruthByPackagePath,
|
|
1205
1813
|
generationRecord,
|
|
1206
1814
|
mode: "initialization",
|
|
1207
1815
|
});
|
|
1208
1816
|
const plan = foundationPlan({
|
|
1209
1817
|
definition: options.definition,
|
|
1210
|
-
context
|
|
1818
|
+
context,
|
|
1211
1819
|
blueprint,
|
|
1820
|
+
foundationContribution: existing.foundationContribution,
|
|
1212
1821
|
contributions: [...existing.contributions, contribution],
|
|
1213
1822
|
renderContributions: [contribution],
|
|
1214
1823
|
existingDeploymentEnvironmentNeeds: existing.deploymentEnvironmentNeeds,
|
|
1215
|
-
|
|
1216
|
-
? {}
|
|
1217
|
-
: {
|
|
1218
|
-
manifestTruthByPackagePath: existing.manifestTruthByPackagePath,
|
|
1219
|
-
}),
|
|
1824
|
+
manifestTruthByPackagePath: existing.manifestTruthByPackagePath,
|
|
1220
1825
|
generationRecord,
|
|
1221
1826
|
mode: "addition",
|
|
1222
1827
|
});
|