Skip to content

Commit

Permalink
feat: plugin hook perf on each url (#66)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
sun0day and antfu committed Apr 11, 2023
1 parent 93489f0 commit 67c3166
Show file tree
Hide file tree
Showing 10 changed files with 288 additions and 47 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@
"@antfu/utils": "^0.7.2",
"@rollup/pluginutils": "^5.0.2",
"debug": "^4.3.4",
"echarts": "^5.4.2",
"fs-extra": "^11.1.1",
"kolorist": "^1.7.0",
"sirv": "^2.0.2",
"ufo": "^1.1.1"
"ufo": "^1.1.1",
"vue-echarts": "^6.5.4"
},
"devDependencies": {
"@antfu/eslint-config": "^0.38.4",
Expand Down
45 changes: 44 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/client/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ declare module '@vue/runtime-core' {
'Carbon:textWrap': typeof import('~icons/carbon/text-wrap')['default']
'Carbon:timer': typeof import('~icons/carbon/timer')['default']
CarbonArrowLeft: typeof import('~icons/carbon/arrow-left')['default']
CarbonArrowUpLeft: typeof import('~icons/carbon/arrow-up-left')['default']
CarbonChevronLeft: typeof import('~icons/carbon/chevron-left')['default']
CarbonMoon: typeof import('~icons/carbon/moon')['default']
CarbonSidePanelClose: typeof import('~icons/carbon/side-panel-close')['default']
CarbonSidePanelOpen: typeof import('~icons/carbon/side-panel-open')['default']
Expand All @@ -31,6 +33,7 @@ declare module '@vue/runtime-core' {
ModuleId: typeof import('./components/ModuleId.vue')['default']
ModuleList: typeof import('./components/ModuleList.vue')['default']
NavBar: typeof import('./components/NavBar.vue')['default']
PluginChart: typeof import('./components/PluginChart.vue')['default']
PluginName: typeof import('./components/PluginName.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Expand Down
4 changes: 2 additions & 2 deletions src/client/components/ModuleList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ defineProps<{
>
<ModuleId :id="m.id" />
<div v-if="listMode === 'detailed'" class="text-xs opacity-50">
<template v-for="i, idx in m.plugins.slice(1)" :key="i">
<template v-for="i, idx in m.plugins.slice(1).filter(plugin => plugin.transform !== undefined)" :key="i">
<span v-if="idx !== 0" class="opacity-20">|</span>
<span class="mx-0.5">
<PluginName :name="i" :hide="true" />
<PluginName :name="i.name" :hide="true" />
</span>
</template>
</div>
Expand Down
16 changes: 9 additions & 7 deletions src/client/components/NavBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { isDark, isStaticMode, refetch, toggleDark } from '../logic'
<template>
<nav class="font-light pr-6 pl-4 border-b border-main flex gap-4 h-54px children:my-auto flex-none">
<slot />
<button v-if="!isStaticMode" class="icon-btn text-lg" title="Refetch" @click="refetch()">
<carbon:renew />
</button>
<button class="icon-btn text-lg" title="Toggle Dark Mode" @click="toggleDark()">
<carbon-moon v-if="isDark" />
<carbon-sun v-else />
</button>
<slot name="actions">
<button v-if="!isStaticMode" class="icon-btn text-lg" title="Refetch" @click="refetch()">
<carbon:renew />
</button>
<button class="icon-btn text-lg" title="Toggle Dark Mode" @click="toggleDark()">
<carbon-moon v-if="isDark" />
<carbon-sun v-else />
</button>
</slot>
</nav>
</template>
163 changes: 163 additions & 0 deletions src/client/components/PluginChart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<script setup lang="ts">
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import type { BarSeriesOption } from 'echarts/charts'
import { BarChart } from 'echarts/charts'
import type {
SingleAxisComponentOption,
TooltipComponentOption,
} from 'echarts/components'
import {
GridComponent,
LegendComponent,
TitleComponent,
TooltipComponent,
} from 'echarts/components'
import VChart from 'vue-echarts'
import { defineProps } from 'vue'
import { isDark, searchResults } from '../logic'
const props = defineProps<{
plugin: string
hook: 'transform' | 'resolveId'
exit: () => void
}>()
use([
CanvasRenderer,
BarChart,
TitleComponent,
TooltipComponent,
LegendComponent,
GridComponent,
])
const sortedSearchResults = computed(() => {
return searchResults.value
.filter(({ plugins }) => {
const plugin = plugins.find(({ name, ...hooks }) => name === props.plugin && hooks[props.hook])
return plugin ? plugin[props.hook] : false
})
.sort((next, cur) => {
const nextTime = next.plugins.find(({ name }) => name === props.plugin)![props.hook]!
const curTime = cur.plugins.find(({ name }) => name === props.plugin)![props.hook]!
return nextTime - curTime
})
})
const yData = computed(() => {
return sortedSearchResults.value.map(({ id }) => id)
})
const seriesData = computed(() => {
return sortedSearchResults.value.map(({ plugins }) => {
const hookTime = plugins.find(({ name, ...hooks }) => name === props.plugin && hooks[props.hook])![props.hook]!
return {
itemStyle: { color: hookTime > 50 ? 'rgba(255, 0, 0, 400)' : hookTime > 20 ? '#faee58' : '#91cc75' },
value: hookTime,
}
})
})
const foregroundColor = computed(() => (isDark.value ? '#fff' : '#111'))
const backgroundColor = computed(() => (isDark.value ? '#111' : '#fff'))
const borderColor = computed(() => '#8888')
const option = computed(() => ({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
borderColor: borderColor.value,
backgroundColor: backgroundColor.value,
textStyle: {
color: foregroundColor.value,
},
formatter(params: any) {
const { name, seriesName, value, marker } = params[0]
return `${name}<br />${marker}${seriesName} (${value}ms)`
},
} satisfies TooltipComponentOption,
grid: {
left: '1%',
right: '2%',
top: '3%',
bottom: '2%',
containLabel: true,
},
xAxis: {
type: 'value',
minInterval: 10,
splitLine: {
lineStyle: {
color: borderColor.value,
},
},
axisLine: {
lineStyle: {
color: borderColor.value,
},
},
} satisfies SingleAxisComponentOption,
yAxis: {
type: 'category',
axisTick: { show: false },
axisLine: {
show: false,
lineStyle: {
opacity: 0.5,
color: borderColor.value,
},
},
axisLabel: {
color: foregroundColor.value,
fontSize: 12,
formatter(value: any) {
return value.split('/').slice(-3).join('/')
},
},
data: yData.value,
} satisfies SingleAxisComponentOption,
series: [
{
name: props.plugin,
type: 'bar',
barWidth: 12,
colorBy: 'data',
data: seriesData.value,
},
] satisfies BarSeriesOption[],
}))
const chartStyle = computed(() => {
return {
height: `${Math.max(sortedSearchResults.value.length * 24, 200)}px`,
}
})
</script>

<template>
<div class="bg-white dark:bg-[#111] border-none h-full w-[calc(100vw-100px)] overflow-auto shadow-lg transition-transform transform duration-300 translate-x-0">
<NavBar>
<a class="icon-btn !outline-none my-auto" @click="props.exit()">
<carbon-chevron-left />
</a>
<div class="text-sm font-mono my-auto">
Metrics for {{ props.plugin }}
</div>
<template #actions>
<div />
</template>
</NavBar>

<div p4>
<div v-if="!yData.length" flex="~" w-full h-40>
<div ma op50 italic>
No data for this plugin
</div>
</div>
<VChart class="w-100%" :style="chartStyle" :option="option" autoresize />
</div>
</div>
</template>
2 changes: 1 addition & 1 deletion src/client/logic/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const searchResults = computed(() => {
if (exactSearch.value) {
return data.filter(item =>
item.id.includes(searchText.value)
|| item.plugins.some(plugin => plugin.includes(searchText.value)),
|| item.plugins.some(plugin => plugin.name.includes(searchText.value)),
)
}
else {
Expand Down
20 changes: 18 additions & 2 deletions src/client/pages/index/plugins-metric.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { rpc } from '../../logic/rpc'
const data = ref(await rpc.getPluginMetrics(inspectSSR.value))
const selectedPlugin = ref('')
const displayHookOptions = ['transform', 'resolveId'].map(h => ({ label: h, value: h }))
const plugins = computed(() => {
Expand Down Expand Up @@ -48,6 +50,16 @@ onRefetch.on(async () => {
await refetch()
})
function selectPlugin(plugin: string) {
selectedPlugin.value = plugin
}
function clearPlugin() {
selectedPlugin.value = ''
}
watch(metricDisplayHook, clearPlugin)
getHot().then((hot) => {
if (hot) {
hot.on('vite-plugin-inspect:update', () => {
Expand All @@ -72,7 +84,8 @@ getHot().then((hot) => {
<div class="flex-auto" />
</NavBar>
<Container v-if="data" class="overflow-auto">
<div class="mb-4 grid grid-cols-[1fr_max-content_max-content_max-content_max-content_max-content_1fr] mt-2 whitespace-nowrap text-sm font-mono children:(px-4 py-2 border-b border-main align-middle)">
<PluginChart v-if="selectedPlugin" :plugin="selectedPlugin" :hook="metricDisplayHook" :exit="clearPlugin" />
<div v-else class="mb-4 grid grid-cols-[1fr_max-content_max-content_max-content_max-content_max-content_1fr] mt-2 whitespace-nowrap text-sm font-mono children:(px-4 py-2 border-b border-main align-middle)">
<div />
<div class="font-bold text-xs">
Name ({{ plugins.length }})
Expand All @@ -94,7 +107,10 @@ getHot().then((hot) => {

<template v-for="{ name, totalTime, invokeCount, enforce } in plugins" :key="name">
<div />
<div>
<div v-if="totalTime > 0" class="text-lime-600 dark:text-lime-200 cursor-pointer hover:underline" @click="selectPlugin(name)">
<PluginName :name="name" />
</div>
<div v-else>
<PluginName :name="name" />
</div>
<div class="text-center p0! flex items-center">
Expand Down

0 comments on commit 67c3166

Please sign in to comment.