ast-search 0.2.3 → 0.3.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 +98 -10
- package/build/backends/js/index.js +30 -0
- package/build/file.d.ts +22 -0
- package/build/file.js +18 -0
- package/build/language.d.ts +23 -0
- package/build/language.js +1 -0
- package/build/main.d.ts +3 -0
- package/build/main.js +65 -23
- package/build/output.d.ts +3 -0
- package/build/plugin.d.ts +3 -0
- package/build/plugin.js +1 -0
- package/build/registry.d.ts +11 -0
- package/build/registry.js +25 -0
- package/build/search.d.ts +8 -0
- package/build/types.d.ts +6 -0
- package/build/types.js +1 -0
- package/build/version.d.ts +1 -0
- package/build/version.js +2 -0
- package/build/walk.d.ts +1 -0
- package/build/walk.js +3 -12
- package/package.json +22 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A CLI tool for searching source files using AST patterns, designed to facilitate large-scale refactors.
|
|
4
4
|
|
|
5
|
-
Accepts
|
|
5
|
+
Accepts a query and searches all supported files under a directory, printing each match with its file path, line, and column. Language support is provided by plugins — the core handles JS/TS/Vue; additional languages are opt-in.
|
|
6
6
|
|
|
7
7
|
## Example
|
|
8
8
|
|
|
@@ -31,17 +31,28 @@ src/components/Foo.vue:5:13: return this.testValue
|
|
|
31
31
|
src/components/Bar.vue:9:18: return this.otherProp
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install -g ast-search
|
|
38
|
+
|
|
39
|
+
# Optional: add Python support
|
|
40
|
+
npm install -g ast-search-python
|
|
41
|
+
```
|
|
42
|
+
|
|
34
43
|
## Usage
|
|
35
44
|
|
|
36
45
|
```
|
|
37
|
-
ast-search <query> [--dir <path>] [--format <fmt>]
|
|
46
|
+
ast-search <query> [--dir <path>] [--format <fmt>] [--lang <id>] [--plugin <pkg>]
|
|
38
47
|
```
|
|
39
48
|
|
|
40
|
-
| Argument
|
|
41
|
-
|
|
|
42
|
-
| `<query>`
|
|
43
|
-
| `-d, --dir`
|
|
44
|
-
| `-f, --format`
|
|
49
|
+
| Argument | Description | Default |
|
|
50
|
+
| ------------------ | ------------------------------------------------------------- | ------------ |
|
|
51
|
+
| `<query>` | Query string (see Query Syntax below) | required |
|
|
52
|
+
| `-d, --dir` | Root directory to search | current dir |
|
|
53
|
+
| `-f, --format` | Output format: `text`, `json`, or `files` | `text` |
|
|
54
|
+
| `-l, --lang` | Restrict search to one language backend (e.g. `js`, `python`) | all languages |
|
|
55
|
+
| `-p, --plugin` | Load a language plugin package (repeatable) | none |
|
|
45
56
|
|
|
46
57
|
### Output formats
|
|
47
58
|
|
|
@@ -51,6 +62,8 @@ ast-search <query> [--dir <path>] [--format <fmt>]
|
|
|
51
62
|
|
|
52
63
|
## Query syntax
|
|
53
64
|
|
|
65
|
+
### JavaScript / TypeScript / Vue
|
|
66
|
+
|
|
54
67
|
Queries use [esquery](https://github.com/estools/esquery) CSS selector syntax over Babel AST node types. A few examples:
|
|
55
68
|
|
|
56
69
|
```bash
|
|
@@ -64,7 +77,7 @@ ast-search 'AwaitExpression'
|
|
|
64
77
|
ast-search 'CatchClause AssignmentExpression'
|
|
65
78
|
```
|
|
66
79
|
|
|
67
|
-
|
|
80
|
+
#### Shorthands
|
|
68
81
|
|
|
69
82
|
Common node types can be written as short keywords:
|
|
70
83
|
|
|
@@ -92,7 +105,7 @@ The original Vue `this` example using shorthands:
|
|
|
92
105
|
ast-search 'ObjectMethod[key.name="setup"] this'
|
|
93
106
|
```
|
|
94
107
|
|
|
95
|
-
|
|
108
|
+
#### Optional chaining
|
|
96
109
|
|
|
97
110
|
Optional chains (`?.`) are normalized transparently — `CallExpression` and `MemberExpression` selectors match both regular and optional-chain variants:
|
|
98
111
|
|
|
@@ -107,6 +120,81 @@ The `optional` flag is preserved on matched nodes, so you can still narrow to st
|
|
|
107
120
|
ast-search 'CallExpression[optional=true]'
|
|
108
121
|
```
|
|
109
122
|
|
|
123
|
+
### Python (via `ast-search-python`)
|
|
124
|
+
|
|
125
|
+
Python queries use [tree-sitter](https://tree-sitter.github.io/tree-sitter/) S-expression syntax. Install the plugin first:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npm install -g ast-search-python
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Then pass `--plugin ast-search-python` to enable `.py` / `.pyw` file support:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# Find all function definitions (shorthand)
|
|
135
|
+
ast-search 'fn' --plugin ast-search-python
|
|
136
|
+
|
|
137
|
+
# Find all class definitions (raw S-expression)
|
|
138
|
+
ast-search '(class_definition) @cls' --plugin ast-search-python
|
|
139
|
+
|
|
140
|
+
# Find all calls to a specific function by name
|
|
141
|
+
ast-search '(call function: (identifier) @n (#eq? @n "my_func")) @c' --plugin ast-search-python
|
|
142
|
+
|
|
143
|
+
# Restrict to only Python files in a mixed-language repo
|
|
144
|
+
ast-search 'fn' --lang python --plugin ast-search-python
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Python shorthands
|
|
148
|
+
|
|
149
|
+
| Shorthand | Expands to |
|
|
150
|
+
| ----------- | ----------------------------------- |
|
|
151
|
+
| `fn` | `(function_definition) @_` |
|
|
152
|
+
| `call` | `(call) @_` |
|
|
153
|
+
| `class` | `(class_definition) @_` |
|
|
154
|
+
| `assign` | `(assignment) @_` |
|
|
155
|
+
| `return` | `(return_statement) @_` |
|
|
156
|
+
| `await` | `(await) @_` |
|
|
157
|
+
| `yield` | `(yield) @_` |
|
|
158
|
+
| `import` | `(import_statement) @_` |
|
|
159
|
+
| `from` | `(import_from_statement) @_` |
|
|
160
|
+
| `if` | `(if_statement) @_` |
|
|
161
|
+
| `for` | `(for_statement) @_` |
|
|
162
|
+
| `while` | `(while_statement) @_` |
|
|
163
|
+
| `raise` | `(raise_statement) @_` |
|
|
164
|
+
| `with` | `(with_statement) @_` |
|
|
165
|
+
| `lambda` | `(lambda) @_` |
|
|
166
|
+
| `decorator` | `(decorator) @_` |
|
|
167
|
+
| `augassign` | `(augmented_assignment) @_` |
|
|
168
|
+
|
|
169
|
+
For raw S-expression queries, include at least one `@capture_name` — tree-sitter's `captures()` requires it to return results.
|
|
170
|
+
|
|
171
|
+
> **Note:** In tree-sitter-python 0.21+, `async def` functions are still typed as `function_definition` (no separate `async_function_definition` node). To match only async functions, use a full predicate query.
|
|
172
|
+
|
|
110
173
|
## Supported file types
|
|
111
174
|
|
|
112
|
-
`.js
|
|
175
|
+
**Core:** `.js` `.ts` `.jsx` `.tsx` `.mjs` `.cjs` `.vue`
|
|
176
|
+
|
|
177
|
+
**With `ast-search-python`:** `.py` `.pyw`
|
|
178
|
+
|
|
179
|
+
## Plugin API
|
|
180
|
+
|
|
181
|
+
To write a language plugin, implement the `LanguageBackend` interface exported from `ast-search/plugin` and export a `register` function:
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import type { LanguageBackend, LanguageRegistry } from 'ast-search/plugin';
|
|
185
|
+
|
|
186
|
+
class MyLanguageBackend implements LanguageBackend {
|
|
187
|
+
readonly langId = 'mylang';
|
|
188
|
+
readonly name = 'My Language';
|
|
189
|
+
readonly extensions = new Set(['.ml']);
|
|
190
|
+
parse(source: string, filePath: string) { /* return opaque AST */ }
|
|
191
|
+
query(ast: unknown, selector: string, source: string, filePath: string) { /* return Match[] */ }
|
|
192
|
+
validateSelector(selector: string) { /* throw on invalid */ }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function register(registry: LanguageRegistry) {
|
|
196
|
+
registry.register(new MyLanguageBackend());
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Name your package `ast-search-<lang>` and users load it with `--plugin ast-search-<lang>`.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { extname } from "node:path";
|
|
2
|
+
import { getAst, parseVueSFC } from "../../file.js";
|
|
3
|
+
import { runQuery, validateSelector as validate, SHORTHANDS, expandShorthands } from "../../search.js";
|
|
4
|
+
export class JSLanguageBackend {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.langId = "js";
|
|
7
|
+
this.name = "JavaScript/TypeScript";
|
|
8
|
+
this.extensions = new Set([
|
|
9
|
+
".js",
|
|
10
|
+
".ts",
|
|
11
|
+
".jsx",
|
|
12
|
+
".tsx",
|
|
13
|
+
".mjs",
|
|
14
|
+
".cjs",
|
|
15
|
+
".vue",
|
|
16
|
+
]);
|
|
17
|
+
}
|
|
18
|
+
parse(source, filePath) {
|
|
19
|
+
const ext = extname(filePath);
|
|
20
|
+
const content = ext === ".vue" ? parseVueSFC(Buffer.from(source)) : source;
|
|
21
|
+
return getAst(content);
|
|
22
|
+
}
|
|
23
|
+
query(ast, selector, source, filePath) {
|
|
24
|
+
return runQuery(selector, ast, source, filePath);
|
|
25
|
+
}
|
|
26
|
+
validateSelector(selector) {
|
|
27
|
+
validate(selector);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export { SHORTHANDS, expandShorthands };
|
package/build/file.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FileHandle } from "node:fs/promises";
|
|
2
|
+
import * as parser from "@babel/parser";
|
|
3
|
+
import { File } from "@babel/types";
|
|
4
|
+
import type { LanguageBackend } from "./language.js";
|
|
5
|
+
import type { LanguageRegistry } from "./registry.js";
|
|
6
|
+
export declare function getAst(contents: string): parser.ParseResult<File>;
|
|
7
|
+
export declare const SCRIPT_OPEN: RegExp;
|
|
8
|
+
export declare const SCRIPT_CLOSE: RegExp;
|
|
9
|
+
export declare function parseVueSFC(lines: Buffer): string;
|
|
10
|
+
interface ParseReturn {
|
|
11
|
+
ast: File;
|
|
12
|
+
file: FileHandle;
|
|
13
|
+
source: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function getAstFromPath(path: string): Promise<ParseReturn>;
|
|
16
|
+
export interface ParsedFile {
|
|
17
|
+
ast: unknown;
|
|
18
|
+
source: string;
|
|
19
|
+
backend: LanguageBackend;
|
|
20
|
+
}
|
|
21
|
+
export declare function parseFile(path: string, registry: LanguageRegistry): Promise<ParsedFile>;
|
|
22
|
+
export {};
|
package/build/file.js
CHANGED
|
@@ -56,3 +56,21 @@ export function getAstFromPath(path) {
|
|
|
56
56
|
return { ast, file, source: fileContents };
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
|
+
export function parseFile(path, registry) {
|
|
60
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
+
const ext = extname(path);
|
|
62
|
+
const backend = registry.getByExtension(ext);
|
|
63
|
+
if (!backend) {
|
|
64
|
+
throw new Error(`No backend registered for extension "${ext}"`);
|
|
65
|
+
}
|
|
66
|
+
const file = yield open(path);
|
|
67
|
+
try {
|
|
68
|
+
const source = (yield file.readFile()).toString();
|
|
69
|
+
const ast = yield backend.parse(source, path);
|
|
70
|
+
return { ast, source, backend };
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
yield file.close();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Match } from "./types.js";
|
|
2
|
+
export type { Match };
|
|
3
|
+
export interface LanguageBackend {
|
|
4
|
+
/** Short identifier used with --lang flag, e.g. "js", "python" */
|
|
5
|
+
readonly langId: string;
|
|
6
|
+
/** File extensions this backend handles, e.g. new Set([".py"]) */
|
|
7
|
+
readonly extensions: ReadonlySet<string>;
|
|
8
|
+
/** Human-readable name for error messages */
|
|
9
|
+
readonly name: string;
|
|
10
|
+
/** Parse source text into an opaque AST. Throws on unrecoverable parse error. */
|
|
11
|
+
parse(source: string, filePath: string): Promise<unknown> | unknown;
|
|
12
|
+
/**
|
|
13
|
+
* Run a selector query against a parsed AST. The selector is in the
|
|
14
|
+
* backend's native query syntax (after shorthand expansion). Returns matches
|
|
15
|
+
* with file/line/col/source fields.
|
|
16
|
+
*/
|
|
17
|
+
query(ast: unknown, selector: string, source: string, filePath: string): Match[];
|
|
18
|
+
/**
|
|
19
|
+
* Validate a selector string. Expands shorthands internally, then checks
|
|
20
|
+
* syntax. Throws with a descriptive message on invalid syntax.
|
|
21
|
+
*/
|
|
22
|
+
validateSelector(selector: string): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/build/main.d.ts
ADDED
package/build/main.js
CHANGED
|
@@ -16,35 +16,34 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
|
16
16
|
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
17
17
|
};
|
|
18
18
|
import yargs from "yargs/yargs";
|
|
19
|
-
import { createRequire } from "module";
|
|
20
19
|
import { walkRepoFiles } from "./walk.js";
|
|
21
|
-
import {
|
|
22
|
-
import { runQuery, validateSelector } from "./search.js";
|
|
20
|
+
import { parseFile } from "./file.js";
|
|
23
21
|
import { formatMatches } from "./output.js";
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
import { defaultRegistry } from "./registry.js";
|
|
23
|
+
import { JSLanguageBackend } from "./backends/js/index.js";
|
|
24
|
+
import { VERSION } from "./version.js";
|
|
25
|
+
// Register built-in JS/TS/Vue backend
|
|
26
|
+
defaultRegistry.register(new JSLanguageBackend());
|
|
27
|
+
export function searchRepo(selector_1, dir_1) {
|
|
28
|
+
return __awaiter(this, arguments, void 0, function* (selector, dir, registry = defaultRegistry) {
|
|
28
29
|
var _a, e_1, _b, _c;
|
|
29
|
-
|
|
30
|
+
// Early validation when only one backend is registered (common JS-only case)
|
|
31
|
+
if (registry.allBackends.length === 1) {
|
|
32
|
+
registry.allBackends[0].validateSelector(selector);
|
|
33
|
+
}
|
|
30
34
|
const results = [];
|
|
31
35
|
try {
|
|
32
|
-
for (var _d = true, _e = __asyncValues(walkRepoFiles(dir)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
36
|
+
for (var _d = true, _e = __asyncValues(walkRepoFiles(dir, registry.allExtensions)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
33
37
|
_c = _f.value;
|
|
34
38
|
_d = false;
|
|
35
39
|
const filePath = _c;
|
|
36
|
-
let file;
|
|
37
40
|
try {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
const matches = runQuery(selector, result.ast, result.source, filePath);
|
|
41
|
+
const { ast, source, backend } = yield parseFile(filePath, registry);
|
|
42
|
+
const matches = backend.query(ast, selector, source, filePath);
|
|
41
43
|
results.push(...matches);
|
|
42
44
|
}
|
|
43
45
|
catch (_g) {
|
|
44
|
-
// skip unparseable files
|
|
45
|
-
}
|
|
46
|
-
finally {
|
|
47
|
-
yield (file === null || file === void 0 ? void 0 : file.close());
|
|
46
|
+
// skip unparseable files / unsupported extensions
|
|
48
47
|
}
|
|
49
48
|
}
|
|
50
49
|
}
|
|
@@ -64,7 +63,7 @@ const y = yargs(process.argv.slice(2))
|
|
|
64
63
|
.command("$0 <query>", "Search a repo for AST patterns using CSS selector syntax", (yargs) => yargs
|
|
65
64
|
.positional("query", {
|
|
66
65
|
type: "string",
|
|
67
|
-
describe: "esquery CSS selector
|
|
66
|
+
describe: "Query string (esquery CSS selector for JS/TS; tree-sitter S-expression for Python)",
|
|
68
67
|
demandOption: true,
|
|
69
68
|
})
|
|
70
69
|
.option("dir", {
|
|
@@ -79,12 +78,47 @@ const y = yargs(process.argv.slice(2))
|
|
|
79
78
|
describe: "output format: text (default), json, files",
|
|
80
79
|
default: "text",
|
|
81
80
|
choices: ["text", "json", "files"],
|
|
81
|
+
})
|
|
82
|
+
.option("lang", {
|
|
83
|
+
alias: "l",
|
|
84
|
+
type: "string",
|
|
85
|
+
describe: "restrict search to a specific language backend by langId (e.g. js, python)",
|
|
86
|
+
})
|
|
87
|
+
.option("plugin", {
|
|
88
|
+
alias: "p",
|
|
89
|
+
type: "string",
|
|
90
|
+
array: true,
|
|
91
|
+
describe: "load a language plugin package (e.g. ast-search-python)",
|
|
82
92
|
}), (argv) => __awaiter(void 0, void 0, void 0, function* () {
|
|
83
|
-
var _a;
|
|
84
|
-
const { query, dir, format } = argv;
|
|
93
|
+
var _a, _b, _c;
|
|
94
|
+
const { query, dir, format, lang, plugin } = argv;
|
|
85
95
|
try {
|
|
86
|
-
|
|
87
|
-
const
|
|
96
|
+
// Load plugins before searching
|
|
97
|
+
for (const pkg of plugin !== null && plugin !== void 0 ? plugin : []) {
|
|
98
|
+
const mod = yield import(pkg);
|
|
99
|
+
const reg = (_a = mod.register) !== null && _a !== void 0 ? _a : (_b = mod.default) === null || _b === void 0 ? void 0 : _b.register;
|
|
100
|
+
if (typeof reg !== "function") {
|
|
101
|
+
throw new Error(`Plugin "${pkg}" does not export a register() function`);
|
|
102
|
+
}
|
|
103
|
+
reg(defaultRegistry);
|
|
104
|
+
}
|
|
105
|
+
// Build a scoped registry if --lang is specified
|
|
106
|
+
let registry = defaultRegistry;
|
|
107
|
+
if (lang) {
|
|
108
|
+
const backend = defaultRegistry.getByLangId(lang);
|
|
109
|
+
if (!backend) {
|
|
110
|
+
const available = defaultRegistry.allBackends.map((b) => b.langId).join(", ");
|
|
111
|
+
throw new Error(`Unknown language "${lang}". Available: ${available}`);
|
|
112
|
+
}
|
|
113
|
+
const { LanguageRegistry } = yield import("./registry.js");
|
|
114
|
+
const scoped = new LanguageRegistry();
|
|
115
|
+
scoped.register(backend);
|
|
116
|
+
registry = scoped;
|
|
117
|
+
// Always validate early when --lang restricts to a single backend
|
|
118
|
+
backend.validateSelector(query);
|
|
119
|
+
}
|
|
120
|
+
const matches = yield searchRepo(query, dir, registry);
|
|
121
|
+
const isTTY = (_c = process.stdout.isTTY) !== null && _c !== void 0 ? _c : false;
|
|
88
122
|
for (const line of formatMatches(matches, isTTY, format)) {
|
|
89
123
|
console.log(line);
|
|
90
124
|
}
|
|
@@ -117,8 +151,16 @@ const y = yargs(process.argv.slice(2))
|
|
|
117
151
|
"$0 'FunctionDeclaration[async=true]' --format files | xargs prettier --write",
|
|
118
152
|
"reformat all files containing async functions",
|
|
119
153
|
],
|
|
154
|
+
[
|
|
155
|
+
"$0 'fn' --dir src --plugin ast-search-python",
|
|
156
|
+
"find all function definitions in Python files",
|
|
157
|
+
],
|
|
158
|
+
[
|
|
159
|
+
"$0 '(class_definition)' --lang python --plugin ast-search-python",
|
|
160
|
+
"find all Python classes (tree-sitter S-expression syntax)",
|
|
161
|
+
],
|
|
120
162
|
])
|
|
121
|
-
.version(
|
|
163
|
+
.version(VERSION)
|
|
122
164
|
.alias("version", "V")
|
|
123
165
|
.help();
|
|
124
166
|
if (process.env.NODE_ENV !== "test") {
|
package/build/plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { LanguageRegistry } from "./registry.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LanguageBackend } from "./language.js";
|
|
2
|
+
export declare class LanguageRegistry {
|
|
3
|
+
private readonly byExt;
|
|
4
|
+
private readonly byId;
|
|
5
|
+
register(backend: LanguageBackend): void;
|
|
6
|
+
getByExtension(ext: string): LanguageBackend | undefined;
|
|
7
|
+
getByLangId(langId: string): LanguageBackend | undefined;
|
|
8
|
+
get allExtensions(): ReadonlySet<string>;
|
|
9
|
+
get allBackends(): LanguageBackend[];
|
|
10
|
+
}
|
|
11
|
+
export declare const defaultRegistry: LanguageRegistry;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class LanguageRegistry {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.byExt = new Map();
|
|
4
|
+
this.byId = new Map();
|
|
5
|
+
}
|
|
6
|
+
register(backend) {
|
|
7
|
+
this.byId.set(backend.langId, backend);
|
|
8
|
+
for (const ext of backend.extensions) {
|
|
9
|
+
this.byExt.set(ext, backend);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
getByExtension(ext) {
|
|
13
|
+
return this.byExt.get(ext);
|
|
14
|
+
}
|
|
15
|
+
getByLangId(langId) {
|
|
16
|
+
return this.byId.get(langId);
|
|
17
|
+
}
|
|
18
|
+
get allExtensions() {
|
|
19
|
+
return new Set(this.byExt.keys());
|
|
20
|
+
}
|
|
21
|
+
get allBackends() {
|
|
22
|
+
return [...this.byId.values()];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export const defaultRegistry = new LanguageRegistry();
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { File } from "@babel/types";
|
|
2
|
+
import type { Match } from "./types.js";
|
|
3
|
+
export type { Match };
|
|
4
|
+
export declare const SHORTHANDS: Record<string, string>;
|
|
5
|
+
export declare function expandShorthands(selector: string): string;
|
|
6
|
+
export declare function validateSelector(selector: string): void;
|
|
7
|
+
export declare function normalizeOptionalChaining(node: any): void;
|
|
8
|
+
export declare function runQuery(selector: string, ast: File, source?: string, filename?: string): Match[];
|
package/build/types.d.ts
ADDED
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION = "0.3.0";
|
package/build/version.js
ADDED
package/build/walk.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function walkRepoFiles(dir: string, extensions: ReadonlySet<string>): AsyncGenerator<string>;
|
package/build/walk.js
CHANGED
|
@@ -25,16 +25,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
|
|
|
25
25
|
};
|
|
26
26
|
import { readdir } from "node:fs/promises";
|
|
27
27
|
import { extname, join } from "node:path";
|
|
28
|
-
|
|
29
|
-
".js",
|
|
30
|
-
".ts",
|
|
31
|
-
".jsx",
|
|
32
|
-
".tsx",
|
|
33
|
-
".mjs",
|
|
34
|
-
".cjs",
|
|
35
|
-
".vue",
|
|
36
|
-
]);
|
|
37
|
-
export function walkRepoFiles(dir) {
|
|
28
|
+
export function walkRepoFiles(dir, extensions) {
|
|
38
29
|
return __asyncGenerator(this, arguments, function* walkRepoFiles_1() {
|
|
39
30
|
const entries = yield __await(readdir(dir, { withFileTypes: true }));
|
|
40
31
|
for (const entry of entries) {
|
|
@@ -44,9 +35,9 @@ export function walkRepoFiles(dir) {
|
|
|
44
35
|
continue;
|
|
45
36
|
const fullPath = join(dir, entry.name);
|
|
46
37
|
if (entry.isDirectory()) {
|
|
47
|
-
yield __await(yield* __asyncDelegator(__asyncValues(walkRepoFiles(fullPath))));
|
|
38
|
+
yield __await(yield* __asyncDelegator(__asyncValues(walkRepoFiles(fullPath, extensions))));
|
|
48
39
|
}
|
|
49
|
-
else if (entry.isFile() &&
|
|
40
|
+
else if (entry.isFile() && extensions.has(extname(entry.name))) {
|
|
50
41
|
yield yield __await(fullPath);
|
|
51
42
|
}
|
|
52
43
|
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ast-search",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "",
|
|
5
|
-
"main": "main.js",
|
|
5
|
+
"main": "build/main.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"import": "./build/main.js",
|
|
9
|
+
"types": "./build/main.d.ts"
|
|
10
|
+
},
|
|
11
|
+
"./plugin": {
|
|
12
|
+
"import": "./build/plugin.js",
|
|
13
|
+
"types": "./build/plugin.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
6
16
|
"scripts": {
|
|
7
17
|
"dev": "npx tsc --watch",
|
|
8
18
|
"debug:vue": "npx tsc && node ./build/main.js 'setup > this' --dir src/__tests__/dummyFiles",
|
|
9
19
|
"debug:react": "npx tsc && node ./build/main.js 'useState' --dir src/__tests__/dummyFiles",
|
|
10
20
|
"debug:empty": "npx tsc && node ./build/main.js 'ValidationsTab > this' --dir src/__tests__/dummyFiles",
|
|
11
21
|
"debug:basics": "npx tsc && node ./build/main.js 'arrowFunction' --dir src/__tests__/dummyFiles",
|
|
12
|
-
"test": "jest",
|
|
22
|
+
"test": "jest --testPathPattern='src/__tests__'",
|
|
13
23
|
"build": "npx tsc && cp build/main.js ast-search",
|
|
14
24
|
"prepublishOnly": "npx tsc"
|
|
15
25
|
},
|
|
@@ -25,6 +35,12 @@
|
|
|
25
35
|
"url": "https://github.com/willey-shiplet/ast-search.git"
|
|
26
36
|
},
|
|
27
37
|
"packageManager": "pnpm@10.33.0",
|
|
38
|
+
"pnpm": {
|
|
39
|
+
"onlyBuiltDependencies": [
|
|
40
|
+
"tree-sitter",
|
|
41
|
+
"tree-sitter-python"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
28
44
|
"dependencies": {
|
|
29
45
|
"@babel/parser": "^7.24.4",
|
|
30
46
|
"@babel/types": "^7.24.0",
|
|
@@ -36,7 +52,9 @@
|
|
|
36
52
|
},
|
|
37
53
|
"files": [
|
|
38
54
|
"build/*.js",
|
|
55
|
+
"build/*.d.ts",
|
|
39
56
|
"build/helpers/*.js",
|
|
57
|
+
"build/backends/**/*.js",
|
|
40
58
|
"README.md"
|
|
41
59
|
],
|
|
42
60
|
"pkg": {
|
|
@@ -49,6 +67,7 @@
|
|
|
49
67
|
"@babel/preset-typescript": "^7.24.1",
|
|
50
68
|
"@jest/globals": "^29.7.0",
|
|
51
69
|
"@semantic-release/changelog": "^6.0.3",
|
|
70
|
+
"@semantic-release/exec": "^6.0.3",
|
|
52
71
|
"@semantic-release/git": "^10.0.1",
|
|
53
72
|
"@semantic-release/github": "^10.0.0",
|
|
54
73
|
"@semantic-release/npm": "^13.1.3",
|