From 65381cd614ce38b47a3c3f2226ce4db0f3d60db1 Mon Sep 17 00:00:00 2001 From: shadcn Date: Mon, 16 Feb 2026 16:07:21 +0400 Subject: [PATCH] fix --- .../shadcn/src/templates/next-monorepo.ts | 4 +- .../tests/src/tests/create-monorepo.test.ts | 209 ------------------ packages/tests/src/tests/init.test.ts | 131 +++++++++++ 3 files changed, 133 insertions(+), 211 deletions(-) delete mode 100644 packages/tests/src/tests/create-monorepo.test.ts diff --git a/packages/shadcn/src/templates/next-monorepo.ts b/packages/shadcn/src/templates/next-monorepo.ts index 3df1276ad9..e6fc01ee87 100644 --- a/packages/shadcn/src/templates/next-monorepo.ts +++ b/packages/shadcn/src/templates/next-monorepo.ts @@ -130,7 +130,7 @@ export const nextMonorepo = createTemplate({ await fs.writeJson(appsWebConfigPath, appsWebConfig, { spaces: 2 }) // Apply preset CSS/style to packages/ui directly. - // We use the packages/ui config (not apps/web) so addProjectComponents runs + // We use the packages/ui config (not apps/web) so addProjectComponents runs. // instead of addWorkspaceComponents. This keeps CSS/deps in packages/ui. const resolvedPackagesUiConfig = await resolveConfigPaths( packagesUiPath, @@ -156,7 +156,7 @@ export const nextMonorepo = createTemplate({ // Handle fonts at the apps/web level. // packages/ui has no next.config so massageTreeForFonts can't detect Next.js. - // We resolve the tree to get fonts, then apply them using the apps/web config + // We resolve the tree to get fonts, then apply them using the apps/web config. // which has next.config and layout.tsx. const tree = await resolveRegistryTree( options.components, diff --git a/packages/tests/src/tests/create-monorepo.test.ts b/packages/tests/src/tests/create-monorepo.test.ts deleted file mode 100644 index 13f3d0b697..0000000000 --- a/packages/tests/src/tests/create-monorepo.test.ts +++ /dev/null @@ -1,209 +0,0 @@ -import path from "path" -import fs from "fs-extra" -import { describe, expect, it } from "vitest" - -import { cssHasProperties, npxShadcn } from "../utils/helpers" -import { TEMP_DIR } from "../utils/setup" - -// These tests download the monorepo template from GitHub and install dependencies. -// They require network access and a running local registry at REGISTRY_URL. -describe("shadcn create - next-monorepo", () => { - it("should create a monorepo project with preset", async () => { - const projectName = `test-monorepo-preset-${process.pid}` - - const result = await npxShadcn( - TEMP_DIR, - [ - "create", - projectName, - "--template", - "next-monorepo", - "--preset", - "radix-nova", - ], - { timeout: 120000 } - ) - - const projectPath = path.join(TEMP_DIR, projectName) - - // Verify project structure exists. - expect(await fs.pathExists(projectPath)).toBe(true) - expect( - await fs.pathExists(path.join(projectPath, "packages/ui/components.json")) - ).toBe(true) - expect( - await fs.pathExists(path.join(projectPath, "apps/web/components.json")) - ).toBe(true) - - // Verify packages/ui/components.json is updated with preset config. - const uiConfig = await fs.readJson( - path.join(projectPath, "packages/ui/components.json") - ) - expect(uiConfig.style).toBe("radix-nova") - expect(uiConfig.iconLibrary).toBe("lucide") - expect(uiConfig.tailwind.css).toBe("src/styles/globals.css") - expect(uiConfig.tailwind.baseColor).toBe("neutral") - expect(uiConfig.tailwind.cssVariables).toBe(true) - expect(uiConfig.aliases.components).toBe("@workspace/ui/components") - expect(uiConfig.aliases.utils).toBe("@workspace/ui/lib/utils") - - // Verify apps/web/components.json is updated with preset config. - const webConfig = await fs.readJson( - path.join(projectPath, "apps/web/components.json") - ) - expect(webConfig.style).toBe("radix-nova") - expect(webConfig.iconLibrary).toBe("lucide") - expect(webConfig.tailwind.css).toBe( - "../../packages/ui/src/styles/globals.css" - ) - expect(webConfig.tailwind.baseColor).toBe("neutral") - // Verify workspace aliases are preserved. - expect(webConfig.aliases.components).toBe("@/components") - expect(webConfig.aliases.utils).toBe("@workspace/ui/lib/utils") - expect(webConfig.aliases.ui).toBe("@workspace/ui/components") - - // Verify CSS was applied to packages/ui. - const cssPath = path.join(projectPath, "packages/ui/src/styles/globals.css") - expect(await fs.pathExists(cssPath)).toBe(true) - const cssContent = await fs.readFile(cssPath, "utf-8") - expect(cssContent).toContain("@layer base") - expect(cssContent).toContain(":root") - expect(cssContent).toContain(".dark") - expect(cssContent).toContain("--background") - expect(cssContent).toContain("--foreground") - expect(cssContent).toContain("--primary") - - // Verify component-example was added to apps/web. - expect( - await fs.pathExists( - path.join(projectPath, "apps/web/components/component-example.tsx") - ) - ).toBe(true) - - // Verify page.tsx was written. - const pageContent = await fs.readFile( - path.join(projectPath, "apps/web/app/page.tsx"), - "utf-8" - ) - expect(pageContent).toContain("ComponentExample") - expect(pageContent).toContain( - 'import { ComponentExample } from "@/components/component-example"' - ) - }) - - it("should create a monorepo with custom base color and font", async () => { - const projectName = `test-monorepo-custom-${process.pid}` - - // Use radix-maia preset which has figtree font and hugeicons. - const result = await npxShadcn( - TEMP_DIR, - [ - "create", - projectName, - "--template", - "next-monorepo", - "--preset", - "radix-maia", - ], - { timeout: 120000 } - ) - - const projectPath = path.join(TEMP_DIR, projectName) - expect(await fs.pathExists(projectPath)).toBe(true) - - // Verify preset config is applied to both components.json files. - const uiConfig = await fs.readJson( - path.join(projectPath, "packages/ui/components.json") - ) - expect(uiConfig.style).toBe("radix-maia") - expect(uiConfig.iconLibrary).toBe("hugeicons") - expect(uiConfig.tailwind.baseColor).toBe("neutral") - - const webConfig = await fs.readJson( - path.join(projectPath, "apps/web/components.json") - ) - expect(webConfig.style).toBe("radix-maia") - expect(webConfig.iconLibrary).toBe("hugeicons") - - // Verify CSS file has theme variables. - const cssPath = path.join(projectPath, "packages/ui/src/styles/globals.css") - const cssContent = await fs.readFile(cssPath, "utf-8") - expect(cssContent).toContain("@layer base") - expect(cssContent).toContain(":root") - expect(cssContent).toContain(".dark") - expect(cssContent).toContain("--background") - expect(cssContent).toContain("--foreground") - - // Verify font registry dependency was installed (figtree font). - expect(cssContent).toContain("--font-sans") - }) - - it("should create a monorepo with custom preset url", async () => { - const projectName = `test-monorepo-url-${process.pid}` - - // Build a custom init URL with specific options. - const registryUrl = process.env.REGISTRY_URL || "http://localhost:4000/r" - const baseUrl = registryUrl.replace(/\/r\/?$/, "") - const initUrl = `${baseUrl}/init?base=radix&style=nova&baseColor=zinc&theme=zinc&iconLibrary=lucide&font=inter&rtl=false&menuAccent=subtle&menuColor=default&radius=default&template=next` - - const result = await npxShadcn( - TEMP_DIR, - [ - "create", - projectName, - "--template", - "next-monorepo", - "--preset", - initUrl, - ], - { timeout: 120000 } - ) - - const projectPath = path.join(TEMP_DIR, projectName) - expect(await fs.pathExists(projectPath)).toBe(true) - - // Verify config reflects the custom URL params. - const uiConfig = await fs.readJson( - path.join(projectPath, "packages/ui/components.json") - ) - expect(uiConfig.style).toBe("radix-nova") - expect(uiConfig.iconLibrary).toBe("lucide") - expect(uiConfig.tailwind.baseColor).toBe("neutral") - - const webConfig = await fs.readJson( - path.join(projectPath, "apps/web/components.json") - ) - expect(webConfig.style).toBe("radix-nova") - expect(webConfig.tailwind.baseColor).toBe("neutral") - - // Verify CSS has zinc color theme applied. - const cssPath = path.join(projectPath, "packages/ui/src/styles/globals.css") - const cssContent = await fs.readFile(cssPath, "utf-8") - expect(cssContent).toContain(":root") - expect(cssContent).toContain(".dark") - expect(cssContent).toContain("--background") - expect(cssContent).toContain("--foreground") - expect( - cssHasProperties(cssContent, [ - { - selector: ":root", - properties: { - "--background": "oklch(1 0 0)", - }, - }, - ]) - ).toBe(true) - - // Verify component-example and page.tsx. - expect( - await fs.pathExists( - path.join(projectPath, "apps/web/components/component-example.tsx") - ) - ).toBe(true) - const pageContent = await fs.readFile( - path.join(projectPath, "apps/web/app/page.tsx"), - "utf-8" - ) - expect(pageContent).toContain("ComponentExample") - }) -}) diff --git a/packages/tests/src/tests/init.test.ts b/packages/tests/src/tests/init.test.ts index e0cac04d09..7b8c4adcfc 100644 --- a/packages/tests/src/tests/init.test.ts +++ b/packages/tests/src/tests/init.test.ts @@ -523,6 +523,137 @@ describe("shadcn init - --name flag", () => { }) }) +describe("shadcn init - next-monorepo", () => { + // Use os.tmpdir() to create projects outside the monorepo tree. + // This prevents pnpm from detecting the monorepo workspace root. + let testBaseDir: string + + beforeAll(async () => { + testBaseDir = path.join(os.tmpdir(), `shadcn-monorepo-test-${process.pid}`) + await fs.ensureDir(testBaseDir) + }) + + afterAll(async () => { + await fs.remove(testBaseDir) + }) + + it("should create a monorepo with preset", async () => { + const projectName = `test-monorepo-preset-${process.pid}` + + await npxShadcn( + testBaseDir, + [ + "init", + "--name", + projectName, + "-t", + "next-monorepo", + "--preset", + "radix-nova", + ], + { timeout: 120000 } + ) + + const projectPath = path.join(testBaseDir, projectName) + + // Verify project structure exists. + expect(await fs.pathExists(projectPath)).toBe(true) + expect( + await fs.pathExists(path.join(projectPath, "packages/ui/components.json")) + ).toBe(true) + expect( + await fs.pathExists(path.join(projectPath, "apps/web/components.json")) + ).toBe(true) + + // Verify packages/ui/components.json is updated with preset config. + const uiConfig = await fs.readJson( + path.join(projectPath, "packages/ui/components.json") + ) + expect(uiConfig.style).toBe("radix-nova") + expect(uiConfig.iconLibrary).toBe("lucide") + expect(uiConfig.tailwind.baseColor).toBe("neutral") + + // Verify apps/web/components.json is updated with preset config. + const webConfig = await fs.readJson( + path.join(projectPath, "apps/web/components.json") + ) + expect(webConfig.style).toBe("radix-nova") + // Verify workspace aliases are preserved. + expect(webConfig.aliases.components).toBe("@/components") + expect(webConfig.aliases.utils).toBe("@workspace/ui/lib/utils") + expect(webConfig.aliases.ui).toBe("@workspace/ui/components") + + // Verify CSS was applied to packages/ui. + const cssPath = path.join(projectPath, "packages/ui/src/styles/globals.css") + expect(await fs.pathExists(cssPath)).toBe(true) + const cssContent = await fs.readFile(cssPath, "utf-8") + expect(cssContent).toContain("@layer base") + expect(cssContent).toContain(":root") + expect(cssContent).toContain(".dark") + expect(cssContent).toContain("--background") + expect(cssContent).toContain("--foreground") + expect(cssContent).toContain("--primary") + }, 120000) + + it("should create a monorepo with custom preset url", async () => { + const projectName = `test-monorepo-url-${process.pid}` + + // Build a custom init URL with specific options. + const registryUrl = process.env.REGISTRY_URL || "http://localhost:4000/r" + const baseUrl = registryUrl.replace(/\/r\/?$/, "") + const initUrl = `${baseUrl}/init?base=radix&style=nova&baseColor=zinc&theme=zinc&iconLibrary=lucide&font=inter&rtl=false&menuAccent=subtle&menuColor=default&radius=default&template=next` + + await npxShadcn( + testBaseDir, + [ + "init", + "--name", + projectName, + "-t", + "next-monorepo", + "--preset", + initUrl, + ], + { timeout: 120000 } + ) + + const projectPath = path.join(testBaseDir, projectName) + expect(await fs.pathExists(projectPath)).toBe(true) + + // Verify config reflects the custom URL params. + const uiConfig = await fs.readJson( + path.join(projectPath, "packages/ui/components.json") + ) + expect(uiConfig.style).toBe("radix-nova") + expect(uiConfig.iconLibrary).toBe("lucide") + expect(uiConfig.tailwind.baseColor).toBe("neutral") + + const webConfig = await fs.readJson( + path.join(projectPath, "apps/web/components.json") + ) + expect(webConfig.style).toBe("radix-nova") + expect(webConfig.tailwind.baseColor).toBe("neutral") + + // Verify CSS has zinc color theme applied. + const cssPath = path.join(projectPath, "packages/ui/src/styles/globals.css") + const cssContent = await fs.readFile(cssPath, "utf-8") + expect(cssContent).toContain(":root") + expect(cssContent).toContain(".dark") + expect(cssContent).toContain("--background") + expect(cssContent).toContain("--foreground") + expect( + cssHasProperties(cssContent, [ + { + selector: ":root", + properties: { + "--background": "oklch(1 0 0)", + }, + }, + ]) + ).toBe(true) + }, 120000) +}) + describe("shadcn init - deprecated --src-dir", () => { it("should reject --src-dir as unknown option", async () => { const fixturePath = await createFixtureTestDirectory("next-app")