feat(web)!: use the WASM module in the bindings, and not the other way around

Parser is no longer the default export, but you *must* call
`Parser.init()` before doing anything still
This commit is contained in:
Amaan Qureshi 2025-01-19 15:15:01 -05:00
parent b1e39d2dba
commit be7716dfa7
29 changed files with 613 additions and 662 deletions

1
lib/binding_web/.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
/lib/tree-sitter.d.ts linguist-generated

View file

@ -1,5 +1,8 @@
debug/
dist/
/lib/tree-sitter.js
/lib/tree-sitter.wasm
/lib/tree-sitter.wasm.map
/tree-sitter.js
/tree-sitter.js.map
/tree-sitter.wasm

View file

@ -7,7 +7,7 @@ mergeInto(LibraryManager.library, {
lengthAddress,
) {
const INPUT_BUFFER_SIZE = 10 * 1024;
const string = currentParseCallback(index, { row, column });
const string = Module.currentParseCallback(index, { row, column });
if (typeof string === 'string') {
setValue(lengthAddress, string.length, 'i32');
stringToUTF16(string, inputBufferAddress, INPUT_BUFFER_SIZE);
@ -17,22 +17,22 @@ mergeInto(LibraryManager.library, {
},
tree_sitter_log_callback(isLexMessage, messageAddress) {
if (currentLogCallback) {
if (Module.currentLogCallback) {
const message = UTF8ToString(messageAddress);
currentLogCallback(message, isLexMessage !== 0);
Module.currentLogCallback(message, isLexMessage !== 0);
}
},
tree_sitter_progress_callback(currentOffset) {
if (currentProgressCallback) {
return currentProgressCallback({ currentOffset });
if (Module.currentProgressCallback) {
return Module.currentProgressCallback({ currentOffset });
}
return false;
},
tree_sitter_query_progress_callback(currentOffset) {
if (currentQueryProgressCallback) {
return currentQueryProgressCallback({ currentOffset });
if (Module.currentQueryProgressCallback) {
return Module.currentQueryProgressCallback({ currentOffset });
}
return false;
},

View file

@ -0,0 +1,4 @@
Module.currentQueryProgressCallback = null;
Module.currentProgressCallback = null;
Module.currentLogCallback = null;
Module.currentParseCallback = null;

217
lib/binding_web/lib/tree-sitter.d.ts generated vendored Normal file
View file

@ -0,0 +1,217 @@
// TypeScript bindings for emscripten-generated code. Automatically generated at compile time.
declare namespace RuntimeExports {
function AsciiToString(ptr: number): string;
function stringToUTF8(str: string, outPtr: number, maxBytesToWrite: number): number;
/**
* Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the
* emscripten HEAP, returns a copy of that string as a Javascript String object.
*
* @param {number} ptr
* @param {number=} maxBytesToRead - An optional length that specifies the
* maximum number of bytes to read. You can omit this parameter to scan the
* string until the first 0 byte. If maxBytesToRead is passed, and the string
* at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the
* string will cut short at that byte index (i.e. maxBytesToRead will not
* produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing
* frequent uses of UTF8ToString() with and without maxBytesToRead may throw
* JS JIT optimizations off, so it is worth to consider consistently using one
* @return {string}
*/
function UTF8ToString(ptr: number, maxBytesToRead?: number): string;
function lengthBytesUTF8(str: string): number;
function stringToUTF16(str: string, outPtr: number, maxBytesToWrite: number): number;
/**
* @param {string=} libName
* @param {Object=} localScope
* @param {number=} handle
*/
function loadWebAssemblyModule(
binary: Uint8Array,
flags: {
allowUndefined?: boolean,
loadAsync?: boolean,
global?: boolean,
nodelete?: boolean;
},
libName?: string,
localScope?: Record<string, any>,
handle?: number
): Promise<Record<string, () => number>>;
/**
* @param {number} ptr
* @param {string} type
*/
function getValue(ptr: number, type?: string): number;
/**
* @param {number} ptr
* @param {number} value
* @param {string} type
*/
function setValue(ptr: number, value: number, type?: string): void;
let currentParseCallback: ((index: number, position: {row: number, column: number}) => string | undefined) | null;
let currentLogCallback: ((message: string, isLex: boolean) => void) | null;
let currentProgressCallback: ((state: {currentOffset: number}) => void) | null;
let currentQueryProgressCallback: ((state: {currentOffset: number}) => void) | null;
let HEAPF32: Float32Array;
let HEAPF64: Float64Array;
let HEAP_DATA_VIEW: DataView;
let HEAP8: Int8Array
let HEAPU8: Uint8Array;
let HEAP16: Int16Array;
let HEAPU16: Uint16Array;
let HEAP32: Int32Array;
let HEAPU32: Uint32Array;
let HEAP64: BigInt64Array;
let HEAPU64: BigUint64Array;
}
interface WasmModule {
_malloc(_0: number): number;
_calloc(_0: number, _1: number): number;
_realloc(_0: number, _1: number): number;
_free(_0: number): void;
_ts_language_symbol_count(_0: number): number;
_ts_language_state_count(_0: number): number;
_ts_language_version(_0: number): number;
_ts_language_name(_0: number): number;
_ts_language_field_count(_0: number): number;
_ts_language_next_state(_0: number, _1: number, _2: number): number;
_ts_language_symbol_name(_0: number, _1: number): number;
_ts_language_symbol_for_name(_0: number, _1: number, _2: number, _3: number): number;
_strncmp(_0: number, _1: number, _2: number): number;
_ts_language_symbol_type(_0: number, _1: number): number;
_ts_language_field_name_for_id(_0: number, _1: number): number;
_ts_lookahead_iterator_new(_0: number, _1: number): number;
_ts_lookahead_iterator_delete(_0: number): void;
_ts_lookahead_iterator_reset_state(_0: number, _1: number): number;
_ts_lookahead_iterator_reset(_0: number, _1: number, _2: number): number;
_ts_lookahead_iterator_next(_0: number): number;
_ts_lookahead_iterator_current_symbol(_0: number): number;
_memset(_0: number, _1: number, _2: number): number;
_memcpy(_0: number, _1: number, _2: number): number;
_ts_parser_delete(_0: number): void;
_ts_parser_reset(_0: number): void;
_ts_parser_set_language(_0: number, _1: number): number;
_ts_parser_timeout_micros(_0: number): number;
_ts_parser_set_timeout_micros(_0: number, _1: number, _2: number): void;
_ts_parser_set_included_ranges(_0: number, _1: number, _2: number): number;
_memmove(_0: number, _1: number, _2: number): number;
_memcmp(_0: number, _1: number, _2: number): number;
_ts_query_new(_0: number, _1: number, _2: number, _3: number, _4: number): number;
_ts_query_delete(_0: number): void;
_iswspace(_0: number): number;
_iswalnum(_0: number): number;
_ts_query_pattern_count(_0: number): number;
_ts_query_capture_count(_0: number): number;
_ts_query_string_count(_0: number): number;
_ts_query_capture_name_for_id(_0: number, _1: number, _2: number): number;
_ts_query_capture_quantifier_for_id(_0: number, _1: number, _2: number): number;
_ts_query_string_value_for_id(_0: number, _1: number, _2: number): number;
_ts_query_predicates_for_pattern(_0: number, _1: number, _2: number): number;
_ts_query_start_byte_for_pattern(_0: number, _1: number): number;
_ts_query_end_byte_for_pattern(_0: number, _1: number): number;
_ts_query_is_pattern_rooted(_0: number, _1: number): number;
_ts_query_is_pattern_non_local(_0: number, _1: number): number;
_ts_query_is_pattern_guaranteed_at_step(_0: number, _1: number): number;
_ts_query_disable_capture(_0: number, _1: number, _2: number): void;
_ts_query_disable_pattern(_0: number, _1: number): void;
_ts_tree_copy(_0: number): number;
_ts_tree_delete(_0: number): void;
_ts_init(): number;
_ts_parser_new_wasm(): void;
_ts_parser_enable_logger_wasm(_0: number, _1: number): void;
_ts_parser_parse_wasm(_0: number, _1: number, _2: number, _3: number, _4: number): number;
_ts_parser_included_ranges_wasm(_0: number): void;
_ts_language_type_is_named_wasm(_0: number, _1: number): number;
_ts_language_type_is_visible_wasm(_0: number, _1: number): number;
_ts_language_supertypes_wasm(_0: number): void;
_ts_language_subtypes_wasm(_0: number, _1: number): void;
_ts_tree_root_node_wasm(_0: number): void;
_ts_tree_root_node_with_offset_wasm(_0: number): void;
_ts_tree_edit_wasm(_0: number): void;
_ts_tree_included_ranges_wasm(_0: number): void;
_ts_tree_get_changed_ranges_wasm(_0: number, _1: number): void;
_ts_tree_cursor_new_wasm(_0: number): void;
_ts_tree_cursor_copy_wasm(_0: number): void;
_ts_tree_cursor_delete_wasm(_0: number): void;
_ts_tree_cursor_reset_wasm(_0: number): void;
_ts_tree_cursor_reset_to_wasm(_0: number, _1: number): void;
_ts_tree_cursor_goto_first_child_wasm(_0: number): number;
_ts_tree_cursor_goto_last_child_wasm(_0: number): number;
_ts_tree_cursor_goto_first_child_for_index_wasm(_0: number): number;
_ts_tree_cursor_goto_first_child_for_position_wasm(_0: number): number;
_ts_tree_cursor_goto_next_sibling_wasm(_0: number): number;
_ts_tree_cursor_goto_previous_sibling_wasm(_0: number): number;
_ts_tree_cursor_goto_descendant_wasm(_0: number, _1: number): void;
_ts_tree_cursor_goto_parent_wasm(_0: number): number;
_ts_tree_cursor_current_node_type_id_wasm(_0: number): number;
_ts_tree_cursor_current_node_state_id_wasm(_0: number): number;
_ts_tree_cursor_current_node_is_named_wasm(_0: number): number;
_ts_tree_cursor_current_node_is_missing_wasm(_0: number): number;
_ts_tree_cursor_current_node_id_wasm(_0: number): number;
_ts_tree_cursor_start_position_wasm(_0: number): void;
_ts_tree_cursor_end_position_wasm(_0: number): void;
_ts_tree_cursor_start_index_wasm(_0: number): number;
_ts_tree_cursor_end_index_wasm(_0: number): number;
_ts_tree_cursor_current_field_id_wasm(_0: number): number;
_ts_tree_cursor_current_depth_wasm(_0: number): number;
_ts_tree_cursor_current_descendant_index_wasm(_0: number): number;
_ts_tree_cursor_current_node_wasm(_0: number): void;
_ts_node_symbol_wasm(_0: number): number;
_ts_node_field_name_for_child_wasm(_0: number, _1: number): number;
_ts_node_field_name_for_named_child_wasm(_0: number, _1: number): number;
_ts_node_children_by_field_id_wasm(_0: number, _1: number): void;
_ts_node_first_child_for_byte_wasm(_0: number): void;
_ts_node_first_named_child_for_byte_wasm(_0: number): void;
_ts_node_grammar_symbol_wasm(_0: number): number;
_ts_node_child_count_wasm(_0: number): number;
_ts_node_named_child_count_wasm(_0: number): number;
_ts_node_child_wasm(_0: number, _1: number): void;
_ts_node_named_child_wasm(_0: number, _1: number): void;
_ts_node_child_by_field_id_wasm(_0: number, _1: number): void;
_ts_node_next_sibling_wasm(_0: number): void;
_ts_node_prev_sibling_wasm(_0: number): void;
_ts_node_next_named_sibling_wasm(_0: number): void;
_ts_node_prev_named_sibling_wasm(_0: number): void;
_ts_node_descendant_count_wasm(_0: number): number;
_ts_node_parent_wasm(_0: number): void;
_ts_node_descendant_for_index_wasm(_0: number): void;
_ts_node_named_descendant_for_index_wasm(_0: number): void;
_ts_node_descendant_for_position_wasm(_0: number): void;
_ts_node_named_descendant_for_position_wasm(_0: number): void;
_ts_node_start_point_wasm(_0: number): void;
_ts_node_end_point_wasm(_0: number): void;
_ts_node_start_index_wasm(_0: number): number;
_ts_node_end_index_wasm(_0: number): number;
_ts_node_to_string_wasm(_0: number): number;
_ts_node_children_wasm(_0: number): void;
_ts_node_named_children_wasm(_0: number): void;
_ts_node_descendants_of_type_wasm(_0: number, _1: number, _2: number, _3: number, _4: number, _5: number, _6: number): void;
_ts_node_is_named_wasm(_0: number): number;
_ts_node_has_changes_wasm(_0: number): number;
_ts_node_has_error_wasm(_0: number): number;
_ts_node_is_error_wasm(_0: number): number;
_ts_node_is_missing_wasm(_0: number): number;
_ts_node_is_extra_wasm(_0: number): number;
_ts_node_parse_state_wasm(_0: number): number;
_ts_node_next_parse_state_wasm(_0: number): number;
_ts_query_matches_wasm(_0: number, _1: number, _2: number, _3: number, _4: number, _5: number, _6: number, _7: number, _8: number, _9: number, _10: number): void;
_ts_query_captures_wasm(_0: number, _1: number, _2: number, _3: number, _4: number, _5: number, _6: number, _7: number, _8: number, _9: number, _10: number): void;
_iswalpha(_0: number): number;
_iswblank(_0: number): number;
_iswdigit(_0: number): number;
_iswlower(_0: number): number;
_iswupper(_0: number): number;
_iswxdigit(_0: number): number;
_memchr(_0: number, _1: number, _2: number): number;
_strlen(_0: number): number;
_strcmp(_0: number, _1: number): number;
_strncat(_0: number, _1: number, _2: number): number;
_strncpy(_0: number, _1: number, _2: number): number;
_towlower(_0: number): number;
_towupper(_0: number): number;
_orig$ts_parser_timeout_micros(_0: number): bigint;
_orig$ts_parser_set_timeout_micros(_0: number, _1: bigint): void;
}
export type MainModule = WasmModule & typeof RuntimeExports;
export default function MainModuleFactory (options?: EmscriptenModule): Promise<MainModule>;

View file

@ -50,12 +50,11 @@
"vitest": "^3.0.2"
},
"scripts": {
"build:ts": "esbuild src/index.ts --bundle --platform=neutral --format=cjs --global-name=TreeSitterImpl --outfile=dist/tree-sitter.js --external:fs/* --external:fs/promises --sourcemap --sources-content=true --keep-names",
"build:ts": "esbuild src/index.ts --bundle --format=esm --platform=node --global-name=TreeSitterImpl --outfile=tree-sitter.js --external:fs/* --external:fs/promises --sourcemap --sources-content=true --keep-names && cp lib/*wasm* .",
"build:wasm": "cd ../../ && cargo xtask build-wasm",
"build:wasm:debug": "cd ../../ && cargo xtask build-wasm --debug",
"build:sourcemap": "tsx script/build-sourcemap.ts",
"build": "npm run build:ts && npm run build:wasm && npm run build:sourcemap",
"build:debug": "npm run build:ts && npm run build:wasm:debug && cp debug/* . && npm run build:sourcemap",
"build": "npm run build:wasm && npm run build:ts",
"build:debug": "npm run build:wasm:debug && npm run build:ts && cp debug/* .",
"lint": "eslint src/*.ts script/*.ts",
"lint:fix": "eslint src/*.ts script/*.ts --fix",
"test": "vitest run",

View file

@ -1,64 +0,0 @@
import { readFileSync, writeFileSync } from 'fs';
import { SourceMapGenerator, SourceMapConsumer, RawSourceMap } from 'source-map';
async function fixSourceMap() {
const distMap = JSON.parse(readFileSync('dist/tree-sitter.js.map', 'utf8')) as RawSourceMap;
const distJs = readFileSync('dist/tree-sitter.js', 'utf8').split('\n');
const finalJs = readFileSync('tree-sitter.js', 'utf8').split('\n');
const lineMap = new Map<number, number>();
let currentFinalLine = 0;
for (let distLine = 0; distLine < distJs.length; distLine++) {
const line = distJs[distLine].trim();
if (!line) continue;
for (let finalLine = currentFinalLine; finalLine < finalJs.length; finalLine++) {
if (finalJs[finalLine].trim() === line) {
lineMap.set(distLine + 1, finalLine + 1);
currentFinalLine = finalLine;
break;
}
}
}
const consumer = await new SourceMapConsumer(distMap);
const generator = new SourceMapGenerator({
file: 'tree-sitter.js',
sourceRoot: '',
});
consumer.eachMapping(mapping => {
const finalLine = lineMap.get(mapping.generatedLine);
if (finalLine) {
generator.addMapping({
generated: {
line: finalLine,
column: mapping.generatedColumn,
},
original: {
line: mapping.originalLine,
column: mapping.originalColumn,
},
// Fix the source path to be relative to binding_web
source: `src/${mapping.source.split('/').pop()}`,
name: mapping.name,
});
}
});
for (const source of consumer.sources) {
const content = consumer.sourceContentFor(source);
if (content) {
generator.setSourceContent(
`src/${source.split('/').pop()}`,
content,
);
}
}
consumer.destroy();
writeFileSync('tree-sitter.js.map', generator.toString());
}
fixSourceMap().catch(console.error);

View file

@ -17,9 +17,9 @@ const inputFiles = [
'../src/tree.ts',
'../src/tree_cursor.ts',
'../dist/tree-sitter.js',
'../wasm/exports.txt',
'../wasm/imports.js',
'../wasm/prefix.js',
'../lib/exports.txt',
'../lib/imports.js',
'../lib/prefix.js',
...listFiles('../../include/tree_sitter'),
...listFiles('../../src'),
];

View file

@ -0,0 +1,14 @@
import createModule, { type MainModule } from '../lib/tree-sitter';
export let Module: MainModule | null = null;
export async function initializeBinding(moduleOptions: EmscriptenModule): Promise<MainModule> {
if (!Module) {
Module = await createModule(moduleOptions);
}
return Module;
}
export function checkModule(): boolean {
return !!Module;
}

View file

@ -1,4 +1,4 @@
import { CaptureQuantifier } from './query';
import { type MainModule } from '../lib/tree-sitter';
export interface Point {
row: number;
@ -56,183 +56,8 @@ export function isPoint(point?: Point): point is Point {
);
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
export const C: EmscriptenModule & {
// Global
_ts_init(): number;
export function setModule(module: MainModule) {
C = module;
}
// Libc
_malloc(size: number): number;
_calloc(count: number, size: number): number;
_free(ptr: number): void;
// Parser
_ts_parser_new_wasm(): void;
_ts_parser_delete(address: number): void;
_ts_parser_set_language(parserAddress: number, languageAddress: number): void;
_ts_parser_enable_logger_wasm(address: number, enabled: number): void;
_ts_parser_parse_wasm(
address: number,
payload: number,
oldTreeAddress: number,
rangeAddress: number,
rangeCount: number
): number;
_ts_parser_reset(address: number): void;
_ts_parser_timeout_micros(address: number): number;
_ts_parser_set_timeout_micros(address: number, timeout: number): void;
_ts_parser_included_ranges_wasm(address: number): void;
// Language
_ts_language_symbol_count(address: number): number;
_ts_language_symbol_name(address: number, index: number): number;
_ts_language_symbol_type(address: number, index: number): number;
_ts_language_field_count(address: number): number;
_ts_language_field_name_for_id(address: number, id: number): number;
_ts_language_name(address: number): number;
_ts_language_version(address: number): number;
_ts_language_state_count(address: number): number;
_ts_language_symbol_for_name(address: number, typeAddress: number, typeLength: number, named: boolean): number;
_ts_language_type_is_named_wasm(address: number, typeId: number): number;
_ts_language_type_is_visible_wasm(address: number, typeId: number): number;
_ts_language_next_state(address: number, stateId: number, typeId: number): number;
_ts_language_supertypes_wasm(address: number): void;
_ts_language_subtypes_wasm(address: number, supertype: number): void;
// Tree
_ts_tree_copy(tree: number): number;
_ts_tree_delete(tree: number): void;
_ts_tree_edit_wasm(tree: number): void;
_ts_tree_root_node_wasm(tree: number): void;
_ts_tree_root_node_with_offset_wasm(tree: number): void;
_ts_tree_get_changed_ranges_wasm(self: number, other: number): void;
_ts_tree_included_ranges_wasm(self: number): void;
// Node
_ts_node_symbol_wasm(tree: number): number;
_ts_node_grammar_symbol_wasm(tree: number): number;
_ts_node_end_point_wasm(tree: number): void;
_ts_node_end_index_wasm(tree: number): number;
_ts_node_parse_state_wasm(tree: number): number;
_ts_node_next_parse_state_wasm(tree: number): number;
_ts_node_is_named_wasm(tree: number): number;
_ts_node_has_error_wasm(tree: number): number;
_ts_node_has_changes_wasm(tree: number): number;
_ts_node_is_error_wasm(tree: number): number;
_ts_node_is_missing_wasm(tree: number): number;
_ts_node_is_extra_wasm(tree: number): number;
_ts_node_child_wasm(tree: number, index: number): void;
_ts_node_named_child_wasm(tree: number, index: number): void;
_ts_node_child_by_field_id_wasm(tree: number, fieldId: number): void;
_ts_node_field_name_for_child_wasm(tree: number, index: number): number;
_ts_node_field_name_for_named_child_wasm(tree: number, index: number): number;
_ts_node_children_by_field_id_wasm(tree: number, fieldId: number): void;
_ts_node_first_child_for_byte_wasm(tree: number): void;
_ts_node_first_named_child_for_byte_wasm(tree: number): void;
_ts_node_child_count_wasm(tree: number): number;
_ts_node_named_child_count_wasm(tree: number): number;
_ts_node_children_wasm(tree: number): void;
_ts_node_named_children_wasm(tree: number): void;
_ts_node_descendants_of_type_wasm(
tree: number,
symbolsAddress: number,
symbolCount: number,
startRow: number,
startColumn: number,
endRow: number,
endColumn: number
): void;
_ts_node_next_sibling_wasm(tree: number): void;
_ts_node_prev_sibling_wasm(tree: number): void;
_ts_node_next_named_sibling_wasm(tree: number): void;
_ts_node_prev_named_sibling_wasm(tree: number): void;
_ts_node_descendant_count_wasm(tree: number): number;
_ts_node_parent_wasm(tree: number): void;
_ts_node_descendant_for_index_wasm(tree: number): void;
_ts_node_named_descendant_for_index_wasm(tree: number): void;
_ts_node_descendant_for_position_wasm(tree: number): void;
_ts_node_named_descendant_for_position_wasm(tree: number): void;
_ts_tree_cursor_new_wasm(tree: number): void;
_ts_node_to_string_wasm(tree: number): number;
// TreeCursor
_ts_tree_cursor_copy_wasm(cursor: number): void;
_ts_tree_cursor_delete_wasm(cursor: number): void;
_ts_tree_cursor_reset_wasm(cursor: number): void;
_ts_tree_cursor_reset_to_wasm(cursor: number, other: number): void;
_ts_tree_cursor_current_node_type_id_wasm(cursor: number): number;
_ts_tree_cursor_current_node_state_id_wasm(cursor: number): number;
_ts_tree_cursor_current_node_id_wasm(cursor: number): number;
_ts_tree_cursor_current_node_is_named_wasm(cursor: number): number;
_ts_tree_cursor_current_node_is_missing_wasm(cursor: number): number;
_ts_tree_cursor_start_index_wasm(cursor: number): number;
_ts_tree_cursor_end_index_wasm(cursor: number): number;
_ts_tree_cursor_start_position_wasm(cursor: number): void;
_ts_tree_cursor_end_position_wasm(cursor: number): void;
_ts_tree_cursor_current_node_wasm(cursor: number): void;
_ts_tree_cursor_current_field_id_wasm(cursor: number): number;
_ts_tree_cursor_current_depth_wasm(cursor: number): number;
_ts_tree_cursor_current_descendant_index_wasm(cursor: number): number;
_ts_tree_cursor_goto_first_child_wasm(cursor: number): number;
_ts_tree_cursor_goto_last_child_wasm(cursor: number): number;
_ts_tree_cursor_goto_first_child_for_index_wasm(cursor: number): number;
_ts_tree_cursor_goto_first_child_for_position_wasm(cursor: number): number;
_ts_tree_cursor_goto_next_sibling_wasm(cursor: number): number;
_ts_tree_cursor_goto_previous_sibling_wasm(cursor: number): number;
_ts_tree_cursor_goto_descendant_wasm(cursor: number, index: number): void;
_ts_tree_cursor_goto_parent_wasm(cursor: number): number;
// Query
_ts_query_new(languageAddress: number, sourceAddress: number, sourceLength: number, errorOffset: number, errorType: number): number;
_ts_query_string_count(address: number): number;
_ts_query_capture_count(address: number): number;
_ts_query_pattern_count(address: number): number;
_ts_query_capture_name_for_id(address: number, id: number, buffer: number): number;
_ts_query_capture_quantifier_for_id(address: number, patternId: number, captureId: number): CaptureQuantifier;
_ts_query_string_value_for_id(address: number, id: number, buffer: number): number;
_ts_query_predicates_for_pattern(address: number, patternId: number, buffer: number): number;
_ts_query_delete(address: number): void;
_ts_query_matches_wasm(
address: number,
treeAddress: number,
startRow: number,
startColumn: number,
endRow: number,
endColumn: number,
startIndex: number,
endIndex: number,
matchLimit: number,
maxStartDepth: number,
timeoutMicros: number
): void;
_ts_query_captures_wasm(
address: number,
treeAddress: number,
startRow: number,
startColumn: number,
endRow: number,
endColumn: number,
startIndex: number,
endIndex: number,
matchLimit: number,
maxStartDepth: number,
timeoutMicros: number
): void;
_ts_query_disable_capture(address: number, nameAddress: number, nameLength: number): void;
_ts_query_disable_pattern(address: number, patternIndex: number): void;
_ts_query_start_byte_for_pattern(address: number, patternIndex: number): number;
_ts_query_end_byte_for_pattern(address: number, patternIndex: number): number;
_ts_query_is_pattern_non_local(address: number, patternIndex: number): number;
_ts_query_is_pattern_rooted(address: number, patternIndex: number): number;
_ts_query_is_pattern_guaranteed_at_step(address: number, patternIndex: number, stepIndex: number): number;
// LookaheadIterator
_ts_lookahead_iterator_new(address: number, stateId: number): number;
_ts_lookahead_iterator_current_symbol(address: number): number;
_ts_lookahead_iterator_delete(address: number): void;
_ts_lookahead_iterator_reset_state(address: number, stateId: number): boolean;
_ts_lookahead_iterator_reset(address: number, languageAddress: number, stateId: number): boolean;
_ts_lookahead_iterator_next(address: number): boolean;
// @ts-expect-error Module is defined after compilation
} = Module;
export let C: MainModule;

View file

@ -1,11 +1,9 @@
import { INTERNAL, Internal, assertInternal, SIZE_OF_INT, SIZE_OF_SHORT, C } from './constants';
import { C, INTERNAL, Internal, assertInternal, SIZE_OF_INT, SIZE_OF_SHORT } from './constants';
import { LookaheadIterator } from './lookahead_iterator';
import { Node } from './node';
import { TRANSFER_BUFFER } from './parser';
import { CaptureQuantifier, Predicate, PredicateStep, Properties, Query, TextPredicate } from './query';
declare const loadWebAssemblyModule: (bytes: Uint8Array, options: { loadAsync: boolean }) => Promise<Record<string, () => number>>;
const PREDICATE_STEP_TYPE_CAPTURE = 1;
const PREDICATE_STEP_TYPE_STRING = 2;
@ -23,14 +21,14 @@ export class Language {
this.types = new Array<string>(C._ts_language_symbol_count(this[0]));
for (let i = 0, n = this.types.length; i < n; i++) {
if (C._ts_language_symbol_type(this[0], i) < 2) {
this.types[i] = UTF8ToString(C._ts_language_symbol_name(this[0], i));
this.types[i] = C.UTF8ToString(C._ts_language_symbol_name(this[0], i));
}
}
this.fields = new Array<string>(C._ts_language_field_count(this[0]) + 1);
for (let i = 0, n = this.fields.length; i < n; i++) {
const fieldName = C._ts_language_field_name_for_id(this[0], i);
if (fieldName !== 0) {
this.fields[i] = UTF8ToString(fieldName);
this.fields[i] = C.UTF8ToString(fieldName);
} else {
this.fields[i] = null;
}
@ -40,7 +38,7 @@ export class Language {
get name(): string | null {
const ptr = C._ts_language_name(this[0]);
if (ptr === 0) return null;
return UTF8ToString(ptr);
return C.UTF8ToString(ptr);
}
get version(): number {
@ -65,10 +63,10 @@ export class Language {
}
idForNodeType(type: string, named: boolean): number | null {
const typeLength = lengthBytesUTF8(type);
const typeLength = C.lengthBytesUTF8(type);
const typeAddress = C._malloc(typeLength + 1);
stringToUTF8(type, typeAddress, typeLength + 1);
const result = C._ts_language_symbol_for_name(this[0], typeAddress, typeLength, named);
C.stringToUTF8(type, typeAddress, typeLength + 1);
const result = C._ts_language_symbol_for_name(this[0], typeAddress, typeLength, named ? 1 : 0);
C._free(typeAddress);
return result || null;
}
@ -79,7 +77,7 @@ export class Language {
nodeTypeForId(typeId: number): string | null {
const name = C._ts_language_symbol_name(this[0], typeId);
return name ? UTF8ToString(name) : null;
return name ? C.UTF8ToString(name) : null;
}
nodeTypeIsNamed(typeId: number): boolean {
@ -92,14 +90,14 @@ export class Language {
get supertypes(): number[] {
C._ts_language_supertypes_wasm(this[0]);
const count = getValue(TRANSFER_BUFFER, 'i32');
const buffer = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const count = C.getValue(TRANSFER_BUFFER, 'i32');
const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const result = new Array<number>(count);
if (count > 0) {
let address = buffer;
for (let i = 0; i < count; i++) {
result[i] = getValue(address, 'i16');
result[i] = C.getValue(address, 'i16');
address += SIZE_OF_SHORT;
}
}
@ -109,14 +107,14 @@ export class Language {
subtypes(supertype: number): number[] {
C._ts_language_subtypes_wasm(this[0], supertype);
const count = getValue(TRANSFER_BUFFER, 'i32');
const buffer = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const count = C.getValue(TRANSFER_BUFFER, 'i32');
const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const result = new Array<number>(count);
if (count > 0) {
let address = buffer;
for (let i = 0; i < count; i++) {
result[i] = getValue(address, 'i16');
result[i] = C.getValue(address, 'i16');
address += SIZE_OF_SHORT;
}
}
@ -135,9 +133,9 @@ export class Language {
}
query(source: string): Query {
const sourceLength = lengthBytesUTF8(source);
const sourceLength = C.lengthBytesUTF8(source);
const sourceAddress = C._malloc(sourceLength + 1);
stringToUTF8(source, sourceAddress, sourceLength + 1);
C.stringToUTF8(source, sourceAddress, sourceLength + 1);
const address = C._ts_query_new(
this[0],
sourceAddress,
@ -147,9 +145,9 @@ export class Language {
);
if (!address) {
const errorId = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const errorByte = getValue(TRANSFER_BUFFER, 'i32');
const errorIndex = UTF8ToString(sourceAddress, errorByte).length;
const errorId = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const errorByte = C.getValue(TRANSFER_BUFFER, 'i32');
const errorIndex = C.UTF8ToString(sourceAddress, errorByte).length;
const suffix = source.slice(errorIndex, errorIndex + 100).split('\n')[0];
let word = suffix.match(QUERY_WORD_REGEX)?.[0] ?? '';
let error: Error;
@ -195,15 +193,15 @@ export class Language {
i,
TRANSFER_BUFFER
);
const nameLength = getValue(TRANSFER_BUFFER, 'i32');
captureNames[i] = UTF8ToString(nameAddress, nameLength);
const nameLength = C.getValue(TRANSFER_BUFFER, 'i32');
captureNames[i] = C.UTF8ToString(nameAddress, nameLength);
}
for (let i = 0; i < patternCount; i++) {
const captureQuantifiersArray = new Array<CaptureQuantifier>(captureCount);
for (let j = 0; j < captureCount; j++) {
const quantifier = C._ts_query_capture_quantifier_for_id(address, i, j);
captureQuantifiersArray[j] = quantifier;
captureQuantifiersArray[j] = quantifier as CaptureQuantifier;
}
captureQuantifiers[i] = captureQuantifiersArray;
}
@ -214,8 +212,8 @@ export class Language {
i,
TRANSFER_BUFFER
);
const nameLength = getValue(TRANSFER_BUFFER, 'i32');
stringValues[i] = UTF8ToString(valueAddress, nameLength);
const nameLength = C.getValue(TRANSFER_BUFFER, 'i32');
stringValues[i] = C.UTF8ToString(valueAddress, nameLength);
}
const setProperties = new Array<Properties>(patternCount);
@ -230,7 +228,7 @@ export class Language {
i,
TRANSFER_BUFFER
);
const stepCount = getValue(TRANSFER_BUFFER, 'i32');
const stepCount = C.getValue(TRANSFER_BUFFER, 'i32');
predicates[i] = [];
textPredicates[i] = [];
@ -238,9 +236,9 @@ export class Language {
const steps: PredicateStep[] = [];
let stepAddress = predicatesAddress;
for (let j = 0; j < stepCount; j++) {
const stepType = getValue(stepAddress, 'i32');
const stepType = C.getValue(stepAddress, 'i32');
stepAddress += SIZE_OF_INT;
const stepValueId: number = getValue(stepAddress, 'i32');
const stepValueId: number = C.getValue(stepAddress, 'i32');
stepAddress += SIZE_OF_INT;
if (stepType === PREDICATE_STEP_TYPE_CAPTURE) {
@ -469,7 +467,7 @@ export class Language {
}
}
const mod = await loadWebAssemblyModule(await bytes, { loadAsync: true });
const mod = await C.loadWebAssemblyModule(await bytes, { loadAsync: true });
const symbolNames = Object.keys(mod);
const functionName = symbolNames.find((key) => LANGUAGE_FUNCTION_REGEX.test(key) &&
!key.includes('external_scanner_'));

View file

@ -1,4 +1,4 @@
import { Edit, INTERNAL, Point, Range, SIZE_OF_INT, SIZE_OF_NODE, SIZE_OF_POINT } from "./constants";
import { Edit, INTERNAL, Point, Range, SIZE_OF_INT, SIZE_OF_NODE, SIZE_OF_POINT, C } from "./constants";
import { Node } from "./node";
import { Tree } from "./tree";
import { Query } from "./query";
@ -7,7 +7,7 @@ import { TRANSFER_BUFFER } from "./parser";
export function unmarshalCaptures(query: Query, tree: Tree, address: number, result: {name: string, node: Node}[]) {
for (let i = 0, n = result.length; i < n; i++) {
const captureIndex = getValue(address, 'i32');
const captureIndex = C.getValue(address, 'i32');
address += SIZE_OF_INT;
const node = unmarshalNode(tree, address)!;
address += SIZE_OF_NODE;
@ -18,29 +18,29 @@ export function unmarshalCaptures(query: Query, tree: Tree, address: number, res
export function marshalNode(node: Node) {
let address = TRANSFER_BUFFER;
setValue(address, node.id, 'i32');
C.setValue(address, node.id, 'i32');
address += SIZE_OF_INT;
setValue(address, node.startIndex, 'i32');
C.setValue(address, node.startIndex, 'i32');
address += SIZE_OF_INT;
setValue(address, node.startPosition.row, 'i32');
C.setValue(address, node.startPosition.row, 'i32');
address += SIZE_OF_INT;
setValue(address, node.startPosition.column, 'i32');
C.setValue(address, node.startPosition.column, 'i32');
address += SIZE_OF_INT;
setValue(address, node[0], 'i32');
C.setValue(address, node[0], 'i32');
}
export function unmarshalNode(tree: Tree, address = TRANSFER_BUFFER): Node | null {
const id = getValue(address, 'i32');
const id = C.getValue(address, 'i32');
address += SIZE_OF_INT;
if (id === 0) return null;
const index = getValue(address, 'i32');
const index = C.getValue(address, 'i32');
address += SIZE_OF_INT;
const row = getValue(address, 'i32');
const row = C.getValue(address, 'i32');
address += SIZE_OF_INT;
const column = getValue(address, 'i32');
const column = C.getValue(address, 'i32');
address += SIZE_OF_INT;
const other = getValue(address, 'i32');
const other = C.getValue(address, 'i32');
const result = new Node(INTERNAL, {
id,
@ -54,28 +54,28 @@ export function unmarshalNode(tree: Tree, address = TRANSFER_BUFFER): Node | nul
}
export function marshalTreeCursor(cursor: TreeCursor, address = TRANSFER_BUFFER) {
setValue(address + 0 * SIZE_OF_INT, cursor[0], 'i32');
setValue(address + 1 * SIZE_OF_INT, cursor[1], 'i32');
setValue(address + 2 * SIZE_OF_INT, cursor[2], 'i32');
setValue(address + 3 * SIZE_OF_INT, cursor[3], 'i32');
C.setValue(address + 0 * SIZE_OF_INT, cursor[0], 'i32');
C.setValue(address + 1 * SIZE_OF_INT, cursor[1], 'i32');
C.setValue(address + 2 * SIZE_OF_INT, cursor[2], 'i32');
C.setValue(address + 3 * SIZE_OF_INT, cursor[3], 'i32');
}
export function unmarshalTreeCursor(cursor: TreeCursor) {
cursor[0] = getValue(TRANSFER_BUFFER + 0 * SIZE_OF_INT, 'i32');
cursor[1] = getValue(TRANSFER_BUFFER + 1 * SIZE_OF_INT, 'i32');
cursor[2] = getValue(TRANSFER_BUFFER + 2 * SIZE_OF_INT, 'i32');
cursor[3] = getValue(TRANSFER_BUFFER + 3 * SIZE_OF_INT, 'i32');
cursor[0] = C.getValue(TRANSFER_BUFFER + 0 * SIZE_OF_INT, 'i32');
cursor[1] = C.getValue(TRANSFER_BUFFER + 1 * SIZE_OF_INT, 'i32');
cursor[2] = C.getValue(TRANSFER_BUFFER + 2 * SIZE_OF_INT, 'i32');
cursor[3] = C.getValue(TRANSFER_BUFFER + 3 * SIZE_OF_INT, 'i32');
}
export function marshalPoint(address: number, point: Point): void {
setValue(address, point.row, 'i32');
setValue(address + SIZE_OF_INT, point.column, 'i32');
C.setValue(address, point.row, 'i32');
C.setValue(address + SIZE_OF_INT, point.column, 'i32');
}
export function unmarshalPoint(address: number): Point {
const result = {
row: getValue(address, 'i32') >>> 0,
column: getValue(address + SIZE_OF_INT, 'i32') >>> 0,
row: C.getValue(address, 'i32') >>> 0,
column: C.getValue(address + SIZE_OF_INT, 'i32') >>> 0,
};
return result;
}
@ -83,16 +83,16 @@ export function unmarshalPoint(address: number): Point {
export function marshalRange(address: number, range: Range): void {
marshalPoint(address, range.startPosition); address += SIZE_OF_POINT;
marshalPoint(address, range.endPosition); address += SIZE_OF_POINT;
setValue(address, range.startIndex, 'i32'); address += SIZE_OF_INT;
setValue(address, range.endIndex, 'i32'); address += SIZE_OF_INT;
C.setValue(address, range.startIndex, 'i32'); address += SIZE_OF_INT;
C.setValue(address, range.endIndex, 'i32'); address += SIZE_OF_INT;
}
export function unmarshalRange(address: number): Range {
const result = {} as Range;
result.startPosition = unmarshalPoint(address); address += SIZE_OF_POINT;
result.endPosition = unmarshalPoint(address); address += SIZE_OF_POINT;
result.startIndex = getValue(address, 'i32') >>> 0; address += SIZE_OF_INT;
result.endIndex = getValue(address, 'i32') >>> 0;
result.startIndex = C.getValue(address, 'i32') >>> 0; address += SIZE_OF_INT;
result.endIndex = C.getValue(address, 'i32') >>> 0;
return result;
}
@ -100,7 +100,7 @@ export function marshalEdit(edit: Edit, address = TRANSFER_BUFFER) {
marshalPoint(address, edit.startPosition); address += SIZE_OF_POINT;
marshalPoint(address, edit.oldEndPosition); address += SIZE_OF_POINT;
marshalPoint(address, edit.newEndPosition); address += SIZE_OF_POINT;
setValue(address, edit.startIndex, 'i32'); address += SIZE_OF_INT;
setValue(address, edit.oldEndIndex, 'i32'); address += SIZE_OF_INT;
setValue(address, edit.newEndIndex, 'i32'); address += SIZE_OF_INT;
C.setValue(address, edit.startIndex, 'i32'); address += SIZE_OF_INT;
C.setValue(address, edit.oldEndIndex, 'i32'); address += SIZE_OF_INT;
C.setValue(address, edit.newEndIndex, 'i32'); address += SIZE_OF_INT;
}

View file

@ -4,8 +4,6 @@ import { TreeCursor } from './tree_cursor';
import { marshalNode, marshalPoint, unmarshalNode, unmarshalPoint } from './marshal';
import { TRANSFER_BUFFER } from './parser';
declare const AsciiToString: (ptr: number) => string;
export class Node {
private [0]: number; // Internal handle for WASM
private _children?: (Node | null)[];
@ -145,14 +143,14 @@ export class Node {
marshalNode(this);
const address = C._ts_node_field_name_for_child_wasm(this.tree[0], index);
if (!address) return null;
return AsciiToString(address);
return C.AsciiToString(address);
}
fieldNameForNamedChild(index: number): string | null {
marshalNode(this);
const address = C._ts_node_field_name_for_named_child_wasm(this.tree[0], index);
if (!address) return null;
return AsciiToString(address);
return C.AsciiToString(address);
}
childrenForFieldName(fieldName: string): (Node | null)[] {
@ -164,8 +162,8 @@ export class Node {
childrenForFieldId(fieldId: number): (Node | null)[] {
marshalNode(this);
C._ts_node_children_by_field_id_wasm(this.tree[0], fieldId);
const count = getValue(TRANSFER_BUFFER, 'i32');
const buffer = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const count = C.getValue(TRANSFER_BUFFER, 'i32');
const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const result = new Array<Node | null>(count);
if (count > 0) {
@ -182,7 +180,7 @@ export class Node {
firstChildForIndex(index: number): Node | null {
marshalNode(this);
const address = TRANSFER_BUFFER + SIZE_OF_NODE;
setValue(address, index, 'i32');
C.setValue(address, index, 'i32');
C._ts_node_first_child_for_byte_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
@ -190,7 +188,7 @@ export class Node {
firstNamedChildForIndex(index: number): Node | null {
marshalNode(this);
const address = TRANSFER_BUFFER + SIZE_OF_NODE;
setValue(address, index, 'i32');
C.setValue(address, index, 'i32');
C._ts_node_first_named_child_for_byte_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
@ -225,8 +223,8 @@ export class Node {
if (!this._children) {
marshalNode(this);
C._ts_node_children_wasm(this.tree[0]);
const count = getValue(TRANSFER_BUFFER, 'i32');
const buffer = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const count = C.getValue(TRANSFER_BUFFER, 'i32');
const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
this._children = new Array<Node>(count);
if (count > 0) {
let address = buffer;
@ -244,8 +242,8 @@ export class Node {
if (!this._namedChildren) {
marshalNode(this);
C._ts_node_named_children_wasm(this.tree[0]);
const count = getValue(TRANSFER_BUFFER, 'i32');
const buffer = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const count = C.getValue(TRANSFER_BUFFER, 'i32');
const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
this._namedChildren = new Array<Node>(count);
if (count > 0) {
let address = buffer;
@ -278,7 +276,7 @@ export class Node {
// Copy the array of symbols to the WASM heap
const symbolsAddress = C._malloc(SIZE_OF_INT * symbols.length);
for (let i = 0, n = symbols.length; i < n; i++) {
setValue(symbolsAddress + i * SIZE_OF_INT, symbols[i], 'i32');
C.setValue(symbolsAddress + i * SIZE_OF_INT, symbols[i], 'i32');
}
// Call the C API to compute the descendants
@ -294,8 +292,8 @@ export class Node {
);
// Instantiate the nodes based on the data returned
const descendantCount = getValue(TRANSFER_BUFFER, 'i32');
const descendantAddress = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const descendantCount = C.getValue(TRANSFER_BUFFER, 'i32');
const descendantAddress = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const result = new Array<Node | null>(descendantCount);
if (descendantCount > 0) {
let address = descendantAddress;
@ -353,8 +351,8 @@ export class Node {
marshalNode(this);
const address = TRANSFER_BUFFER + SIZE_OF_NODE;
setValue(address, start, 'i32');
setValue(address + SIZE_OF_INT, end, 'i32');
C.setValue(address, start, 'i32');
C.setValue(address + SIZE_OF_INT, end, 'i32');
C._ts_node_descendant_for_index_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
@ -366,8 +364,8 @@ export class Node {
marshalNode(this);
const address = TRANSFER_BUFFER + SIZE_OF_NODE;
setValue(address, start, 'i32');
setValue(address + SIZE_OF_INT, end, 'i32');
C.setValue(address, start, 'i32');
C.setValue(address + SIZE_OF_INT, end, 'i32');
C._ts_node_named_descendant_for_index_wasm(this.tree[0]);
return unmarshalNode(this.tree);
}
@ -437,7 +435,7 @@ export class Node {
toString() {
marshalNode(this);
const address = C._ts_node_to_string_wasm(this.tree[0]);
const result = AsciiToString(address);
const result = C.AsciiToString(address);
C._free(address);
return result;
}

View file

@ -1,6 +1,7 @@
import { C, INTERNAL, Point, Range, SIZE_OF_INT, SIZE_OF_RANGE } from './constants';
import { C, INTERNAL, Point, Range, SIZE_OF_INT, SIZE_OF_RANGE, setModule } from './constants';
import { Language } from './language';
import { marshalRange, unmarshalRange } from './marshal';
import { checkModule, initializeBinding } from './bindings';
import { Tree } from './tree';
interface ParseOptions {
@ -17,29 +18,38 @@ export let TRANSFER_BUFFER: number;
let VERSION: number;
let MIN_COMPATIBLE_VERSION: number;
let currentParseCallback: ((index: number, position: Point) => string) | null = null;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let currentLogCallback: LogCallback = null;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let currentProgressCallback: ((percent: number) => void) | null = null;
// declare let currentParseCallback: ((index: number, position: Point) => string) | null;
// // eslint-disable-next-line @typescript-eslint/no-unused-vars
// declare let currentLogCallback: LogCallback;
// // eslint-disable-next-line @typescript-eslint/no-unused-vars
// declare let currentProgressCallback: ((percent: number) => void) | null;
export class ParserImpl {
export class Parser {
protected [0] = 0;
protected [1] = 0;
protected language: Language | null = null;
protected logCallback: LogCallback = null;
static Language: typeof Language;
static init() {
// This must always be called before creating a Parser.
static async init(moduleOptions: EmscriptenModule) {
setModule(await initializeBinding(moduleOptions));
TRANSFER_BUFFER = C._ts_init();
VERSION = getValue(TRANSFER_BUFFER, 'i32');
MIN_COMPATIBLE_VERSION = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
VERSION = C.getValue(TRANSFER_BUFFER, 'i32');
MIN_COMPATIBLE_VERSION = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
}
constructor() {
this.initialize();
}
initialize() {
if (!checkModule()) {
throw new Error("cannot construct a Parser before calling `init()`");
}
C._ts_parser_new_wasm();
this[0] = getValue(TRANSFER_BUFFER, 'i32');
this[1] = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
this[0] = C.getValue(TRANSFER_BUFFER, 'i32');
this[1] = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
}
delete() {
@ -82,24 +92,24 @@ export class ParserImpl {
options: ParseOptions = {}
): Tree {
if (typeof callback === 'string') {
currentParseCallback = (index: number) => callback.slice(index);
C.currentParseCallback = (index: number) => callback.slice(index);
} else if (typeof callback === 'function') {
currentParseCallback = callback;
C.currentParseCallback = callback;
} else {
throw new Error('Argument must be a string or a function');
}
if (options.progressCallback) {
currentProgressCallback = options.progressCallback;
C.currentProgressCallback = options.progressCallback;
} else {
currentProgressCallback = null;
C.currentProgressCallback = null;
}
if (this.logCallback) {
currentLogCallback = this.logCallback;
C.currentLogCallback = this.logCallback;
C._ts_parser_enable_logger_wasm(this[0], 1);
} else {
currentLogCallback = null;
C.currentLogCallback = null;
C._ts_parser_enable_logger_wasm(this[0], 0);
}
@ -124,9 +134,9 @@ export class ParserImpl {
);
if (!treeAddress) {
currentParseCallback = null;
currentLogCallback = null;
currentProgressCallback = null;
C.currentParseCallback = null;
C.currentLogCallback = null;
C.currentProgressCallback = null;
throw new Error('Parsing failed');
}
@ -134,10 +144,10 @@ export class ParserImpl {
throw new Error('Parser must have a language to parse');
}
const result = new Tree(INTERNAL, treeAddress, this.language, currentParseCallback);
currentParseCallback = null;
currentLogCallback = null;
currentProgressCallback = null;
const result = new Tree(INTERNAL, treeAddress, this.language, C.currentParseCallback);
C.currentParseCallback = null;
C.currentLogCallback = null;
C.currentProgressCallback = null;
return result;
}
@ -147,8 +157,8 @@ export class ParserImpl {
getIncludedRanges(): Range[] {
C._ts_parser_included_ranges_wasm(this[0]);
const count = getValue(TRANSFER_BUFFER, 'i32');
const buffer = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const count = C.getValue(TRANSFER_BUFFER, 'i32');
const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const result = new Array<Range>(count);
if (count > 0) {
@ -168,7 +178,7 @@ export class ParserImpl {
}
setTimeoutMicros(timeout: number): void {
C._ts_parser_set_timeout_micros(this[0], timeout);
C._ts_parser_set_timeout_micros(this[0], 0, timeout);
}
setLogger(callback: LogCallback): this {

View file

@ -3,8 +3,7 @@ import { Node } from './node';
import { marshalNode, unmarshalCaptures } from './marshal';
import { TRANSFER_BUFFER } from './parser';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
let currentQueryProgressCallback: ((percent: number) => void) | null = null;
// let currentQueryProgressCallback: ((percent: number) => void) | null = null;
interface QueryOptions {
startPosition?: Point;
@ -127,7 +126,7 @@ export class Query {
}
if (progressCallback) {
currentQueryProgressCallback = progressCallback;
C.currentQueryProgressCallback = progressCallback;
}
marshalNode(node);
@ -146,18 +145,18 @@ export class Query {
timeoutMicros,
);
const rawCount = getValue(TRANSFER_BUFFER, 'i32');
const startAddress = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const didExceedMatchLimit = getValue(TRANSFER_BUFFER + 2 * SIZE_OF_INT, 'i32');
const rawCount = C.getValue(TRANSFER_BUFFER, 'i32');
const startAddress = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const didExceedMatchLimit = C.getValue(TRANSFER_BUFFER + 2 * SIZE_OF_INT, 'i32');
const result = new Array<QueryMatch>(rawCount);
this.exceededMatchLimit = Boolean(didExceedMatchLimit);
let filteredCount = 0;
let address = startAddress;
for (let i = 0; i < rawCount; i++) {
const pattern = getValue(address, 'i32');
const pattern = C.getValue(address, 'i32');
address += SIZE_OF_INT;
const captureCount = getValue(address, 'i32');
const captureCount = C.getValue(address, 'i32');
address += SIZE_OF_INT;
const captures = new Array<Capture>(captureCount);
@ -177,7 +176,7 @@ export class Query {
result.length = filteredCount;
C._free(startAddress);
currentQueryProgressCallback = null;
C.currentQueryProgressCallback = null;
return result;
}
@ -211,7 +210,7 @@ export class Query {
}
if (progressCallback) {
currentQueryProgressCallback = progressCallback;
C.currentQueryProgressCallback = progressCallback;
}
marshalNode(node);
@ -230,20 +229,20 @@ export class Query {
timeoutMicros,
);
const count = getValue(TRANSFER_BUFFER, 'i32');
const startAddress = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const didExceedMatchLimit = getValue(TRANSFER_BUFFER + 2 * SIZE_OF_INT, 'i32');
const count = C.getValue(TRANSFER_BUFFER, 'i32');
const startAddress = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const didExceedMatchLimit = C.getValue(TRANSFER_BUFFER + 2 * SIZE_OF_INT, 'i32');
const result: Capture[] = [];
this.exceededMatchLimit = Boolean(didExceedMatchLimit);
const captures: Capture[] = [];
let address = startAddress;
for (let i = 0; i < count; i++) {
const pattern = getValue(address, 'i32');
const pattern = C.getValue(address, 'i32');
address += SIZE_OF_INT;
const captureCount = getValue(address, 'i32');
const captureCount = C.getValue(address, 'i32');
address += SIZE_OF_INT;
const captureIndex = getValue(address, 'i32');
const captureIndex = C.getValue(address, 'i32');
address += SIZE_OF_INT;
captures.length = captureCount;
@ -262,7 +261,7 @@ export class Query {
}
C._free(startAddress);
currentQueryProgressCallback = null;
C.currentQueryProgressCallback = null;
return result;
}
@ -271,9 +270,9 @@ export class Query {
}
disableCapture(captureName: string): void {
const captureNameLength = lengthBytesUTF8(captureName);
const captureNameLength = C.lengthBytesUTF8(captureName);
const captureNameAddress = C._malloc(captureNameLength + 1);
stringToUTF8(captureName, captureNameAddress, captureNameLength + 1);
C.stringToUTF8(captureName, captureNameAddress, captureNameLength + 1);
C._ts_query_disable_capture(this[0], captureNameAddress, captureNameLength);
C._free(captureNameAddress);
}
@ -317,11 +316,7 @@ export class Query {
return C._ts_query_is_pattern_rooted(this[0], patternIndex) === 1;
}
isPatternGuaranteedAtStep(patternIndex: number, stepIndex: number): boolean {
return C._ts_query_is_pattern_guaranteed_at_step(
this[0],
patternIndex,
stepIndex
) === 1;
isPatternGuaranteedAtStep(byteIndex: number): boolean {
return C._ts_query_is_pattern_guaranteed_at_step(this[0], byteIndex) === 1;
}
}

View file

@ -61,7 +61,7 @@ export class Tree {
rootNodeWithOffset(offsetBytes: number, offsetExtent: Point): Node {
const address = TRANSFER_BUFFER + SIZE_OF_NODE;
setValue(address, offsetBytes, 'i32');
C.setValue(address, offsetBytes, 'i32');
marshalPoint(address + SIZE_OF_INT, offsetExtent);
C._ts_tree_root_node_with_offset_wasm(this[0]);
return unmarshalNode(this)!;
@ -81,8 +81,8 @@ export class Tree {
}
C._ts_tree_get_changed_ranges_wasm(this[0], other[0]);
const count = getValue(TRANSFER_BUFFER, 'i32');
const buffer = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const count = C.getValue(TRANSFER_BUFFER, 'i32');
const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const result = new Array<Range>(count);
if (count > 0) {
@ -98,8 +98,8 @@ export class Tree {
getIncludedRanges(): Range[] {
C._ts_tree_included_ranges_wasm(this[0]);
const count = getValue(TRANSFER_BUFFER, 'i32');
const buffer = getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const count = C.getValue(TRANSFER_BUFFER, 'i32');
const buffer = C.getValue(TRANSFER_BUFFER + SIZE_OF_INT, 'i32');
const result = new Array<Range>(count);
if (count > 0) {

View file

@ -150,7 +150,7 @@ export class TreeCursor {
gotoFirstChildForIndex(goalIndex: number): boolean {
marshalTreeCursor(this);
setValue(TRANSFER_BUFFER + SIZE_OF_CURSOR, goalIndex, 'i32');
C.setValue(TRANSFER_BUFFER + SIZE_OF_CURSOR, goalIndex, 'i32');
const result = C._ts_tree_cursor_goto_first_child_for_index_wasm(this.tree[0]);
unmarshalTreeCursor(this);
return result === 1;

View file

@ -1,9 +1,11 @@
import type { default as ParserType } from 'web-tree-sitter';
import { type Parser as ParserType, type Language as LanguageType } from '../src';
import path from 'path';
// @ts-expect-error We're intentionally importing ../tree-sitter.js
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
const Parser: typeof ParserType = await import('..').then(m => m.default);
import { Parser as ParserImpl, Language as LanguageImpl } from '..';
const Parser = ParserImpl as typeof ParserType;
const Language = LanguageImpl as typeof LanguageType;
// https://github.com/tree-sitter/tree-sitter/blob/master/xtask/src/fetch.rs#L15
export type LanguageName = 'bash' | 'c' | 'cpp' | 'embedded-template' | 'go' | 'html' | 'java' | 'javascript' | 'jsdoc' | 'json' | 'php' | 'python' | 'ruby' | 'rust' | 'typescript' | 'tsx';
@ -15,12 +17,13 @@ function languageURL(name: LanguageName): string {
export default Parser.init().then(async () => ({
Parser,
Language,
languageURL,
C: await Parser.Language.load(languageURL('c')),
EmbeddedTemplate: await Parser.Language.load(languageURL('embedded-template')),
HTML: await Parser.Language.load(languageURL('html')),
JavaScript: await Parser.Language.load(languageURL('javascript')),
JSON: await Parser.Language.load(languageURL('json')),
Python: await Parser.Language.load(languageURL('python')),
Rust: await Parser.Language.load(languageURL('rust')),
C: await Language.load(languageURL('c')),
EmbeddedTemplate: await Language.load(languageURL('embedded-template')),
HTML: await Language.load(languageURL('html')),
JavaScript: await Language.load(languageURL('javascript')),
JSON: await Language.load(languageURL('json')),
Python: await Language.load(languageURL('python')),
Rust: await Language.load(languageURL('rust')),
}));

View file

@ -1,7 +1,8 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import helper from './helper';
import type { default as ParserType, LookaheadIterable, Language } from 'web-tree-sitter';
import type { Parser as ParserType, LookaheadIterator, Language } from '../src';
let Parser: typeof ParserType;
let JavaScript: Language;
let Rust: Language;
@ -35,8 +36,8 @@ describe('Language', () => {
describe('.idForNodeType, .nodeTypeForId, .nodeTypeIsNamed', () => {
it('converts between the string and integer representations of a node type', () => {
const exportStatementId = JavaScript.idForNodeType('export_statement', true);
const starId = JavaScript.idForNodeType('*', false);
const exportStatementId = JavaScript.idForNodeType('export_statement', true)!;
const starId = JavaScript.idForNodeType('*', false)!;
expect(exportStatementId).toBeLessThan(JavaScript.nodeTypeCount);
expect(starId).toBeLessThan(JavaScript.nodeTypeCount);
@ -131,12 +132,11 @@ describe('Language', () => {
});
describe('Lookahead iterator', () => {
let lookahead: LookaheadIterable;
let lookahead: LookaheadIterator;
let state: number;
beforeAll(async () => {
let Parser: typeof ParserType;
({ JavaScript, Parser } = await helper);
({ Parser, JavaScript } = await helper);
const parser = new Parser();
parser.setLanguage(JavaScript);
const tree = parser.parse('function fn() {}');

View file

@ -1,5 +1,5 @@
import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest';
import type { default as ParserType, Language, Tree, SyntaxNode } from 'web-tree-sitter';
import type { Parser as ParserType, Language, Tree, Node } from '../src';
import helper from './helper';
let Parser: typeof ParserType;
@ -19,8 +19,8 @@ const JSON_EXAMPLE = `
]
`;
function getAllNodes(tree: Tree): SyntaxNode[] {
const result: SyntaxNode[] = [];
function getAllNodes(tree: Tree): Node[] {
const result: Node[] = [];
let visitedChildren = false;
const cursor = tree.walk();
@ -55,15 +55,15 @@ describe('Node', () => {
afterEach(() => {
parser.delete();
tree!.delete();
tree?.delete();
});
describe('.children', () => {
it('returns an array of child nodes', () => {
tree = parser.parse('x10 + 1000');
expect(tree.rootNode.children).toHaveLength(1);
const sumNode = tree.rootNode.firstChild!.firstChild;
expect(sumNode!.children.map(child => child.type)).toEqual([
const sumNode = tree.rootNode.firstChild?.firstChild;
expect(sumNode?.children.map(child => child?.type)).toEqual([
'identifier',
'+',
'number'
@ -74,9 +74,9 @@ describe('Node', () => {
describe('.namedChildren', () => {
it('returns an array of named child nodes', () => {
tree = parser.parse('x10 + 1000');
const sumNode = tree.rootNode.firstChild!.firstChild;
const sumNode = tree.rootNode.firstChild?.firstChild;
expect(tree.rootNode.namedChildren).toHaveLength(1);
expect(sumNode!.namedChildren.map(child => child.type)).toEqual([
expect(sumNode?.namedChildren.map(child => child?.type)).toEqual([
'identifier',
'number'
]);
@ -98,11 +98,11 @@ describe('Node', () => {
tree = parser.parse(source);
const node = tree.rootNode.firstChild;
expect(node!.type).toBe('if_statement');
const alternatives = node!.childrenForFieldName('alternative');
const alternativeTexts = alternatives.map(n => {
const condition = n.childForFieldName('condition');
return source.slice(condition!.startIndex, condition!.endIndex);
expect(node?.type).toBe('if_statement');
const alternatives = node?.childrenForFieldName('alternative');
const alternativeTexts = alternatives?.map(n => {
const condition = n?.childForFieldName('condition');
return source.slice(condition?.startIndex, condition?.endIndex);
});
expect(alternativeTexts).toEqual(['two', 'three', 'four']);
});
@ -111,29 +111,29 @@ describe('Node', () => {
describe('.startIndex and .endIndex', () => {
it('returns the character index where the node starts/ends in the text', () => {
tree = parser.parse('a👍👎1 / b👎c👎');
const quotientNode = tree.rootNode.firstChild!.firstChild;
const quotientNode = tree.rootNode.firstChild?.firstChild;
expect(quotientNode!.startIndex).toBe(0);
expect(quotientNode!.endIndex).toBe(15);
expect(quotientNode!.children.map(child => child.startIndex)).toEqual([0, 7, 9]);
expect(quotientNode!.children.map(child => child.endIndex)).toEqual([6, 8, 15]);
expect(quotientNode?.startIndex).toBe(0);
expect(quotientNode?.endIndex).toBe(15);
expect(quotientNode?.children.map(child => child?.startIndex)).toEqual([0, 7, 9]);
expect(quotientNode?.children.map(child => child?.endIndex)).toEqual([6, 8, 15]);
});
});
describe('.startPosition and .endPosition', () => {
it('returns the row and column where the node starts/ends in the text', () => {
tree = parser.parse('x10 + 1000');
const sumNode = tree.rootNode.firstChild!.firstChild!;
expect(sumNode.type).toBe('binary_expression');
const sumNode = tree.rootNode.firstChild?.firstChild;
expect(sumNode?.type).toBe('binary_expression');
expect(sumNode.startPosition).toEqual({ row: 0, column: 0 });
expect(sumNode.endPosition).toEqual({ row: 0, column: 10 });
expect(sumNode.children.map((child) => child.startPosition)).toEqual([
expect(sumNode?.startPosition).toEqual({ row: 0, column: 0 });
expect(sumNode?.endPosition).toEqual({ row: 0, column: 10 });
expect(sumNode?.children.map((child) => child?.startPosition)).toEqual([
{ row: 0, column: 0 },
{ row: 0, column: 4 },
{ row: 0, column: 6 },
]);
expect(sumNode.children.map((child) => child.endPosition)).toEqual([
expect(sumNode?.children.map((child) => child?.endPosition)).toEqual([
{ row: 0, column: 3 },
{ row: 0, column: 5 },
{ row: 0, column: 10 },
@ -142,8 +142,8 @@ describe('Node', () => {
it('handles characters that occupy two UTF16 code units', () => {
tree = parser.parse('a👍👎1 /\n b👎c👎');
const sumNode = tree.rootNode.firstChild!.firstChild;
expect(sumNode!.children.map(child => [child.startPosition, child.endPosition])).toEqual([
const sumNode = tree.rootNode.firstChild?.firstChild;
expect(sumNode?.children.map(child => [child?.startPosition, child?.endPosition])).toEqual([
[{ row: 0, column: 0 }, { row: 0, column: 6 }],
[{ row: 0, column: 7 }, { row: 0, column: 8 }],
[{ row: 1, column: 1 }, { row: 1, column: 7 }]
@ -155,23 +155,23 @@ describe('Node', () => {
it('returns the node\'s parent', () => {
tree = parser.parse('x10 + 1000');
const sumNode = tree.rootNode.firstChild;
const variableNode = sumNode!.firstChild;
expect(sumNode!.id).not.toBe(variableNode!.id);
expect(sumNode!.id).toBe(variableNode!.parent!.id);
expect(tree.rootNode.id).toBe(sumNode!.parent!.id);
const variableNode = sumNode?.firstChild;
expect(sumNode?.id).not.toBe(variableNode?.id);
expect(sumNode?.id).toBe(variableNode?.parent?.id);
expect(tree.rootNode.id).toBe(sumNode?.parent?.id);
});
});
describe('.child(), .firstChild, .lastChild', () => {
it('returns null when the node has no children', () => {
tree = parser.parse('x10 + 1000');
const sumNode = tree.rootNode.firstChild!.firstChild;
const variableNode = sumNode!.firstChild;
expect(variableNode!.firstChild).toBeNull();
expect(variableNode!.lastChild).toBeNull();
expect(variableNode!.firstNamedChild).toBeNull();
expect(variableNode!.lastNamedChild).toBeNull();
expect(variableNode!.child(1)).toBeNull();
const sumNode = tree.rootNode.firstChild?.firstChild;
const variableNode = sumNode?.firstChild;
expect(variableNode?.firstChild).toBeNull();
expect(variableNode?.lastChild).toBeNull();
expect(variableNode?.firstNamedChild).toBeNull();
expect(variableNode?.lastNamedChild).toBeNull();
expect(variableNode?.child(1)).toBeNull();
});
});
@ -180,57 +180,57 @@ describe('Node', () => {
tree = parser.parse('class A { b() {} }');
const classNode = tree.rootNode.firstChild;
expect(classNode!.type).toBe('class_declaration');
expect(classNode?.type).toBe('class_declaration');
const classNameNode = classNode!.childForFieldName('name');
expect(classNameNode!.type).toBe('identifier');
expect(classNameNode!.text).toBe('A');
const classNameNode = classNode?.childForFieldName('name');
expect(classNameNode?.type).toBe('identifier');
expect(classNameNode?.text).toBe('A');
const bodyNode = classNode!.childForFieldName('body');
expect(bodyNode!.type).toBe('class_body');
expect(bodyNode!.text).toBe('{ b() {} }');
const bodyNode = classNode?.childForFieldName('body');
expect(bodyNode?.type).toBe('class_body');
expect(bodyNode?.text).toBe('{ b() {} }');
const methodNode = bodyNode!.firstNamedChild;
expect(methodNode!.type).toBe('method_definition');
expect(methodNode!.text).toBe('b() {}');
const methodNode = bodyNode?.firstNamedChild;
expect(methodNode?.type).toBe('method_definition');
expect(methodNode?.text).toBe('b() {}');
});
});
describe('.nextSibling and .previousSibling', () => {
it('returns the node\'s next and previous sibling', () => {
tree = parser.parse('x10 + 1000');
const sumNode = tree.rootNode.firstChild!.firstChild;
expect(sumNode!.children[1].id).toBe(sumNode!.children[0].nextSibling!.id);
expect(sumNode!.children[2].id).toBe(sumNode!.children[1].nextSibling!.id);
expect(sumNode!.children[0].id).toBe(sumNode!.children[1].previousSibling!.id);
expect(sumNode!.children[1].id).toBe(sumNode!.children[2].previousSibling!.id);
const sumNode = tree.rootNode.firstChild?.firstChild;
expect(sumNode?.children[1]?.id).toBe(sumNode?.children[0]?.nextSibling?.id);
expect(sumNode?.children[2]?.id).toBe(sumNode?.children[1]?.nextSibling?.id);
expect(sumNode?.children[0]?.id).toBe(sumNode?.children[1]?.previousSibling?.id);
expect(sumNode?.children[1]?.id).toBe(sumNode?.children[2]?.previousSibling?.id);
});
});
describe('.nextNamedSibling and .previousNamedSibling', () => {
it('returns the node\'s next and previous named sibling', () => {
tree = parser.parse('x10 + 1000');
const sumNode = tree.rootNode.firstChild!.firstChild;
expect(sumNode!.namedChildren[1].id).toBe(sumNode!.namedChildren[0].nextNamedSibling!.id);
expect(sumNode!.namedChildren[0].id).toBe(sumNode!.namedChildren[1].previousNamedSibling!.id);
const sumNode = tree.rootNode.firstChild?.firstChild;
expect(sumNode?.namedChildren[1]?.id).toBe(sumNode?.namedChildren[0]?.nextNamedSibling?.id);
expect(sumNode?.namedChildren[0]?.id).toBe(sumNode?.namedChildren[1]?.previousNamedSibling?.id);
});
});
describe('.descendantForIndex(min, max)', () => {
it('returns the smallest node that spans the given range', () => {
tree = parser.parse('x10 + 1000');
const sumNode = tree.rootNode.firstChild!.firstChild;
expect(sumNode!.descendantForIndex(1, 2).type).toBe('identifier');
expect(sumNode!.descendantForIndex(4, 4).type).toBe('+');
const sumNode = tree.rootNode.firstChild?.firstChild;
expect(sumNode?.descendantForIndex(1, 2)?.type).toBe('identifier');
expect(sumNode?.descendantForIndex(4, 4)?.type).toBe('+');
expect(() => {
// @ts-expect-error Testing invalid arguments
sumNode!.descendantForIndex(1, {});
sumNode.descendantForIndex(1, {});
}).toThrow('Arguments must be numbers');
expect(() => {
// @ts-expect-error Testing invalid arguments
sumNode!.descendantForIndex(undefined);
sumNode.descendantForIndex(undefined);
}).toThrow('Arguments must be numbers');
});
});
@ -238,36 +238,36 @@ describe('Node', () => {
describe('.namedDescendantForIndex', () => {
it('returns the smallest named node that spans the given range', () => {
tree = parser.parse('x10 + 1000');
const sumNode = tree.rootNode.firstChild;
expect(sumNode!.descendantForIndex(1, 2).type).toBe('identifier');
expect(sumNode!.descendantForIndex(4, 4).type).toBe('+');
const sumNode = tree.rootNode.firstChild!;
expect(sumNode.descendantForIndex(1, 2)?.type).toBe('identifier');
expect(sumNode.descendantForIndex(4, 4)?.type).toBe('+');
});
});
describe('.descendantForPosition', () => {
it('returns the smallest node that spans the given range', () => {
tree = parser.parse('x10 + 1000');
const sumNode = tree.rootNode.firstChild;
const sumNode = tree.rootNode.firstChild!;
expect(
sumNode!.descendantForPosition(
sumNode.descendantForPosition(
{ row: 0, column: 1 },
{ row: 0, column: 2 }
).type
)?.type
).toBe('identifier');
expect(
sumNode!.descendantForPosition({ row: 0, column: 4 }).type
sumNode.descendantForPosition({ row: 0, column: 4 })?.type
).toBe('+');
expect(() => {
// @ts-expect-error Testing invalid arguments
sumNode!.descendantForPosition(1, {});
sumNode.descendantForPosition(1, {});
}).toThrow('Arguments must be {row, column} objects');
expect(() => {
// @ts-expect-error Testing invalid arguments
sumNode!.descendantForPosition(undefined);
sumNode.descendantForPosition(undefined);
}).toThrow('Arguments must be {row, column} objects');
});
});
@ -281,11 +281,11 @@ describe('Node', () => {
sumNode.namedDescendantForPosition(
{ row: 0, column: 1 },
{ row: 0, column: 2 },
).type
)?.type
).toBe('identifier')
expect(
sumNode.namedDescendantForPosition({ row: 0, column: 4 }).type
sumNode.namedDescendantForPosition({ row: 0, column: 4 })?.type
).toBe('binary_expression');
});
});
@ -298,11 +298,11 @@ describe('Node', () => {
'(program (expression_statement (binary_expression left: (number) right: (binary_expression left: (number) (ERROR) right: (number)))))'
);
const sum = node.firstChild!.firstChild;
expect(sum!.hasError).toBe(true);
expect(sum!.children[0].hasError).toBe(false);
expect(sum!.children[1].hasError).toBe(false);
expect(sum!.children[2].hasError).toBe(true);
const sum = node.firstChild?.firstChild;
expect(sum?.hasError).toBe(true);
expect(sum?.children[0]?.hasError).toBe(false);
expect(sum?.children[1]?.hasError).toBe(false);
expect(sum?.children[2]?.hasError).toBe(true);
});
});
@ -314,12 +314,12 @@ describe('Node', () => {
'(program (expression_statement (binary_expression left: (number) (ERROR) right: (number))))'
);
const multi = node.firstChild!.firstChild;
expect(multi!.hasError).toBe(true);
expect(multi!.children[0].isError).toBe(false);
expect(multi!.children[1].isError).toBe(false);
expect(multi!.children[2].isError).toBe(true);
expect(multi!.children[3].isError).toBe(false);
const multi = node.firstChild?.firstChild;
expect(multi?.hasError).toBe(true);
expect(multi?.children[0]?.isError).toBe(false);
expect(multi?.children[1]?.isError).toBe(false);
expect(multi?.children[2]?.isError).toBe(true);
expect(multi?.children[3]?.isError).toBe(false);
});
});
@ -331,12 +331,12 @@ describe('Node', () => {
'(program (expression_statement (parenthesized_expression (binary_expression left: (number) right: (MISSING identifier)))))'
);
const sum = node.firstChild!.firstChild!.firstNamedChild;
expect(sum!.type).toBe('binary_expression');
expect(sum!.hasError).toBe(true);
expect(sum!.children[0].isMissing).toBe(false);
expect(sum!.children[1].isMissing).toBe(false);
expect(sum!.children[2].isMissing).toBe(true);
const sum = node.firstChild?.firstChild?.firstNamedChild;
expect(sum?.type).toBe('binary_expression');
expect(sum?.hasError).toBe(true);
expect(sum?.children[0]?.isMissing).toBe(false);
expect(sum?.children[1]?.isMissing).toBe(false);
expect(sum?.children[2]?.isMissing).toBe(true);
});
});
@ -344,7 +344,7 @@ describe('Node', () => {
it('returns true if the node is an extra node like comments', () => {
tree = parser.parse('foo(/* hi */);');
const node = tree.rootNode;
const commentNode = node.descendantForIndex(7, 7);
const commentNode = node.descendantForIndex(7, 7)!;
expect(node.type).toBe('program');
expect(commentNode.type).toBe('comment');
@ -363,14 +363,14 @@ describe('Node', () => {
it(`returns the text of a node generated by ${method}`, () => {
const [numeratorSrc, denominatorSrc] = text.split(/\s*\/\s+/);
tree = parser.parse(_parse);
const quotientNode = tree.rootNode.firstChild!.firstChild!;
const [numerator, slash, denominator] = quotientNode.children;
const quotientNode = tree.rootNode.firstChild?.firstChild;
const [numerator, slash, denominator] = quotientNode!.children;
expect(tree.rootNode.text).toBe(text);
expect(denominator.text).toBe(denominatorSrc);
expect(quotientNode.text).toBe(text);
expect(numerator.text).toBe(numeratorSrc);
expect(slash.text).toBe('/');
expect(denominator?.text).toBe(denominatorSrc);
expect(quotientNode?.text).toBe(text);
expect(numerator?.text).toBe(numeratorSrc);
expect(slash?.text).toBe('/');
});
});
});
@ -427,12 +427,12 @@ describe('Node', () => {
expect(node.startPosition).toEqual({ row: 2, column: 4 });
expect(node.endPosition).toEqual({ row: 2, column: 12 });
let child = node.firstChild!.child(2);
expect(child!.type).toBe('expression_statement');
expect(child!.startIndex).toBe(15);
expect(child!.endIndex).toBe(16);
expect(child!.startPosition).toEqual({ row: 2, column: 11 });
expect(child!.endPosition).toEqual({ row: 2, column: 12 });
let child = node.firstChild?.child(2);
expect(child?.type).toBe('expression_statement');
expect(child?.startIndex).toBe(15);
expect(child?.endIndex).toBe(16);
expect(child?.startPosition).toEqual({ row: 2, column: 11 });
expect(child?.endPosition).toEqual({ row: 2, column: 12 });
const cursor = node.walk();
cursor.gotoFirstChild();
@ -452,23 +452,23 @@ describe('Node', () => {
it('returns node parse state ids', () => {
tree = parser.parse(text);
const quotientNode = tree.rootNode.firstChild!.firstChild;
const quotientNode = tree.rootNode.firstChild?.firstChild;
const [numerator, slash, denominator] = quotientNode!.children;
expect(tree.rootNode.parseState).toBe(0);
// parse states will change on any change to the grammar so test that it
// returns something instead
expect(numerator.parseState).toBeGreaterThan(0);
expect(slash.parseState).toBeGreaterThan(0);
expect(denominator.parseState).toBeGreaterThan(0);
expect(numerator?.parseState).toBeGreaterThan(0);
expect(slash?.parseState).toBeGreaterThan(0);
expect(denominator?.parseState).toBeGreaterThan(0);
});
it('returns next parse state equal to the language', () => {
tree = parser.parse(text);
const quotientNode = tree.rootNode.firstChild!.firstChild;
quotientNode!.children.forEach((node) => {
expect(node.nextParseState).toBe(
JavaScript.nextState(node.parseState, node.grammarId)
const quotientNode = tree.rootNode.firstChild?.firstChild;
quotientNode?.children.forEach((node) => {
expect(node?.nextParseState).toBe(
JavaScript.nextState(node!.parseState, node!.grammarId)
);
});
});
@ -477,11 +477,11 @@ describe('Node', () => {
describe('.descendantsOfType', () => {
it('finds all descendants of a given type in the given range', () => {
tree = parser.parse('a + 1 * b * 2 + c + 3');
const outerSum = tree.rootNode.firstChild!.firstChild;
const outerSum = tree.rootNode.firstChild?.firstChild;
const descendants = outerSum!.descendantsOfType('number', { row: 0, column: 2 }, { row: 0, column: 15 });
expect(descendants.map(node => node.startIndex)).toEqual([4, 12]);
expect(descendants.map(node => node.endPosition)).toEqual([
const descendants = outerSum?.descendantsOfType('number', { row: 0, column: 2 }, { row: 0, column: 15 }) ?? [];
expect(descendants.map(node => node?.startIndex)).toEqual([4, 12]);
expect(descendants.map(node => node?.endPosition)).toEqual([
{ row: 0, column: 5 },
{ row: 0, column: 13 },
]);
@ -493,23 +493,23 @@ describe('Node', () => {
describe('.firstChildForIndex(index)', () => {
it('returns the first child that contains or starts after the given index', () => {
tree = parser.parse('x10 + 1000');
const sumNode = tree.rootNode.firstChild!.firstChild;
const sumNode = tree.rootNode.firstChild?.firstChild;
expect(sumNode!.firstChildForIndex(0)!.type).toBe('identifier');
expect(sumNode!.firstChildForIndex(1)!.type).toBe('identifier');
expect(sumNode!.firstChildForIndex(3)!.type).toBe('+');
expect(sumNode!.firstChildForIndex(5)!.type).toBe('number');
expect(sumNode?.firstChildForIndex(0)?.type).toBe('identifier');
expect(sumNode?.firstChildForIndex(1)?.type).toBe('identifier');
expect(sumNode?.firstChildForIndex(3)?.type).toBe('+');
expect(sumNode?.firstChildForIndex(5)?.type).toBe('number');
});
});
describe('.firstNamedChildForIndex(index)', () => {
it('returns the first child that contains or starts after the given index', () => {
tree = parser.parse('x10 + 1000');
const sumNode = tree.rootNode.firstChild!.firstChild;
const sumNode = tree.rootNode.firstChild?.firstChild;
expect(sumNode!.firstNamedChildForIndex(0)!.type).toBe('identifier');
expect(sumNode!.firstNamedChildForIndex(1)!.type).toBe('identifier');
expect(sumNode!.firstNamedChildForIndex(3)!.type).toBe('number');
expect(sumNode?.firstNamedChildForIndex(0)?.type).toBe('identifier');
expect(sumNode?.firstNamedChildForIndex(1)?.type).toBe('identifier');
expect(sumNode?.firstNamedChildForIndex(3)?.type).toBe('number');
});
});
@ -517,19 +517,19 @@ describe('Node', () => {
it('returns true if the nodes are the same', () => {
tree = parser.parse('1 + 2');
const sumNode = tree.rootNode.firstChild!.firstChild;
const node1 = sumNode!.firstChild;
const node2 = sumNode!.firstChild;
expect(node1!.equals(node2!)).toBe(true);
const sumNode = tree.rootNode.firstChild?.firstChild;
const node1 = sumNode?.firstChild;
const node2 = sumNode?.firstChild;
expect(node1?.equals(node2!)).toBe(true);
});
it('returns false if the nodes are not the same', () => {
tree = parser.parse('1 + 2');
const sumNode = tree.rootNode.firstChild!.firstChild;
const node1 = sumNode!.firstChild;
const node2 = node1!.nextSibling;
expect(node1!.equals(node2!)).toBe(false);
const sumNode = tree.rootNode.firstChild?.firstChild;
const node1 = sumNode?.firstChild;
const node2 = node1?.nextSibling;
expect(node1?.equals(node2!)).toBe(false);
});
});
@ -551,13 +551,13 @@ describe('Node', () => {
// right: (identifier) 3
// -------------------
expect(binaryExpressionNode!.fieldNameForChild(0)).toBe('left');
expect(binaryExpressionNode!.fieldNameForChild(1)).toBe('operator');
expect(binaryExpressionNode?.fieldNameForChild(0)).toBe('left');
expect(binaryExpressionNode?.fieldNameForChild(1)).toBe('operator');
// The comment should not have a field name, as it's just an extra
expect(binaryExpressionNode!.fieldNameForChild(2)).toBeNull();
expect(binaryExpressionNode!.fieldNameForChild(3)).toBe('right');
expect(binaryExpressionNode?.fieldNameForChild(2)).toBeNull();
expect(binaryExpressionNode?.fieldNameForChild(3)).toBe('right');
// Negative test - Not a valid child index
expect(binaryExpressionNode!.fieldNameForChild(4)).toBeNull();
expect(binaryExpressionNode?.fieldNameForChild(4)).toBeNull();
});
});
@ -579,13 +579,13 @@ describe('Node', () => {
// right: (identifier) 2
// -------------------
expect(binaryExpressionNode!.fieldNameForNamedChild(0)).toBe('left');
expect(binaryExpressionNode?.fieldNameForNamedChild(0)).toBe('left');
// The comment should not have a field name, as it's just an extra
expect(binaryExpressionNode!.fieldNameForNamedChild(1)).toBeNull();
expect(binaryExpressionNode?.fieldNameForNamedChild(1)).toBeNull();
// The operator is not a named child, so the named child at index 2 is the right child
expect(binaryExpressionNode!.fieldNameForNamedChild(2)).toBe('right');
expect(binaryExpressionNode?.fieldNameForNamedChild(2)).toBe('right');
// Negative test - Not a valid child index
expect(binaryExpressionNode!.fieldNameForNamedChild(3)).toBeNull();
expect(binaryExpressionNode?.fieldNameForNamedChild(3)).toBeNull();
});
});
});

View file

@ -1,18 +1,19 @@
import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest';
import helper, { type LanguageName } from './helper';
import type { default as ParserType, Language } from 'web-tree-sitter';
import type { ParseState, Tree, Parser as ParserType, Language as LanguageType } from '../src';
let Parser: typeof ParserType;
let JavaScript: Language;
let HTML: Language;
let JSON: Language;
let Language: typeof LanguageType;
let JavaScript: LanguageType;
let HTML: LanguageType;
let JSON: LanguageType;
let languageURL: (name: LanguageName) => string;
describe('Parser', () => {
let parser: ParserType;
beforeAll(async () => {
({ Parser, JavaScript, HTML, JSON, languageURL } = await helper);
({ Parser, Language, JavaScript, HTML, JSON, languageURL } = await helper);
});
beforeEach(() => {
@ -25,7 +26,7 @@ describe('Parser', () => {
describe('.setLanguage', () => {
it('allows setting the language to null', () => {
expect(parser.getLanguage()).toBeUndefined();
expect(parser.getLanguage()).toBeNull();
parser.setLanguage(JavaScript);
expect(parser.getLanguage()).toBe(JavaScript);
parser.setLanguage(null);
@ -134,7 +135,7 @@ describe('Parser', () => {
const templateStringNode = jsTree.rootNode.descendantForIndex(
sourceCode.indexOf('`<'),
sourceCode.indexOf('>`')
);
)!;
expect(templateStringNode.type).toBe('template_string');
const openQuoteNode = templateStringNode.child(0)!;
@ -222,7 +223,7 @@ describe('Parser', () => {
});
describe('.parse', () => {
let tree: ParserType.Tree | null;
let tree: Tree | null;
beforeEach(() => {
tree = null;
@ -266,7 +267,7 @@ describe('Parser', () => {
});
it('can use the bash parser', { timeout: 5000 }, async () => {
parser.setLanguage(await Parser.Language.load(languageURL('bash')));
parser.setLanguage(await Language.load(languageURL('bash')));
tree = parser.parse('FOO=bar echo <<EOF 2> err.txt > hello.txt \nhello${FOO}\nEOF');
expect(tree.rootNode.toString()).toBe(
'(program ' +
@ -283,7 +284,7 @@ describe('Parser', () => {
});
it('can use the c++ parser', { timeout: 5000 }, async () => {
parser.setLanguage(await Parser.Language.load(languageURL('cpp')));
parser.setLanguage(await Language.load(languageURL('cpp')));
tree = parser.parse('const char *s = R"EOF(HELLO WORLD)EOF";');
expect(tree.rootNode.toString()).toBe(
'(translation_unit (declaration ' +
@ -296,7 +297,7 @@ describe('Parser', () => {
});
it('can use the HTML parser', { timeout: 5000 }, async () => {
parser.setLanguage(await Parser.Language.load(languageURL('html')));
parser.setLanguage(await Language.load(languageURL('html')));
tree = parser.parse('<div><span><custom></custom></span></div>');
expect(tree.rootNode.toString()).toBe(
'(document (element (start_tag (tag_name)) (element (start_tag (tag_name)) ' +
@ -305,7 +306,7 @@ describe('Parser', () => {
});
it('can use the python parser', { timeout: 5000 }, async () => {
parser.setLanguage(await Parser.Language.load(languageURL('python')));
parser.setLanguage(await Language.load(languageURL('python')));
tree = parser.parse('class A:\n def b():\n c()');
expect(tree.rootNode.toString()).toBe(
'(module (class_definition ' +
@ -321,7 +322,7 @@ describe('Parser', () => {
});
it('can use the rust parser', { timeout: 5000 }, async () => {
parser.setLanguage(await Parser.Language.load(languageURL('rust')));
parser.setLanguage(await Language.load(languageURL('rust')));
tree = parser.parse('const x: &\'static str = r###"hello"###;');
expect(tree.rootNode.toString()).toBe(
'(source_file (const_item ' +
@ -332,7 +333,7 @@ describe('Parser', () => {
});
it('can use the typescript parser', { timeout: 5000 }, async () => {
parser.setLanguage(await Parser.Language.load(languageURL('typescript')));
parser.setLanguage(await Language.load(languageURL('typescript')));
tree = parser.parse('a()\nb()\n[c]');
expect(tree.rootNode.toString()).toBe(
'(program ' +
@ -346,7 +347,7 @@ describe('Parser', () => {
});
it('can use the tsx parser', { timeout: 5000 }, async () => {
parser.setLanguage(await Parser.Language.load(languageURL('tsx')));
parser.setLanguage(await Language.load(languageURL('tsx')));
tree = parser.parse('a()\nb()\n[c]');
expect(tree.rootNode.toString()).toBe(
'(program ' +
@ -397,7 +398,7 @@ describe('Parser', () => {
const startTime = performance.now();
let currentByteOffset = 0;
const progressCallback = (state: ParserType.State) => {
const progressCallback = (state: ParseState) => {
expect(state.currentOffset).toBeGreaterThanOrEqual(currentByteOffset);
currentByteOffset = state.currentOffset;

View file

@ -1,5 +1,5 @@
import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest';
import type { default as ParserType, Language, Tree, Query, QueryCapture, QueryMatch } from 'web-tree-sitter';
import type { Parser as ParserType, Language, Tree, Query, QueryMatch, QueryCapture } from '../src';
import helper from './helper';
let Parser: typeof ParserType;
@ -576,12 +576,11 @@ function formatMatches(matches: QueryMatch[]): QueryMatch[] {
}));
}
function formatCaptures(captures: QueryCapture[]): QueryCapture[] {
function formatCaptures(captures: QueryCapture[]): (QueryCapture & { text: string })[] {
return captures.map((c) => {
const node = c.node;
// @ts-expect-error We're not interested in the node object for these tests
delete c.node;
c.text = node.text;
return c;
return { ...c, text: node.text };
});
}

View file

@ -1,5 +1,5 @@
import { describe, it, expect, beforeAll, beforeEach, afterEach } from 'vitest';
import type { default as ParserType, Language, Tree, TreeCursor, Edit, Point } from 'web-tree-sitter';
import type { Parser as ParserType, Point, Language, Tree, Edit, TreeCursor } from '../src';
import helper from './helper';
let Parser: typeof ParserType;

View file

@ -1,19 +0,0 @@
var TreeSitter = function() {
var initPromise;
var document = typeof window == 'object'
? {currentScript: window.document.currentScript}
: null;
class Parser {
constructor() {
this.initialize();
}
initialize() {
throw new Error("cannot construct a Parser before calling `init()`");
}
static init(moduleOptions) {
if (initPromise) return initPromise;
Module = Object.assign({}, Module, moduleOptions);
return initPromise = new Promise((resolveInitPromise) => {

View file

@ -1,23 +0,0 @@
for (const name of Object.getOwnPropertyNames(ParserImpl.prototype)) {
Object.defineProperty(Parser.prototype, name, {
value: ParserImpl.prototype[name],
enumerable: false,
writable: false,
})
}
Parser.Language = Language;
Module.onRuntimeInitialized = () => {
ParserImpl.init();
resolveInitPromise();
};
});
}
}
return Parser;
}();
if (typeof exports === 'object') {
module.exports = TreeSitter;
}

View file

@ -24,19 +24,18 @@ enum EmccSource {
Podman,
}
pub fn run_wasm(args: &BuildWasm) -> Result<()> {
let npm = if cfg!(target_os = "windows") {
"npm.cmd"
} else {
"npm"
};
let npm = Command::new(npm)
.current_dir("lib/binding_web")
.args(["run", "build:ts"])
.output()
.expect("Failed to run npm run build:ts");
bail_on_err(&npm, "Failed to run npm run build:ts")?;
const EXPORTED_RUNTIME_METHODS: [&str; 8] = [
"AsciiToString",
"stringToUTF8",
"UTF8ToString",
"lengthBytesUTF8",
"stringToUTF16",
"loadWebAssemblyModule",
"getValue",
"setValue",
];
pub fn run_wasm(args: &BuildWasm) -> Result<()> {
let mut emscripten_flags = vec!["-O3", "--minify", "0"];
if args.debug {
@ -118,7 +117,7 @@ pub fn run_wasm(args: &BuildWasm) -> Result<()> {
let exported_functions = format!(
"{}{}",
fs::read_to_string("lib/src/wasm/stdlib-symbols.txt")?,
fs::read_to_string("lib/binding_web/wasm/exports.txt")?
fs::read_to_string("lib/binding_web/lib/exports.txt")?
)
.replace('"', "")
.lines()
@ -130,7 +129,10 @@ pub fn run_wasm(args: &BuildWasm) -> Result<()> {
.to_string();
let exported_functions = format!("EXPORTED_FUNCTIONS={exported_functions}");
let exported_runtime_methods = "EXPORTED_RUNTIME_METHODS=stringToUTF16,AsciiToString";
let exported_runtime_methods = format!(
"EXPORTED_RUNTIME_METHODS={}",
EXPORTED_RUNTIME_METHODS.join(",")
);
std::env::set_var("EMCC_DEBUG_SAVE", "1");
@ -138,7 +140,11 @@ pub fn run_wasm(args: &BuildWasm) -> Result<()> {
emscripten_flags.extend([
"-gsource-map",
"--source-map-base", ".",
"-fno-exceptions",
"-std=c11",
"-s", "WASM=1",
"-s", "EXPORT_ES6",
"-s", "MODULARIZE=1",
"-s", "INITIAL_MEMORY=33554432",
"-s", "ALLOW_MEMORY_GROWTH=1",
"-s", "SUPPORT_BIG_ENDIAN=1",
@ -147,60 +153,40 @@ pub fn run_wasm(args: &BuildWasm) -> Result<()> {
"-s", "NODEJS_CATCH_EXIT=0",
"-s", "NODEJS_CATCH_REJECTION=0",
"-s", &exported_functions,
"-s", exported_runtime_methods,
"-fno-exceptions",
"-std=c11",
"-s", &exported_runtime_methods,
"-D", "fprintf(...)=",
"-D", "NDEBUG=",
"-D", "_POSIX_C_SOURCE=200112L",
"-D", "_DEFAULT_SOURCE=",
"-I", "lib/src",
"-I", "lib/include",
"--js-library", "lib/binding_web/wasm/imports.js",
"--pre-js", "lib/binding_web/wasm/prefix.js",
"--post-js", "lib/binding_web/dist/tree-sitter.js",
"--post-js", "lib/binding_web/wasm/suffix.js",
"-o", "target/scratch/tree-sitter.js",
"--js-library", "lib/binding_web/lib/imports.js",
"--pre-js", "lib/binding_web/lib/prefix.js",
"-o", "lib/binding_web/lib/tree-sitter.js",
"lib/src/lib.c",
"lib/binding_web/lib/tree-sitter.c",
]);
if args.emit_tsd {
emscripten_flags.extend(["--emit-tsd", "tree-sitter.d.ts"]);
}
let command = command.args(&emscripten_flags);
if args.watch {
watch_wasm!(|| build_wasm(command, args.debug));
watch_wasm!(|| build_wasm(command));
} else {
build_wasm(command, args.debug)?;
build_wasm(command)?;
}
Ok(())
}
fn build_wasm(cmd: &mut Command, debug: bool) -> Result<()> {
fn build_wasm(cmd: &mut Command) -> Result<()> {
bail_on_err(
&cmd.spawn()?.wait_with_output()?,
"Failed to compile the Tree-sitter WASM library",
)?;
let dir = if debug {
PathBuf::from("lib/binding_web/debug")
} else {
PathBuf::from("lib/binding_web")
};
fs::create_dir_all(&dir)?;
fs::rename("target/scratch/tree-sitter.js", dir.join("tree-sitter.js"))?;
fs::rename(
"target/scratch/tree-sitter.wasm",
dir.join("tree-sitter.wasm"),
)?;
fs::rename(
"target/scratch/tree-sitter.wasm.map",
dir.join("tree-sitter.wasm.map"),
)?;
Ok(())
}

View file

@ -60,7 +60,7 @@ pub fn run(args: &CheckWasmExports) -> Result<()> {
}
fn check_wasm_exports() -> Result<()> {
let mut wasm_exports = std::fs::read_to_string("lib/binding_web/wasm/exports.txt")?
let mut wasm_exports = std::fs::read_to_string("lib/binding_web/lib/exports.txt")?
.lines()
.map(|s| s.replace("_wasm", "").replace("byte", "index"))
// remove leading and trailing quotes, trailing comma

View file

@ -85,6 +85,10 @@ struct BuildWasm {
/// Rebuild when relevant files are changed.
#[arg(long, short)]
watch: bool,
/// Emit TypeScript type definitions for the generated bindings,
/// requires `tsc` to be available.
#[arg(long, short)]
emit_tsd: bool,
}
#[derive(Args)]