diff --git a/package.json b/package.json index 2f62338a..91707340 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@iconify-json/carbon": "catalog:icons", "@iconify-json/catppuccin": "catalog:icons", "@iconify-json/codicon": "catalog:icons", + "@iconify-json/fluent": "catalog:icons", "@iconify-json/logos": "catalog:icons", "@iconify-json/ph": "catalog:icons", "@iconify-json/ri": "catalog:icons", diff --git a/packages/devtools-vite/src/app/components/data/PluginDetailsLoader.vue b/packages/devtools-vite/src/app/components/data/PluginDetailsLoader.vue new file mode 100644 index 00000000..f8c216f9 --- /dev/null +++ b/packages/devtools-vite/src/app/components/data/PluginDetailsLoader.vue @@ -0,0 +1,163 @@ + + + diff --git a/packages/devtools-vite/src/app/components/data/PluginDetailsTable.vue b/packages/devtools-vite/src/app/components/data/PluginDetailsTable.vue new file mode 100644 index 00000000..9be94675 --- /dev/null +++ b/packages/devtools-vite/src/app/components/data/PluginDetailsTable.vue @@ -0,0 +1,174 @@ + + + diff --git a/packages/devtools-vite/src/app/components/data/SearchPanel.vue b/packages/devtools-vite/src/app/components/data/SearchPanel.vue index 6b8cf256..38f6367f 100644 --- a/packages/devtools-vite/src/app/components/data/SearchPanel.vue +++ b/packages/devtools-vite/src/app/components/data/SearchPanel.vue @@ -3,18 +3,20 @@ import type { FilterMatchRule } from '~/utils/icon' import { useVModel } from '@vueuse/core' import { withDefaults } from 'vue' -interface ModelValue { search: string, selected?: string[] | null } +interface ModelValue { search?: string | false, selected?: string[] | null } const props = withDefaults( defineProps<{ rules: FilterMatchRule[] modelValue?: ModelValue + selectedContainerClass?: string }>(), { modelValue: () => ({ search: '', selected: null, }), + selectedContainerClass: '', }, ) @@ -71,7 +73,7 @@ function unselectToggle() { diff --git a/packages/devtools-vite/src/app/state/settings.ts b/packages/devtools-vite/src/app/state/settings.ts index 1cf21321..e3338302 100644 --- a/packages/devtools-vite/src/app/state/settings.ts +++ b/packages/devtools-vite/src/app/state/settings.ts @@ -16,6 +16,11 @@ export interface ClientSettings { assetViewType: 'list' | 'folder' | 'treemap' | 'sunburst' | 'flamegraph' chartAnimation: boolean moduleDetailsViewType: 'flow' | 'charts' | 'imports' + pluginDetailsViewType: 'flow' | 'charts' + pluginDetailsTableFields: string[] | null + pluginDetailsModuleTypes: string[] | null + pluginDetailsDurationSortType: string + pluginDetailSelectedHook: string } export const settings = useLocalStorage( @@ -34,6 +39,11 @@ export const settings = useLocalStorage( assetViewType: 'list', chartAnimation: true, moduleDetailsViewType: 'flow', + pluginDetailsViewType: 'flow', + pluginDetailsTableFields: null, + pluginDetailsModuleTypes: null, + pluginDetailsDurationSortType: '', + pluginDetailSelectedHook: '', }, { mergeDefaults: true, diff --git a/packages/devtools-vite/src/node/rolldown/events-manager.ts b/packages/devtools-vite/src/node/rolldown/events-manager.ts index 201e4b5f..e5ce4dcc 100644 --- a/packages/devtools-vite/src/node/rolldown/events-manager.ts +++ b/packages/devtools-vite/src/node/rolldown/events-manager.ts @@ -1,5 +1,5 @@ import type { Asset as AssetInfo, Chunk as ChunkInfo, Event, HookLoadCallEnd, HookLoadCallStart, HookResolveIdCallEnd, HookResolveIdCallStart, HookTransformCallEnd, HookTransformCallStart, Module as ModuleInfo } from '@rolldown/debug' -import type { ModuleBuildMetrics } from '~~/shared/types' +import type { ModuleBuildMetrics, PluginBuildMetrics } from '~~/shared/types' import { getContentByteSize } from '../utils/format' export type RolldownEvent = Event & { @@ -20,6 +20,7 @@ export class RolldownEventsManager { source_refs: Map = new Map() module_build_hook_events: Map = new Map() module_build_metrics: Map = new Map() + plugin_build_metrics: Map = new Map() build_start_time: number = 0 build_end_time: number = 0 @@ -34,7 +35,7 @@ export class RolldownEventsManager { } } - recordModuleBuildMetrics(event: ModuleBuildHookEvents) { + recordBuildMetrics(event: ModuleBuildHookEvents) { if (MODULE_BUILD_START_HOOKS.includes(event.action)) { this.module_build_hook_events.set(event.call_id, event) } @@ -42,15 +43,21 @@ export class RolldownEventsManager { const start = this.module_build_hook_events.get(event.call_id) const module_id = event.action === 'HookResolveIdCallEnd' ? event.resolved_id! : (event as HookLoadCallEnd | HookTransformCallEnd).module_id if (start) { + const pluginId = event.plugin_id const info = { id: event.event_id, timestamp_start: +start.timestamp, timestamp_end: +event.timestamp, duration: +event.timestamp - +start.timestamp, - plugin_id: event.plugin_id, + plugin_id: pluginId, plugin_name: event.plugin_name, } const module_build_metrics = this.module_build_metrics.get(module_id) ?? { resolve_ids: [], loads: [], transforms: [] } + const plugin_build_metrics = this.plugin_build_metrics.get(pluginId) ?? { + plugin_id: pluginId, + plugin_name: event.plugin_name, + calls: [], + } if (event.action === 'HookResolveIdCallEnd') { module_build_metrics.resolve_ids.push({ ...info, @@ -60,6 +67,11 @@ export class RolldownEventsManager { import_kind: (start as HookResolveIdCallStart).import_kind, resolved_id: event.resolved_id, }) + plugin_build_metrics.calls.push({ + ...info, + type: 'resolve', + module: (start as HookResolveIdCallStart).module_request, + }) } else if (event.action === 'HookLoadCallEnd') { if (!event.content && info.duration < DURATION_THRESHOLD) { @@ -70,6 +82,11 @@ export class RolldownEventsManager { type: 'load', content: event.content, }) + plugin_build_metrics.calls.push({ + ...info, + type: 'load', + module: event.module_id, + }) } else if (event.action === 'HookTransformCallEnd') { const _start = start as HookTransformCallStart @@ -87,7 +104,13 @@ export class RolldownEventsManager { source_code_size: getContentByteSize(_start.content!), transformed_code_size: getContentByteSize(_end.content!), }) + plugin_build_metrics.calls.push({ + ...info, + type: 'transform', + module: event.module_id, + }) } + this.plugin_build_metrics.set(pluginId, plugin_build_metrics) this.module_build_metrics.set(module_id, module_build_metrics) } } @@ -121,7 +144,7 @@ export class RolldownEventsManager { } this.interpretSourceRefs(event, 'content') - this.recordModuleBuildMetrics(event as ModuleBuildHookEvents) + this.recordBuildMetrics(event as ModuleBuildHookEvents) if ('module_id' in event) { if (this.modules.has(event.module_id)) diff --git a/packages/devtools-vite/src/node/rpc/functions/rolldown-get-plugin-details.ts b/packages/devtools-vite/src/node/rpc/functions/rolldown-get-plugin-details.ts new file mode 100644 index 00000000..9f8b6460 --- /dev/null +++ b/packages/devtools-vite/src/node/rpc/functions/rolldown-get-plugin-details.ts @@ -0,0 +1,37 @@ +import type { RolldownPluginBuildMetrics } from '~~/shared/types' +import { defineRpcFunction } from '@vitejs/devtools-kit' +import { getLogsManager } from '../utils' + +export const rolldownGetPluginDetails = defineRpcFunction({ + name: 'vite:rolldown:get-plugin-details', + type: 'query', + setup: (context) => { + const manager = getLogsManager(context) + return { + handler: async ({ session, id }: { session: string, id: string }) => { + const reader = await manager.loadSession(session) + const pluginBuildMetrics = reader.manager.plugin_build_metrics.get(+id)! + if (!pluginBuildMetrics) { + const plugin = reader.meta!.plugins!.find(p => p.plugin_id === +id)! + return { + plugin_name: plugin?.name, + plugin_id: +id, + calls: [], + loadMetrics: [], + resolveIdMetrics: [], + transformMetrics: [], + } satisfies RolldownPluginBuildMetrics + } + const resolveIdMetrics = pluginBuildMetrics.calls.filter(c => c.type === 'resolve')! + const loadMetrics = pluginBuildMetrics.calls.filter(c => c.type === 'load')! + const transformMetrics = pluginBuildMetrics.calls.filter(c => c.type === 'transform')! + return { + ...pluginBuildMetrics, + resolveIdMetrics, + loadMetrics, + transformMetrics, + } satisfies RolldownPluginBuildMetrics + }, + } + }, +}) diff --git a/packages/devtools-vite/src/node/rpc/index.ts b/packages/devtools-vite/src/node/rpc/index.ts index ad23f62a..8236cda4 100644 --- a/packages/devtools-vite/src/node/rpc/index.ts +++ b/packages/devtools-vite/src/node/rpc/index.ts @@ -8,6 +8,7 @@ import { rolldownGetChunksGraph } from './functions/rolldown-get-chunks-graph' import { rolldownGetModuleInfo } from './functions/rolldown-get-module-info' import { rolldownGetModuleRawEvents } from './functions/rolldown-get-module-raw-events' import { rolldownGetModuleTransforms } from './functions/rolldown-get-module-transforms' +import { rolldownGetPluginDetails } from './functions/rolldown-get-plugin-details' import { rolldownGetRawEvents } from './functions/rolldown-get-raw-events' import { rolldownGetSessionSummary } from './functions/rolldown-get-session-summary' import { rolldownListSessions } from './functions/rolldown-list-sessions' @@ -25,6 +26,7 @@ export const rpcFunctions = [ rolldownGetChunksGraph, rolldownGetAssetsList, rolldownGetAssetDetails, + rolldownGetPluginDetails, ] as const export type ServerFunctions = RpcDefinitionsToFunctions diff --git a/packages/devtools-vite/src/shared/types/data.ts b/packages/devtools-vite/src/shared/types/data.ts index da66e12f..b2df94c6 100644 --- a/packages/devtools-vite/src/shared/types/data.ts +++ b/packages/devtools-vite/src/shared/types/data.ts @@ -17,6 +17,24 @@ export interface ModuleBuildMetrics { loads: RolldownModuleLoadInfo[] transforms: Array & { source_code_size: number, transformed_code_size: number }> } + +export interface PluginBuildInfo { + type: 'resolve' | 'load' | 'transform' + id: string + duration: number + plugin_id: number + plugin_name: string + module: string + timestamp_start: number + timestamp_end: number +} + +export interface PluginBuildMetrics { + plugin_name: string + plugin_id: number + calls: PluginBuildInfo[] +} + export type { PluginItem } export interface ModuleListItem { @@ -57,6 +75,12 @@ export interface ModuleTreeNode { items: ModuleDest[] } +export type RolldownPluginBuildMetrics = PluginBuildMetrics & { + resolveIdMetrics: PluginBuildInfo[] + loadMetrics: PluginBuildInfo[] + transformMetrics: PluginBuildInfo[] +} + export interface RolldownResolveInfo { type: 'resolve' id: string diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d79e8d56..5a8b399e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -181,6 +181,9 @@ catalogs: '@iconify-json/codicon': specifier: ^1.2.25 version: 1.2.25 + '@iconify-json/fluent': + specifier: ^1.2.28 + version: 1.2.28 '@iconify-json/logos': specifier: ^1.2.5 version: 1.2.5 @@ -263,6 +266,9 @@ importers: '@iconify-json/codicon': specifier: catalog:icons version: 1.2.25 + '@iconify-json/fluent': + specifier: catalog:icons + version: 1.2.28 '@iconify-json/logos': specifier: catalog:icons version: 1.2.5 @@ -1047,6 +1053,9 @@ packages: '@iconify-json/codicon@1.2.25': resolution: {integrity: sha512-Kp9GIuqZ/eGjYyR2JfrjwsvkTnNgtSEYw1oxmgSuULQkAZzZdh0XBlY0mqNtY5TdfCgG5GMHKe/PltGt5tU+IQ==} + '@iconify-json/fluent@1.2.28': + resolution: {integrity: sha512-Lnaf38ruWopJv8PdEsK8vXgxKpFSsKED2kI0/575ZXg6XHUMvzSuLlldAVAOEWZmMVPJFC0MXe07Ybnh1ziLvw==} + '@iconify-json/logos@1.2.5': resolution: {integrity: sha512-WR8+9kFwx1tIR+hWpKYC+rpVkAuzHyaVxZRfhGGIjqCfgbodK7rS4+OZhktdKtZKKcdmhpLZKvlmRm4IA4dKRg==} @@ -7224,6 +7233,10 @@ snapshots: dependencies: '@iconify/types': 2.0.0 + '@iconify-json/fluent@1.2.28': + dependencies: + '@iconify/types': 2.0.0 + '@iconify-json/logos@1.2.5': dependencies: '@iconify/types': 2.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 81faf6e3..3a4d8d84 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -72,6 +72,7 @@ catalogs: '@iconify-json/carbon': ^1.2.11 '@iconify-json/catppuccin': ^1.2.13 '@iconify-json/codicon': ^1.2.25 + '@iconify-json/fluent': ^1.2.28 '@iconify-json/logos': ^1.2.5 '@iconify-json/ph': ^1.2.2 '@iconify-json/ri': ^1.2.5