swc_ecma_compat_es3/
reserved_word.rs1use swc_common::{util::take::Take, DUMMY_SP};
2use swc_ecma_ast::*;
3use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
4
5pub fn reserved_words(preserve_import: bool) -> impl Pass {
23 visit_mut_pass(ReservedWord { preserve_import })
24}
25struct ReservedWord {
26 pub preserve_import: bool,
27}
28
29impl VisitMut for ReservedWord {
30 noop_visit_mut_type!(fail);
31
32 fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
33 let mut extra_exports = Vec::new();
34
35 n.iter_mut().for_each(|module_item| {
36 match module_item {
37 ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
38 decl: decl @ Decl::Fn(..) | decl @ Decl::Class(..),
39 ..
40 })) => {
41 let ident = match decl {
42 Decl::Class(d) => d.ident.clone(),
43 Decl::Fn(d) => d.ident.clone(),
44 _ => {
45 unreachable!()
46 }
47 };
48
49 if !ident.is_reserved_in_es3() {
50 return;
51 }
52
53 *module_item = decl.take().into();
54
55 let mut orig = ident.clone();
56 orig.visit_mut_with(self);
57
58 extra_exports.push(
59 ExportNamedSpecifier {
60 span: DUMMY_SP,
61 orig: orig.into(),
62 exported: Some(ident.into()),
63 is_type_only: false,
64 }
65 .into(),
66 );
67 }
68
69 ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
70 decl: Decl::Var(var),
71 ..
72 })) => {
73 if var.decls.iter().all(|var| {
74 if let Pat::Ident(i) = &var.name {
75 !i.sym.is_reserved_in_es3()
76 } else {
77 true
78 }
79 }) {
80 return;
81 }
82
83 for var in &var.decls {
84 let ident = Ident::from(var.name.clone().expect_ident());
85
86 if !ident.is_reserved_in_es3() {
87 return;
88 }
89
90 let mut orig = ident.clone();
91 orig.visit_mut_with(self);
92
93 extra_exports.push(
94 ExportNamedSpecifier {
95 span: DUMMY_SP,
96 orig: orig.into(),
97 exported: Some(ident.into()),
98 is_type_only: false,
99 }
100 .into(),
101 );
102 }
103
104 *module_item = var.take().into();
105 }
106
107 _ => {}
108 }
109
110 module_item.visit_mut_with(self);
111 });
112
113 if !extra_exports.is_empty() {
114 let module_item = NamedExport {
115 span: DUMMY_SP,
116 specifiers: extra_exports,
117 src: None,
118 type_only: false,
119 with: None,
120 }
121 .into();
122
123 n.push(module_item);
124 }
125 }
126
127 fn visit_mut_export_named_specifier(&mut self, n: &mut ExportNamedSpecifier) {
128 if matches!(&n.orig, ModuleExportName::Ident(ident) if ident.is_reserved_in_es3()) {
129 n.exported.get_or_insert_with(|| n.orig.clone());
130 n.orig.visit_mut_with(self);
131 }
132 }
133
134 fn visit_mut_named_export(&mut self, n: &mut NamedExport) {
135 if n.src.is_none() {
136 n.visit_mut_children_with(self);
137 }
138 }
139
140 fn visit_mut_ident(&mut self, i: &mut Ident) {
141 if self.preserve_import && i.sym == *"import" {
142 return;
143 }
144
145 if i.is_reserved_in_es3() {
146 i.sym = format!("_{}", i.sym).into()
147 }
148 }
149
150 fn visit_mut_import_named_specifier(&mut self, s: &mut ImportNamedSpecifier) {
151 if s.local.is_reserved_in_es3() {
152 s.imported.get_or_insert_with(|| s.local.clone().into());
153 s.local.visit_mut_with(self);
154 }
155 }
156
157 fn visit_mut_member_expr(&mut self, e: &mut MemberExpr) {
158 e.obj.visit_mut_with(self);
159
160 if let MemberProp::Computed(c) = &mut e.prop {
161 c.visit_mut_with(self);
162 }
163 }
164
165 fn visit_mut_prop_name(&mut self, _: &mut PropName) {}
166}
167
168#[cfg(test)]
169mod tests {
170 use swc_ecma_transforms_testing::test;
171
172 use super::*;
173
174 macro_rules! identical {
175 ($name:ident, $src:literal) => {
176 test!(
177 ::swc_ecma_parser::Syntax::default(),
178 |_| reserved_words(false),
179 $name,
180 $src
181 );
182 };
183 }
184
185 test!(
186 ::swc_ecma_parser::Syntax::default(),
187 |_| reserved_words(false),
188 babel_issue_6477,
189 r#"
190function utf8CheckByte(byte) {
191 if (byte <= 0x7F) return 0;
192 else if (byte >> 5 === 0x06) return 2;
193 else if (byte >> 4 === 0x0E) return 3;
194 else if (byte >> 3 === 0x1E) return 4;
195 return -1;
196}
197"#
198 );
199
200 identical!(export_as_default, "export { Foo as default }");
201
202 test!(
203 ::swc_ecma_parser::Syntax::default(),
204 |_| reserved_words(false),
205 issue_7164,
206 r#"
207 import { int } from './a.js'
208 console.log(int)
209 export { int };
210 "#
211 );
212
213 test!(
214 Default::default(),
215 |_| reserved_words(false),
216 issue_7237,
217 r#"
218 export function char() {
219 console.log("char====char");
220 return "";
221 }
222 "#
223 );
224}