ast-search-python 1.0.3 → 1.0.4

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 CHANGED
@@ -19,6 +19,8 @@ npm install -g ast-search-js
19
19
  npm install -g ast-search-python
20
20
  ```
21
21
 
22
+ > **pnpm users:** `tree-sitter` requires compiling a native addon. pnpm blocks build scripts by default, so the binary won't be built without extra steps. Either run `pnpm approve-builds -g` (select `tree-sitter` and `tree-sitter-python`), then reinstall — or use `npm install -g` instead.
23
+
22
24
  ## Usage
23
25
 
24
26
  Pass `--plugin ast-search-python` to enable Python file support:
package/build/index.d.ts CHANGED
@@ -3,9 +3,9 @@ export declare class PythonLanguageBackend implements LanguageBackend {
3
3
  readonly langId = "python";
4
4
  readonly name = "Python";
5
5
  readonly extensions: Set<string>;
6
- parse(source: string, _filePath: string): unknown;
7
- query(ast: unknown, selector: string, source: string, filePath: string): Match[];
8
- validateSelector(selector: string): void;
6
+ parse(source: string, _filePath: string): Promise<unknown>;
7
+ query(ast: unknown, selector: string, source: string, filePath: string): Promise<Match[]>;
8
+ validateSelector(selector: string): Promise<void>;
9
9
  }
10
10
  /**
11
11
  * Register the Python backend with an ast-search LanguageRegistry.
package/build/index.js CHANGED
@@ -1,28 +1,36 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
1
10
  import { createRequire } from "module";
11
+ import path from "path";
2
12
  import { expandShorthands } from "./shorthands.js";
3
13
  import { runTreeSitterQuery, validateTreeSitterQuery } from "./query.js";
4
- // tree-sitter is a CommonJS module; use createRequire for ESM compat.
5
14
  const _require = createRequire(import.meta.url);
6
- // Lazy-initialize the parser so the native addon is only loaded when
7
- // a Python file is actually parsed (not at module import time).
8
- let _parser;
9
- let _language;
10
- let _QueryClass;
15
+ let _runtimePromise = null;
11
16
  function getRuntime() {
12
- if (!_parser) {
13
- const Parser = _require("tree-sitter");
14
- const pythonModule = _require("tree-sitter-python");
15
- _language = pythonModule.language;
16
- _QueryClass = Parser.Query;
17
- const p = new Parser();
18
- p.setLanguage(pythonModule);
19
- _parser = p;
20
- }
21
- return {
22
- parser: _parser,
23
- language: _language,
24
- QueryClass: _QueryClass,
25
- };
17
+ return __awaiter(this, void 0, void 0, function* () {
18
+ if (_runtimePromise)
19
+ return _runtimePromise;
20
+ _runtimePromise = (() => __awaiter(this, void 0, void 0, function* () {
21
+ const { default: Parser } = yield import("web-tree-sitter");
22
+ const wasmDir = path.dirname(_require.resolve("web-tree-sitter"));
23
+ yield Parser.init({
24
+ locateFile: (_name) => path.join(wasmDir, "tree-sitter.wasm"),
25
+ });
26
+ const wasmPath = path.join(path.dirname(_require.resolve("tree-sitter-wasms/package.json")), "out", "tree-sitter-python.wasm");
27
+ const Python = yield Parser.Language.load(wasmPath);
28
+ const parser = new Parser();
29
+ parser.setLanguage(Python);
30
+ return { parser: parser, language: Python };
31
+ }))();
32
+ return _runtimePromise;
33
+ });
26
34
  }
27
35
  export class PythonLanguageBackend {
28
36
  constructor() {
@@ -31,17 +39,23 @@ export class PythonLanguageBackend {
31
39
  this.extensions = new Set([".py", ".pyw"]);
32
40
  }
33
41
  parse(source, _filePath) {
34
- const { parser } = getRuntime();
35
- return parser.parse(source);
42
+ return __awaiter(this, void 0, void 0, function* () {
43
+ const { parser } = yield getRuntime();
44
+ return parser.parse(source);
45
+ });
36
46
  }
37
47
  query(ast, selector, source, filePath) {
38
- const { language, QueryClass } = getRuntime();
39
- const expanded = expandShorthands(selector);
40
- return runTreeSitterQuery(ast, expanded, source, filePath, language, QueryClass);
48
+ return __awaiter(this, void 0, void 0, function* () {
49
+ const { language } = yield getRuntime();
50
+ const expanded = expandShorthands(selector);
51
+ return runTreeSitterQuery(ast, expanded, source, filePath, language);
52
+ });
41
53
  }
42
54
  validateSelector(selector) {
43
- const { language, QueryClass } = getRuntime();
44
- validateTreeSitterQuery(expandShorthands(selector), language, QueryClass);
55
+ return __awaiter(this, void 0, void 0, function* () {
56
+ const { language } = yield getRuntime();
57
+ validateTreeSitterQuery(expandShorthands(selector), language);
58
+ });
45
59
  }
46
60
  }
47
61
  /**
package/build/query.d.ts CHANGED
@@ -1,23 +1,3 @@
1
1
  import type { Match } from "ast-search-js/plugin";
2
- interface TSNode {
3
- startPosition: {
4
- row: number;
5
- column: number;
6
- };
7
- startIndex: number;
8
- endIndex: number;
9
- type: string;
10
- }
11
- interface TSCapture {
12
- node: TSNode;
13
- name: string;
14
- }
15
- interface TSQueryConstructor {
16
- new (language: unknown, pattern: string): TSQuery;
17
- }
18
- interface TSQuery {
19
- captures(node: TSNode): TSCapture[];
20
- }
21
- export declare function runTreeSitterQuery(ast: unknown, pattern: string, source: string, filePath: string, language: unknown, QueryClass: TSQueryConstructor): Match[];
22
- export declare function validateTreeSitterQuery(pattern: string, language: unknown, QueryClass: TSQueryConstructor): void;
23
- export {};
2
+ export declare function runTreeSitterQuery(ast: unknown, pattern: string, source: string, filePath: string, language: unknown): Match[];
3
+ export declare function validateTreeSitterQuery(pattern: string, language: unknown): void;
package/build/query.js CHANGED
@@ -7,18 +7,21 @@ function assertValidPattern(pattern) {
7
7
  `Use a shorthand (e.g. "fn", "call") or write a full S-expression (e.g. "(function_definition) @fn").`);
8
8
  }
9
9
  }
10
- export function runTreeSitterQuery(ast, pattern, source, filePath, language, QueryClass) {
10
+ export function runTreeSitterQuery(ast, pattern, source, filePath, language) {
11
11
  assertValidPattern(pattern);
12
12
  const tree = ast;
13
- const q = new QueryClass(language, pattern);
13
+ const q = language.query(pattern);
14
14
  const captures = q.captures(tree.rootNode);
15
+ // web-tree-sitter may return different JS objects for the same node when
16
+ // multiple capture names match it, so deduplicate by position instead of identity.
15
17
  const seen = new Set();
16
18
  const results = [];
17
19
  for (const capture of captures) {
18
20
  const node = capture.node;
19
- if (seen.has(node))
21
+ const key = `${node.startIndex}:${node.endIndex}`;
22
+ if (seen.has(key))
20
23
  continue;
21
- seen.add(node);
24
+ seen.add(key);
22
25
  const text = source.slice(node.startIndex, node.endIndex);
23
26
  const firstLine = text.split("\n")[0].trimEnd();
24
27
  results.push({
@@ -30,10 +33,10 @@ export function runTreeSitterQuery(ast, pattern, source, filePath, language, Que
30
33
  }
31
34
  return results;
32
35
  }
33
- export function validateTreeSitterQuery(pattern, language, QueryClass) {
36
+ export function validateTreeSitterQuery(pattern, language) {
34
37
  try {
35
38
  assertValidPattern(pattern);
36
- new QueryClass(language, pattern);
39
+ language.query(pattern);
37
40
  }
38
41
  catch (e) {
39
42
  throw new Error(`Invalid tree-sitter query: ${e instanceof Error ? e.message : String(e)}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ast-search-python",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Python language plugin for ast-search",
5
5
  "type": "module",
6
6
  "main": "./build/index.js",
@@ -32,19 +32,19 @@
32
32
  "provenance": true
33
33
  },
34
34
  "dependencies": {
35
- "tree-sitter": "^0.21.1",
36
- "tree-sitter-python": "^0.21.0"
35
+ "web-tree-sitter": "^0.24.0",
36
+ "tree-sitter-wasms": "^0.1.12"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@jest/globals": "^29.7.0",
40
40
  "@types/jest": "^29.5.12",
41
41
  "@types/node": "^20.12.7",
42
- "ast-search-js": "1.0.0",
42
+ "ast-search-js": "1.0.1",
43
43
  "jest": "^29.7.0",
44
44
  "ts-jest": "^29.1.2",
45
45
  "typescript": "^5.4.5"
46
46
  },
47
47
  "peerDependencies": {
48
- "ast-search-js": "1.0.0"
48
+ "ast-search-js": "1.0.1"
49
49
  }
50
50
  }