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/README.md +30 -7
- package/client/dist/assets/index-BCaKoHBH.js +17 -0
- package/client/dist/assets/index-BmGW_M3W.css +1 -0
- package/client/dist/index.html +2 -2
- package/client/src/composables/useResizablePane.ts +65 -0
- package/client/src/stores/observatory.ts +12 -2
- package/client/src/style.css +203 -28
- package/client/src/views/ComposableTracker.vue +324 -259
- package/client/src/views/FetchDashboard.vue +104 -133
- package/client/src/views/ProvideInjectGraph.vue +99 -109
- package/client/src/views/RenderHeatmap.vue +191 -147
- package/client/src/views/TransitionTimeline.vue +166 -131
- package/client/tsconfig.json +3 -1
- package/client/vite.config.ts +8 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +156 -156
- package/dist/runtime/plugin.js +1 -1
- package/package.json +9 -6
- package/client/dist/assets/index-DXCGQOSF.js +0 -17
- package/client/dist/assets/index-htI4WwBU.css +0 -1
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$
|
|
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$
|
|
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
|
|
204
|
-
const 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
|
|
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
|
|
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 {
|
package/dist/runtime/plugin.js
CHANGED
|
@@ -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.
|
|
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
|
-
"
|
|
39
|
-
"docs:
|
|
40
|
-
"docs:
|
|
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",
|