Skip to content

Commit f18df2c

Browse files
committed
refactor: enhance performance tracking and add new features to the Qwik DevTools
- Updated context management in `context.ts` for better type safety. - Introduced new performance tracking interfaces and types in `globals.ts`. - Added performance monitoring capabilities in the UI, including a new `Performance` component. - Implemented data handling for performance metrics in `computePerfViewModel.ts` and `transformPerformanceData.ts`. - Enhanced the `RenderTree` component to support performance data visualization. - Updated the `package.json` for development server configuration. - Removed deprecated `HookDataStore` and related utilities to streamline codebase.
1 parent 9f84c67 commit f18df2c

32 files changed

+2241
-822
lines changed

packages/kit/src/context.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,37 @@ import {
99
} from './globals';
1010
import { ServerRpc, ClientRpc } from './types';
1111

12+
type GlobalTarget = Record<string, unknown>;
13+
const t = target as unknown as GlobalTarget;
14+
1215
export function getViteClientContext(): ViteClientContext {
13-
return target[CLIENT_CTX];
16+
return t[CLIENT_CTX] as ViteClientContext;
1417
}
1518

1619
export function setViteClientContext(ctx: ViteClientContext) {
17-
target[CLIENT_CTX] = ctx;
20+
t[CLIENT_CTX] = ctx;
1821
}
1922

2023
export function getViteServerContext() {
21-
return target[SERVER_CTX];
24+
return t[SERVER_CTX] as ViteServerContext;
2225
}
2326

2427
export function setViteServerContext(ctx: ViteServerContext) {
25-
target[SERVER_CTX] = ctx;
28+
t[SERVER_CTX] = ctx;
2629
}
2730

2831
export function getViteServerRpc() {
29-
return target[SERVER_RPC];
32+
return t[SERVER_RPC] as ServerRpc;
3033
}
3134

3235
export function setViteServerRpc(rpc: ServerRpc) {
33-
target[SERVER_RPC] = rpc;
36+
t[SERVER_RPC] = rpc;
3437
}
3538

3639
export function getViteClientRpc() {
37-
return target[CLIENT_RPC];
40+
return t[CLIENT_RPC] as ClientRpc;
3841
}
3942

4043
export function setViteClientRpc(rpc: ClientRpc) {
41-
target[CLIENT_RPC] = rpc;
44+
t[CLIENT_RPC] = rpc;
4245
}

packages/kit/src/globals.ts

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ViteDevServer } from 'vite';
2-
import { ClientRpc, ServerRpc } from './types';
2+
import { ClientRpc, ParsedStructure, ServerRpc } from './types';
33

44
interface EventEmitter {
55
on: (name: string, handler: (data: any) => void) => void;
@@ -14,9 +14,71 @@ export const SERVER_CTX = '__qwik_server_ctx__';
1414
export const SERVER_RPC = '__qwik_server_rpc__';
1515
export const CLIENT_RPC = '__qwik_client_rpc__';
1616

17+
// Devtools global state types
18+
export type QwikPerfPhaseRemembered = 'ssr' | 'csr';
19+
20+
export interface QwikPerfErrorRemembered {
21+
name: string;
22+
message: string;
23+
}
24+
25+
export interface QwikPerfEntryRemembered {
26+
id: number;
27+
component: string;
28+
phase: QwikPerfPhaseRemembered;
29+
duration: number;
30+
start: number;
31+
end: number;
32+
error?: QwikPerfErrorRemembered;
33+
/**
34+
* Present for wrapped `_component_` render-function modules; helps de-dupe.
35+
*/
36+
viteId?: string;
37+
/**
38+
* Present for wrapped `_component_` render-function modules.
39+
*/
40+
renderCount?: number;
41+
}
42+
43+
export interface QwikPerfStoreRemembered {
44+
ssr: QwikPerfEntryRemembered[];
45+
csr: QwikPerfEntryRemembered[];
46+
47+
}
48+
49+
export interface DevtoolsRenderStats {
50+
/**
51+
* In-memory performance store written by devtools instrumentation.
52+
* (Populated at runtime; optional in types.)
53+
*/
54+
perf?: QwikPerfStoreRemembered;
55+
}
56+
export interface ComponentDevtoolsState {
57+
hooks: ParsedStructure[];
58+
stats: DevtoolsRenderStats;
59+
}
60+
61+
62+
declare global {
63+
interface Window {
64+
QWIK_DEVTOOLS_GLOBAL_STATE?: Record<string, ComponentDevtoolsState>;
65+
/**
66+
* Performance store (CSR + injected SSR snapshot).
67+
* Written by `@devtools/plugin` instrumentation.
68+
*/
69+
__QWIK_PERF__?: QwikPerfStoreRemembered;
70+
}
71+
}
72+
1773
declare global {
18-
var __qwik_client_ctx__: ViteClientContext;
19-
var __qwik_server_ctx__: ViteServerContext;
20-
var __qwik_server_rpc__: ServerRpc;
21-
var __qwik_client_rpc__: ClientRpc;
74+
// SSR collector lives on `process` (preferred) or `globalThis` via dynamic properties.
75+
// We type the `process` case here to avoid `any` in plugin code.
76+
namespace NodeJS {
77+
interface Process {
78+
__QWIK_SSR_PERF__?: QwikPerfEntryRemembered[];
79+
__QWIK_SSR_PERF_SET__?: Set<string>;
80+
__QWIK_SSR_PERF_ID__?: number;
81+
__QWIK_SSR_PERF_INDEX__?: Record<string, number>;
82+
}
83+
}
2284
}

packages/kit/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export * from './client';
22
export * from './server';
33
export * from './context';
44
export * from './types';
5-
export * from './constants';
5+
export * from './constants';
6+
export * from './globals';

packages/playgrounds/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"build.preview": "vite build --ssr src/entry.preview.tsx",
1818
"build.types": "tsc --incremental --noEmit",
1919
"deploy": "echo 'Run \"npm run qwik add\" to install a server adapter'",
20-
"dev": "MODE=dev vite --mode ssr",
20+
"dev": "MODE=dev vite --mode ssr --port 5174",
2121
"dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
2222
"fmt": "prettier --write .",
2323
"fmt.check": "prettier --check .",

packages/plugin/src/index.ts

Lines changed: 1 addition & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,114 +1 @@
1-
import { ResolvedConfig, type Plugin } from 'vite';
2-
import { getServerFunctions } from './rpc';
3-
import { createServerRpc, setViteServerContext, VIRTUAL_QWIK_DEVTOOLS_KEY, INNER_USE_HOOK } from '@devtools/kit';
4-
import VueInspector from 'vite-plugin-inspect'
5-
import useCollectHooksSource from './utils/useCollectHooks'
6-
import { parseQwikCode } from './parse/parse';
7-
import { startPreloading } from './npm/index';
8-
import updateConf from './utils/updateConf';
9-
import {debug} from 'debug'
10-
11-
const log = debug('qwik:devtools:plugin');
12-
export function qwikDevtools(): Plugin[] {
13-
let _config: ResolvedConfig;
14-
const qwikData = new Map<string, any>();
15-
let preloadStarted = false;
16-
const qwikDevtoolsPlugin: Plugin = {
17-
name: 'vite-plugin-qwik-devtools',
18-
apply: 'serve',
19-
resolveId(id) {
20-
// Normalize to a stable, absolute-like id so Qwik can generate runtime chunks
21-
const clean = id.split('?')[0].split('#')[0];
22-
if (
23-
clean === VIRTUAL_QWIK_DEVTOOLS_KEY ||
24-
clean === `/${VIRTUAL_QWIK_DEVTOOLS_KEY}` ||
25-
clean === `\u0000${VIRTUAL_QWIK_DEVTOOLS_KEY}` ||
26-
clean === `/@id/${VIRTUAL_QWIK_DEVTOOLS_KEY}`
27-
) {
28-
return `/${VIRTUAL_QWIK_DEVTOOLS_KEY}`;
29-
}
30-
},
31-
load(id) {
32-
if (
33-
id === `/${VIRTUAL_QWIK_DEVTOOLS_KEY}` ||
34-
id === VIRTUAL_QWIK_DEVTOOLS_KEY ||
35-
id === `\u0000${VIRTUAL_QWIK_DEVTOOLS_KEY}` ||
36-
id === `/@id/${VIRTUAL_QWIK_DEVTOOLS_KEY}`
37-
) {
38-
return {
39-
code: useCollectHooksSource,
40-
map: { mappings: '' },
41-
};
42-
}
43-
},
44-
configResolved(viteConfig) {
45-
_config = viteConfig;
46-
updateConf(_config);
47-
// Start preloading as early as possible, right after config is resolved
48-
if (!preloadStarted) {
49-
preloadStarted = true;
50-
startPreloading({ config: _config }).catch((err) => {
51-
log('[Qwik DevTools] Failed to start preloading:', err);
52-
});
53-
}
54-
},
55-
transform: {
56-
order: 'pre',
57-
handler(code, id) {
58-
const mode = process.env.MODE;
59-
// Ensure virtual import is present at the very top once when a component$ is present
60-
if (id.endsWith('.tsx') && code.includes('component$')) {
61-
if (!code.includes(VIRTUAL_QWIK_DEVTOOLS_KEY)) {
62-
63-
const importLine = `import { ${INNER_USE_HOOK} } from '${VIRTUAL_QWIK_DEVTOOLS_KEY}';\n`
64-
code = importLine + code
65-
}else {
66-
log('importing virtual qwik devtools', VIRTUAL_QWIK_DEVTOOLS_KEY, code);
67-
}
68-
code = parseQwikCode(code, {path: id})
69-
}
70-
// Only transform the root component file
71-
if (id.endsWith('root.tsx')) {
72-
const importPath =
73-
mode === 'dev' ? '@devtools/ui' : '@qwik.dev/devtools/ui';
74-
// Check if QwikDevtools import already exists
75-
if (!code.includes(importPath)) {
76-
// Add import for QwikDevtools using the correct package name
77-
code = `import { QwikDevtools } from '${importPath}';\n${code}`;
78-
}
79-
80-
// Find the closing body tag and append QwikDevtools at the end of body
81-
const match = code.match(/<body[^>]*>([\s\S]*?)<\/body>/);
82-
if (match) {
83-
const bodyContent = match[1];
84-
const newBodyContent = `${bodyContent}\n <QwikDevtools />`;
85-
code = code.replace(bodyContent, newBodyContent);
86-
}
87-
88-
return {
89-
code,
90-
map: null,
91-
};
92-
}
93-
94-
return {
95-
code,
96-
map: { mappings: '' },
97-
};
98-
},
99-
},
100-
configureServer(server) {
101-
setViteServerContext(server as any);
102-
103-
const rpcFunctions = getServerFunctions({ server, config: _config, qwikData });
104-
105-
createServerRpc(rpcFunctions);
106-
107-
// Preloading should have already started in configResolved
108-
},
109-
}
110-
return [
111-
qwikDevtoolsPlugin,
112-
VueInspector(), // Add the VueInspector plugin instance
113-
];
114-
}
1+
export { qwikDevtools } from './plugin';

0 commit comments

Comments
 (0)