mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-08 03:08:13 +00:00
feat(bash): add Bash language provider and all extractors
Full tree-sitter-based Bash support using tree-sitter-bash@0.23.3 (N-API binding, ABI version 14): - BASH_QUERIES: function definitions, source/dot imports, command calls, variable assignments - Minimal extractors (no classes, no types, no heritage — shell semantics) - Export checker (all public, like Ruby) - Import resolver for source/dot file paths - Registered in all exhaustive records
This commit is contained in:
parent
c1f8cc51a1
commit
c350181c27
14 changed files with 296 additions and 0 deletions
|
|
@ -0,0 +1,6 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { CallExtractionConfig } from '../../call-types.js';
|
||||
|
||||
export const bashCallConfig: CallExtractionConfig = {
|
||||
language: SupportedLanguages.Bash,
|
||||
};
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { ClassExtractionConfig } from '../../class-types.js';
|
||||
|
||||
export const bashClassConfig: ClassExtractionConfig = {
|
||||
language: SupportedLanguages.Bash,
|
||||
typeDeclarationNodes: [],
|
||||
fileScopeNodeTypes: ['program'],
|
||||
};
|
||||
|
|
@ -246,3 +246,6 @@ export const rubyExportChecker: ExportChecker = (_node, _name) => true;
|
|||
|
||||
/** Dart: public if no leading underscore (convention, same as Python). */
|
||||
export const dartExportChecker: ExportChecker = (_node, name) => !name.startsWith('_');
|
||||
|
||||
/** Bash: all definitions are public (no visibility modifiers in shell). */
|
||||
export const bashExportChecker: ExportChecker = (_node, _name) => true;
|
||||
|
|
|
|||
26
gitnexus/src/core/ingestion/field-extractors/configs/bash.ts
Normal file
26
gitnexus/src/core/ingestion/field-extractors/configs/bash.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { FieldExtractionConfig } from '../generic.js';
|
||||
|
||||
export const bashFieldConfig: FieldExtractionConfig = {
|
||||
language: SupportedLanguages.Bash,
|
||||
typeDeclarationNodes: [],
|
||||
fieldNodeTypes: [],
|
||||
bodyNodeTypes: [],
|
||||
defaultVisibility: 'public',
|
||||
|
||||
extractName(_node) {
|
||||
return undefined;
|
||||
},
|
||||
extractType(_node) {
|
||||
return undefined;
|
||||
},
|
||||
extractVisibility(_node) {
|
||||
return 'public';
|
||||
},
|
||||
isStatic(_node) {
|
||||
return false;
|
||||
},
|
||||
isReadonly(_node) {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { HeritageExtractionConfig } from '../../heritage-types.js';
|
||||
|
||||
export const bashHeritageConfig: HeritageExtractionConfig = {
|
||||
language: SupportedLanguages.Bash,
|
||||
};
|
||||
21
gitnexus/src/core/ingestion/import-resolvers/configs/bash.ts
Normal file
21
gitnexus/src/core/ingestion/import-resolvers/configs/bash.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Bash import resolution config.
|
||||
* Bash uses `source file.sh` or `. file.sh` to include other scripts.
|
||||
*/
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { ImportResolutionConfig, ImportResolverStrategy } from '../types.js';
|
||||
import { suffixResolve } from '../utils.js';
|
||||
|
||||
export const bashSourceStrategy: ImportResolverStrategy = (rawImportPath, _filePath, ctx) => {
|
||||
const cleanPath = rawImportPath.replace(/^['"]|['"]$/g, '').replace(/^\$\{.*\}\/?/, '');
|
||||
if (!cleanPath) return null;
|
||||
const pathParts = cleanPath.split('/').filter(Boolean);
|
||||
const resolved = suffixResolve(pathParts, ctx.normalizedFileList, ctx.allFileList, ctx.index);
|
||||
return resolved ? { kind: 'files', files: [resolved] } : null;
|
||||
};
|
||||
|
||||
export const bashImportConfig: ImportResolutionConfig = {
|
||||
language: SupportedLanguages.Bash,
|
||||
strategies: [bashSourceStrategy],
|
||||
};
|
||||
|
|
@ -47,6 +47,9 @@ export const EXTENSIONS = [
|
|||
'.swift',
|
||||
// Ruby
|
||||
'.rb',
|
||||
// Bash
|
||||
'.sh',
|
||||
'.bash',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
108
gitnexus/src/core/ingestion/languages/bash.ts
Normal file
108
gitnexus/src/core/ingestion/languages/bash.ts
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* Bash Language Provider
|
||||
*
|
||||
* Key Bash traits:
|
||||
* - importSemantics: 'wildcard-leaf' (source brings everything into scope)
|
||||
* - No classes or OO constructs
|
||||
* - Functions defined with `function foo() {}` or `foo() {}`
|
||||
* - Variables are untyped (all strings)
|
||||
* - Imports via `source file.sh` or `. file.sh`
|
||||
*/
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import { createClassExtractor } from '../class-extractors/generic.js';
|
||||
import { bashClassConfig } from '../class-extractors/configs/bash.js';
|
||||
import { defineLanguage } from '../language-provider.js';
|
||||
import { typeConfig as bashConfig } from '../type-extractors/bash.js';
|
||||
import { bashExportChecker } from '../export-detection.js';
|
||||
import { createImportResolver } from '../import-resolvers/resolver-factory.js';
|
||||
import { bashImportConfig } from '../import-resolvers/configs/bash.js';
|
||||
import { BASH_QUERIES } from '../tree-sitter-queries.js';
|
||||
import { createFieldExtractor } from '../field-extractors/generic.js';
|
||||
import { bashFieldConfig } from '../field-extractors/configs/bash.js';
|
||||
import { createMethodExtractor } from '../method-extractors/generic.js';
|
||||
import { bashMethodConfig } from '../method-extractors/configs/bash.js';
|
||||
import { createVariableExtractor } from '../variable-extractors/generic.js';
|
||||
import { bashVariableConfig } from '../variable-extractors/configs/bash.js';
|
||||
import { createCallExtractor } from '../call-extractors/generic.js';
|
||||
import { bashCallConfig } from '../call-extractors/configs/bash.js';
|
||||
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
||||
import { bashHeritageConfig } from '../heritage-extractors/configs/bash.js';
|
||||
|
||||
const BUILT_INS: ReadonlySet<string> = new Set([
|
||||
'echo',
|
||||
'printf',
|
||||
'read',
|
||||
'cd',
|
||||
'pwd',
|
||||
'ls',
|
||||
'cat',
|
||||
'grep',
|
||||
'sed',
|
||||
'awk',
|
||||
'find',
|
||||
'xargs',
|
||||
'sort',
|
||||
'uniq',
|
||||
'wc',
|
||||
'head',
|
||||
'tail',
|
||||
'cut',
|
||||
'tr',
|
||||
'tee',
|
||||
'test',
|
||||
'true',
|
||||
'false',
|
||||
'exit',
|
||||
'return',
|
||||
'export',
|
||||
'unset',
|
||||
'local',
|
||||
'declare',
|
||||
'readonly',
|
||||
'shift',
|
||||
'set',
|
||||
'eval',
|
||||
'exec',
|
||||
'trap',
|
||||
'wait',
|
||||
'kill',
|
||||
'sleep',
|
||||
'date',
|
||||
'basename',
|
||||
'dirname',
|
||||
'mktemp',
|
||||
'mkdir',
|
||||
'rm',
|
||||
'cp',
|
||||
'mv',
|
||||
'chmod',
|
||||
'chown',
|
||||
'touch',
|
||||
'ln',
|
||||
'which',
|
||||
'command',
|
||||
'type',
|
||||
'hash',
|
||||
'getopts',
|
||||
'pushd',
|
||||
'popd',
|
||||
'dirs',
|
||||
]);
|
||||
|
||||
export const bashProvider = defineLanguage({
|
||||
id: SupportedLanguages.Bash,
|
||||
extensions: ['.sh', '.bash'],
|
||||
treeSitterQueries: BASH_QUERIES,
|
||||
typeConfig: bashConfig,
|
||||
exportChecker: bashExportChecker,
|
||||
importResolver: createImportResolver(bashImportConfig),
|
||||
importSemantics: 'wildcard-leaf',
|
||||
callExtractor: createCallExtractor(bashCallConfig),
|
||||
fieldExtractor: createFieldExtractor(bashFieldConfig),
|
||||
methodExtractor: createMethodExtractor(bashMethodConfig),
|
||||
variableExtractor: createVariableExtractor(bashVariableConfig),
|
||||
classExtractor: createClassExtractor(bashClassConfig),
|
||||
heritageExtractor: createHeritageExtractor(bashHeritageConfig),
|
||||
builtInNames: BUILT_INS,
|
||||
});
|
||||
|
|
@ -24,6 +24,7 @@ import { rubyProvider } from './ruby.js';
|
|||
import { swiftProvider } from './swift.js';
|
||||
import { dartProvider } from './dart.js';
|
||||
import { vueProvider } from './vue.js';
|
||||
import { bashProvider } from './bash.js';
|
||||
import { cobolProvider } from './cobol.js';
|
||||
|
||||
export const providers = {
|
||||
|
|
@ -41,6 +42,7 @@ export const providers = {
|
|||
[SupportedLanguages.Ruby]: rubyProvider,
|
||||
[SupportedLanguages.Swift]: swiftProvider,
|
||||
[SupportedLanguages.Dart]: dartProvider,
|
||||
[SupportedLanguages.Bash]: bashProvider,
|
||||
[SupportedLanguages.Vue]: vueProvider,
|
||||
[SupportedLanguages.Cobol]: cobolProvider,
|
||||
} satisfies Record<SupportedLanguages, LanguageProvider>;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { MethodExtractionConfig, ParameterInfo } from '../../method-types.js';
|
||||
import type { SyntaxNode } from '../../utils/ast-helpers.js';
|
||||
|
||||
export const bashMethodConfig: MethodExtractionConfig = {
|
||||
language: SupportedLanguages.Bash,
|
||||
typeDeclarationNodes: [],
|
||||
methodNodeTypes: ['function_definition'],
|
||||
bodyNodeTypes: [],
|
||||
|
||||
extractName(node: SyntaxNode) {
|
||||
return node.childForFieldName('name')?.text;
|
||||
},
|
||||
|
||||
extractReturnType(_node) {
|
||||
return undefined;
|
||||
},
|
||||
|
||||
extractParameters(_node): ParameterInfo[] {
|
||||
return [];
|
||||
},
|
||||
|
||||
extractVisibility(_node) {
|
||||
return 'public';
|
||||
},
|
||||
|
||||
isStatic(_node) {
|
||||
return false;
|
||||
},
|
||||
|
||||
isAbstract(_node) {
|
||||
return false;
|
||||
},
|
||||
|
||||
isFinal(_node) {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
|
@ -1373,6 +1373,23 @@ export const DART_QUERIES = `
|
|||
(type_identifier) @heritage.trait))) @heritage
|
||||
`;
|
||||
|
||||
export const BASH_QUERIES = `
|
||||
; Functions: function foo() {} or foo() {}
|
||||
(function_definition name: (word) @name) @definition.function
|
||||
|
||||
; Source imports: source file.sh or . file.sh
|
||||
(command
|
||||
name: (command_name) @_cmd
|
||||
argument: (_) @import.source
|
||||
(#match? @_cmd "^(source|\\\\.)$")) @import
|
||||
|
||||
; Command calls: foo args...
|
||||
(command name: (command_name (word) @call.name)) @call
|
||||
|
||||
; Variable assignments: FOO=bar
|
||||
(variable_assignment name: (variable_name) @name) @definition.variable
|
||||
`;
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
|
||||
export const LANGUAGE_QUERIES: Record<SupportedLanguages, string> = {
|
||||
|
|
@ -1390,6 +1407,7 @@ export const LANGUAGE_QUERIES: Record<SupportedLanguages, string> = {
|
|||
[SupportedLanguages.Ruby]: RUBY_QUERIES,
|
||||
[SupportedLanguages.Swift]: SWIFT_QUERIES,
|
||||
[SupportedLanguages.Dart]: DART_QUERIES,
|
||||
[SupportedLanguages.Bash]: BASH_QUERIES,
|
||||
[SupportedLanguages.Vue]: TYPESCRIPT_QUERIES, // Vue <script> blocks are parsed as TypeScript
|
||||
[SupportedLanguages.Cobol]: '', // Standalone regex processor — no tree-sitter queries
|
||||
};
|
||||
|
|
|
|||
18
gitnexus/src/core/ingestion/type-extractors/bash.ts
Normal file
18
gitnexus/src/core/ingestion/type-extractors/bash.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import type { SyntaxNode } from '../utils/ast-helpers.js';
|
||||
import type { LanguageTypeConfig, ParameterExtractor, TypeBindingExtractor } from './types.js';
|
||||
|
||||
const extractDeclaration: TypeBindingExtractor = (
|
||||
_node: SyntaxNode,
|
||||
_env: Map<string, string>,
|
||||
): void => {};
|
||||
|
||||
const extractParameter: ParameterExtractor = (
|
||||
_node: SyntaxNode,
|
||||
_env: Map<string, string>,
|
||||
): void => {};
|
||||
|
||||
export const typeConfig: LanguageTypeConfig = {
|
||||
declarationNodeTypes: new Set(['variable_assignment']),
|
||||
extractDeclaration,
|
||||
extractParameter,
|
||||
};
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { VariableExtractionConfig, VariableVisibility } from '../../variable-types.js';
|
||||
import type { SyntaxNode } from '../../utils/ast-helpers.js';
|
||||
|
||||
export const bashVariableConfig: VariableExtractionConfig = {
|
||||
language: SupportedLanguages.Bash,
|
||||
constNodeTypes: [],
|
||||
staticNodeTypes: [],
|
||||
variableNodeTypes: ['variable_assignment'],
|
||||
|
||||
extractName(node: SyntaxNode) {
|
||||
return node.childForFieldName('name')?.text;
|
||||
},
|
||||
|
||||
extractType(_node) {
|
||||
return undefined;
|
||||
},
|
||||
|
||||
extractVisibility(_node): VariableVisibility {
|
||||
return 'public';
|
||||
},
|
||||
|
||||
isConst(_node) {
|
||||
return false;
|
||||
},
|
||||
|
||||
isStatic(_node) {
|
||||
return false;
|
||||
},
|
||||
|
||||
isMutable(_node) {
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
|
@ -153,6 +153,11 @@ const SOURCES: Record<string, GrammarSource> = {
|
|||
'Likely cause: native compile failed at install (missing python3/make/g++). ' +
|
||||
`See ${ISSUES_URL}/1125.`,
|
||||
},
|
||||
[SupportedLanguages.Bash]: {
|
||||
load: () => _require('tree-sitter-bash'),
|
||||
unavailableNote:
|
||||
'Bash parsing requires `tree-sitter-bash`. Check the install and native binding.',
|
||||
},
|
||||
[SupportedLanguages.Kotlin]: {
|
||||
load: () => _require('tree-sitter-kotlin'),
|
||||
optional: true,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue