docsgov 0.1.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/README.md +242 -0
- package/dist/apispec/apispec.js +401 -0
- package/dist/apispec/apispec.test.js +444 -0
- package/dist/apispec/errors.js +17 -0
- package/dist/apispec/index.js +2 -0
- package/dist/check/doclinks.js +167 -0
- package/dist/check/index.js +8 -0
- package/dist/check/run.js +391 -0
- package/dist/check/run.test.js +513 -0
- package/dist/check/suggest.js +134 -0
- package/dist/check/suggest.test.js +92 -0
- package/dist/check/tokens.js +125 -0
- package/dist/cmd/main.js +330 -0
- package/dist/cmd/main.test.js +422 -0
- package/dist/codeq/cache.js +71 -0
- package/dist/codeq/cache.test.js +67 -0
- package/dist/codeq/errors.js +52 -0
- package/dist/codeq/grammars/tree-sitter-go.wasm +0 -0
- package/dist/codeq/grammars/tree-sitter-java.wasm +0 -0
- package/dist/codeq/grammars/tree-sitter-javascript.wasm +0 -0
- package/dist/codeq/grammars/tree-sitter-tsx.wasm +0 -0
- package/dist/codeq/grammars/tree-sitter-typescript.wasm +0 -0
- package/dist/codeq/index.js +11 -0
- package/dist/codeq/resolve.test.js +109 -0
- package/dist/codeq/resolver.js +128 -0
- package/dist/codeq/resolver.test.js +124 -0
- package/dist/codeq/resolvers/go.js +242 -0
- package/dist/codeq/resolvers/go.test.js +143 -0
- package/dist/codeq/resolvers/java.js +349 -0
- package/dist/codeq/resolvers/java.test.js +138 -0
- package/dist/codeq/resolvers/java_queries.js +63 -0
- package/dist/codeq/resolvers/javascript.js +412 -0
- package/dist/codeq/resolvers/javascript.test.js +125 -0
- package/dist/codeq/resolvers/javascript_queries.js +46 -0
- package/dist/codeq/resolvers/typescript.js +366 -0
- package/dist/codeq/resolvers/typescript.test.js +180 -0
- package/dist/codeq/resolvers/typescript_queries.js +78 -0
- package/dist/codeq/signature.js +50 -0
- package/dist/codeq/signature.test.js +50 -0
- package/dist/codeq/suggest.js +96 -0
- package/dist/codeq/treesitter.js +122 -0
- package/dist/codeq/treesitter.test.js +118 -0
- package/dist/config/config.js +74 -0
- package/dist/config/config.test.js +98 -0
- package/dist/config/fs.js +116 -0
- package/dist/config/glob.js +82 -0
- package/dist/config/glob.test.js +61 -0
- package/dist/config/index.js +4 -0
- package/dist/dedup/analyzer/analyzer.js +533 -0
- package/dist/dedup/analyzer/analyzer.test.js +530 -0
- package/dist/dedup/analyzer/canonical.js +74 -0
- package/dist/dedup/analyzer/canonical.test.js +70 -0
- package/dist/dedup/analyzer/cosine_clusters.js +169 -0
- package/dist/dedup/analyzer/cosine_clusters.test.js +131 -0
- package/dist/dedup/analyzer/distinctive.js +85 -0
- package/dist/dedup/analyzer/distinctive.test.js +49 -0
- package/dist/dedup/analyzer/exact_clusters.js +63 -0
- package/dist/dedup/analyzer/exact_clusters.test.js +81 -0
- package/dist/dedup/analyzer/index.js +14 -0
- package/dist/dedup/analyzer/multiplicity.js +110 -0
- package/dist/dedup/analyzer/multiplicity.test.js +123 -0
- package/dist/dedup/analyzer/order.js +22 -0
- package/dist/dedup/analyzer/partial_overlaps.js +65 -0
- package/dist/dedup/analyzer/partial_overlaps.test.js +161 -0
- package/dist/dedup/analyzer/preview.js +84 -0
- package/dist/dedup/analyzer/preview.test.js +46 -0
- package/dist/dedup/analyzer/safety.js +27 -0
- package/dist/dedup/analyzer/safety.test.js +39 -0
- package/dist/dedup/config.js +18 -0
- package/dist/dedup/configload.js +299 -0
- package/dist/dedup/configload.test.js +410 -0
- package/dist/dedup/dedup.index.test.js +203 -0
- package/dist/dedup/dedup.js +143 -0
- package/dist/dedup/dedup.test.js +212 -0
- package/dist/dedup/dedupcfg/config.js +112 -0
- package/dist/dedup/dedupcfg/config.test.js +70 -0
- package/dist/dedup/dedupcfg/index.js +1 -0
- package/dist/dedup/deduptypes/index.js +1 -0
- package/dist/dedup/deduptypes/types.js +9 -0
- package/dist/dedup/deduptypes/types.test.js +34 -0
- package/dist/dedup/embedder/cache.js +23 -0
- package/dist/dedup/embedder/cache.test.js +50 -0
- package/dist/dedup/embedder/constants.js +10 -0
- package/dist/dedup/embedder/embedder.js +76 -0
- package/dist/dedup/embedder/embedder.mock.test.js +128 -0
- package/dist/dedup/embedder/embedder.test.js +96 -0
- package/dist/dedup/embedder/errors.js +20 -0
- package/dist/dedup/embedder/errors.test.js +35 -0
- package/dist/dedup/embedder/index.js +4 -0
- package/dist/dedup/embedder/session.js +78 -0
- package/dist/dedup/embedder/session.test.js +172 -0
- package/dist/dedup/gitignore.js +97 -0
- package/dist/dedup/gitignore.test.js +98 -0
- package/dist/dedup/index.js +11 -0
- package/dist/dedup/indexdb/errors.js +48 -0
- package/dist/dedup/indexdb/index.js +6 -0
- package/dist/dedup/indexdb/indexdb.js +302 -0
- package/dist/dedup/indexdb/indexdb.test.js +739 -0
- package/dist/dedup/indexdb/load.js +110 -0
- package/dist/dedup/indexdb/migrations.js +58 -0
- package/dist/dedup/indexdb/schema.js +83 -0
- package/dist/dedup/indexer/index.js +9 -0
- package/dist/dedup/indexer/indexer.js +501 -0
- package/dist/dedup/indexer/indexer.test.js +510 -0
- package/dist/dedup/indexer/links.js +89 -0
- package/dist/dedup/mdsection/anchor.js +60 -0
- package/dist/dedup/mdsection/anchor.test.js +39 -0
- package/dist/dedup/mdsection/blocks.js +409 -0
- package/dist/dedup/mdsection/blocks.test.js +359 -0
- package/dist/dedup/mdsection/index.js +4 -0
- package/dist/dedup/mdsection/parse.js +21 -0
- package/dist/dedup/mdsection/section.js +234 -0
- package/dist/dedup/mdsection/section.test.js +221 -0
- package/dist/dedup/report/floatfmt.js +71 -0
- package/dist/dedup/report/floatfmt.test.js +42 -0
- package/dist/dedup/report/index.js +8 -0
- package/dist/dedup/report/quote.js +77 -0
- package/dist/dedup/report/quote.test.js +67 -0
- package/dist/dedup/report/text.js +251 -0
- package/dist/dedup/report/text.test.js +420 -0
- package/dist/dedup/report_types.js +8 -0
- package/dist/dedup/sectionid/index.js +1 -0
- package/dist/dedup/sectionid/sectionid.js +16 -0
- package/dist/dedup/sectionid/sectionid.test.js +49 -0
- package/dist/guard/api/errors.js +12 -0
- package/dist/guard/api/index.js +2 -0
- package/dist/guard/api/parser.js +81 -0
- package/dist/guard/api/parser.test.js +58 -0
- package/dist/guard/api/types.js +1 -0
- package/dist/guard/code/errors.js +16 -0
- package/dist/guard/code/index.js +2 -0
- package/dist/guard/code/parser.js +54 -0
- package/dist/guard/code/parser.test.js +111 -0
- package/dist/guard/code/types.js +6 -0
- package/dist/index.js +1 -0
- package/dist/index.test.js +5 -0
- package/dist/repo/boundary.js +92 -0
- package/dist/repo/boundary.test.js +65 -0
- package/dist/repo/errors.js +56 -0
- package/dist/repo/errors.test.js +85 -0
- package/dist/repo/exists.test.js +72 -0
- package/dist/repo/filename.js +46 -0
- package/dist/repo/filename.test.js +39 -0
- package/dist/repo/fs.js +53 -0
- package/dist/repo/index.js +7 -0
- package/dist/repo/overlay.js +36 -0
- package/dist/repo/overlay.test.js +80 -0
- package/dist/repo/repo.js +353 -0
- package/dist/repo/repo.test.js +255 -0
- package/dist/repo/testutil.js +27 -0
- package/dist/repo/write.test.js +125 -0
- package/dist/report/color.js +73 -0
- package/dist/report/index.js +1 -0
- package/dist/report/report.js +112 -0
- package/dist/report/report.test.js +368 -0
- package/dist/violation/index.js +1 -0
- package/dist/violation/types.js +22 -0
- package/dist/violation/types.test.js +70 -0
- package/package.json +48 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// java_queries.ts ports internal/codeq/java_queries.go: the S-expression query
|
|
2
|
+
// text the Java resolver runs against the tree-sitter-java grammar. The query
|
|
3
|
+
// strings are VERBATIM from the Go package — the upstream wasm grammar uses the
|
|
4
|
+
// same node-type and field names, so they compile and capture identically.
|
|
5
|
+
//
|
|
6
|
+
// Unlike Go's package-level `var … = mustJavaQuery(…)` (compiled at init against
|
|
7
|
+
// java.GetLanguage()), the Language here is only available once loaded (async),
|
|
8
|
+
// so these are exported as raw source strings and compiled eagerly in
|
|
9
|
+
// createJavaResolver via compileQuery (which throws on a bad query — the
|
|
10
|
+
// analogue of Go's mustJavaQuery panic).
|
|
11
|
+
/**
|
|
12
|
+
* SYMBOL matches top-level type declarations (class, interface, enum, record).
|
|
13
|
+
* Each match has a single capture "name" with the declared identifier.
|
|
14
|
+
*/
|
|
15
|
+
export const SYMBOL = `
|
|
16
|
+
(class_declaration name: (identifier) @name)
|
|
17
|
+
(interface_declaration name: (identifier) @name)
|
|
18
|
+
(enum_declaration name: (identifier) @name)
|
|
19
|
+
(record_declaration name: (identifier) @name)
|
|
20
|
+
`;
|
|
21
|
+
/**
|
|
22
|
+
* MEMBER matches fields, methods, and enum constants. Each match provides
|
|
23
|
+
* @owner (the containing class or enum name) and @name (the field, method, or
|
|
24
|
+
* enum-constant name).
|
|
25
|
+
*/
|
|
26
|
+
export const MEMBER = `
|
|
27
|
+
(class_declaration name: (identifier) @owner
|
|
28
|
+
body: (class_body (field_declaration (variable_declarator name: (identifier) @name))))
|
|
29
|
+
(class_declaration name: (identifier) @owner
|
|
30
|
+
body: (class_body (method_declaration name: (identifier) @name)))
|
|
31
|
+
(enum_declaration name: (identifier) @owner
|
|
32
|
+
body: (enum_body (enum_constant name: (identifier) @name)))
|
|
33
|
+
`;
|
|
34
|
+
/**
|
|
35
|
+
* MEMBER_DECL matches method declarations inside a class body. Each match
|
|
36
|
+
* provides @owner (class name), @mname (method name), and @mdecl (the
|
|
37
|
+
* method_declaration node itself — walked for formal_parameters to disambiguate
|
|
38
|
+
* overloads by signature).
|
|
39
|
+
*/
|
|
40
|
+
export const MEMBER_DECL = `
|
|
41
|
+
(class_declaration name: (identifier) @owner
|
|
42
|
+
body: (class_body (method_declaration name: (identifier) @mname) @mdecl))
|
|
43
|
+
`;
|
|
44
|
+
/**
|
|
45
|
+
* PARAM matches parameters of methods inside a class body. Each match provides
|
|
46
|
+
* @owner (class), @fn (method name), and @param (parameter name).
|
|
47
|
+
*/
|
|
48
|
+
export const PARAM = `
|
|
49
|
+
(class_declaration name: (identifier) @owner
|
|
50
|
+
body: (class_body (method_declaration name: (identifier) @fn
|
|
51
|
+
parameters: (formal_parameters (formal_parameter name: (identifier) @param)))))
|
|
52
|
+
`;
|
|
53
|
+
/**
|
|
54
|
+
* CONSTRUCTOR_DECL matches constructor declarations inside a class body. @owner
|
|
55
|
+
* is the class name; @cdecl is the constructor_declaration node (walked for its
|
|
56
|
+
* parameter types via collectParamTypes). A constructor's name is the class name
|
|
57
|
+
* by the Java grammar, so @owner == the referenced symbol is the only filter
|
|
58
|
+
* needed — there is no separate method-name capture.
|
|
59
|
+
*/
|
|
60
|
+
export const CONSTRUCTOR_DECL = `
|
|
61
|
+
(class_declaration name: (identifier) @owner
|
|
62
|
+
body: (class_body (constructor_declaration) @cdecl))
|
|
63
|
+
`;
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
// javascript.ts ports internal/codeq/javascript_lang.go: the per-language
|
|
2
|
+
// Resolver for JavaScript (.js / .jsx). It is a faithful port of jsResolver.Resolve
|
|
3
|
+
// MINUS the cache.Read + ErrFileNotFound prelude (the dispatch layer owns I/O —
|
|
4
|
+
// see resolver.ts). The grammar + compiled queries are bound at factory time
|
|
5
|
+
// (createJSResolver), so resolve() is SYNC: parse + matches only.
|
|
6
|
+
//
|
|
7
|
+
// JavaScript has no type annotations, so a Signature degrades to ARITY-ONLY:
|
|
8
|
+
// only the parameter COUNT is compared (len(sig) == param count), never type
|
|
9
|
+
// text. That is why this module does NOT import signature.ts — there is nothing
|
|
10
|
+
// to normalize.
|
|
11
|
+
import { ParseFailedError } from "../errors.js";
|
|
12
|
+
import { collectCapture, collectOwned, refKind, suggestFromExtractors, } from "../suggest.js";
|
|
13
|
+
import { Parser, compileQuery, nodeText, parseTree, runQuery, } from "../treesitter.js";
|
|
14
|
+
import { jsFuncDeclQuery, jsMemberDeclQuery, jsMemberQuery, jsSymbolQuery, } from "./javascript_queries.js";
|
|
15
|
+
/**
|
|
16
|
+
* createJSResolver builds the JavaScript Resolver from an already-loaded
|
|
17
|
+
* tree-sitter Language. It compiles + caches the four queries eagerly (the
|
|
18
|
+
* analogue of Go's package-level mustJSQuery vars); compileQuery THROWS on a bad
|
|
19
|
+
* query (the mustQuery-panic contract), surfacing a hard-coded-query bug at
|
|
20
|
+
* construction time.
|
|
21
|
+
*/
|
|
22
|
+
export function createJSResolver(language) {
|
|
23
|
+
return new JSResolver(language);
|
|
24
|
+
}
|
|
25
|
+
class JSResolver {
|
|
26
|
+
language;
|
|
27
|
+
symbolQuery;
|
|
28
|
+
memberQuery;
|
|
29
|
+
memberDeclQuery;
|
|
30
|
+
funcDeclQuery;
|
|
31
|
+
constructor(language) {
|
|
32
|
+
this.language = language;
|
|
33
|
+
// Compile once, reuse across trees (mirrors Go's package-level mustJSQuery
|
|
34
|
+
// vars). A bad literal throws here, not at resolve time.
|
|
35
|
+
this.symbolQuery = compileQuery(language, jsSymbolQuery);
|
|
36
|
+
this.memberQuery = compileQuery(language, jsMemberQuery);
|
|
37
|
+
this.memberDeclQuery = compileQuery(language, jsMemberDeclQuery);
|
|
38
|
+
this.funcDeclQuery = compileQuery(language, jsFuncDeclQuery);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* resolve parses `source` and delegates based on which facets of `ref` are set.
|
|
42
|
+
*
|
|
43
|
+
* Dispatch precedence (faithful to jsResolver.Resolve):
|
|
44
|
+
* - Member && Param → findMemberParam
|
|
45
|
+
* - Member → findMember
|
|
46
|
+
* - Signature && Param → findBareSignatureParam (constructor or top-level fn + param)
|
|
47
|
+
* - Signature → findBareSignature (constructor or top-level fn by arity)
|
|
48
|
+
* - Param → findParam (bare param lookup)
|
|
49
|
+
* - default → findSymbol (type-name lookup)
|
|
50
|
+
*
|
|
51
|
+
* THROWS ParseFailedError(path) when the parse tree has ERROR nodes. (The Go
|
|
52
|
+
* `ref.Signature != nil` checks become `ref.Signature !== undefined`: an empty
|
|
53
|
+
* but defined array — the zero-arg "()" overload — is still a signature.)
|
|
54
|
+
*/
|
|
55
|
+
resolve(source, path, ref) {
|
|
56
|
+
const parser = new Parser();
|
|
57
|
+
parser.setLanguage(this.language);
|
|
58
|
+
const root = parseTree(parser, source);
|
|
59
|
+
if (root.hasError) {
|
|
60
|
+
throw new ParseFailedError(path);
|
|
61
|
+
}
|
|
62
|
+
if (ref.Member !== "" && ref.Param !== "") {
|
|
63
|
+
return this.findMemberParam(root, ref.Symbol, ref.Member, ref.Param, ref.Signature);
|
|
64
|
+
}
|
|
65
|
+
if (ref.Member !== "") {
|
|
66
|
+
return this.findMember(root, ref.Symbol, ref.Member, ref.Signature);
|
|
67
|
+
}
|
|
68
|
+
if (ref.Signature !== undefined && ref.Param !== "") {
|
|
69
|
+
return this.findBareSignatureParam(root, ref.Symbol, ref.Param, ref.Signature);
|
|
70
|
+
}
|
|
71
|
+
if (ref.Signature !== undefined) {
|
|
72
|
+
return this.findBareSignature(root, ref.Symbol, ref.Signature);
|
|
73
|
+
}
|
|
74
|
+
if (ref.Param !== "") {
|
|
75
|
+
return this.findParam(root, ref.Symbol, ref.Param);
|
|
76
|
+
}
|
|
77
|
+
return this.findSymbol(root, ref.Symbol);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* suggest lists same-kind candidate names for a not-found ref (best-effort; a
|
|
81
|
+
* parse error yields no candidates). Params are gathered from BOTH sources
|
|
82
|
+
* findParam scans — methods named `fn` and top-level functions named `fn` —
|
|
83
|
+
* mirroring the oracle's reach.
|
|
84
|
+
*/
|
|
85
|
+
suggest(source, _path, ref) {
|
|
86
|
+
const parser = new Parser();
|
|
87
|
+
parser.setLanguage(this.language);
|
|
88
|
+
const root = parseTree(parser, source);
|
|
89
|
+
if (root.hasError) {
|
|
90
|
+
return { kind: refKind(ref), candidates: [] };
|
|
91
|
+
}
|
|
92
|
+
return suggestFromExtractors(root, ref, {
|
|
93
|
+
symbolNames: (r) => collectCapture(r, this.symbolQuery, "name"),
|
|
94
|
+
memberNames: (r, owner) => collectOwned(r, this.memberQuery, owner, "owner", "name"),
|
|
95
|
+
paramNames: (r, fn) => this.collectParamNames(r, fn),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* collectParamNames lists the parameter names of every method or top-level
|
|
100
|
+
* function named `fn`, the union findParam searches. Destructured params are
|
|
101
|
+
* not name-resolvable (jsParamNames skips them), matching jsParamExists.
|
|
102
|
+
*/
|
|
103
|
+
collectParamNames(root, fn) {
|
|
104
|
+
const out = [];
|
|
105
|
+
for (const m of runQuery(root, this.memberDeclQuery)) {
|
|
106
|
+
const mname = textOf(m.captures["mname"]);
|
|
107
|
+
const mdecl = m.captures["mdecl"];
|
|
108
|
+
if (mname === fn && mdecl !== undefined) {
|
|
109
|
+
out.push(...jsParamNames(mdecl));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
for (const m of runQuery(root, this.funcDeclQuery)) {
|
|
113
|
+
const fname = textOf(m.captures["fname"]);
|
|
114
|
+
const fdecl = m.captures["fdecl"];
|
|
115
|
+
if (fname === fn && fdecl !== undefined) {
|
|
116
|
+
out.push(...jsParamNames(fdecl));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* findSymbol searches for a top-level declaration with the given name. Any
|
|
123
|
+
* @name capture across the symbol query's patterns that equals `name` is a hit.
|
|
124
|
+
*/
|
|
125
|
+
findSymbol(root, name) {
|
|
126
|
+
for (const m of runQuery(root, this.symbolQuery)) {
|
|
127
|
+
// Each match has one or more @name captures; the Go code scanned every
|
|
128
|
+
// capture in the match, so check all[name] (one per match here).
|
|
129
|
+
for (const n of m.all["name"] ?? []) {
|
|
130
|
+
if (nodeText(n) === name) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* findMember searches for a field or method named `member` on the class named
|
|
139
|
+
* `symbol`. When `sig` is undefined, name-only matching uses the flat member
|
|
140
|
+
* query (fields + methods). When `sig` is defined, only method overloads whose
|
|
141
|
+
* arity matches sig.length are considered (fields have no params, so an
|
|
142
|
+
* arity-qualified member ref never resolves a field — matching Go).
|
|
143
|
+
*/
|
|
144
|
+
findMember(root, symbol, member, sig) {
|
|
145
|
+
if (sig === undefined) {
|
|
146
|
+
// Name-only: flat member query matches fields and methods.
|
|
147
|
+
for (const m of runQuery(root, this.memberQuery)) {
|
|
148
|
+
const owner = textOf(m.captures["owner"]);
|
|
149
|
+
const name = textOf(m.captures["name"]);
|
|
150
|
+
if (owner === symbol && name === member) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
// Arity-filtered: iterate method declarations and compare param count.
|
|
157
|
+
for (const m of runQuery(root, this.memberDeclQuery)) {
|
|
158
|
+
const owner = textOf(m.captures["owner"]);
|
|
159
|
+
const mname = textOf(m.captures["mname"]);
|
|
160
|
+
const mdecl = m.captures["mdecl"];
|
|
161
|
+
if (owner === symbol && mname === member && mdecl !== undefined) {
|
|
162
|
+
if (sig.length === jsParamCount(mdecl)) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* findMemberParam searches for a parameter named `paramName` on a method named
|
|
171
|
+
* `member` of the class named `symbol`. When `sig` is defined, only the method
|
|
172
|
+
* whose arity matches sig.length is considered (arity-only for JS).
|
|
173
|
+
*/
|
|
174
|
+
findMemberParam(root, symbol, member, paramName, sig) {
|
|
175
|
+
for (const m of runQuery(root, this.memberDeclQuery)) {
|
|
176
|
+
const owner = textOf(m.captures["owner"]);
|
|
177
|
+
const mname = textOf(m.captures["mname"]);
|
|
178
|
+
const mdecl = m.captures["mdecl"];
|
|
179
|
+
if (owner !== symbol || mname !== member || mdecl === undefined) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (sig !== undefined && sig.length !== jsParamCount(mdecl)) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (jsParamExists(mdecl, paramName)) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* findParam searches for a parameter named `paramName` on any method or
|
|
193
|
+
* top-level function named `symbol`.
|
|
194
|
+
*/
|
|
195
|
+
findParam(root, symbol, paramName) {
|
|
196
|
+
// (a) Method decls: any method on a class with mname == symbol.
|
|
197
|
+
for (const m of runQuery(root, this.memberDeclQuery)) {
|
|
198
|
+
const mname = textOf(m.captures["mname"]);
|
|
199
|
+
const mdecl = m.captures["mdecl"];
|
|
200
|
+
if (mname === symbol && mdecl !== undefined) {
|
|
201
|
+
if (jsParamExists(mdecl, paramName)) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// (b) Top-level function decls: fname == symbol.
|
|
207
|
+
for (const m of runQuery(root, this.funcDeclQuery)) {
|
|
208
|
+
const fname = textOf(m.captures["fname"]);
|
|
209
|
+
const fdecl = m.captures["fdecl"];
|
|
210
|
+
if (fname === symbol && fdecl !== undefined) {
|
|
211
|
+
if (jsParamExists(fdecl, paramName)) {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* findBareSignature resolves a bare-symbol + signature ref (no member). It
|
|
220
|
+
* matches either a constructor (method_definition named "constructor" on the
|
|
221
|
+
* class named `symbol`, by arity) OR a top-level function (by arity). Returns
|
|
222
|
+
* true on the first arity match.
|
|
223
|
+
*/
|
|
224
|
+
findBareSignature(root, symbol, sig) {
|
|
225
|
+
// (a) Constructor: method decls on `symbol`, name "constructor".
|
|
226
|
+
for (const m of runQuery(root, this.memberDeclQuery)) {
|
|
227
|
+
const owner = textOf(m.captures["owner"]);
|
|
228
|
+
const mname = textOf(m.captures["mname"]);
|
|
229
|
+
const mdecl = m.captures["mdecl"];
|
|
230
|
+
if (owner === symbol && mname === "constructor" && mdecl !== undefined) {
|
|
231
|
+
if (sig.length === jsParamCount(mdecl)) {
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// (b) Top-level function named `symbol`.
|
|
237
|
+
for (const m of runQuery(root, this.funcDeclQuery)) {
|
|
238
|
+
const fname = textOf(m.captures["fname"]);
|
|
239
|
+
const fdecl = m.captures["fdecl"];
|
|
240
|
+
if (fname === symbol && fdecl !== undefined) {
|
|
241
|
+
if (sig.length === jsParamCount(fdecl)) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* findBareSignatureParam resolves a bare-symbol + signature + param ref. Same
|
|
250
|
+
* two sources as findBareSignature (constructor or top-level function), arity
|
|
251
|
+
* must match, then reports whether `paramName` exists in the matching decl.
|
|
252
|
+
*/
|
|
253
|
+
findBareSignatureParam(root, symbol, paramName, sig) {
|
|
254
|
+
// (a) Constructor.
|
|
255
|
+
for (const m of runQuery(root, this.memberDeclQuery)) {
|
|
256
|
+
const owner = textOf(m.captures["owner"]);
|
|
257
|
+
const mname = textOf(m.captures["mname"]);
|
|
258
|
+
const mdecl = m.captures["mdecl"];
|
|
259
|
+
if (owner === symbol && mname === "constructor" && mdecl !== undefined) {
|
|
260
|
+
if (sig.length === jsParamCount(mdecl)) {
|
|
261
|
+
return jsParamExists(mdecl, paramName);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// (b) Top-level function.
|
|
266
|
+
for (const m of runQuery(root, this.funcDeclQuery)) {
|
|
267
|
+
const fname = textOf(m.captures["fname"]);
|
|
268
|
+
const fdecl = m.captures["fdecl"];
|
|
269
|
+
if (fname === symbol && fdecl !== undefined) {
|
|
270
|
+
if (sig.length === jsParamCount(fdecl)) {
|
|
271
|
+
return jsParamExists(fdecl, paramName);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/** textOf returns a node's text, or "" when the capture is absent (Go's zero-value string). */
|
|
279
|
+
function textOf(n) {
|
|
280
|
+
return n === undefined ? "" : nodeText(n);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* formalParameters returns the `formal_parameters` child of `decl`, or null.
|
|
284
|
+
* Both jsParamCount and jsParamExists locate the param list this way (Go scanned
|
|
285
|
+
* decl's children for the first formal_parameters node).
|
|
286
|
+
*/
|
|
287
|
+
function formalParameters(decl) {
|
|
288
|
+
for (let i = 0; i < decl.childCount; i++) {
|
|
289
|
+
const c = decl.child(i);
|
|
290
|
+
if (c !== null && c.type === "formal_parameters") {
|
|
291
|
+
return c;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* jsParamCount returns the number of parameters in the formal_parameters child
|
|
298
|
+
* of `decl`. Counts identifier, rest_pattern, assignment_pattern, object_pattern,
|
|
299
|
+
* and array_pattern nodes — all param shapes in JS.
|
|
300
|
+
*/
|
|
301
|
+
function jsParamCount(decl) {
|
|
302
|
+
const fp = formalParameters(decl);
|
|
303
|
+
if (fp === null) {
|
|
304
|
+
return 0;
|
|
305
|
+
}
|
|
306
|
+
let count = 0;
|
|
307
|
+
for (let i = 0; i < fp.childCount; i++) {
|
|
308
|
+
const c = fp.child(i);
|
|
309
|
+
if (c === null) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
switch (c.type) {
|
|
313
|
+
case "identifier":
|
|
314
|
+
case "rest_pattern":
|
|
315
|
+
case "assignment_pattern":
|
|
316
|
+
case "object_pattern":
|
|
317
|
+
case "array_pattern":
|
|
318
|
+
count++;
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return count;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* jsParamNames lists the resolvable parameter names in the formal_parameters of
|
|
326
|
+
* `decl`, in document order. It mirrors jsParamExists's resolvable set exactly —
|
|
327
|
+
* plain identifiers, rest_pattern, and assignment_pattern left-hand identifiers;
|
|
328
|
+
* destructured params (object/array_pattern) are skipped — so a name suggested
|
|
329
|
+
* here is always one jsParamExists would have matched.
|
|
330
|
+
*/
|
|
331
|
+
function jsParamNames(decl) {
|
|
332
|
+
const fp = formalParameters(decl);
|
|
333
|
+
if (fp === null) {
|
|
334
|
+
return [];
|
|
335
|
+
}
|
|
336
|
+
const names = [];
|
|
337
|
+
for (let i = 0; i < fp.childCount; i++) {
|
|
338
|
+
const c = fp.child(i);
|
|
339
|
+
if (c === null) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
switch (c.type) {
|
|
343
|
+
case "identifier":
|
|
344
|
+
names.push(nodeText(c));
|
|
345
|
+
break;
|
|
346
|
+
case "rest_pattern":
|
|
347
|
+
for (let j = 0; j < c.childCount; j++) {
|
|
348
|
+
const inner = c.child(j);
|
|
349
|
+
if (inner !== null && inner.type === "identifier") {
|
|
350
|
+
names.push(nodeText(inner));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
break;
|
|
354
|
+
case "assignment_pattern": {
|
|
355
|
+
const left = c.childForFieldName("left");
|
|
356
|
+
if (left !== null && left.type === "identifier") {
|
|
357
|
+
names.push(nodeText(left));
|
|
358
|
+
}
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return names;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* jsParamExists reports whether a parameter named `name` exists in the
|
|
367
|
+
* formal_parameters of `decl`. Matches plain identifiers, rest_pattern, and
|
|
368
|
+
* assignment_pattern (left-hand identifier). Destructured params
|
|
369
|
+
* (object_pattern/array_pattern) count toward arity in jsParamCount but are NOT
|
|
370
|
+
* name-resolvable here (existence-only non-goal per the plan).
|
|
371
|
+
*/
|
|
372
|
+
function jsParamExists(decl, name) {
|
|
373
|
+
const fp = formalParameters(decl);
|
|
374
|
+
if (fp === null) {
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
for (let i = 0; i < fp.childCount; i++) {
|
|
378
|
+
const c = fp.child(i);
|
|
379
|
+
if (c === null) {
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
switch (c.type) {
|
|
383
|
+
case "identifier":
|
|
384
|
+
if (nodeText(c) === name) {
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
break;
|
|
388
|
+
case "rest_pattern":
|
|
389
|
+
// rest_pattern wraps an identifier: ...args → identifier child.
|
|
390
|
+
for (let j = 0; j < c.childCount; j++) {
|
|
391
|
+
const inner = c.child(j);
|
|
392
|
+
if (inner !== null &&
|
|
393
|
+
inner.type === "identifier" &&
|
|
394
|
+
nodeText(inner) === name) {
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
break;
|
|
399
|
+
case "assignment_pattern": {
|
|
400
|
+
// assignment_pattern: left = default → left is the identifier.
|
|
401
|
+
const left = c.childForFieldName("left");
|
|
402
|
+
if (left !== null &&
|
|
403
|
+
left.type === "identifier" &&
|
|
404
|
+
nodeText(left) === name) {
|
|
405
|
+
return true;
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, it } from "vitest";
|
|
2
|
+
import { ParseFailedError } from "../errors.js";
|
|
3
|
+
import { loadGrammar } from "../treesitter.js";
|
|
4
|
+
import { createJSResolver } from "./javascript.js";
|
|
5
|
+
// WHY: the JS resolver is the existence oracle for .js/.jsx refs. JavaScript has
|
|
6
|
+
// no type annotations, so signature matching is ARITY-ONLY (param count) — a sig
|
|
7
|
+
// with the wrong count must NOT match, the right count must. Name resolution for
|
|
8
|
+
// symbol/member/param must work; the param-shape branches (rest_pattern,
|
|
9
|
+
// assignment_pattern) must be name-resolvable while still counting toward arity.
|
|
10
|
+
// These cases are the behavior-encoding cases ported from
|
|
11
|
+
// internal/codeq/javascript_lang_test.go.
|
|
12
|
+
// Same source sample as the Go test (jsSrc). It exercises: top-level function +
|
|
13
|
+
// const + class; a class field; methods with 2 params (get), a constructor with
|
|
14
|
+
// 2 params, a rest param, and a default (assignment) param.
|
|
15
|
+
const jsSrc = `export function top(a, b) { return a + b; }
|
|
16
|
+
export const VERSION = "1";
|
|
17
|
+
export class C {
|
|
18
|
+
field = 0;
|
|
19
|
+
get(names, id) { return ""; }
|
|
20
|
+
constructor(names, req) {}
|
|
21
|
+
rest(first, ...others) { return first; }
|
|
22
|
+
def(a, b = 2) { return a + b; }
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
// path is irrelevant to a sync resolver except as the ParseFailedError payload;
|
|
26
|
+
// the Go test ran every case under both "a.js" and "a.jsx" because the dispatch
|
|
27
|
+
// layer routes both extensions to THIS resolver. The resolver itself is
|
|
28
|
+
// extension-blind, so one path suffices here — extension routing is the dispatch
|
|
29
|
+
// layer's concern (resolver.test.ts), not this oracle's.
|
|
30
|
+
const PATH = "a.js";
|
|
31
|
+
let resolver;
|
|
32
|
+
beforeAll(async () => {
|
|
33
|
+
// Fast: the wasm grammar loads in ms; no env-gating needed.
|
|
34
|
+
const lang = await loadGrammar("javascript");
|
|
35
|
+
resolver = createJSResolver(lang);
|
|
36
|
+
});
|
|
37
|
+
/** ref builds a CodeRef with the empty-string/undefined defaults the parser uses. */
|
|
38
|
+
function ref(partial) {
|
|
39
|
+
return { Path: PATH, Symbol: "", Member: "", Param: "", ...partial };
|
|
40
|
+
}
|
|
41
|
+
describe("JS resolver", () => {
|
|
42
|
+
// --- top-level symbols (findSymbol) ---
|
|
43
|
+
it("resolves a class declaration by name", () => {
|
|
44
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C" }))).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
it("resolves a top-level function by name", () => {
|
|
47
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "top" }))).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
it("resolves a top-level const by name", () => {
|
|
50
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "VERSION" }))).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
// --- members (findMember): a missing member must be reported absent so a stale
|
|
53
|
+
// doc ref fails the guard rather than silently passing. ---
|
|
54
|
+
it("resolves a method member", () => {
|
|
55
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Member: "get" }))).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
it("resolves a field member (field_definition, not method)", () => {
|
|
58
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Member: "field" }))).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it("reports an absent member as not found", () => {
|
|
61
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Member: "nope" }))).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
// --- member params (findMemberParam) ---
|
|
64
|
+
it("resolves a param of a method", () => {
|
|
65
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Member: "get", Param: "id" }))).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
it("reports an absent method param as not found", () => {
|
|
68
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Member: "get", Param: "zzz" }))).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
// --- arity-only signature on a method: WHY this matters — JS has no types, so
|
|
71
|
+
// the ONLY discriminator a {{code:…(a,b)}} ref carries is param count. A wrong
|
|
72
|
+
// count must miss, a right count must hit. ---
|
|
73
|
+
it("matches a method by arity (param count)", () => {
|
|
74
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Member: "get", Signature: ["x", "y"] }))).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
it("rejects a method on arity mismatch", () => {
|
|
77
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Member: "get", Signature: ["x"] }))).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
// --- bare signature = constructor by arity (findBareSignature) ---
|
|
80
|
+
it("matches the constructor by arity for a bare signature", () => {
|
|
81
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Signature: ["x", "y"] }))).toBe(true);
|
|
82
|
+
});
|
|
83
|
+
it("rejects the constructor on arity mismatch", () => {
|
|
84
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Signature: ["x"] }))).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
// --- bare top-level-function param (findParam path) ---
|
|
87
|
+
it("resolves a bare top-level-function param", () => {
|
|
88
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "top", Param: "a" }))).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
it("reports an absent bare-function param as not found", () => {
|
|
91
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "top", Param: "zzz" }))).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
// --- constructor param by arity-signature (findBareSignatureParam path) ---
|
|
94
|
+
it("resolves a constructor param under a matching arity signature", () => {
|
|
95
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Signature: ["x", "y"], Param: "req" }))).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
it("reports an absent constructor param (matching arity) as not found", () => {
|
|
98
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Signature: ["x", "y"], Param: "zzz" }))).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
// --- top-level-function param by arity-signature (findBareSignatureParam) ---
|
|
101
|
+
it("resolves a top-level-function param under a matching arity signature", () => {
|
|
102
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "top", Signature: ["x", "y"], Param: "a" }))).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
it("rejects a top-level-function param under a wrong arity signature", () => {
|
|
105
|
+
// WHY: arity gates BEFORE param lookup — the wrong-arity overload is not the
|
|
106
|
+
// one being referenced, so even a real param name must not resolve.
|
|
107
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "top", Signature: ["x", "y", "z"], Param: "a" }))).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
// --- param-shape branches in jsParamExists: rest and default (assignment) ---
|
|
110
|
+
it("resolves a rest param (...others) by name", () => {
|
|
111
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Member: "rest", Param: "others" }))).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
it("resolves a default param (b = 2) by its identifier", () => {
|
|
114
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Member: "def", Param: "b" }))).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
// --- zero-arg signature "()" is a DEFINED empty array, not undefined: it must
|
|
117
|
+
// still take the signature dispatch path (arity 0), not the name-only path. ---
|
|
118
|
+
it("treats a zero-arg signature () as arity 0, missing a 2-arg constructor", () => {
|
|
119
|
+
expect(resolver.resolve(jsSrc, PATH, ref({ Symbol: "C", Signature: [] }))).toBe(false);
|
|
120
|
+
});
|
|
121
|
+
// --- parse failure → ParseFailedError carrying the path ---
|
|
122
|
+
it("throws ParseFailedError on invalid JS (ERROR nodes)", () => {
|
|
123
|
+
expect(() => resolver.resolve("class C { (((", "bad.js", ref({ Symbol: "C" }))).toThrow(ParseFailedError);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// javascript_queries.ts ports internal/codeq/javascript_queries.go: the four
|
|
2
|
+
// tree-sitter S-expression queries the JS resolver runs. The S-expression text
|
|
3
|
+
// is VERBATIM from the Go package — the upstream tree-sitter-javascript node-type
|
|
4
|
+
// names match the vendored wasm grammar, so the queries port unchanged.
|
|
5
|
+
//
|
|
6
|
+
// Go compiled these at package init via mustJSQuery (panic on bad query). Here
|
|
7
|
+
// the factory (createJSResolver) compiles them once per loaded Language via
|
|
8
|
+
// compileQuery (which THROWS on a bad query — the mustQuery-panic contract) and
|
|
9
|
+
// caches the resulting Query objects on the resolver instance.
|
|
10
|
+
/**
|
|
11
|
+
* jsSymbolQuery matches top-level declarations. JS class names are `identifier`
|
|
12
|
+
* (not `type_identifier` — that is TS-only). Capture: @name.
|
|
13
|
+
*/
|
|
14
|
+
export const jsSymbolQuery = `
|
|
15
|
+
(class_declaration name: (identifier) @name)
|
|
16
|
+
(function_declaration name: (identifier) @name)
|
|
17
|
+
(generator_function_declaration name: (identifier) @name)
|
|
18
|
+
(lexical_declaration (variable_declarator name: (identifier) @name))
|
|
19
|
+
(variable_declaration (variable_declarator name: (identifier) @name))
|
|
20
|
+
`;
|
|
21
|
+
/**
|
|
22
|
+
* jsMemberQuery matches fields and methods on classes. JS class fields use the
|
|
23
|
+
* `property` field; methods use the `name` field. Captures: @owner, @name.
|
|
24
|
+
*/
|
|
25
|
+
export const jsMemberQuery = `
|
|
26
|
+
(class_declaration name: (identifier) @owner body: (class_body [
|
|
27
|
+
(method_definition name: (property_identifier) @name)
|
|
28
|
+
(field_definition property: (property_identifier) @name)
|
|
29
|
+
]))
|
|
30
|
+
`;
|
|
31
|
+
/**
|
|
32
|
+
* jsMemberDeclQuery captures method declaration nodes (@mdecl) alongside @owner
|
|
33
|
+
* and @mname, for param lookup and arity-based signature matching.
|
|
34
|
+
*/
|
|
35
|
+
export const jsMemberDeclQuery = `
|
|
36
|
+
(class_declaration name: (identifier) @owner body: (class_body
|
|
37
|
+
(method_definition name: (property_identifier) @mname) @mdecl))
|
|
38
|
+
`;
|
|
39
|
+
/**
|
|
40
|
+
* jsFuncDeclQuery captures top-level function declarations (@fname, @fdecl) for
|
|
41
|
+
* top-level-function param lookup and constructor/function arity matching.
|
|
42
|
+
*/
|
|
43
|
+
export const jsFuncDeclQuery = `
|
|
44
|
+
(function_declaration name: (identifier) @fname) @fdecl
|
|
45
|
+
(generator_function_declaration name: (identifier) @fname) @fdecl
|
|
46
|
+
`;
|