swc/ecmascript/transforms/tests/optimization_simplify_dce.rs
강동윤 ba13db54db
fix(bundler): Fix remaining bugs (#1296)
swc_bundler:
 -  Allow `export *` and `import` from same source. (denoland/deno#8828, denoland/deno#8725)
 -  Handle contextual keywords. (denoland/deno#8680)
 -  Prevent duplicated identifiers. (denoland/deno#8725)

swc_ecma_transforms:
 - dce: Preserve tags in tagged template literals. (denoland/deno#8746)
 - dce: Handle throw statement correctly. (denoland/deno#8736)
2020-12-28 19:53:40 +09:00

839 lines
15 KiB
Rust

#![feature(test)]
use swc_common::{chain, Mark, SyntaxContext};
use swc_ecma_parser::{EsConfig, Syntax, TsConfig};
use swc_ecma_transforms::{
optimization::simplify::dce::{self, dce},
proposals::decorators,
resolver,
typescript::{self, strip},
};
#[macro_use]
mod common;
macro_rules! to {
($name:ident, $src:expr, $expected:expr) => {
test!(
Syntax::Es(EsConfig {
decorators: true,
..Default::default()
}),
|_| chain!(resolver(), dce(Default::default())),
$name,
$src,
$expected
);
};
}
fn used(ids: &[&str], src: &str, expected: &str) {
test_transform!(
Default::default(),
|_| {
let mark = Mark::fresh(Mark::root());
chain!(
resolver(),
dce(dce::Config {
used: Some(
ids.into_iter()
.map(|&v| { (v.into(), SyntaxContext::empty().apply_mark(mark)) })
.collect()
),
used_mark: mark,
..Default::default()
})
)
},
src,
expected,
false
);
}
macro_rules! optimized_out {
($name:ident, $src:expr) => {
to!($name, $src, "");
};
}
macro_rules! noop {
($name:ident, $src:expr) => {
to!($name, $src, $src);
};
}
optimized_out!(
single_pass,
"
const a = 1;
if (a) {
const b = 2;
}
"
);
optimized_out!(issue_607, "let a");
noop!(
noop_1,
"
let b = 2;
let a = 1;
if (b) {
a = 2;
}
let c;
if (a) {
c = 3;
}
console.log(c);
"
);
noop!(
noop_2,
"
switch (1){
case 1:
a = '1';
}
console.log(a);
"
);
noop!(
noop_3,
"
try {
console.log(foo())
} catch (e) {
console.error(e);
}"
);
to!(
custom_loop_2,
"let b = 2;
let a = 1;
a = 2;
let c;
if (2) c = 3
console.log(c)",
"let c;
if (2) c = 3;
console.log(c);"
);
optimized_out!(simple_const, "{const x = 1}");
noop!(assign_op, "x *= 2; use(x)");
optimized_out!(import_default_unused, "import foo from 'foo'");
optimized_out!(import_specific_unused, "import {foo} from 'foo'");
optimized_out!(import_mixed_unused, "import foo, { bar } from 'foo'");
noop!(export_named, "export { x };");
noop!(export_named_from, "export {foo} from 'src';");
noop!(
import_default_export_named,
"import foo from 'src'; export { foo }; "
);
to!(
import_unused_export_named,
"import foo, { bar } from 'src'; export { foo }; ",
"import foo from 'src'; export { foo }; "
);
#[test]
fn export_named_unused() {
used(&["foo"], "export { foo, bat }", "export { foo }");
}
#[test]
fn export_default_expr_unused() {
used(&[], "export default 5;", "");
}
#[test]
fn export_default_expr_used() {
used(&["default"], "export default 5;", "export default 5;");
}
noop!(
issue_760_1,
"var ref;
export const Auth = window === null || window === void 0 ? void 0 : (ref = window.aws) === \
null || ref === void 0 ? void 0 : ref.Auth;
"
);
noop!(
issue_760_2_export_default,
"const initialState = 'foo';
export default function reducer(state = initialState, action = {}) {
}"
);
noop!(
issue_760_2_export_named,
"const initialState = 'foo';
export function reducer(state = initialState, action = {}) {
}"
);
optimized_out!(
issue_760_2_no_export,
"const initialState = 'foo';
function reducer(state = initialState, action = {}) {
}"
);
to!(
issue_763_1,
"import {
INSTAGRAM_CHECK_PATTERN,
RESOURCE_FACEBOOK,
RESOURCE_INSTAGRAM,
RESOURCE_WEBSITE,
} from '../../../../consts'
export const resources = [
{
value: RESOURCE_WEBSITE,
label: 'Webové stránky',
},
{
value: RESOURCE_FACEBOOK,
label: 'Facebook',
},
{
value: RESOURCE_INSTAGRAM,
label: 'Instagram',
},
]",
"import {
RESOURCE_FACEBOOK,
RESOURCE_INSTAGRAM,
RESOURCE_WEBSITE,
} from '../../../../consts'
export const resources = [
{
value: RESOURCE_WEBSITE,
label: 'Webové stránky',
},
{
value: RESOURCE_FACEBOOK,
label: 'Facebook',
},
{
value: RESOURCE_INSTAGRAM,
label: 'Instagram',
},
]"
);
to!(
issue_763_2,
"import {
INSTAGRAM_CHECK_PATTERN,
RESOURCE_FACEBOOK,
RESOURCE_INSTAGRAM,
RESOURCE_WEBSITE,
} from '../../../../consts'
const resources = [
{
value: RESOURCE_WEBSITE,
label: 'Webové stránky',
},
{
value: RESOURCE_FACEBOOK,
label: 'Facebook',
},
{
value: RESOURCE_INSTAGRAM,
label: 'Instagram',
},
]
resources.map(console.log.bind(console));",
"import {
RESOURCE_FACEBOOK,
RESOURCE_INSTAGRAM,
RESOURCE_WEBSITE,
} from '../../../../consts'
const resources = [
{
value: RESOURCE_WEBSITE,
label: 'Webové stránky',
},
{
value: RESOURCE_FACEBOOK,
label: 'Facebook',
},
{
value: RESOURCE_INSTAGRAM,
label: 'Instagram',
},
];
resources.map(console.log.bind(console));"
);
noop!(
issue_763_3,
"import {
RESOURCE_FACEBOOK,
RESOURCE_INSTAGRAM,
RESOURCE_WEBSITE,
} from '../../../../consts'
const resources = [
{
value: RESOURCE_WEBSITE,
label: 'Webové stránky',
},
{
value: RESOURCE_FACEBOOK,
label: 'Facebook',
},
{
value: RESOURCE_INSTAGRAM,
label: 'Instagram',
},
];
resources.map(v => v)"
);
noop!(
issue_763_4,
"import { RESOURCE_FACEBOOK, RESOURCE_INSTAGRAM, RESOURCE_WEBSITE } from './consts';
const resources = [
{
value: RESOURCE_WEBSITE,
label: 'Webové stránky',
},
{
value: RESOURCE_FACEBOOK,
label: 'Facebook',
},
{
value: RESOURCE_INSTAGRAM,
label: 'Instagram',
},
];
export function foo(websites) {
const a = resources.map((resource) => (
{
value: resource.value,
}
));
const b = website.type_id === RESOURCE_INSTAGRAM ? 'text' : 'url';
return a + b;
}"
);
noop!(
issue_763_5_1,
"import { A, B } from './consts';
const resources = [A, B];
use(B)
resources.map(v => v)
"
);
noop!(
issue_763_5_2,
"import { A, B } from './consts';
const resources = {A, B};
use(B)
resources.map(v => v)
"
);
#[test]
fn spack_issue_001() {
used(
&["FOO"],
"export const FOO = 'foo';",
"export const FOO = 'foo';",
);
}
#[test]
fn spack_issue_002() {
used(
&["FOO"],
"export const FOO = 'foo', BAR = 'bar';",
"export const FOO = 'foo';",
);
}
#[test]
fn spack_issue_003() {
used(
&["default"],
"export const FOO = 'foo', BAR = 'bar';
export default BAR;",
"export const BAR = 'bar';
export default BAR;",
);
}
to!(
spack_issue_004,
"const FOO = 'foo', BAR = 'bar';
export default BAR;",
"const BAR = 'bar';
export default BAR;"
);
noop!(
spack_issue_005,
"function a() {
}
function foo() {
}
console.log(a(), foo());"
);
noop!(
spack_issue_006,
"import * as a from './a';
function foo() {}
console.log(foo(), a.a(), a.foo());"
);
noop!(
spack_issue_007,
"
var load = function(){}
var { progress } = load();
console.log(progress);"
);
noop!(
spack_issue_008,
"class B {
}
class A extends B {
}
console.log('foo');
new A();"
);
noop!(
spack_issue_009,
"
class A {
}
function a() {
return new A();
}
console.log(a, a());
"
);
noop!(
spack_issue_010,
"
class A {}
console.log(new A());
"
);
noop!(
issue_898_1,
"
export default class X {
@whatever
anything= 0;
x() {
const localVar = aFunctionSomewhere();
return localVar;
}
}
"
);
test!(
Syntax::Typescript(TsConfig {
decorators: true,
..Default::default()
}),
|_| chain!(
resolver(),
typescript::strip(),
decorators(decorators::Config {
legacy: true,
emit_metadata: false
}),
dce(Default::default())
),
issue_898_2,
"export default class X {
@whatever
anything: number = 0;
x() {
const localVar = aFunctionSomewhere();
return localVar;
}
}
",
"
var _class, _descriptor;
let X = ((_class = class X {
x() {
const localVar = aFunctionSomewhere();
return localVar;
}
constructor(){
_initializerDefineProperty(this, \"anything\", _descriptor, this);
}
}) || _class, _descriptor = _applyDecoratedDescriptor(_class.prototype, \"anything\", [
whatever
], {
configurable: true,
enumerable: true,
writable: true,
initializer: function() {
return 0;
}
}), _class);
export { X as default };"
);
test!(
Syntax::Typescript(TsConfig {
decorators: true,
..Default::default()
}),
|_| chain!(
resolver(),
typescript::strip(),
decorators(decorators::Config {
legacy: true,
emit_metadata: false
}),
dce(Default::default())
),
issue_1111,
"
const a = 1;
export const d = { a };
",
"
const a = 1;
export const d = {
a
};
"
);
test!(
Syntax::Typescript(TsConfig {
decorators: true,
..Default::default()
}),
|_| chain!(resolver(), dce(Default::default())),
issue_1150_1,
"
class A {
constructor(o: AOptions = {}) {
const {
a = defaultA,
c,
} = o;
this.#a = a;
this.#c = c;
}
a() {
this.#a();
}
c() {
console.log(this.#c);
}
}
new A();
",
"
class A {
constructor(o: AOptions = {
}){
const { a = defaultA , c } = o;
this.#a = a;
this.#c = c;
}
a() {
this.#a();
}
c() {
console.log(this.#c);
}
}
new A();
"
);
test!(
Syntax::Typescript(TsConfig {
decorators: true,
..Default::default()
}),
|_| chain!(strip(), resolver(), dce(Default::default())),
issue_1156_1,
"
export interface D {
resolve: any;
reject: any;
}
export function d(): D {
let methods;
const promise = new Promise((resolve, reject) => {
methods = { resolve, reject };
});
return Object.assign(promise, methods);
}
",
"
export function d() {
let methods;
const promise = new Promise((resolve, reject)=>{
methods = {
resolve,
reject
};
});
return Object.assign(promise, methods);
}
"
);
test!(
Syntax::Typescript(TsConfig {
decorators: true,
..Default::default()
}),
|_| chain!(strip(), resolver(), dce(Default::default()),),
issue_1156_2,
"
interface D {
resolve: any;
reject: any;
}
function d(): D {
let methods;
const promise = new Promise((resolve, reject) => {
methods = { resolve, reject };
});
return Object.assign(promise, methods);
}
class A {
private s: D = d();
a() {
this.s.resolve();
}
b() {
this.s = d();
}
}
new A();
",
"
function d() {
let methods;
const promise = new Promise((resolve, reject)=>{
methods = {
resolve,
reject
};
});
return Object.assign(promise, methods);
}
class A {
s = d();
a() {
this.s.resolve();
}
b() {
this.s = d();
}
}
new A();
"
);
test!(
Syntax::Typescript(TsConfig {
decorators: true,
..Default::default()
}),
|_| chain!(strip(), resolver(), dce(Default::default()),),
issue_1156_3,
"
function d() {
let methods;
const promise = new Promise((resolve, reject) => {
methods = { resolve, reject };
});
return Object.assign(promise, methods);
}
d()
",
"
function d() {
let methods;
const promise = new Promise((resolve, reject)=>{
methods = {
resolve,
reject
};
});
return Object.assign(promise, methods);
}
d();
"
);
test!(
Syntax::Typescript(TsConfig {
decorators: true,
..Default::default()
}),
|_| chain!(strip(), resolver(), dce(Default::default()),),
issue_1156_4,
"
interface D {
resolve: any;
reject: any;
}
function d(): D {
let methods;
const promise = new Promise((resolve, reject) => {
methods = { resolve, reject };
});
return Object.assign(promise, methods);
}
class A {
private s: D = d();
a() {
this.s.resolve();
}
}
new A();
",
"
function d() {
let methods;
const promise = new Promise((resolve, reject)=>{
methods = {
resolve,
reject
};
});
return Object.assign(promise, methods);
}
class A {
s = d();
a() {
this.s.resolve();
}
}
new A();
"
);
optimized_out!(
self_referential_function_01,
"
function foo() {
if (Math.random() > 0.5) {
foo()
}
}
"
);
optimized_out!(
pr_1199_01,
"
function _nonIterableSpread() {
throw new TypeError('Invalid attempt to spread non-iterable instance');
}
"
);
noop!(
deno_8736_1,
"
class DenoStdInternalError1 extends Error {
constructor(message){
super(message);
this.name = 'DenoStdInternalError';
}
}
const DenoStdInternalError = DenoStdInternalError1;
function assert2(expr, msg = '') {
if (!expr) {
throw new DenoStdInternalError(msg);
}
}
const assert1 = assert2;
const assert = assert1;
const TEST = Deno.env.get('TEST');
assert(TEST, 'TEST must be defined!');
console.log(`Test is ${TEST}`);
"
);
noop!(
deno_8736_2,
"
class DenoStdInternalError extends Error {
constructor(message){
super(message);
this.name = 'DenoStdInternalError';
}
}
function assert(expr, msg = '') {
throw new DenoStdInternalError(msg);
}
const TEST = Deno.env.get('TEST');
assert(TEST, 'TEST must be defined!');
console.log(`Test is ${TEST}`);
"
);