mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-22 00:31:17 +00:00
* feat(vue): add Vue SFC (.vue) support for indexing
Vue Single File Components are now fully supported in the indexing pipeline.
The implementation extracts <script> / <script setup> blocks from .vue files
and parses them using the existing TypeScript tree-sitter grammar — no new
npm dependencies required.
Key changes:
- SFC script extractor: regex-based extraction of <script setup lang="ts">
blocks with correct line offset mapping back to the .vue file
- Vue language provider: reuses TypeScript queries, type config, field
extractors, and named binding extraction
- Import resolution: .vue added to EXTENSIONS so `import Foo from './Foo'`
resolves to Foo.vue; Vue import resolver delegates to TS resolver for
tsconfig path alias support
- Export detection: <script setup> top-level bindings are implicitly exported
- Template component detection: PascalCase tags in <template> emit CALLS edges
- Line offsets applied to all emitted positions (startLine, endLine, route
lineNumbers, decorator positions) in both worker and sequential paths
Validated on a 3,553-file Vue project:
Before: 24,693 nodes | 73,614 edges | 0 symbols from .vue
After: 30,495 nodes | 112,324 edges | 5,213 symbols from .vue
18,682 imports from .vue | 5,826 vue-to-vue imports
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(typescript): track destructured call results in TypeEnv
Extend `extractPendingAssignment` to handle object destructuring from
function calls and await expressions:
const { isMaker } = useUserRole()
const { data } = await fetchData()
const { name } = repo.getProfile()
Previously, only `const { x } = someVariable` (identifier RHS) produced
TypeEnv bindings. Call-expression RHS was silently skipped, leaving
destructured properties untracked.
The fix emits a synthetic `callResult` item plus N `fieldAccess` items
per destructured property, which the existing fixpoint resolver processes
in 2 iterations. No changes needed to type-env.ts, PendingAssignment
types, or call-processor — the existing infrastructure handles it.
Also extracts a `collectDestructuredFields` helper to share the
object_pattern property iteration logic between the identifier and
call-expression branches.
Note: Full property-type resolution requires the callee to have a
declared returnType in the SymbolTable. Arrow-function composables
without type annotations (common in Vue/React) won't resolve property
types until return-type inference is added in a future change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(vue): address PR review issues for Vue SFC support
- Extract duplicated isVueSetupTopLevel to vue-sfc-extractor.ts shared
utility, removing identical copies from parse-worker.ts and
parsing-processor.ts
- Fix VUE_BUILT_INS to be a superset of TS BUILT_INS by importing and
spreading the TypeScript set, preventing spurious unresolved calls for
standard built-ins (Symbol, BigInt, WeakMap, array methods, etc.)
- Add Vue template component CALLS edge resolution in both sequential
and worker paths (call-processor.ts), matching PascalCase template
tags against imported .vue file basenames via the import map
- Add integration test for template PascalCase CALLS edges
(App.vue → Button.vue)
- Add integration test for isExported: false on non-setup <script>
blocks (OldStyle.vue options API)
- Add comment explaining TEMPLATE_RE greedy regex behavior for nested
template tags
- Fix stale language count comment (14 → 15) and remove dead code
branch in test
Made-with: Cursor
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
148 lines
4.9 KiB
TypeScript
148 lines
4.9 KiB
TypeScript
/**
|
|
* Language Detection — maps file paths to SupportedLanguages enum values.
|
|
*
|
|
* Shared between CLI (ingestion pipeline) and web (syntax highlighting).
|
|
*
|
|
* ADDING A NEW LANGUAGE:
|
|
* 1. Add enum member to SupportedLanguages in languages.ts
|
|
* 2. Add file extensions to EXTENSION_MAP below
|
|
* 3. TypeScript will error if you miss either step (exhaustive Record)
|
|
*/
|
|
|
|
import { SupportedLanguages } from './languages.js';
|
|
|
|
/** Ruby extensionless filenames recognised as Ruby source */
|
|
const RUBY_EXTENSIONLESS_FILES = new Set([
|
|
'Rakefile',
|
|
'Gemfile',
|
|
'Guardfile',
|
|
'Vagrantfile',
|
|
'Brewfile',
|
|
]);
|
|
|
|
/**
|
|
* Exhaustive map: every SupportedLanguages member → its file extensions.
|
|
*
|
|
* If a new language is added to the enum without adding an entry here,
|
|
* TypeScript emits a compile error: "Property 'NewLang' is missing in type..."
|
|
*/
|
|
const EXTENSION_MAP: Record<SupportedLanguages, readonly string[]> = {
|
|
[SupportedLanguages.JavaScript]: ['.js', '.jsx', '.mjs', '.cjs'],
|
|
[SupportedLanguages.TypeScript]: ['.ts', '.tsx', '.mts', '.cts'],
|
|
[SupportedLanguages.Python]: ['.py'],
|
|
[SupportedLanguages.Java]: ['.java'],
|
|
[SupportedLanguages.C]: ['.c'],
|
|
[SupportedLanguages.CPlusPlus]: ['.cpp', '.cc', '.cxx', '.h', '.hpp', '.hxx', '.hh'],
|
|
[SupportedLanguages.CSharp]: ['.cs'],
|
|
[SupportedLanguages.Go]: ['.go'],
|
|
[SupportedLanguages.Ruby]: ['.rb', '.rake', '.gemspec'],
|
|
[SupportedLanguages.Rust]: ['.rs'],
|
|
[SupportedLanguages.PHP]: ['.php', '.phtml', '.php3', '.php4', '.php5', '.php8'],
|
|
[SupportedLanguages.Kotlin]: ['.kt', '.kts'],
|
|
[SupportedLanguages.Swift]: ['.swift'],
|
|
[SupportedLanguages.Dart]: ['.dart'],
|
|
[SupportedLanguages.Vue]: ['.vue'],
|
|
[SupportedLanguages.Cobol]: ['.cbl', '.cob', '.cpy', '.cobol'],
|
|
} satisfies Record<SupportedLanguages, readonly string[]>; // Ensure exhaustiveness
|
|
|
|
/** Pre-built reverse lookup: extension → language (built once at module load). */
|
|
const extToLang = new Map<string, SupportedLanguages>();
|
|
for (const [lang, exts] of Object.entries(EXTENSION_MAP) as [
|
|
SupportedLanguages,
|
|
readonly string[],
|
|
][]) {
|
|
for (const ext of exts) {
|
|
extToLang.set(ext, lang);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Map file extension to SupportedLanguage enum.
|
|
* Returns null if the file extension is not recognized.
|
|
*/
|
|
export const getLanguageFromFilename = (filename: string): SupportedLanguages | null => {
|
|
// Fast path: check the extension map
|
|
const lastDot = filename.lastIndexOf('.');
|
|
if (lastDot >= 0) {
|
|
const ext = filename.slice(lastDot).toLowerCase();
|
|
const lang = extToLang.get(ext);
|
|
if (lang !== undefined) return lang;
|
|
}
|
|
|
|
// Ruby extensionless files (Rakefile, Gemfile, etc.)
|
|
const basename = filename.split('/').pop() || filename;
|
|
if (RUBY_EXTENSIONLESS_FILES.has(basename)) {
|
|
return SupportedLanguages.Ruby;
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
/**
|
|
* Exhaustive map: every SupportedLanguages member → Prism syntax identifier.
|
|
*
|
|
* If a new language is added to the enum without adding an entry here,
|
|
* TypeScript emits a compile error.
|
|
*/
|
|
const SYNTAX_MAP: Record<SupportedLanguages, string> = {
|
|
[SupportedLanguages.JavaScript]: 'javascript',
|
|
[SupportedLanguages.TypeScript]: 'typescript',
|
|
[SupportedLanguages.Python]: 'python',
|
|
[SupportedLanguages.Java]: 'java',
|
|
[SupportedLanguages.C]: 'c',
|
|
[SupportedLanguages.CPlusPlus]: 'cpp',
|
|
[SupportedLanguages.CSharp]: 'csharp',
|
|
[SupportedLanguages.Go]: 'go',
|
|
[SupportedLanguages.Ruby]: 'ruby',
|
|
[SupportedLanguages.Rust]: 'rust',
|
|
[SupportedLanguages.PHP]: 'php',
|
|
[SupportedLanguages.Kotlin]: 'kotlin',
|
|
[SupportedLanguages.Swift]: 'swift',
|
|
[SupportedLanguages.Dart]: 'dart',
|
|
[SupportedLanguages.Vue]: 'typescript',
|
|
[SupportedLanguages.Cobol]: 'cobol',
|
|
} satisfies Record<SupportedLanguages, string>; // Ensure exhaustiveness
|
|
|
|
/** Non-code file extensions → Prism-compatible syntax identifiers */
|
|
const AUXILIARY_SYNTAX_MAP: Record<string, string> = {
|
|
json: 'json',
|
|
yaml: 'yaml',
|
|
yml: 'yaml',
|
|
md: 'markdown',
|
|
mdx: 'markdown',
|
|
html: 'markup',
|
|
htm: 'markup',
|
|
erb: 'markup',
|
|
xml: 'markup',
|
|
css: 'css',
|
|
scss: 'css',
|
|
sass: 'css',
|
|
sh: 'bash',
|
|
bash: 'bash',
|
|
zsh: 'bash',
|
|
sql: 'sql',
|
|
toml: 'toml',
|
|
ini: 'ini',
|
|
dockerfile: 'docker',
|
|
};
|
|
|
|
/** Extensionless filenames → Prism-compatible syntax identifiers */
|
|
const AUXILIARY_BASENAME_MAP: Record<string, string> = {
|
|
Makefile: 'makefile',
|
|
Dockerfile: 'docker',
|
|
};
|
|
|
|
/**
|
|
* Map file path to a Prism-compatible syntax highlight language string.
|
|
* Covers all SupportedLanguages (code files) plus common non-code formats.
|
|
* Returns 'text' for unrecognised files.
|
|
*/
|
|
export const getSyntaxLanguageFromFilename = (filePath: string): string => {
|
|
const lang = getLanguageFromFilename(filePath);
|
|
if (lang) return SYNTAX_MAP[lang];
|
|
const ext = filePath.split('.').pop()?.toLowerCase();
|
|
if (ext && ext in AUXILIARY_SYNTAX_MAP) return AUXILIARY_SYNTAX_MAP[ext];
|
|
const basename = filePath.split('/').pop() || '';
|
|
if (basename in AUXILIARY_BASENAME_MAP) return AUXILIARY_BASENAME_MAP[basename];
|
|
return 'text';
|
|
};
|