Skip to content

Commit

Permalink
feat: filter for used/unused components, close #358
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Aug 10, 2023
1 parent 509a1ec commit 5012012
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 51 deletions.
7 changes: 7 additions & 0 deletions packages/devtools-kit/src/_types/integrations.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { RouteRecordNormalized } from 'vue-router'
import type { Import, UnimportMeta } from 'unimport'
import type { Component } from 'nuxt/schema'

export interface HookInfo {
name: string
Expand Down Expand Up @@ -195,3 +196,9 @@ export interface ComponentRelationship {
id: string
deps: string[]
}

export interface ComponentWithRelationships {
component: Component
dependencies?: string[]
dependents?: string[]
}
2 changes: 1 addition & 1 deletion packages/devtools-ui-kit/src/components/NSelectTabs.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const input = useVModel(props, 'modelValue', emit, { passive: true })

<template>
<fieldset
class="n-select-tabs flex flex-inline flex-wrap items-center border n-border-base rounded focus-within:n-focus-base focus-within:border-context n-bg-base"
class="n-select-tabs flex flex-inline flex-wrap items-center border n-border-base rounded n-bg-base"
>
<label
v-for="i, idx of options"
Expand Down
20 changes: 5 additions & 15 deletions packages/devtools/client/components/ComponentItem.vue
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
<script setup lang="ts">
import type { Component } from 'nuxt/schema'
import type { ComponentRelationship } from '../../types'
const props = defineProps<{
component: Component
relationships?: ComponentRelationship[] | null
dependencies?: string[]
dependents?: string[]
}>()
const dependencies = computed(() => {
const deps = props.relationships?.find(i => i.id === props.component.filePath)?.deps
return deps?.map(i => props.relationships?.find(j => j.id === i)?.id).filter(Boolean) as string[]
})
const dependents = computed(() => {
const deps = props.relationships?.filter(i => i.deps.includes(props.component.filePath))
return deps?.map(i => props.relationships?.find(j => j.id === i.id)?.id).filter(Boolean) as string[]
})
// @ts-expect-error types
const filePath = computed(() => props.component.filePath || props.component.file || props.component.__file || '')
</script>
Expand All @@ -29,7 +19,7 @@ const filePath = computed(() => props.component.filePath || props.component.file
w-full items-center rounded px2 py1
>
<VDropdown>
<button hover:text-primary>
<button hover:text-primary :class="dependents && dependents.length === 0 ? 'op50' : ''">
<ComponentName :component="component" />
</button>
<template #popper>
Expand All @@ -41,8 +31,8 @@ const filePath = computed(() => props.component.filePath || props.component.file
/>
</template>
</VDropdown>
<sup v-if="dependents?.length" ml--1 op50>
{{ dependents?.length }}
<sup v-if="dependents?.length" ml--1 text-primary>
x{{ dependents?.length }}
</sup>
<Badge
v-if="component.global"
Expand Down
74 changes: 47 additions & 27 deletions packages/devtools/client/components/ComponentsList.vue
Original file line number Diff line number Diff line change
@@ -1,28 +1,39 @@
<script setup lang="ts">
import type { Component } from 'nuxt/schema'
import Fuse from 'fuse.js'
import type { ComponentRelationship } from '../../types'
import type { ComponentRelationship, ComponentWithRelationships } from '../../types'
const props = defineProps<{
components: Component[]
relationships?: ComponentRelationship[] | null
}>()
const search = ref('')
const filterMode = ref<'all' | 'using' | 'not-used'>('all')
const fuse = computed(() => new Fuse(props.components, {
const componentWithRelationships = computed(() => {
const components = props.components
.map(c => getComponentRelationships(c, props.relationships))
if (filterMode.value === 'using')
return components.filter(c => c.dependents?.length)
else if (filterMode.value === 'not-used')
return components.filter(c => !c.dependents?.length)
return components
})
const fuse = computed(() => new Fuse(componentWithRelationships.value, {
keys: [
'pascalName',
'filePath',
'kebabName',
'component.pascalName',
'component.filePath',
'component.kebabName',
],
}))
const filtered = computed(() => {
const user: Component[] = []
const lib = new Map<string, Component[]>()
const builtin: Component[] = []
const runtime: Component[] = []
const user: ComponentWithRelationships[] = []
const lib = new Map<string, ComponentWithRelationships[]>()
const builtin: ComponentWithRelationships[] = []
const runtime: ComponentWithRelationships[] = []
const count = {
user: 0,
Expand All @@ -33,12 +44,13 @@ const filtered = computed(() => {
const result = search.value
? fuse.value.search(search.value).map(i => i.item)
: props.components
: componentWithRelationships.value
result
.forEach((component) => {
if (component.filePath && isNodeModulePath(component.filePath)) {
const name = getModuleNameFromPath(component.filePath)
const c = component.component
if (c.filePath && isNodeModulePath(c.filePath)) {
const name = getModuleNameFromPath(c.filePath)
if (!name)
return
if (name === 'nuxt') {
Expand All @@ -52,7 +64,7 @@ const filtered = computed(() => {
count.lib++
}
}
else if (component.global && !component.filePath) {
else if (c.global && !c.filePath) {
runtime.push(component)
count.runtime++
}
Expand All @@ -64,7 +76,6 @@ const filtered = computed(() => {
return {
count,
user,
builtin,
lib,
Expand All @@ -74,10 +85,22 @@ const filtered = computed(() => {
</script>

<template>
<Navbar v-model:search="search">
<Navbar v-model:search="search" pb3>
<template #actions>
<slot />
</template>
<div flex="~ gap-2 items-center">
<NIcon icon="carbon-filter" op50 />
<NSelectTabs
v-model="filterMode"
n="primary sm"
:options="[
{ label: 'All', value: 'all' },
{ label: 'Using', value: 'using' },
{ label: 'Not used', value: 'not-used' },
]"
/>
</div>
</Navbar>
<NSectionBlock
v-if="filtered.user.length"
Expand All @@ -88,9 +111,8 @@ const filtered = computed(() => {
>
<ComponentItem
v-for="c of filtered.user"
:key="c.filePath"
:component="c"
:relationships="relationships"
:key="c.component.filePath"
v-bind="c"
/>
</NSectionBlock>
<NSectionBlock
Expand All @@ -101,9 +123,9 @@ const filtered = computed(() => {
:description="`Total components: ${filtered.count.runtime}`"
>
<ComponentItem
v-for="c of filtered.runtime" :key="c.filePath"
:component="c"
:relationships="relationships"
v-for="c of filtered.runtime"
:key="c.component.filePath"
v-bind="c"
/>
</NSectionBlock>
<NSectionBlock
Expand All @@ -114,9 +136,8 @@ const filtered = computed(() => {
>
<ComponentItem
v-for="c of filtered.builtin"
:key="c.filePath"
:component="c"
:relationships="relationships"
:key="c.component.filePath"
v-bind="c"
/>
</NSectionBlock>
<NSectionBlock
Expand All @@ -131,9 +152,8 @@ const filtered = computed(() => {
<div pl4>
<ComponentItem
v-for="c of value"
:key="c.filePath"
:component="c"
:relationships="relationships"
:key="c.component.filePath"
v-bind="c"
/>
</div>
</div>
Expand Down
20 changes: 19 additions & 1 deletion packages/devtools/client/composables/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { relative } from 'pathe'
import type { Ref } from 'vue'
import type { Component } from 'nuxt/schema'
import type { ComponentRelationship, ComponentWithRelationships, NormalizedHeadTag, SocialPreviewCard, SocialPreviewResolved } from '~/../src/types'
import type { AsyncDataOptions } from '#app'
import type { NormalizedHeadTag, SocialPreviewCard, SocialPreviewResolved } from '~/../src/types'

export function isNodeModulePath(path: string) {
return !!path.match(/[/\\]node_modules[/\\]/) || isPackageName(path)
Expand Down Expand Up @@ -141,3 +142,20 @@ export function useSessionState<T>(name: string, initialValue: T) {
return useSessionStorage(name, initialValue, { listenToStorageChanges: false })
})
}

export function getComponentRelationships(component: Component, relationships?: ComponentRelationship[] | null): ComponentWithRelationships {
const dependencies = relationships
?.find(i => i.id === component.filePath)
?.deps
?.map(i => relationships?.find(j => j.id === i)?.id)
.filter(Boolean) as string[] | undefined
const dependents = relationships
?.filter(i => i.deps.includes(component.filePath))
.map(i => i.id)

return {
component,
dependencies,
dependents,
}
}
25 changes: 18 additions & 7 deletions packages/devtools/client/pages/modules/imports.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ definePageMeta({
})
const config = useServerConfig()
const onlyUsed = ref(false)
const filterMode = ref<'all' | 'using' | 'not-used'>('all')
const search = ref('')
const autoImports = useAutoImports()
Expand Down Expand Up @@ -37,10 +37,14 @@ const filtered = computed(() => {
? fuse.value.search(search.value).map(i => i.item)
: functions.value
if (onlyUsed.value && importsMetadata.value) {
if (filterMode.value === 'using' && importsMetadata.value) {
result = result
.filter(i => (i.as || i.name) in importsMetadata.value!.injectionUsage)
}
else if (filterMode.value === 'not-used' && importsMetadata.value) {
result = result
.filter(i => !((i.as || i.name) in importsMetadata.value!.injectionUsage))
}
const count = {
user: 0,
Expand Down Expand Up @@ -72,11 +76,18 @@ const filtered = computed(() => {

<template>
<div v-if="config" relative h-full of-auto>
<Navbar v-model:search="search" pb2>
<div v-if="importsMetadata">
<NSwitch v-model="onlyUsed" n="primary sm">
Show used only
</NSwitch>
<Navbar v-model:search="search" pb3>
<div v-if="importsMetadata" flex="~ gap-2 items-center">
<NIcon icon="carbon-filter" op50 />
<NSelectTabs
v-model="filterMode"
n="primary sm"
:options="[
{ label: 'All', value: 'all' },
{ label: 'Using', value: 'using' },
{ label: 'Not used', value: 'not-used' },
]"
/>
</div>
</Navbar>
<NSectionBlock
Expand Down

0 comments on commit 5012012

Please sign in to comment.