swc_ecma_compat_es2015/
block_scoped_fn.rs1use swc_common::{util::take::Take, DUMMY_SP};
2use swc_ecma_ast::*;
3use swc_ecma_utils::IdentUsageFinder;
4use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
5use swc_trace_macro::swc_trace;
6
7pub fn block_scoped_functions() -> impl Pass {
8 visit_mut_pass(BlockScopedFns)
9}
10
11#[derive(Clone, Copy)]
12struct BlockScopedFns;
13
14#[swc_trace]
15impl VisitMut for BlockScopedFns {
16 noop_visit_mut_type!(fail);
17
18 fn visit_mut_function(&mut self, n: &mut Function) {
19 let Some(body) = &mut n.body else { return };
20
21 n.params.visit_mut_with(self);
22
23 body.visit_mut_children_with(self);
25 }
26
27 fn visit_mut_block_stmt(&mut self, n: &mut BlockStmt) {
28 n.visit_mut_children_with(self);
29
30 let mut stmts = Vec::with_capacity(n.stmts.len());
31 let mut extra_stmts = Vec::with_capacity(n.stmts.len());
32
33 for stmt in n.stmts.take() {
34 if let Stmt::Expr(ExprStmt { ref expr, .. }) = stmt {
35 if let Expr::Lit(Lit::Str(..)) = &**expr {
36 stmts.push(stmt);
37 continue;
38 }
39 }
40
41 if let Stmt::Decl(Decl::Fn(decl)) = stmt {
42 if IdentUsageFinder::find(&decl.ident.to_id(), &decl.function) {
43 extra_stmts.push(decl.into());
44 continue;
45 }
46 stmts.push(
47 VarDecl {
48 span: DUMMY_SP,
49 kind: VarDeclKind::Let,
50 decls: vec![VarDeclarator {
51 span: DUMMY_SP,
52 name: decl.ident.clone().into(),
53 init: Some(Box::new(Expr::Fn(FnExpr {
54 ident: Some(decl.ident),
55 function: decl.function,
56 }))),
57 definite: false,
58 }],
59 ..Default::default()
60 }
61 .into(),
62 )
63 } else {
64 extra_stmts.push(stmt)
65 }
66 }
67
68 stmts.append(&mut extra_stmts);
69
70 n.stmts = stmts
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use swc_ecma_transforms_testing::test;
77
78 use super::*;
79
80 test!(
81 ::swc_ecma_parser::Syntax::default(),
82 |_| block_scoped_functions(),
83 hoisting,
84 r#"
85{
86 function fn1() { fn2(); }
87
88 fn1();
89
90 function fn2() { }
91}
92"#
93 );
94
95 test!(
96 ::swc_ecma_parser::Syntax::default(),
97 |_| block_scoped_functions(),
98 basic,
99 r#"{
100 function name (n) {
101 return n;
102 }
103}
104
105name("Steve");"#
106 );
107
108 test!(
109 ::swc_ecma_parser::Syntax::default(),
110 |_| block_scoped_functions(),
111 basic_2,
112 r#"
113 {
114 function foo() {
115 return function bar() {
116 {
117 function baz() {}
118 }
119 };
120 function baz() {}
121 {
122 function bar() {}
123 {
124 function bar() {}
125 }
126 }
127 }
128 }
129 "#
130 );
131
132 test!(
133 ::swc_ecma_parser::Syntax::default(),
134 |_| block_scoped_functions(),
135 issue_271,
136 "
137function foo(scope) {
138 scope.startOperation = startOperation;
139
140 function startOperation(operation) {
141 scope.agentOperation = operation;
142 }
143}
144"
145 );
146
147 test!(
148 ::swc_ecma_parser::Syntax::default(),
149 |_| block_scoped_functions(),
150 issue_288_1,
151 "function components_Link_extends() { components_Link_extends = Object.assign || function \
152 (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for \
153 (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { \
154 target[key] = source[key]; } } } return target; }; return \
155 components_Link_extends.apply(this, arguments); }
156
157"
158 );
159
160 test!(
161 ::swc_ecma_parser::Syntax::default(),
162 |_| block_scoped_functions(),
163 issue_288_2,
164 "function _extends() {
165 module.exports = _extends = Object.assign || function (target) {
166 for (var i = 1; i < arguments.length; i++) {
167 var source = arguments[i];
168
169 for (var key in source) {
170 if (Object.prototype.hasOwnProperty.call(source, key)) {
171 target[key] = source[key];
172 }
173 }
174 }
175
176 return target;
177 };
178
179 return _extends.apply(this, arguments);
180}
181"
182 );
183
184 test!(
185 ::swc_ecma_parser::Syntax::default(),
186 |_| block_scoped_functions(),
187 hoisting_directives,
188 "function foo() {
189 'use strict';
190 function _interop_require_default(obj) {
191 return obj && obj.__esModule ? obj : {
192 default: obj
193 };
194 }
195 }"
196 );
197}