Skip to content

Commit 8f246ef

Browse files
committed
feat: basic events reader and manager
1 parent b76b0fc commit 8f246ef

File tree

17 files changed

+379
-125
lines changed

17 files changed

+379
-125
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<script setup lang="ts">
2+
import { computed } from 'vue'
3+
import { parseReadablePath } from '~/utils/filepath'
4+
5+
const props = defineProps<{
6+
filepath?: string
7+
lineBreak?: boolean
8+
subpath?: boolean
9+
override?: string
10+
}>()
11+
12+
const root = '/'
13+
const parsed = computed(() => (props.filepath && root)
14+
? parseReadablePath(props.filepath, root)
15+
: { path: props.filepath || '' })
16+
</script>
17+
18+
<template>
19+
<span flex="~ gap-2 items-center" class="group">
20+
<span
21+
:class="[
22+
lineBreak ? '' : 'ws-nowrap of-hidden truncate',
23+
]"
24+
font-mono
25+
:title="override || filepath"
26+
>
27+
<template v-if="override">
28+
{{ override }}
29+
</template>
30+
<template v-else-if="parsed.moduleName">
31+
<span>{{ parsed.moduleName }}</span>
32+
<span v-if="subpath" op50>
33+
{{ parsed.path.slice(parsed.moduleName.length) }}
34+
</span>
35+
</template>
36+
<template v-else>
37+
{{ parsed.path }}
38+
</template>
39+
</span>
40+
<slot />
41+
</span>
42+
</template>
Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,40 @@
11
<script setup lang="ts">
2-
import type { Event } from '@rolldown/debug'
2+
import type { RolldownEvent } from '~/node/rpc/functions/rolldown-get-raw-events'
33
import { useRoute } from '#app/composables/router'
4-
import { onMounted, shallowRef } from 'vue'
4+
import { computed, onMounted, shallowRef } from 'vue'
55
import { backend } from '~/state/backend'
66
77
const params = useRoute().params
88
9-
const events = shallowRef<Event[]>([])
9+
const events = shallowRef<RolldownEvent[]>([])
1010
1111
onMounted(async () => {
1212
events.value = await backend.value!.functions['vite:rolldown:get-raw-events']({
1313
buildId: params.id,
1414
})
1515
})
16+
17+
const modules = computed(() => {
18+
const map = new Map<string, RolldownEvent[]>()
19+
for (const event of events.value) {
20+
if (!map.has(event.module_id)) {
21+
map.set(event.module_id, [])
22+
}
23+
map.get(event.module_id)!.push(event)
24+
}
25+
return map
26+
})
1627
</script>
1728

1829
<template>
19-
<div>
20-
{{ params }}
21-
{{ events }}
22-
</div>
30+
<table>
31+
<tbody>
32+
<tr v-for="[key, e] of modules.entries()" :key="key">
33+
<td>
34+
<UiFilepathItem :filepath="key" :subpath="true" />
35+
</td>
36+
<td>{{ e.length }}</td>
37+
</tr>
38+
</tbody>
39+
</table>
2340
</template>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { relative } from 'pathe'
2+
3+
export function isNodeModulePath(path: string) {
4+
return !!path.match(/[/\\]node_modules[/\\]/) || isPackageName(path)
5+
}
6+
7+
export function isPackageName(name: string) {
8+
return name[0] === '#' || !!name.match(/^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/)
9+
}
10+
11+
export function getModuleNameFromPath(path: string) {
12+
if (isPackageName(path))
13+
return path
14+
const match = path.replace(/\\/g, '/').match(/.*\/node_modules\/(.*)$/)?.[1]
15+
if (!match)
16+
return undefined
17+
if (match.startsWith('@'))
18+
return match.split('/').slice(0, 2).join('/')
19+
return match.split('/')[0]
20+
}
21+
22+
function getModuleSubpathFromPath(path: string) {
23+
const match = path.match(/.*\/node_modules\/(.*)$/)?.[1]
24+
if (!match)
25+
return undefined
26+
return match
27+
}
28+
29+
export function isBuiltInModule(name: string | undefined) {
30+
if (!name)
31+
return
32+
return ['nuxt', '#app', '#head', 'vue'].includes(name)
33+
}
34+
35+
export function parseReadablePath(path: string, root: string) {
36+
path = path.replace(/\\/g, '/')
37+
if (isPackageName(path)) {
38+
return {
39+
moduleName: path,
40+
path,
41+
}
42+
}
43+
const moduleName = getModuleNameFromPath(path)
44+
const subpath = getModuleSubpathFromPath(path)
45+
if (moduleName && subpath) {
46+
return {
47+
moduleName,
48+
path: subpath,
49+
}
50+
}
51+
// Workaround https://github.com/unjs/pathe/issues/113
52+
try {
53+
let result = relative(root, path)
54+
if (!result.startsWith('./') && !result.startsWith('../'))
55+
result = `./${result}`
56+
if (result.startsWith('./.nuxt/'))
57+
result = `#build${result.slice(7)}`
58+
return { path: result }
59+
}
60+
catch {
61+
return { path }
62+
}
63+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { Action, Event } from '@rolldown/debug'
2+
3+
export type RolldownEvent = Action & {
4+
event_id: string
5+
timestamp: string
6+
}
7+
8+
export class RolldownEventsManager {
9+
events: RolldownEvent[] = []
10+
11+
handleEvent(raw: Event) {
12+
const event = {
13+
...raw.fields.action,
14+
event_id: `${raw.timestamp}#${this.events.length}`,
15+
timestamp: raw.timestamp,
16+
}
17+
this.events.push(event)
18+
return event
19+
}
20+
21+
dispose() {
22+
this.events = []
23+
}
24+
25+
[Symbol.dispose]() {
26+
this.dispose()
27+
}
28+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import type { Event } from '@rolldown/debug'
2+
import fs from 'node:fs'
3+
import { parseJsonStreamWithConcatArrays } from '../utils/json-parse-stream'
4+
import { RolldownEventsManager } from './events-manager'
5+
6+
const readers: Map<string, RolldownEventsReader> = new Map()
7+
8+
export class RolldownEventsReader {
9+
lastBytes: number = 0
10+
lastTimestamp: number = 0
11+
manager = new RolldownEventsManager()
12+
13+
private constructor(
14+
readonly filepath: string,
15+
) {
16+
}
17+
18+
static get(filepath: string) {
19+
if (readers.has(filepath)) {
20+
return readers.get(filepath)!
21+
}
22+
const reader = new RolldownEventsReader(filepath)
23+
readers.set(filepath, reader)
24+
return reader
25+
}
26+
27+
async read() {
28+
const { mtime, size } = await fs.promises.stat(this.filepath)
29+
if (mtime.getTime() <= this.lastTimestamp) {
30+
return
31+
}
32+
const stream = fs.createReadStream(this.filepath, {
33+
start: this.lastBytes,
34+
})
35+
this.lastTimestamp = mtime.getTime()
36+
this.lastBytes = size
37+
await parseJsonStreamWithConcatArrays<Event>(
38+
stream,
39+
(event) => {
40+
this.manager.handleEvent(event)
41+
return event
42+
},
43+
)
44+
}
45+
46+
dispose() {
47+
readers.delete(this.filepath)
48+
this.manager.dispose()
49+
}
50+
51+
[Symbol.dispose]() {
52+
this.dispose()
53+
}
54+
}

packages/devtools/src/node/rpc/functions/get-payload.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineRpcFunction } from '../types'
1+
import { defineRpcFunction } from '../utils'
22

33
export const getPayload = defineRpcFunction({
44
name: 'vite:get-payload',

packages/devtools/src/node/rpc/functions/open-in-editor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineRpcFunction } from '../types'
1+
import { defineRpcFunction } from '../utils'
22

33
export const openInEditor = defineRpcFunction({
44
name: 'vite:open-in-editor',

packages/devtools/src/node/rpc/functions/open-in-finder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { defineRpcFunction } from '../types'
1+
import { defineRpcFunction } from '../utils'
22

33
export const openInFinder = defineRpcFunction({
44
name: 'vite:open-in-finder',
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { Event } from '@rolldown/debug'
2+
import { join } from 'pathe'
3+
import { RolldownEventsReader } from '../../rolldown/events-reader'
4+
import { defineRpcFunction } from '../utils'
5+
6+
export type RolldownEvent = Event['fields']['action'] & {
7+
timestamp: string
8+
}
9+
10+
export const rolldownGetModuleList = defineRpcFunction({
11+
name: 'vite:rolldown:get-module-list',
12+
type: 'query',
13+
setup: async ({ cwd }) => {
14+
return {
15+
handler: async ({ buildId }) => {
16+
const reader = RolldownEventsReader.get(join(cwd, '.rolldown', buildId, 'log.json'))
17+
await reader.read()
18+
const modules = new Set<string>()
19+
for (const event of reader.manager.events) {
20+
modules.add(event.module_id)
21+
}
22+
return Array.from(modules)
23+
},
24+
}
25+
},
26+
})

packages/devtools/src/node/rpc/functions/rolldown-get-raw-events.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
1-
import type { Event } from '@rolldown/debug'
2-
import fs from 'node:fs/promises'
31
import { join } from 'pathe'
4-
import { parseJsonStreamWithConcatArrays } from '../../utils/json-parse-stream'
5-
import { defineRpcFunction } from '../types'
2+
import { RolldownEventsReader } from '../../rolldown/events-reader'
3+
import { defineRpcFunction } from '../utils'
64

75
export const rolldownGetRawEvents = defineRpcFunction({
86
name: 'vite:rolldown:get-raw-events',
97
type: 'query',
108
setup: ({ cwd }) => {
119
return {
1210
handler: async ({ buildId }) => {
13-
const raw = await fs.open(join(cwd, '.rolldown', buildId, 'log.json'), 'r')
14-
const stream = raw.createReadStream()
15-
const events = await parseJsonStreamWithConcatArrays(stream) as Event[]
16-
return events
11+
const reader = RolldownEventsReader.get(join(cwd, '.rolldown', buildId, 'log.json'))
12+
await reader.read()
13+
return reader.manager.events
1714
},
1815
}
1916
},

0 commit comments

Comments
 (0)