mirror of
https://github.com/swc-project/swc.git
synced 2024-10-04 04:07:18 +03:00
fix(common): Fix source map generation with inputSourceMap
(#8546)
**Description:** - This PR fixes the source map generation when `inputSourceMap` is specified. - This PR fixes `minify()` not accepting parsed source map in the option. **Related issue:** - Closes #8372.
This commit is contained in:
parent
bfdfd3a26b
commit
043ee85d08
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -3963,9 +3963,12 @@ dependencies = [
|
||||
name = "swc_config"
|
||||
version = "0.1.9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap 2.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sourcemap",
|
||||
"swc_cached",
|
||||
"swc_config_macro",
|
||||
]
|
||||
|
||||
@ -4534,7 +4537,6 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"swc_atoms",
|
||||
"swc_cached",
|
||||
"swc_common",
|
||||
"swc_config",
|
||||
"swc_ecma_ast",
|
||||
|
@ -765,14 +765,8 @@ impl Compiler {
|
||||
.source_map
|
||||
.as_ref()
|
||||
.map(|obj| -> Result<_, Error> {
|
||||
let orig = obj
|
||||
.content
|
||||
.as_ref()
|
||||
.map(|s| sourcemap::SourceMap::from_slice(s.as_bytes()));
|
||||
let orig = match orig {
|
||||
Some(v) => Some(v?),
|
||||
None => None,
|
||||
};
|
||||
let orig = obj.content.as_ref().map(|s| s.to_sourcemap()).transpose()?;
|
||||
|
||||
Ok((SourceMapsConfig::Bool(true), orig))
|
||||
})
|
||||
.unwrap_as_option(|v| {
|
||||
|
23
crates/swc/tests/minify/issue-7475/issue-8372/1/config.json
Normal file
23
crates/swc/tests/minify/issue-7475/issue-8372/1/config.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"compress": {
|
||||
"negate_iife": false,
|
||||
"sequences": 30
|
||||
},
|
||||
"mangle": {
|
||||
"safari10": true
|
||||
},
|
||||
"output": {
|
||||
"semicolons": false
|
||||
},
|
||||
"sourceMap": {
|
||||
"filename": "input.js",
|
||||
"url": "input.map",
|
||||
"content": {
|
||||
"version": 3,
|
||||
"sources": ["input-preprocess.js"],
|
||||
"sourcesContent": ["const a = {aób: 'ó'}\n\nconsole.log(a)"],
|
||||
"names": ["console", "log", "aób"],
|
||||
"mappings": "AAEAA,QAAQC,GAAG,CAFD;IAACC,UAAK;AAAG"
|
||||
}
|
||||
}
|
||||
}
|
3
crates/swc/tests/minify/issue-7475/issue-8372/1/input.js
Normal file
3
crates/swc/tests/minify/issue-7475/issue-8372/1/input.js
Normal file
@ -0,0 +1,3 @@
|
||||
const a = { aób: 'ó' }
|
||||
|
||||
console.log(a)
|
@ -0,0 +1 @@
|
||||
let a={aób:"\xf3"};console.log(a);
|
16
crates/swc/tests/minify/issue-7475/issue-8372/1/output.map
Normal file
16
crates/swc/tests/minify/issue-7475/issue-8372/1/output.map
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"mappings": "AAEAA,IAAAA,EAAQC,CAFEC,IAAA,MAAA,EAASF,QAAAC,GAAA,CAAAE",
|
||||
"names": [
|
||||
"console",
|
||||
"log",
|
||||
"aób",
|
||||
"a"
|
||||
],
|
||||
"sources": [
|
||||
"input-preprocess.js"
|
||||
],
|
||||
"sourcesContent": [
|
||||
"const a = {aób: 'ó'}\n\nconsole.log(a)"
|
||||
],
|
||||
"version": 3
|
||||
}
|
12
crates/swc/tests/minify/issue-7475/issue-8372/2/config.json
Normal file
12
crates/swc/tests/minify/issue-7475/issue-8372/2/config.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"compress": {
|
||||
"negate_iife": false,
|
||||
"sequences": 30
|
||||
},
|
||||
"mangle": {
|
||||
"safari10": true
|
||||
},
|
||||
"output": {
|
||||
"semicolons": false
|
||||
}
|
||||
}
|
3
crates/swc/tests/minify/issue-7475/issue-8372/2/input.js
Normal file
3
crates/swc/tests/minify/issue-7475/issue-8372/2/input.js
Normal file
@ -0,0 +1,3 @@
|
||||
const a = { aób: 'ó' }
|
||||
|
||||
console.log(a)
|
@ -0,0 +1 @@
|
||||
let a={aób:"\xf3"};console.log(a);
|
16
crates/swc/tests/minify/issue-7475/issue-8372/2/output.map
Normal file
16
crates/swc/tests/minify/issue-7475/issue-8372/2/output.map
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"mappings": "AAAA,IAAMA,EAAI,CAAEC,IAAK,MAAI,EAErBC,QAAQC,GAAG,CAACH",
|
||||
"names": [
|
||||
"a",
|
||||
"aób",
|
||||
"console",
|
||||
"log"
|
||||
],
|
||||
"sources": [
|
||||
"$DIR/tests/minify/issue-7475/issue-8372/2/input.js"
|
||||
],
|
||||
"sourcesContent": [
|
||||
"const a = { aób: 'ó' }\n\nconsole.log(a)"
|
||||
],
|
||||
"version": 3
|
||||
}
|
@ -1128,10 +1128,13 @@ fn minify(input_js: PathBuf) {
|
||||
let c = Compiler::new(cm);
|
||||
let fm = c.cm.load_file(&input_js).unwrap();
|
||||
|
||||
let mut config: JsMinifyOptions =
|
||||
serde_json::from_str(&std::fs::read_to_string(&config_json_path).unwrap()).unwrap();
|
||||
let config_str = std::fs::read_to_string(&config_json_path).unwrap();
|
||||
let mut config: JsMinifyOptions = serde_json::from_str(&config_str).unwrap();
|
||||
|
||||
if config.source_map.inner().is_none() {
|
||||
config.source_map = BoolOrDataConfig::from_bool(true);
|
||||
}
|
||||
|
||||
config.source_map = BoolOrDataConfig::from_bool(true);
|
||||
let output = c.minify(fm, &handler, &config).unwrap();
|
||||
|
||||
NormalizedOutput::from(output.code)
|
||||
|
@ -4,3 +4,4 @@
|
||||
#![deny(warnings)]
|
||||
|
||||
pub mod regex;
|
||||
pub use anyhow::Error;
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Regex cache
|
||||
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
use std::{ops::Deref, str::FromStr, sync::Arc};
|
||||
|
||||
pub use anyhow::Error;
|
||||
use anyhow::{Context, Result};
|
||||
@ -77,3 +77,11 @@ impl From<&'_ str> for CachedRegex {
|
||||
Self::new(s).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for CachedRegex {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::new(s)
|
||||
}
|
||||
}
|
||||
|
@ -1269,11 +1269,13 @@ impl SourceMap {
|
||||
if config.skip(&f.name) {
|
||||
continue;
|
||||
}
|
||||
src_id = builder.add_source(&config.file_name_to_source(&f.name));
|
||||
if orig.is_none() {
|
||||
src_id = builder.add_source(&config.file_name_to_source(&f.name));
|
||||
|
||||
inline_sources_content = config.inline_sources_content(&f.name);
|
||||
if inline_sources_content && orig.is_none() {
|
||||
builder.set_source_contents(src_id, Some(&f.src));
|
||||
inline_sources_content = config.inline_sources_content(&f.name);
|
||||
if inline_sources_content && orig.is_none() {
|
||||
builder.set_source_contents(src_id, Some(&f.src));
|
||||
}
|
||||
}
|
||||
|
||||
ch_state = ByteToCharPosState::default();
|
||||
|
@ -9,10 +9,13 @@ repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.1.9"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
indexmap = "2.0.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
sourcemap = { version = "6.4", optional = true }
|
||||
|
||||
swc_cached = { version = "0.3.18", path = "../swc_cached" }
|
||||
swc_config_macro = { version = "0.1.3", path = "../swc_config_macro" }
|
||||
|
||||
[lib]
|
||||
|
@ -69,6 +69,14 @@ impl<T> BoolOrDataConfig<T> {
|
||||
pub fn into_inner(self) -> Option<BoolOr<T>> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> Option<BoolOr<&T>> {
|
||||
match &self.0 {
|
||||
Some(BoolOr::Data(v)) => Some(BoolOr::Data(v)),
|
||||
Some(BoolOr::Bool(b)) => Some(BoolOr::Bool(*b)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for BoolOrDataConfig<T> {
|
||||
|
@ -4,3 +4,10 @@
|
||||
mod macros;
|
||||
pub mod config_types;
|
||||
pub mod merge;
|
||||
#[cfg(feature = "sourcemap")]
|
||||
mod source_map;
|
||||
|
||||
pub use swc_cached::{regex::CachedRegex, Error};
|
||||
|
||||
#[cfg(feature = "sourcemap")]
|
||||
pub use crate::source_map::*;
|
||||
|
118
crates/swc_config/src/source_map.rs
Normal file
118
crates/swc_config/src/source_map.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use anyhow::{bail, Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sourcemap::{vlq::parse_vlq_segment, RawToken, SourceMap};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum SourceMapContent {
|
||||
Json(String),
|
||||
#[serde(rename_all = "camelCase")]
|
||||
Parsed {
|
||||
#[serde(default)]
|
||||
sources: Vec<String>,
|
||||
#[serde(default)]
|
||||
names: Vec<String>,
|
||||
#[serde(default)]
|
||||
mappings: String,
|
||||
#[serde(default)]
|
||||
file: Option<String>,
|
||||
#[serde(default)]
|
||||
source_root: Option<String>,
|
||||
#[serde(default)]
|
||||
sources_content: Option<Vec<Option<String>>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl SourceMapContent {
|
||||
pub fn to_sourcemap(&self) -> Result<SourceMap> {
|
||||
match self {
|
||||
SourceMapContent::Json(s) => {
|
||||
SourceMap::from_slice(s.as_bytes()).context("failed to parse sourcemap")
|
||||
}
|
||||
SourceMapContent::Parsed {
|
||||
sources,
|
||||
names,
|
||||
mappings,
|
||||
file,
|
||||
source_root,
|
||||
sources_content,
|
||||
} => {
|
||||
let mut dst_col;
|
||||
let mut src_id = 0;
|
||||
let mut src_line = 0;
|
||||
let mut src_col = 0;
|
||||
let mut name_id = 0;
|
||||
|
||||
let allocation_size = mappings.matches(&[',', ';'][..]).count() + 10;
|
||||
let mut tokens = Vec::with_capacity(allocation_size);
|
||||
|
||||
let mut nums = Vec::with_capacity(6);
|
||||
|
||||
for (dst_line, line) in mappings.split(';').enumerate() {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
dst_col = 0;
|
||||
|
||||
for segment in line.split(',') {
|
||||
if segment.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
nums.clear();
|
||||
nums = parse_vlq_segment(segment)?;
|
||||
dst_col = (i64::from(dst_col) + nums[0]) as u32;
|
||||
|
||||
let mut src = !0;
|
||||
let mut name = !0;
|
||||
|
||||
if nums.len() > 1 {
|
||||
if nums.len() != 4 && nums.len() != 5 {
|
||||
bail!(
|
||||
"invalid vlq segment size; expected 4 or 5, got {}",
|
||||
nums.len()
|
||||
);
|
||||
}
|
||||
src_id = (i64::from(src_id) + nums[1]) as u32;
|
||||
if src_id >= sources.len() as u32 {
|
||||
bail!("invalid source reference: {}", src_id);
|
||||
}
|
||||
|
||||
src = src_id;
|
||||
src_line = (i64::from(src_line) + nums[2]) as u32;
|
||||
src_col = (i64::from(src_col) + nums[3]) as u32;
|
||||
|
||||
if nums.len() > 4 {
|
||||
name_id = (i64::from(name_id) + nums[4]) as u32;
|
||||
if name_id >= names.len() as u32 {
|
||||
bail!("invalid name reference: {}", name_id);
|
||||
}
|
||||
name = name_id;
|
||||
}
|
||||
}
|
||||
|
||||
tokens.push(RawToken {
|
||||
dst_line: dst_line as u32,
|
||||
dst_col,
|
||||
src_line,
|
||||
src_col,
|
||||
src_id: src,
|
||||
name_id: name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let mut map = SourceMap::new(
|
||||
file.clone(),
|
||||
tokens,
|
||||
names.clone(),
|
||||
sources.clone(),
|
||||
sources_content.clone(),
|
||||
);
|
||||
map.set_source_root(source_root.clone());
|
||||
Ok(map)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -51,9 +51,10 @@ serde_json = "1.0.61"
|
||||
tracing = "0.1.37"
|
||||
|
||||
swc_atoms = { version = "0.6.5", path = "../swc_atoms" }
|
||||
swc_cached = { version = "0.3.18", path = "../swc_cached" }
|
||||
swc_common = { version = "0.33.15", path = "../swc_common" }
|
||||
swc_config = { version = "0.1.9", path = "../swc_config" }
|
||||
swc_config = { version = "0.1.9", path = "../swc_config", features = [
|
||||
"sourcemap",
|
||||
] }
|
||||
swc_ecma_ast = { version = "0.111.1", path = "../swc_ecma_ast", features = [
|
||||
"serde",
|
||||
] }
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! NOT A PUBLIC API
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use swc_config::config_types::BoolOrDataConfig;
|
||||
use swc_config::{config_types::BoolOrDataConfig, SourceMapContent};
|
||||
|
||||
use crate::option::{
|
||||
terser::{TerserCompressorOptions, TerserEcmaVersion},
|
||||
@ -59,6 +59,8 @@ fn true_by_default() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// `sourceMap` of `minify()`.`
|
||||
///
|
||||
/// `jsc.minify.sourceMap`
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
||||
@ -73,7 +75,7 @@ pub struct TerserSourceMapOption {
|
||||
pub root: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub content: Option<String>,
|
||||
pub content: Option<SourceMapContent>,
|
||||
}
|
||||
|
||||
/// Parser options for `minify()`, which should have the same API as terser.
|
||||
|
@ -2,9 +2,8 @@
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use swc_atoms::JsWord;
|
||||
use swc_cached::regex::CachedRegex;
|
||||
use swc_common::{collections::AHashMap, Mark};
|
||||
use swc_config::merge::Merge;
|
||||
use swc_config::{merge::Merge, CachedRegex};
|
||||
use swc_ecma_ast::{EsVersion, Expr};
|
||||
|
||||
/// Implement default using serde.
|
||||
|
Loading…
Reference in New Issue
Block a user