Handle input source map correctly (#830)

This commit is contained in:
강동윤 2020-06-09 20:20:20 +09:00 committed by GitHub
parent d1117350a9
commit 88edb71e0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 96 additions and 123 deletions

View File

@ -76,7 +76,7 @@ impl Task for TransformTask {
let program: Program =
serde_json::from_str(&s).expect("failed to deserialize Program");
// TODO: Source map
self.c.process_js(program, None, &self.options)
self.c.process_js(program, &self.options)
}
Input::File(ref path) => {
@ -144,8 +144,7 @@ where
if is_module.value() {
let program: Program =
serde_json::from_str(&s.value()).expect("failed to deserialize Program");
// TODO: Source map
c.process_js(program, None, &options)
c.process_js(program, &options)
} else {
let fm = op(&c, s.value(), &options).expect("failed to create fm");
c.process_js_file(fm, &options)
@ -246,16 +245,13 @@ impl Task for ParseTask {
fn perform(&self) -> Result<Self::Output, Self::Error> {
self.c.run(|| {
self.c
.parse_js(
self.fm.clone(),
self.options.target,
self.options.syntax,
self.options.is_module,
self.options.comments,
&Default::default(),
)
.map(|v| v.0)
self.c.parse_js(
self.fm.clone(),
self.options.target,
self.options.syntax,
self.options.is_module,
self.options.comments,
)
})
}
@ -281,16 +277,13 @@ impl Task for ParseFileTask {
.load_file(&self.path)
.context("failed to read module")?;
self.c
.parse_js(
fm,
self.options.target,
self.options.syntax,
self.options.is_module,
self.options.comments,
&Default::default(),
)
.map(|v| v.0)
self.c.parse_js(
fm,
self.options.target,
self.options.syntax,
self.options.is_module,
self.options.comments,
)
})
}
@ -348,9 +341,7 @@ fn parse_sync(mut cx: MethodContext<JsCompiler>) -> JsResult<JsValue> {
options.syntax,
options.is_module,
options.comments,
&Default::default(),
)
.map(|v| v.0)
};
complete_parse(cx, program, &c)
@ -381,9 +372,7 @@ fn parse_file_sync(mut cx: MethodContext<JsCompiler>) -> JsResult<JsValue> {
options.syntax,
options.is_module,
options.comments,
&Default::default(),
)
.map(|v| v.0)
};
complete_parse(cx, program, &c)

View File

@ -1,6 +1,6 @@
{
"name": "@swc/core",
"version": "1.1.54",
"version": "1.1.55",
"description": "Super-fast alternative for babel",
"main": "./index.js",
"author": "강동윤 <kdy1997.dev@gmail.com>",

View File

@ -101,6 +101,7 @@ fn default_is_module() -> bool {
true
}
/// Configuration related to source map generaged by swc.
#[derive(Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum SourceMapsConfig {
@ -135,7 +136,7 @@ pub enum InputSourceMap {
impl Default for InputSourceMap {
fn default() -> Self {
InputSourceMap::Bool(true)
InputSourceMap::Bool(false)
}
}

View File

@ -5,15 +5,12 @@ pub use swc_atoms as atoms;
pub use swc_common as common;
pub use swc_ecmascript as ecmascript;
mod builder;
pub mod config;
pub use crate::builder::PassBuilder;
use crate::config::{
BuiltConfig, Config, ConfigFile, InputSourceMap, JscTarget, Merge, Options, Rc, RootMode,
SourceMapsConfig,
};
use anyhow::{Context, Error};
use anyhow::{bail, Context, Error};
use common::{
comments::{Comment, Comments},
errors::Handler,
@ -41,6 +38,9 @@ use std::{
sync::Arc,
};
mod builder;
pub mod config;
pub struct Compiler {
/// swc uses rustc's span interning.
///
@ -81,6 +81,65 @@ impl Compiler {
})
}
fn get_orig_src_map(
&self,
name: &FileName,
input_src_map: &InputSourceMap,
) -> Result<Option<sourcemap::SourceMap>, Error> {
self.run(|| -> Result<_, Error> {
// Load original source map
match input_src_map {
InputSourceMap::Bool(false) => Ok(None),
InputSourceMap::Bool(true) => {
// Load original source map if possible
match &name {
FileName::Real(filename) => {
let path = format!("{}.map", filename.display());
let file = File::open(&path)
.context("failed to open input source map file")?;
Ok(Some(sourcemap::SourceMap::from_reader(file).with_context(
|| format!("failed to read input source map from file at {}", path),
)?))
}
_ => {
log::error!("Failed to load source map for non-file input");
return Ok(None);
}
}
}
InputSourceMap::Str(ref s) => {
if s == "inline" {
// Load inline source map by simple string
// operations
let s = "sourceMappingURL=data:application/json;base64,";
let idx = s.rfind(s);
let idx = match idx {
None => bail!(
"failed to parse inline source map: `sourceMappingURL` not found"
),
Some(v) => v,
};
let encoded = &s[idx + s.len()..];
let res = base64::decode(encoded.as_bytes())
.context("failed to decode base64-encoded source map")?;
Ok(Some(sourcemap::SourceMap::from_slice(&res).context(
"failed to read input source map from inlined base64 encoded string",
)?))
} else {
// Load source map passed by user
Ok(Some(
sourcemap::SourceMap::from_slice(s.as_bytes()).context(
"failed to read input source map from user-provided sourcemap",
)?,
))
}
}
}
})
}
/// This method parses a javascript / typescript file
pub fn parse_js(
&self,
@ -89,63 +148,8 @@ impl Compiler {
syntax: Syntax,
is_module: bool,
parse_comments: bool,
input_source_map: &InputSourceMap,
) -> Result<(Program, Option<sourcemap::SourceMap>), Error> {
) -> Result<Program, Error> {
self.run(|| {
let orig = (|| {
// Load original source map
match input_source_map {
InputSourceMap::Bool(false) => None,
InputSourceMap::Bool(true) => {
// Load original source map if possible
match &fm.name {
FileName::Real(filename) => {
let path = format!("{}.map", filename.display());
let file = File::open(&path).ok()?;
Some(sourcemap::SourceMap::from_reader(file).with_context(|| {
format!("failed to read input source map from file at {}", path)
}))
}
_ => {
log::error!("Failed to load source map for non-file input");
return None;
}
}
}
InputSourceMap::Str(ref s) => {
if s == "inline" {
// Load inline source map by simple string
// operations
let s = "sourceMappingURL=data:application/json;base64,";
let idx = s.rfind(s)?;
let encoded = &s[idx + s.len()..];
let res = base64::decode(encoded.as_bytes())
.context("failed to decode base64-encoded source map");
let res = match res {
Ok(v) => v,
Err(err) => return Some(Err(err)),
};
Some(sourcemap::SourceMap::from_slice(&res).context(
"failed to read input source map from inlined base64 encoded \
string",
))
} else {
// Load source map passed by user
Some(sourcemap::SourceMap::from_slice(s.as_bytes()).context(
"failed to read input source map from user-provided sourcemap",
))
}
}
}
})();
let orig = match orig {
None => None,
Some(v) => Some(v?),
};
let session = ParseSess {
handler: &self.handler,
};
@ -179,7 +183,7 @@ impl Compiler {
.map(Program::Script)?
};
Ok((program, orig))
Ok(program)
})
}
@ -365,16 +369,16 @@ impl Compiler {
) -> Result<TransformOutput, Error> {
self.run(|| -> Result<_, Error> {
let config = self.run(|| self.config_for_file(opts, &fm.name))?;
let (program, src_map) = self.parse_js(
let orig = self.get_orig_src_map(&fm.name, &opts.input_source_map)?;
let program = self.parse_js(
fm.clone(),
config.target,
config.syntax,
config.is_module,
true,
&config.input_source_map,
)?;
self.process_js_inner(program, src_map, config)
self.process_js_inner(program, orig.as_ref(), config)
})
.context("failed to process js file")
}
@ -382,19 +386,15 @@ impl Compiler {
/// You can use custom pass with this method.
///
/// There exists a [PassBuilder] to help building custom passes.
pub fn process_js(
&self,
program: Program,
src_map: Option<sourcemap::SourceMap>,
opts: &Options,
) -> Result<TransformOutput, Error> {
pub fn process_js(&self, program: Program, opts: &Options) -> Result<TransformOutput, Error> {
self.run(|| -> Result<_, Error> {
let loc = self.cm.lookup_char_pos(program.span().lo());
let fm = loc.file;
let orig = self.get_orig_src_map(&fm.name, &opts.input_source_map)?;
let config = self.run(|| self.config_for_file(opts, &fm.name))?;
self.process_js_inner(program, src_map, config)
self.process_js_inner(program, orig.as_ref(), config)
})
.context("failed to process js module")
}
@ -402,7 +402,7 @@ impl Compiler {
fn process_js_inner(
&self,
program: Program,
src_map: Option<sourcemap::SourceMap>,
orig: Option<&sourcemap::SourceMap>,
config: BuiltConfig<impl Pass>,
) -> Result<TransformOutput, Error> {
self.run(|| {
@ -426,7 +426,7 @@ impl Compiler {
&program,
&self.comments,
config.source_maps,
src_map.as_ref(),
orig,
config.minify,
)
})

View File

@ -11,7 +11,7 @@ use swc::{
errors::{EmitterWriter, Handler, HandlerFlags, SourceMapperDyn},
FileName, FilePathMapping, SourceMap,
},
config::{InputSourceMap, Options, ParseOptions, SourceMapsConfig},
config::{Options, ParseOptions, SourceMapsConfig},
ecmascript::ast::Program,
Compiler,
};
@ -28,28 +28,11 @@ pub fn parse_sync(s: &str, opts: JsValue) -> Result<JsValue, JsValue> {
let (c, errors) = compiler();
let fm = c.cm.new_source_file(FileName::Anon, s.into());
let (prog, src_map) = c
.parse_js(
fm,
opts.target,
opts.syntax,
opts.is_module,
opts.comments,
&InputSourceMap::Bool(false),
)
let program = c
.parse_js(fm, opts.target, opts.syntax, opts.is_module, opts.comments)
.map_err(|err| format!("failed to parse: {}\n{}", err, errors))?;
let mut source_map = vec![];
if let Some(src_map) = src_map {
src_map
.to_writer(&mut source_map)
.map_err(|err| format!("failed to print source map file: {}", err))?;
}
Ok(
JsValue::from_serde(&(prog, &*String::from_utf8_lossy(&source_map)))
.map_err(|err| format!("failed to return value: {}", err))?,
)
Ok(JsValue::from_serde(&program).map_err(|err| format!("failed to return value: {}", err))?)
}
#[wasm_bindgen(js_name = "printSync")]