vuetify-nuxt-module 1.0.0-alpha.2 β†’ 1.0.0-alpha.4

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/README.md ADDED
@@ -0,0 +1,95 @@
1
+ <div align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://github.com/vuetifyjs/nuxt-module/raw/main/hero-dark.svg" />
4
+ <img alt="vuetify-nuxt-module - Zero-config Nuxt Module for Vuetify" src='https://github.com/vuetifyjs/nuxt-module/raw/main/hero.svg' alt="vuetify-nuxt-module - Zero-config Nuxt Module for Vuetify"><br>
5
+ </picture>
6
+ <p>Zero-config Nuxt Module for Vuetify</p>
7
+ </div>
8
+
9
+ <p align='center'>
10
+ <a href='https://www.npmjs.com/package/vuetify-nuxt-module' target="__blank">
11
+ <img src='https://img.shields.io/npm/v/vuetify-nuxt-module?color=33A6B8&label=' alt="NPM version">
12
+ </a>
13
+ <a href="https://www.npmjs.com/package/vuetify-nuxt-module" target="__blank">
14
+ <img alt="NPM Downloads" src="https://img.shields.io/npm/dm/vuetify-nuxt-module?color=476582&label=">
15
+ </a>
16
+ <a href="https://nuxt.vuetifyjs.com/" target="__blank">
17
+ <img src="https://img.shields.io/static/v1?label=&message=docs%20%26%20guides&color=2e859c" alt="Docs & Guides">
18
+ </a>
19
+ <br>
20
+ <a href="https://github.com/vuetifyjs/nuxt-module" target="__blank">
21
+ <img alt="GitHub stars" src="https://img.shields.io/github/stars/userquin/vuetify-nuxt-module?style=social">
22
+ </a>
23
+ </p>
24
+
25
+ <br>
26
+
27
+ ## πŸš€ Features
28
+
29
+ - πŸ“– [**Documentation & guides**](https://nuxt.vuetifyjs.com/)
30
+ - πŸ‘Œ **Zero-Config**: sensible built-in default [Vuetify](https://vuetifyjs.com/) configuration for common use cases
31
+ - πŸ”Œ **Extensible**: expose the ability to customize the Vuetify configuration via [Nuxt Runtime Hooks](https://nuxt.com/docs/guide/going-further/hooks#usage-with-plugins)
32
+ - ⚑ **Fully Tree Shakable**: by default, only the needed Vuetify components are imported
33
+ - πŸ› οΈ **Versatile**: custom Vuetify [directives](https://vuetifyjs.com/en/getting-started/installation/#manual-steps) and [labs components](https://vuetifyjs.com/en/labs/introduction/) registration
34
+ - ✨ **Configurable Styles**: configure your variables using [Vuetify SASS Variables](https://vuetifyjs.com/en/features/sass-variables/)
35
+ - πŸ’₯ **SSR**: automatic SSR detection and configuration including [HTTP Client hints](https://developer.mozilla.org/en-US/docs/Web/HTTP/Client_hints)
36
+ - πŸ”© **Nuxt Layers and Module Hooks**: load your Vuetify configuration using [Nuxt Layers](https://nuxt.com/docs/getting-started/layers#layers) or using a custom module via `vuetify:registerModule` [Nuxt Module Hook](https://nuxt.com/docs/guide/going-further/hooks#nuxt-hooks-build-time)
37
+ - πŸ“₯ **Vuetify Configuration File**: configure your Vuetify options using a custom `vuetify.config` file, no dev server restart needed
38
+ - πŸ”₯ **Pure CSS Icons**: no more font/js icons, use the new `unocss-mdi` icon set or build your own with UnoCSS Preset Icons
39
+ - πŸ˜ƒ **Icon Fonts**: configure the [icon font](https://vuetifyjs.com/en/features/icon-fonts/) you want to use, the module will automatically import it for you using CDN or local dependencies
40
+ - 🎭 **SVG Icons**: ready to use [@mdi/js](https://www.npmjs.com/package/@mdi/js) and [@fortawesome/vue-fontawesome](https://www.npmjs.com/package/@fortawesome/vue-fontawesome) SVG icons packs
41
+ - πŸ“¦ **Multiple Icon Sets**: register [multiple icon sets](https://vuetifyjs.com/en/features/icon-fonts/#multiple-icon-sets)
42
+ - 🌍 **I18n Ready**: install [@nuxtjs/i18n](https://i18n.nuxtjs.org/) Nuxt module, and you're ready to use Vuetify [internationalization](https://vuetifyjs.com/en/features/internationalization/) features
43
+ - πŸ“† **Date Components**: use Vuetify components [that require date functionality](https://vuetifyjs.com/en/features/dates/) installing and configuring one of the [@date-io](https://github.com/dmtrKovalenko/date-io#projects) adapters
44
+ - πŸ’¬ **Auto-Import Vuetify Locale Messages**: add [Vuetify Locale Messages](https://vuetifyjs.com/en/features/internationalization/#getting-started) adding just the locales you want to use, no more imports needed
45
+ - βš™οΈ **Auto-Import Vuetify Composables**: you don't need to import Vuetify composables manually, they are automatically imported for you
46
+ - 🎨 **Vuetify Blueprints**: use [Vuetify Blueprints](https://vuetifyjs.com/en/features/blueprints/) to quickly scaffold components
47
+ - πŸ‘€ **Nuxt DevTools**: ready to inspect your Vuetify styles with the [Nuxt DevTools](https://github.com/nuxt/devtools) inspector
48
+ - 🦾 **Type Strong**: written in [TypeScript](https://www.typescriptlang.org/)
49
+
50
+ ## πŸ“¦ Install
51
+
52
+ > Requires Vite, will not work with Webpack
53
+
54
+ ```bash
55
+ npx nuxi@latest module add vuetify-nuxt-module
56
+ ```
57
+
58
+ [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/userquin/vuetify-nuxt-module)
59
+
60
+ ## πŸ¦„ Usage
61
+
62
+ > `vuetify-nuxt-module` is strongly opinionated and has a built-in default configuration out of the box. You can use it without any configuration, and it will work for most use cases.
63
+
64
+ Add `vuetify-nuxt-module` module to `nuxt.config.ts` and configure it:
65
+
66
+ ```ts
67
+ // Nuxt config file
68
+ import { defineNuxtConfig } from 'nuxt/config'
69
+
70
+ export default defineNuxtConfig({
71
+ modules: [
72
+ 'vuetify-nuxt-module'
73
+ ],
74
+ vuetify: {
75
+ moduleOptions: {
76
+ /* module specific options */
77
+ },
78
+ vuetifyOptions: {
79
+ /* vuetify options */
80
+ }
81
+ }
82
+ })
83
+ ```
84
+
85
+ Read the [πŸ“– documentation](https://nuxt.vuetifyjs.com/) for a complete guide on how to configure and use this module.
86
+
87
+ ## πŸ‘€ Full config
88
+
89
+ Check out the [types](https://github.com/vuetifyjs/nuxt-module/blob/main/src/types.ts).
90
+
91
+ The virtual modules can be found in [configuration.d.ts](https://github.com/vuetifyjs/nuxt-module/blob/main/configuration.d.ts) file.
92
+
93
+ ## πŸ“„ License
94
+
95
+ [MIT](https://github.com/vuetifyjs/nuxt-module/blob/main/LICENSE) License &copy; 2023-PRESENT [JoaquΓ­n SΓ‘nchez](https://github.com/userquin)
package/dist/module.d.mts CHANGED
@@ -309,7 +309,8 @@ interface MOptions {
309
309
  * @default true
310
310
  */
311
311
  rulesConfiguration?: {
312
- fromLabs: boolean;
312
+ fromLabs?: boolean;
313
+ configFile?: string;
313
314
  };
314
315
  /**
315
316
  * Vuetify SSR client hints.
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=3.15.0"
6
6
  },
7
- "version": "1.0.0-alpha.2",
7
+ "version": "1.0.0-alpha.4",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -2,22 +2,22 @@ import { addPluginTemplate, addTemplate, resolvePath, extendWebpackConfig, isNux
2
2
  import { isPackageExists, getPackageInfo } from 'local-pkg';
3
3
  import semver from 'semver';
4
4
  import { createFilter, version as version$1 } from 'vite';
5
+ import fs, { existsSync, statSync } from 'node:fs';
6
+ import { isAbsolute, resolve, relative } from 'pathe';
5
7
  import defu from 'defu';
6
8
  import { transformAssetUrls } from 'vite-plugin-vuetify';
7
9
  import { pathToFileURL } from 'node:url';
8
10
  import { generateImports, resolveVuetifyBase, isObject, normalizePath } from '@vuetify/loader-shared';
9
11
  import destr from 'destr';
10
- import { isAbsolute, relative } from 'pathe';
11
12
  import { parseQuery, parseURL } from 'ufo';
12
- import fs, { existsSync, statSync } from 'node:fs';
13
13
  import fsp, { readFile } from 'node:fs/promises';
14
14
  import path from 'upath';
15
- import { resolve, dirname, relative as relative$1 } from 'node:path';
15
+ import { resolve as resolve$1, dirname, relative as relative$1 } from 'node:path';
16
16
  import { debounce } from 'perfect-debounce';
17
17
  import process from 'node:process';
18
18
  import { createConfigLoader } from 'unconfig';
19
19
 
20
- const version = "1.0.0-alpha.2";
20
+ const version = "1.0.0-alpha.4";
21
21
 
22
22
  const VIRTUAL_VUETIFY_CONFIGURATION = "virtual:vuetify-configuration";
23
23
  const RESOLVED_VIRTUAL_VUETIFY_CONFIGURATION = `\0${VIRTUAL_VUETIFY_CONFIGURATION}`;
@@ -158,7 +158,7 @@ function addVuetifyNuxtPlugin(nuxt, ctx, mode) {
158
158
  }
159
159
  let rulesImports = "";
160
160
  let rulesPlugin = "";
161
- if (mode === "client" && ctx.enableRules) {
161
+ if (ctx.enableRules) {
162
162
  rulesImports = [
163
163
  "",
164
164
  `import { rulesOptions } from '#build/vuetify/${ctx.rulesConfiguration.fromLabs ? "labs-" : ""}rules-configuration.mjs'`,
@@ -197,6 +197,17 @@ export default defineNuxtPlugin({
197
197
  function getTemplate(source, settings) {
198
198
  return [settings ? `@use '${settings}';` : "", `@use '${source}';`].filter(Boolean).join("\n");
199
199
  }
200
+ function resolveVuetifyConfigFile(configFile, nuxt) {
201
+ if (typeof configFile === "string" && !isAbsolute(configFile)) {
202
+ for (const layer of nuxt.options._layers) {
203
+ const resolved = resolve(layer.config.rootDir, configFile);
204
+ if (existsSync(resolved)) {
205
+ return resolved;
206
+ }
207
+ }
208
+ }
209
+ return configFile;
210
+ }
200
211
  async function configureNuxt(configKey, nuxt, ctx) {
201
212
  const {
202
213
  styles,
@@ -204,15 +215,16 @@ async function configureNuxt(configKey, nuxt, ctx) {
204
215
  prefixComposables
205
216
  } = ctx.moduleOptions;
206
217
  const runtimeDir = ctx.resolver.resolve("./runtime");
207
- if (ctx.enableRules !== void 0) {
218
+ if (ctx.enableRules === void 0) {
208
219
  ctx.enableRules = ctx.vuetifyGte("3.8.0");
209
220
  }
210
221
  if (styles !== "none" && styles !== false) {
211
222
  nuxt.options.css ??= [];
212
223
  if (typeof styles === "object" && "configFile" in styles) {
224
+ const configFile = resolveVuetifyConfigFile(styles.configFile, nuxt);
213
225
  const a = addTemplate({
214
226
  filename: "vuetify.settings.scss",
215
- getContents: async () => getTemplate("vuetify/styles", await resolvePath(styles.configFile))
227
+ getContents: async () => getTemplate("vuetify/styles", await resolvePath(configFile))
216
228
  });
217
229
  nuxt.options.css.push(a.dst);
218
230
  } else if (ctx.vuetifyGte("4.0.0")) {
@@ -229,7 +241,19 @@ async function configureNuxt(configKey, nuxt, ctx) {
229
241
  }
230
242
  nuxt.options.build.transpile.push(configKey, runtimeDir);
231
243
  if (ctx.enableRules) {
232
- nuxt.options.build.transpile.push(`#build/vuetify/${ctx.rulesConfiguration.fromLabs ? "labs-" : ""}rules-configuration.mjs`);
244
+ const rulesConfigurationFile = `vuetify/${ctx.rulesConfiguration.fromLabs ? "labs-" : ""}rules-configuration.mjs`;
245
+ nuxt.options.build.transpile.push(`#build/${rulesConfigurationFile}`);
246
+ addTemplate({
247
+ filename: rulesConfigurationFile,
248
+ getContents: async () => {
249
+ if (ctx.rulesConfiguration?.configFile) {
250
+ const resolvedPath = await resolvePath(ctx.rulesConfiguration.configFile);
251
+ return `export { default as rulesOptions } from '${resolvedPath}'`;
252
+ }
253
+ return "export const rulesOptions = {}";
254
+ },
255
+ write: true
256
+ });
233
257
  }
234
258
  nuxt.options.build.transpile.push(/\/vuetify-nuxt-plugin\.(client|server)\.mjs$/);
235
259
  nuxt.options.imports.transform ??= {};
@@ -267,12 +291,18 @@ async function configureNuxt(configKey, nuxt, ctx) {
267
291
  if (ctx.vuetifyGte("3.10.0")) {
268
292
  composables.push("useMask");
269
293
  }
270
- addImports(composables.map((name) => ({
271
- name,
272
- from: ctx.vuetifyGte("3.4.0") || name !== "useDate" ? "vuetify" : "vuetify/labs/date",
273
- as: prefixComposables ? name.replace(/^use/, "useV") : void 0,
274
- meta: { docsUrl: name === "useRules" ? "https://vuetifyjs.com/en/features/rules/" : `https://vuetifyjs.com/en/api/${toKebabCase(name)}/` }
275
- })));
294
+ addImports(composables.map((name) => {
295
+ let from = ctx.vuetifyGte("3.4.0") || name !== "useDate" ? "vuetify" : "vuetify/labs/date";
296
+ if (name === "useRules" && ctx.rulesConfiguration?.fromLabs) {
297
+ from = "vuetify/labs/rules";
298
+ }
299
+ return {
300
+ name,
301
+ from,
302
+ as: prefixComposables ? name.replace(/^use/, "useV") : void 0,
303
+ meta: { docsUrl: name === "useRules" ? "https://vuetifyjs.com/en/features/rules/" : `https://vuetifyjs.com/en/api/${toKebabCase(name)}/` }
304
+ };
305
+ }));
276
306
  }
277
307
  if (ctx.ssrClientHints.enabled) {
278
308
  addPlugin({
@@ -406,7 +436,8 @@ async function buildConfiguration(ctx) {
406
436
  const importMapComponents = await componentsPromise;
407
437
  const componentsToImport = /* @__PURE__ */ new Map();
408
438
  for (const component of config.components) {
409
- const { from } = importMapComponents[component];
439
+ const componentEntry = importMapComponents[component];
440
+ const from = componentEntry?.from;
410
441
  if (!from) {
411
442
  logger.warn(`Component ${component} not found in Vuetify.`);
412
443
  continue;
@@ -416,16 +447,21 @@ async function buildConfiguration(ctx) {
416
447
  logger.warn(`Component ${component} not found in Vuetify, please report a new issue.`);
417
448
  continue;
418
449
  }
419
- if (!componentsToImport.has(parts[1])) {
420
- componentsToImport.set(parts[1], []);
450
+ const bucket = parts[1];
451
+ if (!bucket) {
452
+ continue;
453
+ }
454
+ if (!componentsToImport.has(bucket)) {
455
+ componentsToImport.set(bucket, []);
421
456
  }
422
- const componentsArray = componentsToImport.get(parts[1]);
457
+ const componentsArray = componentsToImport.get(bucket);
423
458
  if (!componentsArray.includes(component)) {
424
459
  componentsArray.push(component);
425
460
  }
426
461
  }
427
462
  for (const [key, component] of Object.entries(config.aliases)) {
428
- const { from } = importMapComponents[component];
463
+ const componentEntry = importMapComponents[component];
464
+ const from = componentEntry?.from;
429
465
  if (!from) {
430
466
  logger.warn(`Component ${component} not found in Vuetify.`);
431
467
  continue;
@@ -435,10 +471,14 @@ async function buildConfiguration(ctx) {
435
471
  logger.warn(`Component ${component} not found in Vuetify, please report a new issue.`);
436
472
  continue;
437
473
  }
438
- if (!componentsToImport.has(parts[1])) {
439
- componentsToImport.set(parts[1], []);
474
+ const bucket = parts[1];
475
+ if (!bucket) {
476
+ continue;
477
+ }
478
+ if (!componentsToImport.has(bucket)) {
479
+ componentsToImport.set(bucket, []);
440
480
  }
441
- const componentsArray = componentsToImport.get(parts[1]);
481
+ const componentsArray = componentsToImport.get(bucket);
442
482
  if (!componentsArray.includes(component)) {
443
483
  componentsArray.push(component);
444
484
  }
@@ -465,7 +505,8 @@ async function buildConfiguration(ctx) {
465
505
  componentsToImport.clear();
466
506
  const importMapLabComponents = await labComponentsPromise;
467
507
  for (const component of useLabComponents) {
468
- const { from } = importMapLabComponents[component];
508
+ const componentEntry = importMapLabComponents[component];
509
+ const from = componentEntry?.from;
469
510
  if (!from) {
470
511
  logger.warn(`Lab Component ${component} not found in Vuetify.`);
471
512
  continue;
@@ -475,10 +516,14 @@ async function buildConfiguration(ctx) {
475
516
  logger.warn(`Lab Component ${component} not found in Vuetify, please report a new issue.`);
476
517
  continue;
477
518
  }
478
- if (!componentsToImport.has(parts[1])) {
479
- componentsToImport.set(parts[1], []);
519
+ const bucket = parts[1];
520
+ if (!bucket) {
521
+ continue;
480
522
  }
481
- const componentsArray = componentsToImport.get(parts[1]);
523
+ if (!componentsToImport.has(bucket)) {
524
+ componentsToImport.set(bucket, []);
525
+ }
526
+ const componentsArray = componentsToImport.get(bucket);
482
527
  if (!componentsArray.includes(component)) {
483
528
  componentsArray.push(component);
484
529
  }
@@ -867,6 +912,7 @@ function vuetifyStylesPlugin(options, viteVersion, _logger) {
867
912
  const PREFIX = "vuetify-styles/";
868
913
  const SSR_PREFIX = `/@${PREFIX}`;
869
914
  const resolveCss = resolveCssFactory();
915
+ const toPath = (file) => fileImport ? pathToFileURL(file).href : normalizePath(file);
870
916
  return {
871
917
  name: "vuetify:styles:nuxt",
872
918
  enforce: "pre",
@@ -900,6 +946,9 @@ function vuetifyStylesPlugin(options, viteVersion, _logger) {
900
946
  return;
901
947
  }
902
948
  const target = await resolveCss(resolution.id);
949
+ if (target.startsWith(PREFIX) || target.startsWith(SSR_PREFIX)) {
950
+ return target;
951
+ }
903
952
  if (isNone) {
904
953
  noneFiles.add(target);
905
954
  return target;
@@ -920,7 +969,7 @@ function vuetifyStylesPlugin(options, viteVersion, _logger) {
920
969
  }
921
970
  const suffix = /\.scss/.test(target) ? ";\n" : "\n";
922
971
  const result = {
923
- code: `@use "${configFile}"${suffix}@use "${fileImport ? pathToFileURL(target).href : normalizePath(target)}"${suffix}`,
972
+ code: `@use "${toPath(configFile)}"${suffix}@use "${toPath(target)}"${suffix}`,
924
973
  map: {
925
974
  mappings: ""
926
975
  }
@@ -1266,7 +1315,7 @@ async function loadVuetifyConfiguration(cwd = process.cwd(), configOrPath = cwd,
1266
1315
  inlineConfig = configOrPath;
1267
1316
  configOrPath = process.cwd();
1268
1317
  }
1269
- const resolved = resolve(cwd, configOrPath);
1318
+ const resolved = resolve$1(cwd, configOrPath);
1270
1319
  let isFile = false;
1271
1320
  if (existsSync(resolved) && statSync(resolved).isFile()) {
1272
1321
  isFile = true;
@@ -1302,6 +1351,14 @@ async function loadVuetifyConfiguration(cwd = process.cwd(), configOrPath = cwd,
1302
1351
  });
1303
1352
  const result = await loader.load();
1304
1353
  result.config = result.config?.config === false ? Object.assign(defaults, inlineConfig) : Object.assign(defaults, result.config || inlineConfig);
1354
+ if (result.config && typeof result.config === "object" && "vuetifyOptions" in result.config) {
1355
+ const nestedOptions = result.config.vuetifyOptions;
1356
+ if (typeof nestedOptions === "object" && nestedOptions !== null) {
1357
+ console.warn('[@vuetify/nuxt-module] Detected nested "vuetifyOptions" in your configuration file. This usually happens when using a named export or wrapping options incorrectly. Please export the options directly using "export default".');
1358
+ Object.assign(result.config, nestedOptions);
1359
+ delete result.config.vuetifyOptions;
1360
+ }
1361
+ }
1305
1362
  delete result.config.config;
1306
1363
  return result;
1307
1364
  }
@@ -1529,7 +1586,7 @@ function registerWatcher(options, nuxt, ctx) {
1529
1586
  if (nuxt.options.dev) {
1530
1587
  let pageReload;
1531
1588
  nuxt.hooks.hook("builder:watch", (_event, path) => {
1532
- path = relative$1(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path));
1589
+ path = relative$1(nuxt.options.srcDir, resolve$1(nuxt.options.srcDir, path));
1533
1590
  if (!pageReload && ctx.vuetifyFilesToWatch.includes(path)) {
1534
1591
  return nuxt.callHook("restart");
1535
1592
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vuetify-nuxt-module",
3
3
  "type": "module",
4
- "version": "1.0.0-alpha.2",
4
+ "version": "1.0.0-alpha.4",
5
5
  "description": "Zero-Config Nuxt Module for Vuetify",
6
6
  "author": "userquin <userquin@gmail.com>",
7
7
  "license": "MIT",
@@ -82,7 +82,7 @@
82
82
  "@parcel/watcher": "^2.5.6",
83
83
  "@types/node": "^25.3.3",
84
84
  "@types/semver": "^7.7.1",
85
- "@unocss/nuxt": "^66.6.2",
85
+ "@unocss/nuxt": "^66.6.5",
86
86
  "bumpp": "^10.4.1",
87
87
  "eslint": "^10.0.2",
88
88
  "luxon": "^3.7.2",