From 63358e794d84801e0a54fd8c928266835b8dadf2 Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Tue, 8 Apr 2025 19:22:10 -0700 Subject: [PATCH] feat: enhance TypeScript/TSX tree-sitter parser - Enhanced the Tree-Sitter parser for JavaScript/TypeScript with support for advanced language constructs - Modified the parser to exclude comments from the output - Consolidated sample code in tests for better maintainability Signed-off-by: Eric Wheeler --- .../parseSourceCodeDefinitions.tsx.test.ts | 234 +++++++++++++++++- src/services/tree-sitter/queries/tsx.ts | 41 ++- .../tree-sitter/queries/typescript.ts | 32 +++ 3 files changed, 304 insertions(+), 3 deletions(-) diff --git a/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.tsx.test.ts b/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.tsx.test.ts index e1761d585e..f03382b024 100644 --- a/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.tsx.test.ts +++ b/src/services/tree-sitter/__tests__/parseSourceCodeDefinitions.tsx.test.ts @@ -7,8 +7,9 @@ import { loadRequiredLanguageParsers } from "../languageParser" import tsxQuery from "../queries/tsx" import { initializeTreeSitter, testParseSourceCodeDefinitions, inspectTreeStructure, debugLog } from "./helpers" -// Sample component content +// Sample component content with enhanced TypeScript language constructs const sampleTsxContent = ` +// Original components interface VSCodeCheckboxProps { checked: boolean onChange: (checked: boolean) => void @@ -66,7 +67,236 @@ const TemperatureControl = ({ ) } -}` + +// Utility Types +type User = { + id: string; + username: string; + password: string; + email: string; +} + +// Partial - Makes all properties optional +type PartialUser = Partial; + +// Required - Makes all properties required +type RequiredConfig = Required<{theme?: string, showHeader?: boolean}>; + +// Readonly - Makes all properties readonly +type ReadonlyState = Readonly<{count: number, status: string}>; + +// Function Overloads +function process(value: string): string; +function process(value: number): number; +function process(value: boolean): boolean; +function process(value: any): any { + return value; +} + +// Async Function +async function fetchData(url: string): Promise { + const response = await fetch(url); + return response; +} + +// Async Arrow Function +const fetchUser = async (id: string): Promise => { + const response = await fetch(\`/api/users/\${id}\`); + return response.json(); +}; + +// Class with Members and Properties +class AdvancedComponent { + // Public property + public name: string; + + // Private property + private _count: number = 0; + + // Protected property + protected status: 'active' | 'inactive' = 'active'; + + // Readonly property + readonly id: string; + + // Static property + static defaultProps = { + theme: 'light', + showHeader: true + }; + + // Constructor + constructor(name: string, id: string) { + this.name = name; + this.id = id; + } + + // Getter method + get count(): number { + return this._count; + } + + // Setter method + set count(value: number) { + if (value >= 0) { + this._count = value; + } + } + + // Public method + public updateName(newName: string): void { + this.name = newName; + } +} + +// React Hooks and Context +import React, { createContext, useContext, useState, useEffect } from 'react'; + +// Create a context +const ThemeContext = createContext({ + theme: 'light', + toggleTheme: () => {} +}); + +// Context provider and consumer +const ThemeProvider = ThemeContext.Provider; +const ThemeConsumer = ThemeContext.Consumer; + +// Custom hook using context +function useTheme() { + const context = useContext(ThemeContext); + if (!context) { + throw new Error('useTheme must be used within a ThemeProvider'); + } + return context; +} + +// Component using hooks +function ThemeToggler() { + // useState hook + const [theme, setTheme] = useState('light'); + + // useEffect hook + useEffect(() => { + document.body.dataset.theme = theme; + return () => { + delete document.body.dataset.theme; + }; + }, [theme]); + + return ( + + ); +} + +// Decorator Example +@Component({ + selector: 'app-root', + template: '
App Component
' +}) +class AppComponent { + title = 'My App'; + + @Input() + data: string[] = []; +} + +// Enum Declaration +enum LogLevel { + Error = 1, + Warning = 2, + Info = 3, + Debug = 4 +} + +// Namespace Declaration +namespace Validation { + export function isValidEmail(email: string): boolean { + return email.includes('@'); + } + + export function isValidPhone(phone: string): boolean { + return phone.length >= 10; + } +} + +// Complex Nested Components and Member Expressions +export const ComplexComponent = () => { + return ( + + Nested content + + } + /> + ); +}; + +export const NestedSelectors = () => ( +
+ + + Deeply nested + + +
+); + +// Template Literal Types +type EventName = \`on\${Capitalize}\`; +type CSSProperty = \`--\${T}\` | \`-webkit-\${T}\` | \`-moz-\${T}\` | \`-ms-\${T}\`; +type RouteParams = T extends \`\${string}:\${infer Param}/\${infer Rest}\` + ? { [K in Param | keyof RouteParams]: string } + : T extends \`\${string}:\${infer Param}\` + ? { [K in Param]: string } + : {}; + +// Conditional Types +type ReturnType = T extends (...args: any[]) => infer R ? R : never; +type Parameters = T extends (...args: infer P) => any ? P : never; +type InstanceType = T extends new (...args: any[]) => infer R ? R : never; +type IsFunction = T extends (...args: any[]) => any ? true : false; + +// Generic Components with Constraints +type ComplexProps = { + data: T[]; + render: (item: T) => React.ReactNode; +}; + +export const GenericList = ({ + data, + render +}: ComplexProps) => ( +
+ {data.map(item => render(item))} +
+); + +export const ConditionalComponent = ({ condition }) => + condition ? ( + +

Main Content

+
+ ) : ( + + ); + +// Dictionary Interface with Constrained Key Types +interface Dictionary { + get(key: K): V | undefined; + set(key: K, value: V): void; + has(key: K): boolean; +} + +type KeyValuePair = { + key: K; + value: V; +}; +` // We'll use the debug test to test the parser directly diff --git a/src/services/tree-sitter/queries/tsx.ts b/src/services/tree-sitter/queries/tsx.ts index 5fc4ecbab0..d98b121711 100644 --- a/src/services/tree-sitter/queries/tsx.ts +++ b/src/services/tree-sitter/queries/tsx.ts @@ -4,7 +4,19 @@ import typescriptQuery from "./typescript" * Tree-sitter Query for TSX Files: * Combines TypeScript queries with TSX-specific React component queries * - * This query captures various TypeScript and React component definitions in TSX files. + * This query captures various TypeScript and React component definitions in TSX files, + * as well as advanced TypeScript language constructs. + * + * SUPPORTED LANGUAGE CONSTRUCTS: + * - React Components (Function, Arrow, Class) + * - Higher Order Components + * - JSX Elements and Expressions + * - React Hooks + * - Context Providers/Consumers + * - React-specific Decorators + * + * Note: Generic TypeScript constructs like Utility Types, Async Functions, + * Class Members, Enums, and Namespaces are defined in typescript.ts * * TSX COMPONENT STRUCTURE: * @@ -182,4 +194,31 @@ export default `${typescriptQuery} alternative: (jsx_self_closing_element name: (identifier) @component)) @definition.conditional_component (#match? @component "^[A-Z]") + +; Enhanced TypeScript Support - React-specific patterns only +; Method Definitions specific to React components +(method_definition + name: (property_identifier) @name.definition.method) @definition.method + +; React Hooks +(variable_declaration + (variable_declarator + name: (array_pattern) @name.definition.hook + value: (call_expression + function: (identifier) @hook_name))) @definition.hook + (#match? @hook_name "^use[A-Z]") + +; Custom Hooks +(function_declaration + name: (identifier) @name.definition.custom_hook) @definition.custom_hook + (#match? @name.definition.custom_hook "^use[A-Z]") + +; Context Providers and Consumers +(variable_declaration + (variable_declarator + name: (identifier) @name.definition.context + value: (member_expression))) @definition.context + +; React-specific decorators +(decorator) @definition.decorator ` diff --git a/src/services/tree-sitter/queries/typescript.ts b/src/services/tree-sitter/queries/typescript.ts index a4601de563..8373b7a047 100644 --- a/src/services/tree-sitter/queries/typescript.ts +++ b/src/services/tree-sitter/queries/typescript.ts @@ -8,6 +8,11 @@ - switch/case statements with complex case blocks - enum declarations with members - namespace declarations +- utility types +- class members and properties +- constructor methods +- getter/setter methods +- async functions and arrow functions */ export default ` (function_signature @@ -88,4 +93,31 @@ export default ` (type_alias_declaration name: (type_identifier) @name.definition.type type_parameters: (type_parameters)?) @definition.type + +; Utility Types +(type_alias_declaration + name: (type_identifier) @name.definition.utility_type) @definition.utility_type + +; Class Members and Properties +(public_field_definition + name: (property_identifier) @name.definition.property) @definition.property + +; Constructor +(method_definition + name: (property_identifier) @name.definition.constructor + (#eq? @name.definition.constructor "constructor")) @definition.constructor + +; Getter/Setter Methods +(method_definition + name: (property_identifier) @name.definition.accessor) @definition.accessor + +; Async Functions +(function_declaration + name: (identifier) @name.definition.async_function) @definition.async_function + +; Async Arrow Functions +(variable_declaration + (variable_declarator + name: (identifier) @name.definition.async_arrow + value: (arrow_function))) @definition.async_arrow `