diff --git a/crates/swc_ecma_minifier/src/analyzer/mod.rs b/crates/swc_ecma_minifier/src/analyzer/mod.rs index c9b4447b6da0..c3aa336dc78b 100644 --- a/crates/swc_ecma_minifier/src/analyzer/mod.rs +++ b/crates/swc_ecma_minifier/src/analyzer/mod.rs @@ -1150,6 +1150,14 @@ where n.visit_children_with(&mut *self.with_ctx(ctx)) } + fn visit_script(&mut self, n: &Script) { + let ctx = Ctx { + skip_standalone: true, + ..self.ctx + }; + n.visit_children_with(&mut *self.with_ctx(ctx)) + } + fn visit_named_export(&mut self, n: &NamedExport) { if n.src.is_some() { return; diff --git a/crates/swc_ecma_minifier/src/compress/mod.rs b/crates/swc_ecma_minifier/src/compress/mod.rs index 62e8586c9609..553fe940ef27 100644 --- a/crates/swc_ecma_minifier/src/compress/mod.rs +++ b/crates/swc_ecma_minifier/src/compress/mod.rs @@ -336,6 +336,10 @@ where { noop_visit_mut_type!(); + fn visit_mut_script(&mut self, n: &mut Script) { + self.optimize_unit_repeatedly(n); + } + fn visit_mut_module(&mut self, n: &mut Module) { self.optimize_unit_repeatedly(n); } diff --git a/crates/swc_ecma_minifier/src/debug.rs b/crates/swc_ecma_minifier/src/debug.rs index 5b2bd547edad..8decb9502cb2 100644 --- a/crates/swc_ecma_minifier/src/debug.rs +++ b/crates/swc_ecma_minifier/src/debug.rs @@ -66,7 +66,7 @@ where /// /// If the cargo feature `debug` is disabled or the environment variable /// `SWC_RUN` is not `1`, this function is noop. -pub(crate) fn invoke(module: &Module) { +pub(crate) fn invoke_module(module: &Module) { debug_assert_valid(module); let _noop_sub = tracing::subscriber::set_default(tracing::subscriber::NoSubscriber::default()); @@ -153,3 +153,94 @@ pub(crate) fn invoke(module: &Module) { ) } } + +/// Invokes code using node.js. +/// +/// If the cargo feature `debug` is disabled or the environment variable +/// `SWC_RUN` is not `1`, this function is noop. +pub(crate) fn invoke_script(script: &Script) { + debug_assert_valid(script); + + let _noop_sub = tracing::subscriber::set_default(tracing::subscriber::NoSubscriber::default()); + + let should_run = + cfg!(debug_assertions) && cfg!(feature = "debug") && option_env!("SWC_RUN") == Some("1"); + let should_check = cfg!(debug_assertions) && option_env!("SWC_CHECK") == Some("1"); + + if !should_run && !should_check { + return; + } + + let script = script + .clone() + .fold_with(&mut hygiene()) + .fold_with(&mut fixer(None)); + let script = drop_span(script); + + let mut buf = vec![]; + let cm = Lrc::new(SourceMap::default()); + + { + let mut emitter = Emitter { + cfg: Default::default(), + cm: cm.clone(), + comments: None, + wr: Box::new(JsWriter::new(cm, "\n", &mut buf, None)), + }; + + emitter.emit_script(&script).unwrap(); + } + + let code = String::from_utf8(buf).unwrap(); + + debug!("Validating with node.js:\n{}", code); + + if should_check { + let mut child = Command::new("node") + .arg("-") + .arg("--check") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("failed to spawn node"); + + { + let child_stdin = child.stdin.as_mut().unwrap(); + child_stdin + .write_all(code.as_bytes()) + .expect("failed to write"); + } + + let output = child.wait_with_output().expect("failed to check syntax"); + + if !output.status.success() { + panic!( + "[SWC_CHECK] Failed to validate code:\n{}\n===== ===== ===== ===== =====\n{}\n{}", + code, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + ); + } + } else { + let output = Command::new("node") + .arg("-e") + .arg(&code) + .output() + .expect("[SWC_RUN] failed to validate code using `node`"); + if !output.status.success() { + panic!( + "[SWC_RUN] Failed to validate code:\n{}\n===== ===== ===== ===== =====\n{}\n{}", + code, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr), + ); + } + + tracing::info!( + "[SWC_RUN]\n{}\n{}", + code, + String::from_utf8_lossy(&output.stdout) + ) + } +} diff --git a/crates/swc_ecma_minifier/src/lib.rs b/crates/swc_ecma_minifier/src/lib.rs index 427192037f8f..f0418bbf9bd3 100644 --- a/crates/swc_ecma_minifier/src/lib.rs +++ b/crates/swc_ecma_minifier/src/lib.rs @@ -87,7 +87,7 @@ pub(crate) static HEAVY_TASK_PARALLELS: Lazy = Lazy::new(|| *CPU_COUNT * pub(crate) static LIGHT_TASK_PARALLELS: Lazy = Lazy::new(|| *CPU_COUNT * 100); pub fn optimize( - mut m: Program, + mut n: Program, _cm: Lrc, comments: Option<&dyn Comments>, mut timings: Option<&mut Timings>, @@ -99,7 +99,7 @@ pub fn optimize( let mut marks = Marks::new(); marks.unresolved_mark = extra.unresolved_mark; - debug_assert_valid(&m); + debug_assert_valid(&n); if let Some(defs) = options.compress.as_ref().map(|c| &c.global_defs) { let _timer = timer!("inline global defs"); @@ -111,7 +111,7 @@ pub fn optimize( if !defs.is_empty() { let defs = defs.iter().map(|(k, v)| (k.clone(), v.clone())).collect(); - m.visit_mut_with(&mut global_defs::globals_defs( + n.visit_mut_with(&mut global_defs::globals_defs( defs, extra.unresolved_mark, extra.top_level_mark, @@ -119,7 +119,7 @@ pub fn optimize( } } - let module_info = match &m { + let module_info = match &n { Program::Script(_) => ModuleInfo::default(), Program::Module(m) => ModuleInfo { blackbox_imports: m @@ -162,20 +162,20 @@ pub fn optimize( if let Some(_options) = &options.compress { let _timer = timer!("precompress"); - m.visit_mut_with(&mut precompress_optimizer()); - debug_assert_valid(&m); + n.visit_mut_with(&mut precompress_optimizer()); + debug_assert_valid(&n); } if options.compress.is_some() { - m.visit_mut_with(&mut info_marker( + n.visit_mut_with(&mut info_marker( options.compress.as_ref(), comments, marks, extra.unresolved_mark, )); - debug_assert_valid(&m); + debug_assert_valid(&n); } - m.visit_mut_with(&mut unique_scope()); + n.visit_mut_with(&mut unique_scope()); if options.wrap { // TODO: wrap_common_js @@ -191,8 +191,8 @@ pub fn optimize( } if let Some(options) = &options.compress { if options.unused { - perform_dce(&mut m, options, extra); - debug_assert_valid(&m); + perform_dce(&mut n, options, extra); + debug_assert_valid(&n); } } @@ -207,7 +207,7 @@ pub fn optimize( if options.rename && DISABLE_BUGGY_PASSES { // toplevel.figure_out_scope(options.mangle); // TODO: Pass `options.mangle` to name expander. - m.visit_mut_with(&mut name_expander()); + n.visit_mut_with(&mut name_expander()); } if let Some(ref mut t) = timings { @@ -217,14 +217,14 @@ pub fn optimize( { let _timer = timer!("compress ast"); - m.visit_mut_with(&mut compressor(&module_info, marks, options, &Minification)) + n.visit_mut_with(&mut compressor(&module_info, marks, options, &Minification)) } // Again, we don't need to validate ast let _timer = timer!("postcompress"); - m.visit_mut_with(&mut postcompress_optimizer(options)); + n.visit_mut_with(&mut postcompress_optimizer(options)); let mut pass = 0; loop { @@ -241,7 +241,7 @@ pub fn optimize( debug_infinite_loop: false, }, ); - m.visit_mut_with(&mut v); + n.visit_mut_with(&mut v); if !v.changed() || options.passes <= pass { break; } @@ -263,30 +263,30 @@ pub fn optimize( let _timer = timer!("mangle names"); // TODO: base54.reset(); - let preserved = idents_to_preserve(mangle.clone(), &m); + let preserved = idents_to_preserve(mangle.clone(), &n); let chars = CharFreq::compute( - &m, + &n, &preserved, SyntaxContext::empty().apply_mark(marks.unresolved_mark), ) .compile(); - m.visit_mut_with(&mut name_mangler(mangle.clone(), preserved, chars)); + n.visit_mut_with(&mut name_mangler(mangle.clone(), preserved, chars)); if let Some(property_mangle_options) = &mangle.props { - mangle_properties(&mut m, &module_info, property_mangle_options.clone(), chars); + mangle_properties(&mut n, &module_info, property_mangle_options.clone(), chars); } } - m.visit_mut_with(&mut merge_exports()); + n.visit_mut_with(&mut merge_exports()); if let Some(ref mut t) = timings { t.section("hygiene"); t.end_section(); } - m + n } fn perform_dce(m: &mut Program, options: &CompressOptions, extra: &ExtraOptions) { diff --git a/crates/swc_ecma_minifier/src/metadata/mod.rs b/crates/swc_ecma_minifier/src/metadata/mod.rs index 9f72e4105a45..5feaa1cede55 100644 --- a/crates/swc_ecma_minifier/src/metadata/mod.rs +++ b/crates/swc_ecma_minifier/src/metadata/mod.rs @@ -191,12 +191,23 @@ impl VisitMut for InfoMarker<'_> { fn visit_mut_lit(&mut self, _: &mut Lit) {} - fn visit_mut_module(&mut self, m: &mut Module) { - m.visit_mut_children_with(self); + fn visit_mut_script(&mut self, n: &mut Script) { + n.visit_mut_children_with(self); + + if self.state.is_bundle { + tracing::info!("Running minifier in the bundle mode"); + n.span = n.span.apply_mark(self.marks.bundle_of_standalone); + } else { + tracing::info!("Running minifier in the normal mode"); + } + } + + fn visit_mut_module(&mut self, n: &mut Module) { + n.visit_mut_children_with(self); if self.state.is_bundle { tracing::info!("Running minifier in the bundle mode"); - m.span = m.span.apply_mark(self.marks.bundle_of_standalone); + n.span = n.span.apply_mark(self.marks.bundle_of_standalone); } else { tracing::info!("Running minifier in the normal mode"); } diff --git a/crates/swc_ecma_minifier/src/pass/mangle_names/preserver.rs b/crates/swc_ecma_minifier/src/pass/mangle_names/preserver.rs index 283299c0ecd1..08a35f2d2809 100644 --- a/crates/swc_ecma_minifier/src/pass/mangle_names/preserver.rs +++ b/crates/swc_ecma_minifier/src/pass/mangle_names/preserver.rs @@ -121,6 +121,13 @@ impl Visit for Preserver { fn visit_ident(&mut self, _: &Ident) {} + fn visit_script(&mut self, n: &Script) { + for n in n.body.iter() { + self.in_top_level = true; + n.visit_with(self); + } + } + fn visit_module_items(&mut self, n: &[ModuleItem]) { for n in n { self.in_top_level = true; diff --git a/crates/swc_ecma_minifier/src/util/unit.rs b/crates/swc_ecma_minifier/src/util/unit.rs index f50be0a4be74..107b01ac1f56 100644 --- a/crates/swc_ecma_minifier/src/util/unit.rs +++ b/crates/swc_ecma_minifier/src/util/unit.rs @@ -69,7 +69,42 @@ impl CompileUnit for Module { { self.visit_mut_with(&mut *visitor); - crate::debug::invoke(self); + crate::debug::invoke_module(self); + } + + fn remove_mark(&mut self) -> Mark { + Mark::root() + } +} + +impl CompileUnit for Script { + fn is_module() -> bool { + false + } + + fn force_dump(&self) -> String { + let _noop_sub = + tracing::subscriber::set_default(tracing::subscriber::NoSubscriber::default()); + + dump( + &self + .clone() + .fold_with(&mut fixer(None)) + .fold_with(&mut hygiene()) + .fold_with(&mut as_folder(DropSpan { + preserve_ctxt: false, + })), + true, + ) + } + + fn apply(&mut self, visitor: &mut V) + where + V: VisitMut, + { + self.visit_mut_with(&mut *visitor); + + crate::debug::invoke_script(self); } fn remove_mark(&mut self) -> Mark { diff --git a/crates/swc_ecma_transforms_base/src/rename/mod.rs b/crates/swc_ecma_transforms_base/src/rename/mod.rs index e1686a445d5d..19bd3019b5e8 100644 --- a/crates/swc_ecma_transforms_base/src/rename/mod.rs +++ b/crates/swc_ecma_transforms_base/src/rename/mod.rs @@ -242,12 +242,11 @@ where if contains_eval(s, true) { s.visit_mut_children_with(self); - return; - } - - let map = self.get_map(s, false, true); + } else { + let map = self.get_map(s, false, true); - s.visit_mut_with(&mut rename_with_config(&map, self.config.clone())); + s.visit_mut_with(&mut rename_with_config(&map, self.config.clone())); + } } } diff --git a/crates/swc_html_minifier/tests/fixture/attribute/javascript-prefix/output.min.html b/crates/swc_html_minifier/tests/fixture/attribute/javascript-prefix/output.min.html index 176f7a3cd6d7..408869084607 100644 --- a/crates/swc_html_minifier/tests/fixture/attribute/javascript-prefix/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/attribute/javascript-prefix/output.min.html @@ -8,5 +8,5 @@
test
- Click me - Click me \ No newline at end of file + Click me + Click me \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/attribute/script-type/output.min.html b/crates/swc_html_minifier/tests/fixture/attribute/script-type/output.min.html index 0827cecf8365..560c6b776f6a 100644 --- a/crates/swc_html_minifier/tests/fixture/attribute/script-type/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/attribute/script-type/output.min.html @@ -1,6 +1,6 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/element/script-group/output.min.html b/crates/swc_html_minifier/tests/fixture/element/script-group/output.min.html index 761bfadc4b90..6da26e2abce7 100644 --- a/crates/swc_html_minifier/tests/fixture/element/script-group/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/element/script-group/output.min.html @@ -1,33 +1,33 @@ Document
breaker
- +
breaker
- +
breaker
- +
breaker
- +
breaker
- +
breaker
- +
breaker
- +
breaker
- +
breaker
@@ -35,26 +35,26 @@
breaker
- +
breaker
- +
breaker
breaker
- +
breaker
- +
breaker
breaker
- +
breaker
@@ -63,34 +63,34 @@
breaker
- +
breaker
breaker
- +
breaker
breaker
- +
breaker
- +
breaker
- +
breaker
breaker
- +
breaker
- +
breaker
diff --git a/crates/swc_html_minifier/tests/fixture/element/script-options-2/config.json b/crates/swc_html_minifier/tests/fixture/element/script-options-2/config.json new file mode 100644 index 000000000000..206c9ac16fa8 --- /dev/null +++ b/crates/swc_html_minifier/tests/fixture/element/script-options-2/config.json @@ -0,0 +1,17 @@ +{ + "minifyJs": { + "parser": { + "comments": true, + "syntax": "ecmascript", + "target": "es2022" + }, + "minifier": { + "mangle": { + "topLevel": true + } + }, + "codegen": { + "asciiOnly": true + } + } +} diff --git a/crates/swc_html_minifier/tests/fixture/element/script-options-2/input.html b/crates/swc_html_minifier/tests/fixture/element/script-options-2/input.html new file mode 100644 index 000000000000..9b1789da264e --- /dev/null +++ b/crates/swc_html_minifier/tests/fixture/element/script-options-2/input.html @@ -0,0 +1,36 @@ + + + + Document + + + +
Script:
+ + +
Module:
+ + + \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/element/script-options-2/output.min.html b/crates/swc_html_minifier/tests/fixture/element/script-options-2/output.min.html new file mode 100644 index 000000000000..6d41c04d5b3d --- /dev/null +++ b/crates/swc_html_minifier/tests/fixture/element/script-options-2/output.min.html @@ -0,0 +1,5 @@ +Document
Script:
+ + +
Module:
+ \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/element/script/input.html b/crates/swc_html_minifier/tests/fixture/element/script/input.html index 85d4becb9c3e..8ebf37e3d74a 100644 --- a/crates/swc_html_minifier/tests/fixture/element/script/input.html +++ b/crates/swc_html_minifier/tests/fixture/element/script/input.html @@ -186,5 +186,32 @@

Party coffee cake recipe

test
+
topLevel - script
+ +
topLevel - module
+ + diff --git a/crates/swc_html_minifier/tests/fixture/element/script/output.min.html b/crates/swc_html_minifier/tests/fixture/element/script/output.min.html index ce945121482d..0b9477f4835f 100644 --- a/crates/swc_html_minifier/tests/fixture/element/script/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/element/script/output.min.html @@ -19,7 +19,7 @@

Party coffee cake recipe

+

Party coffee cake recipe

by Mary Stone, 2018-03-10

@@ -54,5 +54,10 @@ alert('test') - -
test
\ No newline at end of file + +
test
+ +
topLevel - script
+ +
topLevel - module
+ \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-advanced-conservative/output.min.html b/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-advanced-conservative/output.min.html index 40772446a8fc..1b4487d9c299 100644 --- a/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-advanced-conservative/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-advanced-conservative/output.min.html @@ -29,4 +29,4 @@ foo baz -
a c
Empty
a b
a b c d
text
text text
test
test test
\ No newline at end of file +
a c
Empty
a b
a b c d
text
text text
test
test test
\ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-smart/output.min.html b/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-smart/output.min.html index 91a99a487500..c7ef9c917cf7 100644 --- a/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-smart/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/text/collapse-whitespace-smart/output.min.html @@ -29,4 +29,4 @@ foo baz -
a c
Empty
a b
a b c d
text
text text
test test
test testtest
test test
test test
test test
test test
testtest test
test
test test
test
test
test test test test test This is bold and red This is bold and red testtestThis is bold and red This is bold and red
test a b test test a b test test a b test
test test
a
\ No newline at end of file +
a c
Empty
a b
a b c d
text
text text
test test
test testtest
test test
test test
test test
test test
testtest test
test
test test
test
test
test test test test test This is bold and red This is bold and red testtestThis is bold and red This is bold and red
test a b test test a b test test a b test
test test
a
\ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/text/slot-3/output.min.html b/crates/swc_html_minifier/tests/fixture/text/slot-3/output.min.html index 4e721de55e94..c1f13e419e9a 100644 --- a/crates/swc_html_minifier/tests/fixture/text/slot-3/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/text/slot-3/output.min.html @@ -1 +1 @@ -element-details - web component using <template> and <slot>

element-details - web component using <template> and <slot>

test foo \ No newline at end of file +element-details - web component using <template> and <slot>

element-details - web component using <template> and <slot>

test foo \ No newline at end of file diff --git a/crates/swc_html_minifier/tests/fixture/text/slot/output.min.html b/crates/swc_html_minifier/tests/fixture/text/slot/output.min.html index 877312d0a282..4a2f5c3a928b 100644 --- a/crates/swc_html_minifier/tests/fixture/text/slot/output.min.html +++ b/crates/swc_html_minifier/tests/fixture/text/slot/output.min.html @@ -1 +1 @@ -element-details - web component using <template> and <slot>

element-details - web component using <template> and <slot>

1 2 3 4 \ No newline at end of file +element-details - web component using <template> and <slot>

element-details - web component using <template> and <slot>

1 2 3 4 \ No newline at end of file