create-next-hydra 0.3.0 → 0.4.2
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/README.md +126 -27
- package/dist/add-command.d.ts +11 -0
- package/dist/add-command.d.ts.map +1 -0
- package/dist/add-command.js +75 -0
- package/dist/addition.d.ts +67 -0
- package/dist/addition.d.ts.map +1 -0
- package/dist/addition.js +422 -0
- package/dist/application-git.d.ts +20 -0
- package/dist/application-git.d.ts.map +1 -0
- package/dist/application-git.js +57 -0
- package/dist/baseline.d.ts +22 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +90 -0
- package/dist/cli.js +5 -18
- package/dist/commands.d.ts +22 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +197 -0
- package/dist/composition-lint.d.ts +20 -0
- package/dist/composition-lint.d.ts.map +1 -0
- package/dist/composition-lint.js +288 -0
- package/dist/composition.d.ts +24 -0
- package/dist/composition.d.ts.map +1 -0
- package/dist/composition.js +420 -0
- package/dist/create-command.d.ts +21 -0
- package/dist/create-command.d.ts.map +1 -0
- package/dist/create-command.js +128 -0
- package/dist/demo-instrumentation.d.ts +5 -0
- package/dist/demo-instrumentation.d.ts.map +1 -0
- package/dist/demo-instrumentation.js +300 -0
- package/dist/environment.d.ts +7 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +81 -0
- package/dist/errors.d.ts +156 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +140 -0
- package/dist/file-policy.d.ts +6 -0
- package/dist/file-policy.d.ts.map +1 -0
- package/dist/file-policy.js +38 -0
- package/dist/files.d.ts +7 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +66 -0
- package/dist/hosting.d.ts +5 -0
- package/dist/hosting.d.ts.map +1 -0
- package/dist/hosting.js +107 -0
- package/dist/index.d.ts +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -89
- package/dist/initialization.d.ts +14 -0
- package/dist/initialization.d.ts.map +1 -0
- package/dist/initialization.js +266 -0
- package/dist/lint-workspaces.d.ts +2 -0
- package/dist/lint-workspaces.d.ts.map +1 -0
- package/dist/lint-workspaces.js +62 -0
- package/dist/model.d.ts +143 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +61 -0
- package/dist/package-catalog.d.ts +8 -0
- package/dist/package-catalog.d.ts.map +1 -0
- package/dist/package-catalog.js +61 -0
- package/dist/packages.d.ts +57 -0
- package/dist/packages.d.ts.map +1 -0
- package/dist/packages.js +237 -0
- package/dist/process-group.d.ts +5 -0
- package/dist/process-group.d.ts.map +1 -0
- package/dist/process-group.js +23 -0
- package/dist/reference-workspace-bindings.d.ts +38 -0
- package/dist/reference-workspace-bindings.d.ts.map +1 -0
- package/dist/reference-workspace-bindings.js +41 -0
- package/dist/reference-workspace.d.ts +6 -0
- package/dist/reference-workspace.d.ts.map +1 -0
- package/dist/reference-workspace.js +62 -0
- package/dist/registry.d.ts +187 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +107 -0
- package/dist/runtime.d.ts +9 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +22 -0
- package/dist/selection.d.ts +704 -0
- package/dist/selection.d.ts.map +1 -0
- package/dist/selection.js +424 -0
- package/dist/shadcn-protocol.d.ts +22 -0
- package/dist/shadcn-protocol.d.ts.map +1 -0
- package/dist/shadcn-protocol.js +13 -0
- package/dist/shadcn-worker.d.ts +2 -0
- package/dist/shadcn-worker.d.ts.map +1 -0
- package/dist/shadcn-worker.js +43 -0
- package/dist/shadcn.d.ts +22 -0
- package/dist/shadcn.d.ts.map +1 -0
- package/dist/shadcn.js +101 -0
- package/dist/source-changes.d.ts +55 -0
- package/dist/source-changes.d.ts.map +1 -0
- package/dist/source-changes.js +225 -0
- package/dist/source-inventory.d.ts +25 -0
- package/dist/source-inventory.d.ts.map +1 -0
- package/dist/source-inventory.js +212 -0
- package/dist/source-root.d.ts +4 -0
- package/dist/source-root.d.ts.map +1 -0
- package/dist/source-root.js +20 -0
- package/dist/staging.d.ts +19 -0
- package/dist/staging.d.ts.map +1 -0
- package/dist/staging.js +42 -0
- package/dist/tasks.d.ts +44 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +137 -0
- package/dist/templates.d.ts +217 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/templates.js +161 -0
- package/dist/test-reference-workspace.d.ts +2 -0
- package/dist/test-reference-workspace.d.ts.map +1 -0
- package/dist/test-reference-workspace.js +6 -0
- package/dist/types.d.ts +2 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/typescript-paths.d.ts +10 -0
- package/dist/typescript-paths.d.ts.map +1 -0
- package/dist/typescript-paths.js +52 -0
- package/dist/workspace-dependencies.d.ts +26 -0
- package/dist/workspace-dependencies.d.ts.map +1 -0
- package/dist/workspace-dependencies.js +478 -0
- package/dist/workspace-files.d.ts +24 -0
- package/dist/workspace-files.d.ts.map +1 -0
- package/dist/workspace-files.js +395 -0
- package/dist/workspace-lock.d.ts +12 -0
- package/dist/workspace-lock.d.ts.map +1 -0
- package/dist/workspace-lock.js +104 -0
- package/dist/workspace-snapshots.d.ts +24 -0
- package/dist/workspace-snapshots.d.ts.map +1 -0
- package/dist/workspace-snapshots.js +218 -0
- package/dist/workspace-sources.d.ts +27 -0
- package/dist/workspace-sources.d.ts.map +1 -0
- package/dist/workspace-sources.js +88 -0
- package/dist/workspace-state.d.ts +325 -0
- package/dist/workspace-state.d.ts.map +1 -0
- package/dist/workspace-state.js +571 -0
- package/dist/workspaces.d.ts +73 -0
- package/dist/workspaces.d.ts.map +1 -0
- package/dist/workspaces.js +396 -0
- package/package.json +27 -20
- package/schema/selection-definition.json +143 -4
- package/dist/clone.d.ts +0 -9
- package/dist/clone.d.ts.map +0 -1
- package/dist/clone.js +0 -19
- package/dist/composition/add.d.ts +0 -14
- package/dist/composition/add.d.ts.map +0 -1
- package/dist/composition/add.js +0 -484
- package/dist/composition/catalog.d.ts +0 -26
- package/dist/composition/catalog.d.ts.map +0 -1
- package/dist/composition/catalog.js +0 -317
- package/dist/composition/errors.d.ts +0 -5
- package/dist/composition/errors.d.ts.map +0 -1
- package/dist/composition/errors.js +0 -8
- package/dist/composition/format.d.ts +0 -6
- package/dist/composition/format.d.ts.map +0 -1
- package/dist/composition/format.js +0 -65
- package/dist/composition/install.d.ts +0 -16
- package/dist/composition/install.d.ts.map +0 -1
- package/dist/composition/install.js +0 -175
- package/dist/composition/packages.d.ts +0 -6
- package/dist/composition/packages.d.ts.map +0 -1
- package/dist/composition/packages.js +0 -55
- package/dist/composition/paths.d.ts +0 -4
- package/dist/composition/paths.d.ts.map +0 -1
- package/dist/composition/paths.js +0 -47
- package/dist/composition/planner.d.ts +0 -4
- package/dist/composition/planner.d.ts.map +0 -1
- package/dist/composition/planner.js +0 -366
- package/dist/composition/schema.d.ts +0 -294
- package/dist/composition/schema.d.ts.map +0 -1
- package/dist/composition/schema.js +0 -166
- package/dist/composition/types.d.ts +0 -111
- package/dist/composition/types.d.ts.map +0 -1
- package/dist/composition/types.js +0 -6
- package/dist/composition/typescript-paths.d.ts +0 -6
- package/dist/composition/typescript-paths.d.ts.map +0 -1
- package/dist/composition/typescript-paths.js +0 -157
- package/dist/composition/use.d.ts +0 -23
- package/dist/composition/use.d.ts.map +0 -1
- package/dist/composition/use.js +0 -187
- package/dist/composition/workspace.d.ts +0 -10
- package/dist/composition/workspace.d.ts.map +0 -1
- package/dist/composition/workspace.js +0 -210
- package/dist/constants.d.ts +0 -11
- package/dist/constants.d.ts.map +0 -1
- package/dist/constants.js +0 -39
- package/dist/fs-utils.d.ts +0 -9
- package/dist/fs-utils.d.ts.map +0 -1
- package/dist/fs-utils.js +0 -64
- package/dist/git.d.ts +0 -28
- package/dist/git.d.ts.map +0 -1
- package/dist/git.js +0 -107
- package/dist/instruction-renderer.d.ts +0 -17
- package/dist/instruction-renderer.d.ts.map +0 -1
- package/dist/instruction-renderer.js +0 -132
- package/dist/logger.d.ts +0 -13
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -26
- package/dist/prompts.d.ts +0 -7
- package/dist/prompts.d.ts.map +0 -1
- package/dist/prompts.js +0 -54
- package/dist/sanitize.d.ts +0 -10
- package/dist/sanitize.d.ts.map +0 -1
- package/dist/sanitize.js +0 -110
- package/dist/scaffold.d.ts +0 -7
- package/dist/scaffold.d.ts.map +0 -1
- package/dist/scaffold.js +0 -330
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js +0 -2
package/dist/composition/add.js
DELETED
|
@@ -1,484 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { confirm, isCancel } from "@clack/prompts";
|
|
4
|
-
import { getRegistriesConfig, resolveRegistryItems } from "shadcn/registry";
|
|
5
|
-
import { pathExists } from "../fs-utils.js";
|
|
6
|
-
import { runCommand } from "../git.js";
|
|
7
|
-
import { info, success } from "../logger.js";
|
|
8
|
-
import { fetchRegistryItemGraph } from "./catalog.js";
|
|
9
|
-
import { CompositionValidationError } from "./errors.js";
|
|
10
|
-
import { addRegistryItemsQuietly, withPreparedRegistryArtifacts, } from "./install.js";
|
|
11
|
-
import { mergePackageRequirements, parsePackageJson, readPackageJson, } from "./packages.js";
|
|
12
|
-
import { resolveRegistryTarget } from "./paths.js";
|
|
13
|
-
import { formatZodError, NEXT_HYDRA_SELECTION_SCHEMA_URL, selectionDefinitionSchema, } from "./schema.js";
|
|
14
|
-
import { PROVIDER_ALIASES } from "./types.js";
|
|
15
|
-
import { applyPackageEntries } from "./workspace.js";
|
|
16
|
-
const EXACT_COPY_FILE_TYPES = new Set(["registry:file", "registry:item"]);
|
|
17
|
-
const KNOWN_PROVIDERS = new Map([
|
|
18
|
-
[
|
|
19
|
-
"next-hydra/auth/clerk",
|
|
20
|
-
{ dependency: "@repo/auth", packageName: "@repo/auth-clerk" },
|
|
21
|
-
],
|
|
22
|
-
[
|
|
23
|
-
"next-hydra/auth/workos",
|
|
24
|
-
{ dependency: "@repo/auth", packageName: "@repo/auth-workos" },
|
|
25
|
-
],
|
|
26
|
-
[
|
|
27
|
-
"next-hydra/cms/contentstack",
|
|
28
|
-
{ dependency: "@repo/cms", packageName: "@repo/cms-contentstack" },
|
|
29
|
-
],
|
|
30
|
-
[
|
|
31
|
-
"next-hydra/cms/drupal",
|
|
32
|
-
{ dependency: "@repo/cms", packageName: "@repo/cms-drupal" },
|
|
33
|
-
],
|
|
34
|
-
[
|
|
35
|
-
"next-hydra/commerce/commercetools",
|
|
36
|
-
{
|
|
37
|
-
dependency: "@repo/commerce-provider",
|
|
38
|
-
packageName: "@repo/commerce-commercetools",
|
|
39
|
-
},
|
|
40
|
-
],
|
|
41
|
-
]);
|
|
42
|
-
function parseDependency(request) {
|
|
43
|
-
const packageEnd = request.startsWith("@")
|
|
44
|
-
? request.indexOf("@", request.indexOf("/") + 1)
|
|
45
|
-
: request.indexOf("@");
|
|
46
|
-
if (packageEnd < 1) {
|
|
47
|
-
return { name: request };
|
|
48
|
-
}
|
|
49
|
-
return {
|
|
50
|
-
name: request.slice(0, packageEnd),
|
|
51
|
-
specifier: request.slice(packageEnd + 1),
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function packageStatus(actual, expected) {
|
|
55
|
-
if (actual === undefined) {
|
|
56
|
-
return "create";
|
|
57
|
-
}
|
|
58
|
-
return actual === expected ? "identical" : "changed";
|
|
59
|
-
}
|
|
60
|
-
function dependencyStatus(actual, requested) {
|
|
61
|
-
if (requested.specifier) {
|
|
62
|
-
return packageStatus(actual, requested.specifier);
|
|
63
|
-
}
|
|
64
|
-
return actual === undefined ? "create" : "identical";
|
|
65
|
-
}
|
|
66
|
-
function parseSelection(item) {
|
|
67
|
-
if (item.meta?.nextHydra === undefined) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
if (item.$schema !== NEXT_HYDRA_SELECTION_SCHEMA_URL) {
|
|
71
|
-
throw new CompositionValidationError(`${item.name} does not use the Next Hydra Selection Definition schema.`, [`$schema must be ${NEXT_HYDRA_SELECTION_SCHEMA_URL}`]);
|
|
72
|
-
}
|
|
73
|
-
const result = selectionDefinitionSchema.safeParse(item.meta.nextHydra);
|
|
74
|
-
if (!result.success) {
|
|
75
|
-
throw new CompositionValidationError(`${item.name} contains invalid Next Hydra metadata. Run the latest create-next-hydra and try again.`, formatZodError(result.error));
|
|
76
|
-
}
|
|
77
|
-
return result.data;
|
|
78
|
-
}
|
|
79
|
-
async function changeStatus(absoluteTarget, expected) {
|
|
80
|
-
if (!(await pathExists(absoluteTarget))) {
|
|
81
|
-
return "create";
|
|
82
|
-
}
|
|
83
|
-
const normalize = (content) => content.replaceAll("\r\n", "\n").trim();
|
|
84
|
-
return normalize(await readFile(absoluteTarget, "utf-8")) ===
|
|
85
|
-
normalize(expected)
|
|
86
|
-
? "identical"
|
|
87
|
-
: "changed";
|
|
88
|
-
}
|
|
89
|
-
function requireExactCopyFile(type, owner) {
|
|
90
|
-
if (!EXACT_COPY_FILE_TYPES.has(type)) {
|
|
91
|
-
throw new CompositionValidationError("Customer add supports only exact-copy registry files in v1.", [
|
|
92
|
-
`${owner} uses ${type}; use registry:file or registry:item with an explicit target so the reviewed content is exactly what ShadCN writes`,
|
|
93
|
-
]);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
async function inspectFiles(workspaceRoot, tree) {
|
|
97
|
-
const registryFiles = await Promise.all((tree.files ?? []).map(async (file) => {
|
|
98
|
-
requireExactCopyFile(file.type, file.path);
|
|
99
|
-
if (!(file.target && file.content !== undefined)) {
|
|
100
|
-
throw new CompositionValidationError("Next Hydra Add-ons require explicit workspace-root file targets.", [`${file.path} must include content and a target beginning with ~/`]);
|
|
101
|
-
}
|
|
102
|
-
const target = resolveRegistryTarget(file.target);
|
|
103
|
-
return {
|
|
104
|
-
kind: "registry file",
|
|
105
|
-
status: await changeStatus(path.join(workspaceRoot, target), file.content),
|
|
106
|
-
target,
|
|
107
|
-
};
|
|
108
|
-
}));
|
|
109
|
-
const claimed = new Map();
|
|
110
|
-
for (const change of registryFiles) {
|
|
111
|
-
const existing = claimed.get(change.target);
|
|
112
|
-
if (existing) {
|
|
113
|
-
throw new CompositionValidationError("The requested Add-on targets the same customer file more than once.", [`${change.target} is both a ${existing} and a ${change.kind}`]);
|
|
114
|
-
}
|
|
115
|
-
claimed.set(change.target, change.kind);
|
|
116
|
-
}
|
|
117
|
-
return registryFiles.sort((left, right) => left.target.localeCompare(right.target));
|
|
118
|
-
}
|
|
119
|
-
async function inspectPackages(workspaceRoot, requirements, tree) {
|
|
120
|
-
const prospectiveManifests = new Map();
|
|
121
|
-
for (const file of tree.files ?? []) {
|
|
122
|
-
if (!(file.target && file.content !== undefined)) {
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
const target = resolveRegistryTarget(file.target);
|
|
126
|
-
if (target.endsWith("/package.json") || target === "package.json") {
|
|
127
|
-
prospectiveManifests.set(target, file.content);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return await Promise.all(requirements.map(async (requirement) => {
|
|
131
|
-
const relativeManifest = path.posix.join(requirement.cwd, "package.json");
|
|
132
|
-
const prospective = prospectiveManifests.get(relativeManifest);
|
|
133
|
-
const manifest = prospective === undefined
|
|
134
|
-
? await readPackageJson(path.join(workspaceRoot, relativeManifest), relativeManifest)
|
|
135
|
-
: parsePackageJson(prospective, relativeManifest);
|
|
136
|
-
const actual = manifest[requirement.section]?.[requirement.name];
|
|
137
|
-
return {
|
|
138
|
-
...requirement,
|
|
139
|
-
actual,
|
|
140
|
-
status: packageStatus(actual, requirement.specifier),
|
|
141
|
-
};
|
|
142
|
-
}));
|
|
143
|
-
}
|
|
144
|
-
async function inspectRootDependencies(workspaceRoot, tree) {
|
|
145
|
-
const manifest = await readPackageJson(path.join(workspaceRoot, "package.json"), "package.json");
|
|
146
|
-
const requests = [
|
|
147
|
-
...(tree.dependencies ?? []).map((request) => ({
|
|
148
|
-
...parseDependency(request),
|
|
149
|
-
section: "dependencies",
|
|
150
|
-
})),
|
|
151
|
-
...(tree.devDependencies ?? []).map((request) => ({
|
|
152
|
-
...parseDependency(request),
|
|
153
|
-
section: "devDependencies",
|
|
154
|
-
})),
|
|
155
|
-
];
|
|
156
|
-
return requests.map((request) => {
|
|
157
|
-
const actual = manifest[request.section]?.[request.name];
|
|
158
|
-
const specifier = request.specifier ?? actual ?? "registry default";
|
|
159
|
-
return {
|
|
160
|
-
actual,
|
|
161
|
-
cwd: ".",
|
|
162
|
-
name: request.name,
|
|
163
|
-
section: request.section,
|
|
164
|
-
specifier,
|
|
165
|
-
status: dependencyStatus(actual, request),
|
|
166
|
-
};
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
function usesProviderAlias(actual, packageName) {
|
|
170
|
-
if (!actual) {
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
return (actual === packageName ||
|
|
174
|
-
actual.startsWith(`workspace:${packageName}@`) ||
|
|
175
|
-
actual.startsWith(`npm:${packageName}@`));
|
|
176
|
-
}
|
|
177
|
-
function providerExpectation(selectionId, selectionsById) {
|
|
178
|
-
const selection = selectionsById.get(selectionId);
|
|
179
|
-
if (selection?.kind === "provider" && selection.slot && selection.binding) {
|
|
180
|
-
return {
|
|
181
|
-
dependency: PROVIDER_ALIASES[selection.slot],
|
|
182
|
-
specifier: selection.binding.specifier,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
return KNOWN_PROVIDERS.get(selectionId);
|
|
186
|
-
}
|
|
187
|
-
function matchesProviderExpectation(actual, expectation) {
|
|
188
|
-
return "specifier" in expectation
|
|
189
|
-
? actual === expectation.specifier
|
|
190
|
-
: usesProviderAlias(actual, expectation.packageName);
|
|
191
|
-
}
|
|
192
|
-
function validateKnownProviderCompatibility(manifest, selections) {
|
|
193
|
-
if (selections.length === 0) {
|
|
194
|
-
return [];
|
|
195
|
-
}
|
|
196
|
-
const issues = [];
|
|
197
|
-
const assumptions = [];
|
|
198
|
-
const selectionsById = new Map(selections.map((item) => [item.id, item]));
|
|
199
|
-
for (const selection of selections) {
|
|
200
|
-
if (selection.kind !== "provider") {
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
const provider = providerExpectation(selection.id, selectionsById);
|
|
204
|
-
if (!provider) {
|
|
205
|
-
issues.push(`${selection.id} is a Provider in the requested graph but does not declare a usable Provider binding`);
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
const actual = manifest.dependencies?.[provider.dependency];
|
|
209
|
-
if (!matchesProviderExpectation(actual, provider)) {
|
|
210
|
-
issues.push(`${selection.id} is a Provider in the requested graph, but the current provider alias ${provider.dependency} is ${actual ?? "missing"}`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
for (const selection of selections) {
|
|
214
|
-
for (const required of selection.compatibility.requires) {
|
|
215
|
-
const provider = providerExpectation(required, selectionsById);
|
|
216
|
-
const requiredSelection = selectionsById.get(required);
|
|
217
|
-
if (provider &&
|
|
218
|
-
!matchesProviderExpectation(manifest.dependencies?.[provider.dependency], provider)) {
|
|
219
|
-
issues.push(`${selection.id} requires ${required}`);
|
|
220
|
-
}
|
|
221
|
-
else if (!(provider || requiredSelection?.kind === "add-on")) {
|
|
222
|
-
assumptions.push(`${selection.id} requires ${required}; this customer workspace has no authoritative selection record, so Next Hydra cannot verify it`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
for (const conflict of selection.compatibility.conflicts) {
|
|
226
|
-
const provider = providerExpectation(conflict, selectionsById);
|
|
227
|
-
const conflictingSelection = selectionsById.get(conflict);
|
|
228
|
-
if ((provider &&
|
|
229
|
-
matchesProviderExpectation(manifest.dependencies?.[provider.dependency], provider)) ||
|
|
230
|
-
conflictingSelection?.kind === "add-on") {
|
|
231
|
-
issues.push(`${selection.id} conflicts with ${conflict}`);
|
|
232
|
-
}
|
|
233
|
-
else if (!provider) {
|
|
234
|
-
assumptions.push(`${selection.id} conflicts with ${conflict}; this customer workspace has no authoritative selection record, so Next Hydra cannot verify its absence`);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
if (issues.length > 0) {
|
|
239
|
-
throw new CompositionValidationError("The Add-on is incompatible with the current provider aliases.", issues);
|
|
240
|
-
}
|
|
241
|
-
return [...new Set(assumptions)].sort((left, right) => left.localeCompare(right));
|
|
242
|
-
}
|
|
243
|
-
function resolveCustomerProviderRequirements(manifest, selections) {
|
|
244
|
-
const requirements = [];
|
|
245
|
-
const issues = [];
|
|
246
|
-
for (const selection of selections) {
|
|
247
|
-
for (const dependency of selection.providerDependencies) {
|
|
248
|
-
const name = PROVIDER_ALIASES[dependency.slot];
|
|
249
|
-
const specifier = manifest.dependencies?.[name];
|
|
250
|
-
if (!specifier) {
|
|
251
|
-
issues.push(`${selection.id} needs the ${dependency.slot} Provider at ${dependency.cwd}, but apps/web/package.json does not declare ${name}`);
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
requirements.push({
|
|
255
|
-
cwd: dependency.cwd,
|
|
256
|
-
name,
|
|
257
|
-
section: dependency.section,
|
|
258
|
-
specifier,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
if (issues.length > 0) {
|
|
263
|
-
throw new CompositionValidationError("The Add-on's Provider dependencies could not be resolved from the customer workspace.", issues);
|
|
264
|
-
}
|
|
265
|
-
return requirements;
|
|
266
|
-
}
|
|
267
|
-
function validateRegistryTargetClaims(items) {
|
|
268
|
-
const claims = new Map();
|
|
269
|
-
const issues = [];
|
|
270
|
-
for (const item of items) {
|
|
271
|
-
for (const file of item.files ?? []) {
|
|
272
|
-
requireExactCopyFile(file.type, `${item.name}:${file.path}`);
|
|
273
|
-
if (!(file.target && file.content !== undefined)) {
|
|
274
|
-
throw new CompositionValidationError("Next Hydra Add-ons require explicit workspace-root file targets.", [
|
|
275
|
-
`${item.name}:${file.path} must include content and a target beginning with ~/`,
|
|
276
|
-
]);
|
|
277
|
-
}
|
|
278
|
-
const target = resolveRegistryTarget(file.target);
|
|
279
|
-
const owner = `${item.name}:${file.path}`;
|
|
280
|
-
const existing = claims.get(target);
|
|
281
|
-
if (existing && existing !== owner) {
|
|
282
|
-
issues.push(`${target} is claimed by both ${existing} and ${owner}`);
|
|
283
|
-
}
|
|
284
|
-
else {
|
|
285
|
-
claims.set(target, owner);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
if (issues.length > 0) {
|
|
290
|
-
throw new CompositionValidationError("The registry dependency graph targets the same customer file more than once.", issues);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
function describeShadcnEffects(tree, items) {
|
|
294
|
-
const hasValues = (value) => {
|
|
295
|
-
if (Array.isArray(value)) {
|
|
296
|
-
return value.some(hasValues);
|
|
297
|
-
}
|
|
298
|
-
if (value !== null && typeof value === "object") {
|
|
299
|
-
return Object.values(value).some(hasValues);
|
|
300
|
-
}
|
|
301
|
-
return (value !== undefined && value !== null && value !== false && value !== "");
|
|
302
|
-
};
|
|
303
|
-
const effects = [];
|
|
304
|
-
const envKeys = Object.keys(tree.envVars ?? {}).sort();
|
|
305
|
-
if (envKeys.length > 0) {
|
|
306
|
-
effects.push(`environment placeholders may be merged by ShadCN: ${envKeys.join(", ")}`);
|
|
307
|
-
}
|
|
308
|
-
if (hasValues(tree.tailwind)) {
|
|
309
|
-
effects.push("Tailwind configuration will be merged by ShadCN");
|
|
310
|
-
}
|
|
311
|
-
if (hasValues(tree.cssVars)) {
|
|
312
|
-
effects.push("CSS variables will be merged by ShadCN");
|
|
313
|
-
}
|
|
314
|
-
if (hasValues(tree.css)) {
|
|
315
|
-
effects.push("CSS declarations will be merged by ShadCN");
|
|
316
|
-
}
|
|
317
|
-
if ((tree.fonts?.length ?? 0) > 0) {
|
|
318
|
-
effects.push(`${tree.fonts?.length ?? 0} font definition(s) will be applied by ShadCN`);
|
|
319
|
-
}
|
|
320
|
-
if ([...items].some((item) => "config" in item && item.config !== undefined)) {
|
|
321
|
-
effects.push("ShadCN project configuration will be updated");
|
|
322
|
-
}
|
|
323
|
-
return [...new Set(effects)];
|
|
324
|
-
}
|
|
325
|
-
async function confirmChanges(fileChanges, packageChanges, options, prompt) {
|
|
326
|
-
const overwrite = options.overwrite ?? false;
|
|
327
|
-
const yes = options.yes === true;
|
|
328
|
-
const conflicts = [
|
|
329
|
-
...fileChanges
|
|
330
|
-
.filter((change) => change.status === "changed")
|
|
331
|
-
.map((change) => ({ kind: change.kind, label: change.target })),
|
|
332
|
-
...packageChanges
|
|
333
|
-
.filter((change) => change.status === "changed")
|
|
334
|
-
.map((change) => ({
|
|
335
|
-
kind: "dependency",
|
|
336
|
-
label: `${change.cwd}/package.json ${change.name}: ${change.actual} -> ${change.specifier}`,
|
|
337
|
-
})),
|
|
338
|
-
];
|
|
339
|
-
if (yes && !overwrite && conflicts.length > 0) {
|
|
340
|
-
throw new CompositionValidationError("Additive installation found customer-owned conflicts.", conflicts.map((conflict) => `${conflict.label} requires --overwrite; --yes only skips confirmation prompts for non-conflicting changes`));
|
|
341
|
-
}
|
|
342
|
-
if (!overwrite) {
|
|
343
|
-
for (const conflict of conflicts) {
|
|
344
|
-
// Without a global overwrite authorization, each customer-owned change
|
|
345
|
-
// receives its own explicit confirmation.
|
|
346
|
-
// oxlint-disable-next-line no-await-in-loop -- Sequential confirmation is the safety contract.
|
|
347
|
-
const approved = await prompt({
|
|
348
|
-
initialValue: false,
|
|
349
|
-
message: `Replace changed ${conflict.kind} ${conflict.label}?`,
|
|
350
|
-
});
|
|
351
|
-
if (isCancel(approved) || !approved) {
|
|
352
|
-
throw new Error("Installation cancelled. No files or package entries were changed.");
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
if (!yes) {
|
|
357
|
-
const approved = await prompt({
|
|
358
|
-
initialValue: true,
|
|
359
|
-
message: "Apply the listed Add-on changes?",
|
|
360
|
-
});
|
|
361
|
-
if (isCancel(approved) || !approved) {
|
|
362
|
-
throw new Error("Installation cancelled. No changes were made.");
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
export async function addRegistryItem(reference, options = {}, dependencies = {}) {
|
|
367
|
-
const cwd = path.resolve(options.cwd ?? process.cwd());
|
|
368
|
-
const localReference = path.resolve(cwd, reference);
|
|
369
|
-
const resolvedReference = !path.isAbsolute(reference) && (await pathExists(localReference))
|
|
370
|
-
? localReference
|
|
371
|
-
: reference;
|
|
372
|
-
const config = await getRegistriesConfig(cwd);
|
|
373
|
-
const graph = await fetchRegistryItemGraph({
|
|
374
|
-
config,
|
|
375
|
-
cwd,
|
|
376
|
-
references: [resolvedReference],
|
|
377
|
-
});
|
|
378
|
-
const primaryName = graph.itemByReference.get(resolvedReference);
|
|
379
|
-
const primary = primaryName ? graph.items.get(primaryName) : undefined;
|
|
380
|
-
if (!primary) {
|
|
381
|
-
throw new Error(`The registry returned no item for ${reference}.`);
|
|
382
|
-
}
|
|
383
|
-
const selection = parseSelection(primary);
|
|
384
|
-
const graphSelections = [...graph.items.values()]
|
|
385
|
-
.map(parseSelection)
|
|
386
|
-
.filter((value) => Boolean(value));
|
|
387
|
-
if (selection) {
|
|
388
|
-
if (selection.kind === "preset") {
|
|
389
|
-
throw new Error("Presets compose a new workspace. Use `create-next-hydra <directory> --preset ...` instead of `add`.");
|
|
390
|
-
}
|
|
391
|
-
if (selection.kind === "provider") {
|
|
392
|
-
throw new Error("Providers must be selected while scaffolding or with `create-next-hydra use`; `add` does not switch a customer workspace provider.");
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
const nestedPresets = graphSelections.filter((candidate) => candidate.kind === "preset");
|
|
396
|
-
if (nestedPresets.length > 0) {
|
|
397
|
-
throw new CompositionValidationError("Presets cannot be installed into a Customer Workspace.", nestedPresets.map((candidate) => `${candidate.id} appears in the requested registry dependency graph`));
|
|
398
|
-
}
|
|
399
|
-
const assetSelections = graphSelections.filter((candidate) => candidate.assets.length > 0);
|
|
400
|
-
if (assetSelections.length > 0) {
|
|
401
|
-
throw new CompositionValidationError("Customer Add-ons cannot install separate binary assets in v1.", assetSelections.map((candidate) => `${candidate.id} must put text files in its registry item; binary asset transport requires a future extension`));
|
|
402
|
-
}
|
|
403
|
-
const patchSelections = graphSelections.filter((candidate) => candidate.pnpmPatches.length > 0);
|
|
404
|
-
if (patchSelections.length > 0) {
|
|
405
|
-
throw new CompositionValidationError("Customer Add-ons cannot change pnpm patches in v1.", patchSelections.map((candidate) => `${candidate.id} must avoid pnpmPatches or be selected during a new scaffold`));
|
|
406
|
-
}
|
|
407
|
-
validateRegistryTargetClaims(graph.items.values());
|
|
408
|
-
const providerSelectionIds = new Set(graphSelections
|
|
409
|
-
.filter((candidate) => candidate.kind === "provider")
|
|
410
|
-
.map((candidate) => candidate.id));
|
|
411
|
-
const needsProviderManifest = graphSelections.some((candidate) => candidate.kind === "provider" ||
|
|
412
|
-
candidate.providerDependencies.length > 0 ||
|
|
413
|
-
[
|
|
414
|
-
...candidate.compatibility.requires,
|
|
415
|
-
...candidate.compatibility.conflicts,
|
|
416
|
-
].some((selectionId) => KNOWN_PROVIDERS.has(selectionId) ||
|
|
417
|
-
providerSelectionIds.has(selectionId)));
|
|
418
|
-
const providerManifest = needsProviderManifest
|
|
419
|
-
? await readPackageJson(path.join(cwd, "apps/web/package.json"), "apps/web/package.json")
|
|
420
|
-
: {};
|
|
421
|
-
const compatibilityAssumptions = validateKnownProviderCompatibility(providerManifest, graphSelections);
|
|
422
|
-
const packageRequirements = mergePackageRequirements([
|
|
423
|
-
...graphSelections.flatMap((candidate) => candidate.packages),
|
|
424
|
-
...resolveCustomerProviderRequirements(providerManifest, graphSelections),
|
|
425
|
-
]);
|
|
426
|
-
await withPreparedRegistryArtifacts({
|
|
427
|
-
artifacts: [...graph.items.values()],
|
|
428
|
-
entryItems: [primary.name],
|
|
429
|
-
itemByReference: graph.itemByReference,
|
|
430
|
-
run: async (entries) => {
|
|
431
|
-
const tree = await resolveRegistryItems(entries, { config });
|
|
432
|
-
if (!tree) {
|
|
433
|
-
throw new Error(`The registry dependency graph for ${reference} is empty.`);
|
|
434
|
-
}
|
|
435
|
-
const fileChanges = await inspectFiles(cwd, tree);
|
|
436
|
-
const packageChanges = await inspectPackages(cwd, packageRequirements, tree);
|
|
437
|
-
const rootDependencyChanges = await inspectRootDependencies(cwd, tree);
|
|
438
|
-
const disclosedPackageChanges = [
|
|
439
|
-
...packageChanges,
|
|
440
|
-
...rootDependencyChanges,
|
|
441
|
-
];
|
|
442
|
-
const shadcnEffects = describeShadcnEffects(tree, graph.items.values());
|
|
443
|
-
info([
|
|
444
|
-
`Registry item: ${primary.name}`,
|
|
445
|
-
"Files:",
|
|
446
|
-
...fileChanges.map((change) => ` ${change.status}: ${change.target} (${change.kind})`),
|
|
447
|
-
"Package entries:",
|
|
448
|
-
...disclosedPackageChanges.map((change) => ` ${change.status}: ${change.cwd} ${change.name} = ${change.specifier}`),
|
|
449
|
-
...(compatibilityAssumptions.length > 0
|
|
450
|
-
? [
|
|
451
|
-
"Compatibility assumptions:",
|
|
452
|
-
...compatibilityAssumptions.map((assumption) => ` ${assumption}`),
|
|
453
|
-
]
|
|
454
|
-
: []),
|
|
455
|
-
...(shadcnEffects.length > 0
|
|
456
|
-
? [
|
|
457
|
-
"Additional ShadCN effects:",
|
|
458
|
-
...shadcnEffects.map((effect) => ` ${effect}`),
|
|
459
|
-
]
|
|
460
|
-
: []),
|
|
461
|
-
].join("\n"));
|
|
462
|
-
await confirmChanges(fileChanges, disclosedPackageChanges, options, dependencies.confirm ?? confirm);
|
|
463
|
-
await addRegistryItemsQuietly(entries, {
|
|
464
|
-
config,
|
|
465
|
-
cwd,
|
|
466
|
-
overwrite: options.overwrite === true ||
|
|
467
|
-
fileChanges.some((change) => change.kind === "registry file" && change.status === "changed"),
|
|
468
|
-
});
|
|
469
|
-
const changedPackageEntries = packageChanges
|
|
470
|
-
.filter((change) => change.status !== "identical")
|
|
471
|
-
.map(({ status: _status, actual: _actual, ...requirement }) => requirement);
|
|
472
|
-
await applyPackageEntries(cwd, changedPackageEntries);
|
|
473
|
-
if (changedPackageEntries.length > 0) {
|
|
474
|
-
if (dependencies.install) {
|
|
475
|
-
await dependencies.install(cwd);
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
await runCommand("pnpm", ["install"], { cwd });
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
},
|
|
482
|
-
});
|
|
483
|
-
success(`Added ${primary.name}.`);
|
|
484
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { getRegistryItems } from "shadcn/registry";
|
|
2
|
-
import type { RegistryItem } from "shadcn/schema";
|
|
3
|
-
import type { CatalogSelection, RegistriesConfig, SourceRegistryCatalog } from "./types.js";
|
|
4
|
-
export declare function resolveRegistryItemGraph(catalog: SourceRegistryCatalog, entryItems: string[]): string[];
|
|
5
|
-
export type RegistryItemGraph = {
|
|
6
|
-
fetchedItemNames: Set<string>;
|
|
7
|
-
itemByReference: Map<string, string>;
|
|
8
|
-
items: Map<string, RegistryItem>;
|
|
9
|
-
};
|
|
10
|
-
export declare function fetchRegistryItemGraph(options: {
|
|
11
|
-
config: RegistriesConfig;
|
|
12
|
-
cwd: string;
|
|
13
|
-
fetchItems?: typeof getRegistryItems;
|
|
14
|
-
itemByReference?: Map<string, string>;
|
|
15
|
-
items?: Iterable<RegistryItem>;
|
|
16
|
-
references: Iterable<string>;
|
|
17
|
-
repository?: string;
|
|
18
|
-
}): Promise<RegistryItemGraph>;
|
|
19
|
-
export declare function loadSourceRegistryCatalog(cwd: string, registryFile?: string): Promise<SourceRegistryCatalog>;
|
|
20
|
-
export declare function loadGitHubSourceRegistryCatalog(repository: string, ref?: string, dependencies?: {
|
|
21
|
-
fetchItems?: (addresses: string[]) => RegistryItem[] | Promise<RegistryItem[]>;
|
|
22
|
-
loadRegistryConfig?: (cwd: string) => RegistriesConfig | Promise<RegistriesConfig>;
|
|
23
|
-
}): Promise<SourceRegistryCatalog>;
|
|
24
|
-
export declare function addCatalogReferences(catalog: SourceRegistryCatalog, references: Iterable<string>, cwd?: string): Promise<SourceRegistryCatalog>;
|
|
25
|
-
export declare function resolveCatalogSelection(catalog: SourceRegistryCatalog, reference: string): CatalogSelection;
|
|
26
|
-
//# sourceMappingURL=catalog.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../src/composition/catalog.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,gBAAgB,EAEjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAclD,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AA8KpB,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,qBAAqB,EAC9B,UAAU,EAAE,MAAM,EAAE,GACnB,MAAM,EAAE,CA+BV;AAaD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAClC,CAAC;AAEF,wBAAsB,sBAAsB,CAAC,OAAO,EAAE;IACpD,MAAM,EAAE,gBAAgB,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,OAAO,gBAAgB,CAAC;IACrC,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC/B,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA0F7B;AAED,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,MAAM,EACX,YAAY,SAAkB,GAC7B,OAAO,CAAC,qBAAqB,CAAC,CAqChC;AAED,wBAAsB,+BAA+B,CACnD,UAAU,EAAE,MAAM,EAClB,GAAG,CAAC,EAAE,MAAM,EACZ,YAAY,GAAE;IACZ,UAAU,CAAC,EAAE,CACX,SAAS,EAAE,MAAM,EAAE,KAChB,YAAY,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC9C,kBAAkB,CAAC,EAAE,CACnB,GAAG,EAAE,MAAM,KACR,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC9C,GACL,OAAO,CAAC,qBAAqB,CAAC,CA4BhC;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,qBAAqB,EAC9B,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC5B,GAAG,SAA+B,GACjC,OAAO,CAAC,qBAAqB,CAAC,CAiDhC;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,qBAAqB,EAC9B,SAAS,EAAE,MAAM,GAChB,gBAAgB,CASlB"}
|