swc_ecma_transforms_compat/es2015/
block_scoped_fn.rsuse swc_common::{util::take::Take, Spanned, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_utils::IdentUsageFinder;
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
use swc_trace_macro::swc_trace;
#[tracing::instrument(level = "info", skip_all)]
pub fn block_scoped_functions() -> impl Fold + VisitMut {
as_folder(BlockScopedFns)
}
#[derive(Clone, Copy)]
struct BlockScopedFns;
#[swc_trace]
impl VisitMut for BlockScopedFns {
noop_visit_mut_type!();
fn visit_mut_stmts(&mut self, items: &mut Vec<Stmt>) {
let mut stmts = Vec::with_capacity(items.len());
let mut extra_stmts = Vec::with_capacity(items.len());
for mut stmt in items.take() {
if let Stmt::Expr(ExprStmt { ref expr, .. }) = stmt {
if let Expr::Lit(Lit::Str(..)) = &**expr {
stmts.push(stmt);
continue;
}
}
if stmt.span().is_dummy() {
extra_stmts.push(stmt)
} else {
match stmt {
Stmt::Decl(Decl::Fn(decl)) => {
if IdentUsageFinder::find(&decl.ident.to_id(), &decl.function) {
extra_stmts.push(Stmt::Decl(Decl::Fn(decl)));
continue;
}
stmts.push(
VarDecl {
span: DUMMY_SP,
kind: VarDeclKind::Let,
decls: vec![VarDeclarator {
span: DUMMY_SP,
name: decl.ident.clone().into(),
init: Some(Box::new(Expr::Fn(FnExpr {
ident: Some(decl.ident),
function: decl.function,
}))),
definite: false,
}],
declare: false,
}
.into(),
)
}
_ => {
stmt.visit_mut_children_with(self);
extra_stmts.push(stmt)
}
}
}
}
stmts.append(&mut extra_stmts);
*items = stmts
}
}
#[cfg(test)]
mod tests {
use swc_ecma_transforms_testing::test;
use super::*;
test!(
::swc_ecma_parser::Syntax::default(),
|_| block_scoped_functions(),
hoisting,
r#"
{
function fn1() { fn2(); }
fn1();
function fn2() { }
}
"#,
r#"
{
let fn1 = function fn1() {
fn2();
};
let fn2 = function fn2() {
};
fn1();
}
"#
);
test!(
::swc_ecma_parser::Syntax::default(),
|_| block_scoped_functions(),
basic,
r#"{
function name (n) {
return n;
}
}
name("Steve");"#,
r#"{
let name = function name(n) {
return n;
};
}
name("Steve");
"#
);
test!(
::swc_ecma_parser::Syntax::default(),
|_| block_scoped_functions(),
issue_271,
"
function foo(scope) {
scope.startOperation = startOperation;
function startOperation(operation) {
scope.agentOperation = operation;
}
}
",
"
function foo(scope) {
let startOperation = function startOperation(operation) {
scope.agentOperation = operation;
};
scope.startOperation = startOperation;
}
"
);
test!(
::swc_ecma_parser::Syntax::default(),
|_| block_scoped_functions(),
issue_288_1,
"function components_Link_extends() { components_Link_extends = Object.assign || function \
(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for \
(var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { \
target[key] = source[key]; } } } return target; }; return \
components_Link_extends.apply(this, arguments); }
",
"function components_Link_extends() { components_Link_extends = Object.assign || function \
(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for \
(var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { \
target[key] = source[key]; } } } return target; }; return \
components_Link_extends.apply(this, arguments); }
"
);
test!(
::swc_ecma_parser::Syntax::default(),
|_| block_scoped_functions(),
issue_288_2,
"function _extends() {
module.exports = _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
",
"function _extends() {
module.exports = _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
"
);
test!(
::swc_ecma_parser::Syntax::default(),
|_| block_scoped_functions(),
hoisting_directives,
"function foo() {
'use strict';
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
}",
"function foo() {
'use strict';
let _interopRequireDefault = function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
};
}
"
);
}