refactor(cli): move to updaters

This commit is contained in:
shadcn
2024-08-18 14:30:08 +04:00
parent 0711a3711e
commit 119bc7b044
13 changed files with 666 additions and 183 deletions

View File

@@ -12,18 +12,6 @@ import {
} from "@/src/utils/get-config"
import { getProjectConfig } from "@/src/utils/get-project-info"
import { handleError } from "@/src/utils/handle-error"
import {
INITIAL_TAILWIND_CONFIG,
INITIAL_TAILWIND_CONFIG_WITH_CSS_VARIABLES,
} from "@/src/utils/initializers/defaults"
import { initializeDependencies } from "@/src/utils/initializers/initialize-dependencies"
import { initializeDestinations } from "@/src/utils/initializers/initialize-destinations"
import {
buildTailwindThemeColorsFromCssVars,
initializeTailwindConfig,
} from "@/src/utils/initializers/initialize-tailwind-config"
import { initializeTailwindCss } from "@/src/utils/initializers/initialize-tailwind-css"
import { initializeUtils } from "@/src/utils/initializers/initialize-utils"
import { logger } from "@/src/utils/logger"
import { preFlight } from "@/src/utils/preflight"
import {
@@ -32,6 +20,14 @@ import {
getRegistryStyleIndex,
getRegistryStyles,
} from "@/src/utils/registry"
import { updateDependencies } from "@/src/utils/updaters/update-dependencies"
import { updateDestinations } from "@/src/utils/updaters/update-destinations"
import {
buildTailwindThemeColorsFromCssVars,
updateTailwindConfig,
} from "@/src/utils/updaters/update-tailwind-config"
import { updateTailwindCss } from "@/src/utils/updaters/update-tailwind-css"
import { updateUtils } from "@/src/utils/updaters/update-utils"
import chalk from "chalk"
import { Command } from "commander"
import deepmerge from "deepmerge"
@@ -307,11 +303,8 @@ export async function promptForMinimalConfig(
}
export async function runInit(config: Config) {
await initializeDestinations(config)
// Run initializers.
const initializersSpinner = ora(`Initializing project...`)?.start()
await updateDestinations(config)
const [payload, baseColor] = await Promise.all([
getRegistryStyleIndex(config.style),
getRegistryBaseColor(config.tailwind.baseColor),
@@ -331,7 +324,7 @@ export async function runInit(config: Config) {
}
// Move the css vars to the tailwind config.
if (payload.tailwind?.config) {
if (payload.tailwind?.config && baseColor.cssVars?.light) {
payload.tailwind.config = deepmerge(payload.tailwind.config, {
theme: {
extend: {
@@ -345,14 +338,16 @@ export async function runInit(config: Config) {
}
if (payload.tailwind?.config) {
await initializeTailwindConfig(config, payload.tailwind?.config)
await updateTailwindConfig(payload.tailwind?.config, config)
}
await initializeTailwindCss(config)
await initializeUtils(config)
await updateTailwindCss(config)
await updateUtils(config)
initializersSpinner?.succeed()
// Install dependencies.
const dependenciesSpinner = ora(`Installing dependencies...`)?.start()
await initializeDependencies(config)
dependenciesSpinner?.succeed()
if (payload.dependencies) {
const dependenciesSpinner = ora(`Installing dependencies...`)?.start()
await updateDependencies(payload.dependencies, config)
dependenciesSpinner?.succeed()
}
}

View File

@@ -1,29 +0,0 @@
import { Config } from "@/src/utils/get-config"
import { getPackageManager } from "@/src/utils/get-package-manager"
import { execa } from "execa"
const PROJECT_DEPENDENCIES = [
"tailwindcss-animate",
"class-variance-authority",
"clsx",
"tailwind-merge",
]
export async function initializeDependencies(config: Config) {
const packageManager = await getPackageManager(config.resolvedPaths.cwd)
const dependencies = [
...PROJECT_DEPENDENCIES,
// TODO: add support for other icon libraries.
// TODO: remove this when we migrate new-york to lucide-react.
config.style === "new-york" ? "@radix-ui/react-icons" : "lucide-react",
]
await execa(
packageManager,
[packageManager === "npm" ? "install" : "add", ...dependencies],
{
cwd: config.resolvedPaths.cwd,
}
)
}

View File

@@ -0,0 +1,18 @@
import { Config } from "@/src/utils/get-config"
import { getPackageManager } from "@/src/utils/get-package-manager"
import { execa } from "execa"
export async function updateDependencies(
dependencies: string[],
config: Config
) {
const packageManager = await getPackageManager(config.resolvedPaths.cwd)
await execa(
packageManager,
[packageManager === "npm" ? "install" : "add", ...dependencies],
{
cwd: config.resolvedPaths.cwd,
}
)
}

View File

@@ -2,7 +2,7 @@ import { existsSync, promises as fs } from "fs"
import path from "path"
import { Config } from "@/src/utils/get-config"
export async function initializeDestinations(config: Config) {
export async function updateDestinations(config: Config) {
// Ensure all resolved paths directories exist.
for (const [key, resolvedPath] of Object.entries(config.resolvedPaths)) {
// Determine if the path is a file or directory.

View File

@@ -15,14 +15,14 @@ import {
VariableStatement,
} from "ts-morph"
export type InitializerTailwindConfig = Omit<TailwindConfig, "plugins"> & {
export type UpdaterTailwindConfig = Omit<TailwindConfig, "plugins"> & {
// We only want string plugins for now.
plugins?: string[]
}
export async function initializeTailwindConfig(
config: Config,
tailwindConfig: InitializerTailwindConfig
export async function updateTailwindConfig(
tailwindConfig: UpdaterTailwindConfig,
config: Config
) {
const raw = await fs.readFile(config.resolvedPaths.tailwindConfig, "utf8")
const output = await transformTailwindConfig(raw, tailwindConfig, {
@@ -33,7 +33,7 @@ export async function initializeTailwindConfig(
export async function transformTailwindConfig(
input: string,
tailwindConfig: InitializerTailwindConfig,
tailwindConfig: UpdaterTailwindConfig,
{
config,
}: {
@@ -150,7 +150,7 @@ function addTailwindConfigProperty(
async function addTailwindConfigTheme(
configObject: ObjectLiteralExpression,
theme: InitializerTailwindConfig["theme"]
theme: UpdaterTailwindConfig["theme"]
) {
// Ensure there is a theme property.
if (!configObject.getProperty("theme")) {

View File

@@ -3,7 +3,7 @@ import { Config } from "@/src/utils/get-config"
import { getRegistryBaseColor } from "@/src/utils/registry"
import { applyPrefixesCss } from "@/src/utils/transformers/transform-tw-prefix"
export async function initializeTailwindCss(config: Config) {
export async function updateTailwindCss(config: Config) {
// Write css file.
const baseColor = await getRegistryBaseColor(config.tailwind.baseColor)
if (baseColor) {

View File

@@ -2,7 +2,7 @@ import { promises as fs } from "fs"
import { Config } from "@/src/utils/get-config"
import * as templates from "@/src/utils/templates"
export async function initializeUtils(config: Config) {
export async function updateUtils(config: Config) {
const extension = config.tsx ? "ts" : "js"
await fs.writeFile(
`${config.resolvedPaths.utils}.${extension}`,

View File

@@ -25,6 +25,41 @@ test("init config-full", async () => {
cssVarsTemplate:
"@tailwind base;\n@tailwind components;\n@tailwind utilities;\n",
})
vi.spyOn(registry, "getRegistryStyleIndex").mockResolvedValue({
name: "new-york",
dependencies: [
"tailwindcss-animate",
"class-variance-authority",
"clsx",
"tailwind-merge",
"lucide-react",
"@radix-ui/react-icons",
],
registryDependencies: [],
tailwind: {
config: {
theme: {
extend: {
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
},
},
plugins: ['require("tailwindcss-animate")'],
},
},
files: [],
cssVariables: {
light: {
"--radius": "0.5rem",
},
dark: {
"--radius": "0.5rem",
},
},
})
const mockMkdir = vi.spyOn(fs.promises, "mkdir").mockResolvedValue(undefined)
const mockWriteFile = vi.spyOn(fs.promises, "writeFile").mockResolvedValue()
@@ -68,6 +103,7 @@ test("init config-full", async () => {
"class-variance-authority",
"clsx",
"tailwind-merge",
"lucide-react",
"@radix-ui/react-icons",
],
{
@@ -89,6 +125,40 @@ test("init config-partial", async () => {
cssVarsTemplate:
"@tailwind base;\n@tailwind components;\n@tailwind utilities;\n",
})
vi.spyOn(registry, "getRegistryStyleIndex").mockResolvedValue({
name: "new-york",
dependencies: [
"tailwindcss-animate",
"class-variance-authority",
"clsx",
"tailwind-merge",
"lucide-react",
],
registryDependencies: [],
tailwind: {
config: {
theme: {
extend: {
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
},
},
plugins: ['require("tailwindcss-animate")'],
},
},
files: [],
cssVariables: {
light: {
"--radius": "0.5rem",
},
dark: {
"--radius": "0.5rem",
},
},
})
const mockMkdir = vi.spyOn(fs.promises, "mkdir").mockResolvedValue(undefined)
const mockWriteFile = vi.spyOn(fs.promises, "writeFile").mockResolvedValue()

View File

@@ -1,115 +0,0 @@
import { describe, expect, test } from "vitest"
import { buildTailwindThemeColorsFromCssVars } from "../../../src/utils/initializers/initialize-tailwind-config"
describe("buildTailwindThemeColorsFromCssVars", () => {
test("should inline color names", () => {
expect(
buildTailwindThemeColorsFromCssVars({
primary: "blue",
"primary-light": "skyblue",
"primary-dark": "navy",
secondary: "green",
accent: "orange",
"accent-hover": "darkorange",
"accent-active": "orangered",
})
).toEqual({
primary: {
DEFAULT: "hsl(var(--primary))",
light: "hsl(var(--primary-light))",
dark: "hsl(var(--primary-dark))",
},
secondary: "hsl(var(--secondary))",
accent: {
DEFAULT: "hsl(var(--accent))",
hover: "hsl(var(--accent-hover))",
active: "hsl(var(--accent-active))",
},
})
})
test("should not add a DEFAULT if not present", () => {
expect(
buildTailwindThemeColorsFromCssVars({
"primary-light": "skyblue",
"primary-dark": "navy",
secondary: "green",
accent: "orange",
"accent-hover": "darkorange",
"accent-active": "orangered",
})
).toEqual({
primary: {
light: "hsl(var(--primary-light))",
dark: "hsl(var(--primary-dark))",
},
secondary: "hsl(var(--secondary))",
accent: {
DEFAULT: "hsl(var(--accent))",
hover: "hsl(var(--accent-hover))",
active: "hsl(var(--accent-active))",
},
})
})
test("should build tailwind theme colors from css vars", () => {
expect(
buildTailwindThemeColorsFromCssVars({
background: "0 0% 100%",
foreground: "224 71.4% 4.1%",
card: "0 0% 100%",
"card-foreground": "224 71.4% 4.1%",
popover: "0 0% 100%",
"popover-foreground": "224 71.4% 4.1%",
primary: "220.9 39.3% 11%",
"primary-foreground": "210 20% 98%",
secondary: "220 14.3% 95.9%",
"secondary-foreground": "220.9 39.3% 11%",
muted: "220 14.3% 95.9%",
"muted-foreground": "220 8.9% 46.1%",
accent: "220 14.3% 95.9%",
"accent-foreground": "220.9 39.3% 11%",
destructive: "0 84.2% 60.2%",
"destructive-foreground": "210 20% 98%",
border: "220 13% 91%",
input: "220 13% 91%",
ring: "224 71.4% 4.1%",
})
).toEqual({
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
})
})
})

View File

@@ -0,0 +1,433 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`transformTailwindConfig -> darkMode property > should add darkMode property if not in config 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [],
}
export default config
"
`;
exports[`transformTailwindConfig -> darkMode property > should add darkMode property if not in config 2`] = `
"/** @type {import('tailwindcss').Config} */
export default {
darkMode: ['class'],
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {},
},
plugins: [],
}
"
`;
exports[`transformTailwindConfig -> darkMode property > should add darkMode property if not in config 3`] = `
"/** @type {import('tailwindcss').Config} */
const foo = {
bar: 'baz',
}
export default {
darkMode: ['class'],
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
theme: {
extend: {},
},
plugins: [],
}
"
`;
exports[`transformTailwindConfig -> darkMode property > should append class to darkMode property if existing array 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ["selector", "class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [],
}
export default config
"
`;
exports[`transformTailwindConfig -> darkMode property > should convert string to array and add class if darkMode is string 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ["selector", "class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [],
}
export default config
"
`;
exports[`transformTailwindConfig -> darkMode property > should not add darkMode property if already in config 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ['class'],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [],
}
export default config
"
`;
exports[`transformTailwindConfig -> darkMode property > should not add darkMode property if already in config 2`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ['class', 'selector'],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [],
}
export default config
"
`;
exports[`transformTailwindConfig -> darkMode property > should preserve quote kind 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ['selector', '[data-mode="dark"]', 'class'],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [],
}
export default config
"
`;
exports[`transformTailwindConfig -> darkMode property > should work with multiple darkMode selectors 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ['variant', [
'@media (prefers-color-scheme: dark) { &:not(.light *) }',
'&:is(.dark *)',
], 'class'],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [],
}
export default config
"
`;
exports[`transformTailwindConfig -> plugin > should add plugin if not in config 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [require("tailwindcss-animate")]
}
export default config
"
`;
exports[`transformTailwindConfig -> plugin > should append plugin to existing array 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [require("@tailwindcss/typography"), require("tailwindcss-animate")],
}
export default config
"
`;
exports[`transformTailwindConfig -> plugin > should not add plugin if already in config 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [require("@tailwindcss/typography"), require("tailwindcss-animate")],
}
export default config
"
`;
exports[`transformTailwindConfig -> theme > should add theme if not in config 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
}
}
}
}
}
export default config
"
`;
exports[`transformTailwindConfig -> theme > should handle multiple properties 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
fontFamily: {
sans: ["var(--font-geist-sans)", ...fontFamily.sans],
mono: ["var(--font-mono)", ...fontFamily.mono],
heading: [
'var(--font-geist-sans)'
]
},
colors: {
...defaultColors,
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
}
},
boxShadow: {
...defaultBoxShadow,
'3xl': '0 35px 60px -15px rgba(0, 0, 0, 0.3)'
},
borderRadius: {
'3xl': '2rem',
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)'
},
animation: {
...defaultAnimation,
'spin-slow': 'spin 3s linear infinite',
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out'
}
}
},
}
export default config
"
`;
exports[`transformTailwindConfig -> theme > should keep spread assignments 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
...defaultColors,
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
}
}
}
},
}
export default config
"
`;
exports[`transformTailwindConfig -> theme > should merge existing theme 1`] = `
"import type { Config } from 'tailwindcss'
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
}
}
}
},
}
export default config
"
`;

View File

@@ -1,10 +1,12 @@
import { Project, SyntaxKind } from "ts-morph"
import { beforeEach, describe, expect, test } from "vitest"
import {
buildTailwindThemeColorsFromCssVars,
nestSpreadProperties,
transformTailwindConfig,
unnestSpreadProperties,
} from "@/src/utils/initializers/initialize-tailwind-config"
import { Project, SyntaxKind } from "ts-morph"
import { beforeEach, describe, expect, test } from "vitest"
} from "../../../src/utils/updaters/update-tailwind-config"
const SHARED_CONFIG = {
$schema: "https://ui.shadcn.com/schema.json",
@@ -794,3 +796,115 @@ describe("unnestSpreadProperties", () => {
)
})
})
describe("buildTailwindThemeColorsFromCssVars", () => {
test("should inline color names", () => {
expect(
buildTailwindThemeColorsFromCssVars({
primary: "blue",
"primary-light": "skyblue",
"primary-dark": "navy",
secondary: "green",
accent: "orange",
"accent-hover": "darkorange",
"accent-active": "orangered",
})
).toEqual({
primary: {
DEFAULT: "hsl(var(--primary))",
light: "hsl(var(--primary-light))",
dark: "hsl(var(--primary-dark))",
},
secondary: "hsl(var(--secondary))",
accent: {
DEFAULT: "hsl(var(--accent))",
hover: "hsl(var(--accent-hover))",
active: "hsl(var(--accent-active))",
},
})
})
test("should not add a DEFAULT if not present", () => {
expect(
buildTailwindThemeColorsFromCssVars({
"primary-light": "skyblue",
"primary-dark": "navy",
secondary: "green",
accent: "orange",
"accent-hover": "darkorange",
"accent-active": "orangered",
})
).toEqual({
primary: {
light: "hsl(var(--primary-light))",
dark: "hsl(var(--primary-dark))",
},
secondary: "hsl(var(--secondary))",
accent: {
DEFAULT: "hsl(var(--accent))",
hover: "hsl(var(--accent-hover))",
active: "hsl(var(--accent-active))",
},
})
})
test("should build tailwind theme colors from css vars", () => {
expect(
buildTailwindThemeColorsFromCssVars({
background: "0 0% 100%",
foreground: "224 71.4% 4.1%",
card: "0 0% 100%",
"card-foreground": "224 71.4% 4.1%",
popover: "0 0% 100%",
"popover-foreground": "224 71.4% 4.1%",
primary: "220.9 39.3% 11%",
"primary-foreground": "210 20% 98%",
secondary: "220 14.3% 95.9%",
"secondary-foreground": "220.9 39.3% 11%",
muted: "220 14.3% 95.9%",
"muted-foreground": "220 8.9% 46.1%",
accent: "220 14.3% 95.9%",
"accent-foreground": "220.9 39.3% 11%",
destructive: "0 84.2% 60.2%",
"destructive-foreground": "210 20% 98%",
border: "220 13% 91%",
input: "220 13% 91%",
ring: "224 71.4% 4.1%",
})
).toEqual({
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
})
})
})

View File

@@ -9,9 +9,6 @@
"@/*": ["./*"]
}
},
"include": [
"src/**/*.ts",
"test/utils/initializers/initialize-tailwind-config.test.ts"
],
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "test/fixtures"]
}