diff --git a/packages/devtools-kit/src/core/component/tree/filter.ts b/packages/devtools-kit/src/core/component/tree/filter.ts index 2f8b00eed..b29a0f630 100644 --- a/packages/devtools-kit/src/core/component/tree/filter.ts +++ b/packages/devtools-kit/src/core/component/tree/filter.ts @@ -4,9 +4,25 @@ import { getInstanceName } from '../utils' export class ComponentFilter { filter: string + positiveFilters: string[] = [] + negativeFilters: string[] = [] constructor(filter: string) { this.filter = filter || '' + const tokens = this.filter.trim().split(/\s+/).filter(Boolean) + tokens.forEach((token) => { + const lower = token.toLowerCase() + if (lower.startsWith('-') && lower.length > 1) { + this.negativeFilters.push(lower.slice(1)) + } + else { + this.positiveFilters.push(lower) + } + }) + } + + get hasNegativeFilters() { + return this.negativeFilters.length > 0 } /** @@ -15,10 +31,29 @@ export class ComponentFilter { * @param {Vue|Vnode} instance * @return {boolean} */ - isQualified(instance: VueAppInstance): boolean { + isQualified(instance: VueAppInstance, checkPositive = true): boolean { const name = getInstanceName(instance) - return classify(name).toLowerCase().includes(this.filter) - || kebabize(name).toLowerCase().includes(this.filter) + const normalizedName = classify(name).toLowerCase() + const kebabName = kebabize(name).toLowerCase() + + if (this.hasNegativeFilters) { + const isExcluded = this.negativeFilters.some(neg => + normalizedName.includes(neg) || kebabName.includes(neg), + ) + if (isExcluded) + return false + } + + if (!checkPositive) { + return true + } + + if (this.positiveFilters.length === 0) + return true + + return this.positiveFilters.some(pos => + normalizedName.includes(pos) || kebabName.includes(pos), + ) } } diff --git a/packages/devtools-kit/src/core/component/tree/walker.ts b/packages/devtools-kit/src/core/component/tree/walker.ts index c650847bd..56ed0cc33 100644 --- a/packages/devtools-kit/src/core/component/tree/walker.ts +++ b/packages/devtools-kit/src/core/component/tree/walker.ts @@ -115,9 +115,15 @@ export class ComponentWalker { // capture children if (depth < this.maxDepth! || instance.type.__isKeepAlive || parents.some(parent => parent.type.__isKeepAlive)) { - treeNode.children = await Promise.all(children - .map(child => this.capture(child, depth + 1)) - .filter(Boolean)) + if (this.componentFilter.hasNegativeFilters) { + const childrenNodes = await Promise.all(children.map(child => this.findQualifiedChildren(child, depth + 1, false))) + treeNode.children = Array.prototype.concat.apply([], childrenNodes) + } + else { + treeNode.children = await Promise.all(children + .map(child => this.capture(child, depth + 1)) + .filter(Boolean)) + } } // keep-alive @@ -178,8 +184,8 @@ export class ComponentWalker { * @param {Vue|Vnode} instance * @return {Vue|Array} */ - private async findQualifiedChildren(instance: VueAppInstance, depth: number): Promise { - if (this.componentFilter.isQualified(instance) && !instance.type.devtools?.hide) { + private async findQualifiedChildren(instance: VueAppInstance, depth: number, checkPositive = true): Promise { + if (this.componentFilter.isQualified(instance, checkPositive) && !instance.type.devtools?.hide) { return [await this.capture(instance, depth)] } else if (instance.subTree) { @@ -187,7 +193,7 @@ export class ComponentWalker { const list = this.isKeepAlive(instance) ? this.getKeepAliveCachedInstances(instance) : this.getInternalInstanceChildren(instance.subTree) - return this.findQualifiedChildrenFromList(list, depth) + return this.findQualifiedChildrenFromList(list, depth, checkPositive) } else { return [] @@ -203,14 +209,12 @@ export class ComponentWalker { * @param {Array} instances * @return {Array} */ - private async findQualifiedChildrenFromList(instances: VueAppInstance[], depth: number): Promise { + private async findQualifiedChildrenFromList(instances: VueAppInstance[], depth: number, checkPositive: boolean): Promise { instances = instances .filter(child => !isBeingDestroyed(child) && !child.type.devtools?.hide) - if (!this.componentFilter.filter) - return Promise.all(instances.map(child => this.capture(child, depth))) - else - return Array.prototype.concat.apply([], await Promise.all(instances.map(i => this.findQualifiedChildren(i, depth)))) + return Array.prototype.concat.apply([], await Promise.all(instances.map(i => this.findQualifiedChildren(i, depth, checkPositive))), + ) } /**