create-next-hydra 0.2.1 → 0.4.1

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.
Files changed (204) hide show
  1. package/README.md +156 -26
  2. package/dist/add-command.d.ts +11 -0
  3. package/dist/add-command.d.ts.map +1 -0
  4. package/dist/add-command.js +75 -0
  5. package/dist/addition.d.ts +67 -0
  6. package/dist/addition.d.ts.map +1 -0
  7. package/dist/addition.js +422 -0
  8. package/dist/application-git.d.ts +20 -0
  9. package/dist/application-git.d.ts.map +1 -0
  10. package/dist/application-git.js +57 -0
  11. package/dist/baseline.d.ts +22 -0
  12. package/dist/baseline.d.ts.map +1 -0
  13. package/dist/baseline.js +90 -0
  14. package/dist/cli.js +5 -18
  15. package/dist/commands.d.ts +22 -0
  16. package/dist/commands.d.ts.map +1 -0
  17. package/dist/commands.js +197 -0
  18. package/dist/composition-lint.d.ts +20 -0
  19. package/dist/composition-lint.d.ts.map +1 -0
  20. package/dist/composition-lint.js +288 -0
  21. package/dist/composition.d.ts +24 -0
  22. package/dist/composition.d.ts.map +1 -0
  23. package/dist/composition.js +420 -0
  24. package/dist/create-command.d.ts +21 -0
  25. package/dist/create-command.d.ts.map +1 -0
  26. package/dist/create-command.js +128 -0
  27. package/dist/demo-instrumentation.d.ts +5 -0
  28. package/dist/demo-instrumentation.d.ts.map +1 -0
  29. package/dist/demo-instrumentation.js +300 -0
  30. package/dist/environment.d.ts +7 -0
  31. package/dist/environment.d.ts.map +1 -0
  32. package/dist/environment.js +81 -0
  33. package/dist/errors.d.ts +156 -0
  34. package/dist/errors.d.ts.map +1 -0
  35. package/dist/errors.js +140 -0
  36. package/dist/file-policy.d.ts +6 -0
  37. package/dist/file-policy.d.ts.map +1 -0
  38. package/dist/file-policy.js +38 -0
  39. package/dist/files.d.ts +7 -0
  40. package/dist/files.d.ts.map +1 -0
  41. package/dist/files.js +66 -0
  42. package/dist/hosting.d.ts +5 -0
  43. package/dist/hosting.d.ts.map +1 -0
  44. package/dist/hosting.js +107 -0
  45. package/dist/index.d.ts +4 -6
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +17 -89
  48. package/dist/initialization.d.ts +14 -0
  49. package/dist/initialization.d.ts.map +1 -0
  50. package/dist/initialization.js +266 -0
  51. package/dist/lint-workspaces.d.ts +2 -0
  52. package/dist/lint-workspaces.d.ts.map +1 -0
  53. package/dist/lint-workspaces.js +62 -0
  54. package/dist/model.d.ts +143 -0
  55. package/dist/model.d.ts.map +1 -0
  56. package/dist/model.js +61 -0
  57. package/dist/package-catalog.d.ts +8 -0
  58. package/dist/package-catalog.d.ts.map +1 -0
  59. package/dist/package-catalog.js +61 -0
  60. package/dist/packages.d.ts +57 -0
  61. package/dist/packages.d.ts.map +1 -0
  62. package/dist/packages.js +237 -0
  63. package/dist/process-group.d.ts +5 -0
  64. package/dist/process-group.d.ts.map +1 -0
  65. package/dist/process-group.js +23 -0
  66. package/dist/reference-workspace-bindings.d.ts +38 -0
  67. package/dist/reference-workspace-bindings.d.ts.map +1 -0
  68. package/dist/reference-workspace-bindings.js +41 -0
  69. package/dist/reference-workspace.d.ts +6 -0
  70. package/dist/reference-workspace.d.ts.map +1 -0
  71. package/dist/reference-workspace.js +62 -0
  72. package/dist/registry.d.ts +187 -0
  73. package/dist/registry.d.ts.map +1 -0
  74. package/dist/registry.js +107 -0
  75. package/dist/runtime.d.ts +9 -0
  76. package/dist/runtime.d.ts.map +1 -0
  77. package/dist/runtime.js +22 -0
  78. package/dist/selection.d.ts +704 -0
  79. package/dist/selection.d.ts.map +1 -0
  80. package/dist/selection.js +424 -0
  81. package/dist/shadcn-protocol.d.ts +22 -0
  82. package/dist/shadcn-protocol.d.ts.map +1 -0
  83. package/dist/shadcn-protocol.js +13 -0
  84. package/dist/shadcn-worker.d.ts +2 -0
  85. package/dist/shadcn-worker.d.ts.map +1 -0
  86. package/dist/shadcn-worker.js +43 -0
  87. package/dist/shadcn.d.ts +22 -0
  88. package/dist/shadcn.d.ts.map +1 -0
  89. package/dist/shadcn.js +101 -0
  90. package/dist/source-changes.d.ts +55 -0
  91. package/dist/source-changes.d.ts.map +1 -0
  92. package/dist/source-changes.js +225 -0
  93. package/dist/source-inventory.d.ts +25 -0
  94. package/dist/source-inventory.d.ts.map +1 -0
  95. package/dist/source-inventory.js +212 -0
  96. package/dist/source-root.d.ts +4 -0
  97. package/dist/source-root.d.ts.map +1 -0
  98. package/dist/source-root.js +20 -0
  99. package/dist/staging.d.ts +19 -0
  100. package/dist/staging.d.ts.map +1 -0
  101. package/dist/staging.js +42 -0
  102. package/dist/tasks.d.ts +44 -0
  103. package/dist/tasks.d.ts.map +1 -0
  104. package/dist/tasks.js +137 -0
  105. package/dist/templates.d.ts +217 -0
  106. package/dist/templates.d.ts.map +1 -0
  107. package/dist/templates.js +161 -0
  108. package/dist/test-reference-workspace.d.ts +2 -0
  109. package/dist/test-reference-workspace.d.ts.map +1 -0
  110. package/dist/test-reference-workspace.js +6 -0
  111. package/dist/types.d.ts +2 -9
  112. package/dist/types.d.ts.map +1 -1
  113. package/dist/typescript-paths.d.ts +10 -0
  114. package/dist/typescript-paths.d.ts.map +1 -0
  115. package/dist/typescript-paths.js +52 -0
  116. package/dist/workspace-dependencies.d.ts +26 -0
  117. package/dist/workspace-dependencies.d.ts.map +1 -0
  118. package/dist/workspace-dependencies.js +478 -0
  119. package/dist/workspace-files.d.ts +24 -0
  120. package/dist/workspace-files.d.ts.map +1 -0
  121. package/dist/workspace-files.js +395 -0
  122. package/dist/workspace-lock.d.ts +12 -0
  123. package/dist/workspace-lock.d.ts.map +1 -0
  124. package/dist/workspace-lock.js +104 -0
  125. package/dist/workspace-snapshots.d.ts +24 -0
  126. package/dist/workspace-snapshots.d.ts.map +1 -0
  127. package/dist/workspace-snapshots.js +218 -0
  128. package/dist/workspace-sources.d.ts +27 -0
  129. package/dist/workspace-sources.d.ts.map +1 -0
  130. package/dist/workspace-sources.js +88 -0
  131. package/dist/workspace-state.d.ts +325 -0
  132. package/dist/workspace-state.d.ts.map +1 -0
  133. package/dist/workspace-state.js +571 -0
  134. package/dist/workspaces.d.ts +73 -0
  135. package/dist/workspaces.d.ts.map +1 -0
  136. package/dist/workspaces.js +396 -0
  137. package/package.json +27 -15
  138. package/schema/selection-definition.json +206 -5
  139. package/dist/clone.d.ts +0 -9
  140. package/dist/clone.d.ts.map +0 -1
  141. package/dist/clone.js +0 -19
  142. package/dist/composition/add.d.ts +0 -14
  143. package/dist/composition/add.d.ts.map +0 -1
  144. package/dist/composition/add.js +0 -453
  145. package/dist/composition/catalog.d.ts +0 -26
  146. package/dist/composition/catalog.d.ts.map +0 -1
  147. package/dist/composition/catalog.js +0 -313
  148. package/dist/composition/errors.d.ts +0 -5
  149. package/dist/composition/errors.d.ts.map +0 -1
  150. package/dist/composition/errors.js +0 -8
  151. package/dist/composition/format.d.ts +0 -6
  152. package/dist/composition/format.d.ts.map +0 -1
  153. package/dist/composition/format.js +0 -63
  154. package/dist/composition/install.d.ts +0 -16
  155. package/dist/composition/install.d.ts.map +0 -1
  156. package/dist/composition/install.js +0 -175
  157. package/dist/composition/packages.d.ts +0 -6
  158. package/dist/composition/packages.d.ts.map +0 -1
  159. package/dist/composition/packages.js +0 -54
  160. package/dist/composition/paths.d.ts +0 -4
  161. package/dist/composition/paths.d.ts.map +0 -1
  162. package/dist/composition/paths.js +0 -47
  163. package/dist/composition/planner.d.ts +0 -4
  164. package/dist/composition/planner.d.ts.map +0 -1
  165. package/dist/composition/planner.js +0 -320
  166. package/dist/composition/schema.d.ts +0 -235
  167. package/dist/composition/schema.d.ts.map +0 -1
  168. package/dist/composition/schema.js +0 -115
  169. package/dist/composition/types.d.ts +0 -87
  170. package/dist/composition/types.d.ts.map +0 -1
  171. package/dist/composition/types.js +0 -6
  172. package/dist/composition/use.d.ts +0 -23
  173. package/dist/composition/use.d.ts.map +0 -1
  174. package/dist/composition/use.js +0 -182
  175. package/dist/composition/workspace.d.ts +0 -10
  176. package/dist/composition/workspace.d.ts.map +0 -1
  177. package/dist/composition/workspace.js +0 -208
  178. package/dist/constants.d.ts +0 -11
  179. package/dist/constants.d.ts.map +0 -1
  180. package/dist/constants.js +0 -38
  181. package/dist/fs-utils.d.ts +0 -9
  182. package/dist/fs-utils.d.ts.map +0 -1
  183. package/dist/fs-utils.js +0 -64
  184. package/dist/git.d.ts +0 -28
  185. package/dist/git.d.ts.map +0 -1
  186. package/dist/git.js +0 -107
  187. package/dist/instruction-renderer.d.ts +0 -17
  188. package/dist/instruction-renderer.d.ts.map +0 -1
  189. package/dist/instruction-renderer.js +0 -132
  190. package/dist/logger.d.ts +0 -13
  191. package/dist/logger.d.ts.map +0 -1
  192. package/dist/logger.js +0 -26
  193. package/dist/prompts.d.ts +0 -7
  194. package/dist/prompts.d.ts.map +0 -1
  195. package/dist/prompts.js +0 -47
  196. package/dist/sanitize.d.ts +0 -10
  197. package/dist/sanitize.d.ts.map +0 -1
  198. package/dist/sanitize.js +0 -23
  199. package/dist/scaffold.d.ts +0 -7
  200. package/dist/scaffold.d.ts.map +0 -1
  201. package/dist/scaffold.js +0 -325
  202. package/dist/version.d.ts +0 -2
  203. package/dist/version.d.ts.map +0 -1
  204. package/dist/version.js +0 -2
@@ -1,313 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import { isDeepStrictEqual } from "node:util";
4
- import { getRegistriesConfig, getRegistryItems, loadRegistry, } from "shadcn/registry";
5
- import { pathExists } from "../fs-utils.js";
6
- import { CompositionValidationError } from "./errors.js";
7
- import { isManagedApplicationSource, resolveRegistryTarget, resolveWorkspacePath, } from "./paths.js";
8
- import { formatZodError, NEXT_HYDRA_SELECTION_SCHEMA_URL, selectionDefinitionSchema, } from "./schema.js";
9
- const GITHUB_HOMEPAGE_PATTERN = /^https:\/\/github\.com\/([^/]+\/[^/]+?)(?:\.git)?\/?$/;
10
- const SHADCN_REGISTRY_ITEM_SCHEMA_URL = "https://ui.shadcn.com/schema/registry-item.json";
11
- const OFFICIAL_REFERENCES = {
12
- commercetools: "next-hydra/commerce/commercetools",
13
- contentstack: "next-hydra/cms/contentstack",
14
- drupal: "next-hydra/cms/drupal",
15
- standard: "next-hydra/preset/standard",
16
- workos: "next-hydra/auth/workos",
17
- };
18
- const OFFICIAL_ITEM_NAMES = [
19
- "auth-workos",
20
- "cms-contentstack",
21
- "cms-drupal",
22
- "commerce-commercetools",
23
- "drupal",
24
- "next-hydra-standard",
25
- ];
26
- function restoreFetchedSelectionSchema(item) {
27
- // ShadCN assigns its registry-item schema to every resolved artifact,
28
- // including artifacts authored with the Next Hydra Selection schema.
29
- if (item.meta?.nextHydra !== undefined &&
30
- item.$schema === SHADCN_REGISTRY_ITEM_SCHEMA_URL) {
31
- return { ...item, $schema: NEXT_HYDRA_SELECTION_SCHEMA_URL };
32
- }
33
- return item;
34
- }
35
- function createCatalog(options) {
36
- const items = new Map(options.registryItems.map((item) => [item.name, item]));
37
- const itemByReference = new Map(options.itemByReference);
38
- const selections = [];
39
- const issues = [];
40
- for (const item of options.registryItems) {
41
- itemByReference.set(item.name, item.name);
42
- if (options.repository) {
43
- itemByReference.set(`${options.repository}/${item.name}`, item.name);
44
- }
45
- }
46
- for (const item of options.registryItems) {
47
- const candidate = item.meta?.nextHydra;
48
- if (candidate === undefined) {
49
- continue;
50
- }
51
- if (item.$schema !== NEXT_HYDRA_SELECTION_SCHEMA_URL) {
52
- issues.push(`${item.name}.$schema must be ${NEXT_HYDRA_SELECTION_SCHEMA_URL}`);
53
- continue;
54
- }
55
- const result = selectionDefinitionSchema.safeParse(candidate);
56
- if (!result.success) {
57
- issues.push(...formatZodError(result.error).map((issue) => `${item.name}.meta.nextHydra.${issue}`));
58
- continue;
59
- }
60
- selections.push({ ...result.data, itemName: item.name });
61
- }
62
- const byId = new Map();
63
- const byReference = new Map();
64
- for (const selection of selections) {
65
- const duplicateItemName = byId.get(selection.id)?.itemName;
66
- if (duplicateItemName !== undefined) {
67
- issues.push(`selection ID ${selection.id} is declared by both ${duplicateItemName} and ${selection.itemName}`);
68
- continue;
69
- }
70
- byId.set(selection.id, selection);
71
- byReference.set(selection.itemName, selection);
72
- byReference.set(selection.id, selection);
73
- itemByReference.set(selection.id, selection.itemName);
74
- }
75
- for (const [reference, id] of Object.entries(OFFICIAL_REFERENCES)) {
76
- const selection = byId.get(id);
77
- if (selection) {
78
- byReference.set(reference, selection);
79
- itemByReference.set(reference, selection.itemName);
80
- }
81
- }
82
- for (const [reference, itemName] of itemByReference) {
83
- const selection = selections.find((candidate) => candidate.itemName === itemName);
84
- if (selection) {
85
- byReference.set(reference, selection);
86
- }
87
- }
88
- if (issues.length > 0) {
89
- throw new CompositionValidationError("The source registry contains invalid Next Hydra metadata.", issues);
90
- }
91
- const managedTargets = [
92
- ...new Set(options.registryItems.flatMap((item) => item.files
93
- ?.filter((file) => isManagedApplicationSource(file.path, file.target))
94
- .map((file) => {
95
- if (!file.target) {
96
- throw new CompositionValidationError("Managed application files require explicit targets.", [`${item.name}:${file.path} does not declare files[].target`]);
97
- }
98
- return resolveRegistryTarget(file.target);
99
- }) ?? [])),
100
- ].sort((left, right) => left.localeCompare(right));
101
- return {
102
- authoringPaths: options.authoringPaths,
103
- byId,
104
- byReference,
105
- cwd: options.cwd,
106
- itemByReference,
107
- items,
108
- managedTargets,
109
- registryConfig: options.registryConfig,
110
- registryFile: options.registryFile,
111
- repository: options.repository,
112
- selections: [...selections].sort((left, right) => left.id.localeCompare(right.id)),
113
- };
114
- }
115
- function registryDependencyName(catalog, reference) {
116
- return catalog.itemByReference.get(reference);
117
- }
118
- export function resolveRegistryItemGraph(catalog, entryItems) {
119
- const resolved = new Set();
120
- const pending = [...entryItems];
121
- while (pending.length > 0) {
122
- const itemName = pending.pop();
123
- if (!itemName || resolved.has(itemName)) {
124
- continue;
125
- }
126
- const item = catalog.items.get(itemName);
127
- if (!item) {
128
- throw new CompositionValidationError("Missing registry item.", [
129
- `${itemName} is not in the source registry`,
130
- ]);
131
- }
132
- resolved.add(itemName);
133
- for (const reference of item.registryDependencies ?? []) {
134
- const dependency = registryDependencyName(catalog, reference);
135
- if (!dependency) {
136
- throw new CompositionValidationError("Registry dependency graph is incomplete.", [`${itemName} depends on unresolved item ${reference}`]);
137
- }
138
- if (!resolved.has(dependency)) {
139
- pending.push(dependency);
140
- }
141
- }
142
- }
143
- return [...resolved].sort((left, right) => left.localeCompare(right));
144
- }
145
- async function resolveRegistryReference(reference, cwd) {
146
- if (path.isAbsolute(reference)) {
147
- return reference;
148
- }
149
- const localReference = path.resolve(cwd, reference);
150
- return (await pathExists(localReference)) ? localReference : reference;
151
- }
152
- export async function fetchRegistryItemGraph(options) {
153
- const items = new Map([...(options.items ?? [])].map((item) => [item.name, item]));
154
- const itemByReference = new Map(options.itemByReference);
155
- const pending = [...new Set(options.references)];
156
- const expandedItems = new Set();
157
- const fetchedItemNames = new Set();
158
- const fetchedItems = new Map();
159
- const fetchItems = options.fetchItems ?? getRegistryItems;
160
- for (const item of items.values()) {
161
- itemByReference.set(item.name, item.name);
162
- if (options.repository) {
163
- itemByReference.set(`${options.repository}/${item.name}`, item.name);
164
- }
165
- }
166
- while (pending.length > 0) {
167
- const reference = pending.shift();
168
- if (!reference) {
169
- continue;
170
- }
171
- let itemName = itemByReference.get(reference);
172
- if (!itemName) {
173
- // oxlint-disable-next-line no-await-in-loop -- Breadth-first discovery reveals each next reference in order.
174
- const resolvedReference = await resolveRegistryReference(reference, options.cwd);
175
- const [fetchedArtifact] = await fetchItems([resolvedReference], {
176
- config: options.config,
177
- });
178
- if (!fetchedArtifact) {
179
- throw new CompositionValidationError("The registry dependency graph is incomplete.", [`${reference} returned no registry item`]);
180
- }
181
- const artifact = restoreFetchedSelectionSchema(fetchedArtifact);
182
- const fetched = fetchedItems.get(artifact.name);
183
- const unpinnedReference = reference.split("#", 1)[0] ?? reference;
184
- const replacesPinnedRepositoryItem = reference.includes("#") &&
185
- options.repository !== undefined &&
186
- unpinnedReference === `${options.repository}/${artifact.name}`;
187
- const existing = items.get(artifact.name);
188
- if ((fetched && !isDeepStrictEqual(fetched, artifact)) ||
189
- (existing &&
190
- !isDeepStrictEqual(existing, artifact) &&
191
- !replacesPinnedRepositoryItem)) {
192
- throw new CompositionValidationError("Registry item names conflict.", [
193
- `${artifact.name} resolves to different content through ${reference}`,
194
- ]);
195
- }
196
- if (existing &&
197
- !isDeepStrictEqual(existing, artifact) &&
198
- replacesPinnedRepositoryItem) {
199
- expandedItems.delete(artifact.name);
200
- }
201
- items.set(artifact.name, artifact);
202
- fetchedItems.set(artifact.name, artifact);
203
- fetchedItemNames.add(artifact.name);
204
- itemName = artifact.name;
205
- itemByReference.set(reference, itemName);
206
- itemByReference.set(resolvedReference, itemName);
207
- itemByReference.set(itemName, itemName);
208
- }
209
- if (expandedItems.has(itemName)) {
210
- continue;
211
- }
212
- const item = items.get(itemName);
213
- if (!item) {
214
- throw new CompositionValidationError("The registry dependency graph is incomplete.", [`${reference} resolves to missing item ${itemName}`]);
215
- }
216
- expandedItems.add(itemName);
217
- pending.push(...(item.registryDependencies ?? []));
218
- }
219
- return { fetchedItemNames, itemByReference, items };
220
- }
221
- export async function loadSourceRegistryCatalog(cwd, registryFile = "registry.json") {
222
- const resolvedCwd = path.resolve(cwd);
223
- const safeRegistryFile = resolveWorkspacePath(registryFile, "source registry file");
224
- const registryPath = path.resolve(resolvedCwd, safeRegistryFile);
225
- const sourceRegistry = JSON.parse(await readFile(registryPath, "utf-8"));
226
- const repository = sourceRegistry.homepage?.match(GITHUB_HOMEPAGE_PATTERN)?.[1];
227
- const registry = await loadRegistry({
228
- cwd: resolvedCwd,
229
- registryFile: safeRegistryFile,
230
- });
231
- const registryConfig = await getRegistriesConfig(resolvedCwd);
232
- const includedRegistries = (sourceRegistry.include ?? []).map((included) => resolveWorkspacePath(included, "source registry include"));
233
- const registrySourcePaths = includedRegistries.map((included) => path.posix.join(path.posix.dirname(included), "registry"));
234
- return createCatalog({
235
- authoringPaths: [
236
- safeRegistryFile,
237
- ...includedRegistries,
238
- ...registrySourcePaths,
239
- ].sort((left, right) => left.localeCompare(right)),
240
- cwd: resolvedCwd,
241
- registryConfig,
242
- registryFile: safeRegistryFile,
243
- registryItems: registry.items,
244
- repository,
245
- });
246
- }
247
- export async function loadGitHubSourceRegistryCatalog(repository, ref, dependencies = {}) {
248
- const suffix = ref ? `#${ref}` : "";
249
- const addresses = OFFICIAL_ITEM_NAMES.map((item) => `${repository}/${item}${suffix}`);
250
- const fetchedItems = await (dependencies.fetchItems ?? getRegistryItems)(addresses);
251
- const registryItems = fetchedItems.map(restoreFetchedSelectionSchema);
252
- const registryConfig = await (dependencies.loadRegistryConfig ?? getRegistriesConfig)(process.cwd());
253
- const itemByReference = new Map(registryItems.flatMap((item, index) => {
254
- const address = addresses[index];
255
- return address ? [[address, item.name]] : [];
256
- }));
257
- return createCatalog({
258
- authoringPaths: [],
259
- cwd: "",
260
- itemByReference,
261
- registryConfig,
262
- registryFile: `${repository}/registry.json${suffix}`,
263
- registryItems,
264
- repository,
265
- });
266
- }
267
- export async function addCatalogReferences(catalog, references, cwd = catalog.cwd || process.cwd()) {
268
- const registryConfig = await getRegistriesConfig(cwd);
269
- const requestedReferences = [...new Set(references)];
270
- const graph = await fetchRegistryItemGraph({
271
- config: registryConfig,
272
- cwd,
273
- itemByReference: catalog.itemByReference,
274
- items: catalog.items.values(),
275
- references: requestedReferences,
276
- repository: catalog.repository,
277
- });
278
- const current = createCatalog({
279
- authoringPaths: catalog.authoringPaths,
280
- cwd: catalog.cwd,
281
- itemByReference: graph.itemByReference,
282
- registryConfig,
283
- registryFile: catalog.registryFile,
284
- registryItems: [...graph.items.values()],
285
- repository: catalog.repository,
286
- });
287
- for (const reference of requestedReferences) {
288
- const itemName = current.itemByReference.get(reference);
289
- const selection = itemName ? current.byReference.get(itemName) : undefined;
290
- if (!selection) {
291
- throw new CompositionValidationError("The requested registry item is not a Next Hydra selection.", [`${reference} does not contain meta.nextHydra`]);
292
- }
293
- current.byReference.set(reference, selection);
294
- }
295
- for (const itemName of graph.fetchedItemNames) {
296
- const externalSelection = current.byReference.get(itemName);
297
- if (externalSelection !== undefined && externalSelection.assets.length > 0) {
298
- throw new CompositionValidationError("External selections cannot declare separate binary assets in v1.", [
299
- `${externalSelection.id} must be included by the starter source registry; separately fetched binary assets are not supported`,
300
- ]);
301
- }
302
- }
303
- return current;
304
- }
305
- export function resolveCatalogSelection(catalog, reference) {
306
- const selection = catalog.byReference.get(reference);
307
- if (!selection) {
308
- throw new CompositionValidationError("Unknown composition selection.", [
309
- `${reference} is not present in ${catalog.registryFile}`,
310
- ]);
311
- }
312
- return selection;
313
- }
@@ -1,5 +0,0 @@
1
- export declare class CompositionValidationError extends Error {
2
- readonly issues: string[];
3
- constructor(message: string, issues: string[], options?: ErrorOptions);
4
- }
5
- //# sourceMappingURL=errors.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/composition/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAE1B,YAAY,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,EAOpE;CACF"}
@@ -1,8 +0,0 @@
1
- export class CompositionValidationError extends Error {
2
- issues;
3
- constructor(message, issues, options) {
4
- super(`${message}\n${issues.map((issue) => `- ${issue}`).join("\n")}`, options);
5
- this.name = "CompositionValidationError";
6
- this.issues = issues;
7
- }
8
- }
@@ -1,6 +0,0 @@
1
- import type { CompositionPlan, WorkspaceSelection } from "./types.js";
2
- export declare function hasCompositionChanges(current: WorkspaceSelection, proposed: WorkspaceSelection): boolean;
3
- export declare function formatCompositionPreview(current: WorkspaceSelection, proposed: WorkspaceSelection): string;
4
- export declare function formatCompositionResult(current: WorkspaceSelection, proposed: WorkspaceSelection): string;
5
- export declare function formatCompositionPlan(plan: CompositionPlan): string;
6
- //# sourceMappingURL=format.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/composition/format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAwCtE,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,kBAAkB,GAC3B,OAAO,CAET;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,kBAAkB,GAC3B,MAAM,CA4BR;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,kBAAkB,GAC3B,MAAM,CAKR;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAuBnE"}
@@ -1,63 +0,0 @@
1
- import { PROVIDER_SLOTS } from "./types.js";
2
- const NO_ADD_ONS = "none";
3
- function formatAddOns(addOns) {
4
- return addOns.length > 0 ? [...addOns].sort().join(", ") : NO_ADD_ONS;
5
- }
6
- function sameAddOns(current, proposed) {
7
- return formatAddOns(current) === formatAddOns(proposed);
8
- }
9
- function formatTransition(current, proposed) {
10
- return current === proposed
11
- ? `${current} (unchanged)`
12
- : `${current} -> ${proposed}`;
13
- }
14
- function compositionChanges(current, proposed) {
15
- const providerChanges = PROVIDER_SLOTS.filter((slot) => current.providers[slot] !== proposed.providers[slot]).map((slot) => `${slot}: ${current.providers[slot]} -> ${proposed.providers[slot]}`);
16
- const addOnChanges = sameAddOns(current.addOns, proposed.addOns)
17
- ? []
18
- : [
19
- `add-ons: ${formatAddOns(current.addOns)} -> ${formatAddOns(proposed.addOns)}`,
20
- ];
21
- return [...providerChanges, ...addOnChanges];
22
- }
23
- export function hasCompositionChanges(current, proposed) {
24
- return compositionChanges(current, proposed).length > 0;
25
- }
26
- export function formatCompositionPreview(current, proposed) {
27
- const currentAddOns = formatAddOns(current.addOns);
28
- const proposedAddOns = formatAddOns(proposed.addOns);
29
- const lines = [
30
- "Maintainer workspace composition",
31
- "",
32
- "Current -> Proposed",
33
- "Providers:",
34
- ...PROVIDER_SLOTS.map((slot) => ` ${slot}: ${formatTransition(current.providers[slot], proposed.providers[slot])}`),
35
- `Add-ons: ${currentAddOns === proposedAddOns ? `${currentAddOns} (unchanged)` : `${currentAddOns} -> ${proposedAddOns}`}`,
36
- ];
37
- if (hasCompositionChanges(current, proposed)) {
38
- lines.push("", "Planned actions:", " replace managed application files", " install selected source", " update package aliases", " update pnpm patches", " run pnpm install");
39
- }
40
- return lines.join("\n");
41
- }
42
- export function formatCompositionResult(current, proposed) {
43
- const changes = compositionChanges(current, proposed);
44
- return changes.length > 0
45
- ? `Maintainer workspace composition updated: ${changes.join("; ")}.`
46
- : "No composition changes to apply.";
47
- }
48
- export function formatCompositionPlan(plan) {
49
- const lines = [
50
- "Providers:",
51
- ...Object.entries(plan.selection.providers).map(([slot, provider]) => ` ${slot}: ${provider}`),
52
- `Add-ons: ${plan.selection.addOns.length > 0 ? plan.selection.addOns.join(", ") : "none"}`,
53
- "Registry items:",
54
- ...plan.registryItems.map((item) => ` ${item}`),
55
- "Package entries:",
56
- ...plan.packageRequirements.map((requirement) => ` ${requirement.cwd}: ${requirement.name} = ${requirement.specifier}`),
57
- "pnpm patches:",
58
- ...plan.pnpmPatches.map((patch) => ` ${patch.dependency} -> ${patch.path}`),
59
- "Managed application files:",
60
- ...plan.managedTargets.map((target) => ` ${target}`),
61
- ];
62
- return lines.join("\n");
63
- }
@@ -1,16 +0,0 @@
1
- import { addRegistryItems } from "shadcn/registry";
2
- import type { RegistryItem } from "shadcn/schema";
3
- import type { CompositionPlan, PreparedComposition, SourceRegistryCatalog } from "./types.js";
4
- type AddRegistryItemsOptions = Omit<NonNullable<Parameters<typeof addRegistryItems>[1]>, "silent">;
5
- export declare function addRegistryItemsQuietly(items: string[], options: AddRegistryItemsOptions): Promise<void>;
6
- export declare function prepareComposition(catalog: SourceRegistryCatalog, plan: CompositionPlan): Promise<PreparedComposition>;
7
- export declare function withPreparedRegistryArtifacts<T>(options: {
8
- artifacts: RegistryItem[];
9
- entryItems: string[];
10
- itemByReference: Map<string, string>;
11
- run: (entries: string[]) => Promise<T>;
12
- }): Promise<T>;
13
- export declare function validatePackageRequirementTargets(workspaceRoot: string, plan: CompositionPlan, prepared: PreparedComposition, removedTargets?: Iterable<string>): Promise<void>;
14
- export declare function installPreparedComposition(workspaceRoot: string, prepared: PreparedComposition): Promise<void>;
15
- export {};
16
- //# sourceMappingURL=install.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/composition/install.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAoB,MAAM,iBAAiB,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAMlD,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAIpB,KAAK,uBAAuB,GAAG,IAAI,CACjC,WAAW,CAAC,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,EACnD,QAAQ,CACT,CAAC;AA0BF,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAIf;AAgBD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,qBAAqB,EAC9B,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,mBAAmB,CAAC,CAuC9B;AAWD,wBAAsB,6BAA6B,CAAC,CAAC,EAAE,OAAO,EAAE;IAC9D,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CACxC,GAAG,OAAO,CAAC,CAAC,CAAC,CAkDb;AAED,wBAAsB,iCAAiC,CACrD,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,eAAe,EACrB,QAAQ,EAAE,mBAAmB,EAC7B,cAAc,GAAE,QAAQ,CAAC,MAAM,CAAM,GACpC,OAAO,CAAC,IAAI,CAAC,CAmDf;AAED,wBAAsB,0BAA0B,CAC9C,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAwBf"}
@@ -1,175 +0,0 @@
1
- import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
3
- import path from "node:path";
4
- import { addRegistryItems, loadRegistryItem } from "shadcn/registry";
5
- import { pathExists } from "../fs-utils.js";
6
- import { CompositionValidationError } from "./errors.js";
7
- import { parsePackageJson, readPackageJson } from "./packages.js";
8
- import { isManagedApplicationSource, resolveRegistryTarget } from "./paths.js";
9
- const SHADCN_ENVIRONMENT_HEADING = "Added the following variables to";
10
- async function suppressShadcnEnvironmentHeading(operation) {
11
- const originalWrite = process.stderr.write;
12
- process.stderr.write = (...args) => {
13
- const [chunk] = args;
14
- // ShadCN 4.16.2 emits this Ora heading even when `silent` is enabled.
15
- if (String(chunk).includes(SHADCN_ENVIRONMENT_HEADING)) {
16
- const callback = args.find((argument) => typeof argument === "function");
17
- callback?.();
18
- return true;
19
- }
20
- return Reflect.apply(originalWrite, process.stderr, args);
21
- };
22
- try {
23
- return await operation();
24
- }
25
- finally {
26
- process.stderr.write = originalWrite;
27
- }
28
- }
29
- export async function addRegistryItemsQuietly(items, options) {
30
- await suppressShadcnEnvironmentHeading(async () => {
31
- await addRegistryItems(items, { ...options, silent: true });
32
- });
33
- }
34
- function resolveArtifact(catalog, item) {
35
- const resolved = catalog.items.get(item);
36
- if (resolved &&
37
- (resolved.files ?? []).every((file) => file.content !== undefined)) {
38
- return resolved;
39
- }
40
- return loadRegistryItem(item, {
41
- cwd: catalog.cwd,
42
- registryFile: catalog.registryFile,
43
- });
44
- }
45
- export async function prepareComposition(catalog, plan) {
46
- const artifacts = await Promise.all([...catalog.items].map(([item]) => resolveArtifact(catalog, item)));
47
- const selectedItemNames = new Set(plan.registryItems);
48
- const managedFiles = artifacts
49
- .filter((artifact) => selectedItemNames.has(artifact.name))
50
- .flatMap((artifact) => artifact.files
51
- ?.filter((file) => isManagedApplicationSource(file.path, file.target))
52
- .map((file) => {
53
- if (!(file.target && file.content !== undefined)) {
54
- throw new CompositionValidationError("Managed application files require targets and resolved content.", [`${artifact.name}:${file.path} cannot be prepared`]);
55
- }
56
- return {
57
- content: file.content,
58
- target: resolveRegistryTarget(file.target),
59
- };
60
- }) ?? [])
61
- .sort((left, right) => left.target.localeCompare(right.target));
62
- return {
63
- artifacts,
64
- assets: await Promise.all(plan.assets.map(async (asset) => ({
65
- ...asset,
66
- content: await readFile(path.join(catalog.cwd, asset.source)),
67
- }))),
68
- entryItems: plan.entryItems,
69
- itemByReference: new Map(catalog.itemByReference),
70
- managedFiles,
71
- registryConfig: catalog.registryConfig,
72
- };
73
- }
74
- function localDependencyName(reference, itemByReference, artifactPaths) {
75
- const itemName = itemByReference.get(reference);
76
- return itemName && artifactPaths.has(itemName) ? itemName : undefined;
77
- }
78
- export async function withPreparedRegistryArtifacts(options) {
79
- const artifactDirectory = await mkdtemp(path.join(tmpdir(), "next-hydra-registry-"));
80
- try {
81
- const artifactPaths = new Map(options.artifacts.map((artifact, index) => [
82
- artifact.name,
83
- path.join(artifactDirectory, `${index}.json`),
84
- ]));
85
- await Promise.all(options.artifacts.map(async (artifact) => {
86
- const artifactPath = artifactPaths.get(artifact.name);
87
- if (!artifactPath) {
88
- throw new Error(`Missing prepared registry item ${artifact.name}.`);
89
- }
90
- const registryDependencies = artifact.registryDependencies?.map((reference) => {
91
- const localName = localDependencyName(reference, options.itemByReference, artifactPaths);
92
- return ((localName ? artifactPaths.get(localName) : undefined) ??
93
- reference);
94
- });
95
- await writeFile(artifactPath, `${JSON.stringify({ ...artifact, registryDependencies }, null, 2)}\n`, "utf-8");
96
- }));
97
- const entries = options.entryItems.map((item) => {
98
- const artifactPath = artifactPaths.get(item);
99
- if (!artifactPath) {
100
- throw new Error(`Missing prepared registry entry ${item}.`);
101
- }
102
- return artifactPath;
103
- });
104
- return await options.run(entries);
105
- }
106
- finally {
107
- await rm(artifactDirectory, { force: true, recursive: true });
108
- }
109
- }
110
- export async function validatePackageRequirementTargets(workspaceRoot, plan, prepared, removedTargets = []) {
111
- const selectedItems = new Set(plan.registryItems);
112
- const removed = new Set(removedTargets);
113
- const prospectiveManifests = new Map();
114
- for (const artifact of prepared.artifacts) {
115
- if (!selectedItems.has(artifact.name)) {
116
- continue;
117
- }
118
- for (const file of artifact.files ?? []) {
119
- if (!(file.target && file.content !== undefined)) {
120
- continue;
121
- }
122
- const target = resolveRegistryTarget(file.target);
123
- if (target.endsWith("/package.json") || target === "package.json") {
124
- prospectiveManifests.set(target, file.content);
125
- }
126
- }
127
- }
128
- const issues = (await Promise.all(plan.packageRequirements.map(async (requirement) => {
129
- const manifest = path.posix.join(requirement.cwd, "package.json");
130
- const prospective = prospectiveManifests.get(manifest);
131
- try {
132
- if (prospective !== undefined) {
133
- parsePackageJson(prospective, manifest);
134
- }
135
- else if (!removed.has(manifest) &&
136
- (await pathExists(path.join(workspaceRoot, manifest)))) {
137
- await readPackageJson(path.join(workspaceRoot, manifest), manifest);
138
- }
139
- else {
140
- return `${manifest} is not present or supplied by the selected graph`;
141
- }
142
- }
143
- catch (error) {
144
- if (error instanceof CompositionValidationError) {
145
- return `${manifest}: ${error.issues.join("; ")}`;
146
- }
147
- return `${manifest} could not be read`;
148
- }
149
- }))).filter((issue) => issue !== undefined);
150
- if (issues.length > 0) {
151
- throw new CompositionValidationError("Package requirements have invalid package.json targets.", [...new Set(issues)].sort((left, right) => left.localeCompare(right)));
152
- }
153
- }
154
- export async function installPreparedComposition(workspaceRoot, prepared) {
155
- await Promise.all(prepared.assets.map(async (asset) => {
156
- const target = path.join(workspaceRoot, asset.target);
157
- await mkdir(path.dirname(target), { recursive: true });
158
- await writeFile(target, asset.content);
159
- }));
160
- await withPreparedRegistryArtifacts({
161
- artifacts: prepared.artifacts.map((artifact) => ({
162
- ...artifact,
163
- docs: undefined,
164
- })),
165
- entryItems: prepared.entryItems,
166
- itemByReference: prepared.itemByReference,
167
- run: async (entries) => {
168
- await addRegistryItemsQuietly(entries, {
169
- config: prepared.registryConfig,
170
- cwd: workspaceRoot,
171
- overwrite: true,
172
- });
173
- },
174
- });
175
- }
@@ -1,6 +0,0 @@
1
- import type { DependencySection, PackageRequirement, SelectionDefinition } from "./types.js";
2
- export type PackageJson = Partial<Record<DependencySection, Record<string, string>>> & Record<string, unknown>;
3
- export declare function parsePackageJson(source: string, label: string): PackageJson;
4
- export declare function readPackageJson(filePath: string, label?: string): Promise<PackageJson>;
5
- export declare function mergePackageRequirements(selections: Pick<SelectionDefinition, "packages">[]): PackageRequirement[];
6
- //# sourceMappingURL=packages.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"packages.d.ts","sourceRoot":"","sources":["../../src/composition/packages.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAWpB,MAAM,MAAM,WAAW,GAAG,OAAO,CAC/B,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAClD,GACC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAe1B,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAY3E;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,KAAK,SAAW,GACf,OAAO,CAAC,WAAW,CAAC,CAEtB;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,EAAE,GAClD,kBAAkB,EAAE,CA8BtB"}
@@ -1,54 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { z } from "zod";
3
- import { CompositionValidationError } from "./errors.js";
4
- import { formatZodError } from "./schema.js";
5
- const dependencyEntriesSchema = z.record(z.string());
6
- const packageJsonSchema = z
7
- .object({
8
- dependencies: dependencyEntriesSchema.optional(),
9
- devDependencies: dependencyEntriesSchema.optional(),
10
- optionalDependencies: dependencyEntriesSchema.optional(),
11
- })
12
- .passthrough();
13
- function decodePackageJson(value, label) {
14
- const result = packageJsonSchema.safeParse(value);
15
- if (!result.success) {
16
- throw new CompositionValidationError(`${label} is not a valid package.json file.`, formatZodError(result.error));
17
- }
18
- // Zod reconstructs objects in schema order, so return the validated input
19
- // to preserve the manifest's existing key positions.
20
- return value;
21
- }
22
- export function parsePackageJson(source, label) {
23
- let value;
24
- try {
25
- value = JSON.parse(source);
26
- }
27
- catch (error) {
28
- throw new CompositionValidationError(`${label} is not a valid package.json file.`, [error instanceof Error ? error.message : "JSON parsing failed"], { cause: error });
29
- }
30
- return decodePackageJson(value, label);
31
- }
32
- export async function readPackageJson(filePath, label = filePath) {
33
- return parsePackageJson(await readFile(filePath, "utf-8"), label);
34
- }
35
- export function mergePackageRequirements(selections) {
36
- const requirements = new Map();
37
- const issues = [];
38
- for (const selection of selections) {
39
- for (const requirement of selection.packages) {
40
- const key = `${requirement.cwd}\0${requirement.section}\0${requirement.name}`;
41
- const existing = requirements.get(key);
42
- if (existing && existing.specifier !== requirement.specifier) {
43
- issues.push(`${requirement.cwd}/${requirement.section}.${requirement.name} is requested as both ${existing.specifier} and ${requirement.specifier}`);
44
- }
45
- else {
46
- requirements.set(key, requirement);
47
- }
48
- }
49
- }
50
- if (issues.length > 0) {
51
- throw new CompositionValidationError("Package requirements conflict.", [...new Set(issues)].sort((left, right) => left.localeCompare(right)));
52
- }
53
- return [...requirements.values()].sort((left, right) => `${left.cwd}/${left.section}/${left.name}`.localeCompare(`${right.cwd}/${right.section}/${right.name}`));
54
- }