From f30efb6e6ad02bb7ab8b79bf9ab6413a210a937f Mon Sep 17 00:00:00 2001 From: shadcn Date: Tue, 28 Jan 2025 16:48:05 +0400 Subject: [PATCH] feat(shadcn): add tailwind version detection --- .../shadcn/src/preflights/preflight-init.ts | 11 +++- packages/shadcn/src/utils/get-config.ts | 7 +-- packages/shadcn/src/utils/get-project-info.ts | 53 +++++++++++++++---- .../frameworks/next-app-src/package.json | 14 +++++ .../fixtures/frameworks/next-app/package.json | 3 ++ .../frameworks/next-pages-src/package.json | 3 ++ .../next-pages-src/src/styles/globals.css | 28 +--------- .../frameworks/next-pages/package.json | 3 ++ .../frameworks/next-pages/styles/globals.css | 28 +--------- .../test/utils/get-project-info.test.ts | 9 ++++ 10 files changed, 91 insertions(+), 68 deletions(-) create mode 100644 packages/shadcn/test/fixtures/frameworks/next-app-src/package.json diff --git a/packages/shadcn/src/preflights/preflight-init.ts b/packages/shadcn/src/preflights/preflight-init.ts index a0742c03afc..fc7d511ddf7 100644 --- a/packages/shadcn/src/preflights/preflight-init.ts +++ b/packages/shadcn/src/preflights/preflight-init.ts @@ -81,7 +81,16 @@ export async function preFlightInit( const tailwindSpinner = spinner(`Validating Tailwind CSS.`, { silent: options.silent, }).start() - if (!projectInfo?.tailwindConfigFile || !projectInfo?.tailwindCssFile) { + if ( + projectInfo.tailwindVersion === "v3" && + (!projectInfo?.tailwindConfigFile || !projectInfo?.tailwindCssFile) + ) { + errors[ERRORS.TAILWIND_NOT_CONFIGURED] = true + tailwindSpinner?.fail() + } else if ( + projectInfo.tailwindVersion === "v4" && + !projectInfo?.tailwindCssFile + ) { errors[ERRORS.TAILWIND_NOT_CONFIGURED] = true tailwindSpinner?.fail() } else { diff --git a/packages/shadcn/src/utils/get-config.ts b/packages/shadcn/src/utils/get-config.ts index d3477ae3272..b8cc9de4f6c 100644 --- a/packages/shadcn/src/utils/get-config.ts +++ b/packages/shadcn/src/utils/get-config.ts @@ -3,7 +3,6 @@ import { highlighter } from "@/src/utils/highlighter" import { resolveImport } from "@/src/utils/resolve-import" import { cosmiconfig } from "cosmiconfig" import fg from "fast-glob" -import fs from "fs-extra" import { loadConfig } from "tsconfig-paths" import { z } from "zod" @@ -27,7 +26,7 @@ export const rawConfigSchema = z rsc: z.coerce.boolean().default(false), tsx: z.coerce.boolean().default(true), tailwind: z.object({ - config: z.string(), + config: z.string().optional(), css: z.string(), baseColor: z.string(), cssVariables: z.boolean().default(true), @@ -96,7 +95,9 @@ export async function resolveConfigPaths(cwd: string, config: RawConfig) { ...config, resolvedPaths: { cwd, - tailwindConfig: path.resolve(cwd, config.tailwind.config), + tailwindConfig: config.tailwind.config + ? path.resolve(cwd, config.tailwind.config) + : "", tailwindCss: path.resolve(cwd, config.tailwind.css), utils: await resolveImport(config.aliases["utils"], tsConfig), components: await resolveImport(config.aliases["components"], tsConfig), diff --git a/packages/shadcn/src/utils/get-project-info.ts b/packages/shadcn/src/utils/get-project-info.ts index 741c9c6b505..a258c5ee607 100644 --- a/packages/shadcn/src/utils/get-project-info.ts +++ b/packages/shadcn/src/utils/get-project-info.ts @@ -19,6 +19,7 @@ type ProjectInfo = { isTsx: boolean tailwindConfigFile: string | null tailwindCssFile: string | null + tailwindVersion: "v3" | "v4" | null aliasPrefix: string | null } @@ -43,6 +44,7 @@ export async function getProjectInfo(cwd: string): Promise { isTsx, tailwindConfigFile, tailwindCssFile, + tailwindVersion, aliasPrefix, packageJson, ] = await Promise.all([ @@ -55,6 +57,7 @@ export async function getProjectInfo(cwd: string): Promise { isTypeScriptProject(cwd), getTailwindConfigFile(cwd), getTailwindCssFile(cwd), + getTailwindVersion(cwd), getTsConfigAliasPrefix(cwd), getPackageInfo(cwd, false), ]) @@ -70,6 +73,7 @@ export async function getProjectInfo(cwd: string): Promise { isTsx, tailwindConfigFile, tailwindCssFile, + tailwindVersion, aliasPrefix, } @@ -121,21 +125,50 @@ export async function getProjectInfo(cwd: string): Promise { return type } +export async function getTailwindVersion( + cwd: string +): Promise { + const packageInfo = getPackageInfo(cwd) + + if ( + !packageInfo?.dependencies?.tailwindcss && + !packageInfo?.devDependencies?.tailwindcss + ) { + return null + } + + if ( + /^(?:\^|~)?3(?:\.\d+)*(?:-.*)?$/.test( + packageInfo?.dependencies?.tailwindcss || + packageInfo?.devDependencies?.tailwindcss || + "" + ) + ) { + return "v3" + } + + return "v4" +} + export async function getTailwindCssFile(cwd: string) { - const files = await fg.glob(["**/*.css", "**/*.scss"], { - cwd, - deep: 5, - ignore: PROJECT_SHARED_IGNORE, - }) + const [files, tailwindVersion] = await Promise.all([ + fg.glob(["**/*.css", "**/*.scss"], { + cwd, + deep: 5, + ignore: PROJECT_SHARED_IGNORE, + }), + getTailwindVersion(cwd), + ]) if (!files.length) { return null } + const needle = + tailwindVersion === "v4" ? `@import "tailwindcss"` : "@tailwind base" for (const file of files) { const contents = await fs.readFile(path.resolve(cwd, file), "utf8") - // Assume that if the file contains `@tailwind base` it's the main css file. - if (contents.includes("@tailwind base")) { + if (contents.includes(needle)) { return file } } @@ -237,8 +270,8 @@ export async function getProjectConfig( if ( !projectInfo || - !projectInfo.tailwindConfigFile || - !projectInfo.tailwindCssFile + !projectInfo.tailwindCssFile || + (projectInfo.tailwindVersion === "v3" && !projectInfo.tailwindConfigFile) ) { return null } @@ -249,7 +282,7 @@ export async function getProjectConfig( tsx: projectInfo.isTsx, style: "new-york", tailwind: { - config: projectInfo.tailwindConfigFile, + config: projectInfo.tailwindConfigFile ?? "", baseColor: "zinc", css: projectInfo.tailwindCssFile, cssVariables: true, diff --git a/packages/shadcn/test/fixtures/frameworks/next-app-src/package.json b/packages/shadcn/test/fixtures/frameworks/next-app-src/package.json new file mode 100644 index 00000000000..dfa6942e1aa --- /dev/null +++ b/packages/shadcn/test/fixtures/frameworks/next-app-src/package.json @@ -0,0 +1,14 @@ +{ + "name": "next-app-src", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "tailwindcss": "3.1.2" + } +} diff --git a/packages/shadcn/test/fixtures/frameworks/next-app/package.json b/packages/shadcn/test/fixtures/frameworks/next-app/package.json index d43fb081b9a..a800f2b7c49 100644 --- a/packages/shadcn/test/fixtures/frameworks/next-app/package.json +++ b/packages/shadcn/test/fixtures/frameworks/next-app/package.json @@ -7,5 +7,8 @@ "build": "next build", "start": "next start", "lint": "next lint" + }, + "dependencies": { + "tailwindcss": "^3.0.0" } } diff --git a/packages/shadcn/test/fixtures/frameworks/next-pages-src/package.json b/packages/shadcn/test/fixtures/frameworks/next-pages-src/package.json index d5be9c4b809..5a9c0f0c6b0 100644 --- a/packages/shadcn/test/fixtures/frameworks/next-pages-src/package.json +++ b/packages/shadcn/test/fixtures/frameworks/next-pages-src/package.json @@ -7,5 +7,8 @@ "build": "next build", "start": "next start", "lint": "next lint" + }, + "dependencies": { + "tailwindcss": "^4.0.0" } } diff --git a/packages/shadcn/test/fixtures/frameworks/next-pages-src/src/styles/globals.css b/packages/shadcn/test/fixtures/frameworks/next-pages-src/src/styles/globals.css index fd81e885836..f1d8c73cdcf 100644 --- a/packages/shadcn/test/fixtures/frameworks/next-pages-src/src/styles/globals.css +++ b/packages/shadcn/test/fixtures/frameworks/next-pages-src/src/styles/globals.css @@ -1,27 +1 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -:root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - } -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} +@import "tailwindcss"; diff --git a/packages/shadcn/test/fixtures/frameworks/next-pages/package.json b/packages/shadcn/test/fixtures/frameworks/next-pages/package.json index 7603dc91c45..6ab0fabb1f0 100644 --- a/packages/shadcn/test/fixtures/frameworks/next-pages/package.json +++ b/packages/shadcn/test/fixtures/frameworks/next-pages/package.json @@ -7,5 +7,8 @@ "build": "next build", "start": "next start", "lint": "next lint" + }, + "devDependencies": { + "tailwindcss": "4.1.2" } } diff --git a/packages/shadcn/test/fixtures/frameworks/next-pages/styles/globals.css b/packages/shadcn/test/fixtures/frameworks/next-pages/styles/globals.css index fd81e885836..f1d8c73cdcf 100644 --- a/packages/shadcn/test/fixtures/frameworks/next-pages/styles/globals.css +++ b/packages/shadcn/test/fixtures/frameworks/next-pages/styles/globals.css @@ -1,27 +1 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -:root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - } -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} +@import "tailwindcss"; diff --git a/packages/shadcn/test/utils/get-project-info.test.ts b/packages/shadcn/test/utils/get-project-info.test.ts index 06288881ce9..cd9708cd54a 100644 --- a/packages/shadcn/test/utils/get-project-info.test.ts +++ b/packages/shadcn/test/utils/get-project-info.test.ts @@ -15,6 +15,7 @@ describe("get project info", async () => { isTsx: true, tailwindConfigFile: "tailwind.config.ts", tailwindCssFile: "app/globals.css", + tailwindVersion: "v3", aliasPrefix: "@", }, }, @@ -27,6 +28,7 @@ describe("get project info", async () => { isTsx: true, tailwindConfigFile: "tailwind.config.ts", tailwindCssFile: "src/app/styles.css", + tailwindVersion: "v3", aliasPrefix: "#", }, }, @@ -39,6 +41,7 @@ describe("get project info", async () => { isTsx: true, tailwindConfigFile: "tailwind.config.ts", tailwindCssFile: "styles/globals.css", + tailwindVersion: "v4", aliasPrefix: "~", }, }, @@ -51,6 +54,7 @@ describe("get project info", async () => { isTsx: true, tailwindConfigFile: "tailwind.config.ts", tailwindCssFile: "src/styles/globals.css", + tailwindVersion: "v4", aliasPrefix: "@", }, }, @@ -63,6 +67,7 @@ describe("get project info", async () => { isTsx: true, tailwindConfigFile: "tailwind.config.ts", tailwindCssFile: "src/styles/globals.css", + tailwindVersion: "v3", aliasPrefix: "~", }, }, @@ -75,6 +80,7 @@ describe("get project info", async () => { isTsx: true, tailwindConfigFile: "tailwind.config.ts", tailwindCssFile: "src/styles/globals.css", + tailwindVersion: "v3", aliasPrefix: "~", }, }, @@ -87,6 +93,7 @@ describe("get project info", async () => { isTsx: true, tailwindConfigFile: "tailwind.config.ts", tailwindCssFile: "app/tailwind.css", + tailwindVersion: "v3", aliasPrefix: "~", }, }, @@ -99,6 +106,7 @@ describe("get project info", async () => { isTsx: true, tailwindConfigFile: "tailwind.config.ts", tailwindCssFile: "app/tailwind.css", + tailwindVersion: "v3", aliasPrefix: "~", }, }, @@ -111,6 +119,7 @@ describe("get project info", async () => { isTsx: true, tailwindConfigFile: "tailwind.config.js", tailwindCssFile: "src/index.css", + tailwindVersion: "v3", aliasPrefix: null, }, },