Skip to content

Commit 69cfa98

Browse files
authored
fix(es/minifier): Fix block unwrapping issue (#2570)
swc_ecma_minifier: - Check more before removing `{` and `}`.
1 parent 4b2903e commit 69cfa98

File tree

14 files changed

+345
-65
lines changed

14 files changed

+345
-65
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ecmascript/minifier/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs", "src/lists/*.json"]
77
license = "Apache-2.0/MIT"
88
name = "swc_ecma_minifier"
99
repository = "https://github.com/swc-project/swc.git"
10-
version = "0.46.6"
10+
version = "0.46.7"
1111

1212
[features]
1313
debug = ["backtrace"]

ecmascript/minifier/src/compress/optimize/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![allow(dead_code)]
22

33
use self::util::MultiReplacer;
4+
use super::util::is_fine_for_if_cons;
45
use crate::{
56
analyzer::{ProgramData, UsageAnalyzer},
67
compress::util::is_pure_undefined,
@@ -1457,7 +1458,9 @@ where
14571458
Stmt::Block(block) if block.stmts.is_empty() => {
14581459
*s = Stmt::Empty(EmptyStmt { span: block.span });
14591460
}
1460-
Stmt::Block(block) if block.stmts.len() == 1 => {
1461+
Stmt::Block(block)
1462+
if block.stmts.len() == 1 && is_fine_for_if_cons(&block.stmts[0]) =>
1463+
{
14611464
*s = block.stmts.take().into_iter().next().unwrap();
14621465
}
14631466
_ => {}

ecmascript/minifier/src/compress/pure/dead_code.rs

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use super::Pure;
2-
use crate::{compress::util::always_terminates, mode::Mode};
3-
use swc_atoms::js_word;
2+
use crate::{
3+
compress::util::{always_terminates, is_fine_for_if_cons},
4+
mode::Mode,
5+
};
46
use swc_common::{util::take::Take, DUMMY_SP};
57
use swc_ecma_ast::*;
68
use swc_ecma_utils::{ExprExt, StmtLike, Value};
@@ -14,31 +16,12 @@ where
1416
where
1517
T: StmtLike,
1618
{
17-
fn is_inliable(b: &BlockStmt) -> bool {
18-
b.stmts.iter().all(|s| match s {
19-
Stmt::Decl(Decl::Fn(FnDecl {
20-
ident:
21-
Ident {
22-
sym: js_word!("undefined"),
23-
..
24-
},
25-
..
26-
})) => false,
27-
28-
Stmt::Decl(
29-
Decl::Var(VarDecl {
30-
kind: VarDeclKind::Var,
31-
..
32-
})
33-
| Decl::Fn(..),
34-
) => true,
35-
Stmt::Decl(..) => false,
36-
_ => true,
37-
})
19+
fn is_ok(b: &BlockStmt) -> bool {
20+
b.stmts.iter().all(|s| is_fine_for_if_cons(s))
3821
}
3922

4023
if stmts.iter().all(|stmt| match stmt.as_stmt() {
41-
Some(Stmt::Block(b)) if is_inliable(b) => false,
24+
Some(Stmt::Block(b)) if is_ok(b) => false,
4225
_ => true,
4326
}) {
4427
return;
@@ -51,7 +34,7 @@ where
5134
for stmt in stmts.take() {
5235
match stmt.try_into_stmt() {
5336
Ok(v) => match v {
54-
Stmt::Block(v) if is_inliable(&v) => {
37+
Stmt::Block(v) if is_ok(&v) => {
5538
new.extend(v.stmts.into_iter().map(T::from_stmt));
5639
}
5740
_ => new.push(T::from_stmt(v)),

ecmascript/minifier/src/compress/pure/if_return.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
use crate::compress::util::negate;
2-
31
use super::Pure;
2+
use crate::compress::util::{is_fine_for_if_cons, negate};
43
use swc_common::{util::take::Take, DUMMY_SP};
54
use swc_ecma_ast::*;
65

@@ -96,7 +95,7 @@ impl<M> Pure<'_, M> {
9695
self.changed = true;
9796
negate(&mut s.test, false);
9897

99-
s.cons = if cons.len() == 1 {
98+
s.cons = if cons.len() == 1 && is_fine_for_if_cons(&cons[0]) {
10099
Box::new(cons.into_iter().next().unwrap())
101100
} else {
102101
Box::new(Stmt::Block(BlockStmt {

ecmascript/minifier/src/compress/pure/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub(crate) fn pure_optimizer<'a, M>(
3333
options: &'a CompressOptions,
3434
marks: Marks,
3535
mode: &'a M,
36-
debug_inifinite_loop: bool,
36+
debug_infinite_loop: bool,
3737
) -> impl 'a + VisitMut + Repeated
3838
where
3939
M: Mode,
@@ -44,7 +44,7 @@ where
4444
ctx: Default::default(),
4545
changed: Default::default(),
4646
mode,
47-
debug_inifinite_loop,
47+
debug_infinite_loop,
4848
}
4949
}
5050

@@ -55,7 +55,7 @@ struct Pure<'a, M> {
5555
changed: bool,
5656
mode: &'a M,
5757

58-
debug_inifinite_loop: bool,
58+
debug_infinite_loop: bool,
5959
}
6060

6161
impl<M> Repeated for Pure<'_, M> {
@@ -117,7 +117,7 @@ where
117117
ctx: self.ctx,
118118
changed: false,
119119
mode: self.mode,
120-
debug_inifinite_loop: self.debug_inifinite_loop,
120+
debug_infinite_loop: self.debug_infinite_loop,
121121
};
122122
node.visit_mut_with(&mut v);
123123

@@ -136,7 +136,7 @@ where
136136
},
137137
changed: false,
138138
mode: self.mode,
139-
debug_inifinite_loop: self.debug_inifinite_loop,
139+
debug_infinite_loop: self.debug_infinite_loop,
140140
};
141141
node.visit_mut_with(&mut v);
142142

@@ -447,7 +447,7 @@ where
447447
}
448448

449449
fn visit_mut_stmt(&mut self, s: &mut Stmt) {
450-
let _tracing = if cfg!(feature = "debug") && self.debug_inifinite_loop {
450+
let _tracing = if cfg!(feature = "debug") && self.debug_infinite_loop {
451451
let text = dump(&*s);
452452

453453
if text.lines().count() < 10 {
@@ -470,7 +470,7 @@ where
470470
s.visit_mut_children_with(&mut *self.with_ctx(ctx));
471471
}
472472

473-
if cfg!(feature = "debug") && self.debug_inifinite_loop {
473+
if cfg!(feature = "debug") && self.debug_infinite_loop {
474474
let text = dump(&*s);
475475

476476
if text.lines().count() < 10 {
@@ -501,7 +501,7 @@ where
501501
_ => {}
502502
}
503503

504-
if cfg!(feature = "debug") && self.debug_inifinite_loop {
504+
if cfg!(feature = "debug") && self.debug_infinite_loop {
505505
let text = dump(&*s);
506506

507507
if text.lines().count() < 10 {

ecmascript/minifier/src/compress/util/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,3 +593,26 @@ where
593593
{
594594
node.visit_mut_with(&mut ExprReplacer { op })
595595
}
596+
597+
pub(super) fn is_fine_for_if_cons(s: &Stmt) -> bool {
598+
match s {
599+
Stmt::Decl(Decl::Fn(FnDecl {
600+
ident:
601+
Ident {
602+
sym: js_word!("undefined"),
603+
..
604+
},
605+
..
606+
})) => false,
607+
608+
Stmt::Decl(
609+
Decl::Var(VarDecl {
610+
kind: VarDeclKind::Var,
611+
..
612+
})
613+
| Decl::Fn(..),
614+
) => true,
615+
Stmt::Decl(..) => false,
616+
_ => true,
617+
}
618+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2+
import * as React from "react";
3+
import invariant from "invariant";
4+
import { injectScript } from "./utils/injectscript";
5+
import { preventGoogleFonts } from "./utils/prevent-google-fonts";
6+
import { isBrowser } from "./utils/isbrowser";
7+
import { makeLoadScriptUrl } from "./utils/make-load-script-url";
8+
let cleaningUp = false;
9+
export function DefaultLoadingElement() {
10+
return (/*#__PURE__*/ _jsx("div", {
11+
children: `Loading...`
12+
}));
13+
}
14+
export const defaultLoadScriptProps = {
15+
id: "script-loader",
16+
version: "weekly"
17+
};
18+
class LoadScript extends React.PureComponent {
19+
componentDidMount() {
20+
if (isBrowser) {
21+
if (window.google && window.google.maps && !cleaningUp) {
22+
console.error("google api is already presented");
23+
return;
24+
}
25+
this.isCleaningUp().then(this.injectScript).catch(function error(err) {
26+
console.error("Error at injecting script after cleaning up: ", err);
27+
});
28+
}
29+
}
30+
componentDidUpdate(prevProps) {
31+
if (this.props.libraries !== prevProps.libraries) {
32+
console.warn("Performance warning! LoadScript has been reloaded unintentionally! You should not pass `libraries` prop as new array. Please keep an array of libraries as static class property for Components and PureComponents, or just a const variable outside of component, or somewhere in config files or ENV variables");
33+
}
34+
if (isBrowser && prevProps.language !== this.props.language) {
35+
this.cleanup();
36+
// TODO: refactor to use gDSFP maybe... wait for hooks refactoring.
37+
// eslint-disable-next-line react/no-did-update-set-state
38+
this.setState(function setLoaded() {
39+
return {
40+
loaded: false
41+
};
42+
}, this.cleanupCallback);
43+
}
44+
}
45+
componentWillUnmount() {
46+
if (isBrowser) {
47+
this.cleanup();
48+
const timeoutCallback = () => {
49+
if (!this.check.current) {
50+
// @ts-ignore
51+
delete window.google;
52+
cleaningUp = false;
53+
}
54+
};
55+
window.setTimeout(timeoutCallback, 1);
56+
if (this.props.onUnmount) {
57+
this.props.onUnmount();
58+
}
59+
}
60+
}
61+
render() {
62+
return (/*#__PURE__*/ _jsxs(_Fragment, {
63+
children: [
64+
/*#__PURE__*/ _jsx("div", {
65+
ref: this.check
66+
}),
67+
this.state.loaded ? this.props.children : this.props.loadingElement || /*#__PURE__*/ _jsx(DefaultLoadingElement, {
68+
})
69+
]
70+
}));
71+
}
72+
constructor(...args) {
73+
super(...args);
74+
this.check = /*#__PURE__*/ React.createRef();
75+
this.state = {
76+
loaded: false
77+
};
78+
this.cleanupCallback = () => {
79+
// @ts-ignore
80+
delete window.google.maps;
81+
this.injectScript();
82+
};
83+
this.isCleaningUp = async () => {
84+
function promiseCallback(resolve) {
85+
if (!cleaningUp) {
86+
resolve();
87+
} else {
88+
if (isBrowser) {
89+
const timer = window.setInterval(function interval() {
90+
if (!cleaningUp) {
91+
window.clearInterval(timer);
92+
resolve();
93+
}
94+
}, 1);
95+
}
96+
}
97+
return;
98+
}
99+
return new Promise(promiseCallback);
100+
};
101+
this.cleanup = () => {
102+
cleaningUp = true;
103+
const script1 = document.getElementById(this.props.id);
104+
if (script1 && script1.parentNode) {
105+
script1.parentNode.removeChild(script1);
106+
}
107+
Array.prototype.slice.call(document.getElementsByTagName("script")).filter(function filter(script) {
108+
return typeof script.src === "string" && script.src.includes("maps.googleapis");
109+
}).forEach(function forEach(script) {
110+
if (script.parentNode) {
111+
script.parentNode.removeChild(script);
112+
}
113+
});
114+
Array.prototype.slice.call(document.getElementsByTagName("link")).filter(function filter(link) {
115+
return link.href === "https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Google+Sans";
116+
}).forEach(function forEach(link) {
117+
if (link.parentNode) {
118+
link.parentNode.removeChild(link);
119+
}
120+
});
121+
Array.prototype.slice.call(document.getElementsByTagName("style")).filter(function filter(style) {
122+
return style.innerText !== undefined && style.innerText.length > 0 && style.innerText.includes(".gm-");
123+
}).forEach(function forEach(style) {
124+
if (style.parentNode) {
125+
style.parentNode.removeChild(style);
126+
}
127+
});
128+
};
129+
this.injectScript = () => {
130+
if (this.props.preventGoogleFontsLoading) {
131+
preventGoogleFonts();
132+
}
133+
invariant(!!this.props.id, 'LoadScript requires "id" prop to be a string: %s', this.props.id);
134+
const injectScriptOptions = {
135+
id: this.props.id,
136+
nonce: this.props.nonce,
137+
url: makeLoadScriptUrl(this.props)
138+
};
139+
injectScript(injectScriptOptions).then(() => {
140+
if (this.props.onLoad) {
141+
this.props.onLoad();
142+
}
143+
this.setState(function setLoaded() {
144+
return {
145+
loaded: true
146+
};
147+
});
148+
return;
149+
}).catch((err) => {
150+
if (this.props.onError) {
151+
this.props.onError(err);
152+
}
153+
console.error(`
154+
There has been an Error with loading Google Maps API script, please check that you provided correct google API key (${this.props.googleMapsApiKey || "-"}) or Client ID (${this.props.googleMapsClientId || "-"}) to <LoadScript />
155+
Otherwise it is a Network issue.
156+
`);
157+
});
158+
};
159+
}
160+
}
161+
LoadScript.defaultProps = defaultLoadScriptProps;
162+
export default LoadScript;

0 commit comments

Comments
 (0)