Skip to content

Commit a80ab37

Browse files
authored
feat(shadcn): update file handling for monorepo (#7955)
* feat(shadcn): update monorepo handling * feat(shadcn): update file handling for monorepo * chore: changeset
1 parent 4692501 commit a80ab37

5 files changed

Lines changed: 439 additions & 103 deletions

File tree

.changeset/hungry-melons-press.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"shadcn": minor
3+
---
4+
5+
update file handling for monorepo

packages/shadcn/src/utils/add-components.ts

Lines changed: 102 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
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"
103
import {
114
configSchema,
125
registryItemFileSchema,
@@ -158,132 +151,143 @@ async function addWorkspaceComponents(
158151
const registrySpinner = spinner(`Checking registry.`, {
159152
silent: options.silent,
160153
})?.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) {
165157
registrySpinner?.fail()
166158
return handleError(new Error("Failed to fetch components from registry."))
167159
}
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[] = []
176160

177-
const files = payload.flatMap((item) => item.files ?? [])
178161
try {
179-
validateFilesTarget(files, config.resolvedPaths.cwd)
162+
validateFilesTarget(tree.files ?? [], config.resolvedPaths.cwd)
180163
} catch (error) {
164+
registrySpinner?.fail()
181165
return handleError(error)
182166
}
183167

184-
const rootSpinner = spinner(`Installing components.`)?.start()
168+
registrySpinner?.succeed()
185169

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[] = []
189173

190-
// We don't support this type of component.
191-
if (!alias) {
192-
continue
193-
}
174+
const rootSpinner = spinner(`Installing components.`)?.start()
194175

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+
)
201186

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+
)
204198
)
199+
}
205200

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)
209212
)
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+
}
224214

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+
}
238224

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,
247239
}
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>()
248244

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, [])
254249
}
250+
filesByType.get(type)!.push(file)
251+
}
255252

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
264262
)
263+
const packageRoot =
264+
(await findPackageRoot(
265+
typeWorkspaceRoot,
266+
targetConfig.resolvedPaths.cwd
267+
)) ?? targetConfig.resolvedPaths.cwd
265268

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, {
268271
overwrite: options.overwrite,
269272
silent: true,
270273
rootSpinner,
271274
isRemote: options.isRemote,
275+
isWorkspace: true,
272276
})
273277

274278
filesCreated.push(
275279
...files.filesCreated.map((file) =>
276-
path.relative(workspaceRoot, path.join(packageRoot, file))
280+
path.relative(typeWorkspaceRoot, path.join(packageRoot, file))
277281
)
278282
)
279283
filesUpdated.push(
280284
...files.filesUpdated.map((file) =>
281-
path.relative(workspaceRoot, path.join(packageRoot, file))
285+
path.relative(typeWorkspaceRoot, path.join(packageRoot, file))
282286
)
283287
)
284288
filesSkipped.push(
285289
...files.filesSkipped.map((file) =>
286-
path.relative(workspaceRoot, path.join(packageRoot, file))
290+
path.relative(typeWorkspaceRoot, path.join(packageRoot, file))
287291
)
288292
)
289293
}
@@ -343,6 +347,10 @@ async function addWorkspaceComponents(
343347
logger.log(` - ${file}`)
344348
}
345349
}
350+
351+
if (tree.docs) {
352+
logger.info(tree.docs)
353+
}
346354
}
347355

348356
async function shouldOverwriteCssVars(

0 commit comments

Comments
 (0)