mirror of
https://github.com/danbulant/oxc
synced 2026-05-21 21:29:01 +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: |
|
||||
rustup target add wasm32-unknown-unknown
|
||||
cargo check -p oxc_wasm --target wasm32-unknown-unknown
|
||||
- name: Build types
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
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
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- 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:
|
||||
name: Spell Check
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ default = ["console_error_panic_hook"]
|
|||
|
||||
[dependencies]
|
||||
oxc = { workspace = true, features = ["serde", "wasm"] }
|
||||
serde = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
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
|
||||
|
||||
## 🚴 Usage
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import initWasm, { parseSync } from "@oxc-parser/wasm";
|
||||
|
|
@ -16,9 +16,37 @@ async function main() {
|
|||
await initWasm();
|
||||
|
||||
const code = "let foo";
|
||||
const result = parseSync(code, { filename: "test.ts" });
|
||||
const result = parseSync(code, { sourceFilename: "test.ts" });
|
||||
console.log(result);
|
||||
}
|
||||
|
||||
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)]
|
||||
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tsify::Tsify;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
|
|
@ -12,31 +12,33 @@ pub fn main() {
|
|||
console_error_panic_hook::set_once();
|
||||
}
|
||||
|
||||
/// Babel Parser Options
|
||||
///
|
||||
/// <https://github.com/babel/babel/blob/main/packages/babel-parser/typings/babel-parser.d.ts>
|
||||
#[wasm_bindgen(getter_with_clone)]
|
||||
#[derive(Default, Tsify)]
|
||||
#[derive(Debug, Default, Clone, Deserialize, Tsify)]
|
||||
#[tsify(from_wasm_abi)]
|
||||
pub struct ParserOptions {
|
||||
#[wasm_bindgen(js_name = sourceType)]
|
||||
#[serde(rename = "sourceType")]
|
||||
#[tsify(optional, type = "\"script\" | \"module\"")]
|
||||
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)]
|
||||
#[wasm_bindgen(getter_with_clone)]
|
||||
pub struct ParseResult {
|
||||
#[wasm_bindgen(readonly, skip_typescript)]
|
||||
#[tsify(type = "Program")]
|
||||
pub program: JsValue,
|
||||
|
||||
#[wasm_bindgen(readonly, skip_typescript)]
|
||||
#[tsify(type = "OxcDiagnostic[]")]
|
||||
#[tsify(type = "Diagnostic[]")]
|
||||
pub errors: Vec<JsValue>,
|
||||
}
|
||||
|
||||
#[derive(Default, Tsify, Serialize)]
|
||||
pub struct OxcDiagnostic {
|
||||
#[derive(Debug, Default, Serialize, Tsify)]
|
||||
pub struct Diagnostic {
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
pub severity: String,
|
||||
|
|
@ -61,7 +63,7 @@ pub fn parse_sync(
|
|||
let allocator = Allocator::default();
|
||||
|
||||
let source_type = options
|
||||
.filename
|
||||
.source_filename
|
||||
.as_ref()
|
||||
.map(|name| SourceType::from_path(name).unwrap())
|
||||
.unwrap_or_default();
|
||||
|
|
@ -87,7 +89,7 @@ pub fn parse_sync(
|
|||
let Some(labels) = error.labels() else { return vec![] };
|
||||
labels
|
||||
.map(|label| {
|
||||
OxcDiagnostic {
|
||||
Diagnostic {
|
||||
start: label.offset(),
|
||||
end: label.offset() + label.len(),
|
||||
severity: format!("{:?}", error.severity().unwrap_or_default()),
|
||||
|
|
@ -96,9 +98,9 @@ pub fn parse_sync(
|
|||
.serialize(&serializer)
|
||||
.unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.collect::<Vec<JsValue>>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.collect::<Vec<JsValue>>()
|
||||
};
|
||||
|
||||
Ok(ParseResult { program, errors })
|
||||
|
|
|
|||
Loading…
Reference in a new issue