vuetify-nuxt-module 1.0.0-beta.9 → 1.0.0-rc.1

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 CHANGED
@@ -26,25 +26,19 @@
26
26
 
27
27
  ## 🚀 Features
28
28
 
29
- - 📖 [**Documentation & guides**](https://nuxt.vuetifyjs.com/)
29
+ > 📖 Full [**documentation & guides**](https://nuxt.vuetifyjs.com/)
30
+
30
31
  - 👌 **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
32
+ - **Fully Tree Shakable**: by default, only the Vuetify components you use are imported
33
+ - 🪄 **Auto-Import**: Vuetify components and composables are auto-imported no manual imports needed
34
+ - 🔌 **Extensible**: customize the Vuetify configuration via [Nuxt Runtime Hooks](https://nuxt.com/docs/guide/going-further/hooks#usage-with-plugins), [Nuxt Layers](https://nuxt.com/docs/getting-started/layers#layers), the `vuetify:registerModule` [module hook](https://nuxt.com/docs/guide/going-further/hooks#nuxt-hooks-build-time), or a dedicated `vuetify.config` file
35
+ - 💥 **SSR**: automatic SSR detection and configuration, including [HTTP Client Hints](https://developer.mozilla.org/en-US/docs/Web/HTTP/Client_hints)
36
+ - **Configurable Styles**: configure your variables using [Vuetify SASS Variables](https://vuetifyjs.com/en/features/sass-variables/)
37
+ - 🛠️ **Directives & Labs**: optional [directives](https://vuetifyjs.com/en/getting-started/installation/#manual-steps) and [labs components](https://vuetifyjs.com/en/labs/introduction/) registration
38
+ - 🎭 **Icons**: pure-CSS icons (UnoCSS), [icon fonts](https://vuetifyjs.com/en/features/icon-fonts/) (CDN or local), SVG packs ([@mdi/js](https://www.npmjs.com/package/@mdi/js), [FontAwesome](https://www.npmjs.com/package/@fortawesome/vue-fontawesome)), and [multiple icon sets](https://vuetifyjs.com/en/features/icon-fonts/#multiple-icon-sets)
39
+ - 🌍 **I18n**: integrate [@nuxtjs/i18n](https://i18n.nuxtjs.org/) for Vuetify [internationalization](https://vuetifyjs.com/en/features/internationalization/), with auto-imported Vuetify locale messages
40
+ - 📆 **Date Components**: use Vuetify [date components](https://vuetifyjs.com/en/features/dates/) via the [@date-io](https://github.com/dmtrKovalenko/date-io#projects) adapters
41
+ - 🎨 **Blueprints**: scaffold quickly with [Vuetify Blueprints](https://vuetifyjs.com/en/features/blueprints/)
48
42
  - 🦾 **Type Strong**: written in [TypeScript](https://www.typescriptlang.org/)
49
43
 
50
44
  ## 📦 Install
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-beta.9",
7
+ "version": "1.0.0-rc.1",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,21 +1,19 @@
1
1
  import { existsSync, statSync, readFileSync } from 'node:fs';
2
2
  import { pathToFileURL, fileURLToPath } from 'node:url';
3
3
  import { addPluginTemplate, resolvePath, addTemplate, extendWebpackConfig, isNuxtMajorVersion, addImports, addPlugin, addVitePlugin, useLogger, defineNuxtModule, getNuxtVersion, findPath, hasNuxtModule, createResolver } from '@nuxt/kit';
4
- import { isAbsolute, resolve, dirname, relative } from 'pathe';
4
+ import { isAbsolute, resolve, dirname } from 'pathe';
5
5
  import semver from 'semver';
6
6
  import { createFilter, version as version$1 } from 'vite';
7
7
  import defu from 'defu';
8
8
  import { transformAssetUrls, generateImports } from '@vuetify/loader-shared';
9
9
  import Styles from '@vuetify/unplugin-styles/vite';
10
10
  import { isPackageExists } from 'local-pkg';
11
- import destr from 'destr';
12
11
  import { parseQuery, parseURL } from 'ufo';
13
12
  import { readFile } from 'node:fs/promises';
14
- import { debounce } from 'perfect-debounce';
15
13
  import process from 'node:process';
16
14
  import { createConfigLoader } from 'unconfig';
17
15
 
18
- const version = "1.0.0-beta.9";
16
+ const version = "1.0.0-rc.1";
19
17
 
20
18
  const VIRTUAL_VUETIFY_CONFIGURATION = "virtual:vuetify-configuration";
21
19
  const RESOLVED_VIRTUAL_VUETIFY_CONFIGURATION = `\0${VIRTUAL_VUETIFY_CONFIGURATION}`;
@@ -355,6 +353,11 @@ function vuetifyConfigurationPlugin(ctx) {
355
353
  },
356
354
  async load(id) {
357
355
  if (id === RESOLVED_VIRTUAL_VUETIFY_CONFIGURATION) {
356
+ if (ctx.isDev && ctx.canHmrConfig) {
357
+ for (const file of ctx.vuetifyFilesToWatch) {
358
+ this.addWatchFile(file);
359
+ }
360
+ }
358
361
  const {
359
362
  directives: _directives,
360
363
  date: _date,
@@ -376,7 +379,12 @@ function vuetifyConfigurationPlugin(ctx) {
376
379
  }
377
380
  const result = await buildConfiguration(ctx);
378
381
  const deepCopy = result.messages.length > 0;
379
- return `${result.imports}
382
+ let configDepImports = "";
383
+ if (ctx.isDev && ctx.canHmrConfig && this.environment?.name === "ssr") {
384
+ configDepImports = ctx.vuetifyFilesToWatch.map((file) => `import ${JSON.stringify(file)}`).join("\n");
385
+ }
386
+ return `${configDepImports}
387
+ ${result.imports}
380
388
 
381
389
  export const isDev = ${ctx.isDev}
382
390
  export function vuetifyConfiguration() {
@@ -670,6 +678,11 @@ function vuetifyDateConfigurationPlugin(ctx) {
670
678
  },
671
679
  async load(id) {
672
680
  if (id === RESOLVED_VIRTUAL_VUETIFY_DATE_CONFIGURATION) {
681
+ if (ctx.isDev && ctx.canHmrConfig) {
682
+ for (const file of ctx.vuetifyFilesToWatch) {
683
+ this.addWatchFile(file);
684
+ }
685
+ }
673
686
  if (!ctx.dateAdapter) {
674
687
  return `
675
688
  export const enabled = false
@@ -742,6 +755,11 @@ function vuetifyIconsPlugin(ctx) {
742
755
  },
743
756
  async load(id) {
744
757
  if (id === RESOLVED_VIRTUAL_VUETIFY_ICONS_CONFIGURATION) {
758
+ if (ctx.isDev && ctx.canHmrConfig) {
759
+ for (const file of ctx.vuetifyFilesToWatch) {
760
+ this.addWatchFile(file);
761
+ }
762
+ }
745
763
  const {
746
764
  enabled,
747
765
  unocss,
@@ -911,10 +929,24 @@ function parseId2(id) {
911
929
  id = id.replace(/^(virtual:nuxt:|virtual:)/, "");
912
930
  return parseURL(decodeURIComponent(isAbsolute(id) ? pathToFileURL(id).href : id));
913
931
  }
932
+ function reviver(key, value) {
933
+ if (key === "__proto__" || key === "constructor") {
934
+ return void 0;
935
+ }
936
+ return value;
937
+ }
938
+ function parseProps(value) {
939
+ try {
940
+ const parsed = JSON.parse(value, reviver);
941
+ return parsed && typeof parsed === "object" ? parsed : void 0;
942
+ } catch {
943
+ return void 0;
944
+ }
945
+ }
914
946
  function parseId(id) {
915
947
  const { search, pathname } = parseId2(id);
916
948
  const query = parseQuery(search);
917
- const urlProps = query.props ? destr(query.props) : void 0;
949
+ const urlProps = query.props ? parseProps(query.props) : void 0;
918
950
  return {
919
951
  query: urlProps,
920
952
  path: pathname ?? id
@@ -1386,14 +1418,8 @@ async function mergeVuetifyModules(options, nuxt) {
1386
1418
  options.vuetifyOptions
1387
1419
  );
1388
1420
  if (nuxt.options.dev && resolvedOptions.sources.length > 0) {
1389
- if (nuxt.options.ssr) {
1390
- for (const s of resolvedOptions.sources) {
1391
- nuxt.options.watch.push(s.replace(/\\/g, "/"));
1392
- }
1393
- } else {
1394
- for (const s of resolvedOptions.sources) {
1395
- vuetifyConfigurationFilesToWatch.add(s.replace(/\\/g, "/"));
1396
- }
1421
+ for (const s of resolvedOptions.sources) {
1422
+ vuetifyConfigurationFilesToWatch.add(s.replace(/\\/g, "/"));
1397
1423
  }
1398
1424
  }
1399
1425
  moduleOptions.unshift({
@@ -1510,7 +1536,7 @@ function prepareSSRClientHints(baseUrl, ctx) {
1510
1536
  return clientHints;
1511
1537
  }
1512
1538
 
1513
- async function load(options, nuxt, ctx) {
1539
+ async function load(options, nuxt, ctx, reload = false) {
1514
1540
  const {
1515
1541
  configuration,
1516
1542
  vuetifyConfigurationFilesToWatch
@@ -1555,9 +1581,11 @@ async function load(options, nuxt, ctx) {
1555
1581
  ctx.dateAdapter = date[0];
1556
1582
  }
1557
1583
  }
1558
- const oldIcons = ctx.icons;
1559
- if (oldIcons && oldIcons.cdn?.length && nuxt.options.app.head.link) {
1560
- nuxt.options.app.head.link = nuxt.options.app.head.link.filter((link) => !link.key || !oldIcons.cdn.some(([key]) => link.key === key));
1584
+ if (!reload) {
1585
+ const oldIcons = ctx.icons;
1586
+ if (oldIcons && oldIcons.cdn?.length && nuxt.options.app.head.link) {
1587
+ nuxt.options.app.head.link = nuxt.options.app.head.link.filter((link) => !link.key || !oldIcons.cdn.some(([key]) => link.key === key));
1588
+ }
1561
1589
  }
1562
1590
  ctx.moduleOptions = configuration.moduleOptions;
1563
1591
  ctx.vuetifyOptions = configuration.vuetifyOptions;
@@ -1569,7 +1597,7 @@ async function load(options, nuxt, ctx) {
1569
1597
  if (ctx.isSSR && !ctx.ssrClientHints.prefersColorScheme && ctx.vuetifyOptions.theme && typeof ctx.vuetifyOptions.theme === "object" && ctx.vuetifyOptions.theme.defaultTheme === "system") {
1570
1598
  ctx.logger.warn('`theme.defaultTheme: "system"` cannot be resolved during SSR/SSG: the server has no access to the OS color-scheme preference, so the first paint defaults to light and may flash on dark systems. Set explicit dark/light themes and enable `moduleOptions.ssrClientHints.prefersColorScheme` (optionally `prefersColorSchemeOptions.useBrowserThemeOnly`). See the SSR guide.');
1571
1599
  }
1572
- if (ctx.icons.enabled) {
1600
+ if (!reload && ctx.icons.enabled) {
1573
1601
  if (ctx.icons.local) {
1574
1602
  for (const css of ctx.icons.local) {
1575
1603
  nuxt.options.css.push(css);
@@ -1589,43 +1617,67 @@ async function load(options, nuxt, ctx) {
1589
1617
  }
1590
1618
  }
1591
1619
  }
1592
- function registerWatcher(options, nuxt, ctx) {
1593
- if (nuxt.options.dev) {
1594
- let pageReload;
1595
- nuxt.hooks.hook("builder:watch", (_event, path) => {
1596
- path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path));
1597
- if (!pageReload && ctx.vuetifyFilesToWatch.includes(path)) {
1598
- return nuxt.callHook("restart");
1599
- }
1600
- });
1601
- nuxt.hook("vite:serverCreated", (server, { isClient }) => {
1602
- if (!server.ws || !isClient) {
1603
- return;
1620
+ function bindInvalidator(graph) {
1621
+ return () => {
1622
+ for (const id of RESOLVED_VIRTUAL_MODULES) {
1623
+ const mod = graph.getModuleById(id);
1624
+ if (mod) {
1625
+ graph.invalidateModule(mod);
1604
1626
  }
1605
- pageReload = debounce(async () => {
1606
- const modules = [];
1607
- for (const v of RESOLVED_VIRTUAL_MODULES) {
1608
- const module = server.moduleGraph.getModuleById(v);
1609
- if (module) {
1610
- modules.push(module);
1611
- }
1612
- }
1613
- await load(options, nuxt, ctx);
1614
- if (modules.length > 0) {
1615
- await Promise.all(modules.map((m) => server.reloadModule(m)));
1616
- }
1617
- }, 50, { trailing: false });
1618
- });
1619
- addVitePlugin({
1620
- name: "vuetify:configuration:watch",
1621
- enforce: "pre",
1622
- handleHotUpdate({ file }) {
1623
- if (pageReload && ctx.vuetifyFilesToWatch.includes(file)) {
1624
- return pageReload();
1625
- }
1627
+ }
1628
+ };
1629
+ }
1630
+ function registerWatcher(options, nuxt, ctx) {
1631
+ if (!nuxt.options.dev) {
1632
+ return;
1633
+ }
1634
+ if (!ctx.canHmrConfig) {
1635
+ for (const file of ctx.vuetifyFilesToWatch) {
1636
+ nuxt.options.watch.push(file);
1637
+ }
1638
+ return;
1639
+ }
1640
+ let clientServer;
1641
+ let invalidateSsrModules;
1642
+ nuxt.hook("vite:serverCreated", (server, { isClient }) => {
1643
+ if (!isClient) {
1644
+ invalidateSsrModules = bindInvalidator(server.moduleGraph);
1645
+ return;
1646
+ }
1647
+ if (!server.ws) {
1648
+ return;
1649
+ }
1650
+ clientServer = server;
1651
+ const ssrEnv = server.environments?.ssr;
1652
+ if (ssrEnv) {
1653
+ invalidateSsrModules ??= bindInvalidator(ssrEnv.moduleGraph);
1654
+ }
1655
+ server.watcher.add(ctx.vuetifyFilesToWatch);
1656
+ });
1657
+ async function reloadConfig() {
1658
+ await load(options, nuxt, ctx, true);
1659
+ invalidateSsrModules?.();
1660
+ clientServer?.ws.send({ type: "full-reload" });
1661
+ }
1662
+ addVitePlugin({
1663
+ name: "vuetify:configuration:watch",
1664
+ enforce: "pre",
1665
+ async handleHotUpdate({ file }) {
1666
+ if (clientServer && ctx.vuetifyFilesToWatch.includes(file)) {
1667
+ await reloadConfig();
1668
+ return [];
1626
1669
  }
1627
- });
1670
+ }
1671
+ });
1672
+ }
1673
+
1674
+ const MIN_NUXT_VERSION_FOR_SSR_CONFIG_HMR = "3.18.0";
1675
+ function supportsSsrConfigHmr(nuxtVersion) {
1676
+ const parsed = semver.parse(nuxtVersion) ?? semver.coerce(nuxtVersion);
1677
+ if (!parsed) {
1678
+ return false;
1628
1679
  }
1680
+ return semver.gte(parsed.version, MIN_NUXT_VERSION_FOR_SSR_CONFIG_HMR);
1629
1681
  }
1630
1682
 
1631
1683
  const CONFIG_KEY = "vuetify";
@@ -1674,6 +1726,7 @@ const module$1 = defineNuxtModule({
1674
1726
  moduleOptions: void 0,
1675
1727
  vuetifyOptions: void 0,
1676
1728
  vuetifyFilesToWatch: [],
1729
+ canHmrConfig: !nuxt.options.ssr || supportsSsrConfigHmr(getNuxtVersion(nuxt)),
1677
1730
  isSSR: nuxt.options.ssr,
1678
1731
  isDev: nuxt.options.dev,
1679
1732
  isNuxtGenerate: !!nuxt.options.nitro.static,
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-beta.9",
4
+ "version": "1.0.0-rc.1",
5
5
  "description": "Zero-Config Nuxt Module for Vuetify",
6
6
  "author": "userquin <userquin@gmail.com>",
7
7
  "license": "MIT",
@@ -51,53 +51,52 @@
51
51
  "*.mjs"
52
52
  ],
53
53
  "dependencies": {
54
- "@nuxt/kit": "^4.3.1",
54
+ "@nuxt/kit": "^4.4.8",
55
55
  "@vuetify/loader-shared": "^2.1.2",
56
56
  "@vuetify/unplugin-styles": "^1.0.0-beta.11",
57
- "defu": "^6.1.4",
58
- "destr": "^2.0.5",
59
- "local-pkg": "^1.1.2",
57
+ "defu": "^6.1.7",
58
+ "local-pkg": "^1.2.1",
60
59
  "pathe": "^2.0.3",
61
60
  "perfect-debounce": "^2.1.0",
62
- "semver": "^7.7.4",
63
- "ufo": "^1.6.3",
61
+ "semver": "^7.8.4",
62
+ "ufo": "^1.6.4",
64
63
  "unconfig": "^7.5.0"
65
64
  },
66
65
  "peerDependencies": {
67
66
  "vuetify": "^3.4.0 || ^4.0.0"
68
67
  },
69
68
  "devDependencies": {
70
- "@antfu/eslint-config": "^7.6.1",
71
- "@antfu/ni": "^28.2.0",
69
+ "@antfu/eslint-config": "^7.7.3",
70
+ "@antfu/ni": "^28.3.0",
72
71
  "@date-io/luxon": "^3.2.0",
73
72
  "@fortawesome/fontawesome-svg-core": "^7.2.0",
74
73
  "@fortawesome/free-solid-svg-icons": "^7.2.0",
75
- "@fortawesome/vue-fontawesome": "^3.1.3",
76
- "@iconify-json/carbon": "^1.2.19",
74
+ "@fortawesome/vue-fontawesome": "^3.2.0",
75
+ "@iconify-json/carbon": "^1.2.23",
77
76
  "@iconify-json/mdi": "^1.2.3",
78
77
  "@mdi/js": "^7.4.47",
79
78
  "@nuxt/devtools": "latest",
80
79
  "@nuxt/module-builder": "^1.0.2",
81
- "@nuxt/schema": "^4.3.1",
82
- "@nuxt/test-utils": "^4.0.0",
83
- "@nuxtjs/i18n": "^10.2.3",
80
+ "@nuxt/schema": "^4.4.8",
81
+ "@nuxt/test-utils": "^4.0.3",
82
+ "@nuxtjs/i18n": "^10.4.0",
84
83
  "@parcel/watcher": "^2.5.6",
85
- "@types/node": "^25.3.3",
84
+ "@types/node": "^25.9.3",
86
85
  "@types/semver": "^7.7.1",
87
- "@unocss/nuxt": "^66.6.5",
88
- "bumpp": "^10.4.1",
89
- "eslint": "^10.0.2",
86
+ "@unocss/nuxt": "^66.7.2",
87
+ "bumpp": "^11.1.0",
88
+ "eslint": "^10.5.0",
90
89
  "luxon": "^3.7.2",
91
- "nuxt": "^4.3.1",
92
- "playwright-core": "^1.58.0",
93
- "publint": "^0.3.18",
90
+ "nuxt": "^4.4.8",
91
+ "playwright-core": "^1.61.0",
92
+ "publint": "^0.3.21",
94
93
  "rimraf": "^6.1.3",
95
- "sass": "^1.97.3",
94
+ "sass": "^1.101.0",
96
95
  "typescript": "^5.9.3",
97
- "vite": "7.3.1",
98
- "vitest": "^4.0.18",
99
- "vue-tsc": "^3.2.5",
100
- "vuetify": "^4.0.1"
96
+ "vite": "7.3.5",
97
+ "vitest": "^4.1.9",
98
+ "vue-tsc": "^3.3.5",
99
+ "vuetify": "^4.1.2"
101
100
  },
102
101
  "build": {
103
102
  "externals": [
@@ -106,7 +105,6 @@
106
105
  "node:child_process",
107
106
  "node:fs",
108
107
  "consola",
109
- "destr",
110
108
  "esbuild",
111
109
  "local-pkg",
112
110
  "pathe",