@nuxt/hints 0.0.0 → 1.0.0-alpha.10

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.
Files changed (210) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +140 -0
  3. package/dist/client/200.html +1 -0
  4. package/dist/client/404.html +1 -0
  5. package/dist/client/_nuxt/2EtD6e53.js +1 -0
  6. package/dist/client/_nuxt/32ctXXKs.js +1 -0
  7. package/dist/client/_nuxt/3e1v2bzS.js +1 -0
  8. package/dist/client/_nuxt/5X7smno1.js +1 -0
  9. package/dist/client/_nuxt/5i3qLPDT.js +1 -0
  10. package/dist/client/_nuxt/B0m2ddpp.js +1 -0
  11. package/dist/client/_nuxt/B1dDrJ26.js +1 -0
  12. package/dist/client/_nuxt/B5Mb093_.js +1 -0
  13. package/dist/client/_nuxt/B6aJPvgy.js +1 -0
  14. package/dist/client/_nuxt/B7mTdjB0.js +1 -0
  15. package/dist/client/_nuxt/BA47KaF1.js +1 -0
  16. package/dist/client/_nuxt/BD7vbABK.js +1 -0
  17. package/dist/client/_nuxt/BEDo0Tqx.js +1 -0
  18. package/dist/client/_nuxt/BGJmEYvX.js +1 -0
  19. package/dist/client/_nuxt/BIGW1oBm.js +1 -0
  20. package/dist/client/_nuxt/BLmx8bSh.js +1 -0
  21. package/dist/client/_nuxt/BLtJtn59.js +1 -0
  22. package/dist/client/_nuxt/BN7gUcg9.js +1 -0
  23. package/dist/client/_nuxt/BPQ3VLAy.js +1 -0
  24. package/dist/client/_nuxt/BUw7H-hv.js +1 -0
  25. package/dist/client/_nuxt/BXkSAIEj.js +1 -0
  26. package/dist/client/_nuxt/BchVSmrD.js +6 -0
  27. package/dist/client/_nuxt/BfHTSMKl.js +1 -0
  28. package/dist/client/_nuxt/BfjtVDDH.js +1 -0
  29. package/dist/client/_nuxt/BgDCqdQA.js +1 -0
  30. package/dist/client/_nuxt/Bkuqu6BP.js +1 -0
  31. package/dist/client/_nuxt/Bp3cYrEr.js +1 -0
  32. package/dist/client/_nuxt/Br6cN0cg.js +1 -0
  33. package/dist/client/_nuxt/BthQWCQV.js +1 -0
  34. package/dist/client/_nuxt/Buea-lGh.js +1 -0
  35. package/dist/client/_nuxt/Bw305WKR.js +1 -0
  36. package/dist/client/_nuxt/Bxupbja9.js +4 -0
  37. package/dist/client/_nuxt/BygCkY0G.js +1 -0
  38. package/dist/client/_nuxt/Byy-EI8A.js +1 -0
  39. package/dist/client/_nuxt/BzJJZx-M.js +1 -0
  40. package/dist/client/_nuxt/C39BiMTA.js +1 -0
  41. package/dist/client/_nuxt/C3Wv6jpd.js +1 -0
  42. package/dist/client/_nuxt/C3mMm8J8.js +1 -0
  43. package/dist/client/_nuxt/C4gqWexZ.js +1 -0
  44. package/dist/client/_nuxt/C8M2exoo.js +1 -0
  45. package/dist/client/_nuxt/C9dUb6Cb.js +1 -0
  46. package/dist/client/_nuxt/C9oPPf7i.js +1 -0
  47. package/dist/client/_nuxt/C9tS-k6U.js +1 -0
  48. package/dist/client/_nuxt/CD8AGNRT.js +1 -0
  49. package/dist/client/_nuxt/CDVJQ6XC.js +1 -0
  50. package/dist/client/_nuxt/CFHQjOhq.js +1 -0
  51. package/dist/client/_nuxt/CG6Dc4jp.js +1 -0
  52. package/dist/client/_nuxt/CH1njM8p.js +1 -0
  53. package/dist/client/_nuxt/CMTm3GFP.js +1 -0
  54. package/dist/client/_nuxt/COt5Ahok.js +1 -0
  55. package/dist/client/_nuxt/CS3Unz2-.js +1 -0
  56. package/dist/client/_nuxt/CTRr51gU.js +1 -0
  57. package/dist/client/_nuxt/CVO1_9PV.js +1 -0
  58. package/dist/client/_nuxt/CVdnzihN.js +1 -0
  59. package/dist/client/_nuxt/CW8IKDeL.js +1 -0
  60. package/dist/client/_nuxt/CXZktZb0.js +1 -0
  61. package/dist/client/_nuxt/CXtECtnM.js +1 -0
  62. package/dist/client/_nuxt/CYsAdtH9.js +1 -0
  63. package/dist/client/_nuxt/CafNBF8u.js +1 -0
  64. package/dist/client/_nuxt/CbfX1IO0.js +1 -0
  65. package/dist/client/_nuxt/Cc68jesL.js +1 -0
  66. package/dist/client/_nuxt/CfQXZHmo.js +1 -0
  67. package/dist/client/_nuxt/Cj5Yp3dK.js +1 -0
  68. package/dist/client/_nuxt/CkXjmgJE.js +1 -0
  69. package/dist/client/_nuxt/Cl0AqbOI.js +1 -0
  70. package/dist/client/_nuxt/Cm3UrAx6.js +1 -0
  71. package/dist/client/_nuxt/CmIQRyeF.js +1 -0
  72. package/dist/client/_nuxt/Cmh6b_Ma.js +1 -0
  73. package/dist/client/_nuxt/Cn-bp-IR.js +1 -0
  74. package/dist/client/_nuxt/CnnebwVN.js +1 -0
  75. package/dist/client/_nuxt/Cp-IABpG.js +1 -0
  76. package/dist/client/_nuxt/Csfq5Kiy.js +1 -0
  77. package/dist/client/_nuxt/CufHLc7y.js +1 -0
  78. package/dist/client/_nuxt/CuhuY25u.js +1 -0
  79. package/dist/client/_nuxt/Cuk6v7N8.js +1 -0
  80. package/dist/client/_nuxt/Cvjx9yec.js +1 -0
  81. package/dist/client/_nuxt/CxbxFI8M.js +1 -0
  82. package/dist/client/_nuxt/CyktbL80.js +1 -0
  83. package/dist/client/_nuxt/CylS5w8V.js +1 -0
  84. package/dist/client/_nuxt/D-2ljcwZ.js +1 -0
  85. package/dist/client/_nuxt/D0r3Knsf.js +1 -0
  86. package/dist/client/_nuxt/D2Qjm2dq.js +1 -0
  87. package/dist/client/_nuxt/D2ZA7i9S.js +1 -0
  88. package/dist/client/_nuxt/D4_iv3hh.js +1 -0
  89. package/dist/client/_nuxt/D4h5O-jR.js +1 -0
  90. package/dist/client/_nuxt/D5KoaKCx.js +1 -0
  91. package/dist/client/_nuxt/D7oLnXFd.js +1 -0
  92. package/dist/client/_nuxt/D87Tk5Gz.js +1 -0
  93. package/dist/client/_nuxt/DAi9KRSo.js +1 -0
  94. package/dist/client/_nuxt/DBKV2OwM.js +1 -0
  95. package/dist/client/_nuxt/DEthMvLe.js +1 -0
  96. package/dist/client/_nuxt/DFWUc33u.js +1 -0
  97. package/dist/client/_nuxt/DFlZdHUX.js +1 -0
  98. package/dist/client/_nuxt/DGP4VlC8.js +1 -0
  99. package/dist/client/_nuxt/DGztddWO.js +1 -0
  100. package/dist/client/_nuxt/DH5Ifo-i.js +1 -0
  101. package/dist/client/_nuxt/DHJKELXO.js +1 -0
  102. package/dist/client/_nuxt/DHQR4-dF.js +1 -0
  103. package/dist/client/_nuxt/DJjDtW9f.js +1 -0
  104. package/dist/client/_nuxt/DPfMkruS.js +1 -0
  105. package/dist/client/_nuxt/DQyhUUbL.js +1 -0
  106. package/dist/client/_nuxt/DRw_LuNl.js +1 -0
  107. package/dist/client/_nuxt/DSeZwD4N.js +1 -0
  108. package/dist/client/_nuxt/DU1UobuO.js +1 -0
  109. package/dist/client/_nuxt/DUszq2jm.js +1 -0
  110. package/dist/client/_nuxt/DVMEJ2y_.js +1 -0
  111. package/dist/client/_nuxt/DWedfzmr.js +1 -0
  112. package/dist/client/_nuxt/DXbdFlpD.js +1 -0
  113. package/dist/client/_nuxt/DYE7WIF3.js +1 -0
  114. package/dist/client/_nuxt/DZxFcAj9.js +1 -0
  115. package/dist/client/_nuxt/DaskW291.js +1 -0
  116. package/dist/client/_nuxt/DcaNXYhu.js +1 -0
  117. package/dist/client/_nuxt/Ddv68eIx.js +1 -0
  118. package/dist/client/_nuxt/Des-eS-w.js +1 -0
  119. package/dist/client/_nuxt/DgsIRcX7.js +1 -0
  120. package/dist/client/_nuxt/DnULxvSX.js +1 -0
  121. package/dist/client/_nuxt/DqQDbK_p.js +1 -0
  122. package/dist/client/_nuxt/DqwNpetd.js +1 -0
  123. package/dist/client/_nuxt/Dspwwk_N.js +1 -0
  124. package/dist/client/_nuxt/Dx-B1_4e.js +1 -0
  125. package/dist/client/_nuxt/DxNHbxmM.js +1 -0
  126. package/dist/client/_nuxt/DxSwrfjg.js +1 -0
  127. package/dist/client/_nuxt/E3gJ1_iC.js +1 -0
  128. package/dist/client/_nuxt/GsRaNv29.js +1 -0
  129. package/dist/client/_nuxt/L9t79GZl.js +1 -0
  130. package/dist/client/_nuxt/MzD3tlZU.js +1 -0
  131. package/dist/client/_nuxt/SCyfE3vN.js +1 -0
  132. package/dist/client/_nuxt/W9tJ9s81.js +1 -0
  133. package/dist/client/_nuxt/X0qVHnNL.js +1 -0
  134. package/dist/client/_nuxt/Yzrsuije.js +1 -0
  135. package/dist/client/_nuxt/bN70gL4F.js +1 -0
  136. package/dist/client/_nuxt/builds/latest.json +1 -0
  137. package/dist/client/_nuxt/builds/meta/988a741f-ba30-4645-9949-60dd86de74e2.json +1 -0
  138. package/dist/client/_nuxt/entry.bXb283Sw.css +1 -0
  139. package/dist/client/_nuxt/error-404.LspKb3Ne.css +1 -0
  140. package/dist/client/_nuxt/error-500.B_qDdXYW.css +1 -0
  141. package/dist/client/_nuxt/fuZLfV_i.js +1 -0
  142. package/dist/client/_nuxt/g9-lgVsj.js +1 -0
  143. package/dist/client/_nuxt/gufSdAgD.js +36 -0
  144. package/dist/client/_nuxt/hJgmCMqR.js +1 -0
  145. package/dist/client/_nuxt/hegEt444.js +1 -0
  146. package/dist/client/_nuxt/hydration.C8A3o8hr.css +1 -0
  147. package/dist/client/_nuxt/m17aaUwq.js +1 -0
  148. package/dist/client/_nuxt/qdsjHGoJ.js +1 -0
  149. package/dist/client/_nuxt/wDzz0qaB.js +1 -0
  150. package/dist/client/_nuxt/zVBI731p.js +9 -0
  151. package/dist/client/component-lazy-load/index.html +1 -0
  152. package/dist/client/hydration/index.html +1 -0
  153. package/dist/client/index.html +1 -0
  154. package/dist/client/third-party-scripts/index.html +1 -0
  155. package/dist/client/web-vitals/index.html +1 -0
  156. package/dist/module.d.mts +10 -0
  157. package/dist/module.json +9 -0
  158. package/dist/module.mjs +442 -0
  159. package/dist/runtime/core/components/nuxt-island.d.ts +2 -0
  160. package/dist/runtime/core/components/nuxt-island.js +15 -0
  161. package/dist/runtime/core/features.d.ts +4 -0
  162. package/dist/runtime/core/features.js +10 -0
  163. package/dist/runtime/core/plugins/features.client.d.ts +2 -0
  164. package/dist/runtime/core/plugins/features.client.js +16 -0
  165. package/dist/runtime/core/plugins/vue-tracer-state.client.d.ts +2 -0
  166. package/dist/runtime/core/plugins/vue-tracer-state.client.js +7 -0
  167. package/dist/runtime/core/server/sse.d.ts +2 -0
  168. package/dist/runtime/core/server/sse.js +16 -0
  169. package/dist/runtime/core/server/types.d.ts +12 -0
  170. package/dist/runtime/core/server/types.js +2 -0
  171. package/dist/runtime/core/types.d.ts +9 -0
  172. package/dist/runtime/core/types.js +0 -0
  173. package/dist/runtime/hydration/component.d.ts +3 -0
  174. package/dist/runtime/hydration/component.js +22 -0
  175. package/dist/runtime/hydration/composables.d.ts +5 -0
  176. package/dist/runtime/hydration/composables.js +45 -0
  177. package/dist/runtime/hydration/nitro.plugin.d.ts +2 -0
  178. package/dist/runtime/hydration/nitro.plugin.js +65 -0
  179. package/dist/runtime/hydration/plugin.client.d.ts +2 -0
  180. package/dist/runtime/hydration/plugin.client.js +11 -0
  181. package/dist/runtime/hydration/types.d.ts +29 -0
  182. package/dist/runtime/hydration/types.js +0 -0
  183. package/dist/runtime/hydration/utils.d.ts +4 -0
  184. package/dist/runtime/hydration/utils.js +26 -0
  185. package/dist/runtime/lazy-load/composables.d.ts +11 -0
  186. package/dist/runtime/lazy-load/composables.js +52 -0
  187. package/dist/runtime/lazy-load/nitro.plugin.d.ts +2 -0
  188. package/dist/runtime/lazy-load/nitro.plugin.js +59 -0
  189. package/dist/runtime/lazy-load/plugin.client.d.ts +2 -0
  190. package/dist/runtime/lazy-load/plugin.client.js +73 -0
  191. package/dist/runtime/lazy-load/schema.d.ts +28 -0
  192. package/dist/runtime/lazy-load/schema.js +16 -0
  193. package/dist/runtime/lazy-load/utils.d.ts +2 -0
  194. package/dist/runtime/lazy-load/utils.js +4 -0
  195. package/dist/runtime/logger.d.ts +3 -0
  196. package/dist/runtime/logger.js +8 -0
  197. package/dist/runtime/third-party-scripts/nitro.plugin.d.ts +2 -0
  198. package/dist/runtime/third-party-scripts/nitro.plugin.js +48 -0
  199. package/dist/runtime/third-party-scripts/plugin.client.d.ts +2 -0
  200. package/dist/runtime/third-party-scripts/plugin.client.js +93 -0
  201. package/dist/runtime/third-party-scripts/utils.d.ts +1 -0
  202. package/dist/runtime/third-party-scripts/utils.js +2 -0
  203. package/dist/runtime/types.d.ts +80 -0
  204. package/dist/runtime/web-vitals/plugin.client.d.ts +13 -0
  205. package/dist/runtime/web-vitals/plugin.client.js +138 -0
  206. package/dist/runtime/web-vitals/utils.d.ts +36 -0
  207. package/dist/runtime/web-vitals/utils.js +15 -0
  208. package/dist/types.d.mts +3 -0
  209. package/package.json +70 -2
  210. /package/dist/{.gitkeep → client/_nuxt/third-party-scripts.tn0RQdqM.css} +0 -0
@@ -0,0 +1,65 @@
1
+ import { createError, defineEventHandler, readBody, setResponseStatus } from "h3";
2
+ const hydrationMismatches = [];
3
+ export default function(nitroApp) {
4
+ const getHandler = defineEventHandler(() => {
5
+ return {
6
+ mismatches: hydrationMismatches
7
+ };
8
+ });
9
+ const postHandler = defineEventHandler(async (event) => {
10
+ const body = await readBody(event);
11
+ assertPayload(body);
12
+ const payload = {
13
+ id: crypto.randomUUID(),
14
+ htmlPreHydration: body.htmlPreHydration,
15
+ htmlPostHydration: body.htmlPostHydration,
16
+ componentName: body.componentName,
17
+ fileLocation: body.fileLocation
18
+ };
19
+ hydrationMismatches.push(payload);
20
+ if (hydrationMismatches.length > 20) {
21
+ hydrationMismatches.shift();
22
+ }
23
+ nitroApp.hooks.callHook("hints:hydration:mismatch", payload);
24
+ setResponseStatus(event, 201);
25
+ return payload;
26
+ });
27
+ const deleteHandler = defineEventHandler(async (event) => {
28
+ const body = await readBody(event);
29
+ if (!body || !Array.isArray(body.id)) {
30
+ throw createError({ statusCode: 400, statusMessage: "Invalid payload" });
31
+ }
32
+ for (const id of body.id) {
33
+ const index = hydrationMismatches.findIndex((m) => m.id === id);
34
+ if (index !== -1) {
35
+ hydrationMismatches.splice(index, 1);
36
+ }
37
+ }
38
+ nitroApp.hooks.callHook("hints:hydration:cleared", { id: body.id });
39
+ setResponseStatus(event, 204);
40
+ });
41
+ nitroApp.router.add("/__nuxt_hints/hydration", getHandler, "get");
42
+ nitroApp.router.add("/__nuxt_hints/hydration", postHandler, "post");
43
+ nitroApp.router.add("/__nuxt_hints/hydration", deleteHandler, "delete");
44
+ nitroApp.hooks.hook("hints:sse:setup", (context) => {
45
+ context.unsubscribers.push(
46
+ nitroApp.hooks.hook("hints:hydration:mismatch", (mismatch) => {
47
+ context.eventStream.push({
48
+ data: JSON.stringify(mismatch),
49
+ event: "hints:hydration:mismatch"
50
+ });
51
+ }),
52
+ nitroApp.hooks.hook("hints:hydration:cleared", (payload) => {
53
+ context.eventStream.push({
54
+ data: JSON.stringify(payload.id),
55
+ event: "hints:hydration:cleared"
56
+ });
57
+ })
58
+ );
59
+ });
60
+ function assertPayload(body) {
61
+ if (typeof body !== "object" || body.htmlPreHydration !== void 0 && typeof body.htmlPreHydration !== "string" || body.htmlPostHydration !== void 0 && typeof body.htmlPostHydration !== "string" || typeof body.componentName !== "string" || typeof body.fileLocation !== "string") {
62
+ throw createError({ statusCode: 400, statusMessage: "Invalid payload" });
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,11 @@
1
+ import { defineNuxtPlugin, useNuxtApp } from "#imports";
2
+ import { defu } from "defu";
3
+ export default defineNuxtPlugin({
4
+ name: "@nuxt/hints:hydration",
5
+ setup() {
6
+ const nuxtApp = useNuxtApp();
7
+ nuxtApp.payload.__hints = defu(nuxtApp.payload.__hints, {
8
+ hydration: []
9
+ });
10
+ }
11
+ });
@@ -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,4 @@
1
+ export declare const logger: import("consola").ConsolaInstance;
2
+ export declare const HYDRATION_ROUTE = "/__nuxt_hints/hydration";
3
+ export declare const HYDRATION_SSE_ROUTE = "/__nuxt_hints/sse";
4
+ export declare function formatHTML(html: string | undefined): string;
@@ -0,0 +1,26 @@
1
+ import { HINTS_ROUTE, HINTS_SSE_ROUTE } from "../core/server/types.js";
2
+ import { createHintsLogger } from "../logger.js";
3
+ export const logger = createHintsLogger("hydration");
4
+ export const HYDRATION_ROUTE = `${HINTS_ROUTE}/hydration`;
5
+ export const HYDRATION_SSE_ROUTE = HINTS_SSE_ROUTE;
6
+ export function formatHTML(html) {
7
+ if (!html) return "";
8
+ let formatted = "";
9
+ let indent = 0;
10
+ const tags = html.split(/(<\/?[^>]+>)/g);
11
+ for (const tag of tags) {
12
+ if (!tag.trim()) continue;
13
+ if (tag.startsWith("</")) {
14
+ indent--;
15
+ formatted += "\n" + " ".repeat(Math.max(0, indent)) + tag;
16
+ } else if (tag.startsWith("<") && !tag.endsWith("/>") && !tag.includes("<!")) {
17
+ formatted += "\n" + " ".repeat(Math.max(0, indent)) + tag;
18
+ indent++;
19
+ } else if (tag.startsWith("<")) {
20
+ formatted += "\n" + " ".repeat(Math.max(0, indent)) + tag;
21
+ } else {
22
+ formatted += "\n" + " ".repeat(Math.max(0, indent)) + tag.trim();
23
+ }
24
+ }
25
+ return formatted.trim();
26
+ }
@@ -0,0 +1,11 @@
1
+ import type { DefineComponent } from 'vue';
2
+ import type { DirectImportInfo } from './schema.js';
3
+ export declare function useLazyComponentTracking(components?: DirectImportInfo[]): any;
4
+ /**
5
+ * Wrap components definition like with defineComponent or defineNuxtComponent or just sfc exports
6
+ */
7
+ export declare function __wrapMainComponent(component: DefineComponent, imports?: DirectImportInfo[]): DefineComponent;
8
+ /**
9
+ * Wrap imported components to track their usage.
10
+ */
11
+ export declare function __wrapImportedComponent(component: DefineComponent, componentName: string, importSource: string, importedBy: string): DefineComponent;
@@ -0,0 +1,52 @@
1
+ import { useNuxtApp } from "#imports";
2
+ import { defu } from "defu";
3
+ export function useLazyComponentTracking(components = []) {
4
+ const nuxtApp = useNuxtApp();
5
+ if (!nuxtApp.payload.__hints?.lazyHydrationState) {
6
+ nuxtApp.payload.__hints = defu(nuxtApp.payload.__hints, {
7
+ lazyHydrationState: {
8
+ directImports: /* @__PURE__ */ new Map(),
9
+ hasReported: false,
10
+ pageLoaded: false
11
+ }
12
+ });
13
+ }
14
+ const state = nuxtApp.payload.__hints.lazyHydrationState;
15
+ for (const comp of components) {
16
+ state.directImports.set(comp.componentName, comp);
17
+ }
18
+ return state;
19
+ }
20
+ export function __wrapMainComponent(component, imports = []) {
21
+ const originalSetup = component.setup;
22
+ component.setup = (props, ctx) => {
23
+ useLazyComponentTracking(imports);
24
+ return originalSetup ? originalSetup(props, ctx) : void 0;
25
+ };
26
+ return component;
27
+ }
28
+ export function __wrapImportedComponent(component, componentName, importSource, importedBy) {
29
+ if (component && component.name === "AsyncComponentWrapper") {
30
+ return component;
31
+ }
32
+ const originalSetup = component.setup;
33
+ component.setup = (props, ctx) => {
34
+ const state = useLazyComponentTracking();
35
+ if (state) {
36
+ if (!state.directImports.has(componentName)) {
37
+ state.directImports.set(componentName, {
38
+ componentName,
39
+ importSource,
40
+ importedBy,
41
+ rendered: false
42
+ });
43
+ }
44
+ const info = state.directImports.get(componentName);
45
+ if (info) {
46
+ info.rendered = true;
47
+ }
48
+ }
49
+ return originalSetup ? originalSetup(props, ctx) : void 0;
50
+ };
51
+ return component;
52
+ }
@@ -0,0 +1,2 @@
1
+ import type { NitroApp } from 'nitropack/types';
2
+ export default function (nitroApp: NitroApp): void;
@@ -0,0 +1,59 @@
1
+ import { createError, defineEventHandler, setResponseStatus, readBody } from "h3";
2
+ import { ComponentLazyLoadDataSchema } from "./schema.js";
3
+ import { parse, ValiError } from "valibot";
4
+ import { LAZY_LOAD_ROUTE } from "./utils.js";
5
+ const data = [];
6
+ export default function(nitroApp) {
7
+ const getHandler = defineEventHandler(() => {
8
+ return data;
9
+ });
10
+ const postHandler = defineEventHandler(async (event) => {
11
+ const body = await readBody(event);
12
+ let parsed;
13
+ try {
14
+ parsed = parse(ComponentLazyLoadDataSchema, body);
15
+ } catch (error) {
16
+ if (error instanceof ValiError) {
17
+ setResponseStatus(event, 400);
18
+ return { error: "Validation failed", message: error.message };
19
+ }
20
+ throw error;
21
+ }
22
+ data.push(parsed);
23
+ nitroApp.hooks.callHook("hints:lazy-load:report", parsed);
24
+ setResponseStatus(event, 201);
25
+ });
26
+ const deleteHandler = defineEventHandler(async (event) => {
27
+ const id = event.context.params?.id;
28
+ if (!id) {
29
+ throw createError({ statusCode: 400, message: "ID is required" });
30
+ }
31
+ const index = data.findIndex((item) => item.id === id);
32
+ if (index !== -1) {
33
+ data.splice(index, 1);
34
+ } else {
35
+ throw createError({ statusCode: 404, message: "Entry not found" });
36
+ }
37
+ nitroApp.hooks.callHook("hints:lazy-load:cleared", { id });
38
+ setResponseStatus(event, 204);
39
+ });
40
+ nitroApp.router.add(LAZY_LOAD_ROUTE, getHandler, "get");
41
+ nitroApp.router.add(LAZY_LOAD_ROUTE, postHandler, "post");
42
+ nitroApp.router.add(`${LAZY_LOAD_ROUTE}/:id`, deleteHandler, "delete");
43
+ nitroApp.hooks.hook("hints:sse:setup", (context) => {
44
+ context.unsubscribers.push(
45
+ nitroApp.hooks.hook("hints:lazy-load:report", (payload) => {
46
+ context.eventStream.push({
47
+ data: JSON.stringify(payload),
48
+ event: "hints:lazy-load:report"
49
+ });
50
+ }),
51
+ nitroApp.hooks.hook("hints:lazy-load:cleared", (payload) => {
52
+ context.eventStream.push({
53
+ data: JSON.stringify(payload.id),
54
+ event: "hints:lazy-load:cleared"
55
+ });
56
+ })
57
+ );
58
+ });
59
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,73 @@
1
+ import { defineNuxtPlugin, useNuxtApp, useRoute } from "#imports";
2
+ import { defu } from "defu";
3
+ import { useLazyComponentTracking } from "./composables.js";
4
+ import { logger, LAZY_LOAD_ROUTE } from "./utils.js";
5
+ import { isFeatureDevtoolsEnabled } from "../core/features.js";
6
+ export default defineNuxtPlugin({
7
+ name: "@nuxt/hints:lazy-load",
8
+ dependsOn: ["nuxt:router"],
9
+ setup() {
10
+ const nuxtApp = useNuxtApp();
11
+ nuxtApp.payload.__hints = defu(nuxtApp.payload.__hints, {
12
+ lazyComponents: []
13
+ });
14
+ if (import.meta.client) {
15
+ const state = useLazyComponentTracking();
16
+ if (!state) return;
17
+ nuxtApp.hook("app:suspense:resolve", () => {
18
+ if (state.hasReported || state.pageLoaded) return;
19
+ state.pageLoaded = true;
20
+ setTimeout(() => {
21
+ nuxtApp.runWithContext(() => checkAndReport(state));
22
+ }, 500);
23
+ });
24
+ }
25
+ }
26
+ });
27
+ function checkAndReport(state) {
28
+ if (state.hasReported) return;
29
+ state.hasReported = true;
30
+ const suggestions = [];
31
+ for (const [_, info] of state.directImports) {
32
+ if (!info.rendered) {
33
+ suggestions.push(info);
34
+ }
35
+ }
36
+ if (suggestions.length > 0) {
37
+ reportSuggestions(suggestions);
38
+ }
39
+ }
40
+ function reportSuggestions(suggestions) {
41
+ const route = useRoute();
42
+ const nuxtApp = useNuxtApp();
43
+ nuxtApp.payload.__hints.lazyComponents = suggestions;
44
+ logger.info(
45
+ `${suggestions.length} component has not been rendered in SSR nor rendered at hydration time. Consider lazy loading it:
46
+ `
47
+ );
48
+ for (const suggestion of suggestions) {
49
+ const lazyName = `Lazy${suggestion.componentName}`;
50
+ logger.info(
51
+ `${suggestion.componentName} \u2192 Use <${lazyName}> or \`defineAsyncComponent\` instead
52
+ Imported from: ${suggestion.importSource}
53
+ Used in: ${suggestion.importedBy}`
54
+ );
55
+ }
56
+ if (suggestions.length && isFeatureDevtoolsEnabled("lazyLoad")) {
57
+ const payload = {
58
+ id: `${encodeURIComponent(route.path)}-${Date.now()}`,
59
+ route: route.path,
60
+ state: {
61
+ pageLoaded: true,
62
+ hasReported: true,
63
+ directImports: suggestions
64
+ }
65
+ };
66
+ $fetch(LAZY_LOAD_ROUTE, {
67
+ method: "POST",
68
+ body: payload
69
+ }).catch((err) => {
70
+ logger.warn("Failed to send lazy-load data to server:", err);
71
+ });
72
+ }
73
+ }
@@ -0,0 +1,28 @@
1
+ import type { InferOutput } from 'valibot';
2
+ export declare const ComponentLazyLoadImportSchema: import("valibot").ObjectSchema<{
3
+ readonly componentName: import("valibot").StringSchema<undefined>;
4
+ readonly importSource: import("valibot").StringSchema<undefined>;
5
+ readonly importedBy: import("valibot").StringSchema<undefined>;
6
+ readonly rendered: import("valibot").BooleanSchema<undefined>;
7
+ }, undefined>;
8
+ export type DirectImportInfo = InferOutput<typeof ComponentLazyLoadImportSchema>;
9
+ export declare const ComponentLazyLoadDataSchema: import("valibot").ObjectSchema<{
10
+ readonly id: import("valibot").StringSchema<undefined>;
11
+ readonly route: import("valibot").StringSchema<undefined>;
12
+ readonly state: import("valibot").ObjectSchema<{
13
+ readonly pageLoaded: import("valibot").BooleanSchema<undefined>;
14
+ readonly hasReported: import("valibot").BooleanSchema<undefined>;
15
+ readonly directImports: import("valibot").ArraySchema<import("valibot").ObjectSchema<{
16
+ readonly componentName: import("valibot").StringSchema<undefined>;
17
+ readonly importSource: import("valibot").StringSchema<undefined>;
18
+ readonly importedBy: import("valibot").StringSchema<undefined>;
19
+ readonly rendered: import("valibot").BooleanSchema<undefined>;
20
+ }, undefined>, undefined>;
21
+ }, undefined>;
22
+ }, undefined>;
23
+ export type ComponentLazyLoadData = InferOutput<typeof ComponentLazyLoadDataSchema>;
24
+ export type ComponentLazyLoadState = {
25
+ directImports: Map<string, DirectImportInfo>;
26
+ hasReported: boolean;
27
+ pageLoaded: boolean;
28
+ };
@@ -0,0 +1,16 @@
1
+ import { array, boolean, object, string } from "valibot";
2
+ export const ComponentLazyLoadImportSchema = object({
3
+ componentName: string(),
4
+ importSource: string(),
5
+ importedBy: string(),
6
+ rendered: boolean()
7
+ });
8
+ export const ComponentLazyLoadDataSchema = object({
9
+ id: string(),
10
+ route: string(),
11
+ state: object({
12
+ pageLoaded: boolean(),
13
+ hasReported: boolean(),
14
+ directImports: array(ComponentLazyLoadImportSchema)
15
+ })
16
+ });
@@ -0,0 +1,2 @@
1
+ export declare const logger: import("consola").ConsolaInstance;
2
+ export declare const LAZY_LOAD_ROUTE = "/__nuxt_hints/lazy-load";
@@ -0,0 +1,4 @@
1
+ import { HINTS_ROUTE } from "../core/server/types.js";
2
+ import { createHintsLogger } from "../logger.js";
3
+ export const logger = createHintsLogger("lazyLoad");
4
+ export const LAZY_LOAD_ROUTE = `${HINTS_ROUTE}/lazy-load`;
@@ -0,0 +1,3 @@
1
+ import type { FeaturesName } from './core/types.js';
2
+ export declare function createHintsLogger(feature: FeaturesName): import("consola").ConsolaInstance;
3
+ export declare const logger: import("consola").ConsolaInstance;
@@ -0,0 +1,8 @@
1
+ import { createConsola } from "consola";
2
+ import { isFeatureLogsEnabled } from "./core/features.js";
3
+ export function createHintsLogger(feature) {
4
+ return createConsola({
5
+ level: isFeatureLogsEnabled(feature) ? void 0 : 0
6
+ }).withTag(`hints:${feature}`);
7
+ }
8
+ export const logger = createConsola().withTag("hints");
@@ -0,0 +1,2 @@
1
+ import type { NitroApp } from 'nitropack/types';
2
+ export default function (nitroApp: NitroApp): void;
@@ -0,0 +1,48 @@
1
+ export default function(nitroApp) {
2
+ nitroApp.hooks.hook("render:html", ({ head }) => {
3
+ head.unshift(`
4
+ <script>
5
+ window.__hints_TPC_start_time = Date.now();
6
+
7
+ function __hints_TPC_saveTime(script, startTime) {
8
+ script.__hints_TPC_end_time = Date.now();
9
+ const scriptStartTime = startTime || script.__hints_TPC_start_time || window.__hints_TPC_start_time;
10
+
11
+ const resourceEntries = performance.getEntriesByName(script.src)
12
+ const scriptEntry = resourceEntries.find(entry => entry.name === script.src)
13
+
14
+ if (scriptEntry) {
15
+ // Calculate parse + execute time using modern API
16
+ const navigationEntry = performance.getEntriesByType('navigation')[0]
17
+ const navigationStart = navigationEntry ? performance.timeOrigin : performance.timeOrigin
18
+
19
+ script.requestTime = (scriptEntry.responseStart - scriptEntry.requestStart);
20
+ script.downloadTime = (scriptEntry.responseEnd - scriptEntry.responseStart);
21
+ script.totalNetworkTime = (scriptEntry.responseEnd - scriptEntry.startTime);
22
+ script.parseExecuteTime = script.__hints_TPC_end_time - (navigationStart + scriptEntry.responseEnd);
23
+ script.loaded = true;
24
+ console.log('[@nuxt/hints]: \u{1F4CA} Detailed timing for', script.src, {
25
+ 'Request': script.requestTime.toFixed(2) + 'ms',
26
+ 'Download': script.downloadTime.toFixed(2) + 'ms',
27
+ 'Total Network': script.totalNetworkTime.toFixed(2) + 'ms',
28
+ 'Parse + Execute': script.parseExecuteTime.toFixed(2) + 'ms'
29
+ });
30
+ }
31
+ }
32
+ <\/script>
33
+ `);
34
+ head.push(`
35
+ <script>
36
+ for (const script of document.scripts) {
37
+ if (script.src && !script.src.startsWith(window.location.origin)) {
38
+ script.__hints_TPC_start_time = window.__hints_TPC_start_time || Date.now();
39
+ script.addEventListener('load', () => {
40
+ __hints_TPC_saveTime(script, script.__hints_TPC_start_time);
41
+ })
42
+ __hints_TPC_saveTime(script, script.__hints_TPC_start_time);
43
+ }
44
+ }
45
+ <\/script>
46
+ `);
47
+ });
48
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,93 @@
1
+ import { defineNuxtPlugin, ref, useNuxtApp } from "#imports";
2
+ import { defu } from "defu";
3
+ import { logger } from "./utils.js";
4
+ const EXTENSIONS_SCHEMES_RE = /^(chrome-extension|moz-extension|safari-extension|ms-browser-extension):/;
5
+ function isExtensionScript(src) {
6
+ try {
7
+ const url = new URL(src, window.location.origin);
8
+ return EXTENSIONS_SCHEMES_RE.test(url.protocol);
9
+ } catch {
10
+ return false;
11
+ }
12
+ }
13
+ function isSameOriginScript(src) {
14
+ try {
15
+ const url = new URL(src, window.location.origin);
16
+ return url.origin === window.location.origin;
17
+ } catch {
18
+ return false;
19
+ }
20
+ }
21
+ function isIgnoredScript(src) {
22
+ return isSameOriginScript(src) || isExtensionScript(src);
23
+ }
24
+ export default defineNuxtPlugin({
25
+ name: "nuxt-hints:third-party-scripts",
26
+ setup() {
27
+ const nuxtApp = useNuxtApp();
28
+ nuxtApp.payload.__hints = defu(nuxtApp.payload.__hints, {
29
+ thirdPartyScripts: ref([])
30
+ });
31
+ const scripts = nuxtApp.payload.__hints.thirdPartyScripts;
32
+ const isUsingNuxtScripts = !!nuxtApp.$scripts;
33
+ nuxtApp.hook("hints:scripts:added", (script) => {
34
+ scripts.value.push({ element: script, loaded: false });
35
+ });
36
+ nuxtApp.hook("hints:scripts:loaded", (script) => {
37
+ const existingScript = scripts.value.find((s) => s.element === script);
38
+ if (existingScript) {
39
+ existingScript.loaded = true;
40
+ } else {
41
+ logger.warn(`Script loaded event received for a script not tracked: ${script.src}. Please open an issue with a minimal reproduction if you think this is a bug.`);
42
+ scripts.value.push({ element: script, loaded: true });
43
+ }
44
+ });
45
+ nuxtApp.hooks.hookOnce("app:mounted", () => {
46
+ let hasThirdPartyScript = false;
47
+ for (const script of document.scripts) {
48
+ if (script.src && !isIgnoredScript(script.src)) {
49
+ hasThirdPartyScript = true;
50
+ onScriptAdded(script);
51
+ }
52
+ }
53
+ if (hasThirdPartyScript && !isUsingNuxtScripts) {
54
+ logger.info("Third-party scripts detected on page load: consider using @nuxt/scripts");
55
+ }
56
+ });
57
+ const observer = new MutationObserver((mutations) => {
58
+ for (const mutation of mutations) {
59
+ if (mutation.type === "childList") {
60
+ for (const node of mutation.addedNodes) {
61
+ if (isScript(node) && node.src && !isIgnoredScript(node.src)) {
62
+ onScriptAdded(node);
63
+ }
64
+ }
65
+ }
66
+ }
67
+ });
68
+ observer.observe(document.documentElement, {
69
+ childList: true,
70
+ subtree: true
71
+ });
72
+ function onScriptAdded(script) {
73
+ if (!script.crossOrigin) {
74
+ logger.warn(`Third-party script "${script.src}" is missing crossorigin attribute. Consider adding crossorigin="anonymous" for better security and error reporting.`);
75
+ }
76
+ nuxtApp.callHook("hints:scripts:added", script).then(() => {
77
+ if (!script.loaded) {
78
+ script.addEventListener("load", () => {
79
+ window.__hints_TPC_saveTime(script, script.__hints_TPC_start_time);
80
+ nuxtApp.callHook("hints:scripts:loaded", script);
81
+ });
82
+ } else {
83
+ window.__hints_TPC_saveTime(script, script.__hints_TPC_start_time);
84
+ nuxtApp.callHook("hints:scripts:loaded", script);
85
+ }
86
+ });
87
+ logger.info(`Dynamically added third-party script detected: ${script.src}`);
88
+ }
89
+ }
90
+ });
91
+ function isScript(node) {
92
+ return node.nodeName === "SCRIPT";
93
+ }
@@ -0,0 +1 @@
1
+ export declare const logger: import("consola").ConsolaInstance;
@@ -0,0 +1,2 @@
1
+ import { createHintsLogger } from "../logger.js";
2
+ export const logger = createHintsLogger("thirdPartyScripts");
@@ -0,0 +1,80 @@
1
+ import type { VNode, Ref } from 'vue'
2
+ import type { LCPMetricWithAttribution, INPMetricWithAttribution, CLSMetricWithAttribution } from 'web-vitals/attribution'
3
+ import type { HydrationMismatchPayload, LocalHydrationMismatch } from './hydration/types'
4
+ import type { DirectImportInfo, LazyHydrationState } from './lazy-load/composables'
5
+ import type { FeaturesName, FeatureFlags } from './core/types'
6
+
7
+ declare global {
8
+ interface Window {
9
+ __hints_TPC_start_time: number
10
+ __hints_TPC_saveTime: (script: HTMLScriptElement, startTime?: number) => void
11
+ }
12
+ interface HTMLScriptElement {
13
+ __hints_TPC_start_time?: number
14
+ __hints_TPC_end_time?: number
15
+ requestTime?: number
16
+ downloadTime?: number
17
+ totalNetworkTime?: number
18
+ parseExecuteTime?: number
19
+ loaded?: boolean
20
+ }
21
+ interface Element {
22
+ __vnode?: VNode
23
+ }
24
+ }
25
+
26
+ declare module '#app' {
27
+ interface RuntimeNuxtHooks {
28
+ 'hints:scripts:added': (script: HTMLScriptElement) => void
29
+ 'hints:scripts:loaded': (script: HTMLScriptElement) => void
30
+
31
+ 'hints:webvitals:sync': (webvitals: NuxtPayload['__hints']['webvitals']) => void
32
+ 'hints:webvitals:lcp': (metric: LCPMetricWithAttribution) => void
33
+ 'hints:webvitals:inp': (metric: INPMetricWithAttribution) => void
34
+ 'hints:webvitals:cls': (metric: CLSMetricWithAttribution) => void
35
+ }
36
+
37
+ interface NuxtApp {
38
+ __tracerOverlay: typeof import('vite-plugin-vue-tracer/client/overlay')
39
+ __tracerRecord: typeof import('vite-plugin-vue-tracer/client/record')
40
+ hints: {
41
+ config: {
42
+ features: Record<FeaturesName, FeatureFlags | boolean>
43
+ }
44
+ }
45
+ }
46
+
47
+ interface NuxtPayload {
48
+ __hints: {
49
+ lazyHydrationState?: LazyHydrationState
50
+ hydration: LocalHydrationMismatch[]
51
+ lazyComponents: DirectImportInfo[]
52
+ webvitals: {
53
+ lcp: Ref<LCPMetricWithAttribution[]>
54
+ inp: Ref<INPMetricWithAttribution[]>
55
+ cls: Ref<CLSMetricWithAttribution[]>
56
+ }
57
+ thirdPartyScripts: Ref<{
58
+ element: HTMLScriptElement
59
+ loaded: boolean
60
+ }[]>
61
+ }
62
+ }
63
+ }
64
+
65
+ declare module 'nitropack' {
66
+ interface NitroRuntimeHooks {
67
+ // Core hints hooks
68
+ 'hints:sse:setup': (context: import('./core/server/types').HintsSseContext) => void
69
+
70
+ // Hydration hooks
71
+ 'hints:hydration:mismatch': (payload: HydrationMismatchPayload) => void
72
+ 'hints:hydration:cleared': (payload: { id: string[] }) => void
73
+
74
+ // Lazy-load hooks
75
+ 'hints:lazy-load:report': (payload: import('./lazy-load/schema').ComponentLazyLoadData) => void
76
+ 'hints:lazy-load:cleared': (payload: { id: string }) => void
77
+ }
78
+ }
79
+
80
+ export {}