From 3be9e842fcac5b2736c13787ac79ad74e50938e3 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sun, 15 Mar 2026 03:48:31 +0900 Subject: [PATCH 01/10] Update README --- README.md | 4 ++++ medias/demo.png | Bin 0 -> 69042 bytes 2 files changed, 4 insertions(+) create mode 100644 medias/demo.png diff --git a/README.md b/README.md index c669662..92a1ba1 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ Visually distinguish Server Components and Client Components in React / Next.js projects directly in your editor. +[![Visual Studio Marketplace](https://img.shields.io/visual-studio-marketplace/v/devfive.react-component-lens)](https://marketplace.visualstudio.com/items?itemName=devfive.react-component-lens) + +![React Component Lens demo](medias/demo.png) + ## Why In Next.js App Router and React Server Components, the boundary between server and client execution is critical for performance and bundle size. But JSX like `` gives no visual cue about where it runs. diff --git a/medias/demo.png b/medias/demo.png new file mode 100644 index 0000000000000000000000000000000000000000..33b678398755b8fee0e82e626093fb9a605b8178 GIT binary patch literal 69042 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�arSt(Y; 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�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 XGT{BlGE6k#fPZq*%2LHI^?m*i7CSO~ literal 0 HcmV?d00001 From 6a8a9bedc12f80f1dea634a93ce3b49840a661b6 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Sun, 15 Mar 2026 15:41:28 +0900 Subject: [PATCH 02/10] Support more expression --- package.json | 25 ++ src/analyzer.ts | 547 ++++++++++++++++++++++++++--- src/extension.ts | 11 +- test/analyzer.test.ts | 791 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 1313 insertions(+), 61 deletions(-) diff --git a/package.json b/package.json index e4ea4bd..18af91b 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,31 @@ "maximum": 2000, "description": "Debounce delay, in milliseconds, before recomputing decorations after workspace changes." }, + "reactComponentLens.scope.element": { + "type": "boolean", + "default": true, + "description": "Highlight JSX element tags (, )." + }, + "reactComponentLens.scope.declaration": { + "type": "boolean", + "default": true, + "description": "Highlight component declaration names (function, class, variable)." + }, + "reactComponentLens.scope.export": { + "type": "boolean", + "default": true, + "description": "Highlight component names in export declarations (export { Component }, export default Component)." + }, + "reactComponentLens.scope.import": { + "type": "boolean", + "default": true, + "description": "Highlight component names in import declarations (import { Component } from ...)." + }, + "reactComponentLens.scope.type": { + "type": "boolean", + "default": true, + "description": "Highlight TypeScript interface and type alias declaration names." + }, "reactComponentLens.highlightColors": { "type": "object", "default": { diff --git a/src/analyzer.ts b/src/analyzer.ts index fb11ca1..a7903be 100644 --- a/src/analyzer.ts +++ b/src/analyzer.ts @@ -4,6 +4,14 @@ import type { ImportResolver, SourceHost } from './resolver' export type ComponentKind = 'client' | 'server' | 'unknown' +export interface ScopeConfig { + declaration: boolean + element: boolean + export: boolean + import: boolean + type: boolean +} + export interface ComponentUsage { kind: Exclude ranges: DecorationSegment[] @@ -27,10 +35,15 @@ interface CachedDirective { } interface FileAnalysis { + exportReferences: LocalComponentDeclaration[] + importReferences: ImportIdentifierReference[] imports: Map jsxTags: JsxTagReference[] + localComponentDeclarations: LocalComponentDeclaration[] + localComponentKinds: Map> localComponentNames: Set ownComponentKind: Exclude + typeIdentifiers: TypeIdentifier[] } interface JsxTagReference { @@ -39,6 +52,23 @@ interface JsxTagReference { tagName: string } +interface ImportIdentifierReference { + name: string + range: DecorationSegment + source: string +} + +interface LocalComponentDeclaration { + name: string + range: DecorationSegment +} + +interface TypeIdentifier { + enclosingComponent: string | undefined + name: string + range: DecorationSegment +} + export class ComponentLensAnalyzer { private readonly analysisCache = new Map() private readonly directiveCache = new Map() @@ -63,77 +93,163 @@ export class ComponentLensAnalyzer { filePath: string, sourceText: string, signature: string, + scope: ScopeConfig = { + declaration: true, + element: true, + export: true, + import: true, + type: true, + }, ): Promise { const analysis = this.getAnalysis(filePath, sourceText, signature) if (!analysis) { return [] } + const usages: ComponentUsage[] = [] + const resolvedPaths = new Map() - const uniqueFilePaths = new Set() + const componentKinds = new Map() - for (const jsxTag of analysis.jsxTags) { - const lookupName = jsxTag.lookupName - if ( - analysis.localComponentNames.has(lookupName) || - resolvedPaths.has(lookupName) - ) { - continue - } + if (scope.element || scope.import) { + const uniqueFilePaths = new Set() - const importSource = analysis.imports.get(lookupName) - if (!importSource) { - continue + for (const [lookupName, source] of analysis.imports) { + if ( + analysis.localComponentNames.has(lookupName) || + resolvedPaths.has(lookupName) + ) { + continue + } + + const resolvedFilePath = this.resolver.resolveImport(filePath, source) + if (resolvedFilePath) { + resolvedPaths.set(lookupName, resolvedFilePath) + uniqueFilePaths.add(resolvedFilePath) + } } - const resolvedFilePath = this.resolver.resolveImport( - filePath, - importSource, + await Promise.all( + Array.from(uniqueFilePaths, (resolvedPath) => + this.getFileComponentKind(resolvedPath).then((kind) => { + componentKinds.set(resolvedPath, kind) + }), + ), ) - if (resolvedFilePath) { - resolvedPaths.set(lookupName, resolvedFilePath) - uniqueFilePaths.add(resolvedFilePath) - } } - const componentKinds = new Map() - await Promise.all( - Array.from(uniqueFilePaths, (resolvedPath) => - this.getFileComponentKind(resolvedPath).then((kind) => { - componentKinds.set(resolvedPath, kind) - }), - ), - ) + if (scope.element) { + for (const jsxTag of analysis.jsxTags) { + if (analysis.localComponentNames.has(jsxTag.lookupName)) { + usages.push({ + kind: + analysis.localComponentKinds.get(jsxTag.lookupName) ?? + analysis.ownComponentKind, + ranges: jsxTag.ranges, + sourceFilePath: filePath, + tagName: jsxTag.tagName, + }) + continue + } - const usages: ComponentUsage[] = [] + const resolvedFilePath = resolvedPaths.get(jsxTag.lookupName) + if (!resolvedFilePath) { + continue + } + + const componentKind = componentKinds.get(resolvedFilePath) + if (!componentKind || componentKind === 'unknown') { + continue + } - for (const jsxTag of analysis.jsxTags) { - if (analysis.localComponentNames.has(jsxTag.lookupName)) { usages.push({ - kind: analysis.ownComponentKind, + kind: componentKind, ranges: jsxTag.ranges, - sourceFilePath: filePath, + sourceFilePath: resolvedFilePath, tagName: jsxTag.tagName, }) - continue } + } - const resolvedFilePath = resolvedPaths.get(jsxTag.lookupName) - if (!resolvedFilePath) { - continue + if (scope.import) { + for (const ref of analysis.importReferences) { + const resolvedFilePath = resolvedPaths.get(ref.name) + if (!resolvedFilePath) { + continue + } + + const componentKind = componentKinds.get(resolvedFilePath) + if (!componentKind || componentKind === 'unknown') { + continue + } + + usages.push({ + kind: componentKind, + ranges: [ref.range], + sourceFilePath: resolvedFilePath, + tagName: ref.name, + }) } + } - const componentKind = componentKinds.get(resolvedFilePath) - if (!componentKind || componentKind === 'unknown') { - continue + if (scope.declaration) { + for (const declaration of analysis.localComponentDeclarations) { + usages.push({ + kind: + analysis.localComponentKinds.get(declaration.name) ?? + analysis.ownComponentKind, + ranges: [declaration.range], + sourceFilePath: filePath, + tagName: declaration.name, + }) } + } - usages.push({ - kind: componentKind, - ranges: jsxTag.ranges, - sourceFilePath: resolvedFilePath, - tagName: jsxTag.tagName, - }) + if (scope.type) { + const typeUsageKinds = new Map< + string, + Exclude + >() + for (const typeId of analysis.typeIdentifiers) { + if (typeId.enclosingComponent) { + const kind = + analysis.localComponentKinds.get(typeId.enclosingComponent) ?? + analysis.ownComponentKind + const existing = typeUsageKinds.get(typeId.name) + if (!existing || kind === 'client') { + typeUsageKinds.set(typeId.name, kind) + } + } + } + + for (const typeId of analysis.typeIdentifiers) { + let kind: Exclude + if (typeId.enclosingComponent) { + kind = + analysis.localComponentKinds.get(typeId.enclosingComponent) ?? + analysis.ownComponentKind + } else { + kind = typeUsageKinds.get(typeId.name) ?? analysis.ownComponentKind + } + + usages.push({ + kind, + ranges: [typeId.range], + sourceFilePath: filePath, + tagName: typeId.name, + }) + } + } + + if (scope.export) { + for (const exportRef of analysis.exportReferences) { + usages.push({ + kind: analysis.ownComponentKind, + ranges: [exportRef.range], + sourceFilePath: filePath, + tagName: exportRef.name, + }) + } } return usages @@ -196,8 +312,17 @@ function parseFileAnalysis(filePath: string, sourceText: string): FileAnalysis { getScriptKind(filePath), ) + const componentRanges: { end: number; name: string; start: number }[] = [] + const exportReferences: LocalComponentDeclaration[] = [] + const importReferences: ImportIdentifierReference[] = [] const imports = new Map() + const localComponentDeclarations: LocalComponentDeclaration[] = [] + const localComponentKinds = new Map< + string, + Exclude + >() const localComponentNames = new Set() + const typeDeclarations: LocalComponentDeclaration[] = [] let ownComponentKind: Exclude = 'server' let statementIndex = 0 @@ -228,14 +353,44 @@ function parseFileAnalysis(filePath: string, sourceText: string): FileAnalysis { if (importClause) { if (importClause.name) { imports.set(importClause.name.text, source) + if (isComponentIdentifier(importClause.name.text)) { + importReferences.push({ + name: importClause.name.text, + range: { + start: importClause.name.getStart(sourceFile), + end: importClause.name.getEnd(), + }, + source, + }) + } } const namedBindings = importClause.namedBindings if (namedBindings) { if (ts.isNamespaceImport(namedBindings)) { imports.set(namedBindings.name.text, source) + if (isComponentIdentifier(namedBindings.name.text)) { + importReferences.push({ + name: namedBindings.name.text, + range: { + start: namedBindings.name.getStart(sourceFile), + end: namedBindings.name.getEnd(), + }, + source, + }) + } } else { for (const element of namedBindings.elements) { imports.set(element.name.text, source) + if (isComponentIdentifier(element.name.text)) { + importReferences.push({ + name: element.name.text, + range: { + start: element.name.getStart(sourceFile), + end: element.name.getEnd(), + }, + source, + }) + } } } } @@ -248,7 +403,32 @@ function parseFileAnalysis(filePath: string, sourceText: string): FileAnalysis { statement.name && isComponentIdentifier(statement.name.text) ) { - localComponentNames.add(statement.name.text) + const name = statement.name.text + localComponentNames.add(name) + localComponentDeclarations.push({ + name, + range: { + start: statement.name.getStart(sourceFile), + end: statement.name.getEnd(), + }, + }) + + let kind = ownComponentKind + if (ownComponentKind === 'server') { + const isAsync = + statement.modifiers?.some( + (m) => m.kind === ts.SyntaxKind.AsyncKeyword, + ) ?? false + if (!isAsync && hasNonServerFunctionProps(statement)) { + kind = 'client' + } + } + localComponentKinds.set(name, kind) + componentRanges.push({ + end: statement.getEnd(), + name, + start: statement.getStart(sourceFile), + }) continue } @@ -257,7 +437,90 @@ function parseFileAnalysis(filePath: string, sourceText: string): FileAnalysis { statement.name && isComponentIdentifier(statement.name.text) ) { - localComponentNames.add(statement.name.text) + const name = statement.name.text + localComponentNames.add(name) + localComponentDeclarations.push({ + name, + range: { + start: statement.name.getStart(sourceFile), + end: statement.name.getEnd(), + }, + }) + + let kind = ownComponentKind + if ( + ownComponentKind === 'server' && + hasNonServerFunctionProps(statement) + ) { + kind = 'client' + } + localComponentKinds.set(name, kind) + componentRanges.push({ + end: statement.getEnd(), + name, + start: statement.getStart(sourceFile), + }) + continue + } + + if ( + ts.isInterfaceDeclaration(statement) && + isComponentIdentifier(statement.name.text) + ) { + typeDeclarations.push({ + name: statement.name.text, + range: { + start: statement.name.getStart(sourceFile), + end: statement.name.getEnd(), + }, + }) + continue + } + + if ( + ts.isTypeAliasDeclaration(statement) && + isComponentIdentifier(statement.name.text) + ) { + typeDeclarations.push({ + name: statement.name.text, + range: { + start: statement.name.getStart(sourceFile), + end: statement.name.getEnd(), + }, + }) + continue + } + + if (ts.isExportDeclaration(statement)) { + if (statement.exportClause && ts.isNamedExports(statement.exportClause)) { + for (const element of statement.exportClause.elements) { + if (isComponentIdentifier(element.name.text)) { + exportReferences.push({ + name: element.name.text, + range: { + start: element.name.getStart(sourceFile), + end: element.name.getEnd(), + }, + }) + } + } + } + continue + } + + if ( + ts.isExportAssignment(statement) && + !statement.isExportEquals && + ts.isIdentifier(statement.expression) && + isComponentIdentifier(statement.expression.text) + ) { + exportReferences.push({ + name: statement.expression.text, + range: { + start: statement.expression.getStart(sourceFile), + end: statement.expression.getEnd(), + }, + }) continue } @@ -268,17 +531,70 @@ function parseFileAnalysis(filePath: string, sourceText: string): FileAnalysis { isComponentIdentifier(declaration.name.text) && isComponentInitializer(declaration.initializer) ) { - localComponentNames.add(declaration.name.text) + const name = declaration.name.text + localComponentNames.add(name) + localComponentDeclarations.push({ + name, + range: { + start: declaration.name.getStart(sourceFile), + end: declaration.name.getEnd(), + }, + }) + + let kind = ownComponentKind + if (ownComponentKind === 'server') { + const fn = getComponentFunction(declaration.initializer) + const isAsync = + fn?.modifiers?.some( + (m) => m.kind === ts.SyntaxKind.AsyncKeyword, + ) ?? false + if (!isAsync && hasNonServerFunctionProps(declaration)) { + kind = 'client' + } + } + localComponentKinds.set(name, kind) + componentRanges.push({ + end: declaration.getEnd(), + name, + start: declaration.getStart(sourceFile), + }) } } } } + const allTypeRefs = collectTypeReferences(sourceFile) + const typeIdentifiers: TypeIdentifier[] = [] + + for (const decl of typeDeclarations) { + typeIdentifiers.push({ + enclosingComponent: undefined, + name: decl.name, + range: decl.range, + }) + } + + for (const ref of allTypeRefs) { + const enclosing = componentRanges.find( + (r) => ref.range.start >= r.start && ref.range.end <= r.end, + ) + typeIdentifiers.push({ + enclosingComponent: enclosing?.name, + name: ref.name, + range: ref.range, + }) + } + return { + exportReferences, + importReferences, imports, jsxTags: collectJsxTags(sourceFile), + localComponentDeclarations, + localComponentKinds, localComponentNames, ownComponentKind, + typeIdentifiers, } } @@ -294,6 +610,117 @@ function isComponentIdentifier(name: string): boolean { return code >= 65 && code <= 90 } +function getComponentFunction( + initializer: ts.Expression | undefined, +): ts.ArrowFunction | ts.FunctionExpression | undefined { + if (!initializer) { + return undefined + } + + if (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer)) { + return initializer + } + + if ( + ts.isCallExpression(initializer) && + COMPONENT_WRAPPER_NAMES.has(getCalleeText(initializer.expression)) + ) { + return initializer.arguments.find( + (arg): arg is ts.ArrowFunction | ts.FunctionExpression => + ts.isArrowFunction(arg) || ts.isFunctionExpression(arg), + ) + } + + return undefined +} + +function hasNonServerFunctionProps(node: ts.Node): boolean { + const localFunctions = collectLocalFunctions(node) + + let found = false + const visit = (n: ts.Node): void => { + if (found) { + return + } + + if ( + ts.isJsxAttribute(n) && + n.initializer && + ts.isJsxExpression(n.initializer) && + n.initializer.expression + ) { + const expr = n.initializer.expression + + if ( + (ts.isArrowFunction(expr) || ts.isFunctionExpression(expr)) && + !hasUseServerDirective(expr) + ) { + found = true + return + } + + if ( + ts.isIdentifier(expr) && + localFunctions.has(expr.text) && + !localFunctions.get(expr.text) + ) { + found = true + return + } + } + + ts.forEachChild(n, visit) + } + ts.forEachChild(node, visit) + return found +} + +function collectLocalFunctions(node: ts.Node): Map { + const functions = new Map() + const visit = (n: ts.Node): void => { + if (ts.isFunctionDeclaration(n) && n.name) { + functions.set(n.name.text, hasUseServerDirective(n)) + } + + if ( + ts.isVariableDeclaration(n) && + ts.isIdentifier(n.name) && + n.initializer && + (ts.isArrowFunction(n.initializer) || + ts.isFunctionExpression(n.initializer)) + ) { + functions.set(n.name.text, hasUseServerDirective(n.initializer)) + } + + ts.forEachChild(n, visit) + } + ts.forEachChild(node, visit) + return functions +} + +function hasUseServerDirective( + fn: ts.ArrowFunction | ts.FunctionDeclaration | ts.FunctionExpression, +): boolean { + const body = fn.body + if (!body || !ts.isBlock(body)) { + return false + } + + for (const stmt of body.statements) { + if ( + !ts.isExpressionStatement(stmt) || + !ts.isStringLiteral(stmt.expression) + ) { + break + } + if (stmt.expression.text === 'use server') { + return true + } + } + + return false +} + function isComponentInitializer( initializer: ts.Expression | undefined, ): boolean { @@ -338,6 +765,30 @@ function getCalleeText(expression: ts.Expression): string { return '' } +function collectTypeReferences( + sourceFile: ts.SourceFile, +): LocalComponentDeclaration[] { + const refs: LocalComponentDeclaration[] = [] + const visit = (node: ts.Node): void => { + if ( + ts.isTypeReferenceNode(node) && + ts.isIdentifier(node.typeName) && + isComponentIdentifier(node.typeName.text) + ) { + refs.push({ + name: node.typeName.text, + range: { + start: node.typeName.getStart(sourceFile), + end: node.typeName.getEnd(), + }, + }) + } + ts.forEachChild(node, visit) + } + ts.forEachChild(sourceFile, visit) + return refs +} + function collectJsxTags(sourceFile: ts.SourceFile): JsxTagReference[] { const jsxTags: JsxTagReference[] = [] const visit = (node: ts.Node): void => { diff --git a/src/extension.ts b/src/extension.ts index 7ea7d42..4eb7967 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,7 +3,7 @@ import * as path from 'node:path' import * as vscode from 'vscode' -import { ComponentLensAnalyzer } from './analyzer' +import { ComponentLensAnalyzer, type ScopeConfig } from './analyzer' import { type HighlightColors, LensDecorations } from './decorations' import { ImportResolver, type SourceHost } from './resolver' @@ -61,6 +61,7 @@ export function activate(context: vscode.ExtensionContext): void { editor.document.fileName, editor.document.getText(), signature, + config.scope, ) decorations.apply(editor, usages) } @@ -171,6 +172,7 @@ function getConfiguration(): { debounceMs: number enabled: boolean highlightColors: HighlightColors + scope: ScopeConfig } { const configuration = vscode.workspace.getConfiguration('reactComponentLens') const debounceMs = configuration.get('debounceMs', 200) @@ -192,6 +194,13 @@ function getConfiguration(): { DEFAULT_HIGHLIGHT_COLORS.serverComponent, ), }, + scope: { + declaration: configuration.get('scope.declaration', true), + element: configuration.get('scope.element', true), + export: configuration.get('scope.export', true), + import: configuration.get('scope.import', true), + type: configuration.get('scope.type', true), + }, } } diff --git a/test/analyzer.test.ts b/test/analyzer.test.ts index 68d2a0b..11d2477 100644 --- a/test/analyzer.test.ts +++ b/test/analyzer.test.ts @@ -4,7 +4,7 @@ import * as path from 'node:path' import { expect, test } from 'bun:test' -import { ComponentLensAnalyzer } from '../src/analyzer' +import { ComponentLensAnalyzer, type ScopeConfig } from '../src/analyzer' import { ImportResolver, type SourceHost } from '../src/resolver' test('detects client and server component usages from relative imports', async () => { @@ -46,12 +46,15 @@ test('detects client and server component usages from relative imports', async ( project.signature('Page.tsx'), ) - expect(usages.length).toBe(2) + expect(usages.length).toBe(5) expect( usages.map((usage) => ({ kind: usage.kind, tagName: usage.tagName })), ).toEqual([ { kind: 'client', tagName: 'Button' }, { kind: 'server', tagName: 'Layout' }, + { kind: 'client', tagName: 'Button' }, + { kind: 'server', tagName: 'Layout' }, + { kind: 'server', tagName: 'Page' }, ]) expect( usages.map((usage) => @@ -60,6 +63,9 @@ test('detects client and server component usages from relative imports', async ( ).toEqual([ [''], [''], + ['Button'], + ['Layout'], + ['Page'], ]) } finally { project[Symbol.dispose]() @@ -98,7 +104,7 @@ test('includes full delimiters for opening and closing tags', async () => { usages.map((usage) => usage.ranges.map((range) => source.slice(range.start, range.end)), ), - ).toEqual([[''], ['']]) + ).toEqual([[''], [''], ['Button'], ['Page']]) } finally { project[Symbol.dispose]() } @@ -134,7 +140,7 @@ test('excludes props while keeping self-closing delimiters', async () => { usages.map((usage) => usage.ranges.map((range) => source.slice(range.start, range.end)), ), - ).toEqual([['']]) + ).toEqual([[''], ['Button'], ['Page']]) } finally { project[Symbol.dispose]() } @@ -162,7 +168,7 @@ test('treats locally declared components as the current file kind', async () => project.signature('Card.tsx'), ) - expect(usages.length).toBe(1) + expect(usages.length).toBe(3) expect(usages[0]?.kind).toBe('client') expect(usages[0]?.tagName).toBe('Action') } finally { @@ -210,7 +216,7 @@ test('resolves tsconfig path aliases when mapping component types', async () => project.signature('src/Page.tsx'), ) - expect(usages.length).toBe(1) + expect(usages.length).toBe(3) expect(usages[0]?.kind).toBe('client') expect(usages[0]?.sourceFilePath ?? '').toMatch(/src[\\/]Button\.tsx$/u) } finally { @@ -288,13 +294,13 @@ test('clear() resets all caches and re-analyses from scratch', async () => { const sig = project.signature('Page.tsx') const before = await analyzer.analyzeDocument(filePath, source, sig) - expect(before.length).toBe(1) + expect(before.length).toBe(3) expect(before[0]?.kind).toBe('client') analyzer.clear() const after = await analyzer.analyzeDocument(filePath, source, sig) - expect(after.length).toBe(1) + expect(after.length).toBe(3) expect(after[0]?.kind).toBe('client') } finally { project[Symbol.dispose]() @@ -323,7 +329,7 @@ test('recognizes forwardRef-wrapped local components', async () => { project.signature('Card.tsx'), ) - expect(usages.length).toBe(1) + expect(usages.length).toBe(3) expect(usages[0]?.kind).toBe('client') expect(usages[0]?.tagName).toBe('Button') } finally { @@ -359,7 +365,7 @@ test('resolves namespaced JSX like ', async () => { project.signature('Page.tsx'), ) - expect(usages.length).toBe(1) + expect(usages.length).toBe(3) expect(usages[0]?.kind).toBe('client') expect(usages[0]?.tagName).toBe('UI.Button') } finally { @@ -398,7 +404,7 @@ test('resolves bare package imports through node_modules', async () => { project.signature('Page.tsx'), ) - expect(usages.length).toBe(1) + expect(usages.length).toBe(3) expect(usages[0]?.kind).toBe('client') expect(usages[0]?.tagName).toBe('Button') } finally { @@ -431,7 +437,7 @@ test('resolves deeply nested namespaced JSX like ', async () = project.signature('Page.tsx'), ) - expect(usages.length).toBe(1) + expect(usages.length).toBe(3) expect(usages[0]?.kind).toBe('client') expect(usages[0]?.tagName).toBe('UI.Forms.Input') } finally { @@ -439,6 +445,767 @@ test('resolves deeply nested namespaced JSX like ', async () = } }) +test('colors interface and type alias declaration names', async () => { + const project = createProject({ + 'Card.tsx': [ + "'use client';", + '', + 'export interface CardProps {', + ' title: string;', + '}', + '', + 'type CardVariant = "primary" | "secondary";', + '', + 'export function Card() {', + ' return
;', + '}', + ].join('\n'), + }) + + try { + const analyzer = createAnalyzer(project.host) + const filePath = project.filePath('Card.tsx') + const source = project.readFile('Card.tsx') + const usages = await analyzer.analyzeDocument( + filePath, + source, + project.signature('Card.tsx'), + ) + + expect(usages.length).toBe(3) + expect( + usages.map((usage) => ({ kind: usage.kind, tagName: usage.tagName })), + ).toEqual([ + { kind: 'client', tagName: 'Card' }, + { kind: 'client', tagName: 'CardProps' }, + { kind: 'client', tagName: 'CardVariant' }, + ]) + expect( + usages.map((usage) => + usage.ranges.map((range) => source.slice(range.start, range.end)), + ), + ).toEqual([['Card'], ['CardProps'], ['CardVariant']]) + } finally { + project[Symbol.dispose]() + } +}) + +test('colors type references in function parameter annotations', async () => { + const project = createProject({ + 'Button.tsx': [ + "'use client';", + '', + 'interface ThemeButtonProps {', + ' color: string;', + '}', + '', + 'export function ThemeButton({ color }: ThemeButtonProps) {', + ' return