mirror of
https://github.com/swc-project/swc.git
synced 2024-11-23 09:38:16 +03:00
fix(es/minifier): Fix minifier using Deno
test suite (#2503)
swc_ecma_codegen: - Emit `;` after private class properties. swc_ecma_minifier: - `pure`: Drop more invalid expressions. - `sequences`: Drop more invalid expressions. - `strings`: Fix concat. - `inline`: Inline into `b` in `a[b] = foo`.
This commit is contained in:
parent
a9869e60f2
commit
9e215769cc
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -2456,6 +2456,7 @@ dependencies = [
|
||||
"swc_ecma_ast",
|
||||
"swc_ecma_codegen",
|
||||
"swc_ecma_loader",
|
||||
"swc_ecma_minifier",
|
||||
"swc_ecma_parser",
|
||||
"swc_ecma_transforms_base",
|
||||
"swc_ecma_transforms_optimization",
|
||||
@ -2601,7 +2602,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_codegen"
|
||||
version = "0.77.1"
|
||||
version = "0.77.2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"memchr",
|
||||
@ -2678,7 +2679,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_minifier"
|
||||
version = "0.44.3"
|
||||
version = "0.44.4"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
"anyhow",
|
||||
@ -3536,7 +3537,7 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "wasm"
|
||||
version = "1.2.102"
|
||||
version = "1.2.103"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"console_error_panic_hook",
|
||||
|
@ -55,6 +55,7 @@ path-clean = "=0.1.0"
|
||||
reqwest = {version = "0.11.4", features = ["blocking"]}
|
||||
sha-1 = "0.9"
|
||||
swc_ecma_loader = {version = "0.22.0", path = "../ecmascript/loader", features = ["node", "lru"]}
|
||||
swc_ecma_minifier = {version = "0.44.3", path = "../ecmascript/minifier"}
|
||||
swc_ecma_transforms_react = {version = "0.53.0", path = "../ecmascript/transforms/react"}
|
||||
swc_ecma_transforms_typescript = {version = "0.54.0", path = "../ecmascript/transforms/typescript"}
|
||||
swc_node_base = {version = "0.5.0", path = "../node/base"}
|
||||
|
@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
export CI=1
|
||||
cargo test --test fixture
|
||||
cargo test --test deno
|
7
bundler/scripts/minifier.sh
Executable file
7
bundler/scripts/minifier.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
export RUST_LOG=swc_ecma_minifier=debug
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
cargo test --features swc_ecma_minifier/debug --test deno $@
|
@ -14,11 +14,15 @@ use std::{
|
||||
};
|
||||
use swc_atoms::js_word;
|
||||
use swc_bundler::{Bundler, Load, ModuleRecord};
|
||||
use swc_common::{collections::AHashSet, FileName, Span, GLOBALS};
|
||||
use swc_common::{collections::AHashSet, FileName, Mark, Span, GLOBALS};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
|
||||
use swc_ecma_codegen::{
|
||||
text_writer::{omit_trailing_semi, JsWriter, WriteJs},
|
||||
Emitter,
|
||||
};
|
||||
use swc_ecma_transforms_base::{fixer::fixer, resolver::resolver_with_mark};
|
||||
use swc_ecma_utils::{find_ids, Id};
|
||||
use swc_ecma_visit::{Node, Visit, VisitWith};
|
||||
use swc_ecma_visit::{Node, Visit, VisitMutWith, VisitWith};
|
||||
use testing::assert_eq;
|
||||
|
||||
#[path = "common/mod.rs"]
|
||||
@ -978,7 +982,7 @@ fn run(url: &str, exports: &[&str]) {
|
||||
let path = dir.path().join("main.js");
|
||||
// println!("{}", path.display());
|
||||
|
||||
let src = bundle(url);
|
||||
let src = bundle(url, false);
|
||||
write(&path, &src).unwrap();
|
||||
|
||||
::testing::run_test2(false, |cm, _| {
|
||||
@ -1017,7 +1021,7 @@ fn run(url: &str, exports: &[&str]) {
|
||||
assert!(output.success());
|
||||
}
|
||||
|
||||
fn bundle(url: &str) -> String {
|
||||
fn bundle(url: &str, minify: bool) -> String {
|
||||
let result = testing::run_test2(false, |cm, _handler| {
|
||||
GLOBALS.with(|globals| {
|
||||
let mut bundler = Bundler::new(
|
||||
@ -1042,15 +1046,42 @@ fn bundle(url: &str) -> String {
|
||||
},
|
||||
);
|
||||
let output = bundler.bundle(entries).unwrap();
|
||||
let module = output.into_iter().next().unwrap().module;
|
||||
let mut module = output.into_iter().next().unwrap().module;
|
||||
|
||||
if minify {
|
||||
let top_level_mark = Mark::fresh(Mark::root());
|
||||
|
||||
module.visit_mut_with(&mut resolver_with_mark(top_level_mark));
|
||||
|
||||
module = swc_ecma_minifier::optimize(
|
||||
module,
|
||||
cm.clone(),
|
||||
None,
|
||||
None,
|
||||
&swc_ecma_minifier::option::MinifyOptions {
|
||||
compress: Some(Default::default()),
|
||||
mangle: Some(Default::default()),
|
||||
..Default::default()
|
||||
},
|
||||
&swc_ecma_minifier::option::ExtraOptions { top_level_mark },
|
||||
);
|
||||
module.visit_mut_with(&mut fixer(None));
|
||||
}
|
||||
|
||||
let mut buf = vec![];
|
||||
{
|
||||
let mut wr: Box<dyn WriteJs> =
|
||||
Box::new(JsWriter::new(cm.clone(), "\n", &mut buf, None));
|
||||
|
||||
if minify {
|
||||
wr = Box::new(omit_trailing_semi(wr));
|
||||
}
|
||||
|
||||
Emitter {
|
||||
cfg: swc_ecma_codegen::Config { minify: false },
|
||||
cfg: swc_ecma_codegen::Config { minify },
|
||||
cm: cm.clone(),
|
||||
comments: None,
|
||||
wr: Box::new(JsWriter::new(cm.clone(), "\n", &mut buf, None)),
|
||||
wr,
|
||||
}
|
||||
.emit_module(&module)
|
||||
.unwrap();
|
||||
@ -1166,7 +1197,38 @@ fn exec(input: PathBuf) {
|
||||
let path = dir.path().join("main.js");
|
||||
println!("{}", path.display());
|
||||
|
||||
let src = bundle(&input.to_string_lossy());
|
||||
let src = bundle(&input.to_string_lossy(), false);
|
||||
write(&path, &src).unwrap();
|
||||
|
||||
// println!("{}", src);
|
||||
|
||||
let output = Command::new("deno")
|
||||
.arg("run")
|
||||
.arg("--no-check")
|
||||
.arg("--allow-net")
|
||||
.arg(&path)
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.status()
|
||||
.unwrap();
|
||||
|
||||
std::mem::forget(dir);
|
||||
|
||||
assert!(output.success());
|
||||
}
|
||||
|
||||
#[testing::fixture("tests/deno-exec/**/entry.ts")]
|
||||
fn exec_minified(input: PathBuf) {
|
||||
// TODO: Fix the bug.
|
||||
if input.to_string_lossy().contains("deno-9464") {
|
||||
return;
|
||||
}
|
||||
|
||||
let dir = tempfile::tempdir().expect("failed to crate temp file");
|
||||
let path = dir.path().join("main.js");
|
||||
println!("{}", path.display());
|
||||
|
||||
let src = bundle(&input.to_string_lossy(), true);
|
||||
write(&path, &src).unwrap();
|
||||
|
||||
// println!("{}", src);
|
||||
|
@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs"]
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_ecma_codegen"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.77.1"
|
||||
version = "0.77.2"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1"
|
||||
|
@ -1189,7 +1189,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
formatting_semi!();
|
||||
semi!();
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
|
@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs", "src/lists/*.json"]
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_ecma_minifier"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.44.3"
|
||||
version = "0.44.4"
|
||||
|
||||
[features]
|
||||
debug = ["backtrace"]
|
||||
|
@ -27,7 +27,7 @@ where
|
||||
return;
|
||||
}
|
||||
|
||||
if self.ctx.is_delete_arg || self.ctx.is_update_arg || self.ctx.in_lhs_of_assign {
|
||||
if self.ctx.is_delete_arg || self.ctx.is_update_arg || self.ctx.is_lhs_of_assign {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ where
|
||||
return;
|
||||
}
|
||||
|
||||
if self.ctx.is_delete_arg || self.ctx.is_update_arg || self.ctx.in_lhs_of_assign {
|
||||
if self.ctx.is_delete_arg || self.ctx.is_update_arg || self.ctx.is_lhs_of_assign {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -307,7 +307,7 @@ where
|
||||
return;
|
||||
}
|
||||
|
||||
if self.ctx.is_delete_arg || self.ctx.is_update_arg || self.ctx.in_lhs_of_assign {
|
||||
if self.ctx.is_delete_arg || self.ctx.is_update_arg || self.ctx.is_lhs_of_assign {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -93,16 +93,18 @@ where
|
||||
match &p.key {
|
||||
PropName::Str(s) => {
|
||||
tracing::trace!(
|
||||
"hoist_props: Storing a varaible to inline \
|
||||
properties"
|
||||
"hoist_props: Storing a variable (`{}`) to inline \
|
||||
properties",
|
||||
name.id
|
||||
);
|
||||
self.simple_props
|
||||
.insert((name.to_id(), s.value.clone()), value);
|
||||
}
|
||||
PropName::Ident(i) => {
|
||||
tracing::trace!(
|
||||
"hoist_props: Storing a varaible to inline \
|
||||
properties"
|
||||
"hoist_props: Storing a variable(`{}`) to inline \
|
||||
properties",
|
||||
name.id
|
||||
);
|
||||
self.simple_props
|
||||
.insert((name.to_id(), i.sym.clone()), value);
|
||||
|
@ -554,7 +554,7 @@ where
|
||||
{
|
||||
match &*value {
|
||||
Expr::Lit(Lit::Num(..)) => {
|
||||
if self.ctx.in_lhs_of_assign {
|
||||
if self.ctx.is_lhs_of_assign {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ where
|
||||
|
||||
///
|
||||
/// - `while(false) { var a; foo() }` => `var a;`
|
||||
pub(super) fn optiimze_loops_if_cond_is_false(&mut self, stmt: &mut Stmt) {
|
||||
pub(super) fn optimize_loops_if_cond_is_false(&mut self, stmt: &mut Stmt) {
|
||||
if !self.options.loops {
|
||||
return;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use self::util::MultiReplacer;
|
||||
use crate::{
|
||||
analyzer::{ProgramData, UsageAnalyzer},
|
||||
compress::util::is_pure_undefined,
|
||||
debug::dump,
|
||||
debug::{dump, AssertValid},
|
||||
marks::Marks,
|
||||
mode::Mode,
|
||||
option::CompressOptions,
|
||||
@ -126,7 +126,7 @@ struct Ctx {
|
||||
is_delete_arg: bool,
|
||||
/// `true` if we are in `arg` of `++arg` or `--arg`.
|
||||
is_update_arg: bool,
|
||||
in_lhs_of_assign: bool,
|
||||
is_lhs_of_assign: bool,
|
||||
/// `false` for `d` in `d[0] = foo`.
|
||||
is_exact_lhs_of_assign: bool,
|
||||
|
||||
@ -685,6 +685,15 @@ where
|
||||
self.changed = true;
|
||||
return None;
|
||||
}
|
||||
|
||||
Expr::Tpl(t) if t.exprs.is_empty() => {
|
||||
if cfg!(feature = "debug") {
|
||||
tracing::debug!("ignore_return_value: Dropping tpl expr without expr");
|
||||
}
|
||||
self.changed = true;
|
||||
return None;
|
||||
}
|
||||
|
||||
// Function expression cannot have a side effect.
|
||||
Expr::Fn(_) => {
|
||||
tracing::debug!(
|
||||
@ -1527,7 +1536,7 @@ where
|
||||
fn visit_mut_assign_expr(&mut self, e: &mut AssignExpr) {
|
||||
{
|
||||
let ctx = Ctx {
|
||||
in_lhs_of_assign: true,
|
||||
is_lhs_of_assign: true,
|
||||
is_exact_lhs_of_assign: true,
|
||||
..self.ctx
|
||||
};
|
||||
@ -1888,6 +1897,10 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cfg!(feature = "debug") && cfg!(debug_assertions) {
|
||||
n.visit_with(&Invalid { span: DUMMY_SP }, &mut AssertValid);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_fn_decl(&mut self, f: &mut FnDecl) {
|
||||
@ -1997,6 +2010,10 @@ where
|
||||
}
|
||||
|
||||
self.ctx.in_asm = old_in_asm;
|
||||
|
||||
if cfg!(feature = "debug") && cfg!(debug_assertions) {
|
||||
n.visit_with(&Invalid { span: DUMMY_SP }, &mut AssertValid);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_if_stmt(&mut self, n: &mut IfStmt) {
|
||||
@ -2041,6 +2058,7 @@ where
|
||||
if n.computed {
|
||||
let ctx = Ctx {
|
||||
is_exact_lhs_of_assign: false,
|
||||
is_lhs_of_assign: false,
|
||||
..self.ctx
|
||||
};
|
||||
n.prop.visit_mut_with(&mut *self.with_ctx(ctx));
|
||||
@ -2149,6 +2167,10 @@ where
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if cfg!(feature = "debug") && cfg!(debug_assertions) {
|
||||
n.visit_with(&Invalid { span: DUMMY_SP }, &mut AssertValid);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_return_stmt(&mut self, n: &mut ReturnStmt) {
|
||||
@ -2241,7 +2263,7 @@ where
|
||||
is_callee: false,
|
||||
is_delete_arg: false,
|
||||
is_update_arg: false,
|
||||
in_lhs_of_assign: false,
|
||||
is_lhs_of_assign: false,
|
||||
in_bang_arg: false,
|
||||
is_exported: false,
|
||||
in_obj_of_non_computed_member: false,
|
||||
@ -2299,11 +2321,11 @@ where
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.optiimze_loops_if_cond_is_false(s);
|
||||
self.optimize_loops_if_cond_is_false(s);
|
||||
self.optimize_loops_with_break(s);
|
||||
|
||||
match s {
|
||||
// We use var devl with no declarator to indicate we dropped an decl.
|
||||
// We use var decl with no declarator to indicate we dropped an decl.
|
||||
Stmt::Decl(Decl::Var(VarDecl { decls, .. })) if decls.is_empty() => {
|
||||
*s = Stmt::Empty(EmptyStmt { span: DUMMY_SP });
|
||||
return;
|
||||
@ -2332,6 +2354,10 @@ where
|
||||
tracing::debug!("after: visit_mut_stmt: {}", text);
|
||||
}
|
||||
}
|
||||
|
||||
if cfg!(feature = "debug") && cfg!(debug_assertions) {
|
||||
s.visit_with(&Invalid { span: DUMMY_SP }, &mut AssertValid);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_stmts(&mut self, stmts: &mut Vec<Stmt>) {
|
||||
@ -2364,6 +2390,10 @@ where
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if cfg!(feature = "debug") && cfg!(debug_assertions) {
|
||||
stmts.visit_with(&Invalid { span: DUMMY_SP }, &mut AssertValid);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_str(&mut self, s: &mut Str) {
|
||||
|
@ -695,17 +695,30 @@ where
|
||||
let _ = self.merge_sequences_in_exprs(&mut exprs);
|
||||
}
|
||||
|
||||
stmts.retain_mut(|stmt| match stmt.as_stmt_mut() {
|
||||
Some(Stmt::Decl(Decl::Var(v))) => {
|
||||
v.decls.retain(|decl| match decl.init.as_deref() {
|
||||
Some(Expr::Invalid(..)) => false,
|
||||
_ => true,
|
||||
});
|
||||
|
||||
!v.decls.is_empty()
|
||||
stmts.retain_mut(|stmt| {
|
||||
match stmt.as_stmt_mut() {
|
||||
Some(Stmt::Expr(es)) => match &mut *es.expr {
|
||||
Expr::Seq(e) => {
|
||||
e.exprs.retain(|e| !e.is_invalid());
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match stmt.as_stmt_mut() {
|
||||
Some(Stmt::Decl(Decl::Var(v))) => {
|
||||
v.decls.retain(|decl| match decl.init.as_deref() {
|
||||
Some(Expr::Invalid(..)) => false,
|
||||
_ => true,
|
||||
});
|
||||
|
||||
!v.decls.is_empty()
|
||||
}
|
||||
Some(Stmt::Expr(s)) if s.expr.is_invalid() => false,
|
||||
|
||||
_ => true,
|
||||
}
|
||||
Some(Stmt::Expr(s)) if s.expr.is_invalid() => false,
|
||||
_ => true,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,32 @@
|
||||
use super::Pure;
|
||||
use crate::{compress::util::is_pure_undefined, mode::Mode};
|
||||
use swc_common::util::take::Take;
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
impl<M> Pure<'_, M>
|
||||
where
|
||||
M: Mode,
|
||||
{
|
||||
pub(super) fn remove_invalid(&mut self, e: &mut Expr) {
|
||||
match e {
|
||||
Expr::Bin(BinExpr { left, right, .. }) => {
|
||||
self.remove_invalid(left);
|
||||
self.remove_invalid(right);
|
||||
|
||||
if left.is_invalid() {
|
||||
*e = *right.take();
|
||||
self.remove_invalid(e);
|
||||
return;
|
||||
} else if right.is_invalid() {
|
||||
*e = *left.take();
|
||||
self.remove_invalid(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn drop_undefined_from_return_arg(&mut self, s: &mut ReturnStmt) {
|
||||
match s.arg.as_deref() {
|
||||
Some(e) => {
|
||||
|
@ -1,6 +1,10 @@
|
||||
use self::ctx::Ctx;
|
||||
use crate::{
|
||||
debug::dump, marks::Marks, mode::Mode, option::CompressOptions, util::ModuleItemExt,
|
||||
debug::{dump, AssertValid},
|
||||
marks::Marks,
|
||||
mode::Mode,
|
||||
option::CompressOptions,
|
||||
util::ModuleItemExt,
|
||||
MAX_PAR_DEPTH,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
@ -219,6 +223,8 @@ where
|
||||
e.visit_mut_children_with(&mut *self.with_ctx(ctx));
|
||||
}
|
||||
|
||||
self.remove_invalid(e);
|
||||
|
||||
match e {
|
||||
Expr::Seq(seq) => {
|
||||
if seq.exprs.is_empty() {
|
||||
@ -386,6 +392,10 @@ where
|
||||
p.visit_mut_children_with(self);
|
||||
|
||||
self.optimize_arrow_method_prop(p);
|
||||
|
||||
if cfg!(feature = "debug") && cfg!(debug_assertions) {
|
||||
p.visit_with(&Invalid { span: DUMMY_SP }, &mut AssertValid);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_prop_name(&mut self, p: &mut PropName) {
|
||||
@ -408,6 +418,16 @@ where
|
||||
fn visit_mut_seq_expr(&mut self, e: &mut SeqExpr) {
|
||||
e.visit_mut_children_with(self);
|
||||
|
||||
e.exprs.retain(|e| {
|
||||
if e.is_invalid() {
|
||||
self.changed = true;
|
||||
tracing::debug!("Removing invalid expr in seq");
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
|
||||
if e.exprs.len() == 0 {
|
||||
return;
|
||||
}
|
||||
@ -488,6 +508,10 @@ where
|
||||
tracing::debug!("after: visit_mut_stmt: {}", text);
|
||||
}
|
||||
}
|
||||
|
||||
if cfg!(feature = "debug") && cfg!(debug_assertions) {
|
||||
s.visit_with(&Invalid { span: DUMMY_SP }, &mut AssertValid);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_stmts(&mut self, items: &mut Vec<Stmt>) {
|
||||
@ -499,6 +523,10 @@ where
|
||||
Stmt::Empty(..) => false,
|
||||
_ => true,
|
||||
});
|
||||
|
||||
if cfg!(feature = "debug") && cfg!(debug_assertions) {
|
||||
items.visit_with(&Invalid { span: DUMMY_SP }, &mut AssertValid);
|
||||
}
|
||||
}
|
||||
|
||||
/// We don't optimize [Tpl] contained in [TaggedTpl].
|
||||
|
@ -14,14 +14,27 @@ where
|
||||
pub(super) fn convert_tpl_to_str(&mut self, e: &mut Expr) {
|
||||
match e {
|
||||
Expr::Tpl(t) if t.quasis.len() == 1 && t.exprs.is_empty() => {
|
||||
if let Some(c) = &t.quasis[0].cooked {
|
||||
if c.value.chars().all(|c| match c {
|
||||
'\u{0020}'..='\u{007e}' => true,
|
||||
'\n' | '\r' => M::force_str_for_tpl(),
|
||||
_ => false,
|
||||
}) {
|
||||
*e = Expr::Lit(Lit::Str(c.clone()));
|
||||
}
|
||||
let c = &t.quasis[0].raw;
|
||||
|
||||
if c.value.chars().all(|c| match c {
|
||||
'\u{0020}'..='\u{007e}' => true,
|
||||
'\n' | '\r' => M::force_str_for_tpl(),
|
||||
_ => false,
|
||||
}) && (M::force_str_for_tpl()
|
||||
|| (!c.value.contains("\\n") && !c.value.contains("\\r")))
|
||||
&& !c.value.contains("\\0")
|
||||
{
|
||||
*e = Expr::Lit(Lit::Str(Str {
|
||||
value: c
|
||||
.value
|
||||
.replace("\\`", "`")
|
||||
.replace("\\$", "$")
|
||||
.replace("\\n", "\n")
|
||||
.replace("\\r", "\r")
|
||||
.replace("\\\\", "\\")
|
||||
.into(),
|
||||
..c.clone()
|
||||
}));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -43,9 +56,12 @@ where
|
||||
return;
|
||||
}
|
||||
|
||||
if cfg!(feature = "debug") {
|
||||
tracing::debug!("compress_tpl");
|
||||
}
|
||||
|
||||
let mut quasis = vec![];
|
||||
let mut exprs = vec![];
|
||||
let mut cur = String::new();
|
||||
let mut cur_raw = String::new();
|
||||
|
||||
for i in 0..(tpl.exprs.len() + tpl.quasis.len()) {
|
||||
@ -53,7 +69,6 @@ where
|
||||
let i = i / 2;
|
||||
let q = tpl.quasis[i].take();
|
||||
|
||||
cur.push_str(&q.cooked.unwrap().value);
|
||||
cur_raw.push_str(&q.raw.value);
|
||||
} else {
|
||||
let i = i / 2;
|
||||
@ -61,19 +76,13 @@ where
|
||||
|
||||
match *e {
|
||||
Expr::Lit(Lit::Str(s)) => {
|
||||
cur.push_str(&s.value);
|
||||
cur_raw.push_str(&s.value);
|
||||
}
|
||||
_ => {
|
||||
quasis.push(TplElement {
|
||||
span: DUMMY_SP,
|
||||
tail: true,
|
||||
cooked: Some(Str {
|
||||
span: DUMMY_SP,
|
||||
value: take(&mut cur).into(),
|
||||
has_escape: false,
|
||||
kind: Default::default(),
|
||||
}),
|
||||
cooked: None,
|
||||
raw: Str {
|
||||
span: DUMMY_SP,
|
||||
value: take(&mut cur_raw).into(),
|
||||
@ -91,12 +100,7 @@ where
|
||||
quasis.push(TplElement {
|
||||
span: DUMMY_SP,
|
||||
tail: true,
|
||||
cooked: Some(Str {
|
||||
span: DUMMY_SP,
|
||||
value: cur.into(),
|
||||
has_escape: false,
|
||||
kind: Default::default(),
|
||||
}),
|
||||
cooked: None,
|
||||
raw: Str {
|
||||
span: DUMMY_SP,
|
||||
value: cur_raw.into(),
|
||||
@ -123,9 +127,10 @@ where
|
||||
"template: Concatted a string (`{}`) on rhs of `+` to a template literal",
|
||||
rs.value
|
||||
);
|
||||
let l_str = l_last.cooked.as_mut().unwrap();
|
||||
let l_str = &mut l_last.raw;
|
||||
|
||||
let new: JsWord = format!("{}{}", l_str.value, rs.value).into();
|
||||
let new: JsWord =
|
||||
format!("{}{}", l_str.value, rs.value.replace("\\", "\\\\")).into();
|
||||
l_str.value = new.clone();
|
||||
l_last.raw.value = new;
|
||||
|
||||
@ -143,9 +148,10 @@ where
|
||||
"template: Prepended a string (`{}`) on lhs of `+` to a template literal",
|
||||
ls.value
|
||||
);
|
||||
let r_str = r_first.cooked.as_mut().unwrap();
|
||||
let r_str = &mut r_first.raw;
|
||||
|
||||
let new: JsWord = format!("{}{}", ls.value, r_str.value).into();
|
||||
let new: JsWord =
|
||||
format!("{}{}", ls.value.replace("\\", "\\\\"), r_str.value).into();
|
||||
r_str.value = new.clone();
|
||||
r_first.raw.value = new;
|
||||
|
||||
@ -162,10 +168,9 @@ where
|
||||
let l_last = l.quasis.pop().unwrap();
|
||||
let mut r_first = rt.quasis.first_mut().unwrap();
|
||||
|
||||
let r_str = r_first.cooked.as_mut().unwrap();
|
||||
let r_str = &mut r_first.raw;
|
||||
|
||||
let new: JsWord =
|
||||
format!("{}{}", l_last.cooked.unwrap().value, r_str.value).into();
|
||||
let new: JsWord = format!("{}{}", l_last.raw.value, r_str.value).into();
|
||||
r_str.value = new.clone();
|
||||
r_first.raw.value = new;
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{env, process::Command};
|
||||
use swc_common::{sync::Lrc, SourceMap, SyntaxContext};
|
||||
use swc_ecma_ast::{Ident, Module, StrKind};
|
||||
use swc_common::{sync::Lrc, SourceMap, SyntaxContext, DUMMY_SP};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_codegen::{text_writer::JsWriter, Emitter};
|
||||
use swc_ecma_transforms::{fixer, hygiene};
|
||||
use swc_ecma_utils::{drop_span, DropSpan};
|
||||
use swc_ecma_visit::{noop_visit_mut_type, FoldWith, VisitMut, VisitMutWith};
|
||||
use swc_ecma_visit::{
|
||||
noop_visit_mut_type, noop_visit_type, FoldWith, Node, Visit, VisitMut, VisitMutWith, VisitWith,
|
||||
};
|
||||
|
||||
pub(crate) struct Debugger;
|
||||
|
||||
@ -62,6 +64,10 @@ pub(crate) fn invoke(module: &Module) {
|
||||
static ENABLED: Lazy<bool> =
|
||||
Lazy::new(|| cfg!(feature = "debug") && env::var("SWC_RUN").unwrap_or_default() == "1");
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
module.visit_with(&Invalid { span: DUMMY_SP }, &mut AssertValid);
|
||||
}
|
||||
|
||||
if !*ENABLED {
|
||||
return;
|
||||
}
|
||||
@ -108,3 +114,23 @@ pub(crate) fn invoke(module: &Module) {
|
||||
String::from_utf8_lossy(&output.stdout)
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) struct AssertValid;
|
||||
|
||||
impl Visit for AssertValid {
|
||||
noop_visit_type!();
|
||||
|
||||
fn visit_invalid(&mut self, _: &Invalid, _: &dyn Node) {
|
||||
panic!("[SWC_RUN] Invalid node found");
|
||||
}
|
||||
|
||||
fn visit_setter_prop(&mut self, p: &SetterProp, _: &dyn Node) {
|
||||
p.body.visit_with(p, self);
|
||||
}
|
||||
|
||||
fn visit_tpl(&mut self, l: &Tpl, _: &dyn Node) {
|
||||
l.visit_children_with(self);
|
||||
|
||||
assert_eq!(l.exprs.len() + 1, l.quasis.len());
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,18 @@
|
||||
use crate::debug::dump;
|
||||
use std::fmt::Debug;
|
||||
use swc_common::Mark;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms::fixer;
|
||||
use swc_ecma_utils::DropSpan;
|
||||
use swc_ecma_visit::{FoldWith, VisitMut, VisitMutWith};
|
||||
|
||||
/// Inidicates a unit of minifaction.
|
||||
/// Indicates a unit of minifaction.
|
||||
pub(crate) trait CompileUnit:
|
||||
swc_ecma_codegen::Node + Clone + VisitMutWith<DropSpan> + VisitMutWith<crate::debug::Debugger>
|
||||
swc_ecma_codegen::Node
|
||||
+ Clone
|
||||
+ VisitMutWith<DropSpan>
|
||||
+ VisitMutWith<crate::debug::Debugger>
|
||||
+ Debug
|
||||
{
|
||||
fn is_module() -> bool;
|
||||
|
||||
|
@ -326,6 +326,14 @@ fn base_exec(input: PathBuf) {
|
||||
testing::run_test2(false, |cm, handler| {
|
||||
let input_src = read_to_string(&input).expect("failed to read input.js as a string");
|
||||
|
||||
let expected_output = stdout_of(&input_src).unwrap();
|
||||
|
||||
eprintln!(
|
||||
"---- {} -----\n{}",
|
||||
Color::Green.paint("Expected"),
|
||||
expected_output
|
||||
);
|
||||
|
||||
let output = run(cm.clone(), &handler, &input, &config, None);
|
||||
let output = output.expect("Parsing in base test should not fail");
|
||||
let output = print(cm.clone(), &[output], false);
|
||||
@ -338,7 +346,6 @@ fn base_exec(input: PathBuf) {
|
||||
|
||||
println!("{}", input.display());
|
||||
|
||||
let expected_output = stdout_of(&input_src).unwrap();
|
||||
let actual_output = stdout_of(&output).expect("failed to execute the optimized code");
|
||||
assert_ne!(actual_output, "");
|
||||
|
||||
|
3
ecmascript/minifier/tests/exec/regexp/1/config.json
Normal file
3
ecmascript/minifier/tests/exec/regexp/1/config.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"defaults": true
|
||||
}
|
9
ecmascript/minifier/tests/exec/regexp/1/input.js
Normal file
9
ecmascript/minifier/tests/exec/regexp/1/input.js
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
|
||||
function compile(attributePattern, flags) {
|
||||
return new RegExp(`(?:^|;)\\s*${attributePattern}\\s*=\\s*` + `(` + `[^";\\s][^;\\s]*` + `|` + `"(?:[^"\\\\]|\\\\"?)+"?` + `)`, flags);
|
||||
}
|
||||
|
||||
console.log(compile("foo", "g"));
|
||||
console.log(compile("bar", "g"));
|
||||
console.log(compile("baz", "g"));
|
@ -1,3 +1,3 @@
|
||||
var foo = `<\/script>${content}`;
|
||||
var bar = "\x3c!--";
|
||||
var baz = "--\x3e";
|
||||
var bar = `\x3c!--`;
|
||||
var baz = `--\x3e`;
|
||||
|
@ -0,0 +1,6 @@
|
||||
error: Expression expected
|
||||
--> $DIR/tests/typescript-errors/parserSuperExpression2/input.ts:3:10
|
||||
|
|
||||
3 | super<T>(0);
|
||||
| ^
|
||||
|
@ -1,146 +0,0 @@
|
||||
{
|
||||
"type": "Script",
|
||||
"span": {
|
||||
"start": 0,
|
||||
"end": 40,
|
||||
"ctxt": 0
|
||||
},
|
||||
"body": [
|
||||
{
|
||||
"type": "ClassDeclaration",
|
||||
"identifier": {
|
||||
"type": "Identifier",
|
||||
"span": {
|
||||
"start": 6,
|
||||
"end": 7,
|
||||
"ctxt": 0
|
||||
},
|
||||
"value": "C",
|
||||
"optional": false
|
||||
},
|
||||
"declare": false,
|
||||
"span": {
|
||||
"start": 0,
|
||||
"end": 40,
|
||||
"ctxt": 0
|
||||
},
|
||||
"decorators": [],
|
||||
"body": [
|
||||
{
|
||||
"type": "ClassMethod",
|
||||
"span": {
|
||||
"start": 12,
|
||||
"end": 38,
|
||||
"ctxt": 0
|
||||
},
|
||||
"key": {
|
||||
"type": "Identifier",
|
||||
"span": {
|
||||
"start": 12,
|
||||
"end": 13,
|
||||
"ctxt": 0
|
||||
},
|
||||
"value": "M",
|
||||
"optional": false
|
||||
},
|
||||
"function": {
|
||||
"params": [],
|
||||
"decorators": [],
|
||||
"span": {
|
||||
"start": 12,
|
||||
"end": 38,
|
||||
"ctxt": 0
|
||||
},
|
||||
"body": {
|
||||
"type": "BlockStatement",
|
||||
"span": {
|
||||
"start": 16,
|
||||
"end": 38,
|
||||
"ctxt": 0
|
||||
},
|
||||
"stmts": [
|
||||
{
|
||||
"type": "ExpressionStatement",
|
||||
"span": {
|
||||
"start": 22,
|
||||
"end": 34,
|
||||
"ctxt": 0
|
||||
},
|
||||
"expression": {
|
||||
"type": "BinaryExpression",
|
||||
"span": {
|
||||
"start": 27,
|
||||
"end": 33,
|
||||
"ctxt": 0
|
||||
},
|
||||
"operator": ">",
|
||||
"left": {
|
||||
"type": "BinaryExpression",
|
||||
"span": {
|
||||
"start": 27,
|
||||
"end": 29,
|
||||
"ctxt": 0
|
||||
},
|
||||
"operator": "<",
|
||||
"left": {
|
||||
"type": "Invalid",
|
||||
"span": {
|
||||
"start": 27,
|
||||
"end": 28,
|
||||
"ctxt": 0
|
||||
}
|
||||
},
|
||||
"right": {
|
||||
"type": "Identifier",
|
||||
"span": {
|
||||
"start": 28,
|
||||
"end": 29,
|
||||
"ctxt": 0
|
||||
},
|
||||
"value": "T",
|
||||
"optional": false
|
||||
}
|
||||
},
|
||||
"right": {
|
||||
"type": "ParenthesisExpression",
|
||||
"span": {
|
||||
"start": 30,
|
||||
"end": 33,
|
||||
"ctxt": 0
|
||||
},
|
||||
"expression": {
|
||||
"type": "NumericLiteral",
|
||||
"span": {
|
||||
"start": 31,
|
||||
"end": 32,
|
||||
"ctxt": 0
|
||||
},
|
||||
"value": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"generator": false,
|
||||
"async": false,
|
||||
"typeParameters": null,
|
||||
"returnType": null
|
||||
},
|
||||
"kind": "method",
|
||||
"isStatic": false,
|
||||
"accessibility": null,
|
||||
"isAbstract": false,
|
||||
"isOptional": false,
|
||||
"isOverride": false
|
||||
}
|
||||
],
|
||||
"superClass": null,
|
||||
"isAbstract": false,
|
||||
"typeParams": null,
|
||||
"superTypeParams": null,
|
||||
"implements": []
|
||||
}
|
||||
],
|
||||
"interpreter": null
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@swc/core",
|
||||
"version": "1.2.102",
|
||||
"version": "1.2.103",
|
||||
"description": "Super-fast alternative for babel",
|
||||
"homepage": "https://swc.rs",
|
||||
"main": "./index.js",
|
||||
|
@ -1,8 +1,8 @@
|
||||
var obj, key;
|
||||
key = 0, 0 in (obj = {
|
||||
var obj;
|
||||
0 in (obj = {
|
||||
}) ? Object.defineProperty(obj, 0, {
|
||||
value: 0,
|
||||
enumerable: !0,
|
||||
configurable: !0,
|
||||
writable: !0
|
||||
}) : obj[key] = 0;
|
||||
}) : obj[0] = 0;
|
||||
|
@ -1,8 +1,8 @@
|
||||
var obj, key;
|
||||
key = 0, 0 in (obj = {
|
||||
var obj;
|
||||
0 in (obj = {
|
||||
}) ? Object.defineProperty(obj, 0, {
|
||||
value: 0,
|
||||
enumerable: !0,
|
||||
configurable: !0,
|
||||
writable: !0
|
||||
}) : obj[key] = 0;
|
||||
}) : obj[0] = 0;
|
||||
|
@ -1 +0,0 @@
|
||||
`\0\0x00\u0000 0 00 0000`;
|
@ -1 +0,0 @@
|
||||
`\0\0x00\u0000 0 00 0000`;
|
@ -1 +0,0 @@
|
||||
`\x19\u0019 19`;
|
@ -1 +0,0 @@
|
||||
`\x19\u0019 19`;
|
@ -1 +0,0 @@
|
||||
`\x1f\u001f 1F 1f`;
|
@ -1 +0,0 @@
|
||||
`\x1f\u001f 1F 1f`;
|
@ -1,3 +0,0 @@
|
||||
`
|
||||
\
|
||||
`;
|
@ -1,3 +0,0 @@
|
||||
`
|
||||
\
|
||||
`;
|
@ -1,3 +0,0 @@
|
||||
`
|
||||
\
|
||||
`;
|
@ -1,3 +0,0 @@
|
||||
`
|
||||
\
|
||||
`;
|
@ -1 +0,0 @@
|
||||
`\t\n\\\r`;
|
@ -1 +0,0 @@
|
||||
`\t\n\\\r`;
|
@ -1 +1 @@
|
||||
`\u0009\u000B\u000C\u0020\u00A0\uFEFF`;
|
||||
"\\u0009\\u000B\\u000C\\u0020\\u00A0\\uFEFF";
|
||||
|
@ -1 +1 @@
|
||||
`\u0009\u000B\u000C\u0020\u00A0\uFEFF`;
|
||||
"\\u0009\\u000B\\u000C\\u0020\\u00A0\\uFEFF";
|
||||
|
@ -1,3 +0,0 @@
|
||||
`head10
|
||||
middle20
|
||||
tail`;
|
@ -1,3 +0,0 @@
|
||||
`head10
|
||||
middle20
|
||||
tail`;
|
@ -1 +0,0 @@
|
||||
`\u{0}`;
|
@ -1 +0,0 @@
|
||||
`\u{0}`;
|
@ -1 +0,0 @@
|
||||
`\u{00}`;
|
@ -1 +0,0 @@
|
||||
`\u{00}`;
|
@ -1 +0,0 @@
|
||||
`\u{0000}`;
|
@ -1 +0,0 @@
|
||||
`\u{0000}`;
|
@ -1 +0,0 @@
|
||||
`\u{00000000}`;
|
@ -1 +0,0 @@
|
||||
`\u{00000000}`;
|
@ -1 +0,0 @@
|
||||
`\u{10FFFF}`;
|
@ -1 +0,0 @@
|
||||
`\u{10FFFF}`;
|
@ -1 +0,0 @@
|
||||
`\u{FFFF}`;
|
@ -1 +0,0 @@
|
||||
`\u{FFFF}`;
|
@ -1 +0,0 @@
|
||||
`\u{10000}`;
|
@ -1 +0,0 @@
|
||||
`\u{10000}`;
|
@ -1 +0,0 @@
|
||||
`\u{DDDDD}`;
|
@ -1 +0,0 @@
|
||||
`\u{DDDDD}`;
|
@ -1 +0,0 @@
|
||||
`\u{abcd}\u{ef12}\u{3456}\u{7890}`;
|
@ -1 +0,0 @@
|
||||
`\u{abcd}\u{ef12}\u{3456}\u{7890}`;
|
@ -1 +0,0 @@
|
||||
`\u{ABCD}\u{EF12}\u{3456}\u{7890}`;
|
@ -1 +0,0 @@
|
||||
`\u{ABCD}\u{EF12}\u{3456}\u{7890}`;
|
@ -1,5 +0,0 @@
|
||||
class C {
|
||||
M() {
|
||||
<invalid> < T > 0;
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
function _defineProperties(target, props) {
|
||||
for(var i = 0; i < props.length; i++){
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
function _createClass(Constructor, protoProps, staticProps) {
|
||||
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) _defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
}
|
||||
var C = /*#__PURE__*/ function() {
|
||||
"use strict";
|
||||
function C() {
|
||||
_classCallCheck(this, C);
|
||||
}
|
||||
_createClass(C, [
|
||||
{
|
||||
key: "M",
|
||||
value: function M() {
|
||||
<invalid> < T > 0;
|
||||
}
|
||||
}
|
||||
]);
|
||||
return C;
|
||||
}();
|
@ -1,22 +0,0 @@
|
||||
function _defineProperties(target, props) {
|
||||
for(var i = 0; i < props.length; i++){
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || !1, descriptor.configurable = !0, "value" in descriptor && (descriptor.writable = !0), Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
var C = function() {
|
||||
"use strict";
|
||||
var Constructor, protoProps, staticProps;
|
||||
function C() {
|
||||
!function(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
|
||||
}(this, C);
|
||||
}
|
||||
return protoProps = [
|
||||
{
|
||||
key: "M",
|
||||
value: function() {
|
||||
}
|
||||
}
|
||||
], _defineProperties((Constructor = C).prototype, protoProps), staticProps && _defineProperties(Constructor, staticProps), C;
|
||||
}();
|
@ -1,3 +1,3 @@
|
||||
({
|
||||
})["21EC2020-3AEA-4069-A2DD-08002B30309D"] = 123, ({
|
||||
})[void 0] = "hello", 12345..toPrecision(0);
|
||||
})[12345] = "hello", 12345..toPrecision(0);
|
||||
|
@ -1,4 +1,3 @@
|
||||
var serialNo;
|
||||
({
|
||||
})["21EC2020-3AEA-4069-A2DD-08002B30309D"] = 123, ({
|
||||
})[serialNo] = "hello", 12345..toPrecision(0);
|
||||
})[12345] = "hello", 12345..toPrecision(0);
|
||||
|
@ -1,2 +0,0 @@
|
||||
`DE
|
||||
F`;
|
@ -1,3 +0,0 @@
|
||||
`AB
|
||||
C`, `DE
|
||||
F`;
|
@ -6,7 +6,7 @@ license = "Apache-2.0 AND MIT"
|
||||
name = "wasm"
|
||||
publish = false
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "1.2.102"
|
||||
version = "1.2.103"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
Loading…
Reference in New Issue
Block a user