vuetify-nuxt-module 1.0.0-beta.5 → 1.0.0-beta.7

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.
@@ -36,6 +36,9 @@ declare module 'virtual:vuetify-ssr-client-hints-configuration' {
36
36
  defaultTheme: string
37
37
  themeNames: string[]
38
38
  cookieName: string
39
+ cookieDomain?: string
40
+ cookieSecure?: boolean
41
+ cookieSameSite: 'lax' | 'strict' | 'none'
39
42
  darkThemeName: string
40
43
  lightThemeName: string
41
44
  useBrowserThemeOnly: boolean
package/dist/module.d.mts CHANGED
@@ -362,9 +362,43 @@ interface MOptions {
362
362
  /**
363
363
  * The name for the cookie.
364
364
  *
365
+ * @deprecated Use `cookie.name` instead.
365
366
  * @default 'color-scheme'
366
367
  */
367
368
  cookieName?: string;
369
+ /**
370
+ * Cookie attributes for the color scheme cookie.
371
+ */
372
+ cookie?: {
373
+ /**
374
+ * The name for the cookie.
375
+ *
376
+ * @default 'color-scheme'
377
+ */
378
+ name?: string;
379
+ /**
380
+ * The domain for the color scheme cookie.
381
+ *
382
+ * Useful to share the cookie across subdomains, e.g. `.example.com`.
383
+ *
384
+ * @default undefined
385
+ */
386
+ domain?: string;
387
+ /**
388
+ * Mark the cookie as `Secure`.
389
+ *
390
+ * Forced to `true` when `sameSite` is `'none'`.
391
+ *
392
+ * @default undefined
393
+ */
394
+ secure?: boolean;
395
+ /**
396
+ * The `SameSite` attribute for the cookie.
397
+ *
398
+ * @default 'lax'
399
+ */
400
+ sameSite?: 'lax' | 'strict' | 'none';
401
+ };
368
402
  /**
369
403
  * The name for the dark theme.
370
404
  *
@@ -459,6 +493,9 @@ interface SSRClientHintsConfiguration {
459
493
  defaultTheme: string;
460
494
  themeNames: string[];
461
495
  cookieName: string;
496
+ cookieDomain?: string;
497
+ cookieSecure?: boolean;
498
+ cookieSameSite: 'lax' | 'strict' | 'none';
462
499
  darkThemeName: string;
463
500
  lightThemeName: string;
464
501
  };
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.5",
7
+ "version": "1.0.0-beta.7",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -1,14 +1,14 @@
1
- import { addPluginTemplate, resolvePath, addTemplate, extendWebpackConfig, isNuxtMajorVersion, addImports, addPlugin, addVitePlugin, useLogger, defineNuxtModule, getNuxtVersion, hasNuxtModule, createResolver } from '@nuxt/kit';
2
- import { isPackageExists, getPackageInfo } from 'local-pkg';
1
+ import { existsSync, statSync, readFileSync } from 'node:fs';
2
+ import { pathToFileURL, fileURLToPath } from 'node:url';
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';
3
5
  import semver from 'semver';
4
6
  import { createFilter, version as version$1 } from 'vite';
5
7
  import defu from 'defu';
6
8
  import { transformAssetUrls } from 'vite-plugin-vuetify';
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
9
  import Styles from '@vuetify/unplugin-styles/vite';
11
- import { pathToFileURL } from 'node:url';
10
+ import { isPackageExists } from 'local-pkg';
11
+ import { generateImports } from '@vuetify/loader-shared';
12
12
  import destr from 'destr';
13
13
  import { parseQuery, parseURL } from 'ufo';
14
14
  import { readFile } from 'node:fs/promises';
@@ -16,7 +16,7 @@ import { debounce } from 'perfect-debounce';
16
16
  import process from 'node:process';
17
17
  import { createConfigLoader } from 'unconfig';
18
18
 
19
- const version = "1.0.0-beta.5";
19
+ const version = "1.0.0-beta.7";
20
20
 
21
21
  const VIRTUAL_VUETIFY_CONFIGURATION = "virtual:vuetify-configuration";
22
22
  const RESOLVED_VIRTUAL_VUETIFY_CONFIGURATION = `\0${VIRTUAL_VUETIFY_CONFIGURATION}`;
@@ -224,7 +224,11 @@ async function configureNuxt(configKey, nuxt, ctx) {
224
224
  const configFile = resolveVuetifyConfigFile(styles.configFile, nuxt);
225
225
  ctx.stylesConfigFile = await resolvePath(configFile);
226
226
  const a = addTemplate({
227
- 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",
228
232
  getContents: async () => getTemplate("vuetify/styles", ctx.stylesConfigFile)
229
233
  });
230
234
  nuxt.options.css.push(a.dst);
@@ -901,7 +905,7 @@ function vuetifySSRClientHintsPlugin(ctx) {
901
905
  };
902
906
  }
903
907
 
904
- function detectDate() {
908
+ function detectDate(paths) {
905
909
  const result = [];
906
910
  for (const adapter of [
907
911
  "date-fns",
@@ -913,12 +917,15 @@ function detectDate() {
913
917
  "jalaali",
914
918
  "hijri"
915
919
  ]) {
916
- if (isPackageExists(`@date-io/${adapter}`)) {
920
+ if (isPackageExists(`@date-io/${adapter}`, { paths })) {
917
921
  result.push(adapter);
918
922
  }
919
923
  }
920
924
  return result;
921
925
  }
926
+ function multipleDateAdaptersError(adapters) {
927
+ return `Multiple date adapters found: ${adapters.map((adapter) => `@date-io/${adapter}`).join(", ")}, please specify the adapter to use in the "vuetifyOptions.date.adapter" option.`;
928
+ }
922
929
  function cleanupBlueprint(vuetifyOptions) {
923
930
  const blueprint = vuetifyOptions.blueprint;
924
931
  if (blueprint) {
@@ -941,12 +948,10 @@ function checkVuetifyPlugins(config) {
941
948
  throw new Error("Remove vite-plugin-vuetify plugin from Vite Plugins entry in Nuxt config file!");
942
949
  }
943
950
  }
944
- function resolveVuetifyComponents(resolver) {
945
- const vuetifyBase = resolveVuetifyBase();
951
+ function resolveVuetifyComponents(resolver, vuetifyBase) {
946
952
  const componentsPromise = importMapResolver();
947
953
  const labComponentsPromise = importMapLabResolver();
948
954
  return {
949
- vuetifyBase,
950
955
  componentsPromise,
951
956
  labComponentsPromise
952
957
  };
@@ -969,7 +974,7 @@ function configureVite(configKey, nuxt, ctx) {
969
974
  if (!ctx.moduleOptions.disableModernSassCompiler) {
970
975
  const enableModernSassCompiler = semver.gte(ctx.viteVersion, "5.4.0") && semver.lt(ctx.viteVersion, "7.0.0-0");
971
976
  if (enableModernSassCompiler) {
972
- const sassEmbedded = isPackageExists("sass-embedded");
977
+ const sassEmbedded = isPackageExists("sass-embedded", { paths: ctx.resolvePaths });
973
978
  if (sassEmbedded) {
974
979
  viteInlineConfig.css ??= {};
975
980
  viteInlineConfig.css.preprocessorOptions ??= {};
@@ -1016,7 +1021,7 @@ function configureVite(configKey, nuxt, ctx) {
1016
1021
  const stylesOption = ctx.moduleOptions.styles;
1017
1022
  if (stylesOption === "none") {
1018
1023
  viteInlineConfig.plugins.push(Styles({ styles: "none" }));
1019
- } else if (isObject(stylesOption) && "configFile" in stylesOption) {
1024
+ } else if (typeof stylesOption === "object" && stylesOption !== null && "configFile" in stylesOption) {
1020
1025
  if (!ctx.stylesConfigFile) {
1021
1026
  throw new Error("vuetify-nuxt-module: styles.configFile could not be resolved");
1022
1027
  }
@@ -1063,7 +1068,8 @@ const disabledResolvedIcons = Object.freeze({
1063
1068
  local: [],
1064
1069
  svg: {}
1065
1070
  });
1066
- function prepareIcons(unocssPresent, logger, vuetifyOptions) {
1071
+ function prepareIcons(unocssPresent, logger, vuetifyOptions, paths) {
1072
+ const iconPackageExists = (name) => isPackageExists(name, { paths });
1067
1073
  if (vuetifyOptions.icons === false) {
1068
1074
  return disabledResolvedIcons;
1069
1075
  }
@@ -1107,7 +1113,7 @@ function prepareIcons(unocssPresent, logger, vuetifyOptions) {
1107
1113
  }
1108
1114
  resolvedIcons.imports.push(`import {${name === defaultSet ? "aliases," : ""}${name}} from 'vuetify/iconsets/${name}'`);
1109
1115
  resolvedIcons.sets.push(name);
1110
- if (isPackageExists(iconsPackageNames[name].name)) {
1116
+ if (iconPackageExists(iconsPackageNames[name].name)) {
1111
1117
  resolvedIcons.local.push(iconsPackageNames[name].css);
1112
1118
  } else {
1113
1119
  resolvedIcons.cdn.push([name, cdn ?? iconsCDN[name]]);
@@ -1126,18 +1132,18 @@ function prepareIcons(unocssPresent, logger, vuetifyOptions) {
1126
1132
  if (!faSvg) {
1127
1133
  faSvg = {};
1128
1134
  }
1129
- let faSvgExists = isPackageExists("@fortawesome/fontawesome-svg-core");
1135
+ let faSvgExists = iconPackageExists("@fortawesome/fontawesome-svg-core");
1130
1136
  if (!faSvgExists) {
1131
1137
  logger.warn("Missing @fortawesome/fontawesome-svg-core dependency, install it!");
1132
1138
  }
1133
- faSvgExists = isPackageExists("@fortawesome/vue-fontawesome");
1139
+ faSvgExists = iconPackageExists("@fortawesome/vue-fontawesome");
1134
1140
  if (faSvgExists) {
1135
1141
  if (!faSvg.libraries?.length) {
1136
1142
  faSvg.libraries = [[false, "fas", "@fortawesome/free-solid-svg-icons"]];
1137
1143
  }
1138
1144
  for (const p in faSvg.libraries) {
1139
1145
  const [_defaultExport, _name, library] = faSvg.libraries[p];
1140
- if (!isPackageExists(library)) {
1146
+ if (!iconPackageExists(library)) {
1141
1147
  faSvgExists = false;
1142
1148
  logger.warn(`Missing library ${library} dependency, install it!`);
1143
1149
  }
@@ -1164,7 +1170,7 @@ function prepareIcons(unocssPresent, logger, vuetifyOptions) {
1164
1170
  if (!mdiSvg) {
1165
1171
  mdiSvg = {};
1166
1172
  }
1167
- const mdiSvgExists = isPackageExists("@mdi/js");
1173
+ const mdiSvgExists = iconPackageExists("@mdi/js");
1168
1174
  if (mdiSvgExists) {
1169
1175
  resolvedIcons.svg.mdi = true;
1170
1176
  resolvedIcons.aliasesImportPresent ||= defaultSet === "mdi-svg";
@@ -1350,6 +1356,18 @@ const disabledClientHints = Object.freeze({
1350
1356
  prefersColorScheme: false,
1351
1357
  prefersReducedMotion: false
1352
1358
  });
1359
+ function resolveColorSchemeCookie(options, logger) {
1360
+ if (options.cookieName !== void 0) {
1361
+ logger.warn("[vuetify-nuxt-module] `prefersColorSchemeOptions.cookieName` is deprecated, use `prefersColorSchemeOptions.cookie.name` instead.");
1362
+ }
1363
+ const cookieSameSite = options.cookie?.sameSite ?? "lax";
1364
+ return {
1365
+ cookieName: options.cookie?.name ?? options.cookieName ?? "color-scheme",
1366
+ cookieDomain: options.cookie?.domain,
1367
+ cookieSecure: cookieSameSite === "none" ? true : options.cookie?.secure,
1368
+ cookieSameSite
1369
+ };
1370
+ }
1353
1371
  function prepareSSRClientHints(baseUrl, ctx) {
1354
1372
  if (!ctx.isSSR || ctx.isNuxtGenerate) {
1355
1373
  return disabledClientHints;
@@ -1390,14 +1408,15 @@ function prepareSSRClientHints(baseUrl, ctx) {
1390
1408
  if (darkThemeName === lightThemeName) {
1391
1409
  throw new Error("Vuetify dark theme and light theme are the same, change darkThemeName or lightThemeName!");
1392
1410
  }
1411
+ const pcsOptions = ssrClientHintsConfiguration.prefersColorSchemeOptions;
1393
1412
  clientHints.prefersColorSchemeOptions = {
1394
1413
  baseUrl,
1395
1414
  defaultTheme,
1396
1415
  themeNames: Array.from(Object.keys(themes)),
1397
- cookieName: ssrClientHintsConfiguration.prefersColorSchemeOptions?.cookieName ?? "color-scheme",
1416
+ ...resolveColorSchemeCookie(pcsOptions, ctx.logger),
1398
1417
  darkThemeName,
1399
1418
  lightThemeName,
1400
- useBrowserThemeOnly: ssrClientHintsConfiguration.prefersColorSchemeOptions?.useBrowserThemeOnly ?? false
1419
+ useBrowserThemeOnly: pcsOptions?.useBrowserThemeOnly ?? false
1401
1420
  };
1402
1421
  }
1403
1422
  return clientHints;
@@ -1412,7 +1431,7 @@ async function load(options, nuxt, ctx) {
1412
1431
  const {
1413
1432
  componentsPromise,
1414
1433
  labComponentsPromise
1415
- } = resolveVuetifyComponents(ctx.resolver);
1434
+ } = resolveVuetifyComponents(ctx.resolver, ctx.vuetifyBase);
1416
1435
  ctx.componentsPromise = componentsPromise;
1417
1436
  ctx.labComponentsPromise = labComponentsPromise;
1418
1437
  }
@@ -1428,9 +1447,9 @@ async function load(options, nuxt, ctx) {
1428
1447
  const dateOptions = vuetifyOptions.date;
1429
1448
  if (dateOptions) {
1430
1449
  const adapter = dateOptions.adapter;
1431
- const date = detectDate();
1450
+ const date = detectDate(ctx.resolvePaths);
1432
1451
  if (!adapter && date.length > 1) {
1433
- 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.`);
1452
+ throw new Error(multipleDateAdaptersError(date));
1434
1453
  }
1435
1454
  if (adapter) {
1436
1455
  if (adapter === "vuetify" || adapter === "custom") {
@@ -1457,7 +1476,7 @@ async function load(options, nuxt, ctx) {
1457
1476
  ctx.enableRules = ctx.moduleOptions.enableRules;
1458
1477
  ctx.rulesConfiguration = ctx.moduleOptions.rulesConfiguration;
1459
1478
  ctx.vuetifyFilesToWatch = Array.from(vuetifyConfigurationFilesToWatch);
1460
- ctx.icons = prepareIcons(ctx.unocss, ctx.logger, vuetifyAppOptions);
1479
+ ctx.icons = prepareIcons(ctx.unocss, ctx.logger, vuetifyAppOptions, ctx.resolvePaths);
1461
1480
  ctx.ssrClientHints = prepareSSRClientHints(nuxt.options.app.baseURL ?? "/", ctx);
1462
1481
  if (ctx.icons.enabled) {
1463
1482
  if (ctx.icons.local) {
@@ -1557,8 +1576,23 @@ const module$1 = defineNuxtModule({
1557
1576
  if (isNuxtMajorVersion(2, nuxt)) {
1558
1577
  logger.error(`Cannot support nuxt version: ${getNuxtVersion(nuxt)}`);
1559
1578
  }
1560
- const vuetifyPkg = await getPackageInfo("vuetify");
1561
- const currentVersion = vuetifyPkg?.version;
1579
+ let vuetifyPkgPath = await findPath("vuetify/package.json");
1580
+ if (!vuetifyPkgPath) {
1581
+ const metaResolve = import.meta.resolve;
1582
+ if (metaResolve) {
1583
+ try {
1584
+ vuetifyPkgPath = fileURLToPath(metaResolve("vuetify/package.json"));
1585
+ } catch {
1586
+ }
1587
+ }
1588
+ }
1589
+ if (!vuetifyPkgPath) {
1590
+ throw new Error(
1591
+ '[vuetify-nuxt-module] Could not resolve "vuetify". Please add it as a dependency of your project (e.g. `npm add vuetify`).'
1592
+ );
1593
+ }
1594
+ const vuetifyBase = dirname(vuetifyPkgPath);
1595
+ const currentVersion = JSON.parse(readFileSync(vuetifyPkgPath, "utf8")).version;
1562
1596
  const vuetifyGte = (version2) => !!currentVersion && semver.gte(currentVersion, version2);
1563
1597
  const viteVersion = version$1;
1564
1598
  const ctx = {
@@ -1578,6 +1612,8 @@ const module$1 = defineNuxtModule({
1578
1612
  labComponentsPromise: void 0,
1579
1613
  vuetifyGte,
1580
1614
  vuetifyVersion: currentVersion || "0.0.0",
1615
+ vuetifyBase,
1616
+ resolvePaths: [nuxt.options.rootDir, ...nuxt.options.modulesDir],
1581
1617
  viteVersion
1582
1618
  };
1583
1619
  await load(options, nuxt, ctx);
@@ -119,12 +119,21 @@ function useSSRClientHints() {
119
119
  const {
120
120
  baseUrl,
121
121
  cookieName,
122
+ cookieDomain,
123
+ cookieSecure,
124
+ cookieSameSite,
122
125
  defaultTheme
123
126
  } = ssrClientHintsConfiguration.prefersColorSchemeOptions;
124
127
  const cookieNamePrefix = `${cookieName}=`;
125
128
  initial.value.colorSchemeFromCookie = document.cookie?.split(";")?.find((c) => c.trim().startsWith(cookieNamePrefix))?.split("=")[1] ?? defaultTheme;
126
129
  const date = /* @__PURE__ */ new Date();
127
130
  const expires = new Date(date.setDate(date.getDate() + 365));
128
- initial.value.colorSchemeCookie = `${cookieName}=${initial.value.colorSchemeFromCookie}; Path=${baseUrl}; Expires=${expires.toUTCString()}; SameSite=Lax`;
131
+ initial.value.colorSchemeCookie = `${cookieName}=${initial.value.colorSchemeFromCookie}; Path=${baseUrl}; Expires=${expires.toUTCString()}; SameSite=${cookieSameSite[0].toUpperCase()}${cookieSameSite.slice(1)}`;
132
+ if (cookieDomain) {
133
+ initial.value.colorSchemeCookie += `; Domain=${cookieDomain}`;
134
+ }
135
+ if (cookieSecure) {
136
+ initial.value.colorSchemeCookie += "; Secure";
137
+ }
129
138
  return initial;
130
139
  }
@@ -273,15 +273,27 @@ function writeThemeCookie(clientHintsRequest, ssrClientHintsConfiguration2) {
273
273
  const cookieName = ssrClientHintsConfiguration2.prefersColorSchemeOptions.cookieName;
274
274
  const themeName = clientHintsRequest.colorSchemeFromCookie ?? ssrClientHintsConfiguration2.prefersColorSchemeOptions.defaultTheme;
275
275
  const path = ssrClientHintsConfiguration2.prefersColorSchemeOptions.baseUrl;
276
+ const domain = ssrClientHintsConfiguration2.prefersColorSchemeOptions.cookieDomain;
277
+ const secure = ssrClientHintsConfiguration2.prefersColorSchemeOptions.cookieSecure;
278
+ const sameSite = ssrClientHintsConfiguration2.prefersColorSchemeOptions.cookieSameSite;
276
279
  const date = /* @__PURE__ */ new Date();
277
280
  const expires = new Date(date.setDate(date.getDate() + 365));
278
281
  if (!clientHintsRequest.firstRequest || !ssrClientHintsConfiguration2.reloadOnFirstRequest) {
279
282
  useCookie(cookieName, {
280
283
  path,
284
+ domain,
281
285
  expires,
282
- sameSite: "lax"
286
+ sameSite,
287
+ secure
283
288
  }).value = themeName;
284
289
  }
285
- return `${cookieName}=${themeName}; Path=${path}; Expires=${expires.toUTCString()}; SameSite=Lax`;
290
+ let cookie = `${cookieName}=${themeName}; Path=${path}; Expires=${expires.toUTCString()}; SameSite=${sameSite[0].toUpperCase()}${sameSite.slice(1)}`;
291
+ if (domain) {
292
+ cookie += `; Domain=${domain}`;
293
+ }
294
+ if (secure) {
295
+ cookie += "; Secure";
296
+ }
297
+ return cookie;
286
298
  }
287
299
  export default plugin;
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.5",
4
+ "version": "1.0.0-beta.7",
5
5
  "description": "Zero-Config Nuxt Module for Vuetify",
6
6
  "author": "userquin <userquin@gmail.com>",
7
7
  "license": "MIT",
@@ -52,7 +52,7 @@
52
52
  ],
53
53
  "dependencies": {
54
54
  "@nuxt/kit": "^4.3.1",
55
- "@vuetify/unplugin-styles": "^1.0.0-beta.10",
55
+ "@vuetify/unplugin-styles": "^1.0.0-beta.11",
56
56
  "defu": "^6.1.4",
57
57
  "destr": "^2.0.5",
58
58
  "local-pkg": "^1.1.2",