mirror of
https://github.com/swc-project/swc.git
synced 2024-11-23 17:54:15 +03:00
Handle input source map correctly (#830)
This commit is contained in:
parent
d1117350a9
commit
88edb71e0a
@ -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)
|
||||||
|
@ -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>",
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
146
src/lib.rs
146
src/lib.rs
@ -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,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -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")]
|
||||||
|
Loading…
Reference in New Issue
Block a user