vuetify-nuxt-module 1.0.0-beta.1 → 1.0.0-beta.11

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,13 @@
1
+ /**
2
+ * Session cookie marking that the one-time `reloadOnFirstRequest` reload has
3
+ * already happened this browser session. Prevents an infinite reload loop when
4
+ * a browser requests client hints but never delivers them (e.g. Brave Shields
5
+ * strip `Sec-CH-*`), since `firstRequest` would otherwise stay `true` forever (#334).
6
+ */
7
+ export declare const RELOAD_GUARD_COOKIE = "vuetify-nuxt-client-hints-reloaded";
8
+ /** True when the guard cookie is present in a `document.cookie` string. */
9
+ export declare function hasReloadGuardCookie(cookie: string): boolean;
10
+ /** Build a session guard cookie (no expiry → cleared on browser close). */
11
+ export declare function buildReloadGuardCookie(path: string): string;
12
+ /** Whether to perform the first-request reload: only once per session. */
13
+ export declare function shouldReloadOnFirstRequest(firstRequest: boolean, reloadOnFirstRequest: boolean, alreadyReloaded: boolean): boolean;
@@ -0,0 +1,11 @@
1
+ export const RELOAD_GUARD_COOKIE = "vuetify-nuxt-client-hints-reloaded";
2
+ export function hasReloadGuardCookie(cookie) {
3
+ const prefix = `${RELOAD_GUARD_COOKIE}=`;
4
+ return cookie.split(";").some((c) => c.trim().startsWith(prefix));
5
+ }
6
+ export function buildReloadGuardCookie(path) {
7
+ return `${RELOAD_GUARD_COOKIE}=1; Path=${path}; SameSite=Lax`;
8
+ }
9
+ export function shouldReloadOnFirstRequest(firstRequest, reloadOnFirstRequest, alreadyReloaded) {
10
+ return firstRequest && reloadOnFirstRequest && !alreadyReloaded;
11
+ }
@@ -4,6 +4,9 @@ import { useI18n } from "vue-i18n";
4
4
  function inferDecimalSeparator(n) {
5
5
  return n(0.1).includes(",") ? "," : ".";
6
6
  }
7
+ function inferNumericGroupSeparator(n) {
8
+ return n(1e4, { useGrouping: true }).replace(/\p{Nd}/gu, "") || " ";
9
+ }
7
10
  export function createAdapter(vuetifyOptions) {
8
11
  vuetifyOptions.locale = {};
9
12
  const nuxtApp = useNuxtApp();
@@ -32,7 +35,8 @@ export function createAdapter(vuetifyOptions) {
32
35
  t: (key, ...params) => i18n.t(key, params),
33
36
  n: i18n.n,
34
37
  provide: createProvideFunction({ current: currentLocale, fallback, messages }),
35
- decimalSeparator: toRef(() => inferDecimalSeparator(i18n.n))
38
+ decimalSeparator: toRef(() => inferDecimalSeparator(i18n.n)),
39
+ numericGroupSeparator: toRef(() => inferNumericGroupSeparator(i18n.n))
36
40
  };
37
41
  }
38
42
  function createProvideFunction(data) {
@@ -61,6 +65,7 @@ function createProvideFunction(data) {
61
65
  t,
62
66
  n,
63
67
  decimalSeparator: toRef(() => props.decimalSeparator ?? inferDecimalSeparator(n)),
68
+ numericGroupSeparator: toRef(() => inferNumericGroupSeparator(n)),
64
69
  provide: createProvideFunction({ current: currentLocale, fallback: data.fallback, messages: data.messages })
65
70
  };
66
71
  };
@@ -2,6 +2,7 @@ import { defineNuxtPlugin, useNuxtApp, useState } from "#imports";
2
2
  import { ssrClientHintsConfiguration } from "virtual:vuetify-ssr-client-hints-configuration";
3
3
  import { reactive, ref, watch } from "vue";
4
4
  import { VuetifyHTTPClientHints } from "./client-hints.js";
5
+ import { buildReloadGuardCookie, hasReloadGuardCookie, shouldReloadOnFirstRequest } from "./first-request-reload-guard.js";
5
6
  const plugin = defineNuxtPlugin({
6
7
  name: "vuetify:client-hints:client:plugin",
7
8
  order: -25,
@@ -22,7 +23,11 @@ const plugin = defineNuxtPlugin({
22
23
  prefersColorScheme,
23
24
  prefersColorSchemeOptions
24
25
  } = ssrClientHintsConfiguration;
25
- if (firstRequest && reloadOnFirstRequest) {
26
+ if (shouldReloadOnFirstRequest(firstRequest, reloadOnFirstRequest, hasReloadGuardCookie(document.cookie))) {
27
+ const markAndReload = () => {
28
+ document.cookie = buildReloadGuardCookie(prefersColorSchemeOptions?.baseUrl ?? "/");
29
+ window.location.reload();
30
+ };
26
31
  if (prefersColorScheme) {
27
32
  const themeCookie = state.value.colorSchemeCookie;
28
33
  if (prefersColorSchemeOptions && themeCookie) {
@@ -32,19 +37,19 @@ const plugin = defineNuxtPlugin({
32
37
  const cookieEntry = `${parseCookieName}${state.value.colorSchemeFromCookie ?? prefersColorSchemeOptions.defaultTheme};`;
33
38
  const newThemeName = prefersDark ? prefersColorSchemeOptions.darkThemeName : prefersColorSchemeOptions.lightThemeName;
34
39
  document.cookie = themeCookie.replace(cookieEntry, `${cookieName}=${newThemeName};`);
35
- window.location.reload();
40
+ markAndReload();
36
41
  } else if (prefersColorSchemeAvailable) {
37
- window.location.reload();
42
+ markAndReload();
38
43
  }
39
44
  }
40
45
  if (prefersReducedMotion && prefersReducedMotionAvailable) {
41
- window.location.reload();
46
+ markAndReload();
42
47
  }
43
48
  if (viewportSize && viewportHeightAvailable) {
44
- window.location.reload();
49
+ markAndReload();
45
50
  }
46
51
  if (viewportSize && viewportWidthAvailable) {
47
- window.location.reload();
52
+ markAndReload();
48
53
  }
49
54
  }
50
55
  nuxtApp.hook("vuetify:before-create", ({ vuetifyOptions }) => {
@@ -119,12 +124,21 @@ function useSSRClientHints() {
119
124
  const {
120
125
  baseUrl,
121
126
  cookieName,
127
+ cookieDomain,
128
+ cookieSecure,
129
+ cookieSameSite,
122
130
  defaultTheme
123
131
  } = ssrClientHintsConfiguration.prefersColorSchemeOptions;
124
132
  const cookieNamePrefix = `${cookieName}=`;
125
133
  initial.value.colorSchemeFromCookie = document.cookie?.split(";")?.find((c) => c.trim().startsWith(cookieNamePrefix))?.split("=")[1] ?? defaultTheme;
126
134
  const date = /* @__PURE__ */ new Date();
127
135
  const expires = new Date(date.setDate(date.getDate() + 365));
128
- initial.value.colorSchemeCookie = `${cookieName}=${initial.value.colorSchemeFromCookie}; Path=${baseUrl}; Expires=${expires.toUTCString()}; SameSite=Lax`;
136
+ initial.value.colorSchemeCookie = `${cookieName}=${initial.value.colorSchemeFromCookie}; Path=${baseUrl}; Expires=${expires.toUTCString()}; SameSite=${cookieSameSite[0].toUpperCase()}${cookieSameSite.slice(1)}`;
137
+ if (cookieDomain) {
138
+ initial.value.colorSchemeCookie += `; Domain=${cookieDomain}`;
139
+ }
140
+ if (cookieSecure) {
141
+ initial.value.colorSchemeCookie += "; Secure";
142
+ }
129
143
  return initial;
130
144
  }
@@ -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.1",
4
+ "version": "1.0.0-beta.11",
5
5
  "description": "Zero-Config Nuxt Module for Vuetify",
6
6
  "author": "userquin <userquin@gmail.com>",
7
7
  "license": "MIT",
@@ -52,17 +52,18 @@
52
52
  ],
53
53
  "dependencies": {
54
54
  "@nuxt/kit": "^4.3.1",
55
- "defu": "^6.1.4",
56
- "destr": "^2.0.5",
55
+ "@vuetify/loader-shared": "^2.1.2",
56
+ "@vuetify/unplugin-styles": "^1.0.0-beta.11",
57
+ "defu": "^6.1.7",
57
58
  "local-pkg": "^1.1.2",
58
59
  "pathe": "^2.0.3",
59
60
  "perfect-debounce": "^2.1.0",
60
61
  "semver": "^7.7.4",
61
62
  "ufo": "^1.6.3",
62
- "unconfig": "^7.5.0",
63
- "upath": "^2.0.1",
64
- "vite-plugin-vuetify": "^2.1.3",
65
- "vuetify": "^4.0.1"
63
+ "unconfig": "^7.5.0"
64
+ },
65
+ "peerDependencies": {
66
+ "vuetify": "^3.4.0 || ^4.0.0"
66
67
  },
67
68
  "devDependencies": {
68
69
  "@antfu/eslint-config": "^7.6.1",
@@ -87,21 +88,23 @@
87
88
  "eslint": "^10.0.2",
88
89
  "luxon": "^3.7.2",
89
90
  "nuxt": "^4.3.1",
91
+ "playwright-core": "^1.58.0",
90
92
  "publint": "^0.3.18",
91
93
  "rimraf": "^6.1.3",
92
94
  "sass": "^1.97.3",
93
95
  "typescript": "^5.9.3",
94
- "vite": "7.3.1",
96
+ "vite": "7.3.5",
95
97
  "vitest": "^4.0.18",
96
- "vue-tsc": "^3.2.5"
98
+ "vue-tsc": "^3.2.5",
99
+ "vuetify": "^4.0.1"
97
100
  },
98
101
  "build": {
99
102
  "externals": [
100
103
  "@vuetify/loader-shared",
104
+ "@vuetify/unplugin-styles",
101
105
  "node:child_process",
102
106
  "node:fs",
103
107
  "consola",
104
- "destr",
105
108
  "esbuild",
106
109
  "local-pkg",
107
110
  "pathe",
@@ -109,11 +112,9 @@
109
112
  "rollup",
110
113
  "sass",
111
114
  "sass-embedded",
112
- "upath",
113
115
  "ufo",
114
116
  "unconfig",
115
117
  "vite",
116
- "vite-plugin-vuetify",
117
118
  "vuetify"
118
119
  ]
119
120
  },
@@ -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
  }