shadcn-vue 1.0.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -99,7 +99,7 @@ var rawConfigSchema = z.object({
99
99
  style: z.string(),
100
100
  typescript: z.boolean().default(true),
101
101
  tailwind: z.object({
102
- config: z.string(),
102
+ config: z.string().optional(),
103
103
  css: z.string(),
104
104
  baseColor: z.string(),
105
105
  cssVariables: z.boolean().default(true),
@@ -152,7 +152,7 @@ async function resolveConfigPaths(cwd, config) {
152
152
  ...config,
153
153
  resolvedPaths: {
154
154
  cwd,
155
- tailwindConfig: path.resolve(cwd, config.tailwind.config),
155
+ tailwindConfig: config.tailwind.config ? path.resolve(cwd, config.tailwind.config) : "",
156
156
  tailwindCss: path.resolve(cwd, config.tailwind.css),
157
157
  utils: await resolveImport(config.aliases.utils, tsConfig),
158
158
  components: await resolveImport(config.aliases.components, tsConfig),
@@ -182,9 +182,14 @@ async function getRawConfig(cwd) {
182
182
  }
183
183
  return rawConfigSchema.parse(configResult.config);
184
184
  } catch (error) {
185
+ console.log(error.message);
185
186
  throw new Error(`Invalid configuration found in ${cwd}/components.json.`);
186
187
  }
187
188
  }
189
+ async function getTargetStyleFromConfig(cwd, fallback) {
190
+ const projectInfo = await getProjectInfo(cwd);
191
+ return projectInfo?.tailwindVersion === "v4" ? "new-york-v4" : fallback;
192
+ }
188
193
 
189
194
  // src/utils/get-package-info.ts
190
195
  import fs from "fs-extra";
@@ -220,6 +225,7 @@ async function getProjectInfo(cwd) {
220
225
  typescript,
221
226
  tailwindConfigFile,
222
227
  tailwindCssFile,
228
+ tailwindVersion,
223
229
  aliasPrefix,
224
230
  packageJson
225
231
  ] = await Promise.all([
@@ -231,6 +237,7 @@ async function getProjectInfo(cwd) {
231
237
  isTypeScriptProject(cwd),
232
238
  getTailwindConfigFile(cwd),
233
239
  getTailwindCssFile(cwd),
240
+ getTailwindVersion(cwd),
234
241
  getTsConfigAliasPrefix(cwd),
235
242
  getPackageInfo(cwd, false)
236
243
  ]);
@@ -239,6 +246,7 @@ async function getProjectInfo(cwd) {
239
246
  typescript,
240
247
  tailwindConfigFile,
241
248
  tailwindCssFile,
249
+ tailwindVersion,
242
250
  aliasPrefix
243
251
  };
244
252
  if (configFiles.find((file) => file.startsWith("nuxt.config."))?.length) {
@@ -259,18 +267,40 @@ async function getProjectInfo(cwd) {
259
267
  }
260
268
  return type;
261
269
  }
270
+ async function getTailwindVersion(cwd) {
271
+ const [packageInfo, config] = await Promise.all([
272
+ getPackageInfo(cwd),
273
+ getConfig(cwd)
274
+ ]);
275
+ if (config?.tailwind?.config === "") {
276
+ return "v4";
277
+ }
278
+ if (!packageInfo?.dependencies?.tailwindcss && !packageInfo?.devDependencies?.tailwindcss) {
279
+ return null;
280
+ }
281
+ if (/^(?:\^|~)?3(?:\.\d+)*(?:-.*)?$/.test(
282
+ packageInfo?.dependencies?.tailwindcss || packageInfo?.devDependencies?.tailwindcss || ""
283
+ )) {
284
+ return "v3";
285
+ }
286
+ return "v4";
287
+ }
262
288
  async function getTailwindCssFile(cwd) {
263
- const files = await glob(["**/*.css", "**/*.scss"], {
264
- cwd,
265
- deep: 5,
266
- ignore: PROJECT_SHARED_IGNORE
267
- });
289
+ const [files, tailwindVersion] = await Promise.all([
290
+ glob(["**/*.css", "**/*.scss"], {
291
+ cwd,
292
+ deep: 5,
293
+ ignore: PROJECT_SHARED_IGNORE
294
+ }),
295
+ getTailwindVersion(cwd)
296
+ ]);
268
297
  if (!files.length) {
269
298
  return null;
270
299
  }
300
+ const needle = tailwindVersion === "v4" ? `@import "tailwindcss"` : "@tailwind base";
271
301
  for (const file of files) {
272
302
  const contents = await fs2.readFile(path3.resolve(cwd, file), "utf8");
273
- if (contents.includes("@tailwind base")) {
303
+ if (contents.includes(`@import "tailwindcss"`) || contents.includes(`@import 'tailwindcss'`) || contents.includes(`@tailwind base`)) {
274
304
  return file;
275
305
  }
276
306
  }
@@ -317,7 +347,7 @@ async function getProjectConfig(cwd, defaultProjectInfo = null) {
317
347
  if (existingConfig) {
318
348
  return existingConfig;
319
349
  }
320
- if (!projectInfo || !projectInfo.tailwindConfigFile || !projectInfo.tailwindCssFile) {
350
+ if (!projectInfo || !projectInfo.tailwindCssFile || projectInfo.tailwindVersion === "v3" && !projectInfo.tailwindConfigFile) {
321
351
  return null;
322
352
  }
323
353
  const config = {
@@ -325,7 +355,7 @@ async function getProjectConfig(cwd, defaultProjectInfo = null) {
325
355
  typescript: projectInfo.typescript,
326
356
  style: "new-york",
327
357
  tailwind: {
328
- config: projectInfo.tailwindConfigFile,
358
+ config: projectInfo.tailwindConfigFile ?? "",
329
359
  baseColor: "zinc",
330
360
  css: projectInfo.tailwindCssFile,
331
361
  cssVariables: true,
@@ -342,6 +372,16 @@ async function getProjectConfig(cwd, defaultProjectInfo = null) {
342
372
  };
343
373
  return await resolveConfigPaths(cwd, config);
344
374
  }
375
+ async function getProjectTailwindVersionFromConfig(config) {
376
+ if (!config.resolvedPaths?.cwd) {
377
+ return "v3";
378
+ }
379
+ const projectInfo = await getProjectInfo(config.resolvedPaths.cwd);
380
+ if (!projectInfo?.tailwindVersion) {
381
+ return null;
382
+ }
383
+ return projectInfo.tailwindVersion;
384
+ }
345
385
 
346
386
  // src/utils/logger.ts
347
387
  import consola from "consola";
@@ -429,19 +469,27 @@ Once configured, you can use the cli to add components.`
429
469
  logger.break();
430
470
  process.exit(1);
431
471
  }
432
- if (!projectInfo) {
433
- logger.break();
434
- process.exit(1);
435
- }
436
472
  frameworkSpinner?.succeed(
437
473
  `Verifying framework. Found ${highlighter.info(
438
474
  projectInfo.framework.label
439
475
  )}.`
440
476
  );
441
- const tailwindSpinner = spinner(`Validating Tailwind CSS.`, {
477
+ let tailwindSpinnerMessage = "Validating Tailwind CSS.";
478
+ if (projectInfo.tailwindVersion === "v4") {
479
+ tailwindSpinnerMessage = `Validating Tailwind CSS config. Found ${highlighter.info(
480
+ "v4"
481
+ )}.`;
482
+ }
483
+ const tailwindSpinner = spinner(tailwindSpinnerMessage, {
442
484
  silent: options.silent
443
485
  }).start();
444
- if (!projectInfo?.tailwindConfigFile || !projectInfo?.tailwindCssFile) {
486
+ if (projectInfo.tailwindVersion === "v3" && (!projectInfo?.tailwindConfigFile || !projectInfo?.tailwindCssFile)) {
487
+ errors[TAILWIND_NOT_CONFIGURED] = true;
488
+ tailwindSpinner?.fail();
489
+ } else if (projectInfo.tailwindVersion === "v4" && !projectInfo?.tailwindCssFile) {
490
+ errors[TAILWIND_NOT_CONFIGURED] = true;
491
+ tailwindSpinner?.fail();
492
+ } else if (!projectInfo.tailwindVersion) {
445
493
  errors[TAILWIND_NOT_CONFIGURED] = true;
446
494
  tailwindSpinner?.fail();
447
495
  } else {
@@ -579,6 +627,10 @@ var registryBaseColorSchema = z3.object({
579
627
  light: z3.record(z3.string(), z3.string()),
580
628
  dark: z3.record(z3.string(), z3.string())
581
629
  }),
630
+ cssVarsV4: z3.object({
631
+ light: z3.record(z3.string(), z3.string()),
632
+ dark: z3.record(z3.string(), z3.string())
633
+ }).optional(),
582
634
  inlineColorsTemplate: z3.string(),
583
635
  cssVarsTemplate: z3.string()
584
636
  });
@@ -609,8 +661,12 @@ async function updateTailwindConfig(tailwindConfig, config, options) {
609
661
  }
610
662
  options = {
611
663
  silent: false,
664
+ tailwindVersion: "v3",
612
665
  ...options
613
666
  };
667
+ if (options.tailwindVersion === "v4") {
668
+ return;
669
+ }
614
670
  const tailwindFileRelativePath = path5.relative(
615
671
  config.resolvedPaths.cwd,
616
672
  config.resolvedPaths.tailwindConfig
@@ -979,29 +1035,30 @@ async function getRegistryIcons() {
979
1035
  return {};
980
1036
  }
981
1037
  }
1038
+ var BASE_COLORS = [
1039
+ {
1040
+ name: "neutral",
1041
+ label: "Neutral"
1042
+ },
1043
+ {
1044
+ name: "gray",
1045
+ label: "Gray"
1046
+ },
1047
+ {
1048
+ name: "zinc",
1049
+ label: "Zinc"
1050
+ },
1051
+ {
1052
+ name: "stone",
1053
+ label: "Stone"
1054
+ },
1055
+ {
1056
+ name: "slate",
1057
+ label: "Slate"
1058
+ }
1059
+ ];
982
1060
  async function getRegistryBaseColors() {
983
- return [
984
- {
985
- name: "neutral",
986
- label: "Neutral"
987
- },
988
- {
989
- name: "gray",
990
- label: "Gray"
991
- },
992
- {
993
- name: "zinc",
994
- label: "Zinc"
995
- },
996
- {
997
- name: "stone",
998
- label: "Stone"
999
- },
1000
- {
1001
- name: "slate",
1002
- label: "Slate"
1003
- }
1004
- ];
1061
+ return BASE_COLORS;
1005
1062
  }
1006
1063
  async function getRegistryBaseColor(baseColor) {
1007
1064
  try {
@@ -1138,9 +1195,10 @@ async function registryResolveItemsTree(names, config) {
1138
1195
  async function resolveRegistryDependencies(url, config) {
1139
1196
  const visited = /* @__PURE__ */ new Set();
1140
1197
  const payload = [];
1198
+ const style = config.resolvedPaths?.cwd ? await getTargetStyleFromConfig(config.resolvedPaths.cwd, config.style) : config.style;
1141
1199
  async function resolveDependencies(itemUrl) {
1142
1200
  const url2 = getRegistryUrl(
1143
- isUrl(itemUrl) ? itemUrl : `styles/${config.style}/${itemUrl}.json`
1201
+ isUrl(itemUrl) ? itemUrl : `styles/${style}/${itemUrl}.json`
1144
1202
  );
1145
1203
  if (visited.has(url2)) {
1146
1204
  return;
@@ -1166,7 +1224,10 @@ async function resolveRegistryDependencies(url, config) {
1166
1224
  return Array.from(new Set(payload));
1167
1225
  }
1168
1226
  async function registryGetTheme(name, config) {
1169
- const baseColor = await getRegistryBaseColor(name);
1227
+ const [baseColor, tailwindVersion] = await Promise.all([
1228
+ getRegistryBaseColor(name),
1229
+ getProjectTailwindVersionFromConfig(config)
1230
+ ]);
1170
1231
  if (!baseColor) {
1171
1232
  return null;
1172
1233
  }
@@ -1209,6 +1270,18 @@ async function registryGetTheme(name, config) {
1209
1270
  ...theme.cssVars.dark
1210
1271
  }
1211
1272
  };
1273
+ if (tailwindVersion === "v4" && baseColor.cssVarsV4) {
1274
+ theme.cssVars = {
1275
+ light: {
1276
+ ...theme.cssVars.light,
1277
+ ...baseColor.cssVarsV4.light
1278
+ },
1279
+ dark: {
1280
+ ...theme.cssVars.dark,
1281
+ ...baseColor.cssVarsV4.dark
1282
+ }
1283
+ };
1284
+ }
1212
1285
  }
1213
1286
  return theme;
1214
1287
  }
@@ -1233,16 +1306,18 @@ function isUrl(path17) {
1233
1306
 
1234
1307
  // src/utils/updaters/update-css-vars.ts
1235
1308
  import { promises as fs5 } from "node:fs";
1236
- import path7 from "pathe";
1309
+ import path7 from "node:path";
1237
1310
  import postcss from "postcss";
1238
1311
  import AtRule from "postcss/lib/at-rule";
1312
+ import { z as z5 } from "zod";
1239
1313
  async function updateCssVars(cssVars, config, options) {
1240
- if (!cssVars || !Object.keys(cssVars).length || !config.resolvedPaths.tailwindCss) {
1314
+ if (!config.resolvedPaths.tailwindCss || !Object.keys(cssVars ?? {}).length) {
1241
1315
  return;
1242
1316
  }
1243
1317
  options = {
1244
1318
  cleanupDefaultNextStyles: false,
1245
1319
  silent: false,
1320
+ tailwindVersion: "v3",
1246
1321
  ...options
1247
1322
  };
1248
1323
  const cssFilepath = config.resolvedPaths.tailwindCss;
@@ -1257,35 +1332,68 @@ async function updateCssVars(cssVars, config, options) {
1257
1332
  }
1258
1333
  ).start();
1259
1334
  const raw = await fs5.readFile(cssFilepath, "utf8");
1260
- const output = await transformCssVars(raw, cssVars, config, {
1261
- cleanupDefaultNextStyles: options.cleanupDefaultNextStyles
1335
+ const output = await transformCssVars(raw, cssVars ?? {}, config, {
1336
+ cleanupDefaultNextStyles: options.cleanupDefaultNextStyles,
1337
+ tailwindVersion: options.tailwindVersion,
1338
+ tailwindConfig: options.tailwindConfig
1262
1339
  });
1263
1340
  await fs5.writeFile(cssFilepath, output, "utf8");
1264
1341
  cssVarsSpinner.succeed();
1265
1342
  }
1266
- async function transformCssVars(input, cssVars, config, options) {
1343
+ async function transformCssVars(input, cssVars, config, options = {
1344
+ cleanupDefaultNextStyles: false,
1345
+ tailwindVersion: "v3",
1346
+ tailwindConfig: void 0
1347
+ }) {
1267
1348
  options = {
1268
1349
  cleanupDefaultNextStyles: false,
1350
+ tailwindVersion: "v3",
1351
+ tailwindConfig: void 0,
1269
1352
  ...options
1270
1353
  };
1271
- const plugins = [updateCssVarsPlugin(cssVars)];
1354
+ let plugins = [updateCssVarsPlugin(cssVars)];
1272
1355
  if (options.cleanupDefaultNextStyles) {
1273
1356
  plugins.push(cleanupDefaultNextStylesPlugin());
1274
1357
  }
1358
+ if (options.tailwindVersion === "v4") {
1359
+ plugins = [addCustomVariant({ params: "dark (&:is(.dark *))" })];
1360
+ if (options.cleanupDefaultNextStyles) {
1361
+ plugins.push(cleanupDefaultNextStylesPlugin());
1362
+ }
1363
+ plugins.push(updateCssVarsPluginV4(cssVars));
1364
+ plugins.push(updateThemePlugin(cssVars));
1365
+ if (options.tailwindConfig) {
1366
+ plugins.push(updateTailwindConfigPlugin(options.tailwindConfig));
1367
+ plugins.push(updateTailwindConfigAnimationPlugin(options.tailwindConfig));
1368
+ plugins.push(updateTailwindConfigKeyframesPlugin(options.tailwindConfig));
1369
+ }
1370
+ }
1275
1371
  if (config.tailwind.cssVariables) {
1276
- plugins.push(updateBaseLayerPlugin());
1372
+ plugins.push(
1373
+ updateBaseLayerPlugin({ tailwindVersion: options.tailwindVersion })
1374
+ );
1277
1375
  }
1278
1376
  const result = await postcss(plugins).process(input, {
1279
1377
  from: void 0
1280
1378
  });
1281
- return result.css;
1379
+ let output = result.css;
1380
+ output = output.replace(/\/\* ---break--- \*\//g, "");
1381
+ if (options.tailwindVersion === "v4") {
1382
+ output = output.replace(/(\n\s*\n)+/g, "\n\n");
1383
+ }
1384
+ return output;
1282
1385
  }
1283
- function updateBaseLayerPlugin() {
1386
+ function updateBaseLayerPlugin({
1387
+ tailwindVersion
1388
+ }) {
1284
1389
  return {
1285
1390
  postcssPlugin: "update-base-layer",
1286
1391
  Once(root) {
1287
1392
  const requiredRules = [
1288
- { selector: "*", apply: "border-border" },
1393
+ {
1394
+ selector: "*",
1395
+ apply: tailwindVersion === "v4" ? "border-border outline-ring/50" : "border-border"
1396
+ },
1289
1397
  { selector: "body", apply: "bg-background text-foreground" }
1290
1398
  ];
1291
1399
  let baseLayer = root.nodes.find(
@@ -1304,6 +1412,7 @@ function updateBaseLayerPlugin() {
1304
1412
  raws: { semicolon: true, between: " ", before: "\n" }
1305
1413
  });
1306
1414
  root.append(baseLayer);
1415
+ root.insertBefore(baseLayer, postcss.comment({ text: "---break---" }));
1307
1416
  }
1308
1417
  requiredRules.forEach(({ selector, apply }) => {
1309
1418
  const existingRule = baseLayer?.nodes?.find(
@@ -1347,6 +1456,7 @@ function updateCssVarsPlugin(cssVars) {
1347
1456
  }
1348
1457
  });
1349
1458
  root.append(baseLayer);
1459
+ root.insertBefore(baseLayer, postcss.comment({ text: "---break---" }));
1350
1460
  }
1351
1461
  if (baseLayer !== void 0) {
1352
1462
  Object.entries(cssVars).forEach(([key, vars]) => {
@@ -1387,6 +1497,9 @@ function cleanupDefaultNextStylesPlugin() {
1387
1497
  bodyRule.nodes.find((node) => {
1388
1498
  return node.type === "decl" && node.prop === "background" && (node.value.startsWith("linear-gradient") || node.value === "var(--background)");
1389
1499
  })?.remove();
1500
+ bodyRule.nodes.find(
1501
+ (node) => node.type === "decl" && node.prop === "font-family" && node.value === "Arial, Helvetica, sans-serif"
1502
+ )?.remove();
1390
1503
  if (bodyRule.nodes.length === 0) {
1391
1504
  bodyRule.remove();
1392
1505
  }
@@ -1430,6 +1543,298 @@ function addOrUpdateVars(baseLayer, selector, vars) {
1430
1543
  existingDecl ? existingDecl.replaceWith(newDecl) : ruleNode?.append(newDecl);
1431
1544
  });
1432
1545
  }
1546
+ function updateCssVarsPluginV4(cssVars) {
1547
+ return {
1548
+ postcssPlugin: "update-css-vars-v4",
1549
+ Once(root) {
1550
+ Object.entries(cssVars).forEach(([key, vars]) => {
1551
+ const selector = key === "light" ? ":root" : `.${key}`;
1552
+ let ruleNode = root.nodes?.find(
1553
+ (node) => node.type === "rule" && node.selector === selector
1554
+ );
1555
+ if (!ruleNode && Object.keys(vars).length > 0) {
1556
+ ruleNode = postcss.rule({
1557
+ selector,
1558
+ nodes: [],
1559
+ raws: { semicolon: true, between: " ", before: "\n" }
1560
+ });
1561
+ root.append(ruleNode);
1562
+ root.insertBefore(ruleNode, postcss.comment({ text: "---break---" }));
1563
+ }
1564
+ Object.entries(vars).forEach(([key2, value]) => {
1565
+ let prop = `--${key2.replace(/^--/, "")}`;
1566
+ if (prop === "--sidebar-background") {
1567
+ prop = "--sidebar";
1568
+ }
1569
+ if (isLocalHSLValue(value)) {
1570
+ value = `hsl(${value})`;
1571
+ }
1572
+ const newDecl = postcss.decl({
1573
+ prop,
1574
+ value,
1575
+ raws: { semicolon: true }
1576
+ });
1577
+ const existingDecl = ruleNode?.nodes.find(
1578
+ (node) => node.type === "decl" && node.prop === prop
1579
+ );
1580
+ if (!existingDecl) {
1581
+ ruleNode?.append(newDecl);
1582
+ }
1583
+ });
1584
+ });
1585
+ }
1586
+ };
1587
+ }
1588
+ function updateThemePlugin(cssVars) {
1589
+ return {
1590
+ postcssPlugin: "update-theme",
1591
+ Once(root) {
1592
+ const variables = Array.from(
1593
+ new Set(
1594
+ Object.keys(cssVars).flatMap(
1595
+ (key) => Object.keys(cssVars[key] || {})
1596
+ )
1597
+ )
1598
+ );
1599
+ if (!variables.length) {
1600
+ return;
1601
+ }
1602
+ const themeNode = upsertThemeNode(root);
1603
+ const themeVarNodes = themeNode.nodes?.filter(
1604
+ (node) => node.type === "decl" && node.prop.startsWith("--")
1605
+ );
1606
+ for (const variable of variables) {
1607
+ const value = Object.values(cssVars).find((vars) => vars[variable])?.[variable];
1608
+ if (!value) {
1609
+ continue;
1610
+ }
1611
+ if (variable === "radius") {
1612
+ const radiusVariables = {
1613
+ sm: "calc(var(--radius) - 4px)",
1614
+ md: "calc(var(--radius) - 2px)",
1615
+ lg: "var(--radius)",
1616
+ xl: "calc(var(--radius) + 4px)"
1617
+ };
1618
+ for (const [key, value2] of Object.entries(radiusVariables)) {
1619
+ const cssVarNode2 = postcss.decl({
1620
+ prop: `--radius-${key}`,
1621
+ value: value2,
1622
+ raws: { semicolon: true }
1623
+ });
1624
+ if (themeNode?.nodes?.find(
1625
+ (node) => node.type === "decl" && node.prop === cssVarNode2.prop
1626
+ )) {
1627
+ continue;
1628
+ }
1629
+ themeNode?.append(cssVarNode2);
1630
+ }
1631
+ continue;
1632
+ }
1633
+ let prop = isLocalHSLValue(value) || isColorValue(value) ? `--color-${variable.replace(/^--/, "")}` : `--${variable.replace(/^--/, "")}`;
1634
+ if (prop === "--color-sidebar-background") {
1635
+ prop = "--color-sidebar";
1636
+ }
1637
+ let propValue = `var(--${variable})`;
1638
+ if (prop === "--color-sidebar") {
1639
+ propValue = "var(--sidebar)";
1640
+ }
1641
+ const cssVarNode = postcss.decl({
1642
+ prop,
1643
+ value: propValue,
1644
+ raws: { semicolon: true }
1645
+ });
1646
+ const existingDecl = themeNode?.nodes?.find(
1647
+ (node) => node.type === "decl" && node.prop === cssVarNode.prop
1648
+ );
1649
+ if (!existingDecl) {
1650
+ if (themeVarNodes?.length) {
1651
+ themeNode?.insertAfter(
1652
+ themeVarNodes[themeVarNodes.length - 1],
1653
+ cssVarNode
1654
+ );
1655
+ } else {
1656
+ themeNode?.append(cssVarNode);
1657
+ }
1658
+ }
1659
+ }
1660
+ }
1661
+ };
1662
+ }
1663
+ function upsertThemeNode(root) {
1664
+ let themeNode = root.nodes.find(
1665
+ (node) => node.type === "atrule" && node.name === "theme" && node.params === "inline"
1666
+ );
1667
+ if (!themeNode) {
1668
+ themeNode = postcss.atRule({
1669
+ name: "theme",
1670
+ params: "inline",
1671
+ nodes: [],
1672
+ raws: { semicolon: true, between: " ", before: "\n" }
1673
+ });
1674
+ root.append(themeNode);
1675
+ root.insertBefore(themeNode, postcss.comment({ text: "---break---" }));
1676
+ }
1677
+ return themeNode;
1678
+ }
1679
+ function addCustomVariant({ params }) {
1680
+ return {
1681
+ postcssPlugin: "add-custom-variant",
1682
+ Once(root) {
1683
+ const customVariant = root.nodes.find(
1684
+ (node) => node.type === "atrule" && node.name === "custom-variant"
1685
+ );
1686
+ if (!customVariant) {
1687
+ const variantNode = postcss.atRule({
1688
+ name: "custom-variant",
1689
+ params,
1690
+ raws: { semicolon: true, before: "\n" }
1691
+ });
1692
+ root.insertAfter(root.nodes[0], variantNode);
1693
+ root.insertBefore(variantNode, postcss.comment({ text: "---break---" }));
1694
+ }
1695
+ }
1696
+ };
1697
+ }
1698
+ function updateTailwindConfigPlugin(tailwindConfig) {
1699
+ return {
1700
+ postcssPlugin: "update-tailwind-config",
1701
+ Once(root) {
1702
+ if (!tailwindConfig?.plugins) {
1703
+ return;
1704
+ }
1705
+ const quoteType = getQuoteType(root);
1706
+ const quote = quoteType === "single" ? "'" : '"';
1707
+ const pluginNodes = root.nodes.filter(
1708
+ (node) => node.type === "atrule" && node.name === "plugin"
1709
+ );
1710
+ const lastPluginNode = pluginNodes[pluginNodes.length - 1] || root.nodes[0];
1711
+ for (const plugin of tailwindConfig.plugins) {
1712
+ const pluginName = plugin.replace(/^require\(["']|["']\)$/g, "");
1713
+ if (pluginNodes.some((node) => {
1714
+ return node.params.replace(/["']/g, "") === pluginName;
1715
+ })) {
1716
+ continue;
1717
+ }
1718
+ const pluginNode = postcss.atRule({
1719
+ name: "plugin",
1720
+ params: `${quote}${pluginName}${quote}`,
1721
+ raws: { semicolon: true, before: "\n" }
1722
+ });
1723
+ root.insertAfter(lastPluginNode, pluginNode);
1724
+ root.insertBefore(pluginNode, postcss.comment({ text: "---break---" }));
1725
+ }
1726
+ }
1727
+ };
1728
+ }
1729
+ function updateTailwindConfigKeyframesPlugin(tailwindConfig) {
1730
+ return {
1731
+ postcssPlugin: "update-tailwind-config-keyframes",
1732
+ Once(root) {
1733
+ if (!tailwindConfig?.theme?.extend?.keyframes) {
1734
+ return;
1735
+ }
1736
+ const themeNode = upsertThemeNode(root);
1737
+ const existingKeyFrameNodes = themeNode.nodes?.filter(
1738
+ (node) => node.type === "atrule" && node.name === "keyframes"
1739
+ );
1740
+ const keyframeValueSchema = z5.record(
1741
+ z5.string(),
1742
+ z5.record(z5.string(), z5.string())
1743
+ );
1744
+ for (const [keyframeName, keyframeValue] of Object.entries(
1745
+ tailwindConfig.theme.extend.keyframes
1746
+ )) {
1747
+ if (typeof keyframeName !== "string") {
1748
+ continue;
1749
+ }
1750
+ const parsedKeyframeValue = keyframeValueSchema.safeParse(keyframeValue);
1751
+ if (!parsedKeyframeValue.success) {
1752
+ continue;
1753
+ }
1754
+ if (existingKeyFrameNodes?.find(
1755
+ (node) => node.type === "atrule" && node.name === "keyframes" && node.params === keyframeName
1756
+ )) {
1757
+ continue;
1758
+ }
1759
+ const keyframeNode = postcss.atRule({
1760
+ name: "keyframes",
1761
+ params: keyframeName,
1762
+ nodes: [],
1763
+ raws: { semicolon: true, between: " ", before: "\n " }
1764
+ });
1765
+ for (const [key, values] of Object.entries(parsedKeyframeValue.data)) {
1766
+ const rule = postcss.rule({
1767
+ selector: key,
1768
+ nodes: Object.entries(values).map(
1769
+ ([key2, value]) => postcss.decl({
1770
+ prop: key2,
1771
+ value,
1772
+ raws: { semicolon: true, before: "\n ", between: ": " }
1773
+ })
1774
+ ),
1775
+ raws: { semicolon: true, between: " ", before: "\n " }
1776
+ });
1777
+ keyframeNode.append(rule);
1778
+ }
1779
+ themeNode.append(keyframeNode);
1780
+ themeNode.insertBefore(
1781
+ keyframeNode,
1782
+ postcss.comment({ text: "---break---" })
1783
+ );
1784
+ }
1785
+ }
1786
+ };
1787
+ }
1788
+ function updateTailwindConfigAnimationPlugin(tailwindConfig) {
1789
+ return {
1790
+ postcssPlugin: "update-tailwind-config-animation",
1791
+ Once(root) {
1792
+ if (!tailwindConfig?.theme?.extend?.animation) {
1793
+ return;
1794
+ }
1795
+ const themeNode = upsertThemeNode(root);
1796
+ const existingAnimationNodes = themeNode.nodes?.filter(
1797
+ (node) => node.type === "decl" && node.prop.startsWith("--animate-")
1798
+ );
1799
+ const parsedAnimationValue = z5.record(z5.string(), z5.string()).safeParse(tailwindConfig.theme.extend.animation);
1800
+ if (!parsedAnimationValue.success) {
1801
+ return;
1802
+ }
1803
+ for (const [key, value] of Object.entries(parsedAnimationValue.data)) {
1804
+ const prop = `--animate-${key}`;
1805
+ if (existingAnimationNodes?.find(
1806
+ (node) => node.prop === prop
1807
+ )) {
1808
+ continue;
1809
+ }
1810
+ const animationNode = postcss.decl({
1811
+ prop,
1812
+ value,
1813
+ raws: { semicolon: true, between: ": ", before: "\n " }
1814
+ });
1815
+ themeNode.append(animationNode);
1816
+ }
1817
+ }
1818
+ };
1819
+ }
1820
+ function getQuoteType(root) {
1821
+ const firstNode = root.nodes[0];
1822
+ const raw = firstNode.toString();
1823
+ if (raw.includes("'")) {
1824
+ return "single";
1825
+ }
1826
+ return "double";
1827
+ }
1828
+ function isLocalHSLValue(value) {
1829
+ if (value.startsWith("hsl") || value.startsWith("rgb") || value.startsWith("#") || value.startsWith("oklch")) {
1830
+ return false;
1831
+ }
1832
+ const chunks = value.split(" ");
1833
+ return chunks.length === 3 && chunks.slice(1, 3).every((chunk) => chunk.includes("%"));
1834
+ }
1835
+ function isColorValue(value) {
1836
+ return value.startsWith("hsl") || value.startsWith("rgb") || value.startsWith("#") || value.startsWith("oklch");
1837
+ }
1433
1838
 
1434
1839
  // src/utils/updaters/update-dependencies.ts
1435
1840
  import { addDependency } from "nypm";
@@ -1836,7 +2241,7 @@ async function updateFiles(files, config, options) {
1836
2241
  if (!existingFolder) {
1837
2242
  folderSkipped.set(folderName, false);
1838
2243
  }
1839
- if (!folderSkipped.has(folderName)) {
2244
+ if (!folderSkipped.has(folderName) && !options.overwrite) {
1840
2245
  filesCreatedSpinner.stop();
1841
2246
  const { overwrite } = await prompts({
1842
2247
  type: "confirm",
@@ -1939,6 +2344,9 @@ async function addComponents(components, config, options) {
1939
2344
  isNewProject: false,
1940
2345
  ...options
1941
2346
  };
2347
+ return await addProjectComponents(components, config, options);
2348
+ }
2349
+ async function addProjectComponents(components, config, options) {
1942
2350
  const registrySpinner = spinner(`Checking registry.`, {
1943
2351
  silent: options.silent
1944
2352
  })?.start();
@@ -1948,12 +2356,16 @@ async function addComponents(components, config, options) {
1948
2356
  return handleError(new Error("Failed to fetch components from registry."));
1949
2357
  }
1950
2358
  registrySpinner?.succeed();
2359
+ const tailwindVersion = await getProjectTailwindVersionFromConfig(config);
1951
2360
  await updateTailwindConfig(tree.tailwind?.config, config, {
1952
- silent: options.silent
2361
+ silent: options.silent,
2362
+ tailwindVersion
1953
2363
  });
1954
2364
  await updateCssVars(tree.cssVars, config, {
1955
2365
  cleanupDefaultNextStyles: options.isNewProject,
1956
- silent: options.silent
2366
+ silent: options.silent,
2367
+ tailwindVersion,
2368
+ tailwindConfig: tree.tailwind?.config
1957
2369
  });
1958
2370
  await updateDependencies(tree.dependencies, config, {
1959
2371
  silent: options.silent
@@ -2040,16 +2452,30 @@ async function addTailwindConfigContent(configObject, content) {
2040
2452
  import { Command } from "commander";
2041
2453
  import path10 from "pathe";
2042
2454
  import prompts2 from "prompts";
2043
- import { z as z5 } from "zod";
2044
- var initOptionsSchema = z5.object({
2045
- cwd: z5.string(),
2046
- components: z5.array(z5.string()).optional(),
2047
- yes: z5.boolean(),
2048
- defaults: z5.boolean(),
2049
- force: z5.boolean(),
2050
- silent: z5.boolean(),
2051
- isNewProject: z5.boolean(),
2052
- srcDir: z5.boolean().optional()
2455
+ import { z as z6 } from "zod";
2456
+ var initOptionsSchema = z6.object({
2457
+ cwd: z6.string(),
2458
+ components: z6.array(z6.string()).optional(),
2459
+ yes: z6.boolean(),
2460
+ defaults: z6.boolean(),
2461
+ force: z6.boolean(),
2462
+ silent: z6.boolean(),
2463
+ isNewProject: z6.boolean(),
2464
+ srcDir: z6.boolean().optional(),
2465
+ cssVariables: z6.boolean(),
2466
+ baseColor: z6.string().optional().refine(
2467
+ (val) => {
2468
+ if (val) {
2469
+ return BASE_COLORS.find((color) => color.name === val);
2470
+ }
2471
+ return true;
2472
+ },
2473
+ {
2474
+ message: `Invalid base color. Please use '${BASE_COLORS.map(
2475
+ (color) => color.name
2476
+ ).join("', '")}'`
2477
+ }
2478
+ )
2053
2479
  });
2054
2480
  var init = new Command().name("init").description("initialize your project and install dependencies").argument(
2055
2481
  "[components...]",
@@ -2058,11 +2484,7 @@ var init = new Command().name("init").description("initialize your project and i
2058
2484
  "-c, --cwd <cwd>",
2059
2485
  "the working directory. defaults to the current directory.",
2060
2486
  process.cwd()
2061
- ).option("-s, --silent", "mute output.", false).option(
2062
- "--src-dir",
2063
- "use the src directory when creating a new project.",
2064
- false
2065
- ).action(async (components, opts) => {
2487
+ ).option("-s, --silent", "mute output.", false).option("--css-variables", "use css variables for theming.", true).option("--no-css-variables", "do not use css variables for theming.").action(async (components, opts) => {
2066
2488
  try {
2067
2489
  const options = initOptionsSchema.parse({
2068
2490
  cwd: path10.resolve(opts.cwd),
@@ -2241,13 +2663,14 @@ async function promptForMinimalConfig(defaultConfig, opts) {
2241
2663
  let baseColor = defaultConfig.tailwind.baseColor;
2242
2664
  let cssVariables = defaultConfig.tailwind.cssVariables;
2243
2665
  if (!opts.defaults) {
2244
- const [styles, baseColors] = await Promise.all([
2666
+ const [styles, baseColors, tailwindVersion] = await Promise.all([
2245
2667
  getRegistryStyles(),
2246
- getRegistryBaseColors()
2668
+ getRegistryBaseColors(),
2669
+ getProjectTailwindVersionFromConfig(defaultConfig)
2247
2670
  ]);
2248
2671
  const options = await prompts2([
2249
2672
  {
2250
- type: "select",
2673
+ type: tailwindVersion === "v4" ? null : "select",
2251
2674
  name: "style",
2252
2675
  message: `Which ${highlighter.info("style")} would you like to use?`,
2253
2676
  choices: styles.map((style2) => ({
@@ -2257,7 +2680,7 @@ async function promptForMinimalConfig(defaultConfig, opts) {
2257
2680
  initial: 0
2258
2681
  },
2259
2682
  {
2260
- type: "select",
2683
+ type: opts.baseColor ? null : "select",
2261
2684
  name: "tailwindBaseColor",
2262
2685
  message: `Which color would you like to use as the ${highlighter.info(
2263
2686
  "base color"
@@ -2266,21 +2689,11 @@ async function promptForMinimalConfig(defaultConfig, opts) {
2266
2689
  title: color.label,
2267
2690
  value: color.name
2268
2691
  }))
2269
- },
2270
- {
2271
- type: "toggle",
2272
- name: "tailwindCssVariables",
2273
- message: `Would you like to use ${highlighter.info(
2274
- "CSS variables"
2275
- )} for theming?`,
2276
- initial: defaultConfig?.tailwind.cssVariables,
2277
- active: "yes",
2278
- inactive: "no"
2279
2692
  }
2280
2693
  ]);
2281
- style = options.style;
2282
- baseColor = options.tailwindBaseColor;
2283
- cssVariables = options.tailwindCssVariables;
2694
+ style = options.style ?? "new-york";
2695
+ baseColor = options.tailwindBaseColor ?? baseColor;
2696
+ cssVariables = opts.cssVariables;
2284
2697
  }
2285
2698
  return rawConfigSchema.parse({
2286
2699
  $schema: defaultConfig?.$schema,
@@ -2347,16 +2760,29 @@ Before you can add components, you must create a valid ${highlighter.info(
2347
2760
  import { Command as Command2 } from "commander";
2348
2761
  import path12 from "pathe";
2349
2762
  import prompts3 from "prompts";
2350
- import { z as z6 } from "zod";
2351
- var addOptionsSchema = z6.object({
2352
- components: z6.array(z6.string()).optional(),
2353
- yes: z6.boolean(),
2354
- overwrite: z6.boolean(),
2355
- cwd: z6.string(),
2356
- all: z6.boolean(),
2357
- path: z6.string().optional(),
2358
- silent: z6.boolean(),
2359
- srcDir: z6.boolean().optional()
2763
+ import { z as z7 } from "zod";
2764
+ var DEPRECATED_COMPONENTS = [
2765
+ {
2766
+ name: "toast",
2767
+ deprecatedBy: "sonner",
2768
+ message: "The toast component is deprecated. Use the sonner component instead."
2769
+ },
2770
+ {
2771
+ name: "toaster",
2772
+ deprecatedBy: "sonner",
2773
+ message: "The toaster component is deprecated. Use the sonner component instead."
2774
+ }
2775
+ ];
2776
+ var addOptionsSchema = z7.object({
2777
+ components: z7.array(z7.string()).optional(),
2778
+ yes: z7.boolean(),
2779
+ overwrite: z7.boolean(),
2780
+ cwd: z7.string(),
2781
+ all: z7.boolean(),
2782
+ path: z7.string().optional(),
2783
+ silent: z7.boolean(),
2784
+ srcDir: z7.boolean().optional(),
2785
+ cssVariables: z7.boolean()
2360
2786
  });
2361
2787
  var add = new Command2().name("add").description("add a component to your project").argument(
2362
2788
  "[components...]",
@@ -2369,7 +2795,7 @@ var add = new Command2().name("add").description("add a component to your projec
2369
2795
  "--src-dir",
2370
2796
  "use the src directory when creating a new project.",
2371
2797
  false
2372
- ).action(async (components, opts) => {
2798
+ ).option("--css-variables", "use css variables for theming.", true).option("--no-css-variables", "do not use css variables for theming.").action(async (components, opts) => {
2373
2799
  try {
2374
2800
  const options = addOptionsSchema.parse({
2375
2801
  components,
@@ -2398,6 +2824,20 @@ var add = new Command2().name("add").description("add a component to your projec
2398
2824
  if (!options.components?.length) {
2399
2825
  options.components = await promptForRegistryComponents(options);
2400
2826
  }
2827
+ const projectInfo = await getProjectInfo(options.cwd);
2828
+ if (projectInfo?.tailwindVersion === "v4") {
2829
+ const deprecatedComponents = DEPRECATED_COMPONENTS.filter(
2830
+ (component) => options.components?.includes(component.name)
2831
+ );
2832
+ if (deprecatedComponents?.length) {
2833
+ logger.break();
2834
+ deprecatedComponents.forEach((component) => {
2835
+ logger.warn(highlighter.warn(component.message));
2836
+ });
2837
+ logger.break();
2838
+ process.exit(1);
2839
+ }
2840
+ }
2401
2841
  let { errors, config } = await preFlightAdd(options);
2402
2842
  if (errors[MISSING_CONFIG]) {
2403
2843
  const { proceed } = await prompts3({
@@ -2420,7 +2860,8 @@ var add = new Command2().name("add").description("add a component to your projec
2420
2860
  skipPreflight: false,
2421
2861
  silent: true,
2422
2862
  isNewProject: false,
2423
- srcDir: options.srcDir
2863
+ srcDir: options.srcDir,
2864
+ cssVariables: options.cssVariables
2424
2865
  });
2425
2866
  }
2426
2867
  if (!config) {
@@ -2442,7 +2883,9 @@ async function promptForRegistryComponents(options) {
2442
2883
  return [];
2443
2884
  }
2444
2885
  if (options.all) {
2445
- return registryIndex.map((entry) => entry.name);
2886
+ return registryIndex.map((entry) => entry.name).filter(
2887
+ (component) => !DEPRECATED_COMPONENTS.some((c) => c.name === component)
2888
+ );
2446
2889
  }
2447
2890
  if (options.components?.length) {
2448
2891
  return options.components;
@@ -2453,7 +2896,11 @@ async function promptForRegistryComponents(options) {
2453
2896
  message: "Which components would you like to add?",
2454
2897
  hint: "Space to select. A to toggle all. Enter to submit.",
2455
2898
  instructions: false,
2456
- choices: registryIndex.filter((entry) => entry.type === "registry:ui").map((entry) => ({
2899
+ choices: registryIndex.filter(
2900
+ (entry) => entry.type === "registry:ui" && !DEPRECATED_COMPONENTS.some(
2901
+ (component) => component.name === entry.name
2902
+ )
2903
+ ).map((entry) => ({
2457
2904
  title: entry.name,
2458
2905
  value: entry.name,
2459
2906
  selected: options.all ? true : options.components?.includes(entry.name)
@@ -2464,7 +2911,7 @@ async function promptForRegistryComponents(options) {
2464
2911
  logger.info("");
2465
2912
  process.exit(1);
2466
2913
  }
2467
- const result = z6.array(z6.string()).safeParse(components);
2914
+ const result = z7.array(z7.string()).safeParse(components);
2468
2915
  if (!result.success) {
2469
2916
  logger.error("");
2470
2917
  handleError(new Error("Something went wrong. Please try again."));
@@ -2478,12 +2925,12 @@ import { existsSync as existsSync2, promises as fs10 } from "node:fs";
2478
2925
  import { Command as Command3 } from "commander";
2479
2926
  import { diffLines } from "diff";
2480
2927
  import path13 from "pathe";
2481
- import { z as z7 } from "zod";
2482
- var updateOptionsSchema = z7.object({
2483
- component: z7.string().optional(),
2484
- yes: z7.boolean(),
2485
- cwd: z7.string(),
2486
- path: z7.string().optional()
2928
+ import { z as z8 } from "zod";
2929
+ var updateOptionsSchema = z8.object({
2930
+ component: z8.string().optional(),
2931
+ yes: z8.boolean(),
2932
+ cwd: z8.string(),
2933
+ path: z8.string().optional()
2487
2934
  });
2488
2935
  var diff = new Command3().name("diff").description("check for updates against the registry").argument("[component]", "the component name").option("-y, --yes", "skip confirmation prompt.", false).option(
2489
2936
  "-c, --cwd <cwd>",
@@ -2843,17 +3290,17 @@ Before you can run a migration, you must create a valid ${highlighter.info(
2843
3290
  import { Command as Command5 } from "commander";
2844
3291
  import consola4 from "consola";
2845
3292
  import path16 from "pathe";
2846
- import { z as z8 } from "zod";
3293
+ import { z as z9 } from "zod";
2847
3294
  var migrations = [
2848
3295
  {
2849
3296
  name: "icons",
2850
3297
  description: "migrate your ui components to a different icon library."
2851
3298
  }
2852
3299
  ];
2853
- var migrateOptionsSchema = z8.object({
2854
- cwd: z8.string(),
2855
- list: z8.boolean(),
2856
- migration: z8.string().refine(
3300
+ var migrateOptionsSchema = z9.object({
3301
+ cwd: z9.string(),
3302
+ list: z9.boolean(),
3303
+ migration: z9.string().refine(
2857
3304
  (value) => value && migrations.some((migration) => migration.name === value),
2858
3305
  {
2859
3306
  message: "You must specify a valid migration. Run `shadcn migrate --list` to see available migrations."
@@ -2909,7 +3356,7 @@ import { Command as Command6 } from "commander";
2909
3356
  var package_default = {
2910
3357
  name: "shadcn-vue",
2911
3358
  type: "module",
2912
- version: "1.0.3",
3359
+ version: "2.0.0",
2913
3360
  description: "Add components to your apps.",
2914
3361
  publishConfig: {
2915
3362
  access: "public"