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

View File

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

View File

@ -101,6 +101,7 @@ fn default_is_module() -> bool {
true true
} }
/// Configuration related to source map generaged by swc.
#[derive(Clone, Serialize, Deserialize)] #[derive(Clone, Serialize, Deserialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum SourceMapsConfig { pub enum SourceMapsConfig {
@ -135,7 +136,7 @@ pub enum InputSourceMap {
impl Default for InputSourceMap { impl Default for InputSourceMap {
fn default() -> Self { 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_common as common;
pub use swc_ecmascript as ecmascript; pub use swc_ecmascript as ecmascript;
mod builder;
pub mod config;
pub use crate::builder::PassBuilder; pub use crate::builder::PassBuilder;
use crate::config::{ use crate::config::{
BuiltConfig, Config, ConfigFile, InputSourceMap, JscTarget, Merge, Options, Rc, RootMode, BuiltConfig, Config, ConfigFile, InputSourceMap, JscTarget, Merge, Options, Rc, RootMode,
SourceMapsConfig, SourceMapsConfig,
}; };
use anyhow::{Context, Error}; use anyhow::{bail, Context, Error};
use common::{ use common::{
comments::{Comment, Comments}, comments::{Comment, Comments},
errors::Handler, errors::Handler,
@ -41,6 +38,9 @@ use std::{
sync::Arc, sync::Arc,
}; };
mod builder;
pub mod config;
pub struct Compiler { pub struct Compiler {
/// swc uses rustc's span interning. /// 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 /// This method parses a javascript / typescript file
pub fn parse_js( pub fn parse_js(
&self, &self,
@ -89,63 +148,8 @@ impl Compiler {
syntax: Syntax, syntax: Syntax,
is_module: bool, is_module: bool,
parse_comments: bool, parse_comments: bool,
input_source_map: &InputSourceMap, ) -> Result<Program, Error> {
) -> Result<(Program, Option<sourcemap::SourceMap>), Error> {
self.run(|| { 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 { let session = ParseSess {
handler: &self.handler, handler: &self.handler,
}; };
@ -179,7 +183,7 @@ impl Compiler {
.map(Program::Script)? .map(Program::Script)?
}; };
Ok((program, orig)) Ok(program)
}) })
} }
@ -365,16 +369,16 @@ impl Compiler {
) -> Result<TransformOutput, Error> { ) -> Result<TransformOutput, Error> {
self.run(|| -> Result<_, Error> { self.run(|| -> Result<_, Error> {
let config = self.run(|| self.config_for_file(opts, &fm.name))?; 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(), fm.clone(),
config.target, config.target,
config.syntax, config.syntax,
config.is_module, config.is_module,
true, 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") .context("failed to process js file")
} }
@ -382,19 +386,15 @@ impl Compiler {
/// You can use custom pass with this method. /// You can use custom pass with this method.
/// ///
/// There exists a [PassBuilder] to help building custom passes. /// There exists a [PassBuilder] to help building custom passes.
pub fn process_js( pub fn process_js(&self, program: Program, opts: &Options) -> Result<TransformOutput, Error> {
&self,
program: Program,
src_map: Option<sourcemap::SourceMap>,
opts: &Options,
) -> Result<TransformOutput, Error> {
self.run(|| -> Result<_, Error> { self.run(|| -> Result<_, Error> {
let loc = self.cm.lookup_char_pos(program.span().lo()); let loc = self.cm.lookup_char_pos(program.span().lo());
let fm = loc.file; 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))?; 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") .context("failed to process js module")
} }
@ -402,7 +402,7 @@ impl Compiler {
fn process_js_inner( fn process_js_inner(
&self, &self,
program: Program, program: Program,
src_map: Option<sourcemap::SourceMap>, orig: Option<&sourcemap::SourceMap>,
config: BuiltConfig<impl Pass>, config: BuiltConfig<impl Pass>,
) -> Result<TransformOutput, Error> { ) -> Result<TransformOutput, Error> {
self.run(|| { self.run(|| {
@ -426,7 +426,7 @@ impl Compiler {
&program, &program,
&self.comments, &self.comments,
config.source_maps, config.source_maps,
src_map.as_ref(), orig,
config.minify, config.minify,
) )
}) })

View File

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