vuetify-nuxt-module 1.0.0-beta.4 → 1.0.0-beta.6

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/dist/module.d.mts CHANGED
@@ -251,6 +251,18 @@ interface MOptions {
251
251
  * Path to the custom Vuetify SASS configuration file.
252
252
  */
253
253
  configFile: string;
254
+ /**
255
+ * Caching options forwarded to `@vuetify/unplugin-styles`.
256
+ *
257
+ * @default true
258
+ */
259
+ cache?: boolean | {
260
+ path?: string;
261
+ sassOptions?: Record<string, unknown>;
262
+ };
263
+ /**
264
+ * @deprecated Use `styles.cache` instead.
265
+ */
254
266
  experimental?: {
255
267
  cache?: boolean;
256
268
  };
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.4",
7
+ "version": "1.0.0-beta.6",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -4,20 +4,19 @@ import semver from 'semver';
4
4
  import { createFilter, version as version$1 } from 'vite';
5
5
  import defu from 'defu';
6
6
  import { transformAssetUrls } from 'vite-plugin-vuetify';
7
- import { createHash } from 'node:crypto';
8
- import fs, { existsSync, readdirSync, rmSync, statSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
9
- import { isAbsolute, resolve, join, relative, dirname } from 'pathe';
7
+ import { existsSync, statSync } from 'node:fs';
8
+ import { isAbsolute, resolve, dirname, relative } from 'pathe';
9
+ import { generateImports, resolveVuetifyBase, isObject } from '@vuetify/loader-shared';
10
+ import Styles from '@vuetify/unplugin-styles/vite';
10
11
  import { pathToFileURL } from 'node:url';
11
- import { generateImports, isObject, resolveVuetifyBase, normalizePath } from '@vuetify/loader-shared';
12
12
  import destr from 'destr';
13
13
  import { parseQuery, parseURL } from 'ufo';
14
- import fsp, { readFile } from 'node:fs/promises';
15
- import path from 'upath';
14
+ import { readFile } from 'node:fs/promises';
16
15
  import { debounce } from 'perfect-debounce';
17
16
  import process from 'node:process';
18
17
  import { createConfigLoader } from 'unconfig';
19
18
 
20
- const version = "1.0.0-beta.4";
19
+ const version = "1.0.0-beta.6";
21
20
 
22
21
  const VIRTUAL_VUETIFY_CONFIGURATION = "virtual:vuetify-configuration";
23
22
  const RESOLVED_VIRTUAL_VUETIFY_CONFIGURATION = `\0${VIRTUAL_VUETIFY_CONFIGURATION}`;
@@ -138,52 +137,6 @@ function resolveVuetifyConfigFile(configFile, nuxt) {
138
137
  }
139
138
  return configFile;
140
139
  }
141
- function createStylesCacheHash(vuetifyVersion, viteVersion, configContent, configFile) {
142
- return createHash("sha256").update(vuetifyVersion).update(viteVersion).update(configContent).update(configFile).digest("hex").slice(0, 8);
143
- }
144
- function resolveStylesCachePaths(rootDir, hash) {
145
- const stylesDir = resolve(rootDir, "node_modules/.cache/vuetify-nuxt-module/styles");
146
- const cacheDir = join(stylesDir, hash);
147
- return {
148
- stylesDir,
149
- cacheDir
150
- };
151
- }
152
- function cleanupOldStylesCaches(stylesDir, currentHash) {
153
- if (!existsSync(stylesDir)) {
154
- return;
155
- }
156
- const dirents = readdirSync(stylesDir, { withFileTypes: true });
157
- for (const dirent of dirents) {
158
- if (dirent.isDirectory() && dirent.name !== currentHash) {
159
- rmSync(join(stylesDir, dirent.name), { recursive: true, force: true });
160
- }
161
- }
162
- }
163
- function collectVuetifyCssFiles(vuetifyBase) {
164
- const files = [];
165
- findCssFiles(join(vuetifyBase, "lib/components"), files);
166
- findCssFiles(join(vuetifyBase, "lib/styles"), files);
167
- return files;
168
- }
169
- function findCssFiles(dir, fileList = []) {
170
- if (!existsSync(dir)) {
171
- return fileList;
172
- }
173
- const files = readdirSync(dir);
174
- for (const file of files) {
175
- const filePath = join(dir, file);
176
- const stat = statSync(filePath);
177
- if (stat.isDirectory()) {
178
- findCssFiles(filePath, fileList);
179
- } else {
180
- if (file.endsWith(".css")) {
181
- fileList.push(filePath);
182
- }
183
- }
184
- }
185
- return fileList;
186
- }
187
140
 
188
141
  function addVuetifyNuxtPlugins(nuxt, ctx) {
189
142
  addVuetifyNuxtPlugin(nuxt, ctx, "client");
@@ -271,7 +224,11 @@ async function configureNuxt(configKey, nuxt, ctx) {
271
224
  const configFile = resolveVuetifyConfigFile(styles.configFile, nuxt);
272
225
  ctx.stylesConfigFile = await resolvePath(configFile);
273
226
  const a = addTemplate({
274
- filename: "vuetify.settings.scss",
227
+ // Write to disk: without `write` Nuxt serves the template from its
228
+ // virtual FS, which 404s on Windows/SSR when the browser requests the
229
+ // real file (#363). Nest under `vuetify/` to match unplugin-styles.
230
+ write: true,
231
+ filename: "vuetify/vuetify.settings.scss",
275
232
  getContents: async () => getTemplate("vuetify/styles", ctx.stylesConfigFile)
276
233
  });
277
234
  nuxt.options.css.push(a.dst);
@@ -948,125 +905,6 @@ function vuetifySSRClientHintsPlugin(ctx) {
948
905
  };
949
906
  }
950
907
 
951
- function vuetifyStylesPlugin(ctx) {
952
- let configFile;
953
- let vuetifyBase;
954
- const options = { styles: ctx.moduleOptions.styles };
955
- const noneFiles = /* @__PURE__ */ new Set();
956
- let isNone = false;
957
- let sassVariables = false;
958
- let fileImport = false;
959
- const PREFIX = "vuetify-styles/";
960
- const SSR_PREFIX = `/@${PREFIX}`;
961
- const resolveCss = resolveCssFactory();
962
- const toPath = (file) => fileImport ? pathToFileURL(file).href : normalizePath(file);
963
- return {
964
- name: "vuetify:styles:nuxt",
965
- enforce: "pre",
966
- async configResolved(config) {
967
- if (config.plugins.some((plugin) => plugin.name === "vuetify:styles")) {
968
- throw new Error("Remove vite-plugin-vuetify from your Nuxt config file, this module registers a modified version.");
969
- }
970
- if (isObject(options.styles) && "configFile" in options.styles) {
971
- sassVariables = true;
972
- fileImport = semver.gt(ctx.viteVersion, "5.4.2");
973
- configFile = ctx.stylesConfigFile ?? await resolvePath(options.styles.configFile);
974
- } else {
975
- isNone = options.styles === "none";
976
- }
977
- vuetifyBase = await resolveVuetifyBase();
978
- },
979
- async resolveId(source, importer, { custom, ssr }) {
980
- if (!sassVariables) {
981
- return;
982
- }
983
- if (source.startsWith(PREFIX) || source.startsWith(SSR_PREFIX)) {
984
- if (/\.s[ca]ss$/.test(source)) {
985
- return source;
986
- }
987
- const idx = source.indexOf("?");
988
- return idx === -1 ? source : source.slice(0, idx);
989
- }
990
- if (vuetifyBase && importer && source.endsWith(".css") && isSubdir(vuetifyBase, path.isAbsolute(source) ? source : importer)) {
991
- let resolutionId;
992
- if (source.startsWith(".")) {
993
- resolutionId = path.resolve(path.dirname(importer), source);
994
- } else if (path.isAbsolute(source)) {
995
- resolutionId = source;
996
- } else {
997
- const resolution = await this.resolve(source, importer, { skipSelf: true, custom });
998
- if (resolution) {
999
- resolutionId = resolution.id;
1000
- }
1001
- }
1002
- if (!resolutionId) {
1003
- return;
1004
- }
1005
- const target = await resolveCss(resolutionId);
1006
- if (isNone) {
1007
- noneFiles.add(target);
1008
- return target;
1009
- }
1010
- if (ctx.stylesCachePath) {
1011
- const relative = path.relative(vuetifyBase, target);
1012
- const cacheFile = path.resolve(ctx.stylesCachePath, relative.replace(/\.s[ac]ss$/, ".css"));
1013
- if (fs.existsSync(cacheFile)) {
1014
- return cacheFile;
1015
- }
1016
- }
1017
- return `${ssr ? SSR_PREFIX : PREFIX}${path.relative(vuetifyBase, target)}`;
1018
- }
1019
- return void 0;
1020
- },
1021
- load(id) {
1022
- if (sassVariables) {
1023
- if (!vuetifyBase) {
1024
- return;
1025
- }
1026
- 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;
1027
- if (target) {
1028
- const suffix = /\.scss/.test(target) ? ";\n" : "\n";
1029
- return {
1030
- code: `@use "${toPath(configFile)}"${suffix}@use "${toPath(target)}"${suffix}`,
1031
- map: {
1032
- mappings: ""
1033
- }
1034
- };
1035
- }
1036
- }
1037
- return isNone && noneFiles.has(id) ? "" : void 0;
1038
- }
1039
- };
1040
- }
1041
- function resolveCssFactory() {
1042
- const mappings = /* @__PURE__ */ new Map();
1043
- return async (source) => {
1044
- let mapping = mappings.get(source);
1045
- if (!mapping) {
1046
- try {
1047
- mapping = source.replace(/\.css$/, ".sass");
1048
- await fsp.access(mapping, fs.constants.R_OK);
1049
- } catch (error) {
1050
- if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) {
1051
- throw error;
1052
- }
1053
- try {
1054
- mapping = source.replace(/\.css$/, ".scss");
1055
- await fsp.access(mapping, fs.constants.R_OK);
1056
- } catch {
1057
- mapping = source;
1058
- }
1059
- }
1060
- mappings.set(source, mapping);
1061
- }
1062
- return mapping;
1063
- };
1064
- }
1065
- function isSubdir(root, test) {
1066
- const relative$1 = relative(root, test);
1067
- return relative$1 && !relative$1.startsWith("..") && !isAbsolute(relative$1);
1068
- }
1069
-
1070
908
  function detectDate() {
1071
909
  const result = [];
1072
910
  for (const adapter of [
@@ -1124,6 +962,9 @@ function resolveVuetifyComponents(resolver) {
1124
962
  }
1125
963
  }
1126
964
 
965
+ function resolveStylesCache(stylesOption) {
966
+ return stylesOption.cache ?? stylesOption.experimental?.cache;
967
+ }
1127
968
  function configureVite(configKey, nuxt, ctx) {
1128
969
  nuxt.hook("vite:extend", ({ config }) => checkVuetifyPlugins(config));
1129
970
  nuxt.hook("vite:extendConfig", (viteInlineConfig) => {
@@ -1176,8 +1017,18 @@ function configureVite(configKey, nuxt, ctx) {
1176
1017
  autoImport.ignore = Array.isArray(ignoreDirectives) ? ignoreDirectives : [ignoreDirectives];
1177
1018
  }
1178
1019
  viteInlineConfig.plugins.push(vuetifyImportPlugin({ autoImport }));
1179
- if (ctx.moduleOptions.styles !== false && ctx.moduleOptions.styles !== "none") {
1180
- viteInlineConfig.plugins.push(vuetifyStylesPlugin(ctx));
1020
+ const stylesOption = ctx.moduleOptions.styles;
1021
+ if (stylesOption === "none") {
1022
+ viteInlineConfig.plugins.push(Styles({ styles: "none" }));
1023
+ } else if (isObject(stylesOption) && "configFile" in stylesOption) {
1024
+ if (!ctx.stylesConfigFile) {
1025
+ throw new Error("vuetify-nuxt-module: styles.configFile could not be resolved");
1026
+ }
1027
+ const cache = resolveStylesCache(stylesOption);
1028
+ viteInlineConfig.plugins.push(Styles({
1029
+ settings: ctx.stylesConfigFile,
1030
+ ...cache === void 0 ? {} : { cache }
1031
+ }));
1181
1032
  }
1182
1033
  viteInlineConfig.plugins.push(vuetifyConfigurationPlugin(ctx), vuetifyIconsPlugin(ctx), vuetifyDateConfigurationPlugin(ctx));
1183
1034
  if (ctx.ssrClientHints.enabled) {
@@ -1556,102 +1407,6 @@ function prepareSSRClientHints(baseUrl, ctx) {
1556
1407
  return clientHints;
1557
1408
  }
1558
1409
 
1559
- async function prepareVuetifyStyles(nuxt, ctx) {
1560
- const stylesConfig = ctx.moduleOptions.styles;
1561
- if (!isObject(stylesConfig) || !("configFile" in stylesConfig)) {
1562
- return;
1563
- }
1564
- if (stylesConfig.experimental?.cache === false) {
1565
- return;
1566
- }
1567
- const vuetifyBase = await resolveVuetifyBase();
1568
- let configFile;
1569
- let configContent = "";
1570
- if (stylesConfig.configFile) {
1571
- configFile = await resolvePath(resolveVuetifyConfigFile(stylesConfig.configFile, nuxt));
1572
- ctx.stylesConfigFile = configFile;
1573
- if (existsSync(configFile)) {
1574
- configContent = readFileSync(configFile, "utf8");
1575
- if (!ctx.vuetifyFilesToWatch.includes(configFile)) {
1576
- ctx.vuetifyFilesToWatch.push(configFile);
1577
- }
1578
- }
1579
- }
1580
- if (!configFile) {
1581
- return;
1582
- }
1583
- const hash = createStylesCacheHash(
1584
- ctx.vuetifyVersion,
1585
- ctx.viteVersion,
1586
- configContent,
1587
- configFile
1588
- );
1589
- const { stylesDir, cacheDir } = resolveStylesCachePaths(nuxt.options.rootDir, hash);
1590
- ctx.stylesCachePath = cacheDir;
1591
- cleanupOldStylesCaches(stylesDir, hash);
1592
- if (existsSync(cacheDir)) {
1593
- return;
1594
- }
1595
- ctx.logger.info("Compiling Vuetify styles...");
1596
- let sass;
1597
- try {
1598
- sass = await import('sass');
1599
- } catch {
1600
- try {
1601
- sass = await import('sass-embedded');
1602
- } catch {
1603
- ctx.logger.warn('Could not load "sass" or "sass-embedded". Skipping styles pre-compilation.');
1604
- return;
1605
- }
1606
- }
1607
- const files = collectVuetifyCssFiles(vuetifyBase);
1608
- for (const file of files) {
1609
- const relativePath = relative(vuetifyBase, file);
1610
- const cacheFile = join(cacheDir, relativePath);
1611
- const sassFile = file.replace(/\.css$/, ".sass");
1612
- const scssFile = file.replace(/\.css$/, ".scss");
1613
- let targetFile;
1614
- if (existsSync(sassFile)) {
1615
- targetFile = sassFile;
1616
- } else if (existsSync(scssFile)) {
1617
- targetFile = scssFile;
1618
- }
1619
- if (targetFile) {
1620
- const dir = dirname(cacheFile);
1621
- if (!existsSync(dir)) {
1622
- mkdirSync(dir, { recursive: true });
1623
- }
1624
- const content = `@use "${normalizePath(configFile)}";
1625
- @use "${normalizePath(targetFile)}";
1626
- `;
1627
- try {
1628
- const result = sass.compileString(content, {
1629
- loadPaths: [
1630
- dirname(configFile),
1631
- dirname(targetFile),
1632
- resolve(vuetifyBase, ".."),
1633
- resolve(vuetifyBase, "../.."),
1634
- // In case of monorepo/hoisting issues, but standard is enough
1635
- vuetifyBase
1636
- ],
1637
- url: new URL(pathToFileURL(cacheFile).href)
1638
- });
1639
- writeFileSync(cacheFile, result.css, "utf8");
1640
- } catch (error) {
1641
- ctx.logger.error(`Failed to compile ${targetFile}:`, error);
1642
- }
1643
- }
1644
- }
1645
- const metadata = {
1646
- hash,
1647
- vuetifyVersion: ctx.vuetifyVersion,
1648
- viteVersion: ctx.viteVersion,
1649
- configFile,
1650
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
1651
- };
1652
- writeFileSync(join(cacheDir, "metadata.json"), JSON.stringify(metadata, null, 2), "utf8");
1653
- }
1654
-
1655
1410
  async function load(options, nuxt, ctx) {
1656
1411
  const {
1657
1412
  configuration,
@@ -1727,7 +1482,6 @@ async function load(options, nuxt, ctx) {
1727
1482
  }
1728
1483
  }
1729
1484
  }
1730
- await prepareVuetifyStyles(nuxt, ctx);
1731
1485
  }
1732
1486
  function registerWatcher(options, nuxt, ctx) {
1733
1487
  if (nuxt.options.dev) {
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.4",
4
+ "version": "1.0.0-beta.6",
5
5
  "description": "Zero-Config Nuxt Module for Vuetify",
6
6
  "author": "userquin <userquin@gmail.com>",
7
7
  "license": "MIT",
@@ -52,6 +52,7 @@
52
52
  ],
53
53
  "dependencies": {
54
54
  "@nuxt/kit": "^4.3.1",
55
+ "@vuetify/unplugin-styles": "^1.0.0-beta.11",
55
56
  "defu": "^6.1.4",
56
57
  "destr": "^2.0.5",
57
58
  "local-pkg": "^1.1.2",
@@ -60,7 +61,6 @@
60
61
  "semver": "^7.7.4",
61
62
  "ufo": "^1.6.3",
62
63
  "unconfig": "^7.5.0",
63
- "upath": "^2.0.1",
64
64
  "vite-plugin-vuetify": "^2.1.3",
65
65
  "vuetify": "^4.0.1"
66
66
  },
@@ -87,6 +87,7 @@
87
87
  "eslint": "^10.0.2",
88
88
  "luxon": "^3.7.2",
89
89
  "nuxt": "^4.3.1",
90
+ "playwright-core": "^1.58.0",
90
91
  "publint": "^0.3.18",
91
92
  "rimraf": "^6.1.3",
92
93
  "sass": "^1.97.3",
@@ -98,6 +99,7 @@
98
99
  "build": {
99
100
  "externals": [
100
101
  "@vuetify/loader-shared",
102
+ "@vuetify/unplugin-styles",
101
103
  "node:child_process",
102
104
  "node:fs",
103
105
  "consola",
@@ -109,7 +111,6 @@
109
111
  "rollup",
110
112
  "sass",
111
113
  "sass-embedded",
112
- "upath",
113
114
  "ufo",
114
115
  "unconfig",
115
116
  "vite",
@@ -139,8 +140,9 @@
139
140
  "lint": "eslint .",
140
141
  "lint:fix": "nr lint --fix",
141
142
  "publint": "publint",
142
- "test": "vitest run",
143
- "test:watch": "vitest watch",
143
+ "test": "vitest run --exclude 'test/e2e/**'",
144
+ "test:watch": "vitest watch --exclude 'test/e2e/**'",
145
+ "test:e2e": "vitest run test/e2e",
144
146
  "release": "bumpp"
145
147
  }
146
148
  }