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.
Files changed (159) hide show
  1. package/README.md +242 -0
  2. package/dist/apispec/apispec.js +401 -0
  3. package/dist/apispec/apispec.test.js +444 -0
  4. package/dist/apispec/errors.js +17 -0
  5. package/dist/apispec/index.js +2 -0
  6. package/dist/check/doclinks.js +167 -0
  7. package/dist/check/index.js +8 -0
  8. package/dist/check/run.js +391 -0
  9. package/dist/check/run.test.js +513 -0
  10. package/dist/check/suggest.js +134 -0
  11. package/dist/check/suggest.test.js +92 -0
  12. package/dist/check/tokens.js +125 -0
  13. package/dist/cmd/main.js +330 -0
  14. package/dist/cmd/main.test.js +422 -0
  15. package/dist/codeq/cache.js +71 -0
  16. package/dist/codeq/cache.test.js +67 -0
  17. package/dist/codeq/errors.js +52 -0
  18. package/dist/codeq/grammars/tree-sitter-go.wasm +0 -0
  19. package/dist/codeq/grammars/tree-sitter-java.wasm +0 -0
  20. package/dist/codeq/grammars/tree-sitter-javascript.wasm +0 -0
  21. package/dist/codeq/grammars/tree-sitter-tsx.wasm +0 -0
  22. package/dist/codeq/grammars/tree-sitter-typescript.wasm +0 -0
  23. package/dist/codeq/index.js +11 -0
  24. package/dist/codeq/resolve.test.js +109 -0
  25. package/dist/codeq/resolver.js +128 -0
  26. package/dist/codeq/resolver.test.js +124 -0
  27. package/dist/codeq/resolvers/go.js +242 -0
  28. package/dist/codeq/resolvers/go.test.js +143 -0
  29. package/dist/codeq/resolvers/java.js +349 -0
  30. package/dist/codeq/resolvers/java.test.js +138 -0
  31. package/dist/codeq/resolvers/java_queries.js +63 -0
  32. package/dist/codeq/resolvers/javascript.js +412 -0
  33. package/dist/codeq/resolvers/javascript.test.js +125 -0
  34. package/dist/codeq/resolvers/javascript_queries.js +46 -0
  35. package/dist/codeq/resolvers/typescript.js +366 -0
  36. package/dist/codeq/resolvers/typescript.test.js +180 -0
  37. package/dist/codeq/resolvers/typescript_queries.js +78 -0
  38. package/dist/codeq/signature.js +50 -0
  39. package/dist/codeq/signature.test.js +50 -0
  40. package/dist/codeq/suggest.js +96 -0
  41. package/dist/codeq/treesitter.js +122 -0
  42. package/dist/codeq/treesitter.test.js +118 -0
  43. package/dist/config/config.js +74 -0
  44. package/dist/config/config.test.js +98 -0
  45. package/dist/config/fs.js +116 -0
  46. package/dist/config/glob.js +82 -0
  47. package/dist/config/glob.test.js +61 -0
  48. package/dist/config/index.js +4 -0
  49. package/dist/dedup/analyzer/analyzer.js +533 -0
  50. package/dist/dedup/analyzer/analyzer.test.js +530 -0
  51. package/dist/dedup/analyzer/canonical.js +74 -0
  52. package/dist/dedup/analyzer/canonical.test.js +70 -0
  53. package/dist/dedup/analyzer/cosine_clusters.js +169 -0
  54. package/dist/dedup/analyzer/cosine_clusters.test.js +131 -0
  55. package/dist/dedup/analyzer/distinctive.js +85 -0
  56. package/dist/dedup/analyzer/distinctive.test.js +49 -0
  57. package/dist/dedup/analyzer/exact_clusters.js +63 -0
  58. package/dist/dedup/analyzer/exact_clusters.test.js +81 -0
  59. package/dist/dedup/analyzer/index.js +14 -0
  60. package/dist/dedup/analyzer/multiplicity.js +110 -0
  61. package/dist/dedup/analyzer/multiplicity.test.js +123 -0
  62. package/dist/dedup/analyzer/order.js +22 -0
  63. package/dist/dedup/analyzer/partial_overlaps.js +65 -0
  64. package/dist/dedup/analyzer/partial_overlaps.test.js +161 -0
  65. package/dist/dedup/analyzer/preview.js +84 -0
  66. package/dist/dedup/analyzer/preview.test.js +46 -0
  67. package/dist/dedup/analyzer/safety.js +27 -0
  68. package/dist/dedup/analyzer/safety.test.js +39 -0
  69. package/dist/dedup/config.js +18 -0
  70. package/dist/dedup/configload.js +299 -0
  71. package/dist/dedup/configload.test.js +410 -0
  72. package/dist/dedup/dedup.index.test.js +203 -0
  73. package/dist/dedup/dedup.js +143 -0
  74. package/dist/dedup/dedup.test.js +212 -0
  75. package/dist/dedup/dedupcfg/config.js +112 -0
  76. package/dist/dedup/dedupcfg/config.test.js +70 -0
  77. package/dist/dedup/dedupcfg/index.js +1 -0
  78. package/dist/dedup/deduptypes/index.js +1 -0
  79. package/dist/dedup/deduptypes/types.js +9 -0
  80. package/dist/dedup/deduptypes/types.test.js +34 -0
  81. package/dist/dedup/embedder/cache.js +23 -0
  82. package/dist/dedup/embedder/cache.test.js +50 -0
  83. package/dist/dedup/embedder/constants.js +10 -0
  84. package/dist/dedup/embedder/embedder.js +76 -0
  85. package/dist/dedup/embedder/embedder.mock.test.js +128 -0
  86. package/dist/dedup/embedder/embedder.test.js +96 -0
  87. package/dist/dedup/embedder/errors.js +20 -0
  88. package/dist/dedup/embedder/errors.test.js +35 -0
  89. package/dist/dedup/embedder/index.js +4 -0
  90. package/dist/dedup/embedder/session.js +78 -0
  91. package/dist/dedup/embedder/session.test.js +172 -0
  92. package/dist/dedup/gitignore.js +97 -0
  93. package/dist/dedup/gitignore.test.js +98 -0
  94. package/dist/dedup/index.js +11 -0
  95. package/dist/dedup/indexdb/errors.js +48 -0
  96. package/dist/dedup/indexdb/index.js +6 -0
  97. package/dist/dedup/indexdb/indexdb.js +302 -0
  98. package/dist/dedup/indexdb/indexdb.test.js +739 -0
  99. package/dist/dedup/indexdb/load.js +110 -0
  100. package/dist/dedup/indexdb/migrations.js +58 -0
  101. package/dist/dedup/indexdb/schema.js +83 -0
  102. package/dist/dedup/indexer/index.js +9 -0
  103. package/dist/dedup/indexer/indexer.js +501 -0
  104. package/dist/dedup/indexer/indexer.test.js +510 -0
  105. package/dist/dedup/indexer/links.js +89 -0
  106. package/dist/dedup/mdsection/anchor.js +60 -0
  107. package/dist/dedup/mdsection/anchor.test.js +39 -0
  108. package/dist/dedup/mdsection/blocks.js +409 -0
  109. package/dist/dedup/mdsection/blocks.test.js +359 -0
  110. package/dist/dedup/mdsection/index.js +4 -0
  111. package/dist/dedup/mdsection/parse.js +21 -0
  112. package/dist/dedup/mdsection/section.js +234 -0
  113. package/dist/dedup/mdsection/section.test.js +221 -0
  114. package/dist/dedup/report/floatfmt.js +71 -0
  115. package/dist/dedup/report/floatfmt.test.js +42 -0
  116. package/dist/dedup/report/index.js +8 -0
  117. package/dist/dedup/report/quote.js +77 -0
  118. package/dist/dedup/report/quote.test.js +67 -0
  119. package/dist/dedup/report/text.js +251 -0
  120. package/dist/dedup/report/text.test.js +420 -0
  121. package/dist/dedup/report_types.js +8 -0
  122. package/dist/dedup/sectionid/index.js +1 -0
  123. package/dist/dedup/sectionid/sectionid.js +16 -0
  124. package/dist/dedup/sectionid/sectionid.test.js +49 -0
  125. package/dist/guard/api/errors.js +12 -0
  126. package/dist/guard/api/index.js +2 -0
  127. package/dist/guard/api/parser.js +81 -0
  128. package/dist/guard/api/parser.test.js +58 -0
  129. package/dist/guard/api/types.js +1 -0
  130. package/dist/guard/code/errors.js +16 -0
  131. package/dist/guard/code/index.js +2 -0
  132. package/dist/guard/code/parser.js +54 -0
  133. package/dist/guard/code/parser.test.js +111 -0
  134. package/dist/guard/code/types.js +6 -0
  135. package/dist/index.js +1 -0
  136. package/dist/index.test.js +5 -0
  137. package/dist/repo/boundary.js +92 -0
  138. package/dist/repo/boundary.test.js +65 -0
  139. package/dist/repo/errors.js +56 -0
  140. package/dist/repo/errors.test.js +85 -0
  141. package/dist/repo/exists.test.js +72 -0
  142. package/dist/repo/filename.js +46 -0
  143. package/dist/repo/filename.test.js +39 -0
  144. package/dist/repo/fs.js +53 -0
  145. package/dist/repo/index.js +7 -0
  146. package/dist/repo/overlay.js +36 -0
  147. package/dist/repo/overlay.test.js +80 -0
  148. package/dist/repo/repo.js +353 -0
  149. package/dist/repo/repo.test.js +255 -0
  150. package/dist/repo/testutil.js +27 -0
  151. package/dist/repo/write.test.js +125 -0
  152. package/dist/report/color.js +73 -0
  153. package/dist/report/index.js +1 -0
  154. package/dist/report/report.js +112 -0
  155. package/dist/report/report.test.js +368 -0
  156. package/dist/violation/index.js +1 -0
  157. package/dist/violation/types.js +22 -0
  158. package/dist/violation/types.test.js +70 -0
  159. 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
+ `;