shadcn-vue
Version:
Add components to your apps.
660 lines (659 loc) • 17 kB
JavaScript
//#region src/registry/constants.ts
const SHADCN_VUE_URL = process.env.SHADCN_VUE_URL ?? "https://shadcn-vue.com";
const REGISTRY_URL = process.env.REGISTRY_URL ?? `${SHADCN_VUE_URL}/r`;
const FALLBACK_STYLE = "new-york-v4";
const BASES = [{
name: "reka",
label: "Reka UI",
description: "Optimized for fast development, easy maintenance, and accessibility.",
dependencies: ["reka-ui"]
}];
const STYLES = [
{
name: "vega",
label: "Vega",
description: "The classic shadcn/ui look. Clean, neutral, and familiar."
},
{
name: "nova",
label: "Nova",
description: "Reduced padding and margins for compact layouts."
},
{
name: "maia",
label: "Maia",
description: "Soft and rounded, with generous spacing."
},
{
name: "lyra",
label: "Lyra",
description: "Boxy and sharp. Pairs well with mono fonts."
},
{
name: "mira",
label: "Mira",
description: "Compact. Made for dense interfaces."
},
{
name: "luma",
label: "Luma",
description: "Fluid, luminous, and glassy."
},
{
name: "sera",
label: "Sera",
description: "Editorial and typographic."
}
];
const FONTS = [
{
name: "inter",
label: "Inter",
family: "Inter",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "geist-sans",
label: "Geist",
family: "Geist",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "noto-sans",
label: "Noto Sans",
family: "Noto Sans",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Noto+Sans:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "nunito-sans",
label: "Nunito Sans",
family: "Nunito Sans",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "figtree",
label: "Figtree",
family: "Figtree",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Figtree:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "roboto",
label: "Roboto",
family: "Roboto",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "raleway",
label: "Raleway",
family: "Raleway",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "dm-sans",
label: "DM Sans",
family: "DM Sans",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "public-sans",
label: "Public Sans",
family: "Public Sans",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Public+Sans:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "outfit",
label: "Outfit",
family: "Outfit",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "jetbrains-mono",
label: "JetBrains Mono",
family: "JetBrains Mono",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap');",
variable: "--font-mono",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "playfair-display",
label: "Playfair Display",
family: "Playfair Display",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700;800;900&display=swap');",
variable: "--font-serif",
weight: [
"400",
"500",
"600",
"700",
"800",
"900"
]
},
{
name: "oxanium",
label: "Oxanium",
family: "Oxanium",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Oxanium:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "manrope",
label: "Manrope",
family: "Manrope",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "space-grotesk",
label: "Space Grotesk",
family: "Space Grotesk",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "montserrat",
label: "Montserrat",
family: "Montserrat",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "ibm-plex-sans",
label: "IBM Plex Sans",
family: "IBM Plex Sans",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "source-sans-3",
label: "Source Sans 3",
family: "Source Sans 3",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Source+Sans+3:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "instrument-sans",
label: "Instrument Sans",
family: "Instrument Sans",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600;700&display=swap');",
variable: "--font-sans",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "geist-mono",
label: "Geist Mono",
family: "Geist Mono",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500;600;700&display=swap');",
variable: "--font-mono",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "noto-serif",
label: "Noto Serif",
family: "Noto Serif",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Noto+Serif:wght@400;500;600;700&display=swap');",
variable: "--font-serif",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "roboto-slab",
label: "Roboto Slab",
family: "Roboto Slab",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@400;500;600;700&display=swap');",
variable: "--font-serif",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "merriweather",
label: "Merriweather",
family: "Merriweather",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Merriweather:wght@400;500;600;700&display=swap');",
variable: "--font-serif",
weight: [
"400",
"500",
"600",
"700"
]
},
{
name: "lora",
label: "Lora",
family: "Lora",
provider: "google",
import: "@import url('https://fonts.googleapis.com/css2?family=Lora:wght@400;500;600;700&display=swap');",
variable: "--font-serif",
weight: [
"400",
"500",
"600",
"700"
]
}
];
const ICON_LIBRARIES$1 = [
{
name: "lucide",
label: "Lucide",
packages: ["@lucide/vue"]
},
{
name: "tabler",
label: "Tabler Icons",
packages: ["@tabler/icons-vue"]
},
{
name: "hugeicons",
label: "HugeIcons",
packages: ["@hugeicons/vue", "@hugeicons/core-free-icons"]
},
{
name: "phosphor",
label: "Phosphor Icons",
packages: ["@phosphor-icons/vue"]
},
{
name: "remixicon",
label: "Remix Icon",
packages: ["@remixicon/vue"]
}
];
const BASE_COLORS = [
{
name: "neutral",
label: "Neutral"
},
{
name: "stone",
label: "Stone"
},
{
name: "zinc",
label: "Zinc"
},
{
name: "mauve",
label: "Mauve"
},
{
name: "olive",
label: "Olive"
},
{
name: "mist",
label: "Mist"
},
{
name: "taupe",
label: "Taupe"
}
];
const BUILTIN_REGISTRIES = { "@shadcn": `${REGISTRY_URL}/styles/{style}/{name}.json` };
const DEPRECATED_COMPONENTS = [{
name: "toast",
deprecatedBy: "sonner",
message: "The toast component is deprecated. Use the sonner component instead."
}, {
name: "toaster",
deprecatedBy: "sonner",
message: "The toaster component is deprecated. Use the sonner component instead."
}];
//#endregion
//#region src/utils/icon-libraries.ts
const LEGACY_ICON_LIBRARY_PACKAGES = ["lucide-vue-next"];
/**
* Get an icon library by name.
*/
function getIconLibrary(name) {
return ICON_LIBRARIES$1.find((lib) => lib.name === name);
}
/**
* Get packages for an icon library.
*/
function getIconLibraryPackages(name) {
const lib = getIconLibrary(name);
return lib?.packages ? [...lib.packages] : [];
}
/**
* Set of every package name owned by any supported icon library (including
* legacy/renamed ones). Used to scrub icon-library deps from registry-supplied
* dependency lists so we can replace them with the user's chosen library.
*/
function getAllIconLibraryPackages() {
const all = new Set(LEGACY_ICON_LIBRARY_PACKAGES);
for (const lib of ICON_LIBRARIES$1) for (const pkg of lib.packages) all.add(pkg);
return all;
}
/**
* Registry style items (e.g. `styles/<style>/index.json`) hardcode `@lucide/vue`
* in their `dependencies` array regardless of which icon library the user
* picked. This swaps any known icon-library package in `dependencies` for the
* packages of `iconLibrary`, so a user who picked Phosphor doesn't end up with
* Lucide installed.
*
* Returns a new array; the input is not mutated. Falls back to the input
* unchanged when `iconLibrary` is unknown so we never silently strip deps.
*/
function replaceIconLibraryInDependencies(dependencies, iconLibrary) {
if (!dependencies?.length) return dependencies ? [...dependencies] : [];
if (!iconLibrary) return [...dependencies];
const chosenPackages = getIconLibraryPackages(iconLibrary);
if (chosenPackages.length === 0) return [...dependencies];
const knownIconPackages = getAllIconLibraryPackages();
const filtered = dependencies.filter((dep) => !knownIconPackages.has(dep));
for (const pkg of chosenPackages) if (!filtered.includes(pkg)) filtered.push(pkg);
return filtered;
}
const ICON_LIBRARIES = {
lucide: {
name: "@lucide/vue",
package: "@lucide/vue",
import: "@lucide/vue"
},
radix: {
name: "@radix-icons/vue",
package: "@radix-icons/vue",
import: "@radix-icons/vue"
},
tabler: {
name: "@tabler/icons-vue",
package: "@tabler/icons-vue",
import: "@tabler/icons-vue"
},
phosphor: {
name: "@phosphor-icons/vue",
package: "@phosphor-icons/vue",
import: "@phosphor-icons/vue"
},
hugeicons: {
name: "@hugeicons/vue",
package: "@hugeicons/vue",
import: "@hugeicons/vue"
},
remixicon: {
name: "@remixicon/vue",
package: "@remixicon/vue",
import: "@remixicon/vue"
}
};
//#endregion
//#region src/utils/transformers/transform-icons.ts
const SOURCE_LIBRARY = "lucide";
const ICON_LIBRARY_IMPORTS = Object.values(ICON_LIBRARIES).map((l) => l.import).filter(Boolean);
function transformIcons(opts, registryIcons) {
return {
type: "codemod",
name: "modify import of icon library on user config",
transform({ scriptASTs, sfcAST, utils: { traverseScriptAST, traverseTemplateAST } }) {
let transformCount = 0;
const { config } = opts;
if (!config.iconLibrary || !(config.iconLibrary in ICON_LIBRARIES)) return transformCount;
const sourceLibrary = SOURCE_LIBRARY;
const targetLibrary = config.iconLibrary;
if (sourceLibrary === targetLibrary) return transformCount;
const targetedIconsMap = /* @__PURE__ */ new Map();
for (const scriptAST of scriptASTs) traverseScriptAST(scriptAST, { visitImportDeclaration(path) {
const source = String(path.node.source.value);
if (!ICON_LIBRARY_IMPORTS.some((prefix) => source.startsWith(prefix))) return this.traverse(path);
let hasChanges = false;
for (const specifier of path.node.specifiers ?? []) if (specifier.type === "ImportSpecifier") {
const iconName = specifier.imported.name;
if (typeof iconName !== "string") continue;
const targetedIcon = registryIcons[iconName]?.[targetLibrary] ?? registryIcons[iconName.replace(/Icon$/, "")]?.[targetLibrary];
if (!targetedIcon || targetedIconsMap.has(iconName)) continue;
targetedIconsMap.set(iconName, targetedIcon);
specifier.imported.name = targetedIcon;
hasChanges = true;
}
if (hasChanges) {
path.node.source.value = ICON_LIBRARIES[targetLibrary].import;
transformCount++;
}
return this.traverse(path);
} });
if (sfcAST && targetedIconsMap.size > 0) traverseTemplateAST(sfcAST, { enterNode(node) {
if (node.type === "VElement" && targetedIconsMap.has(node.rawName)) {
node.rawName = targetedIconsMap.get(node.rawName) ?? "";
transformCount++;
}
} });
return transformCount;
}
};
}
//#endregion
//#region src/utils/transformers/transform-menu.ts
function transformMenu(opts) {
return {
type: "codemod",
name: "transform menu color classes",
transform({ scriptASTs, sfcAST, utils: { traverseScriptAST, traverseTemplateAST } }) {
let transformCount = 0;
const { config } = opts;
const replacement = config.menuColor === "inverted" ? "dark" : "";
const processClassString = (classString) => {
if (!classString.includes("cn-menu-target")) return classString;
let newText = classString.replace(/cn-menu-target/g, replacement);
if (!replacement) {
newText = newText.replace(/\s{2,}/g, " ");
newText = newText.trim();
}
return newText;
};
for (const scriptAST of scriptASTs) traverseScriptAST(scriptAST, {
visitLiteral(path) {
if (path.parent.value.type === "ImportDeclaration") return this.traverse(path);
if (typeof path.node.value === "string" && path.node.value.includes("cn-menu-target")) {
const original = path.node.value;
const processed = processClassString(original);
if (processed !== original) {
path.node.value = processed;
transformCount++;
}
}
return this.traverse(path);
},
visitJSXAttribute(path) {
if (path.node.name?.name !== "className") return this.traverse(path);
const value = path.node.value;
if (!value) return this.traverse(path);
if (value.type === "Literal" && typeof value.value === "string") {
if (value.value.includes("cn-menu-target")) {
const original = value.value;
const processed = processClassString(original);
if (processed !== original) {
value.value = processed;
transformCount++;
}
}
}
return this.traverse(path);
}
});
if (sfcAST) traverseTemplateAST(sfcAST, {
enterNode(node) {
if (node.type === "VLiteral" && typeof node.value === "string") {
if (node.parent?.type === "VAttribute" && node.parent.key?.type === "VIdentifier" && ["class", "className"].includes(node.parent.key.name)) {
if (node.value.includes("cn-menu-target")) {
const cleanValue = node.value.replace(/"/g, "");
const processed = processClassString(cleanValue);
if (processed !== cleanValue) {
node.value = processed;
transformCount++;
}
}
}
} else if (node.type === "Literal" && typeof node.value === "string") {
if (node.value.includes("cn-menu-target")) {
const original = node.value;
const processed = processClassString(original);
if (processed !== original) {
node.value = processed;
transformCount++;
}
}
}
},
leaveNode() {}
});
return transformCount;
}
};
}
//#endregion
export { BASES as a, DEPRECATED_COMPONENTS as c, ICON_LIBRARIES$1 as d, REGISTRY_URL as f, replaceIconLibraryInDependencies as i, FALLBACK_STYLE as l, STYLES as m, transformIcons as n, BASE_COLORS as o, SHADCN_VUE_URL as p, ICON_LIBRARIES as r, BUILTIN_REGISTRIES as s, transformMenu as t, FONTS as u };
//# sourceMappingURL=transform-menu-CLNZ5vUh.js.map