Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions packages/devtools/src/app/components/assets/Folder.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script setup lang="ts">
import type { Asset as AssetInfo } from '@rolldown/debug'
import type { ModuleDest, SessionContext } from '~~/shared/types'
import { computed } from 'vue'
import { toTree } from '../../utils/format'

const props = defineProps<{
assets: AssetInfo[]
session: SessionContext
}>()
const assetTree = computed(() => {
const nodes: ModuleDest[] = []
props.assets.forEach((i) => {
nodes.push({
full: i.filename,
path: i.filename,
})
})
return toTree(nodes, 'Project')
})
</script>

<template>
<div flex="~ gap-2">
<DisplayTreeNode
v-if="assets?.length"
flex-1
:node="assetTree"
icon="i-catppuccin:folder-dist catppuccin"
icon-open="i-catppuccin:folder-dist-open catppuccin"
:link="true"
link-query-key="asset"
/>
</div>
</template>

<style scoped>

</style>
25 changes: 25 additions & 0 deletions packages/devtools/src/app/components/assets/List.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script setup lang="ts">
import type { Asset as AssetInfo } from '@rolldown/debug'

defineProps<{
assets: AssetInfo[]
}>()
</script>

<template>
<div p4>
<div flex="~ col gap-1">
<template v-for="asset in assets" :key="asset.id">
<NuxtLink
:to="{ query: { asset: asset.filename } }"
font-mono border="~ rounded base" px2 py1 text-sm hover="bg-active"
>
<div flex="~ gap-1">
<DisplayFileIcon :filename="asset.filename" />
{{ asset.filename }}
</div>
</NuxtLink>
</template>
</div>
</div>
</template>
42 changes: 42 additions & 0 deletions packages/devtools/src/app/components/data/AssetDetailsLoader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script setup lang="ts">
import type { RolldownAssetInfo, RolldownChunkInfo, SessionContext } from '~~/shared/types'
import { useRpc } from '#imports'
import { useAsyncState } from '@vueuse/core'

const props = defineProps<{
asset: string
session: SessionContext
}>()

const emit = defineEmits<{
(e: 'close'): void
}>()

const rpc = useRpc()
const { state } = useAsyncState(
async () => {
const res = await rpc.value!['vite:rolldown:get-asset-details']?.({
session: props.session.id,
id: props.asset,
})
return {
asset: { ...res?.asset, type: 'asset' },
chunks: [{ ...res?.chunk, type: 'chunk' }],
} satisfies {
asset: RolldownAssetInfo
chunks: RolldownChunkInfo[]
}
},
null,
)
</script>

<template>
<div v-if="state?.asset" p4 relative h-full w-full of-auto pt12>
<DisplayCloseButton
absolute right-2 top-1.5
@click="emit('close')"
/>
<DataAssetDetails :asset="state.asset" :session="session" :chunks="state?.chunks" />
</div>
</template>
6 changes: 5 additions & 1 deletion packages/devtools/src/app/components/display/TreeNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ const props = withDefaults(defineProps<{
icon?: string
iconOpen?: string
link?: string | boolean
linkQueryKey?: string
padding?: number
open?: boolean
}>(), {
icon: 'i-catppuccin:folder icon-catppuccin',
iconOpen: 'i-catppuccin:folder-open icon-catppuccin',
padding: 0,
linkQueryKey: 'module',
})

const emit = defineEmits<{
Expand Down Expand Up @@ -53,11 +55,13 @@ function select(node: ModuleDest) {
v-for="e of Object.entries(node.children)"
:key="e[0]" :node="e[1]" :link="link"
:padding="padding + 1"
:link-query-key="linkQueryKey"
@select="select"
/>
<template v-for="i of node.items" :key="i.full">
<component
:is="link ? NuxtLink : 'div'"
:to="link ? (typeof link === 'string' ? link : { path: route.path, query: { ...route.query, module: i.full }, hash: location.hash }) : undefined"
:to="link ? (typeof link === 'string' ? link : { path: route.path, query: { ...route.query, [linkQueryKey]: i.full }, hash: location.hash }) : undefined"
text-sm
ws-nowrap
flex="~ gap-1"
Expand Down
24 changes: 24 additions & 0 deletions packages/devtools/src/app/pages/session/[session].vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ function closeFlowPanel() {
router.replace({ query: { ...route.query, module: undefined } })
}

function closeAssetPanel() {
router.replace({ query: { ...route.query, asset: undefined } })
}

useSideNav(() => {
if (!session.meta)
return []
Expand Down Expand Up @@ -101,5 +105,25 @@ onMounted(async () => {
/>
</div>
</div>

<!-- for assets -->
<div
v-if="route.query.asset" fixed inset-0
backdrop-blur-8 backdrop-brightness-95 z-panel-content
>
<div
:key="(route.query.asset as string)"
v-on-click-outside="closeAssetPanel"
fixed right-0 bottom-0 top-30 z-panel-content of-auto
bg-glass border="l t base rounded-tl-xl"
class="left-20 xl:left-100 2xl:left-150"
>
<DataAssetDetailsLoader
:asset="(route.query.asset as string)"
:session="session"
@close="closeAssetPanel"
/>
</div>
</div>
</div>
</template>
60 changes: 44 additions & 16 deletions packages/devtools/src/app/pages/session/[session]/assets.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
<script setup lang="ts">
import type { SessionContext } from '~~/shared/types'
import type { ClientSettings } from '~/state/settings'
import { useRpc } from '#imports'
import { useAsyncState } from '@vueuse/core'
import { settings } from '~/state/settings'

const props = defineProps<{
session: SessionContext
}>()

const assetViewTpyes = [
{
label: 'List',
value: 'list',
icon: 'i-ph-list-duotone',
},
{
label: 'Folder',
value: 'folder',
icon: 'i-ph-folder-duotone',
},
{
label: 'Flamechart',
value: 'flamechart',
icon: 'i-ph-chart-donut-duotone',
},
] as const
const rpc = useRpc()
const { state: assets, isLoading } = useAsyncState(
async () => {
Expand All @@ -16,26 +35,35 @@ const { state: assets, isLoading } = useAsyncState(
},
null,
)

function toggleDisplay(type: ClientSettings['assetViewType']) {
settings.value.assetViewType = type
}
</script>

<template>
<VisualLoading v-if="isLoading" />
<div p5 flex="~ col gap-4">
Assets
<!--
WIP:
- Use tree view to show assets
- Show asssets detail on the right panel
- View of Flamechart with nanovis
-->
<template v-for="asset of assets" :key="asset.filename">
<pre>{{
{
filename: asset.filename,
size: asset.size,
chunk: asset.chunk_id,
}
}}</pre>
<div v-else p4 flex="~ col gap-4">
<div flex="~ gap-2">
<button
v-for="viewType of assetViewTpyes"
:key="viewType.value"
btn-action
:class="settings.assetViewType === viewType.value ? 'bg-active' : 'grayscale op50'"
@click="toggleDisplay(viewType.value)"
>
<div :class="viewType.icon" />
{{ viewType.label }}
</button>
</div>
<template v-if="settings.assetViewType === 'list'">
<AssetsList v-if="assets?.length" :assets="assets" :session="session" />
</template>
<template v-else-if="settings.assetViewType === 'folder'">
<AssetsFolder v-if="assets?.length" :assets="assets" :session="session" />
</template>
<template v-else>
WIP: Flamechart view
</template>
</div>
</template>
2 changes: 2 additions & 0 deletions packages/devtools/src/app/state/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface ClientSettings {
flowExpandAssets: boolean
flowShowAllTransforms: boolean
flowShowAllLoads: boolean
assetViewType: 'list' | 'folder' | 'flamechart'
}

export const settings = useLocalStorage<ClientSettings>(
Expand All @@ -28,6 +29,7 @@ export const settings = useLocalStorage<ClientSettings>(
flowExpandAssets: true,
flowShowAllTransforms: false,
flowShowAllLoads: false,
assetViewType: 'list',
},
{
mergeDefaults: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { defineRpcFunction } from '../utils'

export const rolldownGetAssetDetails = defineRpcFunction({
name: 'vite:rolldown:get-asset-details',
type: 'query',
setup: ({ manager }) => {
return {
handler: async ({ session, id }: { session: string, id: string }) => {
const reader = await manager.loadSession(session)
const assets = await reader.manager.assets
const chunks = await reader.manager.chunks
const asset = assets.get(id)!
const chunk = chunks.get(asset.chunk_id!)!
return {
asset,
chunk,
}
},
}
},
})
2 changes: 2 additions & 0 deletions packages/devtools/src/node/rpc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { DefinitionsToFunctions, FilterDefinitions } from './types'
import { getPayload } from './functions/get-payload'
import { openInEditor } from './functions/open-in-editor'
import { openInFinder } from './functions/open-in-finder'
import { rolldownGetAssetDetails } from './functions/rolldown-get-asset-details'
import { rolldownGetAssetsList } from './functions/rolldown-get-assets-list'
import { rolldownGetChunksGraph } from './functions/rolldown-get-chunks-graph'
import { rolldownGetModuleInfo } from './functions/rolldown-get-module-info'
Expand All @@ -23,6 +24,7 @@ export const rpcFunctions = [
rolldownGetModuleTransforms,
rolldownGetChunksGraph,
rolldownGetAssetsList,
rolldownGetAssetDetails,
] as const

export type ServerFunctions = DefinitionsToFunctions<typeof rpcFunctions>
Expand Down
Loading