UNPKG

vuetify-nuxt-module

Version:
1,357 lines (1,335 loc) 55 kB
import { addVitePlugin, addPluginTemplate, getNuxtVersion, extendWebpackConfig, addImports, addPlugin, useLogger, defineNuxtModule, isNuxt2, createResolver, hasNuxtModule } from '@nuxt/kit'; import { isPackageExists, getPackageInfo } from 'local-pkg'; import { createFilter, version as version$1 } from 'vite'; import { resolve, dirname, relative } from 'node:path'; import defu from 'defu'; import { debounce } from 'perfect-debounce'; import fs, { existsSync, statSync } from 'node:fs'; import process from 'node:process'; import { createConfigLoader } from 'unconfig'; import fsp, { readFile } from 'node:fs/promises'; import { resolveVuetifyBase, isObject, normalizePath, generateImports } from '@vuetify/loader-shared'; import { pathToFileURL } from 'node:url'; import { relative as relative$1, isAbsolute } from 'pathe'; import path from 'upath'; import { transformAssetUrls } from 'vite-plugin-vuetify'; import { parseQuery, parseURL } from 'ufo'; import destr from 'destr'; const version = "0.18.6"; const VIRTUAL_VUETIFY_CONFIGURATION = "virtual:vuetify-configuration"; const RESOLVED_VIRTUAL_VUETIFY_CONFIGURATION = `\0${VIRTUAL_VUETIFY_CONFIGURATION}`; const VIRTUAL_VUETIFY_DATE_CONFIGURATION = "virtual:vuetify-date-configuration"; const RESOLVED_VIRTUAL_VUETIFY_DATE_CONFIGURATION = `\0${VIRTUAL_VUETIFY_DATE_CONFIGURATION}`; const VIRTUAL_VUETIFY_ICONS_CONFIGURATION = "virtual:vuetify-icons-configuration"; const RESOLVED_VIRTUAL_VUETIFY_ICONS_CONFIGURATION = `\0${VIRTUAL_VUETIFY_ICONS_CONFIGURATION}`; const VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION = "virtual:vuetify-ssr-client-hints-configuration"; const RESOLVED_VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION = `\0${VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION}`; const RESOLVED_VIRTUAL_MODULES = [ RESOLVED_VIRTUAL_VUETIFY_DATE_CONFIGURATION, RESOLVED_VIRTUAL_VUETIFY_ICONS_CONFIGURATION, RESOLVED_VIRTUAL_VUETIFY_CONFIGURATION, RESOLVED_VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION ]; async function loadVuetifyConfiguration(cwd = process.cwd(), configOrPath = cwd, defaults = {}, extraConfigSources = []) { let inlineConfig = {}; if (typeof configOrPath !== "string") { inlineConfig = configOrPath; configOrPath = process.cwd(); } const resolved = resolve(cwd, configOrPath); let isFile = false; if (existsSync(resolved) && statSync(resolved).isFile()) { isFile = true; cwd = dirname(resolved).replace(/\\/g, "/"); } const rewrite = (config) => { if (typeof config === "function") return config(); return config; }; const loader = createConfigLoader({ sources: isFile ? [ { files: resolved, extensions: [], rewrite } ] : [ { files: [ "vuetify.config" ], // we don't want `package.json` to be loaded extensions: ["mts", "cts", "ts", "mjs", "cjs", "js"], rewrite }, ...extraConfigSources ], cwd, defaults: inlineConfig, merge: false }); const result = await loader.load(); if (result.config?.config === false) result.config = Object.assign(defaults, inlineConfig); else result.config = Object.assign(defaults, result.config || inlineConfig); delete result.config.config; return result; } async function mergeVuetifyModules(options, nuxt) { const moduleOptions = []; const vuetifyConfigurationFilesToWatch = /* @__PURE__ */ new Set(); await nuxt.callHook("vuetify:registerModule", (layerModuleOptions) => moduleOptions.push(layerModuleOptions)); if (nuxt.options._layers.length > 1) { for (let i = 1; i < nuxt.options._layers.length; i++) { const layer = nuxt.options._layers[i]; const resolvedOptions2 = await loadVuetifyConfiguration( layer.config.rootDir, layer.config.vuetify?.vuetifyOptions ); if (resolvedOptions2.sources.length) { resolvedOptions2.sources.map((s) => s.replace(/\\/g, "/")).filter((s) => !s.includes("/node_modules/")).forEach((s) => vuetifyConfigurationFilesToWatch.add(s)); } moduleOptions.push({ moduleOptions: layer.config.vuetify?.moduleOptions, vuetifyOptions: resolvedOptions2.config }); } } const resolvedOptions = await loadVuetifyConfiguration( nuxt.options.rootDir, options.vuetifyOptions ); if (nuxt.options.dev && resolvedOptions.sources.length) { if (nuxt.options.ssr) resolvedOptions.sources.forEach((s) => nuxt.options.watch.push(s.replace(/\\/g, "/"))); else resolvedOptions.sources.forEach((s) => vuetifyConfigurationFilesToWatch.add(s.replace(/\\/g, "/"))); } moduleOptions.unshift({ moduleOptions: options.moduleOptions, vuetifyOptions: resolvedOptions.config }); if (moduleOptions.length > 1) { const [app, ...rest] = moduleOptions; const configuration = defu(app, ...rest); dedupeIcons(configuration, moduleOptions.reverse()); return { configuration, vuetifyConfigurationFilesToWatch }; } else { return { configuration: { moduleOptions: options.moduleOptions, vuetifyOptions: resolvedOptions.config }, vuetifyConfigurationFilesToWatch }; } } function dedupeIcons(configuration, moduleOptions) { const vuetifyOptions = configuration.vuetifyOptions; if (vuetifyOptions.icons) { if (vuetifyOptions.icons.sets) { const sets = /* @__PURE__ */ new Map(); for (const { vuetifyOptions: vuetifyOptions2 } of moduleOptions) { if (vuetifyOptions2.icons && vuetifyOptions2.icons.sets) { const mSets = vuetifyOptions2.icons.sets; if (typeof mSets === "string") { sets.set(mSets, { name: mSets }); } else { for (const set of mSets) { if (typeof set === "string") sets.set(set, { name: set }); else sets.set(set.name, set); } } } } vuetifyOptions.icons.sets = Array.from(sets.values()); } } } function detectDate() { const result = []; [ "date-fns", "moment", "luxon", "dayjs", "js-joda", "date-fns-jalali", "jalaali", "hijri" ].forEach((adapter) => { if (isPackageExists(`@date-io/${adapter}`)) result.push(adapter); }); return result; } function cleanupBlueprint(vuetifyOptions) { const blueprint = vuetifyOptions.blueprint; if (blueprint) { delete blueprint.ssr; delete blueprint.components; delete blueprint.directives; delete blueprint.locale; delete blueprint.date; delete blueprint.icons; vuetifyOptions.blueprint = blueprint; } } function checkVuetifyPlugins(config) { let plugin = config.plugins?.find((p) => p && typeof p === "object" && "name" in p && p.name === "vuetify:import"); if (plugin) throw new Error("Remove vite-plugin-vuetify plugin from Vite Plugins entry in Nuxt config file!"); plugin = config.plugins?.find((p) => p && typeof p === "object" && "name" in p && p.name === "vuetify:styles"); if (plugin) throw new Error("Remove vite-plugin-vuetify plugin from Vite Plugins entry in Nuxt config file!"); } function resolveVuetifyComponents(resolver) { const vuetifyBase = resolveVuetifyBase(); const componentsPromise = importMapResolver(); const labComponentsPromise = importMapLabResolver(); return { vuetifyBase, componentsPromise, labComponentsPromise }; async function importMapResolver() { return JSON.parse(await readFile(resolver.resolve(vuetifyBase, "dist/json/importMap.json"), "utf-8")).components; } async function importMapLabResolver() { return JSON.parse(await readFile(resolver.resolve(vuetifyBase, "dist/json/importMap-labs.json"), "utf-8")).components; } } const cssFonts = ["unocss-mdi", "mdi", "md", "fa", "fa4"]; const iconsPackageNames = { "unocss-mdi": { name: "@mdi/font", css: "" }, "mdi": { name: "@mdi/font", css: "@mdi/font/css/materialdesignicons.css" }, "md": { name: "material-design-icons-iconfont", css: "@mdi/font/css/materialdesignicons.css" }, "fa": { name: "@fortawesome/fontawesome-free", css: "@fortawesome/fontawesome-free/css/all.css" }, "fa4": { name: "font-awesome@4.7.0", css: "font-awesome/css/font-awesome.min.css" } }; const iconsCDN = { "unocss-mdi": "", "mdi": "https://cdn.jsdelivr.net/npm/@mdi/font@5.x/css/materialdesignicons.min.css", "md": "https://fonts.googleapis.com/css?family=Material+Icons", "fa": "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@latest/css/all.min.css", "fa4": "https://cdn.jsdelivr.net/npm/font-awesome@4.x/css/font-awesome.min.css" }; const disabledResolvedIcons = Object.freeze({ enabled: false, unocss: false, unocssAliases: false, unocssIconPrefix: "i-", unocssIcons: {}, unocssAdditionalIcons: {}, imports: [], aliases: [], aliasesImportPresent: false, sets: [], cdn: [], local: [], svg: {} }); function prepareIcons(unocssPresent, logger, vuetifyOptions) { if (vuetifyOptions.icons === false) return disabledResolvedIcons; const icons = vuetifyOptions.icons || {}; let { defaultSet = "mdi", sets } = icons; if (!defaultSet) defaultSet = icons.defaultSet = "mdi"; if (!sets && defaultSet !== "mdi-svg" && defaultSet !== "fa-svg" && defaultSet !== "custom") sets = [{ name: defaultSet || "mdi" }]; sets = sets ? convertFontSetsToObjectNotation(sets) : []; const resolvedIcons = { enabled: true, unocss: unocssPresent && (defaultSet === "unocss-mdi" || sets.some((s) => s.name === "unocss-mdi")), unocssAliases: defaultSet === "unocss-mdi", unocssIconPrefix: icons.unocssIconPrefix ?? "i-", unocssIcons: icons.unocssIcons ?? {}, unocssAdditionalIcons: icons.unocssAdditionalIcons ?? {}, defaultSet, sets: [], aliases: [], aliasesImportPresent: false, imports: [], cdn: [], local: [], svg: { mdi: false } }; if (sets) { if (!unocssPresent && defaultSet === "unocss-mdi") { logger.warn("Configured unocss-mdi as default icon set and @unocss/nuxt is not installed, reverting configuration to use mdi icon set: install @unocss/nuxt module or change the default icon set!"); defaultSet = "mdi"; sets = sets.filter((s) => s.name !== "unocss-mdi"); } sets.filter((s) => cssFonts.includes(s.name)).forEach(({ name, cdn }) => { resolvedIcons.aliasesImportPresent ||= name === defaultSet; if (name === "unocss-mdi") return; resolvedIcons.imports.push(`import {${name === defaultSet ? "aliases," : ""}${name}} from 'vuetify/iconsets/${name}'`); resolvedIcons.sets.push(name); if (isPackageExists(iconsPackageNames[name].name)) resolvedIcons.local.push(iconsPackageNames[name].css); else resolvedIcons.cdn.push([name, cdn ?? iconsCDN[name]]); }); if (resolvedIcons.unocss && defaultSet === "unocss-mdi") { if (!resolvedIcons.sets.includes("mdi")) { resolvedIcons.sets.push("mdi"); resolvedIcons.imports.push("import {mdi} from 'vuetify/iconsets/mdi'"); } resolvedIcons.defaultSet = "mdi"; } } let faSvg = icons.svg?.fa; if (defaultSet === "fa-svg" || faSvg) { if (!faSvg) faSvg = {}; let faSvgExists = isPackageExists("@fortawesome/fontawesome-svg-core"); if (!faSvgExists) logger.warn("Missing @fortawesome/fontawesome-svg-core dependency, install it!"); faSvgExists = isPackageExists("@fortawesome/vue-fontawesome"); if (faSvgExists) { if (!faSvg.libraries?.length) faSvg.libraries = [[false, "fas", "@fortawesome/free-solid-svg-icons"]]; for (const p in faSvg.libraries) { const [_defaultExport, _name, library] = faSvg.libraries[p]; if (!isPackageExists(library)) { faSvgExists = false; logger.warn(`Missing library ${library} dependency, install it!`); } } } else { logger.warn("Missing @fortawesome/vue-fontawesome dependency, install it!"); } if (faSvgExists) { resolvedIcons.aliasesImportPresent ||= defaultSet === "fa-svg"; resolvedIcons.imports.push(`import {${defaultSet === "fa-svg" ? "aliases," : ""}fa} from 'vuetify/iconsets/fa-svg'`); resolvedIcons.imports.push("import { library } from '@fortawesome/fontawesome-svg-core'"); resolvedIcons.imports.push("import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'"); resolvedIcons.imports.push("import { useNuxtApp } from '#imports'"); resolvedIcons.svg.fa = ["useNuxtApp().vueApp.component('font-awesome-icon', FontAwesomeIcon)"]; faSvg.libraries.forEach(([defaultExport, name, library]) => { resolvedIcons.imports.push(`import ${defaultExport ? name : `{${name}}`} from '${library}'`); resolvedIcons.svg.fa.push(`library.add(${name})`); }); resolvedIcons.sets.push("fa"); if (defaultSet === "fa-svg") resolvedIcons.defaultSet = "fa"; } } let mdiSvg = icons.svg?.mdi; if (defaultSet === "mdi-svg" || mdiSvg) { if (!mdiSvg) mdiSvg = {}; const mdiSvgExists = isPackageExists("@mdi/js"); if (mdiSvgExists) { resolvedIcons.svg.mdi = true; resolvedIcons.aliasesImportPresent ||= defaultSet === "mdi-svg"; resolvedIcons.imports.push(`import {${defaultSet === "mdi-svg" ? "aliases," : ""}mdi} from 'vuetify/iconsets/mdi-svg'`); if (mdiSvg && mdiSvg.aliases) { resolvedIcons.imports.push(`import {${Object.values(mdiSvg.aliases).join(",")}} from '@mdi/js'`); Object.entries(mdiSvg.aliases).forEach(([alias, icon]) => { resolvedIcons.aliases.push(`${alias}: ${icon}`); }); } resolvedIcons.sets.push("mdi"); if (defaultSet === "mdi-svg") resolvedIcons.defaultSet = "mdi"; } else { resolvedIcons.svg.mdi = false; logger.warn("Missing @mdi/js dependency, install it!"); } } if (defaultSet !== "custom" && !resolvedIcons.unocss && !resolvedIcons.local?.length && !resolvedIcons.cdn?.length && !resolvedIcons.svg?.mdi && !resolvedIcons.svg?.fa?.length) { logger.warn("No icons found, icons disabled!"); return disabledResolvedIcons; } return resolvedIcons; } function convertFontSetsToObjectNotation(sets) { const result = []; if (typeof sets === "string") { result.push({ name: sets }); } else { for (const set of sets) { if (typeof set === "string") result.push({ name: set }); else result.push(set); } } return result; } const disabledClientHints = Object.freeze({ enabled: false, reloadOnFirstRequest: false, viewportSize: false, prefersColorScheme: false, prefersReducedMotion: false }); function prepareSSRClientHints(baseUrl, ctx) { if (!ctx.isSSR || ctx.isNuxtGenerate) return disabledClientHints; const { ssrClientHints: ssrClientHintsConfiguration } = ctx.moduleOptions; const clientHints = { enabled: false, reloadOnFirstRequest: ssrClientHintsConfiguration?.reloadOnFirstRequest ?? false, viewportSize: ssrClientHintsConfiguration?.viewportSize ?? false, prefersColorScheme: ssrClientHintsConfiguration?.prefersColorScheme ?? false, prefersReducedMotion: ssrClientHintsConfiguration?.prefersReducedMotion ?? false }; clientHints.enabled = clientHints.viewportSize || clientHints.prefersColorScheme || clientHints.prefersReducedMotion; if (clientHints.enabled && clientHints.prefersColorScheme && ssrClientHintsConfiguration?.prefersColorSchemeOptions) { const theme = ctx.vuetifyOptions.theme; if (!theme) throw new Error("Vuetify theme is disabled"); const themes = theme.themes; if (!themes) throw new Error("Vuetify themes is missing in theme!"); const defaultTheme = theme.defaultTheme; if (!defaultTheme) throw new Error("Vuetify default theme is missing in theme!"); if (!themes[defaultTheme]) throw new Error(`Missing default theme ${defaultTheme} in the Vuetify themes!`); const darkThemeName = ssrClientHintsConfiguration.prefersColorSchemeOptions?.darkThemeName ?? "dark"; if (!themes[darkThemeName]) throw new Error(`Missing theme ${darkThemeName} in the Vuetify themes!`); const lightThemeName = ssrClientHintsConfiguration.prefersColorSchemeOptions?.lightThemeName ?? "light"; if (!themes[lightThemeName]) throw new Error(`Missing theme ${lightThemeName} in the Vuetify themes!`); if (darkThemeName === lightThemeName) throw new Error("Vuetify dark theme and light theme are the same, change darkThemeName or lightThemeName!"); clientHints.prefersColorSchemeOptions = { baseUrl, defaultTheme, themeNames: Array.from(Object.keys(themes)), cookieName: ssrClientHintsConfiguration.prefersColorSchemeOptions?.cookieName ?? "color-scheme", darkThemeName, lightThemeName, useBrowserThemeOnly: ssrClientHintsConfiguration.prefersColorSchemeOptions?.useBrowserThemeOnly ?? false }; } return clientHints; } async function load(options, nuxt, ctx) { const { configuration, vuetifyConfigurationFilesToWatch } = await mergeVuetifyModules(options, nuxt); if (typeof ctx.componentsPromise === "undefined") { const { componentsPromise, labComponentsPromise } = resolveVuetifyComponents(ctx.resolver); ctx.componentsPromise = componentsPromise; ctx.labComponentsPromise = labComponentsPromise; } const { vuetifyOptions = {} } = configuration; const { directives: _directives, labComponents: _labComponents, ...vOptions } = vuetifyOptions; const vuetifyAppOptions = defu(vOptions, {}); cleanupBlueprint(vuetifyAppOptions); ctx.dateAdapter = void 0; const dateOptions = vuetifyOptions.date; if (dateOptions) { const adapter = dateOptions.adapter; const date = detectDate(); if (!adapter && date.length > 1) throw new Error(`Multiple date adapters found: ${date.map((d) => `@date-io/${d[0]}`).join(", ")}, please specify the adapter to use in the "vuetifyOptions.date.adapter" option.`); if (adapter) { if (adapter === "vuetify" || adapter === "custom") { ctx.dateAdapter = adapter; } else { if (date.find((d) => d === adapter) === void 0) ctx.logger.warn(`[vuetify-nuxt-module] Ignoring Vuetify Date configuration, date adapter "@date-io/${adapter}" not installed!`); else ctx.dateAdapter = adapter; } } else if (date.length === 0) { ctx.dateAdapter = "vuetify"; } else { ctx.dateAdapter = date[0]; } } const oldIcons = ctx.icons; if (oldIcons && oldIcons.cdn?.length && nuxt.options.app.head.link) nuxt.options.app.head.link = nuxt.options.app.head.link.filter((link) => !link.key || !oldIcons.cdn.some(([key]) => link.key === key)); ctx.moduleOptions = configuration.moduleOptions; ctx.vuetifyOptions = configuration.vuetifyOptions; ctx.vuetifyFilesToWatch = Array.from(vuetifyConfigurationFilesToWatch); ctx.icons = prepareIcons(ctx.unocss, ctx.logger, vuetifyAppOptions); ctx.ssrClientHints = prepareSSRClientHints(nuxt.options.app.baseURL ?? "/", ctx); if (ctx.icons.enabled) { ctx.icons.local?.forEach((css) => nuxt.options.css.push(css)); if (ctx.icons.cdn?.length) { nuxt.options.app.head.link ??= []; ctx.icons.cdn.forEach(([key, href]) => nuxt.options.app.head.link.push({ key, rel: "stylesheet", href, type: "text/css", crossorigin: "anonymous" })); } } } function registerWatcher(options, nuxt, ctx) { if (nuxt.options.dev) { let pageReload; nuxt.hooks.hook("builder:watch", (_event, path) => { path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path)); if (!pageReload && ctx.vuetifyFilesToWatch.includes(path)) return nuxt.callHook("restart"); }); nuxt.hook("vite:serverCreated", (server, { isClient }) => { if (!server.ws || !isClient) return; pageReload = debounce(async () => { const modules = []; for (const v of RESOLVED_VIRTUAL_MODULES) { const module = server.moduleGraph.getModuleById(v); if (module) modules.push(module); } await load(options, nuxt, ctx); if (modules.length) await Promise.all(modules.map((m) => server.reloadModule(m))); }, 50, { trailing: false }); }); addVitePlugin({ name: "vuetify:configuration:watch", enforce: "pre", handleHotUpdate({ file }) { if (pageReload && ctx.vuetifyFilesToWatch.includes(file)) return pageReload(); } }); } } function vuetifyStylesPlugin(options, [major, minor, patch], _logger) { let configFile; const vuetifyBase = resolveVuetifyBase(); const noneFiles = /* @__PURE__ */ new Set(); let isNone = false; let sassVariables = false; let fileImport = false; const PREFIX = "vuetify-styles/"; const SSR_PREFIX = `/@${PREFIX}`; const resolveCss = resolveCssFactory(); return { name: "vuetify:styles:nuxt", enforce: "pre", configResolved(config) { if (config.plugins.findIndex((plugin) => plugin.name === "vuetify:styles") > -1) throw new Error("Remove vite-plugin-vuetify from your Nuxt config file, this module registers a modified version."); if (isObject(options.styles)) { sassVariables = true; fileImport = major > 5 || major === 5 && minor > 4 || major === 5 && minor === 4 && patch > 2; if (path.isAbsolute(options.styles.configFile)) configFile = path.resolve(options.styles.configFile); else configFile = path.resolve(path.join(config.root || process.cwd(), options.styles.configFile)); configFile = fileImport ? pathToFileURL(configFile).href : normalizePath(configFile); } else { isNone = options.styles === "none"; } }, async resolveId(source, importer, { custom, ssr }) { if (source.startsWith(PREFIX) || source.startsWith(SSR_PREFIX)) { if (source.match(/\.s[ca]ss$/)) return source; const idx = source.indexOf("?"); return idx > -1 ? source.slice(0, idx) : source; } if (source === "vuetify/styles" || importer && source.endsWith(".css") && isSubdir(vuetifyBase, path.isAbsolute(source) ? source : importer)) { if (options.styles === "sass") return this.resolve(await resolveCss(source), importer, { skipSelf: true, custom }); const resolution = await this.resolve(source, importer, { skipSelf: true, custom }); if (!resolution) return void 0; const target = await resolveCss(resolution.id); if (isNone) { noneFiles.add(target); return target; } return `${ssr ? SSR_PREFIX : PREFIX}${path.relative(vuetifyBase, target)}`; } return void 0; }, load(id) { if (sassVariables) { const target = id.startsWith(PREFIX) ? path.resolve(vuetifyBase, id.slice(PREFIX.length)) : id.startsWith(SSR_PREFIX) ? path.resolve(vuetifyBase, id.slice(SSR_PREFIX.length)) : void 0; if (target) { const suffix = target.match(/\.scss/) ? ";\n" : "\n"; return { code: `@use "${configFile}"${suffix}@use "${fileImport ? pathToFileURL(target).href : normalizePath(target)}"${suffix}`, map: { mappings: "" } }; } } return isNone && noneFiles.has(id) ? "" : void 0; } }; } function resolveCssFactory() { const mappings = /* @__PURE__ */ new Map(); return async (source) => { let mapping = mappings.get(source); if (!mapping) { try { mapping = source.replace(/\.css$/, ".sass"); await fsp.access(mapping, fs.constants.R_OK); } catch (err) { if (!(err instanceof Error && "code" in err && err.code === "ENOENT")) throw err; mapping = source.replace(/\.css$/, ".scss"); } mappings.set(source, mapping); } return mapping; }; } function isSubdir(root, test) { const relative = relative$1(root, test); return relative && !relative.startsWith("..") && !isAbsolute(relative); } function toKebabCase(str = "") { if (toKebabCase.cache.has(str)) return toKebabCase.cache.get(str); const kebab = str.replace(/[^a-z]/gi, "-").replace(/\B([A-Z])/g, "-$1").toLowerCase(); toKebabCase.cache.set(str, kebab); return kebab; } toKebabCase.cache = /* @__PURE__ */ new Map(); function camelize(str) { if (camelize.cache.has(str)) return camelize.cache.get(str); const camel = str.replace(/-([a-z0-9])/g, (g) => g[1].toUpperCase()); camelize.cache.set(str, camel); return camel; } camelize.cache = /* @__PURE__ */ new Map(); function pascalize(str) { if (pascalize.cache.has(str)) return pascalize.cache.get(str); let pascal = camelize(str); pascal = pascal.slice(0, 1).toUpperCase() + pascal.slice(1); pascalize.cache.set(str, pascal); return pascal; } pascalize.cache = /* @__PURE__ */ new Map(); function createTransformAssetUrls(ctx, viteInlineConfig) { const { includeTransformAssetsUrls } = ctx.moduleOptions; if (!includeTransformAssetsUrls) return void 0; let existingTransformAssetUrls = viteInlineConfig.vue?.template?.transformAssetUrls ?? {}; let useURLOptions; if (typeof existingTransformAssetUrls === "boolean") { existingTransformAssetUrls = {}; } else if ("base" in existingTransformAssetUrls || "includeAbsolute" in existingTransformAssetUrls || "tags" in existingTransformAssetUrls) { useURLOptions = { base: existingTransformAssetUrls.base, includeAbsolute: existingTransformAssetUrls.includeAbsolute }; existingTransformAssetUrls = existingTransformAssetUrls.tags ?? {}; } const transformAssetUrls$1 = normalizeTransformAssetUrls( typeof includeTransformAssetsUrls === "object" ? defu(existingTransformAssetUrls, transformAssetUrls, includeTransformAssetsUrls) : defu(existingTransformAssetUrls, transformAssetUrls) ); if (!useURLOptions) return transformAssetUrls$1; useURLOptions.tags = transformAssetUrls$1; return useURLOptions; } function normalizeTransformAssetUrls(transformAssetUrls) { const names = new Set(Object.keys(transformAssetUrls)); let kebab; let pascal; for (const name of names) { transformAssetUrls[name] = normalizeTransformAssetUrlsAttrs(transformAssetUrls[name]); kebab = toKebabCase(name); pascal = pascalize(name); if (!names.has(kebab)) transformAssetUrls[kebab] = [...transformAssetUrls[name]]; if (!names.has(pascal)) transformAssetUrls[pascal] = [...transformAssetUrls[name]]; } return transformAssetUrls; } function normalizeTransformAssetUrlsAttrs(attrs) { const result = /* @__PURE__ */ new Set(); let kebab; let camel; let bind; let idx; for (const attr of attrs) { result.add(attr); idx = attr.indexOf(":"); if (idx > 0) continue; bind = idx === 0; kebab = toKebabCase(bind ? attr.slice(1) : attr); camel = camelize(bind ? attr.slice(1) : attr); result.add(kebab); result.add(camel); result.add(`:${kebab}`); result.add(`:${camel}`); } return [...result]; } function vuetifyConfigurationPlugin(ctx) { return { name: "vuetify:configuration:nuxt", enforce: "pre", resolveId(id) { if (id === VIRTUAL_VUETIFY_CONFIGURATION) return RESOLVED_VIRTUAL_VUETIFY_CONFIGURATION; }, async load(id) { if (id === RESOLVED_VIRTUAL_VUETIFY_CONFIGURATION) { const { directives: _directives, date: _date, icons: _icons, localeMessages: _localeMessages, components: _components, labComponents: _labComponents, ssr, aliases: _aliases, ...newVuetifyOptions } = ctx.vuetifyOptions; if (ctx.isSSR) newVuetifyOptions.ssr = ssr ?? true; if (ctx.i18n && newVuetifyOptions.locale) { delete newVuetifyOptions.locale.rtl; delete newVuetifyOptions.locale.locale; delete newVuetifyOptions.locale.fallback; } const result = await buildConfiguration(ctx); const deepCopy = result.messages.length > 0; return `${result.imports} export const isDev = ${ctx.isDev} export function vuetifyConfiguration() { const options = ${JSON.stringify(newVuetifyOptions)} ${result.directives} ${result.aliases} ${result.components} ${result.messages} return options } ${deepCopy ? `function deepCopy(src,des) { for (const key in src) { if (typeof src[key] === 'object') { if (typeof des[key] !== 'object') des[key] = {} deepCopy(src[key], des[key]) } else { des[key] = src[key] } } } ` : ""} `; } } }; } async function buildConfiguration(ctx) { const { componentsPromise, labComponentsPromise, logger, vuetifyOptions } = ctx; const { aliases, components, directives, localeMessages, labComponents, date: dateOptions } = vuetifyOptions; const config = { directives: "", imports: [], aliasEntries: [], aliases: aliases || {}, components: new Set(components ? Array.isArray(components) ? components : [components] : []), labComponents: /* @__PURE__ */ new Set(), messages: "" }; if (directives) { if (typeof directives === "boolean") { config.imports.push("import * as directives from 'vuetify/directives'"); config.directives = "options.directives = directives"; } else { const useDirectives = Array.isArray(directives) ? [...new Set(directives)] : [directives]; config.imports.push(useDirectives.map((d) => `import {${d}} from 'vuetify/directives/${toKebabCase(d)}'`).join("\n")); config.directives = `options.directives = {${useDirectives.join(",")}}`; } } const importMapComponents = await componentsPromise; const componentsToImport = /* @__PURE__ */ new Map(); config.components.forEach((component) => { const { from } = importMapComponents[component]; if (!from) { logger.warn(`Component ${component} not found in Vuetify.`); return; } const parts = from.split("/"); if (parts.length < 2) { logger.warn(`Component ${component} not found in Vuetify, please report a new issue.`); return; } if (!componentsToImport.has(parts[1])) componentsToImport.set(parts[1], []); const componentsArray = componentsToImport.get(parts[1]); if (!componentsArray.includes(component)) componentsArray.push(component); }); Object.entries(config.aliases).forEach(([key, component]) => { const { from } = importMapComponents[component]; if (!from) { logger.warn(`Component ${component} not found in Vuetify.`); return; } const parts = from.split("/"); if (parts.length < 2) { logger.warn(`Component ${component} not found in Vuetify, please report a new issue.`); return; } if (!componentsToImport.has(parts[1])) componentsToImport.set(parts[1], []); const componentsArray = componentsToImport.get(parts[1]); if (!componentsArray.includes(component)) componentsArray.push(component); config.aliasEntries.push(`'${key}': ${component}`); }); componentsToImport.forEach((componentsArray, from) => { config.imports.push(`import {${Array.from(new Set(componentsArray)).join(",")}} from 'vuetify/components/${from}'`); }); let addDatePicker = ctx.vuetify3_4 === true ? !Array.from(componentsToImport.values()).some((components2) => components2.includes("VDatePicker")) : true; if (labComponents) { const useLabComponents = []; if (typeof labComponents === "boolean") { config.imports.push("import * as labsComponents from 'vuetify/labs/components'"); config.labComponents.add("*"); if (ctx.vuetify3_4 === false) addDatePicker = false; } else if (typeof labComponents === "string") { useLabComponents.push(labComponents); } else if (Array.isArray(labComponents)) { useLabComponents.push(...labComponents); } if (useLabComponents.length) { componentsToImport.clear(); const importMapLabComponents = await labComponentsPromise; useLabComponents.forEach((component) => { const { from } = importMapLabComponents[component]; if (!from) { logger.warn(`Lab Component ${component} not found in Vuetify.`); return; } const parts = from.split("/"); if (parts.length < 2) { logger.warn(`Lab Component ${component} not found in Vuetify, please report a new issue.`); return; } if (!componentsToImport.has(parts[1])) componentsToImport.set(parts[1], []); const componentsArray = componentsToImport.get(parts[1]); if (!componentsArray.includes(component)) componentsArray.push(component); config.labComponents.add(component); }); if (ctx.vuetify3_4 === false && dateOptions && !addDatePicker) { const entry = componentsToImport.get("VDatePicker"); if (entry) { entry.push("VDatePicker"); config.labComponents.add("VDatePicker"); addDatePicker = false; } } componentsToImport.forEach((componentsArray, from) => { config.imports.push(`import {${Array.from(new Set(componentsArray)).join(",")}} from 'vuetify/labs/${from}'`); }); } } if (dateOptions && addDatePicker) { let warn = true; if (typeof ctx.vuetify3_4 === "boolean") { warn = false; if (ctx.vuetify3_4) { config.components.add("VDatePicker"); config.imports.push("import {VDatePicker} from 'vuetify/components/VDatePicker'"); } else { config.labComponents.add("VDatePicker"); config.imports.push("import {VDatePicker} from 'vuetify/labs/VDatePicker'"); } } warn && logger.warn("Unable to load Vuetify version from package.json, add VDatePicker to components or labComponents"); } let componentsEntry = ""; if (config.components.size) { if (config.labComponents.size) { if (config.labComponents.has("*")) componentsEntry = `options.components = {${Array.from(config.components).join(",")},...labsComponents}`; else componentsEntry = `options.components = {${Array.from(config.components).join(",")},${Array.from(config.labComponents).join(",")}}`; } else { componentsEntry = `options.components = {${Array.from(config.components).join(",")}}`; } } else if (config.labComponents.size) { if (config.labComponents.has("*")) componentsEntry = "options.components = {...labsComponents}"; else componentsEntry = `options.components = {${Array.from(config.labComponents).join(",")}}`; } if (!ctx.i18n && localeMessages) { const useLocales = Array.isArray(localeMessages) ? [.../* @__PURE__ */ new Set([...localeMessages])] : [localeMessages]; config.imports.push(`import {${useLocales.join(",")}} from 'vuetify/locale'`); config.messages = ` options.locale = options.locale || {} options.locale.messages = options.locale.messages || {} ${useLocales.map((locale) => { return ` if ('${locale}' in options.locale.messages) deepCopy(options.locale.messages['${locale}'],${locale}) options.locale.messages['${locale}'] = ${locale} `; }).join("")} `; } return { imports: config.imports.length ? config.imports.join("\n") : "", components: componentsEntry, aliases: config.aliasEntries.length ? `options.aliases = {${config.aliasEntries.join(",")}}` : "", directives: config.directives, messages: config.messages }; } function vuetifyIconsPlugin(ctx) { return { name: "vuetify:icons-configuration:nuxt", enforce: "pre", resolveId(id) { if (id === VIRTUAL_VUETIFY_ICONS_CONFIGURATION) return RESOLVED_VIRTUAL_VUETIFY_ICONS_CONFIGURATION; }, async load(id) { if (id === RESOLVED_VIRTUAL_VUETIFY_ICONS_CONFIGURATION) { const { enabled, unocss, aliases, fa, defaultSet, imports, sets } = await prepareIcons(); if (!enabled) { return `export const enabled = false export const isDev = ${ctx.isDev} export function iconsConfiguration() { return {} } `; } if (!defaultSet) { return `export const enabled = true export const isDev = ${ctx.isDev} export function iconsConfiguration() { return {} } `; } return `${imports} export const enabled = true export const isDev = ${ctx.isDev} export function iconsConfiguration() { ${fa.map((f) => ` ${f}`).join("\n")} return { defaultSet: '${defaultSet}', ${aliases} sets: { ${sets} } } } ${unocss} `; } } }; async function prepareIcons() { if (!ctx.icons.enabled) { return { enabled: false, unocss: "", defaultSet: void 0, imports: "", sets: "", aliases: "", fa: [] }; } let aliases = "aliases,"; if (!ctx.icons.aliasesImportPresent || ctx.vuetifyOptions.icons && ctx.vuetifyOptions.icons.defaultSet === "custom") { aliases = ""; } else { const alias = ctx.icons.aliases; if (alias.length) { aliases = `aliases: { ...aliases, ${alias.join(",\n")} }, `; } } let unocss = ""; if (ctx.icons.unocss && ctx.icons.unocssAliases) { ctx.icons.imports.unshift("// @unocss-include"); const prefix = `${ctx.icons.unocssIconPrefix}mdi:`; const { collapse = `${prefix}chevron-up`, complete = `${prefix}check`, cancel = `${prefix}close-circle`, close = `${prefix}close`, // delete (e.g. v-chip close) clear = `${prefix}close-circle`, success = `${prefix}check-circle`, info = `${prefix}information`, warning = `${prefix}alert-circle`, error = `${prefix}close-circle`, prev = `${prefix}chevron-left`, next = `${prefix}chevron-right`, checkboxOn = `${prefix}checkbox-marked`, checkboxOff = `${prefix}checkbox-blank-outline`, checkboxIndeterminate = `${prefix}minus-box`, delimiter = `${prefix}circle`, // for carousel sortAsc = `${prefix}arrow-up`, sortDesc = `${prefix}arrow-down`, expand = `${prefix}chevron-down`, menu = `${prefix}menu`, subgroup = `${prefix}menu-down`, dropdown = `${prefix}menu-down`, radioOn = `${prefix}radiobox-marked`, radioOff = `${prefix}radiobox-blank`, edit = `${prefix}pencil`, ratingEmpty = `${prefix}star-outline`, ratingFull = `${prefix}star`, ratingHalf = `${prefix}star-half-full`, loading = `${prefix}cached`, first = `${prefix}page-first`, last = `${prefix}page-last`, unfold = `${prefix}unfold-more-horizontal`, file = `${prefix}paperclip`, plus = `${prefix}plus`, minus = `${prefix}minus`, calendar = `${prefix}calendar` } = ctx.icons.unocssIcons; const useIcons = { collapse, complete, cancel, close, delete: ctx.icons.unocssIcons.delete ?? `${prefix}close-circle`, clear, success, info, warning, error, prev, next, checkboxOn, checkboxOff, checkboxIndeterminate, delimiter, sortAsc, sortDesc, expand, menu, subgroup, dropdown, radioOn, radioOff, edit, ratingEmpty, ratingFull, ratingHalf, loading, first, last, unfold, file, plus, minus, calendar }; Object.entries(ctx.icons.unocssAdditionalIcons).forEach(([key, value]) => { useIcons[key] = value; }); unocss = `const aliases = JSON.parse('${JSON.stringify(useIcons)}'); `; } return { enabled: true, unocss, fa: ctx.icons.svg?.fa ?? [], defaultSet: ctx.icons.defaultSet, imports: Object.values(ctx.icons.imports).join("\n"), sets: ctx.icons.sets.join(","), aliases }; } } function vuetifyDateConfigurationPlugin(ctx) { return { name: "vuetify:date-configuration:nuxt", enforce: "pre", resolveId(id) { if (id === VIRTUAL_VUETIFY_DATE_CONFIGURATION) return RESOLVED_VIRTUAL_VUETIFY_DATE_CONFIGURATION; }, async load(id) { if (id === RESOLVED_VIRTUAL_VUETIFY_DATE_CONFIGURATION) { if (!ctx.dateAdapter) { return ` export const enabled = false export const isDev = ${ctx.isDev} export const i18n = ${ctx.i18n} export const adapter = 'custom' export function dateConfiguration() { return {} } `; } const { adapter: _adapter, ...newDateOptions } = ctx.vuetifyOptions.date ?? {}; return `${buildImports()} export const enabled = true export const isDev = ${ctx.isDev} export const i18n = ${ctx.i18n} export const adapter = '${ctx.dateAdapter}' export function dateConfiguration() { const options = JSON.parse('${JSON.stringify(newDateOptions)}') ${buildAdapter()} return options } `; } } }; function buildAdapter() { if (ctx.dateAdapter === "custom" || ctx.dateAdapter === "vuetify" && ctx.vuetify3_4 === true) return ""; if (ctx.dateAdapter === "vuetify") return "options.adapter = VuetifyDateAdapter"; const locale = ctx.vuetifyOptions.locale?.locale ?? "en"; if (ctx.dateAdapter === "date-fns") return `options.adapter = new Adapter({ locale: ${locale} })`; return "options.adapter = Adapter"; } function buildImports() { if (ctx.dateAdapter === "custom" || ctx.dateAdapter === "vuetify" && ctx.vuetify3_4 === true) return ""; if (ctx.dateAdapter === "vuetify") return "import { VuetifyDateAdapter } from 'vuetify/labs/date/adapters/vuetify'"; const imports = [`import Adapter from '@date-io/${ctx.dateAdapter}'`]; if (ctx.dateAdapter === "date-fns") imports.push(`import { ${ctx.vuetifyOptions.locale?.locale ?? "en"} } from 'date-fns/locale'`); return imports.join("\n"); } } function vuetifySSRClientHintsPlugin(ctx) { return { name: "vuetify:ssr-client-hints:nuxt", enforce: "pre", resolveId(id) { if (id === VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION) return RESOLVED_VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION; }, async load(id) { if (id === RESOLVED_VIRTUAL_VUETIFY_SSR_CLIENT_HINTS_CONFIGURATION) { const data = { reloadOnFirstRequest: ctx.ssrClientHints.reloadOnFirstRequest, viewportSize: ctx.ssrClientHints.viewportSize, prefersColorScheme: ctx.ssrClientHints.prefersColorScheme, prefersReducedMotion: ctx.ssrClientHints.prefersReducedMotion, clientWidth: ctx.vuetifyOptions.ssr?.clientWidth, clientHeight: ctx.vuetifyOptions.ssr?.clientHeight, prefersColorSchemeOptions: ctx.ssrClientHints.prefersColorSchemeOptions }; return `export const ssrClientHintsConfiguration = JSON.parse('${JSON.stringify(data)}');`; } } }; } function parseId2(id) { id = id.replace(/^(virtual:nuxt:|virtual:)/, ""); return parseURL(decodeURIComponent(isAbsolute(id) ? pathToFileURL(id).href : id)); } function parseId(id) { const { search, pathname } = parseId2(id); const query = parseQuery(search); const urlProps = query.props ? destr(query.props) : void 0; return { query: urlProps, path: pathname ?? id }; } function vuetifyImportPlugin(options) { let filter; return { name: "vuetify:import:nuxt", configResolved(config) { if (config.plugins.findIndex((plugin) => plugin.name === "vuetify:import") > -1) throw new Error("Remove vite-plugin-vuetify from your Nuxt config file, this module registers a modified version."); const vueIdx = config.plugins.findIndex((plugin) => plugin.name === "vite:vue"); const vueOptions = vueIdx > -1 ? config.plugins[vueIdx].api?.options : {}; filter = createFilter(vueOptions.include, vueOptions.exclude); }, async transform(code, id) { const { query, path } = parseId(id); const isVueVirtual = query && "vue" in query; const isVueFile = !isVueVirtual && filter(path) && !/^import { render as _sfc_render } from ".*"$/m.test(code); const isVueTemplate = isVueVirtual && (query.type === "template" || query.type === "script" && query.setup === "true"); if (isVueFile || isVueTemplate) { const { code: imports, source } = generateImports(code, options); return { code: source + imports, map: null }; } return null; } }; } function configureVite(configKey, nuxt, ctx) { nuxt.hook("vite:extend", ({ config }) => checkVuetifyPlugins(config)); nuxt.hook("vite:extendConfig", (viteInlineConfig) => { viteInlineConfig.plugins = viteInlineConfig.plugins || []; checkVuetifyPlugins(viteInlineConfig); viteInlineConfig.optimizeDeps = defu(viteInlineConfig.optimizeDeps, { exclude: ["vuetify"] }); if (ctx.isSSR) { viteInlineConfig.ssr ||= {}; viteInlineConfig.ssr.noExternal = [ ...Array.isArray(viteInlineConfig.ssr.noExternal) ? viteInlineConfig.ssr.noExternal : viteInlineConfig.ssr.noExternal && typeof viteInlineConfig.ssr.noExternal !== "boolean" ? [viteInlineConfig.ssr.noExternal] : [], configKey ]; } const transformAssetUrls = createTransformAssetUrls( ctx, viteInlineConfig ); if (transformAssetUrls) { viteInlineConfig.vue ??= {}; viteInlineConfig.vue.template ??= {}; viteInlineConfig.vue.template.transformAssetUrls = transformAssetUrls; } if (!ctx.moduleOptions.disableModernSassCompiler) { const [major, minor, patch] = ctx.viteVersion; const enableModernSassCompiler = major > 5 || major === 5 && minor >= 4; if (enableModernSassCompiler) { const sassEmbedded = isPackageExists("sass-embedded"); if (sassEmbedded) { viteInlineConfig.css ??= {}; viteInlineConfig.css.preprocessorOptions ??= {}; viteInlineConfig.css.preprocessorOptions.sass ??= {}; viteInlineConfig.css.preprocessorOptions.sass.api = "modern-compiler"; viteInlineConfig.css.preprocessorOptions.scss ??= {}; viteInlineConfig.css.preprocessorOptions.scss.api = "modern-compiler"; } else { viteInlineConfig.css ??= {}; viteInlineConfig.css.preprocessorOptions ??= {}; viteInlineConfig.css.preprocessorOptions.sass ??= {}; viteInlineConfig.css.preprocessorOptions.sass.api = "modern"; viteInlineConfig.css.preprocessorOptions.scss ??= {}; viteInlineConfig.css.preprocessorOptions.scss.api = "modern"; if (!("preprocessorMaxWorkers" in viteInlineConfig.css)) viteInlineConfig.css.preprocessorMaxWorkers = true; } } } const autoImport = { labs: true }; const ignoreDirectives = ctx.moduleOptions.ignoreDirectives; if (ignoreDirectives) { autoImport.ignore = Array.isArray(ignoreDirectives) ? ignoreDirectives : [ignoreDirectives]; } viteInlineConfig.plugins.push(vuetifyImportPlugin({ autoImport })); if (typeof ctx.moduleOptions.styles !== "boolean") viteInlineConfig.plugins.push(vuetifyStylesPlugin({ styles: ctx.moduleOptions.styles }, ctx.viteVersion, ctx.logger)); viteInlineConfig.plugins.push(vuetifyConfigurationPlugin(ctx)); viteInlineConfig.plugins.push(vuetifyIconsPlugin(ctx)); viteInlineConfig.plugins.push(vuetifyDateConfigurationPlugin(ctx)); if (ctx.ssrClientHints.enabled) viteInlineConfig.plugins.push(vuetifySSRClientHintsPlugin(ctx)); }); } function addVuetifyNuxtPlugins(nuxt, ctx) { addVuetifyNuxtPlugin(nuxt, ctx, "client"); addVuetifyNuxtPlugin(nuxt, ctx, "server"); } function addVuetifyNuxtPlugin(nuxt, ctx, mode) { addPluginTemplate({ filename: `vuetify-nuxt-plugin.${mode}.mjs`, name: `vuetify:nuxt:${mode}:plugin`, write: false, mode, getContents() { const dependsOn = ["vuetify:icons:plugin"]; if (ctx.ssrClientHints.enabled) { if (mode === "client") dependsOn.push("vuetify:client-hints:client:plugin"); else dependsOn.push("vuetify:client-hints:server:plugin"); } if (ctx.i18n) { dependsOn.push("vuetify:i18n:plugin"); } if (nuxt.options.dev || ctx.dateAdapter) { if (ctx.i18n) { dependsOn.push("vuetify:date-i18n:plugin"); } else { dependsOn.push("vuetify:date:plugin"); } } return ` import { defineNuxtPlugin } from '#imports' import { isDev, vuetifyConfiguration } from 'virtual:vuetify-configuration' import { createVuetify } from 'vuetify' export default defineNuxtPlugin({ name: 'vuetify:nuxt:${mode}:plugin', order: 25, dependsOn: ${JSON.stringify(dependsOn)}, parallel: true, async setup(nuxtApp) { const vuetifyOptions = vuetifyConfiguration() await nuxtApp.hooks.callHook('vuetify:configuration', { isDev, vuetifyOptions }) await nuxtApp.hooks.callHook('vuetify:before-create', { isDev, vuetifyOptions }) const vuetify = createVuetify(vuetifyOptions) nuxtApp.vueApp.use(vuetify) nuxtApp.provide('vuetify', vuetify) await nuxtApp.hooks.callHook('vuetify:ready', vuetify) if (import.meta.client) isDev && console.log('Vuetify 3 initialized', vuetify) }, }) `; } }); } function configureNuxt(configKey, nuxt, ctx) { const { styles,