mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(wasm/parser): improve FFI (#2232)
This commit is contained in:
parent
5fb5d8a01e
commit
9c6c17b9aa
4 changed files with 58 additions and 22 deletions
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
|
|
@ -63,15 +63,21 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
rustup target add wasm32-unknown-unknown
|
rustup target add wasm32-unknown-unknown
|
||||||
cargo check -p oxc_wasm --target wasm32-unknown-unknown
|
cargo check -p oxc_wasm --target wasm32-unknown-unknown
|
||||||
- name: Build types
|
|
||||||
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
npx -y wasm-pack build --target web --dev ./crates/oxc_wasm
|
npx -y wasm-pack build --target web --dev ./crates/oxc_wasm
|
||||||
|
npx -y wasm-pack build --target web --dev ./wasm/parser
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
|
|
||||||
- name: Check output types
|
- name: Check output types
|
||||||
run: npx -y -p typescript tsc --lib es2020,dom crates/oxc_wasm/pkg/oxc_wasm.d.ts
|
run: |
|
||||||
|
npx -y -p typescript tsc --lib es2020,dom crates/oxc_wasm/pkg/oxc_wasm.d.ts
|
||||||
|
|
||||||
typos:
|
typos:
|
||||||
name: Spell Check
|
name: Spell Check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ default = ["console_error_panic_hook"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
oxc = { workspace = true, features = ["serde", "wasm"] }
|
oxc = { workspace = true, features = ["serde", "wasm"] }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
|
||||||
wasm-bindgen = { workspace = true }
|
wasm-bindgen = { workspace = true }
|
||||||
serde-wasm-bindgen = { workspace = true }
|
serde-wasm-bindgen = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ Checkout [oxc-parser](https://www.npmjs.com/package/oxc-parser) for usage in nod
|
||||||
|
|
||||||
Source code: https://github.com/oxc-project/oxc/tree/main/wasm/parser
|
Source code: https://github.com/oxc-project/oxc/tree/main/wasm/parser
|
||||||
|
|
||||||
## 🚴 Usage
|
## Usage
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import initWasm, { parseSync } from "@oxc-parser/wasm";
|
import initWasm, { parseSync } from "@oxc-parser/wasm";
|
||||||
|
|
@ -16,9 +16,37 @@ async function main() {
|
||||||
await initWasm();
|
await initWasm();
|
||||||
|
|
||||||
const code = "let foo";
|
const code = "let foo";
|
||||||
const result = parseSync(code, { filename: "test.ts" });
|
const result = parseSync(code, { sourceFilename: "test.ts" });
|
||||||
console.log(result);
|
console.log(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
### UTF8 vs UTF16 byte offsets
|
||||||
|
|
||||||
|
The `span` value returned from the ASTs and diagnostics is in UTF8 byte offsets. Converting to UTF16 byte offsets:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let sourceTextUtf8 = new TextEncoder().encode(sourceText);
|
||||||
|
|
||||||
|
const convertToUtf8 = (sourceTextUtf8, d) => {
|
||||||
|
return new TextDecoder().decode(sourceTextUtf8.slice(0, d)).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const diagnostics = result.errors.map((d) => ({
|
||||||
|
from: convertToUtf8(sourceTextUtf8, d.start),
|
||||||
|
to: convertToUtf8(sourceTextUtf8, d.end),
|
||||||
|
severity: d.severity.toLowerCase(),
|
||||||
|
message: d.message,
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vite
|
||||||
|
|
||||||
|
`wasm-pack build --target web` is used for the wasm build.
|
||||||
|
|
||||||
|
You may need something like https://github.com/nshen/vite-plugin-wasm-pack to get it working with vite,
|
||||||
|
otherwise vite will load the wasm file as a HTML file causing a `CompileError: WebAssembly.instantiate(): expected magic word` error.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#![allow(clippy::needless_pass_by_value)]
|
#![allow(clippy::needless_pass_by_value)]
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use tsify::Tsify;
|
use tsify::Tsify;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
|
@ -12,31 +12,33 @@ pub fn main() {
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Babel Parser Options
|
#[derive(Debug, Default, Clone, Deserialize, Tsify)]
|
||||||
///
|
#[tsify(from_wasm_abi)]
|
||||||
/// <https://github.com/babel/babel/blob/main/packages/babel-parser/typings/babel-parser.d.ts>
|
|
||||||
#[wasm_bindgen(getter_with_clone)]
|
|
||||||
#[derive(Default, Tsify)]
|
|
||||||
pub struct ParserOptions {
|
pub struct ParserOptions {
|
||||||
#[wasm_bindgen(js_name = sourceType)]
|
#[serde(rename = "sourceType")]
|
||||||
|
#[tsify(optional, type = "\"script\" | \"module\"")]
|
||||||
pub source_type: Option<String>,
|
pub source_type: Option<String>,
|
||||||
#[wasm_bindgen]
|
|
||||||
pub filename: Option<String>,
|
/// "module" and "jsx" will be inferred from `sourceFilename`.
|
||||||
|
#[serde(rename = "sourceFilename")]
|
||||||
|
#[tsify(optional)]
|
||||||
|
pub source_filename: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen(getter_with_clone)]
|
|
||||||
#[derive(Default, Tsify)]
|
#[derive(Default, Tsify)]
|
||||||
|
#[wasm_bindgen(getter_with_clone)]
|
||||||
pub struct ParseResult {
|
pub struct ParseResult {
|
||||||
#[wasm_bindgen(readonly, skip_typescript)]
|
#[wasm_bindgen(readonly, skip_typescript)]
|
||||||
#[tsify(type = "Program")]
|
#[tsify(type = "Program")]
|
||||||
pub program: JsValue,
|
pub program: JsValue,
|
||||||
|
|
||||||
#[wasm_bindgen(readonly, skip_typescript)]
|
#[wasm_bindgen(readonly, skip_typescript)]
|
||||||
#[tsify(type = "OxcDiagnostic[]")]
|
#[tsify(type = "Diagnostic[]")]
|
||||||
pub errors: Vec<JsValue>,
|
pub errors: Vec<JsValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Tsify, Serialize)]
|
#[derive(Debug, Default, Serialize, Tsify)]
|
||||||
pub struct OxcDiagnostic {
|
pub struct Diagnostic {
|
||||||
pub start: usize,
|
pub start: usize,
|
||||||
pub end: usize,
|
pub end: usize,
|
||||||
pub severity: String,
|
pub severity: String,
|
||||||
|
|
@ -61,7 +63,7 @@ pub fn parse_sync(
|
||||||
let allocator = Allocator::default();
|
let allocator = Allocator::default();
|
||||||
|
|
||||||
let source_type = options
|
let source_type = options
|
||||||
.filename
|
.source_filename
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|name| SourceType::from_path(name).unwrap())
|
.map(|name| SourceType::from_path(name).unwrap())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
@ -87,7 +89,7 @@ pub fn parse_sync(
|
||||||
let Some(labels) = error.labels() else { return vec![] };
|
let Some(labels) = error.labels() else { return vec![] };
|
||||||
labels
|
labels
|
||||||
.map(|label| {
|
.map(|label| {
|
||||||
OxcDiagnostic {
|
Diagnostic {
|
||||||
start: label.offset(),
|
start: label.offset(),
|
||||||
end: label.offset() + label.len(),
|
end: label.offset() + label.len(),
|
||||||
severity: format!("{:?}", error.severity().unwrap_or_default()),
|
severity: format!("{:?}", error.severity().unwrap_or_default()),
|
||||||
|
|
@ -96,9 +98,9 @@ pub fn parse_sync(
|
||||||
.serialize(&serializer)
|
.serialize(&serializer)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<JsValue>>()
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<JsValue>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ParseResult { program, errors })
|
Ok(ParseResult { program, errors })
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue