nuxt-devtools-observatory 0.1.28 → 0.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.mjs CHANGED
@@ -24,6 +24,156 @@ function extractScriptBlock(code) {
24
24
 
25
25
  const traverse$2 = _traverse.default ?? _traverse;
26
26
  const generate$2 = _generate.default ?? _generate;
27
+ const COMPOSABLE_RE = /\buse[A-Z]/;
28
+ const SKIP_LIST = /* @__PURE__ */ new Set([
29
+ // useFetch family — tracked by the fetch dashboard
30
+ "useFetch",
31
+ "useAsyncData",
32
+ "useLazyFetch",
33
+ "useLazyAsyncData",
34
+ // Nuxt auto-imports
35
+ "useCookie",
36
+ "useRequestEvent",
37
+ "useRequestHeaders",
38
+ "useRequestURL",
39
+ "useResponseHeader",
40
+ "useNuxtApp",
41
+ "useRuntimeConfig",
42
+ "useRoute",
43
+ "useRouter",
44
+ "useNuxtData",
45
+ "useError",
46
+ "useState",
47
+ "useAppConfig",
48
+ // Nuxt head
49
+ "useHead",
50
+ "useSeoMeta",
51
+ "useServerSeoMeta",
52
+ "useHeadSafe",
53
+ // Nuxt i18n (common plugin)
54
+ "useI18n",
55
+ "useLocalePath",
56
+ "useLocaleRoute",
57
+ // Vue built-ins
58
+ "useSlots",
59
+ "useAttrs",
60
+ "useModel",
61
+ "useTemplateRef",
62
+ "useId",
63
+ "useCssModule",
64
+ "useCssVars"
65
+ ]);
66
+ function composableTrackerPlugin() {
67
+ return {
68
+ name: "vite-plugin-observatory-composables",
69
+ enforce: "pre",
70
+ transform(code, id) {
71
+ const isVue = id.endsWith(".vue");
72
+ if (!isVue && !id.endsWith(".ts") && !id.endsWith(".js")) {
73
+ return;
74
+ }
75
+ if (id.includes("node_modules") || id.includes("composable-registry") || id.includes("provide-inject-registry") || id.includes("fetch-registry")) {
76
+ return;
77
+ }
78
+ let scriptCode = code;
79
+ let scriptStart = 0;
80
+ if (isVue) {
81
+ const block = extractScriptBlock(code);
82
+ if (!block) {
83
+ return null;
84
+ }
85
+ scriptCode = block.content;
86
+ scriptStart = block.start;
87
+ }
88
+ if (!COMPOSABLE_RE.test(scriptCode)) {
89
+ return;
90
+ }
91
+ try {
92
+ const ast = parse(scriptCode, {
93
+ sourceType: "module",
94
+ plugins: ["typescript"]
95
+ });
96
+ let modified = false;
97
+ traverse$2(ast, {
98
+ CallExpression(path) {
99
+ const callee = path.node.callee;
100
+ if (!t.isIdentifier(callee)) {
101
+ return;
102
+ }
103
+ const name = callee.name;
104
+ if (!COMPOSABLE_RE.test(name)) {
105
+ return;
106
+ }
107
+ if (SKIP_LIST.has(name)) {
108
+ return;
109
+ }
110
+ const binding = path.scope.getBinding(name);
111
+ if (binding?.path.isImportSpecifier() || binding?.path.isImportDefaultSpecifier()) {
112
+ const importDecl = binding.path.parentPath?.node;
113
+ const source = importDecl?.source?.value ?? "";
114
+ if (source && !source.startsWith(".") && !source.startsWith("/")) {
115
+ return;
116
+ }
117
+ }
118
+ let parent = path.parentPath;
119
+ let isWrapped = false;
120
+ while (parent) {
121
+ if (t.isCallExpression(parent.node) && t.isIdentifier(parent.node.callee) && parent.node.callee.name === "__trackComposable") {
122
+ isWrapped = true;
123
+ break;
124
+ }
125
+ parent = parent.parentPath ?? null;
126
+ }
127
+ if (isWrapped) {
128
+ return;
129
+ }
130
+ const comments = (path.node.leadingComments ?? []).concat(path.parentPath?.node?.leadingComments ?? []);
131
+ const ignored = comments.some((c) => c.value.includes("@devtools-ignore"));
132
+ if (ignored) {
133
+ return;
134
+ }
135
+ const args = path.node.arguments;
136
+ const loc = path.node.loc;
137
+ const fileName = id.split(/[\\/]/).pop() || id;
138
+ const meta = t.objectExpression([
139
+ t.objectProperty(t.identifier("file"), t.stringLiteral(fileName)),
140
+ t.objectProperty(t.identifier("line"), t.numericLiteral(loc?.start.line ?? 0))
141
+ ]);
142
+ path.replaceWith(
143
+ t.callExpression(t.identifier("__trackComposable"), [
144
+ t.stringLiteral(name),
145
+ t.arrowFunctionExpression([], t.callExpression(t.identifier(name), args)),
146
+ meta
147
+ ])
148
+ );
149
+ modified = true;
150
+ }
151
+ });
152
+ if (!modified) {
153
+ return null;
154
+ }
155
+ const importLine = `import { __trackComposable } from 'nuxt-devtools-observatory/runtime/composable-registry';
156
+ `;
157
+ const output = generate$2(ast, { retainLines: true }, scriptCode);
158
+ const alreadyImported = output.code.includes("nuxt-devtools-observatory/runtime/composable-registry");
159
+ const prefix = alreadyImported ? "" : importLine;
160
+ let finalCode;
161
+ if (isVue) {
162
+ finalCode = code.slice(0, scriptStart) + prefix + output.code + code.slice(scriptStart + scriptCode.length);
163
+ } else {
164
+ finalCode = prefix + output.code;
165
+ }
166
+ return { code: finalCode, map: output.map };
167
+ } catch (err) {
168
+ console.warn("[observatory] composable transform error:", err);
169
+ return null;
170
+ }
171
+ }
172
+ };
173
+ }
174
+
175
+ const traverse$1 = _traverse.default ?? _traverse;
176
+ const generate$1 = _generate.default ?? _generate;
27
177
  const FETCH_FNS = /* @__PURE__ */ new Set(["useFetch", "useAsyncData", "useLazyFetch", "useLazyAsyncData"]);
28
178
  function isHandlerExpression(node) {
29
179
  return Boolean(node && (t.isIdentifier(node) || t.isArrowFunctionExpression(node) || t.isFunctionExpression(node)));
@@ -63,7 +213,7 @@ function fetchInstrumentPlugin() {
63
213
  let needsFetchHandlerHelper = false;
64
214
  const hasFetchCallImport = scriptCode.includes("__devFetchCall");
65
215
  const hasFetchHandlerImport = scriptCode.includes("__devFetchHandler");
66
- traverse$2(ast, {
216
+ traverse$1(ast, {
67
217
  CallExpression(path) {
68
218
  if (path.node.__observatoryTransformed) {
69
219
  return;
@@ -181,7 +331,7 @@ function fetchInstrumentPlugin() {
181
331
  ].filter(Boolean);
182
332
  const importStatement = importNames.length ? `import { ${importNames.join(", ")} } from 'nuxt-devtools-observatory/runtime/fetch-registry';
183
333
  ` : "";
184
- const output = generate$2(ast, { retainLines: true }, scriptCode);
334
+ const output = generate$1(ast, { retainLines: true }, scriptCode);
185
335
  let finalCode;
186
336
  if (isVue) {
187
337
  finalCode = code.slice(0, scriptStart) + importStatement + output.code + code.slice(scriptStart + scriptCode.length);
@@ -200,8 +350,8 @@ function fetchInstrumentPlugin() {
200
350
  };
201
351
  }
202
352
 
203
- const traverse$1 = _traverse.default ?? _traverse;
204
- const generate$1 = _generate.default ?? _generate;
353
+ const traverse = _traverse.default ?? _traverse;
354
+ const generate = _generate.default ?? _generate;
205
355
  function provideInjectPlugin() {
206
356
  return {
207
357
  name: "vite-plugin-observatory-provide-inject",
@@ -233,7 +383,7 @@ function provideInjectPlugin() {
233
383
  plugins: ["typescript"]
234
384
  });
235
385
  let modified = false;
236
- traverse$1(ast, {
386
+ traverse(ast, {
237
387
  CallExpression(path) {
238
388
  const callee = path.node.callee;
239
389
  if (!t.isIdentifier(callee)) {
@@ -267,7 +417,7 @@ function provideInjectPlugin() {
267
417
  }
268
418
  const importLine = `import { __devProvide, __devInject } from 'nuxt-devtools-observatory/runtime/provide-inject-registry';
269
419
  `;
270
- const output = generate$1(ast, { retainLines: true }, scriptCode);
420
+ const output = generate(ast, { retainLines: true }, scriptCode);
271
421
  let finalCode;
272
422
  if (isVue) {
273
423
  finalCode = code.slice(0, scriptStart) + (scriptCode.includes("__devProvide") ? "" : importLine) + output.code + code.slice(scriptStart + scriptCode.length);
@@ -283,156 +433,6 @@ function provideInjectPlugin() {
283
433
  };
284
434
  }
285
435
 
286
- const traverse = _traverse.default ?? _traverse;
287
- const generate = _generate.default ?? _generate;
288
- const COMPOSABLE_RE = /\buse[A-Z]/;
289
- const SKIP_LIST = /* @__PURE__ */ new Set([
290
- // useFetch family — tracked by the fetch dashboard
291
- "useFetch",
292
- "useAsyncData",
293
- "useLazyFetch",
294
- "useLazyAsyncData",
295
- // Nuxt auto-imports
296
- "useCookie",
297
- "useRequestEvent",
298
- "useRequestHeaders",
299
- "useRequestURL",
300
- "useResponseHeader",
301
- "useNuxtApp",
302
- "useRuntimeConfig",
303
- "useRoute",
304
- "useRouter",
305
- "useNuxtData",
306
- "useError",
307
- "useState",
308
- "useAppConfig",
309
- // Nuxt head
310
- "useHead",
311
- "useSeoMeta",
312
- "useServerSeoMeta",
313
- "useHeadSafe",
314
- // Nuxt i18n (common plugin)
315
- "useI18n",
316
- "useLocalePath",
317
- "useLocaleRoute",
318
- // Vue built-ins
319
- "useSlots",
320
- "useAttrs",
321
- "useModel",
322
- "useTemplateRef",
323
- "useId",
324
- "useCssModule",
325
- "useCssVars"
326
- ]);
327
- function composableTrackerPlugin() {
328
- return {
329
- name: "vite-plugin-observatory-composables",
330
- enforce: "pre",
331
- transform(code, id) {
332
- const isVue = id.endsWith(".vue");
333
- if (!isVue && !id.endsWith(".ts") && !id.endsWith(".js")) {
334
- return;
335
- }
336
- if (id.includes("node_modules") || id.includes("composable-registry") || id.includes("provide-inject-registry") || id.includes("fetch-registry")) {
337
- return;
338
- }
339
- let scriptCode = code;
340
- let scriptStart = 0;
341
- if (isVue) {
342
- const block = extractScriptBlock(code);
343
- if (!block) {
344
- return null;
345
- }
346
- scriptCode = block.content;
347
- scriptStart = block.start;
348
- }
349
- if (!COMPOSABLE_RE.test(scriptCode)) {
350
- return;
351
- }
352
- try {
353
- const ast = parse(scriptCode, {
354
- sourceType: "module",
355
- plugins: ["typescript"]
356
- });
357
- let modified = false;
358
- traverse(ast, {
359
- CallExpression(path) {
360
- const callee = path.node.callee;
361
- if (!t.isIdentifier(callee)) {
362
- return;
363
- }
364
- const name = callee.name;
365
- if (!COMPOSABLE_RE.test(name)) {
366
- return;
367
- }
368
- if (SKIP_LIST.has(name)) {
369
- return;
370
- }
371
- const binding = path.scope.getBinding(name);
372
- if (binding?.path.isImportSpecifier() || binding?.path.isImportDefaultSpecifier()) {
373
- const importDecl = binding.path.parentPath?.node;
374
- const source = importDecl?.source?.value ?? "";
375
- if (source && !source.startsWith(".") && !source.startsWith("/")) {
376
- return;
377
- }
378
- }
379
- let parent = path.parentPath;
380
- let isWrapped = false;
381
- while (parent) {
382
- if (t.isCallExpression(parent.node) && t.isIdentifier(parent.node.callee) && parent.node.callee.name === "__trackComposable") {
383
- isWrapped = true;
384
- break;
385
- }
386
- parent = parent.parentPath ?? null;
387
- }
388
- if (isWrapped) {
389
- return;
390
- }
391
- const comments = (path.node.leadingComments ?? []).concat(path.parentPath?.node?.leadingComments ?? []);
392
- const ignored = comments.some((c) => c.value.includes("@devtools-ignore"));
393
- if (ignored) {
394
- return;
395
- }
396
- const args = path.node.arguments;
397
- const loc = path.node.loc;
398
- const fileName = id.split(/[\\/]/).pop() || id;
399
- const meta = t.objectExpression([
400
- t.objectProperty(t.identifier("file"), t.stringLiteral(fileName)),
401
- t.objectProperty(t.identifier("line"), t.numericLiteral(loc?.start.line ?? 0))
402
- ]);
403
- path.replaceWith(
404
- t.callExpression(t.identifier("__trackComposable"), [
405
- t.stringLiteral(name),
406
- t.arrowFunctionExpression([], t.callExpression(t.identifier(name), args)),
407
- meta
408
- ])
409
- );
410
- modified = true;
411
- }
412
- });
413
- if (!modified) {
414
- return null;
415
- }
416
- const importLine = `import { __trackComposable } from 'nuxt-devtools-observatory/runtime/composable-registry';
417
- `;
418
- const output = generate(ast, { retainLines: true }, scriptCode);
419
- const alreadyImported = output.code.includes("nuxt-devtools-observatory/runtime/composable-registry");
420
- const prefix = alreadyImported ? "" : importLine;
421
- let finalCode;
422
- if (isVue) {
423
- finalCode = code.slice(0, scriptStart) + prefix + output.code + code.slice(scriptStart + scriptCode.length);
424
- } else {
425
- finalCode = prefix + output.code;
426
- }
427
- return { code: finalCode, map: output.map };
428
- } catch (err) {
429
- console.warn("[observatory] composable transform error:", err);
430
- return null;
431
- }
432
- }
433
- };
434
- }
435
-
436
436
  const VIRTUAL_ID = "\0obs:vue-proxy";
437
437
  const PROXY_MODULE = `
438
438
  import {
@@ -1,8 +1,8 @@
1
1
  import { defineNuxtPlugin, useNuxtApp, useRuntimeConfig, useRouter } from "#app";
2
2
  import { nextTick } from "vue";
3
+ import { setupComposableRegistry } from "./composables/composable-registry.js";
3
4
  import { setupFetchRegistry } from "./composables/fetch-registry.js";
4
5
  import { setupProvideInjectRegistry } from "./composables/provide-inject-registry.js";
5
- import { setupComposableRegistry } from "./composables/composable-registry.js";
6
6
  import { setupRenderRegistry } from "./composables/render-registry.js";
7
7
  import { setupTransitionRegistry } from "./composables/transition-registry.js";
8
8
  export default defineNuxtPlugin(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-devtools-observatory",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "description": "Nuxt DevTools: useFetch Dashboard, provide/inject Graph, Composable Tracker, Render Heatmap",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -34,16 +34,18 @@
34
34
  "client"
35
35
  ],
36
36
  "scripts": {
37
- "dev": "nuxi dev playground",
38
- "docs:dev": "pnpm --dir docs dev",
39
- "docs:build": "pnpm --dir docs build",
40
- "docs:generate": "pnpm --dir docs generate",
37
+ "dev": "concurrently -r -n client,nuxt \"pnpm dev:client\" \"nuxi dev playground\"",
38
+ "dev:client": "vite build --watch --config client/vite.config.ts",
39
+ "docs:clean": "rm -rf docs/.nuxt docs/.output",
40
+ "docs:dev": "pnpm docs:clean && pnpm --dir docs dev",
41
+ "docs:build": "pnpm docs:clean && pnpm --dir docs build",
42
+ "docs:generate": "pnpm docs:clean && pnpm --dir docs generate",
41
43
  "docs:preview": "pnpm --dir docs preview",
42
44
  "build:client": "vite build --config client/vite.config.ts",
43
45
  "build": "npm run build:client && nuxt-module-build build",
44
46
  "prepack": "npm run build",
45
47
  "lint": "eslint .",
46
- "format": "prettier --write '**/*.{ts,vue,js,json}' --ignore-path .prettierignore && stylelint '**/*.{css,vue}' --ignore-pattern '**/.nuxt/**' --ignore-pattern '**/.output/**' --ignore-pattern 'coverage/**' --ignore-pattern 'client/dist/**' --ignore-pattern 'scripts/**' --fix && eslint --fix '**/*.{ts,vue,js}' --ignore-pattern '**/.nuxt/**' --ignore-pattern '**/.output/**' --ignore-pattern 'coverage/**' --ignore-pattern 'client/dist/**' --ignore-pattern 'scripts/**'",
48
+ "format": "prettier --write '**/*.{ts,vue,js,json}' --ignore-path .prettierignore && stylelint '**/*.{css,vue}' --ignore-pattern '**/.nuxt/**' --ignore-pattern '**/.output/**' --ignore-pattern 'coverage/**' --ignore-pattern 'client/dist/**' --ignore-pattern 'scripts/**' --fix && eslint --fix '**/*.{ts,vue,js}' --ignore-pattern '**/.nuxt/**' --ignore-pattern '**/.output/**' --ignore-pattern 'coverage/**' --ignore-pattern 'client/dist/**' --ignore-pattern 'scripts/**' --ignore-pattern 'docs/dist/**'",
47
49
  "typecheck": "vue-tsc --noEmit",
48
50
  "test": "vitest run",
49
51
  "test:watch": "vitest",
@@ -72,6 +74,7 @@
72
74
  "@types/node": "^25.5.0",
73
75
  "@vitejs/plugin-vue": "^6.0.0",
74
76
  "@vitest/coverage-v8": "^4.1.0",
77
+ "concurrently": "^9.2.1",
75
78
  "eslint": "^9.39.2",
76
79
  "eslint-config-prettier": "^10.1.8",
77
80
  "eslint-plugin-jsdoc": "^62.5.0",