graph-indexer 2.1.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +260 -1
- package/README.md +232 -312
- package/SECURITY.md +37 -59
- package/bin/graph-indexer.mjs +251 -0
- package/integrations/claude-code/.claude-plugin/plugin.json +8 -0
- package/integrations/claude-code/.mcp.json +8 -0
- package/integrations/claude-code/agents/code-structure.md +22 -0
- package/integrations/claude-code/hooks/gi-hook.mjs +99 -0
- package/integrations/claude-code/hooks/hooks.json +16 -0
- package/integrations/opencode/graph-indexer.js +124 -0
- package/package.json +32 -109
- package/src/cli/helper.mjs +67 -0
- package/src/cli/hook.mjs +356 -0
- package/src/cli/init.mjs +255 -0
- package/src/cli/resident.mjs +175 -0
- package/src/index/discover.mjs +130 -0
- package/src/index/fingerprint.mjs +30 -0
- package/src/index/graph.mjs +61 -0
- package/src/index/indexer.mjs +476 -0
- package/src/index/modules.mjs +331 -0
- package/src/index/resolver.mjs +954 -0
- package/src/mcp/render.mjs +60 -0
- package/src/mcp/server.mjs +147 -0
- package/src/mcp/tools.mjs +973 -0
- package/src/parse/arity.mjs +152 -0
- package/src/parse/extract.mjs +1031 -0
- package/src/parse/lang/csharp.mjs +106 -0
- package/src/parse/lang/ecma.mjs +438 -0
- package/src/parse/lang/go.mjs +194 -0
- package/src/parse/lang/jvm.mjs +118 -0
- package/src/parse/lang/kotlin.mjs +97 -0
- package/src/parse/lang/misc.mjs +115 -0
- package/src/parse/lang/native.mjs +126 -0
- package/src/parse/lang/python.mjs +153 -0
- package/src/parse/lang/rust.mjs +157 -0
- package/src/parse/lang/scripting.mjs +219 -0
- package/src/parse/languages.mjs +48 -0
- package/src/parse/runtime.mjs +56 -0
- package/src/query/check.mjs +343 -0
- package/src/query/facts.mjs +323 -0
- package/src/query/intel.mjs +680 -0
- package/src/query/read.mjs +81 -0
- package/src/query/testcmd.mjs +119 -0
- package/src/query/textsearch.mjs +160 -0
- package/src/search/search.mjs +210 -0
- package/src/search/tokenize.mjs +188 -0
- package/src/store/db.mjs +169 -0
- package/src/util/paths.mjs +24 -0
- package/vendor/grammars/bash.wasm.gz +0 -0
- package/vendor/grammars/c.wasm.gz +0 -0
- package/vendor/grammars/c_sharp.wasm.gz +0 -0
- package/vendor/grammars/cpp.wasm.gz +0 -0
- package/vendor/grammars/css.wasm.gz +0 -0
- package/vendor/grammars/go.wasm.gz +0 -0
- package/vendor/grammars/java.wasm.gz +0 -0
- package/vendor/grammars/javascript.wasm.gz +0 -0
- package/vendor/grammars/kotlin.wasm.gz +0 -0
- package/vendor/grammars/manifest.json +148 -0
- package/vendor/grammars/php.wasm.gz +0 -0
- package/vendor/grammars/python.wasm.gz +0 -0
- package/vendor/grammars/ruby.wasm.gz +0 -0
- package/vendor/grammars/rust.wasm.gz +0 -0
- package/vendor/grammars/scala.wasm.gz +0 -0
- package/vendor/grammars/tsx.wasm.gz +0 -0
- package/vendor/grammars/typescript.wasm.gz +0 -0
- package/vendor/web-tree-sitter/web-tree-sitter.js +3980 -0
- package/vendor/web-tree-sitter/web-tree-sitter.wasm +0 -0
- package/cli-ui.mjs +0 -78
- package/colbert.mjs +0 -168
- package/config.mjs +0 -494
- package/daemon-ctl.mjs +0 -175
- package/daemon-lock.mjs +0 -69
- package/embedders/python/mlx_embed_server.py +0 -61
- package/embedders/python/requirements-mlx.txt +0 -5
- package/embedders/setup-mlx.mjs +0 -110
- package/embeddings.mjs +0 -571
- package/engine/binary.mjs +0 -437
- package/engine/memory.mjs +0 -1003
- package/engine/sqlite.mjs +0 -1136
- package/enrichment.mjs +0 -521
- package/git-signals.mjs +0 -248
- package/indexer.mjs +0 -546
- package/init.mjs +0 -1809
- package/layout.mjs +0 -123
- package/mcp/centrality.mjs +0 -117
- package/mcp/format.mjs +0 -61
- package/mcp/resolver.mjs +0 -116
- package/mcp/symbolgraph.mjs +0 -137
- package/mcp/taint.mjs +0 -225
- package/mcp/tools.mjs +0 -1582
- package/mcp/topology.mjs +0 -676
- package/mcp-server.mjs +0 -197
- package/parse/extractor.mjs +0 -628
- package/parse/imports.mjs +0 -365
- package/parse/interprocedural.mjs +0 -134
- package/parse/languages.mjs +0 -253
- package/parse/metadata.mjs +0 -701
- package/parse/routes.mjs +0 -327
- package/parse/scip.mjs +0 -391
- package/parse/taint-patterns.mjs +0 -119
- package/prompts/CORE.md +0 -91
- package/prompts/DOMAIN_TEMPLATE.md +0 -46
- package/prompts/INTEGRATION.md +0 -123
- package/prompts/frameworks/ANDROID.md +0 -17
- package/prompts/frameworks/ASPNET_CORE.md +0 -19
- package/prompts/frameworks/FASTAPI_DJANGO.md +0 -20
- package/prompts/frameworks/LARAVEL_SYMFONY.md +0 -22
- package/prompts/frameworks/NODE_EXPRESS_NESTJS.md +0 -19
- package/prompts/frameworks/RAILS.md +0 -20
- package/prompts/frameworks/REACT.md +0 -20
- package/prompts/frameworks/SPRING_BOOT.md +0 -20
- package/prompts/languages/BASH.md +0 -22
- package/prompts/languages/C.md +0 -21
- package/prompts/languages/CSHARP.md +0 -24
- package/prompts/languages/CSS_SCSS.md +0 -22
- package/prompts/languages/GO.md +0 -24
- package/prompts/languages/JAVA.md +0 -23
- package/prompts/languages/JAVASCRIPT_TYPESCRIPT.md +0 -24
- package/prompts/languages/KOTLIN.md +0 -24
- package/prompts/languages/PHP.md +0 -23
- package/prompts/languages/PYTHON.md +0 -23
- package/prompts/languages/RUBY.md +0 -23
- package/prompts/languages/RUST.md +0 -21
- package/prompts/languages/SWIFT.md +0 -21
- package/seal.mjs +0 -400
- package/search-core.mjs +0 -777
- package/search-sparse.mjs +0 -183
- package/storage.mjs +0 -73
- package/watch-daemon.mjs +0 -289
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/** Ruby, PHP and Bash extraction specs. */
|
|
2
|
+
|
|
3
|
+
// ─── Ruby ──────────────────────────────────────────────────────────────────────
|
|
4
|
+
const RUBY_CALLBACKS = 'before_action|after_action|around_action|skip_before_action|prepend_before_action|before_filter|after_filter|validate|validates_with|before_save|after_save|around_save|after_commit|after_create_commit|after_update_commit|before_validation|after_validation|before_create|after_create|before_update|after_update|before_destroy|after_destroy|helper_method|delegate|alias_method';
|
|
5
|
+
const RUBY_QUERY = `
|
|
6
|
+
[(block) (do_block) (lambda)] @scope
|
|
7
|
+
(method name: (_) @name) @def.method
|
|
8
|
+
(singleton_method name: (_) @name) @def.method
|
|
9
|
+
(class name: [(constant) @name (scope_resolution name: (constant) @name)]) @def.class
|
|
10
|
+
(module name: [(constant) @name (scope_resolution name: (constant) @name)]) @def.module
|
|
11
|
+
(assignment left: (constant) @name) @def.constant
|
|
12
|
+
|
|
13
|
+
(call method: (identifier) @name) @ref.call
|
|
14
|
+
(call receiver: (_) @recv method: (identifier) @name) @ref.call
|
|
15
|
+
(call receiver: [(constant) @name (scope_resolution name: (constant) @name)] method: (identifier) @_new (#eq? @_new "new")) @ref.new
|
|
16
|
+
(superclass [(constant) @name (scope_resolution name: (constant) @name)]) @ref.inherit
|
|
17
|
+
(call method: (identifier) @_inc arguments: (argument_list [(constant) @name (scope_resolution name: (constant) @name)]) (#match? @_inc "^(include|extend|prepend)$")) @ref.inherit
|
|
18
|
+
(call method: (identifier) @_cb arguments: (argument_list (simple_symbol) @name) (#match? @_cb "^(${RUBY_CALLBACKS})$")) @ref.value
|
|
19
|
+
(scope_resolution scope: (constant) @recv name: (constant) @name) @ref.type
|
|
20
|
+
(constant) @name @ref.type
|
|
21
|
+
|
|
22
|
+
(call method: (identifier) @_r arguments: (argument_list . (string) @src) (#match? @_r "^(require|require_relative|load|autoload)$")) @import.require
|
|
23
|
+
|
|
24
|
+
(assignment left: (identifier) @bind.name right: (call receiver: (constant) @bind.new method: (identifier) @_n (#eq? @_n "new"))) @bind
|
|
25
|
+
(assignment left: (instance_variable) @field.name right: (call receiver: (constant) @field.new method: (identifier) @_n2 (#eq? @_n2 "new"))) @field
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
function rubyRequire(callNode, srcNode) {
|
|
29
|
+
const method = callNode.childForFieldName('method')?.text;
|
|
30
|
+
const source = srcNode.text.replace(/^['"]|['"]$/g, '');
|
|
31
|
+
return [{ source: method === 'require_relative' ? (source.startsWith('.') ? source : './' + source) : source, imported: '*', local: null }];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const ruby = {
|
|
35
|
+
id: 'ruby',
|
|
36
|
+
grammar: 'ruby',
|
|
37
|
+
extensions: ['.rb', '.rake', '.gemspec'],
|
|
38
|
+
family: 'ruby',
|
|
39
|
+
query: RUBY_QUERY,
|
|
40
|
+
implicitThis: true,
|
|
41
|
+
selfNames: ['self'],
|
|
42
|
+
externalReceivers: new Set(['Rails', 'ActiveRecord', 'Time', 'Date', 'DateTime', 'File', 'Dir', 'JSON', 'YAML',
|
|
43
|
+
'Kernel', 'Math', 'Logger', 'ENV', 'STDOUT', 'STDERR', 'Struct', 'Integer', 'String', 'Array', 'Hash', 'Set',
|
|
44
|
+
'Float', 'Regexp', 'Thread', 'Process', 'IO', 'URI', 'Net', 'OpenStruct', 'SecureRandom', 'Digest', 'Base64']),
|
|
45
|
+
builtinCalls: new Set(['puts', 'print', 'p', 'pp', 'raise', 'require', 'require_relative', 'include', 'extend',
|
|
46
|
+
'attr_reader', 'attr_writer', 'attr_accessor', 'private', 'protected', 'public', 'lambda', 'proc', 'loop',
|
|
47
|
+
'block_given?', 'yield', 'new', 'freeze', 'dup', 'to_s', 'to_i', 'to_a', 'to_h', 'to_sym', 'nil?', 'present?',
|
|
48
|
+
'blank?', 'respond_to?', 'is_a?', 'kind_of?', 'send', 'public_send', 'tap', 'then', 'each', 'map', 'select',
|
|
49
|
+
'reject', 'find', 'where', 'let', 'describe', 'it', 'context', 'expect', 'subject', 'before', 'after']),
|
|
50
|
+
testFile: /(^|\/)(spec|test)\/|_spec\.rb$|_test\.rb$/,
|
|
51
|
+
commentTypes: ['comment'],
|
|
52
|
+
parseImport: () => [],
|
|
53
|
+
parseRequire: rubyRequire,
|
|
54
|
+
normalizeRefName: (name) => name.replace(/^:/, ''),
|
|
55
|
+
refineKind(kind, d, parent) {
|
|
56
|
+
if (kind === 'method' && !parent) return 'function';
|
|
57
|
+
return kind;
|
|
58
|
+
},
|
|
59
|
+
isExported: () => true,
|
|
60
|
+
visibility: null,
|
|
61
|
+
receiverDescriptor: (node) => (node.type === 'instance_variable' ? 'this.' + node.text.slice(1) : undefined),
|
|
62
|
+
isPrimitiveType: () => false,
|
|
63
|
+
bodyField: 'body',
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// ─── PHP ───────────────────────────────────────────────────────────────────────
|
|
67
|
+
const PT = (cap) => `[(named_type (name) @${cap}) (named_type (qualified_name (name) @${cap} .)) (optional_type (named_type (name) @${cap}))]`;
|
|
68
|
+
const PHP_QUERY = `
|
|
69
|
+
[(anonymous_function) (arrow_function)] @scope
|
|
70
|
+
(return_statement (_) @ret)
|
|
71
|
+
(function_definition name: (name) @name) @def.function
|
|
72
|
+
(class_declaration name: (name) @name) @def.class
|
|
73
|
+
(interface_declaration name: (name) @name) @def.interface
|
|
74
|
+
(trait_declaration name: (name) @name) @def.trait
|
|
75
|
+
(enum_declaration name: (name) @name) @def.enum
|
|
76
|
+
(method_declaration name: (name) @name return_type: ${PT('type')}) @def.method
|
|
77
|
+
(method_declaration name: (name) @name) @def.method
|
|
78
|
+
(property_declaration type: ${PT('type')} (property_element name: (variable_name (name) @name))) @def.property
|
|
79
|
+
(property_declaration (property_element name: (variable_name (name) @name))) @def.property
|
|
80
|
+
(const_declaration (const_element (name) @name)) @def.constant
|
|
81
|
+
(enum_case name: (name) @name) @def.constant
|
|
82
|
+
|
|
83
|
+
(function_call_expression function: (name) @name) @ref.call
|
|
84
|
+
(function_call_expression function: (qualified_name (name) @name .)) @ref.call
|
|
85
|
+
(member_call_expression object: (_) @recv name: (name) @name) @ref.call
|
|
86
|
+
(nullsafe_member_call_expression object: (_) @recv name: (name) @name) @ref.call
|
|
87
|
+
(scoped_call_expression scope: (_) @recv name: (name) @name) @ref.call
|
|
88
|
+
(object_creation_expression (name) @name) @ref.new
|
|
89
|
+
(object_creation_expression (qualified_name (name) @name .)) @ref.new
|
|
90
|
+
(base_clause (name) @name) @ref.inherit
|
|
91
|
+
(base_clause (qualified_name (name) @name .)) @ref.inherit
|
|
92
|
+
(class_interface_clause (name) @name) @ref.inherit
|
|
93
|
+
(class_interface_clause (qualified_name (name) @name .)) @ref.inherit
|
|
94
|
+
(use_declaration (name) @name) @ref.inherit
|
|
95
|
+
(use_declaration (qualified_name (name) @name .)) @ref.inherit
|
|
96
|
+
(named_type (name) @name) @ref.type
|
|
97
|
+
(named_type (qualified_name (name) @name .)) @ref.type
|
|
98
|
+
(class_constant_access_expression . (name) @name) @ref.type
|
|
99
|
+
(class_constant_access_expression . (qualified_name (name) @name .)) @ref.type
|
|
100
|
+
(attribute (name) @name) @ref.decorator
|
|
101
|
+
(attribute (qualified_name (name) @name .)) @ref.decorator
|
|
102
|
+
|
|
103
|
+
(namespace_use_declaration) @import
|
|
104
|
+
|
|
105
|
+
(simple_parameter type: ${PT('bind.type')} name: (variable_name (name) @bind.name)) @bind
|
|
106
|
+
(assignment_expression left: (variable_name (name) @bind.name) right: (object_creation_expression (name) @bind.new)) @bind
|
|
107
|
+
(assignment_expression left: (variable_name (name) @bind.name) right: (object_creation_expression (qualified_name (name) @bind.new .))) @bind
|
|
108
|
+
(property_promotion_parameter type: ${PT('field.type')} name: (variable_name (name) @field.name)) @field
|
|
109
|
+
(property_declaration type: ${PT('field.type')} (property_element name: (variable_name (name) @field.name))) @field
|
|
110
|
+
(assignment_expression left: (member_access_expression object: (variable_name (name) @_t) name: (name) @field.name) right: (object_creation_expression (name) @field.new) (#eq? @_t "this")) @field
|
|
111
|
+
(assignment_expression left: (member_access_expression object: (variable_name (name) @_t2) name: (name) @field.name) right: (variable_name (name) @field.var) (#eq? @_t2 "this")) @field
|
|
112
|
+
`;
|
|
113
|
+
|
|
114
|
+
function phpUse(node) {
|
|
115
|
+
const out = [];
|
|
116
|
+
for (const clause of node.namedChildren) {
|
|
117
|
+
if (clause.type === 'namespace_use_clause') {
|
|
118
|
+
const nameNode = clause.namedChildren.find(c => c.type === 'qualified_name' || c.type === 'name');
|
|
119
|
+
const full = nameNode?.text.replace(/^\\/, '') ?? '';
|
|
120
|
+
const alias = clause.childForFieldName('alias')?.text;
|
|
121
|
+
const parts = full.split('\\');
|
|
122
|
+
const name = parts.pop();
|
|
123
|
+
out.push({ source: parts.join('\\'), imported: name, local: alias || name });
|
|
124
|
+
} else if (clause.type === 'namespace_use_group') {
|
|
125
|
+
const prefix = node.namedChildren.find(c => c.type === 'namespace_name')?.text ?? '';
|
|
126
|
+
for (const g of clause.namedChildren) {
|
|
127
|
+
const nameNode = g.namedChildren.find(c => c.type === 'qualified_name' || c.type === 'name');
|
|
128
|
+
if (!nameNode) continue;
|
|
129
|
+
const full = (prefix ? prefix + '\\' : '') + nameNode.text;
|
|
130
|
+
const parts = full.split('\\');
|
|
131
|
+
const name = parts.pop();
|
|
132
|
+
out.push({ source: parts.join('\\'), imported: name, local: g.childForFieldName('alias')?.text || name });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function phpVisibility(node) {
|
|
140
|
+
const v = node.namedChildren.find(c => c.type === 'visibility_modifier');
|
|
141
|
+
return v ? v.text : null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function phpFileInfo(root) {
|
|
145
|
+
const ns = root.namedChildren.find(c => c.type === 'namespace_definition');
|
|
146
|
+
return { package: ns?.childForFieldName('name')?.text ?? null };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const php = {
|
|
150
|
+
id: 'php',
|
|
151
|
+
grammar: 'php',
|
|
152
|
+
extensions: ['.php', '.phtml'],
|
|
153
|
+
family: 'php',
|
|
154
|
+
query: PHP_QUERY,
|
|
155
|
+
implicitThis: false,
|
|
156
|
+
selfNames: ['$this', 'this', 'self', 'static'],
|
|
157
|
+
externalReceivers: new Set(['DB', 'Log', 'Cache', 'Config', 'Route', 'Auth', 'Str', 'Arr', 'Carbon', 'Http', 'Storage',
|
|
158
|
+
'Session', 'Request', 'Response', 'Validator', 'Hash', 'Event', 'Queue', 'Mail', 'Gate', 'App', 'URL', 'View',
|
|
159
|
+
'Redirect', 'Artisan', 'Schema', 'Crypt', 'Cookie', 'Lang', 'Notification', 'Password', 'Bus', 'File']),
|
|
160
|
+
builtinCalls: new Set(['array_map', 'array_filter', 'array_merge', 'array_keys', 'array_values', 'in_array', 'count',
|
|
161
|
+
'isset', 'empty', 'unset', 'sprintf', 'printf', 'implode', 'explode', 'str_replace', 'strlen', 'strtolower',
|
|
162
|
+
'strtoupper', 'trim', 'json_encode', 'json_decode', 'is_array', 'is_string', 'is_null', 'is_int', 'intval',
|
|
163
|
+
'array_key_exists', 'var_dump', 'print_r', 'die', 'exit', 'define', 'defined', 'compact', 'extract', 'app', 'config',
|
|
164
|
+
'env', 'view', 'response', 'redirect', 'route', 'url', 'trans', '__', 'collect', 'now', 'dd', 'dump', 'abort',
|
|
165
|
+
'request', 'auth', 'session', 'logger', 'info', 'event', 'dispatch', 'optional', 'tap', 'value', 'with']),
|
|
166
|
+
testFile: /(^|\/)(tests?|Tests?)\/|Test\.php$/,
|
|
167
|
+
commentTypes: ['comment'],
|
|
168
|
+
parseImport: phpUse,
|
|
169
|
+
isExported: (node) => phpVisibility(node) !== 'private',
|
|
170
|
+
visibility: phpVisibility,
|
|
171
|
+
receiverDescriptor(node) {
|
|
172
|
+
const t = node.text.replace(/\s+/g, '');
|
|
173
|
+
if (t === '$this') return 'this';
|
|
174
|
+
const norm = t.replace(/^\$/, '').replace(/->|::|\?->/g, '.').replace(/\.\$/g, '.');
|
|
175
|
+
if (/^[A-Za-z_][\w]*(\.[A-Za-z_][\w]*){0,4}$/.test(norm)) return norm.startsWith('this.') ? norm : norm;
|
|
176
|
+
return undefined;
|
|
177
|
+
},
|
|
178
|
+
isPrimitiveType: (t) => /^(int|string|bool|float|array|mixed|void|null|object|callable|iterable|self|static|never|false|true)$/i.test(t),
|
|
179
|
+
decoratorsOf(node) {
|
|
180
|
+
const al = node.childForFieldName('attributes');
|
|
181
|
+
return al ? al.text.replace(/[#[\]]/g, ' ').split(/[\s,]+/).filter(x => /^[A-Z]\w*$/.test(x)).slice(0, 8) : [];
|
|
182
|
+
},
|
|
183
|
+
fileInfo: phpFileInfo,
|
|
184
|
+
bodyField: 'body',
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// ─── Bash ──────────────────────────────────────────────────────────────────────
|
|
188
|
+
const BASH_QUERY = `
|
|
189
|
+
(function_definition name: (word) @name) @def.function
|
|
190
|
+
(command name: (command_name (word) @name)) @ref.call
|
|
191
|
+
(command name: (command_name (word) @_src) argument: (_) @src (#match? @_src "^(source|\\\\.)$")) @import.require
|
|
192
|
+
`;
|
|
193
|
+
|
|
194
|
+
const BASH_BUILTINS = new Set(['echo', 'cd', 'ls', 'cat', 'rm', 'cp', 'mv', 'mkdir', 'rmdir', 'touch', 'ln', 'export',
|
|
195
|
+
'local', 'readonly', 'declare', 'unset', 'shift', 'read', 'printf', 'exit', 'return', 'eval', 'exec', 'trap', 'wait',
|
|
196
|
+
'sleep', 'pwd', 'set', 'shopt', 'source', '.', 'test', '[', '[[', 'true', 'false', 'kill', 'jobs', 'type', 'command',
|
|
197
|
+
'getopts', 'grep', 'sed', 'awk', 'cut', 'tr', 'sort', 'uniq', 'head', 'tail', 'find', 'xargs', 'chmod', 'chown', 'tee',
|
|
198
|
+
'wc', 'basename', 'dirname', 'tar', 'curl', 'wget', 'env', 'git', 'npm', 'node', 'make', 'docker', 'python', 'python3',
|
|
199
|
+
'which', 'hash', 'builtin', 'alias', 'unalias', 'printenv', 'date', 'uname', 'id', 'whoami', 'sudo', 'ps', 'nohup', 'bash', 'sh', 'zsh']);
|
|
200
|
+
|
|
201
|
+
export const bash = {
|
|
202
|
+
id: 'bash',
|
|
203
|
+
grammar: 'bash',
|
|
204
|
+
extensions: ['.sh', '.bash', '.zsh'],
|
|
205
|
+
family: 'shell',
|
|
206
|
+
query: BASH_QUERY,
|
|
207
|
+
implicitThis: false,
|
|
208
|
+
selfNames: [],
|
|
209
|
+
externalReceivers: new Set(),
|
|
210
|
+
builtinCalls: BASH_BUILTINS,
|
|
211
|
+
testFile: /(^|\/)(tests?)\/|_test\.sh$|\.bats$/,
|
|
212
|
+
commentTypes: ['comment'],
|
|
213
|
+
parseImport: () => [],
|
|
214
|
+
parseRequire: (node, src) => [{ source: src.text.replace(/^['"]|['"]$/g, '').replace(/^\$\{?\w+\}?\//, ''), imported: '*', local: null }],
|
|
215
|
+
isExported: () => true,
|
|
216
|
+
visibility: null,
|
|
217
|
+
isPrimitiveType: () => true,
|
|
218
|
+
bodyField: 'body',
|
|
219
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language registry: file extension → extraction spec.
|
|
3
|
+
*/
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { javascript, typescript, tsx } from './lang/ecma.mjs';
|
|
6
|
+
import { python } from './lang/python.mjs';
|
|
7
|
+
import { go } from './lang/go.mjs';
|
|
8
|
+
import { java } from './lang/jvm.mjs';
|
|
9
|
+
import { kotlin } from './lang/kotlin.mjs';
|
|
10
|
+
import { csharp } from './lang/csharp.mjs';
|
|
11
|
+
import { rust } from './lang/rust.mjs';
|
|
12
|
+
import { c, cpp } from './lang/native.mjs';
|
|
13
|
+
import { ruby, php, bash } from './lang/scripting.mjs';
|
|
14
|
+
import { scala, css } from './lang/misc.mjs';
|
|
15
|
+
|
|
16
|
+
export const LANGUAGES = [javascript, typescript, tsx, python, go, java, kotlin, scala, csharp, rust, c, cpp, ruby, php, bash, css];
|
|
17
|
+
|
|
18
|
+
const byExt = new Map();
|
|
19
|
+
for (const spec of LANGUAGES) for (const ext of spec.extensions) byExt.set(ext, spec);
|
|
20
|
+
|
|
21
|
+
/** Headers are ambiguous between C and C++; a sibling .cpp/.cc or C++ syntax decides at index time. */
|
|
22
|
+
export function specForPath(p) {
|
|
23
|
+
const base = path.basename(p);
|
|
24
|
+
const ext = path.extname(base).toLowerCase();
|
|
25
|
+
if (base.endsWith('.d.ts')) return typescript;
|
|
26
|
+
return byExt.get(ext) ?? null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function supportedExtensions() { return [...byExt.keys()]; }
|
|
30
|
+
|
|
31
|
+
export function languageById(id) { return LANGUAGES.find(l => l.id === id) ?? null; }
|
|
32
|
+
|
|
33
|
+
// constructors named by the language rather than declared as such: a method called `initialize` is
|
|
34
|
+
// Ruby's constructor (`Foo.new`) but an ordinary method in TypeScript or Python
|
|
35
|
+
const CONSTRUCTOR_NAMES = {
|
|
36
|
+
javascript: ['constructor'], typescript: ['constructor'], tsx: ['constructor'],
|
|
37
|
+
python: ['__init__', '__new__'], ruby: ['initialize'], php: ['__construct'],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Is this definition a constructor, invoked through its class (`new Foo()`, `Foo()`, `Foo.new`)?
|
|
42
|
+
* `sym`: { name, kind, lang } — a language id, as the index stores it.
|
|
43
|
+
*/
|
|
44
|
+
export function isConstructor(sym) {
|
|
45
|
+
if (!sym) return false;
|
|
46
|
+
if (sym.kind === 'constructor') return true;
|
|
47
|
+
return CONSTRUCTOR_NAMES[sym.lang]?.includes(sym.name) ?? false;
|
|
48
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree-sitter runtime: the WASM build of tree-sitter (vendored web-tree-sitter) plus
|
|
3
|
+
* gzip-compressed grammar binaries shipped in vendor/grammars. No native compilation,
|
|
4
|
+
* no network, no per-platform binaries — the same bytes run on every OS and Node ≥ 22.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import zlib from 'node:zlib';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { Parser, Language, Query } from '../../vendor/web-tree-sitter/web-tree-sitter.js';
|
|
11
|
+
|
|
12
|
+
const VENDOR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../vendor');
|
|
13
|
+
const RUNTIME_WASM = path.join(VENDOR, 'web-tree-sitter', 'web-tree-sitter.wasm');
|
|
14
|
+
const GRAMMAR_DIR = path.join(VENDOR, 'grammars');
|
|
15
|
+
|
|
16
|
+
let initPromise = null;
|
|
17
|
+
const grammarCache = new Map(); // grammar name -> Promise<Language>
|
|
18
|
+
|
|
19
|
+
export function initRuntime() {
|
|
20
|
+
initPromise ??= Parser.init({ locateFile: () => RUNTIME_WASM });
|
|
21
|
+
return initPromise;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function availableGrammars() {
|
|
25
|
+
return fs.readdirSync(GRAMMAR_DIR).filter(f => f.endsWith('.wasm.gz')).map(f => f.slice(0, -'.wasm.gz'.length));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function loadGrammar(name) {
|
|
29
|
+
let p = grammarCache.get(name);
|
|
30
|
+
if (!p) {
|
|
31
|
+
p = (async () => {
|
|
32
|
+
await initRuntime();
|
|
33
|
+
const bytes = zlib.gunzipSync(fs.readFileSync(path.join(GRAMMAR_DIR, `${name}.wasm.gz`)));
|
|
34
|
+
return Language.load(new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength));
|
|
35
|
+
})();
|
|
36
|
+
grammarCache.set(name, p);
|
|
37
|
+
}
|
|
38
|
+
return p;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Compile a query, reporting the offending grammar on failure (queries are per-grammar). */
|
|
42
|
+
export function compileQuery(language, source, label) {
|
|
43
|
+
try {
|
|
44
|
+
return new Query(language, source);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
throw new Error(`query compile failed for ${label}: ${err.message}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function createParser(language) {
|
|
51
|
+
const parser = new Parser();
|
|
52
|
+
parser.setLanguage(language);
|
|
53
|
+
return parser;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { Parser, Language, Query };
|