Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(napi/parser): introduce experimental MagicString #7529

Merged
merged 1 commit into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 51 additions & 9 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ unicode-id-start = "1"
oxc-browserslist = "1.1.0"
oxc_index = "2"
oxc_resolver = "2.1.1"
oxc_sourcemap = "1.0.3"
oxc_sourcemap = "1"

#
allocator-api2 = "0.2.21"
Expand Down Expand Up @@ -180,13 +180,15 @@ rustc-hash = "2.*"
ryu-js = "1.0.1"
saphyr = "0.0.3"
schemars = "0.8.21"
self_cell = "1.0.4"
seq-macro = "0.3.5"
serde = "1.0.215"
serde-wasm-bindgen = "0.6.5"
serde_json = "1.0.133"
sha1 = "0.10.6"
simdutf8 = { version = "0.1.5", features = ["aarch64_neon"] }
similar = "2.6.0"
string_wizard = "0.0.23"
tempfile = "3.14.0"
tokio = "1.42.0"
tower-lsp = "0.20.0"
Expand Down
3 changes: 3 additions & 0 deletions napi/parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ oxc_ast = { workspace = true, features = ["serialize"] } # enable feature only
oxc_napi = { workspace = true }

rustc-hash = { workspace = true }
self_cell = { workspace = true }
serde_json = { workspace = true }
string_wizard = { workspace = true, features = ["sourcemap"] }
# oxc_sourcemap = { workspace = true, features = ["napi"] }

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
40 changes: 33 additions & 7 deletions napi/parser/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,29 @@
/* eslint-disable */

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

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

export interface Comment {
type: 'Line' | 'Block'
value: string
Expand Down Expand Up @@ -108,20 +131,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 Expand Up @@ -154,6 +174,12 @@ export declare const enum Severity {
Advice = 'Advice'
}

export interface SourceMapOptions {
includeContent?: boolean
source?: string
hires?: boolean
}

export interface Span {
start: number
end: number
Expand Down
42 changes: 36 additions & 6 deletions napi/parser/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
const bindings = require('./bindings.js');

module.exports.MagicString = bindings.MagicString;
module.exports.ParseResult = bindings.ParseResult;
module.exports.ExportExportNameKind = bindings.ExportExportNameKind;
module.exports.ExportImportNameKind = bindings.ExportImportNameKind;
module.exports.ExportLocalNameKind = bindings.ExportLocalNameKind;
module.exports.ImportNameKind = bindings.ImportNameKind;
module.exports.parseWithoutReturn = bindings.parseWithoutReturn;
module.exports.Severity = bindings.Severity;

function wrap(result) {
let program, module, comments, errors, magicString;
return {
get program() {
if (!program) program = JSON.parse(result.program);
return program;
},
get module() {
if (!module) module = result.module;
return module;
},
get comments() {
if (!comments) comments = result.comments;
return comments;
},
get errors() {
if (!errors) errors = result.errors;
return errors;
},
get magicString() {
if (!magicString) magicString = result.magicString;
return magicString;
},
};
}

module.exports.parseAsync = async function parseAsync(...args) {
const result = await bindings.parseAsync(...args);
result.program = JSON.parse(result.program);
return result;
return wrap(await bindings.parseAsync(...args));
};

module.exports.parseSync = function parseSync(...args) {
const result = bindings.parseSync(...args);
result.program = JSON.parse(result.program);
return result;
return wrap(bindings.parseSync(...args));
};
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
Loading
Loading