Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
Boshen committed Dec 9, 2024
1 parent 5806942 commit 763aebd
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 20 deletions.
1 change: 1 addition & 0 deletions napi/parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ oxc_napi = { workspace = true }

rustc-hash = { workspace = true }
serde_json = { workspace = true }
# string_wizard = { versoin = "0.0.23", features = ["sourcemap"] }

napi = { workspace = true, features = ["async"] }
napi-derive = { workspace = true }
Expand Down
2 changes: 2 additions & 0 deletions napi/parser/bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,8 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

module.exports.MagicString = nativeBinding.MagicString
module.exports.ParseResult = nativeBinding.ParseResult
module.exports.ExportExportNameKind = nativeBinding.ExportExportNameKind
module.exports.ExportImportNameKind = nativeBinding.ExportImportNameKind
module.exports.ExportLocalNameKind = nativeBinding.ExportLocalNameKind
Expand Down
26 changes: 19 additions & 7 deletions napi/parser/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@
/* eslint-disable */

export * from '@oxc-project/types';
export declare class MagicString {
length(): number
toString(): string
append(input: string): this
prepend(input: string): this
appendLeft(index: number, input: string): this
appendRight(index: number, input: string): this
prependLeft(index: number, input: string): this
prependRight(index: number, input: string): this
}

export declare class ParseResult {
ast(): import("@oxc-project/types").Program
}

export interface Comment {
type: 'Line' | 'Block'
value: string
Expand Down Expand Up @@ -108,20 +123,17 @@ export declare const enum ImportNameKind {
Default = 'Default'
}

export interface OverwriteOptions {
contentOnly: boolean
}

/**
* Parse asynchronously.
*
* Note: This function can be slower than `parseSync` due to the overhead of spawning a thread.
*/
export declare function parseAsync(filename: string, sourceText: string, options?: ParserOptions | undefined | null): Promise<ParseResult>

export interface ParseResult {
program: import("@oxc-project/types").Program
module: EcmaScriptModule
comments: Array<Comment>
errors: Array<Error>
}

export interface ParserOptions {
sourceType?: 'script' | 'module' | 'unambiguous' | undefined
/** Treat the source text as `js`, `jsx`, `ts`, or `tsx`. */
Expand Down
21 changes: 14 additions & 7 deletions napi/parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
)]

mod convert;
mod magic_string;
mod types;

use std::mem;

use napi::{bindgen_prelude::AsyncTask, Task};
use napi_derive::napi;

Expand All @@ -16,7 +19,10 @@ use oxc::{
};
use oxc_napi::Error;

pub use crate::types::{Comment, EcmaScriptModule, ParseResult, ParserOptions};
pub use crate::{
magic_string::MagicString,
types::{Comment, EcmaScriptModule, ParseResult, ParserOptions},
};

fn get_source_type(filename: &str, options: &ParserOptions) -> SourceType {
match options.lang.as_deref() {
Expand Down Expand Up @@ -62,10 +68,10 @@ pub fn parse_without_return(filename: String, source_text: String, options: Opti
parse(&allocator, source_type, &source_text, &options);
}

fn parse_with_return(filename: &str, source_text: &str, options: &ParserOptions) -> ParseResult {
fn parse_with_return(filename: &str, source_text: String, options: &ParserOptions) -> ParseResult {
let allocator = Allocator::default();
let source_type = get_source_type(filename, options);
let ret = parse(&allocator, source_type, source_text, options);
let ret = parse(&allocator, source_type, &source_text, options);
let program = serde_json::to_string(&ret.program).unwrap();

let errors = ret.errors.into_iter().map(Error::from).collect::<Vec<_>>();
Expand All @@ -79,14 +85,14 @@ fn parse_with_return(filename: &str, source_text: &str, options: &ParserOptions)
CommentKind::Line => String::from("Line"),
CommentKind::Block => String::from("Block"),
},
value: comment.content_span().source_text(source_text).to_string(),
value: comment.content_span().source_text(&source_text).to_string(),
start: comment.span.start,
end: comment.span.end,
})
.collect::<Vec<Comment>>();

let module = EcmaScriptModule::from(&ret.module_record);
ParseResult { program, module, comments, errors }
ParseResult { source_text, program, module, comments, errors }
}

/// Parse synchronously.
Expand All @@ -97,7 +103,7 @@ pub fn parse_sync(
options: Option<ParserOptions>,
) -> ParseResult {
let options = options.unwrap_or_default();
parse_with_return(&filename, &source_text, &options)
parse_with_return(&filename, source_text, &options)
}

pub struct ResolveTask {
Expand All @@ -112,7 +118,8 @@ impl Task for ResolveTask {
type Output = ParseResult;

fn compute(&mut self) -> napi::Result<Self::Output> {
Ok(parse_with_return(&self.filename, &self.source_text, &self.options))
let source_text = mem::take(&mut self.source_text);
Ok(parse_with_return(&self.filename, source_text, &self.options))
}

fn resolve(&mut self, _: napi::Env, result: Self::Output) -> napi::Result<Self::JsValue> {
Expand Down
109 changes: 109 additions & 0 deletions napi/parser/src/magic_string.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use napi_derive::napi;

#[napi]
pub struct MagicString; // (string_wizard::MagicString<'static>);

impl MagicString {
pub fn new(_s: String) -> Self {
Self //(string_wizard::MagicString::new(s))
}
}

#[napi(object)]
pub struct OverwriteOptions {
pub content_only: bool,
}

#[napi]
impl MagicString {
#[napi]
pub fn length(&self) -> u32 {
// self.0.len() as u32
unimplemented!()
}

#[napi]
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
// self.0.to_string()
unimplemented!()
}

#[napi]
pub fn append(&mut self, _input: String) -> &Self {
// self.0.append(input);
// self
unimplemented!()
}

#[napi]
pub fn prepend(&mut self, _input: String) -> &Self {
// self.0.prepend(input);
// self
unimplemented!()
}

#[napi]
pub fn append_left(&mut self, _index: u32, _input: String) -> &Self {
// self.0.append_left(index as usize, input);
// self
unimplemented!()
}

#[napi]
pub fn append_right(&mut self, _index: u32, _input: String) -> &Self {
// self.0.append_right(index as usize, input);
// self
unimplemented!()
}

#[napi]
pub fn prepend_left(&mut self, _index: u32, _input: String) -> &Self {
// self.0.prepend_left(index as usize, input);
// self
unimplemented!()
}

#[napi]
pub fn prepend_right(&mut self, _index: u32, _input: String) -> &Self {
// self.0.prepend_right(index as usize, input);
// self
unimplemented!()
}

// #[napi]
// pub fn overwrite(
// &mut self,
// start: i64,
// end: i64,
// content: String,
// options: OverwriteOptions,
// ) -> &Self {
// self.0.overwrite(start, end, content, options);
// self
// }

// #[napi]
// pub fn trim(&mut self, pattern: Option<String>) -> &Self {
// self.0.trim(pattern.as_deref());
// self
// }

// #[napi]
// pub fn trim_start(&mut self, pattern: Option<String>) -> &Self {
// self.0.trim_start(pattern.as_deref());
// self
// }

// #[napi]
// pub fn trim_end(&mut self, pattern: Option<String>) -> &Self {
// self.0.trim_end(pattern.as_deref());
// self
// }

// #[napi]
// pub fn trim_lines(&mut self) -> &Self {
// self.0.trim_lines();
// self
// }
}
41 changes: 35 additions & 6 deletions napi/parser/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use napi_derive::napi;
use std::mem;

use oxc_napi::Error;

use crate::magic_string::MagicString;

#[napi(object)]
#[derive(Default)]
pub struct ParserOptions {
Expand All @@ -22,13 +25,38 @@ pub struct ParserOptions {
pub preserve_parens: Option<bool>,
}

#[napi(object)]
#[napi]
pub struct ParseResult {
#[napi(ts_type = "import(\"@oxc-project/types\").Program")]
pub program: String,
pub module: EcmaScriptModule,
pub comments: Vec<Comment>,
pub errors: Vec<Error>,
pub(crate) source_text: String,
// #[napi(ts_type = "import(\"@oxc-project/types\").Program")]
pub(crate) program: String,
pub(crate) module: EcmaScriptModule,
pub(crate) comments: Vec<Comment>,
pub(crate) errors: Vec<Error>,
}

#[napi]
impl ParseResult {
#[napi(ts_return_type = "import(\"@oxc-project/types\").Program")]
pub fn ast(&mut self) -> String {
mem::take(&mut self.program)
}

pub fn module(&mut self) -> EcmaScriptModule {
mem::take(&mut self.module)
}

pub fn comments(&mut self) -> Vec<Comment> {
mem::take(&mut self.comments)
}

pub fn errors(&mut self) -> Vec<Error> {
mem::take(&mut self.errors)
}

pub fn magic_string(&mut self) -> MagicString {
MagicString::new(mem::take(&mut self.source_text))
}
}

#[napi(object)]
Expand All @@ -41,6 +69,7 @@ pub struct Comment {
}

#[napi(object)]
#[derive(Default)]
pub struct EcmaScriptModule {
/// Has ESM syntax.
///
Expand Down
26 changes: 26 additions & 0 deletions napi/parser/test/magic_string.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { assert, describe, it } from 'vitest';

import type { StringLiteral, VariableDeclaration } from '../index.js';
import { parseSync } from '../index.js';

describe('simple', () => {
const code = 'const s: String = "测试"';

it('calls magic string APIs', () => {
// `oxc` holds a magic string instance on the Rust side.
const { program, magicString } = parseSync('test.ts', code);
const declaration = ast.body[0] as VariableDeclaration;
const stringLiteral = declaration.declarations[0].init as StringLiteral;

// These spans are in utf8 offsets.
const start = stringLiteral.start + 1;
const end = stringLiteral.end - 1;

// Access source text by utf8 offset.
assert.equal(oxc.sourceText(start, end), '测试');

// Magic string manipulation.
oxc.remove(start, end);
assert.equal(oxc.toString(), 'const s: String = ""');
});
});

0 comments on commit 763aebd

Please sign in to comment.