@unocss/nuxt
Version:
Nuxt module for UnoCSS
173 lines (167 loc) • 6.19 kB
JavaScript
import { dirname, resolve } from 'node:path';
import process from 'node:process';
import { fileURLToPath } from 'node:url';
import { defineNuxtModule, addPluginTemplate, isNuxt2, addComponentsDir, addTemplate, findPath, isNuxt3, extendViteConfig, extendWebpackConfig } from '@nuxt/kit';
import { createRecoveryConfigLoader } from '@unocss/config';
import { cssIdRE } from '@unocss/core';
import presetAttributify from '@unocss/preset-attributify';
import presetIcons from '@unocss/preset-icons';
import presetTagify from '@unocss/preset-tagify';
import presetTypography from '@unocss/preset-typography';
import presetWebFonts from '@unocss/preset-web-fonts';
import presetWind3 from '@unocss/preset-wind3';
import presetWind4 from '@unocss/preset-wind4';
const defaultPipelineExclude = [cssIdRE];
async function resolveOptions(options) {
if (options.wind3 && options.wind4) {
console.warn("[unocss/nuxt]: wind3 and wind4 presets are mutually exclusive. wind3 will be disabled in favor of wind4.");
options.wind3 = false;
}
if (options.presets == null) {
options.presets = [];
const presetMap = {
wind3: presetWind3,
wind4: presetWind4,
attributify: presetAttributify,
icons: presetIcons,
webFonts: presetWebFonts,
typography: presetTypography,
tagify: presetTagify
};
for (const [key, preset] of Object.entries(presetMap)) {
const option = options[key];
if (option) {
options.presets.push(preset(typeof option === "boolean" ? {} : option));
}
}
}
options.content ??= {};
options.content.pipeline ??= {};
if (options.content.pipeline !== false) {
options.content.pipeline.exclude ??= defaultPipelineExclude;
if (Array.isArray(options.content.pipeline.exclude))
options.content.pipeline.exclude.push(/\?macro=true/);
}
}
const dir = dirname(fileURLToPath(import.meta.url));
const index = defineNuxtModule({
meta: {
name: "unocss",
configKey: "unocss"
},
defaults: {
mode: "global",
autoImport: true,
preflight: false,
components: true,
disableNuxtInlineStyle: true,
nuxtLayers: false,
// presets
attributify: false,
webFonts: false,
icons: false,
wind3: true,
wind4: false
},
async setup(options, nuxt) {
resolveOptions(options);
const loadConfig = createRecoveryConfigLoader();
options.mode ??= "global";
const InjectModes = ["global", "dist-chunk"];
if (options.disableNuxtInlineStyle) {
nuxt.options.features ||= {};
nuxt.options.features.inlineStyles = false;
}
if (options.injectPosition != null)
console.warn("[unocss/nuxt] options `injectPosition` is temporary removed due to the incompatibility with Nuxt 3.9. We are seeking for better solution. It's not effective at this moment.");
if (options.autoImport) {
addPluginTemplate({
filename: "unocss.mjs",
getContents: () => {
const lines = [
InjectModes.includes(options.mode) ? "import 'uno.css'" : "",
isNuxt2() ? "export default () => {}" : "import { defineNuxtPlugin } from '#imports'; export default defineNuxtPlugin(() => {})"
];
if (options.preflight)
lines.unshift("import '@unocss/reset/tailwind.css'");
return lines.join("\n");
}
});
}
if (options.components) {
addComponentsDir({
path: resolve(dir, "../runtime"),
watch: false
});
}
if (options.nuxtLayers) {
addTemplate({
filename: "uno.config.mjs",
async getContents() {
const configPaths = (await Promise.all(nuxt.options._layers.slice(1).map(
(layer) => findPath(options.configFile || ["uno.config", "unocss.config"], { cwd: layer.config.rootDir })
))).filter(Boolean).reverse();
return `import { mergeConfigs } from '@unocss/core'
${configPaths.map((path, index) => `import cfg${index} from '${path}'`.trimStart()).join("\n").trimEnd()}
export default mergeConfigs([${configPaths.map((_, index) => `cfg${index}`).join(", ")}])
`;
},
write: true
});
}
nuxt.hook("build:before", async () => {
const { config: unoConfig } = await loadConfig(
process.cwd(),
{ configFile: options.configFile },
[],
options
);
if (isNuxt3() && nuxt.options.builder === "@nuxt/vite-builder" && nuxt.options.postcss.plugins.cssnano && unoConfig.transformers?.some((t) => t.name === "@unocss/transformer-directives" && t.enforce !== "pre")) {
const preset = nuxt.options.postcss.plugins.cssnano.preset;
nuxt.options.postcss.plugins.cssnano = {
preset: [preset?.[0] || "default", Object.assign(
// Following optimizations result in invalid CSS if the directives not transformed yet
{ mergeRules: false, normalizeWhitespace: false, discardComments: false },
preset?.[1]
)]
};
}
await nuxt.callHook("unocss:config", unoConfig);
extendViteConfig(async (config) => {
const { default: VitePlugin } = await import('@unocss/vite');
config.plugins = config.plugins || [];
config.plugins.unshift(...VitePlugin({
mode: options.mode
}, unoConfig));
});
extendWebpackConfig(async (config) => {
const { default: WebpackPlugin } = await import('@unocss/webpack');
config.plugins = config.plugins || [];
config.plugins.unshift(WebpackPlugin({}, unoConfig));
});
});
if (nuxt.options.dev) {
nuxt.hook("devtools:customTabs", (tabs) => {
tabs.push({
title: "UnoCSS",
name: "unocss",
icon: "/__unocss/favicon.svg",
view: {
type: "iframe",
src: "/__unocss/"
}
});
});
}
if (isNuxt2()) {
nuxt.hook("app:resolve", (config) => {
const plugin = { src: "unocss.mjs", mode: "client" };
if (config.plugins)
config.plugins.push(plugin);
else
config.plugins = [plugin];
});
}
}
});
export { index as default };