Skip to content

Commit

Permalink
✨ feat: Add source map to options
Browse files Browse the repository at this point in the history
  • Loading branch information
caoccao committed Mar 9, 2024
1 parent 6b8404a commit 03f6833
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 28 deletions.
6 changes: 5 additions & 1 deletion rust/src/converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@
*/

use jni::objects::JString;
use jni::sys::jstring;
use jni::sys::{jboolean, jstring};
use jni::JNIEnv;
use std::ptr::null_mut;

use deno_media_type::MediaType;

pub fn jboolean_to_bool(b: jboolean) -> bool {
b != 0
}

pub fn jstring_to_string<'local>(env: &mut JNIEnv<'local>, s: jstring) -> String {
unsafe {
match env.get_string(&JString::from_raw(s)) {
Expand Down
36 changes: 27 additions & 9 deletions rust/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,38 @@ const VERSION: &'static str = "0.1.0";
pub fn transpile<'local>(code: String, options: options::TranspileOptions) -> Result<outputs::TranspileOutput, String> {
match parse_module(ParseParams {
specifier: options.specifier,
text_info: SourceTextInfo::from_string(code.to_string()),
text_info: SourceTextInfo::from_string(code),
media_type: options.media_type,
capture_tokens: false,
maybe_syntax: None,
scope_analysis: false,
}) {
Ok(parsed_source) => match parsed_source.transpile(&EmitOptions::default()) {
Ok(transpiled_js_code) => Ok(outputs::TranspileOutput {
code: transpiled_js_code.text,
module: parsed_source.is_module(),
source_map: transpiled_js_code.source_map,
}),
Err(e) => Err(e.to_string()),
},
Ok(parsed_source) => {
let default_emit_options = EmitOptions::default();
let emit_options = EmitOptions {
emit_metadata: default_emit_options.emit_metadata,
imports_not_used_as_values: default_emit_options.imports_not_used_as_values,
inline_source_map: options.inline_source_map,
inline_sources: options.inline_sources,
jsx_automatic: default_emit_options.jsx_automatic,
jsx_development: default_emit_options.jsx_development,
jsx_factory: default_emit_options.jsx_factory,
jsx_fragment_factory: default_emit_options.jsx_fragment_factory,
jsx_import_source: default_emit_options.jsx_import_source,
precompile_jsx: default_emit_options.precompile_jsx,
source_map: options.source_map,
transform_jsx: default_emit_options.transform_jsx,
var_decl_imports: default_emit_options.var_decl_imports,
};
match parsed_source.transpile(&emit_options) {
Ok(transpiled_js_code) => Ok(outputs::TranspileOutput {
code: transpiled_js_code.text,
module: parsed_source.is_module(),
source_map: transpiled_js_code.source_map,
}),
Err(e) => Err(e.to_string()),
}
}
Err(e) => Err(e.to_string()),
}
}
Expand Down
101 changes: 86 additions & 15 deletions rust/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,25 @@ use deno_ast::MediaType;
use crate::converter;

struct JniCalls {
pub jmethod_id_media_type_get_id: JMethodID,
pub jmethod_id_transpile_options_get_media_type: JMethodID,
pub jmethod_id_transpile_options_get_specifier: JMethodID,
pub jmethod_id_media_type_get_id: JMethodID,
pub jmethod_id_transpile_options_is_inline_source_map: JMethodID,
pub jmethod_id_transpile_options_is_inline_sources: JMethodID,
pub jmethod_id_transpile_options_is_source_map: JMethodID,
}
unsafe impl Send for JniCalls {}
unsafe impl Sync for JniCalls {}

static mut JNI_CALLS: Option<JniCalls> = None;

pub fn init<'local>(env: &mut JNIEnv<'local>) {
let jclass_media_type = env
.find_class("com/caoccao/javet/swc4j/enums/Swc4jMediaType")
.expect("Couldn't find class Swc4jMediaType");
let jmethod_id_media_type_get_id = env
.get_method_id(&jclass_media_type, "getId", "()I")
.expect("Couldn't find method Swc4jMediaType.getId");
let jclass_transpile_options = env
.find_class("com/caoccao/javet/swc4j/options/Swc4jTranspileOptions")
.expect("Couldn't find class Swc4jTranspileOptions");
Expand All @@ -48,17 +57,23 @@ pub fn init<'local>(env: &mut JNIEnv<'local>) {
let jmethod_id_transpile_options_get_specifier = env
.get_method_id(&jclass_transpile_options, "getSpecifier", "()Ljava/lang/String;")
.expect("Couldn't find method Swc4jTranspileOptions.getSpecifier");
let jclass_media_type = env
.find_class("com/caoccao/javet/swc4j/enums/Swc4jMediaType")
.expect("Couldn't find class Swc4jMediaType");
let jmethod_id_media_type_get_id = env
.get_method_id(&jclass_media_type, "getId", "()I")
.expect("Couldn't find method Swc4jMediaType.getId");
let jmethod_id_transpile_options_is_inline_source_map = env
.get_method_id(&jclass_transpile_options, "isInlineSourceMap", "()Z")
.expect("Couldn't find method Swc4jTranspileOptions.isInlineSourceMap");
let jmethod_id_transpile_options_is_inline_sources = env
.get_method_id(&jclass_transpile_options, "isInlineSources", "()Z")
.expect("Couldn't find method Swc4jTranspileOptions.isInlineSources");
let jmethod_id_transpile_options_is_source_map = env
.get_method_id(&jclass_transpile_options, "isSourceMap", "()Z")
.expect("Couldn't find method Swc4jTranspileOptions.isSourceMap");
unsafe {
JNI_CALLS = Some(JniCalls {
jmethod_id_transpile_options_get_media_type,
jmethod_id_transpile_options_get_specifier: jmethod_id_transpile_options_get_specifier,
jmethod_id_media_type_get_id,
jmethod_id_transpile_options_get_media_type,
jmethod_id_transpile_options_get_specifier,
jmethod_id_transpile_options_is_inline_source_map,
jmethod_id_transpile_options_is_inline_sources,
jmethod_id_transpile_options_is_source_map,
});
}
}
Expand All @@ -69,23 +84,51 @@ pub trait FromJniType {

#[derive(Debug)]
pub struct TranspileOptions {
/// Should the source map be inlined in the emitted code file, or provided
/// as a separate file. Defaults to `true`.
pub inline_source_map: bool,
/// Should the sources be inlined in the source map. Defaults to `true`.
pub inline_sources: bool,
/// Media type of the source text.
pub media_type: MediaType,
/// Should a corresponding .map file be created for the output. This should be
/// false if inline_source_map is true. Defaults to `false`.
pub source_map: bool,
/// Specifier of the source text.
pub specifier: String,
}

impl FromJniType for TranspileOptions {
fn from_jni_type<'local>(env: &mut JNIEnv<'local>, o: jobject) -> TranspileOptions {
let o = unsafe { JObject::from_raw(o) };
let specifier = unsafe {
// inline_source_map
let inline_source_map = unsafe {
env.call_method_unchecked(
o.as_ref(),
JNI_CALLS.as_ref().unwrap().jmethod_id_transpile_options_get_specifier,
ReturnType::Object,
JNI_CALLS
.as_ref()
.unwrap()
.jmethod_id_transpile_options_is_inline_source_map,
ReturnType::Primitive(Primitive::Boolean),
&[],
)
};
let specifier = unsafe { specifier.unwrap().as_jni().l };
let specifier = converter::jstring_to_string(env, specifier);
let inline_source_map = unsafe { inline_source_map.unwrap().as_jni().z };
let inline_source_map = converter::jboolean_to_bool(inline_source_map);
// inline_sources
let inline_sources = unsafe {
env.call_method_unchecked(
o.as_ref(),
JNI_CALLS
.as_ref()
.unwrap()
.jmethod_id_transpile_options_is_inline_sources,
ReturnType::Primitive(Primitive::Boolean),
&[],
)
};
let inline_sources = unsafe { inline_sources.unwrap().as_jni().z };
let inline_sources = converter::jboolean_to_bool(inline_sources);
// media_type
let media_type = unsafe {
env.call_method_unchecked(
Expand All @@ -106,7 +149,35 @@ impl FromJniType for TranspileOptions {
};
let media_type = unsafe { media_type.unwrap().as_jni().i };
let media_type = converter::media_type_id_to_media_type(media_type);
// source_map
let source_map = unsafe {
env.call_method_unchecked(
o.as_ref(),
JNI_CALLS.as_ref().unwrap().jmethod_id_transpile_options_is_source_map,
ReturnType::Primitive(Primitive::Boolean),
&[],
)
};
let source_map = unsafe { source_map.unwrap().as_jni().z };
let source_map = converter::jboolean_to_bool(source_map);
// specifier
let specifier = unsafe {
env.call_method_unchecked(
o.as_ref(),
JNI_CALLS.as_ref().unwrap().jmethod_id_transpile_options_get_specifier,
ReturnType::Object,
&[],
)
};
let specifier = unsafe { specifier.unwrap().as_jni().l };
let specifier = converter::jstring_to_string(env, specifier);
// construct
TranspileOptions { specifier, media_type }
TranspileOptions {
inline_source_map,
inline_sources,
media_type,
source_map,
specifier,
}
}
}
41 changes: 39 additions & 2 deletions rust/tests/test_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,55 @@ fn test_get_version() {
}

#[test]
fn test_transpile_type_script_inline_source_map() {
fn test_transpile_type_script_with_inline_source_map() {
let code = "function add(a:number, b:number) { return a+b; }";
let expected_code = "function add(a, b) {\n return a + b;\n}\n";
let expected_source_map_prefix = "//# sourceMappingURL=data:application/json;base64,";
let options = options::TranspileOptions {
inline_source_map: true,
inline_sources: true,
media_type: MediaType::TypeScript,
source_map: false,
specifier: "file:///abc.ts".to_owned(),
};
let output = core::transpile(code.to_owned(), options);
assert!(output.is_ok());
let output_code = output.unwrap().code;
let output = output.unwrap();
assert!(output.module);
let output_code = output.code;
assert_eq!(expected_code, &output_code[0..expected_code.len()]);
assert!(output_code[expected_code.len()..].starts_with(expected_source_map_prefix));
}

#[test]
fn test_transpile_type_script_without_inline_source_map() {
let code = "function add(a:number, b:number) { return a+b; }";
let expected_code = "function add(a, b) {\n return a + b;\n}\n";
let expected_properties = vec![
"version",
"sources",
"sourcesContent",
"file:///abc.ts",
"names",
"mappings",
];
let options = options::TranspileOptions {
inline_source_map: false,
inline_sources: true,
media_type: MediaType::TypeScript,
source_map: true,
specifier: "file:///abc.ts".to_owned(),
};
let output = core::transpile(code.to_owned(), options);
assert!(output.is_ok());
let output = output.unwrap();
assert!(output.module);
let output_code = output.code;
assert_eq!(expected_code, output_code);
let source_map = output.source_map.unwrap();
expected_properties.iter().for_each(|p| assert!(source_map.contains(p), "{} is not found", p));
}

#[test]
fn test_transpile_wrong_media_type() {
let code = "function add(a:number, b:number) { return a+b; }";
Expand All @@ -48,7 +82,10 @@ fn test_transpile_wrong_media_type() {
+ " function add(a:number, b:number) { return a+b; }\n"
+ " ~";
let options = options::TranspileOptions {
inline_source_map: true,
inline_sources: true,
media_type: MediaType::JavaScript,
source_map: false,
specifier: "file:///abc.ts".to_owned(),
};
let output = core::transpile(code.to_owned(), options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ public final class Swc4jTranspileOptions {
* @since 0.1.0
*/
public static final String DEFAULT_SPECIFIER = "file:///main.js";
private boolean inlineSourceMap;
private boolean inlineSources;
private Swc4jMediaType mediaType;
private boolean sourceMap;
private String specifier;

/**
Expand All @@ -40,8 +43,11 @@ public final class Swc4jTranspileOptions {
* @since 0.1.0
*/
public Swc4jTranspileOptions() {
setInlineSourceMap(true);
setInlineSources(true);
setSpecifier(DEFAULT_SPECIFIER);
setMediaType(Swc4jMediaType.JavaScript);
setSourceMap(false);
}

/**
Expand All @@ -64,6 +70,61 @@ public String getSpecifier() {
return specifier;
}

/**
* Should the source map be inlined, or provided as a separate string. Defaults to `true`.
*
* @return true : source map is inlined, false : source map is separated
* @since 0.1.0
*/
public boolean isInlineSourceMap() {
return inlineSourceMap;
}

/**
* Should the sources be inlined in the source map. Defaults to `true`.
*
* @return true : source is inlined, false : source is not inlined
* @since 0.1.0
*/
public boolean isInlineSources() {
return inlineSources;
}

/**
* Should a corresponding map string be created for the output.
* This should be false if isInlineSourceMap() is true. Defaults to `false`.
*
* @return true : source map string is separated, false : source map string is not separated
* @since 0.1.0
*/
public boolean isSourceMap() {
return sourceMap;
}

/**
* Sets inline source map.
*
* @param inlineSourceMap the inline source map
* @return the self
* @since 0.1.0
*/
public Swc4jTranspileOptions setInlineSourceMap(boolean inlineSourceMap) {
this.inlineSourceMap = inlineSourceMap;
return this;
}

/**
* Sets inline sources.
*
* @param inlineSources the inline sources
* @return the self
* @since 0.1.0
*/
public Swc4jTranspileOptions setInlineSources(boolean inlineSources) {
this.inlineSources = inlineSources;
return this;
}

/**
* Sets Media type of the source text.
*
Expand All @@ -76,6 +137,18 @@ public Swc4jTranspileOptions setMediaType(Swc4jMediaType mediaType) {
return this;
}

/**
* Sets source map.
*
* @param sourceMap the source map
* @return the self
* @since 0.1.0
*/
public Swc4jTranspileOptions setSourceMap(boolean sourceMap) {
this.sourceMap = sourceMap;
return this;
}

/**
* Sets Specifier of the source text.
*
Expand Down
Loading

0 comments on commit 03f6833

Please sign in to comment.