mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-11 09:51:40 +00:00
Compare commits
1 Commits
shadcn/cli
...
shadcn/cli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8392c7fa0c |
@@ -1,5 +1,6 @@
|
||||
import { existsSync, promises as fs } from "fs"
|
||||
import path from "path"
|
||||
import { applyCSSUpdates } from "@/src/utils/css"
|
||||
import {
|
||||
DEFAULT_COMPONENTS,
|
||||
DEFAULT_TAILWIND_CONFIG,
|
||||
@@ -357,16 +358,17 @@ export async function runInit(cwd: string, config: Config) {
|
||||
|
||||
// Write css file.
|
||||
const baseColor = await getRegistryBaseColor(config.tailwind.baseColor)
|
||||
|
||||
if (baseColor) {
|
||||
await fs.writeFile(
|
||||
const cssFileInput = await fs.readFile(
|
||||
config.resolvedPaths.tailwindCss,
|
||||
config.tailwind.cssVariables
|
||||
? config.tailwind.prefix
|
||||
? applyPrefixesCss(baseColor.cssVarsTemplate, config.tailwind.prefix)
|
||||
: baseColor.cssVarsTemplate
|
||||
: baseColor.inlineColorsTemplate,
|
||||
"utf8"
|
||||
)
|
||||
// const cssFileTemplate = config.tailwind.cssVariables
|
||||
// ? baseColor.cssVarsTemplate
|
||||
// : baseColor.inlineColorsTemplate
|
||||
const cssFileContent = applyCSSUpdates(cssFileInput, baseColor, config)
|
||||
await fs.writeFile(config.resolvedPaths.tailwindCss, cssFileContent, "utf8")
|
||||
}
|
||||
|
||||
// Write cn file.
|
||||
|
||||
86
packages/cli/src/utils/css.ts
Normal file
86
packages/cli/src/utils/css.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Config } from "@/src/utils/get-config"
|
||||
import { registryBaseColorSchema } from "@/src/utils/registry/schema"
|
||||
import { applyPrefixesCss } from "@/src/utils/transformers/transform-tw-prefix"
|
||||
import * as z from "zod"
|
||||
|
||||
// This does a simple string replacement.
|
||||
// TODO: Do we want to use a proper CSS transformer here? Probably not right now.
|
||||
export function applyCSSUpdates(
|
||||
input: string,
|
||||
baseColor: Pick<z.infer<typeof registryBaseColorSchema>, "cssVars">,
|
||||
config: Partial<Config>
|
||||
) {
|
||||
if (!isCSSUpdateRequired(input, config)) {
|
||||
return input
|
||||
}
|
||||
|
||||
let output = input
|
||||
// Check if the file contains the `@tailwind` directives.
|
||||
// We assume it does since Tailwind CSS is checked in preflight.
|
||||
if (!input.includes("@tailwind base")) {
|
||||
// Prepend the `@tailwind` directives.
|
||||
output = `@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n${input}`
|
||||
}
|
||||
|
||||
// No additional changes if not using CSS variables.
|
||||
if (!config.tailwind?.cssVariables) {
|
||||
return output
|
||||
}
|
||||
|
||||
const lightColors = Object.entries(baseColor.cssVars.light).map(
|
||||
([name, value]) => ` --${name}: ${value};`
|
||||
)
|
||||
const darkColors = Object.entries(baseColor.cssVars.dark).map(
|
||||
([name, value]) => ` --${name}: ${value};`
|
||||
)
|
||||
|
||||
output = output.replace(
|
||||
/@tailwind utilities;/,
|
||||
`@tailwind utilities;\n
|
||||
@layer base {
|
||||
:root {
|
||||
${lightColors.join("\n")}
|
||||
}
|
||||
|
||||
.dark {
|
||||
${darkColors.join("\n")}
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}`
|
||||
)
|
||||
|
||||
if (config.tailwind.prefix) {
|
||||
output = applyPrefixesCss(output, config.tailwind.prefix)
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
function isCSSUpdateRequired(input: string, config: Partial<Config>) {
|
||||
// Check for tailwind directives.
|
||||
const hasTailwindDirectives =
|
||||
input.includes("@tailwind base") &&
|
||||
input.includes("@tailwind utilities") &&
|
||||
input.includes("@tailwind components")
|
||||
|
||||
// If we're not using CSS variables, we only check for the `@tailwind` directives.
|
||||
if (!config.tailwind?.cssVariables) {
|
||||
return !hasTailwindDirectives
|
||||
}
|
||||
|
||||
// If we are using CSS variables, we check for the `@layer base` directives.
|
||||
// And we check for the `--background` and `--foreground` variables.
|
||||
return !(
|
||||
input.includes("@layer base") &&
|
||||
input.includes("--background") &&
|
||||
input.includes("--foreground")
|
||||
)
|
||||
}
|
||||
27
packages/cli/test/fixtures/css/app-globals.css
vendored
Normal file
27
packages/cli/test/fixtures/css/app-globals.css
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
@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));
|
||||
}
|
||||
38
packages/cli/test/fixtures/css/applied-css-vars.css
vendored
Normal file
38
packages/cli/test/fixtures/css/applied-css-vars.css
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--primary: 220.9 39.3% 11%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 71.4% 4.1%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--primary: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
7
packages/cli/test/fixtures/css/applied.css
vendored
Normal file
7
packages/cli/test/fixtures/css/applied.css
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
3
packages/cli/test/fixtures/css/no-tailwind.css
vendored
Normal file
3
packages/cli/test/fixtures/css/no-tailwind.css
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
304
packages/cli/test/utils/__snapshots__/css.test.ts.snap
Normal file
304
packages/cli/test/utils/__snapshots__/css.test.ts.snap
Normal file
@@ -0,0 +1,304 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`apply css updates 1`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 2`] = `
|
||||
"@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));
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 3`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--primary: 220.9 39.3% 11%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 71.4% 4.1%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--primary: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 4`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--primary: 220.9 39.3% 11%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 71.4% 4.1%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--primary: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
: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));
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 5`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--primary: 220.9 39.3% 11%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 71.4% 4.1%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--primary: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply tw-border-border;
|
||||
}
|
||||
body {
|
||||
@apply tw-bg-background tw-text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 6`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--primary: 220.9 39.3% 11%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 71.4% 4.1%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--primary: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply cn-border-border;
|
||||
}
|
||||
body {
|
||||
@apply cn-bg-background cn-text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
: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));
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 7`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`apply css updates 8`] = `
|
||||
"@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--primary: 220.9 39.3% 11%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 71.4% 4.1%;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--primary: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: tomato;
|
||||
}
|
||||
"
|
||||
`;
|
||||
154
packages/cli/test/utils/css.test.ts
Normal file
154
packages/cli/test/utils/css.test.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import { promises as fs } from "fs"
|
||||
import path from "path"
|
||||
import { expect, test } from "vitest"
|
||||
|
||||
import { applyCSSUpdates } from "../../src/utils/css"
|
||||
|
||||
const baseColor = {
|
||||
cssVars: {
|
||||
light: {
|
||||
background: "0 0% 100%",
|
||||
foreground: "224 71.4% 4.1%",
|
||||
primary: "220.9 39.3% 11%",
|
||||
"primary-foreground": "210 20% 98%",
|
||||
border: "220 13% 91%",
|
||||
input: "220 13% 91%",
|
||||
ring: "224 71.4% 4.1%",
|
||||
},
|
||||
dark: {
|
||||
background: "224 71.4% 4.1%",
|
||||
foreground: "210 20% 98%",
|
||||
primary: "210 20% 98%",
|
||||
"primary-foreground": "220.9 39.3% 11%",
|
||||
border: "215 27.9% 16.9%",
|
||||
input: "215 27.9% 16.9%",
|
||||
ring: "216 12.2% 83.9%",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
test("apply css updates", async () => {
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/no-tailwind.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/app-globals.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/no-tailwind.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/app-globals.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
// Prefix.
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/no-tailwind.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: true,
|
||||
prefix: "tw-",
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/app-globals.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: true,
|
||||
prefix: "cn-",
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
// Applied.
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/applied.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: false,
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
|
||||
expect(
|
||||
await applyCSSUpdates(
|
||||
await fs.readFile(
|
||||
path.resolve(__dirname, "../fixtures/css/applied-css-vars.css"),
|
||||
"utf8"
|
||||
),
|
||||
baseColor,
|
||||
{
|
||||
tailwind: {
|
||||
cssVariables: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot()
|
||||
})
|
||||
Reference in New Issue
Block a user