@hyper-light/vorpal-wasm 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 ADDED
@@ -0,0 +1,250 @@
1
+ # @hyper-light/vorpal-wasm
2
+
3
+ WebAssembly build of [vorpal](https://vorpal.github.io/) for use in browsers and Node.js.
4
+
5
+ This package provides the same API as [`@hyper-light/vorpal-node`](https://www.npmjs.com/package/@hyper-light/vorpal-node) but runs in any JavaScript environment that supports WebAssembly, including browsers, Deno, and edge runtimes.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ yarn add @hyper-light/vorpal-wasm web-tree-sitter
11
+ ```
12
+
13
+ `web-tree-sitter` is a required peer dependency.
14
+
15
+ ### Vite
16
+
17
+ When using Vite, you need to make `tree-sitter.wasm` available in your public directory. You can automate this with a `postinstall` script in your `package.json` (see [web-tree-sitter setup](https://github.com/tree-sitter/tree-sitter/tree/master/lib/binding_web#setup)):
18
+
19
+ ```json
20
+ "postinstall": "cp node_modules/web-tree-sitter/tree-sitter.wasm public"
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ Unlike `@hyper-light/vorpal-node`, this package has no predefined language support. All languages must be registered at runtime by loading their tree-sitter WASM parser.
26
+
27
+ ```js
28
+ import { initializeTreeSitter, registerDynamicLanguage, parse, kind } from '@hyper-light/vorpal-wasm'
29
+
30
+ // 1. Initialize the tree-sitter WASM runtime (once)
31
+ await initializeTreeSitter()
32
+
33
+ // 2. Register a language by loading its WASM parser
34
+ await registerDynamicLanguage({
35
+ javascript: { libraryPath: '/path/to/tree-sitter-javascript.wasm' },
36
+ })
37
+
38
+ // 3. Parse and search code
39
+ const sg = parse('javascript', 'console.log("hello world")')
40
+ const node = sg.root().find('console.log($ARG)')
41
+ console.log(node.getMatch('ARG').text()) // "hello world"
42
+ ```
43
+
44
+ ### Registering Languages
45
+
46
+ `registerDynamicLanguage` accepts a map of language name to registration config. It can be called multiple times to add or update languages.
47
+
48
+ ```js
49
+ await registerDynamicLanguage({
50
+ javascript: { libraryPath: '/path/to/tree-sitter-javascript.wasm' },
51
+ python: {
52
+ libraryPath: '/path/to/tree-sitter-python.wasm',
53
+ expandoChar: 'µ', // custom expando char for languages where $ is special
54
+ },
55
+ })
56
+ ```
57
+
58
+ The `expandoChar` option sets the character used internally to represent metavariables (defaults to `$`). Use a different character for languages where `$` is a valid identifier character (e.g. PHP, Bash).
59
+
60
+ ### Pattern Matching
61
+
62
+ ```js
63
+ // By pattern string
64
+ sg.root().find('console.log($$$ARGS)')
65
+
66
+ // By kind number
67
+ const k = kind('javascript', 'call_expression')
68
+ sg.root().find(k)
69
+
70
+ // By rule config (same as YAML rules)
71
+ sg.root().find({
72
+ rule: { pattern: 'console.log($A)' },
73
+ constraints: { A: { kind: 'string' } },
74
+ })
75
+ ```
76
+
77
+ ### Code Rewriting
78
+
79
+ ```js
80
+ const match = sg.root().find('console.log($A)')
81
+ const edit = match.replace('console.error($A)')
82
+ const newCode = sg.root().commitEdits([edit])
83
+ ```
84
+
85
+ ## API Reference
86
+
87
+ ### Top-level functions
88
+
89
+ #### `initializeTreeSitter(): Promise<void>`
90
+
91
+ Initializes the tree-sitter WASM runtime. Must be called once before any other function.
92
+
93
+ #### `registerDynamicLanguage(langs: Record<string, { libraryPath: string, expandoChar?: string }>): Promise<void>`
94
+
95
+ Registers one or more language parsers by loading their WASM binaries. Can be called multiple times; existing languages are updated.
96
+
97
+ #### `parse(lang: string, src: string): SgRoot`
98
+
99
+ Parses source code and returns an `SgRoot` instance. Throws if the language has not been registered.
100
+
101
+ #### `kind(lang: string, kindName: string): number`
102
+
103
+ Returns the numeric kind ID for a named node type in the given language. Useful for matching by node kind.
104
+
105
+ #### `pattern(lang: string, patternStr: string): object`
106
+
107
+ Compiles a pattern string into a rule config object (equivalent to `{ rule: { pattern: patternStr } }`). Useful for building rule configs programmatically.
108
+
109
+ #### `dumpPattern(lang: string, patternStr: string, selector?: string, strictness?: string): PatternTree`
110
+
111
+ Dumps the internal structure of a pattern for inspection and debugging. Returns a tree showing how vorpal parses the pattern, including source positions and node kinds.
112
+
113
+ - `selector`: optional kind name for contextual patterns (e.g. `'field_definition'`)
114
+ - `strictness`: one of `"cst"`, `"smart"` (default), `"ast"`, `"relaxed"`, `"signature"`, `"template"`
115
+
116
+ Each `PatternTree` node has:
117
+ - `kind`: the tree-sitter node kind string
118
+ - `pattern`: `"metaVar"`, `"terminal"`, or `"internal"`
119
+ - `isNamed`: whether the node is a named node
120
+ - `text`: source text (for metavar and terminal nodes)
121
+ - `children`: child `PatternTree` nodes
122
+ - `start`, `end`: `{ line, column }` positions in the pattern source
123
+
124
+ ### `SgRoot`
125
+
126
+ Represents the parsed tree of code.
127
+
128
+ #### `root(): SgNode`
129
+
130
+ Returns the root `SgNode`.
131
+
132
+ #### `filename(): string`
133
+
134
+ Returns `"anonymous"` when the instance is created via `parse`.
135
+
136
+ #### `getInnerTree(): Tree`
137
+
138
+ Returns the underlying `web-tree-sitter` `Tree` object. Useful for low-level inspection or debugging.
139
+
140
+ ### `SgNode`
141
+
142
+ Represents a single AST node.
143
+
144
+ #### Position and info
145
+
146
+ | Method | Description |
147
+ |--------|-------------|
148
+ | `range()` | Returns `{ start, end }` where each is `{ line, column, index }` |
149
+ | `isLeaf()` | True if the node has no children |
150
+ | `isNamed()` | True if the node is a named (non-anonymous) node |
151
+ | `isNamedLeaf()` | True if the node is a named node with no named children |
152
+ | `kind()` | Returns the node kind string |
153
+ | `is(kind: string)` | True if the node kind equals `kind` |
154
+ | `text()` | Returns the source text of the node |
155
+ | `id()` | Returns the unique node ID |
156
+
157
+ #### Searching
158
+
159
+ | Method | Description |
160
+ |--------|-------------|
161
+ | `find(matcher)` | Returns the first descendant matching the matcher, or `undefined` |
162
+ | `findAll(matcher)` | Returns all descendants matching the matcher |
163
+
164
+ Matchers can be a pattern string, a kind number (from `kind()`), or a rule config object.
165
+
166
+ #### Relational matchers
167
+
168
+ | Method | Description |
169
+ |--------|-------------|
170
+ | `matches(matcher)` | True if the node itself matches |
171
+ | `inside(matcher)` | True if the node is inside an ancestor matching the matcher |
172
+ | `has(matcher)` | True if the node has a descendant matching the matcher |
173
+ | `precedes(matcher)` | True if the node comes before a sibling matching the matcher |
174
+ | `follows(matcher)` | True if the node comes after a sibling matching the matcher |
175
+
176
+ #### Match environment
177
+
178
+ | Method | Description |
179
+ |--------|-------------|
180
+ | `getMatch(name: string)` | Returns the node bound to a metavariable (e.g. `$VAR`) |
181
+ | `getMultipleMatches(name: string)` | Returns nodes bound to a multi-metavariable (e.g. `$$$ARGS`) |
182
+ | `getTransformed(name: string)` | Returns the string value of a transformed variable |
183
+
184
+ #### Tree traversal
185
+
186
+ | Method | Description |
187
+ |--------|-------------|
188
+ | `children_nodes()` | Returns all child nodes |
189
+ | `parent_node()` | Returns the parent node, or `undefined` |
190
+ | `child(nth: number)` | Returns the nth child, or `undefined` |
191
+ | `ancestors()` | Returns all ancestors from parent to root |
192
+ | `next_node()` | Returns the next sibling, or `undefined` |
193
+ | `nextAll()` | Returns all following siblings |
194
+ | `prev()` | Returns the previous sibling, or `undefined` |
195
+ | `prevAll()` | Returns all preceding siblings |
196
+ | `field(name: string)` | Returns the child node for a named field, or `undefined` |
197
+ | `fieldChildren(name: string)` | Returns all child nodes for a named field |
198
+
199
+ #### Editing
200
+
201
+ | Method | Description |
202
+ |--------|-------------|
203
+ | `replace(text: string)` | Creates a `WasmEdit` replacing this node's range with `text` |
204
+ | `commitEdits(edits: WasmEdit[])` | Applies edits to the node's text and returns the new source string |
205
+
206
+ `WasmEdit` has `start_pos`, `end_pos` (character offsets), and `inserted_text`. These fields can be modified before calling `commitEdits`.
207
+
208
+ ## Building from Source
209
+
210
+ Requires [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/).
211
+
212
+ ```bash
213
+ # For browser (ES module)
214
+ yarn build
215
+
216
+ # For Node.js (CommonJS)
217
+ yarn build:nodejs
218
+
219
+ # For bundlers
220
+ yarn build:bundler
221
+ ```
222
+
223
+ ## Testing
224
+
225
+ ```bash
226
+ yarn install
227
+ ```
228
+
229
+ ### Rust WASM tests
230
+
231
+ ```bash
232
+ yarn test
233
+ ```
234
+
235
+ > **Note:** `wasm-pack test --node` runs the test harness from a temporary directory.
236
+ > The `test` script sets `NODE_PATH=$PWD/node_modules` so that `web-tree-sitter` and
237
+ > parser WASM files can be resolved. If you run `wasm-pack test --node` directly,
238
+ > set `NODE_PATH` yourself:
239
+ >
240
+ > ```bash
241
+ > NODE_PATH=$PWD/node_modules wasm-pack test --node
242
+ > ```
243
+
244
+ ### JavaScript tests
245
+
246
+ ```bash
247
+ yarn test:js
248
+ ```
249
+
250
+ This builds the WASM package for Node.js and runs AVA tests against it.
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@hyper-light/vorpal-wasm",
3
+ "type": "module",
4
+ "collaborators": [
5
+ "Herrington Darkholme <2883231+HerringtonDarkholme@users.noreply.github.com>"
6
+ ],
7
+ "description": "Search and Rewrite code at large scale using precise AST pattern",
8
+ "version": "0.1.0",
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/hyper-light/vorpal"
13
+ },
14
+ "files": [
15
+ "wasm_bg.wasm",
16
+ "wasm.js",
17
+ "wasm_bg.js",
18
+ "wasm.d.ts"
19
+ ],
20
+ "main": "wasm.js",
21
+ "types": "wasm.d.ts",
22
+ "sideEffects": [
23
+ "./wasm.js",
24
+ "./snippets/*"
25
+ ],
26
+ "keywords": [
27
+ "ast",
28
+ "pattern",
29
+ "codemod",
30
+ "search",
31
+ "rewrite"
32
+ ]
33
+ }
package/wasm.d.ts ADDED
@@ -0,0 +1,146 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ export function registerDynamicLanguage(map: Record<string, {libraryPath: string, expandoChar?: string}>): Promise<void>;
5
+
6
+
7
+
8
+ export class Pos {
9
+ private constructor();
10
+ free(): void;
11
+ [Symbol.dispose](): void;
12
+ /**
13
+ * column number starting from 0
14
+ */
15
+ column: number;
16
+ /**
17
+ * character offset of the position
18
+ */
19
+ index: number;
20
+ /**
21
+ * line number starting from 0
22
+ */
23
+ line: number;
24
+ }
25
+
26
+ export class Range {
27
+ private constructor();
28
+ free(): void;
29
+ [Symbol.dispose](): void;
30
+ /**
31
+ * ending position of the range
32
+ */
33
+ end: Pos;
34
+ /**
35
+ * starting position of the range
36
+ */
37
+ start: Pos;
38
+ }
39
+
40
+ /**
41
+ * Represents a single AST node.
42
+ */
43
+ export class SgNode {
44
+ private constructor();
45
+ free(): void;
46
+ [Symbol.dispose](): void;
47
+ ancestors(): SgNode[];
48
+ child(nth: number): SgNode | undefined;
49
+ children_nodes(): SgNode[];
50
+ commitEdits(edits: any): string;
51
+ field(name: string): SgNode | undefined;
52
+ fieldChildren(name: string): SgNode[];
53
+ find(matcher: any): SgNode | undefined;
54
+ findAll(matcher: any): SgNode[];
55
+ follows(m: any): boolean;
56
+ getMatch(m: string): SgNode | undefined;
57
+ getMultipleMatches(m: string): SgNode[];
58
+ getTransformed(m: string): string | undefined;
59
+ has(m: any): boolean;
60
+ id(): number;
61
+ inside(m: any): boolean;
62
+ is(kind: string): boolean;
63
+ isLeaf(): boolean;
64
+ isNamed(): boolean;
65
+ isNamedLeaf(): boolean;
66
+ kind(): string;
67
+ matches(m: any): boolean;
68
+ next(): SgNode | undefined;
69
+ nextAll(): SgNode[];
70
+ parent_node(): SgNode | undefined;
71
+ precedes(m: any): boolean;
72
+ prev(): SgNode | undefined;
73
+ prevAll(): SgNode[];
74
+ range(): Range;
75
+ replace(text: string): WasmEdit;
76
+ text(): string;
77
+ }
78
+
79
+ /**
80
+ * Represents the parsed tree of code.
81
+ */
82
+ export class SgRoot {
83
+ private constructor();
84
+ free(): void;
85
+ [Symbol.dispose](): void;
86
+ /**
87
+ * Returns the path of the file if it is discovered by vorpal's `findInFiles`.
88
+ * Returns `"anonymous"` if the instance is created by `parse`.
89
+ */
90
+ filename(): string;
91
+ /**
92
+ * This method is mainly for debugging tree parsing result.
93
+ */
94
+ getInnerTree(): any;
95
+ /**
96
+ * Returns the root SgNode of the Vorpal instance.
97
+ */
98
+ root(): SgNode;
99
+ }
100
+
101
+ export class WasmEdit {
102
+ private constructor();
103
+ free(): void;
104
+ [Symbol.dispose](): void;
105
+ /**
106
+ * The end position of the edit (character offset)
107
+ */
108
+ end_pos: number;
109
+ /**
110
+ * The text to be inserted
111
+ */
112
+ inserted_text: string;
113
+ /**
114
+ * The start position of the edit (character offset)
115
+ */
116
+ start_pos: number;
117
+ }
118
+
119
+ /**
120
+ * Dump a pattern's internal structure for inspection.
121
+ * `selector` is an optional kind name for contextual patterns.
122
+ * `strictness` is one of: "cst", "smart", "ast", "relaxed", "signature", "template".
123
+ * Returns a tree structure showing how vorpal parses the pattern, including source positions.
124
+ */
125
+ export function dumpPattern(lang: string, pattern_str: string, selector?: string | null, strictness?: string | null): any;
126
+
127
+ /**
128
+ * Initialize the tree-sitter WASM runtime.
129
+ * Must be called before any other function.
130
+ */
131
+ export function initializeTreeSitter(): Promise<void>;
132
+
133
+ /**
134
+ * Get the `kind` number from its string name.
135
+ */
136
+ export function kind(lang: string, kind_name: string): number;
137
+
138
+ /**
139
+ * Parse a string to a Vorpal instance.
140
+ */
141
+ export function parse(lang: string, src: string): SgRoot;
142
+
143
+ /**
144
+ * Compile a string to a Vorpal Pattern config.
145
+ */
146
+ export function pattern(lang: string, pattern_str: string): any;
package/wasm.js ADDED
@@ -0,0 +1,9 @@
1
+ /* @ts-self-types="./wasm.d.ts" */
2
+ import * as wasm from "./wasm_bg.wasm";
3
+ import { __wbg_set_wasm } from "./wasm_bg.js";
4
+
5
+ __wbg_set_wasm(wasm);
6
+ wasm.__wbindgen_start();
7
+ export {
8
+ Pos, Range, SgNode, SgRoot, WasmEdit, dumpPattern, initializeTreeSitter, kind, parse, pattern, registerDynamicLanguage
9
+ } from "./wasm_bg.js";