|
1 | 1 | import path from "path" |
2 | | -import { |
3 | | - fetchRegistry, |
4 | | - getRegistryItem, |
5 | | - getRegistryParentMap, |
6 | | - getRegistryTypeAliasMap, |
7 | | - registryResolveItemsTree, |
8 | | - resolveRegistryItems, |
9 | | -} from "@/src/registry/api" |
| 2 | +import { getRegistryItem, registryResolveItemsTree } from "@/src/registry/api" |
10 | 3 | import { |
11 | 4 | configSchema, |
12 | 5 | registryItemFileSchema, |
@@ -158,132 +151,143 @@ async function addWorkspaceComponents( |
158 | 151 | const registrySpinner = spinner(`Checking registry.`, { |
159 | 152 | silent: options.silent, |
160 | 153 | })?.start() |
161 | | - let registryItems = await resolveRegistryItems(components, config) |
162 | | - let result = await fetchRegistry(registryItems) |
163 | | - const payload = z.array(registryItemSchema).parse(result) |
164 | | - if (!payload) { |
| 154 | + const tree = await registryResolveItemsTree(components, config) |
| 155 | + |
| 156 | + if (!tree) { |
165 | 157 | registrySpinner?.fail() |
166 | 158 | return handleError(new Error("Failed to fetch components from registry.")) |
167 | 159 | } |
168 | | - registrySpinner?.succeed() |
169 | | - |
170 | | - const registryParentMap = getRegistryParentMap(payload) |
171 | | - const registryTypeAliasMap = getRegistryTypeAliasMap() |
172 | | - |
173 | | - const filesCreated: string[] = [] |
174 | | - const filesUpdated: string[] = [] |
175 | | - const filesSkipped: string[] = [] |
176 | 160 |
|
177 | | - const files = payload.flatMap((item) => item.files ?? []) |
178 | 161 | try { |
179 | | - validateFilesTarget(files, config.resolvedPaths.cwd) |
| 162 | + validateFilesTarget(tree.files ?? [], config.resolvedPaths.cwd) |
180 | 163 | } catch (error) { |
| 164 | + registrySpinner?.fail() |
181 | 165 | return handleError(error) |
182 | 166 | } |
183 | 167 |
|
184 | | - const rootSpinner = spinner(`Installing components.`)?.start() |
| 168 | + registrySpinner?.succeed() |
185 | 169 |
|
186 | | - for (const component of payload) { |
187 | | - const alias = registryTypeAliasMap.get(component.type) |
188 | | - const registryParent = registryParentMap.get(component.name) |
| 170 | + const filesCreated: string[] = [] |
| 171 | + const filesUpdated: string[] = [] |
| 172 | + const filesSkipped: string[] = [] |
189 | 173 |
|
190 | | - // We don't support this type of component. |
191 | | - if (!alias) { |
192 | | - continue |
193 | | - } |
| 174 | + const rootSpinner = spinner(`Installing components.`)?.start() |
194 | 175 |
|
195 | | - // A good start is ui for now. |
196 | | - // TODO: Add support for other types. |
197 | | - let targetConfig = |
198 | | - component.type === "registry:ui" || registryParent?.type === "registry:ui" |
199 | | - ? workspaceConfig.ui |
200 | | - : config |
| 176 | + // Process global updates (tailwind, css vars, dependencies) first for the main target. |
| 177 | + // These should typically go to the UI package in a workspace. |
| 178 | + const mainTargetConfig = workspaceConfig.ui |
| 179 | + const tailwindVersion = await getProjectTailwindVersionFromConfig( |
| 180 | + mainTargetConfig |
| 181 | + ) |
| 182 | + const workspaceRoot = findCommonRoot( |
| 183 | + config.resolvedPaths.cwd, |
| 184 | + mainTargetConfig.resolvedPaths.ui |
| 185 | + ) |
201 | 186 |
|
202 | | - const tailwindVersion = await getProjectTailwindVersionFromConfig( |
203 | | - targetConfig |
| 187 | + // 1. Update tailwind config. |
| 188 | + if (tree.tailwind?.config) { |
| 189 | + await updateTailwindConfig(tree.tailwind?.config, mainTargetConfig, { |
| 190 | + silent: true, |
| 191 | + tailwindVersion, |
| 192 | + }) |
| 193 | + filesUpdated.push( |
| 194 | + path.relative( |
| 195 | + workspaceRoot, |
| 196 | + mainTargetConfig.resolvedPaths.tailwindConfig |
| 197 | + ) |
204 | 198 | ) |
| 199 | + } |
205 | 200 |
|
206 | | - const workspaceRoot = findCommonRoot( |
207 | | - config.resolvedPaths.cwd, |
208 | | - targetConfig.resolvedPaths.ui |
| 201 | + // 2. Update css vars. |
| 202 | + if (tree.cssVars) { |
| 203 | + const overwriteCssVars = await shouldOverwriteCssVars(components, config) |
| 204 | + await updateCssVars(tree.cssVars, mainTargetConfig, { |
| 205 | + silent: true, |
| 206 | + tailwindVersion, |
| 207 | + tailwindConfig: tree.tailwind?.config, |
| 208 | + overwriteCssVars, |
| 209 | + }) |
| 210 | + filesUpdated.push( |
| 211 | + path.relative(workspaceRoot, mainTargetConfig.resolvedPaths.tailwindCss) |
209 | 212 | ) |
210 | | - const packageRoot = |
211 | | - (await findPackageRoot(workspaceRoot, targetConfig.resolvedPaths.cwd)) ?? |
212 | | - targetConfig.resolvedPaths.cwd |
213 | | - |
214 | | - // 1. Update tailwind config. |
215 | | - if (component.tailwind?.config) { |
216 | | - await updateTailwindConfig(component.tailwind?.config, targetConfig, { |
217 | | - silent: true, |
218 | | - tailwindVersion, |
219 | | - }) |
220 | | - filesUpdated.push( |
221 | | - path.relative(workspaceRoot, targetConfig.resolvedPaths.tailwindConfig) |
222 | | - ) |
223 | | - } |
| 213 | + } |
224 | 214 |
|
225 | | - // 2. Update css vars. |
226 | | - if (component.cssVars) { |
227 | | - const overwriteCssVars = await shouldOverwriteCssVars(components, config) |
228 | | - await updateCssVars(component.cssVars, targetConfig, { |
229 | | - silent: true, |
230 | | - tailwindVersion, |
231 | | - tailwindConfig: component.tailwind?.config, |
232 | | - overwriteCssVars, |
233 | | - }) |
234 | | - filesUpdated.push( |
235 | | - path.relative(workspaceRoot, targetConfig.resolvedPaths.tailwindCss) |
236 | | - ) |
237 | | - } |
| 215 | + // 3. Update CSS |
| 216 | + if (tree.css) { |
| 217 | + await updateCss(tree.css, mainTargetConfig, { |
| 218 | + silent: true, |
| 219 | + }) |
| 220 | + filesUpdated.push( |
| 221 | + path.relative(workspaceRoot, mainTargetConfig.resolvedPaths.tailwindCss) |
| 222 | + ) |
| 223 | + } |
238 | 224 |
|
239 | | - // 3. Update CSS |
240 | | - if (component.css) { |
241 | | - await updateCss(component.css, targetConfig, { |
242 | | - silent: true, |
243 | | - }) |
244 | | - filesUpdated.push( |
245 | | - path.relative(workspaceRoot, targetConfig.resolvedPaths.tailwindCss) |
246 | | - ) |
| 225 | + // 4. Update environment variables |
| 226 | + if (tree.envVars) { |
| 227 | + await updateEnvVars(tree.envVars, mainTargetConfig, { |
| 228 | + silent: true, |
| 229 | + }) |
| 230 | + } |
| 231 | + |
| 232 | + // 5. Update dependencies. |
| 233 | + await updateDependencies( |
| 234 | + tree.dependencies, |
| 235 | + tree.devDependencies, |
| 236 | + mainTargetConfig, |
| 237 | + { |
| 238 | + silent: true, |
247 | 239 | } |
| 240 | + ) |
| 241 | + |
| 242 | + // 6. Group files by their type to determine target config and update files. |
| 243 | + const filesByType = new Map<string, typeof tree.files>() |
248 | 244 |
|
249 | | - // 4. Update environment variables |
250 | | - if (component.envVars) { |
251 | | - await updateEnvVars(component.envVars, targetConfig, { |
252 | | - silent: true, |
253 | | - }) |
| 245 | + for (const file of tree.files ?? []) { |
| 246 | + const type = file.type || "registry:ui" |
| 247 | + if (!filesByType.has(type)) { |
| 248 | + filesByType.set(type, []) |
254 | 249 | } |
| 250 | + filesByType.get(type)!.push(file) |
| 251 | + } |
255 | 252 |
|
256 | | - // 5. Update dependencies. |
257 | | - await updateDependencies( |
258 | | - component.dependencies, |
259 | | - component.devDependencies, |
260 | | - targetConfig, |
261 | | - { |
262 | | - silent: true, |
263 | | - } |
| 253 | + // Process each type of component with its appropriate target config. |
| 254 | + for (const type of Array.from(filesByType.keys())) { |
| 255 | + const typeFiles = filesByType.get(type)! |
| 256 | + |
| 257 | + let targetConfig = type === "registry:ui" ? workspaceConfig.ui : config |
| 258 | + |
| 259 | + const typeWorkspaceRoot = findCommonRoot( |
| 260 | + config.resolvedPaths.cwd, |
| 261 | + targetConfig.resolvedPaths.ui || targetConfig.resolvedPaths.cwd |
264 | 262 | ) |
| 263 | + const packageRoot = |
| 264 | + (await findPackageRoot( |
| 265 | + typeWorkspaceRoot, |
| 266 | + targetConfig.resolvedPaths.cwd |
| 267 | + )) ?? targetConfig.resolvedPaths.cwd |
265 | 268 |
|
266 | | - // 6. Update files. |
267 | | - const files = await updateFiles(component.files, targetConfig, { |
| 269 | + // Update files for this type. |
| 270 | + const files = await updateFiles(typeFiles, targetConfig, { |
268 | 271 | overwrite: options.overwrite, |
269 | 272 | silent: true, |
270 | 273 | rootSpinner, |
271 | 274 | isRemote: options.isRemote, |
| 275 | + isWorkspace: true, |
272 | 276 | }) |
273 | 277 |
|
274 | 278 | filesCreated.push( |
275 | 279 | ...files.filesCreated.map((file) => |
276 | | - path.relative(workspaceRoot, path.join(packageRoot, file)) |
| 280 | + path.relative(typeWorkspaceRoot, path.join(packageRoot, file)) |
277 | 281 | ) |
278 | 282 | ) |
279 | 283 | filesUpdated.push( |
280 | 284 | ...files.filesUpdated.map((file) => |
281 | | - path.relative(workspaceRoot, path.join(packageRoot, file)) |
| 285 | + path.relative(typeWorkspaceRoot, path.join(packageRoot, file)) |
282 | 286 | ) |
283 | 287 | ) |
284 | 288 | filesSkipped.push( |
285 | 289 | ...files.filesSkipped.map((file) => |
286 | | - path.relative(workspaceRoot, path.join(packageRoot, file)) |
| 290 | + path.relative(typeWorkspaceRoot, path.join(packageRoot, file)) |
287 | 291 | ) |
288 | 292 | ) |
289 | 293 | } |
@@ -343,6 +347,10 @@ async function addWorkspaceComponents( |
343 | 347 | logger.log(` - ${file}`) |
344 | 348 | } |
345 | 349 | } |
| 350 | + |
| 351 | + if (tree.docs) { |
| 352 | + logger.info(tree.docs) |
| 353 | + } |
346 | 354 | } |
347 | 355 |
|
348 | 356 | async function shouldOverwriteCssVars( |
|
0 commit comments