Skip to content

Commit

Permalink
fix(common): Fix source map generation with inputSourceMap (#8546)
Browse files Browse the repository at this point in the history
**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.
  • Loading branch information
kdy1 authored Jan 24, 2024
1 parent bfdfd3a commit 043ee85
Show file tree
Hide file tree
Showing 21 changed files with 246 additions and 23 deletions.
4 changes: 3 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 2 additions & 8 deletions crates/swc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand Down
23 changes: 23 additions & 0 deletions crates/swc/tests/minify/issue-7475/issue-8372/1/config.json
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions crates/swc/tests/minify/issue-7475/issue-8372/1/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const a = { aób: 'ó' }

console.log(a)
1 change: 1 addition & 0 deletions crates/swc/tests/minify/issue-7475/issue-8372/1/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let a={aób:"\xf3"};console.log(a);
16 changes: 16 additions & 0 deletions crates/swc/tests/minify/issue-7475/issue-8372/1/output.map
Original file line number Diff line number Diff line change
@@ -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 changes: 12 additions & 0 deletions crates/swc/tests/minify/issue-7475/issue-8372/2/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"compress": {
"negate_iife": false,
"sequences": 30
},
"mangle": {
"safari10": true
},
"output": {
"semicolons": false
}
}
3 changes: 3 additions & 0 deletions crates/swc/tests/minify/issue-7475/issue-8372/2/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const a = { aób: 'ó' }

console.log(a)
1 change: 1 addition & 0 deletions crates/swc/tests/minify/issue-7475/issue-8372/2/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
let a={aób:"\xf3"};console.log(a);
16 changes: 16 additions & 0 deletions crates/swc/tests/minify/issue-7475/issue-8372/2/output.map
Original file line number Diff line number Diff line change
@@ -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
}
9 changes: 6 additions & 3 deletions crates/swc/tests/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions crates/swc_cached/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
#![deny(warnings)]

pub mod regex;
pub use anyhow::Error;
10 changes: 9 additions & 1 deletion crates/swc_cached/src/regex.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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)
}
}
10 changes: 6 additions & 4 deletions crates/swc_common/src/source_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions crates/swc_config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
8 changes: 8 additions & 0 deletions crates/swc_config/src/config_types/bool_or_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
7 changes: 7 additions & 0 deletions crates/swc_config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 118 additions & 0 deletions crates/swc_config/src/source_map.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
}
5 changes: 3 additions & 2 deletions crates/swc_ecma_minifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
] }
Expand Down
6 changes: 4 additions & 2 deletions crates/swc_ecma_minifier/src/js.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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")]
Expand All @@ -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.
Expand Down
3 changes: 1 addition & 2 deletions crates/swc_ecma_minifier/src/option/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit 043ee85

Please sign in to comment.