@nuxt/hints 1.0.0-alpha.3 → 1.0.0-alpha.5
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 +14 -9
- package/dist/client/200.html +1 -1
- package/dist/client/404.html +1 -1
- package/dist/client/_nuxt/BSOx1wTR.js +1 -0
- package/dist/client/_nuxt/BZNZ3uhE.js +1 -0
- package/dist/client/_nuxt/{DE7yhJ9n.js → BbfnXyuh.js} +1 -1
- package/dist/client/_nuxt/{CeyKylGo.js → Begz4AOL.js} +3 -3
- package/dist/client/_nuxt/{BTIAbEX4.js → BhEtv9w2.js} +1 -1
- package/dist/client/_nuxt/{BYW8DJWC.js → CFPKd07t.js} +1 -1
- package/dist/client/_nuxt/Cnhn90sM.js +1 -0
- package/dist/client/_nuxt/{DxmC8vYz.js → CpX0l1y3.js} +1 -1
- package/dist/client/_nuxt/CzKOi7Jv.js +1 -0
- package/dist/client/_nuxt/D6ZzJpIK.js +1 -0
- package/dist/client/_nuxt/DAb3G4RE.js +1 -0
- package/dist/client/_nuxt/{3uZvltmj.js → DI96rpYV.js} +1 -1
- package/dist/client/_nuxt/{s2_R2YM5.js → DdCWK8Sp.js} +1 -1
- package/dist/client/_nuxt/DtIwcMt9.js +6 -0
- package/dist/client/_nuxt/KEVM2KzW.js +36 -0
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/a5258e2b-a12d-475a-8f36-441ee2727d81.json +1 -0
- package/dist/client/_nuxt/{entry.CqH3PPiL.css → entry.BHEgHyMn.css} +1 -1
- package/dist/client/_nuxt/error-404.CAnB1sNT.css +1 -0
- package/dist/client/_nuxt/error-500.CHqHY76U.css +1 -0
- package/dist/client/_nuxt/hydration.BDJR-Z6Z.css +1 -0
- package/dist/client/_nuxt/{DyZ7ilof.js → otccQYWM.js} +1 -1
- package/dist/client/hydration/index.html +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/third-party-scripts/index.html +1 -1
- package/dist/client/web-vitals/index.html +1 -1
- package/dist/module.d.mts +1 -3
- package/dist/module.json +1 -1
- package/dist/module.mjs +60 -24
- package/dist/runtime/core/components/nuxt-island.d.ts +1 -136
- package/dist/runtime/core/plugins/vue-tracer-state.client.d.ts +1 -1
- package/dist/runtime/hydration/component.d.ts +1 -1
- package/dist/runtime/hydration/component.js +1 -2
- package/dist/runtime/hydration/composables.js +16 -26
- package/dist/runtime/hydration/handler.nitro.d.ts +5 -0
- package/dist/runtime/hydration/handler.nitro.js +52 -0
- package/dist/runtime/hydration/plugin.client.d.ts +1 -1
- package/dist/runtime/hydration/sse.nitro.d.ts +2 -0
- package/dist/runtime/hydration/sse.nitro.js +22 -0
- package/dist/runtime/hydration/types.d.ts +29 -0
- package/dist/runtime/hydration/types.js +0 -0
- package/dist/runtime/hydration/utils.d.ts +3 -0
- package/dist/runtime/hydration/utils.js +23 -0
- package/dist/runtime/third-party-scripts/nitro.plugin.js +2 -2
- package/dist/runtime/third-party-scripts/plugin.client.d.ts +1 -1
- package/dist/runtime/third-party-scripts/plugin.client.js +2 -2
- package/dist/runtime/types.d.ts +11 -2
- package/dist/runtime/web-vitals/plugin.client.d.ts +1 -1
- package/package.json +7 -6
- package/dist/client/_nuxt/BAIAvlAa.js +0 -1
- package/dist/client/_nuxt/Ckm7x3qc.js +0 -6
- package/dist/client/_nuxt/CsLEpyV7.js +0 -1
- package/dist/client/_nuxt/CzzNqrSg.js +0 -1
- package/dist/client/_nuxt/DBrXuSAZ.js +0 -1
- package/dist/client/_nuxt/DDotc1jX.js +0 -21
- package/dist/client/_nuxt/REBTOKr5.js +0 -1
- package/dist/client/_nuxt/builds/meta/b0fc6608-9102-4a88-9842-0a2ed6526798.json +0 -1
- package/dist/client/_nuxt/error-404.MLlw4bJt.css +0 -1
- package/dist/client/_nuxt/error-500.CBQQ1PSP.css +0 -1
- package/dist/client/_nuxt/hydration.B5wxUWyr.css +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-hints/_nuxt/entry.
|
|
1
|
+
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/__nuxt-hints/_nuxt/entry.BHEgHyMn.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/__nuxt-hints/_nuxt/DtIwcMt9.js"><script type="module" src="/__nuxt-hints/_nuxt/DtIwcMt9.js" crossorigin></script></head><body><div id="__nuxt"></div><div id="teleports"></div><script>window.__NUXT__={};window.__NUXT__.config={public:{},app:{baseURL:"/__nuxt-hints",buildId:"a5258e2b-a12d-475a-8f36-441ee2727d81",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-hints-iframe" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1766698975596,false]</script></body></html>
|
package/dist/module.d.mts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
-
|
|
3
1
|
interface ModuleOptions {
|
|
4
2
|
devtools: boolean;
|
|
5
3
|
}
|
|
6
|
-
declare const _default:
|
|
4
|
+
declare const _default: any;
|
|
7
5
|
|
|
8
6
|
export { _default as default };
|
|
9
7
|
export type { ModuleOptions };
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import { addDevServerHandler, defineNuxtModule, createResolver, addComponent, addPlugin, addBuildPlugin, addServerPlugin } from '@nuxt/kit';
|
|
1
|
+
import { addDevServerHandler, defineNuxtModule, createResolver, addComponent, addPlugin, addBuildPlugin, addServerHandler, addServerPlugin } from '@nuxt/kit';
|
|
2
|
+
import { HYDRATION_ROUTE, HYDRATION_SSE_ROUTE } from '../dist/runtime/hydration/utils.js';
|
|
2
3
|
import { existsSync } from 'node:fs';
|
|
3
4
|
import { eventHandler, proxyRequest } from 'h3';
|
|
4
5
|
import { genImport } from 'knitwork';
|
|
5
6
|
import MagicString from 'magic-string';
|
|
7
|
+
import { dirname, resolve } from 'node:path';
|
|
6
8
|
import { parseSync } from 'oxc-parser';
|
|
7
9
|
import { createUnplugin } from 'unplugin';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
8
11
|
|
|
9
12
|
const DEVTOOLS_UI_ROUTE = "/__nuxt-hints";
|
|
10
13
|
const DEVTOOLS_UI_LOCAL_PORT = 3300;
|
|
@@ -41,10 +44,13 @@ function setupDevToolsUI(nuxt, resolver) {
|
|
|
41
44
|
});
|
|
42
45
|
}
|
|
43
46
|
|
|
47
|
+
const distDir = dirname(fileURLToPath(import.meta.url));
|
|
48
|
+
|
|
44
49
|
const INCLUDE_VUE_RE = /\.vue$/;
|
|
45
50
|
const EXCLUDE_NODE_MODULES = /node_modules/;
|
|
46
51
|
const DEFINE_COMPONENT_RE = /defineComponent/;
|
|
47
52
|
const DEFINE_NUXT_COMPONENT_RE = /defineNuxtComponent/;
|
|
53
|
+
const skipPath = normalizePath(resolve(distDir, "runtime/hydration/component.ts"));
|
|
48
54
|
const InjectHydrationPlugin = createUnplugin(() => {
|
|
49
55
|
return [
|
|
50
56
|
{
|
|
@@ -54,32 +60,42 @@ const InjectHydrationPlugin = createUnplugin(() => {
|
|
|
54
60
|
filter: {
|
|
55
61
|
id: {
|
|
56
62
|
include: /.(vue|ts|js|tsx|jsx)$/,
|
|
57
|
-
exclude: EXCLUDE_NODE_MODULES
|
|
63
|
+
exclude: [skipPath, EXCLUDE_NODE_MODULES]
|
|
58
64
|
},
|
|
59
|
-
code:
|
|
65
|
+
code: {
|
|
66
|
+
include: [DEFINE_COMPONENT_RE, DEFINE_NUXT_COMPONENT_RE]
|
|
67
|
+
}
|
|
60
68
|
},
|
|
61
|
-
handler(code, id) {
|
|
69
|
+
async handler(code, id) {
|
|
62
70
|
const m = new MagicString(code);
|
|
63
71
|
const { program } = parseSync(id, code);
|
|
64
72
|
const imports = program.body.filter((node) => node.type === "ImportDeclaration");
|
|
65
73
|
const hasDefineComponent = DEFINE_COMPONENT_RE.test(code);
|
|
66
74
|
const hasDefineNuxtComponent = DEFINE_NUXT_COMPONENT_RE.test(code);
|
|
67
|
-
const defineComponentImport = findImportSpecifier(
|
|
75
|
+
const defineComponentImport = findImportSpecifier(
|
|
76
|
+
imports,
|
|
77
|
+
"defineComponent",
|
|
78
|
+
["vue", "#imports"],
|
|
79
|
+
(specifier, nextSpecifier) => {
|
|
80
|
+
m.remove(
|
|
81
|
+
specifier.start,
|
|
82
|
+
nextSpecifier?.start ?? specifier.end
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
);
|
|
68
86
|
const defineComponentAlias = defineComponentImport?.local.name || "defineComponent";
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
|
|
87
|
+
const defineNuxtComponentImport = findImportSpecifier(
|
|
88
|
+
imports,
|
|
89
|
+
"defineNuxtComponent",
|
|
90
|
+
["#app/composables/component", "#imports", "#app", "nuxt/app"],
|
|
91
|
+
(specifier, next) => {
|
|
92
|
+
m.remove(
|
|
93
|
+
specifier.start,
|
|
94
|
+
next?.start ?? specifier.end
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
);
|
|
76
98
|
const defineNuxtComponentAlias = defineNuxtComponentImport?.local.name || "defineNuxtComponent";
|
|
77
|
-
if (defineNuxtComponentImport) {
|
|
78
|
-
m.remove(
|
|
79
|
-
defineNuxtComponentImport.start,
|
|
80
|
-
defineNuxtComponentImport.end
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
99
|
const importsToAdd = new Set([
|
|
84
100
|
hasDefineComponent && genImport(
|
|
85
101
|
"@nuxt/hints/runtime/hydration/component",
|
|
@@ -107,9 +123,11 @@ const InjectHydrationPlugin = createUnplugin(() => {
|
|
|
107
123
|
filter: {
|
|
108
124
|
id: {
|
|
109
125
|
include: INCLUDE_VUE_RE,
|
|
110
|
-
exclude: EXCLUDE_NODE_MODULES
|
|
126
|
+
exclude: [skipPath, EXCLUDE_NODE_MODULES]
|
|
111
127
|
},
|
|
112
|
-
code:
|
|
128
|
+
code: {
|
|
129
|
+
exclude: [DEFINE_COMPONENT_RE, DEFINE_NUXT_COMPONENT_RE]
|
|
130
|
+
}
|
|
113
131
|
},
|
|
114
132
|
handler(code, id) {
|
|
115
133
|
const m = new MagicString(code);
|
|
@@ -139,11 +157,19 @@ const InjectHydrationPlugin = createUnplugin(() => {
|
|
|
139
157
|
}
|
|
140
158
|
];
|
|
141
159
|
});
|
|
142
|
-
function findImportSpecifier(importDecl, importedName, pkgNames) {
|
|
160
|
+
function findImportSpecifier(importDecl, importedName, pkgNames, callback) {
|
|
143
161
|
const names = Array.isArray(pkgNames) ? pkgNames : [pkgNames];
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
162
|
+
const allSpecifiers = importDecl.filter((imp) => names.includes(imp.source.value)).flatMap((decl) => decl.specifiers.map((spec, i) => ({ spec, next: decl.specifiers[i + 1] })));
|
|
163
|
+
const match = allSpecifiers.find(
|
|
164
|
+
({ spec }) => spec.type === "ImportSpecifier" && spec.imported.type === "Identifier" && spec.imported.name === importedName
|
|
165
|
+
);
|
|
166
|
+
if (match) {
|
|
167
|
+
callback?.(match.spec, match.next);
|
|
168
|
+
return match.spec;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function normalizePath(path) {
|
|
172
|
+
return path.replace(/\\/g, "/");
|
|
147
173
|
}
|
|
148
174
|
|
|
149
175
|
const moduleName = "@nuxt/hints";
|
|
@@ -159,6 +185,8 @@ const module$1 = defineNuxtModule({
|
|
|
159
185
|
if (!nuxt.options.dev) {
|
|
160
186
|
return;
|
|
161
187
|
}
|
|
188
|
+
nuxt.options.nitro.experimental = nuxt.options.nitro.experimental || {};
|
|
189
|
+
nuxt.options.nitro.experimental.websocket = true;
|
|
162
190
|
const resolver = createResolver(import.meta.url);
|
|
163
191
|
addComponent({
|
|
164
192
|
name: "NuxtIsland",
|
|
@@ -168,6 +196,14 @@ const module$1 = defineNuxtModule({
|
|
|
168
196
|
addPlugin(resolver.resolve("./runtime/web-vitals/plugin.client"));
|
|
169
197
|
addPlugin(resolver.resolve("./runtime/hydration/plugin.client"));
|
|
170
198
|
addBuildPlugin(InjectHydrationPlugin);
|
|
199
|
+
addServerHandler({
|
|
200
|
+
route: HYDRATION_ROUTE,
|
|
201
|
+
handler: resolver.resolve("./runtime/hydration/handler.nitro")
|
|
202
|
+
});
|
|
203
|
+
addServerHandler({
|
|
204
|
+
route: HYDRATION_SSE_ROUTE,
|
|
205
|
+
handler: resolver.resolve("./runtime/hydration/sse.nitro")
|
|
206
|
+
});
|
|
171
207
|
addPlugin(resolver.resolve("./runtime/third-party-scripts/plugin.client"));
|
|
172
208
|
addServerPlugin(resolver.resolve("./runtime/third-party-scripts/nitro.plugin"));
|
|
173
209
|
nuxt.hook("prepare:types", ({ references }) => {
|
|
@@ -1,137 +1,2 @@
|
|
|
1
|
-
declare const HintsNuxtIsland:
|
|
2
|
-
new (...args: any[]): import("vue").CreateComponentPublicInstanceWithMixins<Readonly<import("vue").ExtractPropTypes<{
|
|
3
|
-
name: {
|
|
4
|
-
type: StringConstructor;
|
|
5
|
-
required: true;
|
|
6
|
-
};
|
|
7
|
-
lazy: BooleanConstructor;
|
|
8
|
-
props: {
|
|
9
|
-
type: ObjectConstructor;
|
|
10
|
-
default: () => undefined;
|
|
11
|
-
};
|
|
12
|
-
context: {
|
|
13
|
-
type: ObjectConstructor;
|
|
14
|
-
default: () => {};
|
|
15
|
-
};
|
|
16
|
-
scopeId: {
|
|
17
|
-
type: import("vue").PropType<string | undefined | null>;
|
|
18
|
-
default: () => undefined;
|
|
19
|
-
};
|
|
20
|
-
source: {
|
|
21
|
-
type: StringConstructor;
|
|
22
|
-
default: () => undefined;
|
|
23
|
-
};
|
|
24
|
-
dangerouslyLoadClientComponents: {
|
|
25
|
-
type: BooleanConstructor;
|
|
26
|
-
default: boolean;
|
|
27
|
-
};
|
|
28
|
-
}>> & Readonly<{
|
|
29
|
-
onError?: ((...args: any[]) => any) | undefined;
|
|
30
|
-
}>, (_ctx: any, _cache: any) => (import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
31
|
-
[key: string]: any;
|
|
32
|
-
}> | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
33
|
-
[key: string]: any;
|
|
34
|
-
}>[])[] | import("vue").VNode<any, any, {
|
|
35
|
-
[key: string]: any;
|
|
36
|
-
}>[], {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "error"[], import("vue").PublicProps, {
|
|
37
|
-
props: Record<string, any>;
|
|
38
|
-
source: string;
|
|
39
|
-
scopeId: string | null | undefined;
|
|
40
|
-
lazy: boolean;
|
|
41
|
-
context: Record<string, any>;
|
|
42
|
-
dangerouslyLoadClientComponents: boolean;
|
|
43
|
-
}, true, {}, {}, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, {}, any, import("vue").ComponentProvideOptions, {
|
|
44
|
-
P: {};
|
|
45
|
-
B: {};
|
|
46
|
-
D: {};
|
|
47
|
-
C: {};
|
|
48
|
-
M: {};
|
|
49
|
-
Defaults: {};
|
|
50
|
-
}, Readonly<import("vue").ExtractPropTypes<{
|
|
51
|
-
name: {
|
|
52
|
-
type: StringConstructor;
|
|
53
|
-
required: true;
|
|
54
|
-
};
|
|
55
|
-
lazy: BooleanConstructor;
|
|
56
|
-
props: {
|
|
57
|
-
type: ObjectConstructor;
|
|
58
|
-
default: () => undefined;
|
|
59
|
-
};
|
|
60
|
-
context: {
|
|
61
|
-
type: ObjectConstructor;
|
|
62
|
-
default: () => {};
|
|
63
|
-
};
|
|
64
|
-
scopeId: {
|
|
65
|
-
type: import("vue").PropType<string | undefined | null>;
|
|
66
|
-
default: () => undefined;
|
|
67
|
-
};
|
|
68
|
-
source: {
|
|
69
|
-
type: StringConstructor;
|
|
70
|
-
default: () => undefined;
|
|
71
|
-
};
|
|
72
|
-
dangerouslyLoadClientComponents: {
|
|
73
|
-
type: BooleanConstructor;
|
|
74
|
-
default: boolean;
|
|
75
|
-
};
|
|
76
|
-
}>> & Readonly<{
|
|
77
|
-
onError?: ((...args: any[]) => any) | undefined;
|
|
78
|
-
}>, (_ctx: any, _cache: any) => (import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
79
|
-
[key: string]: any;
|
|
80
|
-
}> | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
81
|
-
[key: string]: any;
|
|
82
|
-
}>[])[] | import("vue").VNode<any, any, {
|
|
83
|
-
[key: string]: any;
|
|
84
|
-
}>[], {}, {}, {}, {
|
|
85
|
-
props: Record<string, any>;
|
|
86
|
-
source: string;
|
|
87
|
-
scopeId: string | null | undefined;
|
|
88
|
-
lazy: boolean;
|
|
89
|
-
context: Record<string, any>;
|
|
90
|
-
dangerouslyLoadClientComponents: boolean;
|
|
91
|
-
}>;
|
|
92
|
-
__isFragment?: never;
|
|
93
|
-
__isTeleport?: never;
|
|
94
|
-
__isSuspense?: never;
|
|
95
|
-
} & import("vue").ComponentOptionsBase<Readonly<import("vue").ExtractPropTypes<{
|
|
96
|
-
name: {
|
|
97
|
-
type: StringConstructor;
|
|
98
|
-
required: true;
|
|
99
|
-
};
|
|
100
|
-
lazy: BooleanConstructor;
|
|
101
|
-
props: {
|
|
102
|
-
type: ObjectConstructor;
|
|
103
|
-
default: () => undefined;
|
|
104
|
-
};
|
|
105
|
-
context: {
|
|
106
|
-
type: ObjectConstructor;
|
|
107
|
-
default: () => {};
|
|
108
|
-
};
|
|
109
|
-
scopeId: {
|
|
110
|
-
type: import("vue").PropType<string | undefined | null>;
|
|
111
|
-
default: () => undefined;
|
|
112
|
-
};
|
|
113
|
-
source: {
|
|
114
|
-
type: StringConstructor;
|
|
115
|
-
default: () => undefined;
|
|
116
|
-
};
|
|
117
|
-
dangerouslyLoadClientComponents: {
|
|
118
|
-
type: BooleanConstructor;
|
|
119
|
-
default: boolean;
|
|
120
|
-
};
|
|
121
|
-
}>> & Readonly<{
|
|
122
|
-
onError?: ((...args: any[]) => any) | undefined;
|
|
123
|
-
}>, (_ctx: any, _cache: any) => (import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
124
|
-
[key: string]: any;
|
|
125
|
-
}> | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
126
|
-
[key: string]: any;
|
|
127
|
-
}>[])[] | import("vue").VNode<any, any, {
|
|
128
|
-
[key: string]: any;
|
|
129
|
-
}>[], {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "error"[], "error", {
|
|
130
|
-
props: Record<string, any>;
|
|
131
|
-
source: string;
|
|
132
|
-
scopeId: string | null | undefined;
|
|
133
|
-
lazy: boolean;
|
|
134
|
-
context: Record<string, any>;
|
|
135
|
-
dangerouslyLoadClientComponents: boolean;
|
|
136
|
-
}, {}, string, {}, import("vue").GlobalComponents, import("vue").GlobalDirectives, string, import("vue").ComponentProvideOptions> & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps;
|
|
1
|
+
declare const HintsNuxtIsland: any;
|
|
137
2
|
export default HintsNuxtIsland;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default:
|
|
1
|
+
declare const _default: any;
|
|
2
2
|
export default _default;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { defineComponent as _defineComponent } from "
|
|
2
|
-
import { defineNuxtComponent as _defineNuxtComponent } from "nuxt/app";
|
|
1
|
+
import { defineNuxtComponent as _defineNuxtComponent, defineComponent as _defineComponent } from "#imports";
|
|
3
2
|
import { useHydrationCheck } from "./composables.js";
|
|
4
3
|
export const defineNuxtComponent = function defineNuxtComponent2(...args) {
|
|
5
4
|
const [options, key] = args;
|
|
@@ -1,26 +1,6 @@
|
|
|
1
1
|
import { getCurrentInstance, onMounted } from "vue";
|
|
2
2
|
import { useNuxtApp } from "#imports";
|
|
3
|
-
|
|
4
|
-
if (!html) return "";
|
|
5
|
-
let formatted = "";
|
|
6
|
-
let indent = 0;
|
|
7
|
-
const tags = html.split(/(<\/?[^>]+>)/g);
|
|
8
|
-
for (const tag of tags) {
|
|
9
|
-
if (!tag.trim()) continue;
|
|
10
|
-
if (tag.startsWith("</")) {
|
|
11
|
-
indent--;
|
|
12
|
-
formatted += "\n" + " ".repeat(Math.max(0, indent)) + tag;
|
|
13
|
-
} else if (tag.startsWith("<") && !tag.endsWith("/>") && !tag.includes("<!")) {
|
|
14
|
-
formatted += "\n" + " ".repeat(Math.max(0, indent)) + tag;
|
|
15
|
-
indent++;
|
|
16
|
-
} else if (tag.startsWith("<")) {
|
|
17
|
-
formatted += "\n" + " ".repeat(Math.max(0, indent)) + tag;
|
|
18
|
-
} else {
|
|
19
|
-
formatted += "\n" + " ".repeat(Math.max(0, indent)) + tag.trim();
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
return formatted.trim();
|
|
23
|
-
}
|
|
3
|
+
import { HYDRATION_ROUTE, formatHTML } from "./utils.js";
|
|
24
4
|
export function useHydrationCheck() {
|
|
25
5
|
if (import.meta.server) return;
|
|
26
6
|
const nuxtApp = useNuxtApp();
|
|
@@ -29,16 +9,26 @@ export function useHydrationCheck() {
|
|
|
29
9
|
}
|
|
30
10
|
const instance = getCurrentInstance();
|
|
31
11
|
if (!instance) return;
|
|
32
|
-
const
|
|
12
|
+
const htmlPreHydration = formatHTML(instance.vnode.el?.outerHTML);
|
|
33
13
|
const vnodePrehydration = instance.vnode;
|
|
34
14
|
onMounted(() => {
|
|
35
15
|
const htmlPostHydration = formatHTML(instance.vnode.el?.outerHTML);
|
|
36
|
-
if (
|
|
16
|
+
if (htmlPreHydration !== htmlPostHydration) {
|
|
17
|
+
const payload = {
|
|
18
|
+
htmlPreHydration,
|
|
19
|
+
htmlPostHydration,
|
|
20
|
+
id: globalThis.crypto.randomUUID(),
|
|
21
|
+
componentName: instance.type.name ?? instance.type.displayName ?? instance.type.__name,
|
|
22
|
+
fileLocation: instance.type.__file ?? "unknown"
|
|
23
|
+
};
|
|
37
24
|
nuxtApp.__hints.hydration.push({
|
|
25
|
+
...payload,
|
|
38
26
|
instance,
|
|
39
|
-
vnode: vnodePrehydration
|
|
40
|
-
|
|
41
|
-
|
|
27
|
+
vnode: vnodePrehydration
|
|
28
|
+
});
|
|
29
|
+
$fetch(new URL(HYDRATION_ROUTE, window.location.origin).href, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
body: payload
|
|
42
32
|
});
|
|
43
33
|
console.warn(`[nuxt/hints:hydration] Component ${instance.type.name ?? instance.type.displayName ?? instance.type.__name ?? instance.type.__file} seems to have different html pre and post-hydration. Please make sure you don't have any hydration issue.`);
|
|
44
34
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createError, defineEventHandler, readBody, setResponseStatus } from "h3";
|
|
2
|
+
import { useNitroApp } from "nitropack/runtime";
|
|
3
|
+
const hydrationMismatches = [];
|
|
4
|
+
export default defineEventHandler((event) => {
|
|
5
|
+
switch (event.method) {
|
|
6
|
+
case "GET":
|
|
7
|
+
return getHandler();
|
|
8
|
+
case "POST":
|
|
9
|
+
return postHandler(event);
|
|
10
|
+
case "DELETE":
|
|
11
|
+
return deleteHandler(event);
|
|
12
|
+
default:
|
|
13
|
+
throw createError({ statusCode: 405, statusMessage: "Method Not Allowed" });
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
function getHandler() {
|
|
17
|
+
return {
|
|
18
|
+
mismatches: hydrationMismatches
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
async function postHandler(event) {
|
|
22
|
+
const body = await readBody(event);
|
|
23
|
+
assertPayload(body);
|
|
24
|
+
const nitro = useNitroApp();
|
|
25
|
+
const payload = { id: body.id, htmlPreHydration: body.htmlPreHydration, htmlPostHydration: body.htmlPostHydration, componentName: body.componentName, fileLocation: body.fileLocation };
|
|
26
|
+
hydrationMismatches.push(payload);
|
|
27
|
+
if (hydrationMismatches.length > 20) {
|
|
28
|
+
hydrationMismatches.shift();
|
|
29
|
+
}
|
|
30
|
+
nitro.hooks.callHook("hints:hydration:mismatch", payload);
|
|
31
|
+
setResponseStatus(event, 201);
|
|
32
|
+
}
|
|
33
|
+
async function deleteHandler(event) {
|
|
34
|
+
const nitro = useNitroApp();
|
|
35
|
+
const body = await readBody(event);
|
|
36
|
+
if (!body || !Array.isArray(body.id)) {
|
|
37
|
+
throw createError({ statusCode: 400, statusMessage: "Invalid payload" });
|
|
38
|
+
}
|
|
39
|
+
for (const id of body.id) {
|
|
40
|
+
const index = hydrationMismatches.findIndex((m) => m.id === id);
|
|
41
|
+
if (index !== -1) {
|
|
42
|
+
hydrationMismatches.splice(index, 1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
nitro.hooks.callHook("hints:hydration:cleared", { id: body.id });
|
|
46
|
+
setResponseStatus(event, 204);
|
|
47
|
+
}
|
|
48
|
+
function assertPayload(body) {
|
|
49
|
+
if (typeof body !== "object" || typeof body.id !== "string" || body.htmlPreHydration !== void 0 && typeof body.htmlPreHydration !== "string" || body.htmlPostHydration !== void 0 && typeof body.htmlPostHydration !== "string" || typeof body.componentName !== "string" || typeof body.fileLocation !== "string") {
|
|
50
|
+
throw createError({ statusCode: 400, statusMessage: "Invalid payload" });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default:
|
|
1
|
+
declare const _default: any;
|
|
2
2
|
export default _default;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createEventStream, defineEventHandler } from "h3";
|
|
2
|
+
import { useNitroApp } from "nitropack/runtime";
|
|
3
|
+
export default defineEventHandler((event) => {
|
|
4
|
+
const nitro = useNitroApp();
|
|
5
|
+
const eventStream = createEventStream(event);
|
|
6
|
+
const unsubs = [nitro.hooks.hook("hints:hydration:mismatch", (mismatch) => {
|
|
7
|
+
eventStream.push({
|
|
8
|
+
data: JSON.stringify(mismatch),
|
|
9
|
+
event: "hints:hydration:mismatch"
|
|
10
|
+
});
|
|
11
|
+
}), nitro.hooks.hook("hints:hydration:cleared", async (payload) => {
|
|
12
|
+
eventStream.push({
|
|
13
|
+
data: JSON.stringify(payload.id),
|
|
14
|
+
event: "hints:hydration:cleared"
|
|
15
|
+
});
|
|
16
|
+
})];
|
|
17
|
+
eventStream.onClosed(async () => {
|
|
18
|
+
unsubs.forEach((unsub) => unsub());
|
|
19
|
+
await eventStream.close();
|
|
20
|
+
});
|
|
21
|
+
return eventStream.send();
|
|
22
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { EventStreamMessage } from 'h3';
|
|
2
|
+
import type { ComponentInternalInstance, VNode } from 'vue';
|
|
3
|
+
export interface HydrationMismatchPayload {
|
|
4
|
+
id: string;
|
|
5
|
+
componentName?: string;
|
|
6
|
+
fileLocation: string;
|
|
7
|
+
htmlPreHydration: string;
|
|
8
|
+
htmlPostHydration: string;
|
|
9
|
+
}
|
|
10
|
+
export interface LocalHydrationMismatch extends HydrationMismatchPayload {
|
|
11
|
+
instance: ComponentInternalInstance;
|
|
12
|
+
vnode: VNode;
|
|
13
|
+
}
|
|
14
|
+
export interface HydrationMismatchResponse {
|
|
15
|
+
mismatches: HydrationMismatchPayload[];
|
|
16
|
+
}
|
|
17
|
+
export interface HydrationDeleteSSE extends EventStreamMessage {
|
|
18
|
+
event: 'hydration:cleared';
|
|
19
|
+
data: string;
|
|
20
|
+
}
|
|
21
|
+
export interface HydrationNewSSE extends EventStreamMessage {
|
|
22
|
+
event: 'hydration:mismatch';
|
|
23
|
+
/**
|
|
24
|
+
* Stringified HydrationMismatchPayload
|
|
25
|
+
* @see HydrationMismatchPayload
|
|
26
|
+
*/
|
|
27
|
+
data: string;
|
|
28
|
+
}
|
|
29
|
+
export type HydrationSSEPayload = HydrationDeleteSSE | HydrationNewSSE;
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const HYDRATION_ROUTE = "/__nuxt_hydration";
|
|
2
|
+
export const HYDRATION_SSE_ROUTE = "/__nuxt_hydration/sse";
|
|
3
|
+
export function formatHTML(html) {
|
|
4
|
+
if (!html) return "";
|
|
5
|
+
let formatted = "";
|
|
6
|
+
let indent = 0;
|
|
7
|
+
const tags = html.split(/(<\/?[^>]+>)/g);
|
|
8
|
+
for (const tag of tags) {
|
|
9
|
+
if (!tag.trim()) continue;
|
|
10
|
+
if (tag.startsWith("</")) {
|
|
11
|
+
indent--;
|
|
12
|
+
formatted += "\n" + " ".repeat(Math.max(0, indent)) + tag;
|
|
13
|
+
} else if (tag.startsWith("<") && !tag.endsWith("/>") && !tag.includes("<!")) {
|
|
14
|
+
formatted += "\n" + " ".repeat(Math.max(0, indent)) + tag;
|
|
15
|
+
indent++;
|
|
16
|
+
} else if (tag.startsWith("<")) {
|
|
17
|
+
formatted += "\n" + " ".repeat(Math.max(0, indent)) + tag;
|
|
18
|
+
} else {
|
|
19
|
+
formatted += "\n" + " ".repeat(Math.max(0, indent)) + tag.trim();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return formatted.trim();
|
|
23
|
+
}
|
|
@@ -36,9 +36,9 @@ function __hints_TPC_saveTime(script, startTime) {
|
|
|
36
36
|
for (const script of document.scripts) {
|
|
37
37
|
if (script.src && !script.src.startsWith(window.location.origin)) {
|
|
38
38
|
script.__hints_TPC_start_time = window.__hints_TPC_start_time || Date.now();
|
|
39
|
-
script.
|
|
39
|
+
script.addEventListener('load', () => {
|
|
40
40
|
__hints_TPC_saveTime(script, script.__hints_TPC_start_time);
|
|
41
|
-
}
|
|
41
|
+
})
|
|
42
42
|
__hints_TPC_saveTime(script, script.__hints_TPC_start_time);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default:
|
|
1
|
+
declare const _default: any;
|
|
2
2
|
export default _default;
|
|
@@ -70,10 +70,10 @@ export default defineNuxtPlugin({
|
|
|
70
70
|
}
|
|
71
71
|
nuxtApp.callHook("hints:scripts:added", script).then(() => {
|
|
72
72
|
if (!script.loaded) {
|
|
73
|
-
script.
|
|
73
|
+
script.addEventListener("load", () => {
|
|
74
74
|
window.__hints_TPC_saveTime(script, script.__hints_TPC_start_time);
|
|
75
75
|
nuxtApp.callHook("hints:scripts:loaded", script);
|
|
76
|
-
};
|
|
76
|
+
});
|
|
77
77
|
} else {
|
|
78
78
|
window.__hints_TPC_saveTime(script, script.__hints_TPC_start_time);
|
|
79
79
|
nuxtApp.callHook("hints:scripts:loaded", script);
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { VNode, Ref } from 'vue'
|
|
2
2
|
import type { LCPMetricWithAttribution, INPMetricWithAttribution, CLSMetricWithAttribution } from 'web-vitals/attribution'
|
|
3
|
+
import type { HydrationMismatchPayload, LocalHydrationMismatch } from './hydration/types'
|
|
3
4
|
|
|
4
5
|
declare global {
|
|
5
6
|
interface Window {
|
|
@@ -19,6 +20,7 @@ declare global {
|
|
|
19
20
|
__vnode?: VNode
|
|
20
21
|
}
|
|
21
22
|
}
|
|
23
|
+
|
|
22
24
|
declare module '#app' {
|
|
23
25
|
interface RuntimeNuxtHooks {
|
|
24
26
|
'hints:scripts:added': (script: HTMLScriptElement) => void
|
|
@@ -33,7 +35,7 @@ declare module '#app' {
|
|
|
33
35
|
interface NuxtApp {
|
|
34
36
|
__hints_tpc: Ref<{ element: HTMLScriptElement, loaded: boolean }[]>
|
|
35
37
|
__hints: {
|
|
36
|
-
hydration:
|
|
38
|
+
hydration: LocalHydrationMismatch[]
|
|
37
39
|
webvitals: {
|
|
38
40
|
lcp: Ref<LCPMetricWithAttribution[]>
|
|
39
41
|
inp: Ref<INPMetricWithAttribution[]>
|
|
@@ -45,4 +47,11 @@ declare module '#app' {
|
|
|
45
47
|
}
|
|
46
48
|
}
|
|
47
49
|
|
|
50
|
+
declare module 'nitropack' {
|
|
51
|
+
interface NitroRuntimeHooks {
|
|
52
|
+
'hints:hydration:mismatch': (payload: HydrationMismatchPayload) => void
|
|
53
|
+
'hints:hydration:cleared': (payload: { id: string[] }) => void
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
48
57
|
export {}
|