vuetify-nuxt4-module 1.1.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.
@@ -0,0 +1,253 @@
1
+ import { defineNuxtModule, useLogger, createResolver, addPlugin, addImports, extendViteConfig, addVitePlugin } from '@nuxt/kit';
2
+ import defu from 'defu';
3
+
4
+ const MODULE_NAME = "nuxt-vuetify-module";
5
+ const module$1 = defineNuxtModule({
6
+ meta: {
7
+ name: MODULE_NAME,
8
+ configKey: "vuetify",
9
+ compatibility: {
10
+ nuxt: ">=4.0.0"
11
+ }
12
+ },
13
+ defaults: {
14
+ vuetifyOptions: {
15
+ blueprint: "md3",
16
+ dateAdapter: "vuetify",
17
+ icons: {
18
+ defaultSet: "mdi"
19
+ },
20
+ ssr: true
21
+ },
22
+ importComposables: true,
23
+ prefixComposables: false,
24
+ transformAssetUrls: true,
25
+ autoImport: true,
26
+ styles: true,
27
+ persistence: {
28
+ enabled: true,
29
+ key: "nuxt-vuetify-theme",
30
+ storage: "cookie",
31
+ cookieOptions: {
32
+ maxAge: 60 * 60 * 24 * 365,
33
+ path: "/",
34
+ sameSite: "lax"
35
+ }
36
+ }
37
+ },
38
+ async setup(options, nuxt) {
39
+ const logger = useLogger(MODULE_NAME);
40
+ const { resolve } = createResolver(import.meta.url);
41
+ logger.info("Setting up Vuetify module...");
42
+ await nuxt.callHook("vuetify:register", (additionalOptions) => {
43
+ options = defu(additionalOptions, options);
44
+ });
45
+ nuxt.options.runtimeConfig.public.vuetify = {
46
+ vuetifyOptions: options.vuetifyOptions,
47
+ persistence: options.persistence
48
+ };
49
+ addStyles(nuxt, options.styles);
50
+ addIconStyles(nuxt, options.vuetifyOptions.icons);
51
+ nuxt.options.build.transpile.push("vuetify");
52
+ addPlugin({
53
+ src: resolve("./runtime/plugins/vuetify"),
54
+ mode: "all"
55
+ });
56
+ if (options.importComposables) {
57
+ addVuetifyComposables(options.prefixComposables);
58
+ }
59
+ addImports([
60
+ {
61
+ name: "useVuetify",
62
+ from: resolve("./runtime/composables/useVuetify")
63
+ },
64
+ {
65
+ name: "onVuetifyHook",
66
+ from: resolve("./utils/hooks")
67
+ },
68
+ {
69
+ name: "offVuetifyHook",
70
+ from: resolve("./utils/hooks")
71
+ }
72
+ ]);
73
+ extendViteConfig((config) => {
74
+ config.optimizeDeps ??= {};
75
+ config.optimizeDeps.include ??= [];
76
+ config.optimizeDeps.include.push("vuetify");
77
+ config.optimizeDeps.exclude ??= [];
78
+ config.optimizeDeps.exclude.push("vuetify/components", "vuetify/directives");
79
+ config.esbuild ??= {};
80
+ if (typeof config.esbuild !== "boolean") {
81
+ config.esbuild.treeShaking = true;
82
+ }
83
+ config.build ??= {};
84
+ config.build.rollupOptions ??= {};
85
+ const existingOnWarn = config.build.rollupOptions.onwarn;
86
+ config.build.rollupOptions.onwarn = (warning, warn) => {
87
+ if (warning.message?.includes("vuetify") && warning.message?.includes("dynamically imported")) {
88
+ return;
89
+ }
90
+ if (existingOnWarn) {
91
+ existingOnWarn(warning, warn);
92
+ } else {
93
+ warn(warning);
94
+ }
95
+ };
96
+ const output = config.build.rollupOptions.output;
97
+ if (output && !Array.isArray(output)) {
98
+ output.manualChunks = (id) => {
99
+ if (id.includes("vuetify") && (id.endsWith(".css") || id.endsWith(".sass") || id.endsWith(".scss"))) {
100
+ return "vuetify-styles";
101
+ }
102
+ if (id.includes("vuetify/lib/composables") || id.includes("vuetify/lib/util")) {
103
+ return "vuetify-core";
104
+ }
105
+ if (id.includes("vuetify/lib/framework")) {
106
+ return "vuetify-framework";
107
+ }
108
+ if (id.includes("vuetify/lib/components/VDataTable")) {
109
+ return "vuetify-datatable";
110
+ }
111
+ if (id.includes("vuetify/lib/components/VCalendar")) {
112
+ return "vuetify-calendar";
113
+ }
114
+ if (id.includes("VDatePicker") || id.includes("VCalendar")) return "vuetify-date";
115
+ if (id.includes("VAutocomplete") || id.includes("VCombobox")) return "vuetify-autocomplete";
116
+ if (id.includes("vuetify/lib/components/VForm") || id.includes("vuetify/lib/components/VTextField") || id.includes("vuetify/lib/components/VSelect")) {
117
+ return "vuetify-forms";
118
+ }
119
+ if (id.includes("vuetify/lib/components/VNavigationDrawer") || id.includes("vuetify/lib/components/VAppBar") || id.includes("vuetify/lib/components/VToolbar")) {
120
+ return "vuetify-navigation";
121
+ }
122
+ if (id.includes("vuetify/lib/components")) {
123
+ return "vuetify-components";
124
+ }
125
+ if (id.includes("vuetify/lib/directives")) {
126
+ return "vuetify-directives";
127
+ }
128
+ if (id.includes("@mdi/font") || id.includes("materialdesignicons")) {
129
+ return "icons";
130
+ }
131
+ if (id.includes("vuetify") && /\.(?:css|sass|scss)$/.test(id)) {
132
+ return "vuetify-styles";
133
+ }
134
+ if (id.includes("vuetify")) {
135
+ return "vuetify";
136
+ }
137
+ };
138
+ }
139
+ config.build.chunkSizeWarningLimit = 600;
140
+ config.build.minify = "esbuild";
141
+ config.build.cssMinify = "lightningcss";
142
+ config.build.cssCodeSplit = true;
143
+ });
144
+ if (options.transformAssetUrls) {
145
+ await setupTransformAssetUrls(nuxt);
146
+ }
147
+ if (options.autoImport) {
148
+ await setupAutoImport(options.autoImport, logger);
149
+ }
150
+ if (options.vuetifyOptions.ssr) {
151
+ nuxt.options.vite.ssr ??= {};
152
+ nuxt.options.vite.ssr.noExternal ??= [];
153
+ if (Array.isArray(nuxt.options.vite.ssr.noExternal)) {
154
+ nuxt.options.vite.ssr.noExternal.push("vuetify");
155
+ }
156
+ }
157
+ addPreloadHints(nuxt, options.vuetifyOptions.icons);
158
+ logger.success("Vuetify module setup complete");
159
+ }
160
+ });
161
+ function addStyles(nuxt, styles) {
162
+ if (typeof styles === "boolean" && !styles || styles === "none") return;
163
+ if (typeof styles === "object" && styles.configFile) {
164
+ nuxt.options.css.unshift(styles.configFile);
165
+ } else if (styles === "sass") {
166
+ nuxt.options.css.unshift("vuetify/styles/main.sass");
167
+ } else {
168
+ nuxt.options.css.unshift("vuetify/styles");
169
+ }
170
+ }
171
+ function addIconStyles(nuxt, icons) {
172
+ const defaultSet = icons?.defaultSet ?? "mdi";
173
+ if (defaultSet.includes("svg")) return;
174
+ const cssMap = {
175
+ mdi: "@mdi/font/css/materialdesignicons.css",
176
+ fa: "@fortawesome/fontawesome-free/css/all.css"
177
+ };
178
+ const css = cssMap[defaultSet];
179
+ if (css && !nuxt.options.css.includes(css)) {
180
+ nuxt.options.css.push(css);
181
+ }
182
+ }
183
+ function addVuetifyComposables(prefix) {
184
+ const composables = [
185
+ "useLocale",
186
+ "useDefaults",
187
+ "useDisplay",
188
+ "useLayout",
189
+ "useRtl",
190
+ "useTheme",
191
+ "useDate",
192
+ "useGoTo"
193
+ ];
194
+ const imports = composables.map((name) => ({
195
+ name,
196
+ as: prefix ? name.replace("use", "useV") : name,
197
+ from: "vuetify"
198
+ }));
199
+ addImports(imports);
200
+ }
201
+ async function setupTransformAssetUrls(nuxt) {
202
+ try {
203
+ const { transformAssetUrls } = await import('vite-plugin-vuetify');
204
+ nuxt.options.vite.vue ??= {};
205
+ nuxt.options.vite.vue.template = defu(nuxt.options.vite.vue.template || {}, {
206
+ transformAssetUrls
207
+ });
208
+ } catch {
209
+ const fallbackTransformAssetUrls = {
210
+ "v-img": ["src"],
211
+ "v-card": ["image"],
212
+ "v-card-item": ["image"],
213
+ "v-carousel-item": ["src"],
214
+ "v-navigation-drawer": ["image"],
215
+ "v-parallax": ["src"],
216
+ "v-avatar": ["image"]
217
+ };
218
+ nuxt.options.vite.vue ??= {};
219
+ nuxt.options.vite.vue.template = defu(nuxt.options.vite.vue.template || {}, {
220
+ transformAssetUrls: fallbackTransformAssetUrls
221
+ });
222
+ }
223
+ }
224
+ async function setupAutoImport(autoImport, logger) {
225
+ try {
226
+ const vuetifyPlugin = await import('vite-plugin-vuetify').then((m) => m.default);
227
+ const autoImportConfig = typeof autoImport === "boolean" ? autoImport : {
228
+ labs: autoImport.labs ?? false,
229
+ ignore: autoImport.ignore ?? []
230
+ };
231
+ addVitePlugin(vuetifyPlugin({ autoImport: autoImportConfig }));
232
+ } catch {
233
+ logger.warn(
234
+ "vite-plugin-vuetify not found. Install it for auto-import support:\n npm install -D vite-plugin-vuetify"
235
+ );
236
+ }
237
+ }
238
+ function addPreloadHints(nuxt, icons) {
239
+ const defaultSet = icons?.defaultSet ?? "mdi";
240
+ nuxt.hook("nitro:config", (config) => {
241
+ config.prerender ??= {};
242
+ config.prerender.routes ??= [];
243
+ });
244
+ nuxt.hook("app:rendered", (ctx) => {
245
+ if (defaultSet === "mdi") {
246
+ ctx.renderResult?.head?.push(
247
+ '<link rel="preload" href="/_nuxt/@mdi/font/fonts/materialdesignicons-webfont.woff2" as="font" type="font/woff2" crossorigin>'
248
+ );
249
+ }
250
+ });
251
+ }
252
+
253
+ export { module$1 as default };
@@ -0,0 +1,19 @@
1
+ export declare function useVuetify(): {
2
+ vuetify: any;
3
+ theme: any;
4
+ locale: any;
5
+ display: any;
6
+ defaults: any;
7
+ isDark: import("vue").ComputedRef<any>;
8
+ isMobile: import("vue").ComputedRef<any>;
9
+ isSmAndDown: import("vue").ComputedRef<any>;
10
+ isMdAndUp: import("vue").ComputedRef<any>;
11
+ isRtl: import("vue").ComputedRef<any>;
12
+ currentBreakpoint: import("vue").ComputedRef<any>;
13
+ currentTheme: import("vue").ComputedRef<any>;
14
+ currentLocale: import("vue").ComputedRef<any>;
15
+ colors: import("vue").ComputedRef<any>;
16
+ toggleTheme(): void;
17
+ setTheme(name: string): void;
18
+ setLocale(locale: string): void;
19
+ };
@@ -0,0 +1,38 @@
1
+ import { createError, useNuxtApp } from "#app";
2
+ import { computed } from "vue";
3
+ export function useVuetify() {
4
+ const { $vuetify } = useNuxtApp();
5
+ if (!$vuetify) {
6
+ throw createError({
7
+ statusCode: 500,
8
+ message: "Vuetify is not initialized. Make sure vuetify-nuxt4-module is properly configured."
9
+ });
10
+ }
11
+ return {
12
+ // Core
13
+ vuetify: $vuetify,
14
+ theme: $vuetify.theme,
15
+ locale: $vuetify.locale,
16
+ display: $vuetify.display,
17
+ defaults: $vuetify.defaults,
18
+ // Computed helpers
19
+ isDark: computed(() => $vuetify.theme.global.current.value.dark),
20
+ isMobile: computed(() => $vuetify.display.mobile.value),
21
+ isSmAndDown: computed(() => $vuetify.display.smAndDown.value),
22
+ isMdAndUp: computed(() => $vuetify.display.mdAndUp.value),
23
+ isRtl: computed(() => $vuetify.locale.isRtl.value),
24
+ currentBreakpoint: computed(() => $vuetify.display.name.value),
25
+ currentTheme: computed(() => $vuetify.theme.global.name.value),
26
+ currentLocale: computed(() => $vuetify.locale.current.value),
27
+ colors: computed(() => $vuetify.theme.global.current.value.colors),
28
+ toggleTheme() {
29
+ $vuetify.theme.change($vuetify.theme.global.current.value.dark ? "light" : "dark");
30
+ },
31
+ setTheme(name) {
32
+ $vuetify.theme.global.name.value = name;
33
+ },
34
+ setLocale(locale) {
35
+ $vuetify.locale.current.value = locale;
36
+ }
37
+ };
38
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: import("#app").Plugin<Record<string, unknown>> & import("#app").ObjectPlugin<Record<string, unknown>>;
2
+ export default _default;
@@ -0,0 +1,67 @@
1
+ import { defineNuxtPlugin, useRuntimeConfig } from "#app";
2
+ import { createVuetify } from "vuetify";
3
+ import { watch } from "vue";
4
+ import {
5
+ logger,
6
+ callVuetifyHook,
7
+ createIconConfig,
8
+ createLocaleConfig,
9
+ createDateConfig,
10
+ loadBlueprint,
11
+ resolvePersistenceConfig,
12
+ getPersistedTheme,
13
+ setPersistedTheme
14
+ } from "../../utils";
15
+ export default defineNuxtPlugin({
16
+ name: "vuetify",
17
+ enforce: "pre",
18
+ parallel: true,
19
+ async setup(nuxtApp) {
20
+ const runtimeConfig = useRuntimeConfig().public.vuetify;
21
+ const { vuetifyOptions: config, persistence: persistenceOptions } = runtimeConfig;
22
+ logger.debug("Initializing Vuetify...");
23
+ const vuetifyOptions = {
24
+ ssr: config.ssr ?? true,
25
+ theme: config.theme,
26
+ defaults: config.defaults,
27
+ aliases: config.aliases
28
+ };
29
+ const [blueprint, dateConfig, iconConfig, localConfig] = await Promise.all([
30
+ config.blueprint ? loadBlueprint(config.blueprint) : void 0,
31
+ config.dateAdapter && config.dateAdapter !== "vuetify" ? createDateConfig(config.dateAdapter) : void 0,
32
+ config.icons ? createIconConfig(config.icons) : createIconConfig(),
33
+ config.locale ? createLocaleConfig(config.locale) : createLocaleConfig()
34
+ ]);
35
+ if (blueprint) vuetifyOptions.blueprint = blueprint;
36
+ if (dateConfig) vuetifyOptions.date = dateConfig;
37
+ if (iconConfig) vuetifyOptions.icons = iconConfig;
38
+ if (config.locale) vuetifyOptions.locale = localConfig;
39
+ await callVuetifyHook(nuxtApp, "vuetify:before-create", { vuetifyOptions });
40
+ const vuetify = createVuetify(vuetifyOptions);
41
+ nuxtApp.vueApp.use(vuetify);
42
+ if (import.meta.client) {
43
+ const persistenceConfig = resolvePersistenceConfig(persistenceOptions);
44
+ if (persistenceConfig.enabled) {
45
+ const setupPersistence = () => {
46
+ const persistedTheme = getPersistedTheme(persistenceConfig);
47
+ if (persistedTheme && vuetify.theme.themes.value[persistedTheme]) {
48
+ vuetify.theme.change(persistedTheme);
49
+ }
50
+ watch(
51
+ () => vuetify.theme.global.name.value,
52
+ (theme) => setPersistedTheme(persistenceConfig, theme)
53
+ );
54
+ };
55
+ if ("requestIdleCallback" in window) {
56
+ requestIdleCallback(setupPersistence);
57
+ } else {
58
+ setTimeout(setupPersistence, 0);
59
+ }
60
+ }
61
+ }
62
+ await callVuetifyHook(nuxtApp, "vuetify:configuration", { vuetifyOptions });
63
+ await callVuetifyHook(nuxtApp, "vuetify:ready", vuetify);
64
+ logger.success("Vuetify initialized");
65
+ nuxtApp.provide("vuetify", vuetify);
66
+ }
67
+ });