mirror of
https://github.com/swc-project/swc.git
synced 2024-11-24 02:06:08 +03:00
fix(es/compat): Make block-scoping
pass rename exports correctly (#8175)
**Related issue:** - Closes #8148
This commit is contained in:
parent
b77d99d4b8
commit
b13bc32027
19
crates/swc/tests/fixture/issues-8xxx/8148/input/.swcrc
Normal file
19
crates/swc/tests/fixture/issues-8xxx/8148/input/.swcrc
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"jsc": {
|
||||
"parser": {
|
||||
"syntax": "ecmascript",
|
||||
"jsx": false
|
||||
},
|
||||
"target": "es5",
|
||||
"loose": false,
|
||||
"minify": {
|
||||
"compress": false,
|
||||
"mangle": false
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "es6"
|
||||
},
|
||||
"minify": false,
|
||||
"isModule": true
|
||||
}
|
10
crates/swc/tests/fixture/issues-8xxx/8148/input/input.js
Normal file
10
crates/swc/tests/fixture/issues-8xxx/8148/input/input.js
Normal file
@ -0,0 +1,10 @@
|
||||
function _toBeMocked() {
|
||||
return "I am the original function";
|
||||
}
|
||||
|
||||
export let toBeMocked = _toBeMocked
|
||||
|
||||
export const mock = {
|
||||
get toBeMocked() { return toBeMocked; },
|
||||
set toBeMocked(mock) { toBeMocked = mock; }
|
||||
}
|
13
crates/swc/tests/fixture/issues-8xxx/8148/output/input.js
Normal file
13
crates/swc/tests/fixture/issues-8xxx/8148/output/input.js
Normal file
@ -0,0 +1,13 @@
|
||||
function _toBeMocked() {
|
||||
return "I am the original function";
|
||||
}
|
||||
export var toBeMocked = _toBeMocked;
|
||||
var _$mock = {
|
||||
get toBeMocked () {
|
||||
return toBeMocked;
|
||||
},
|
||||
set toBeMocked (mock){
|
||||
toBeMocked = mock;
|
||||
}
|
||||
};
|
||||
export { _$mock as mock };
|
@ -21,7 +21,6 @@ use swc_ecma_visit::{
|
||||
};
|
||||
use swc_trace_macro::swc_trace;
|
||||
|
||||
mod operator;
|
||||
mod vars;
|
||||
|
||||
///
|
||||
|
@ -1,94 +0,0 @@
|
||||
use swc_common::{collections::AHashMap, SyntaxContext, DUMMY_SP};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
use swc_trace_macro::swc_trace;
|
||||
|
||||
pub(super) fn rename(map: AHashMap<Id, Id>) -> Rename {
|
||||
Rename { map }
|
||||
}
|
||||
|
||||
pub(super) struct Rename {
|
||||
map: AHashMap<Id, Id>,
|
||||
}
|
||||
|
||||
#[swc_trace]
|
||||
impl VisitMut for Rename {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn visit_mut_ident(&mut self, i: &mut Ident) {
|
||||
// fast path.
|
||||
if i.span.ctxt == SyntaxContext::empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(id) = self.map.get(&i.to_id()) {
|
||||
*i = Ident::new(id.0.clone(), i.span.with_ctxt(id.1));
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_member_prop(&mut self, n: &mut MemberProp) {
|
||||
if let MemberProp::Computed(n) = n {
|
||||
n.visit_mut_with(self);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_object_pat_prop(&mut self, i: &mut ObjectPatProp) {
|
||||
match i {
|
||||
ObjectPatProp::Assign(p) => {
|
||||
p.value.visit_mut_with(self);
|
||||
|
||||
let orig = p.key.clone();
|
||||
p.key.visit_mut_with(self);
|
||||
|
||||
if orig.to_id() == p.key.to_id() {
|
||||
return;
|
||||
}
|
||||
|
||||
match p.value.take() {
|
||||
Some(default) => {
|
||||
*i = ObjectPatProp::KeyValue(KeyValuePatProp {
|
||||
key: PropName::Ident(orig),
|
||||
value: Box::new(Pat::Assign(AssignPat {
|
||||
span: DUMMY_SP,
|
||||
left: Box::new(Pat::Ident(p.key.clone().into())),
|
||||
right: default,
|
||||
})),
|
||||
});
|
||||
}
|
||||
None => {
|
||||
*i = ObjectPatProp::KeyValue(KeyValuePatProp {
|
||||
key: PropName::Ident(orig),
|
||||
value: Box::new(Pat::Ident(p.key.clone().into())),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
i.visit_mut_children_with(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_prop(&mut self, n: &mut Prop) {
|
||||
if let Prop::Shorthand(ident) = n {
|
||||
if let Some(id) = self.map.get(&ident.to_id()) {
|
||||
*n = KeyValueProp {
|
||||
key: PropName::Ident(ident.clone()),
|
||||
value: Box::new(Ident::new(id.0.clone(), ident.span.with_ctxt(id.1)).into()),
|
||||
}
|
||||
.into();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
n.visit_mut_children_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_prop_name(&mut self, n: &mut PropName) {
|
||||
if let PropName::Computed(n) = n {
|
||||
n.visit_mut_with(self);
|
||||
}
|
||||
}
|
||||
}
|
@ -5,12 +5,10 @@ use swc_common::{
|
||||
Mark, SyntaxContext,
|
||||
};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_transforms_base::scope::ScopeKind;
|
||||
use swc_ecma_transforms_base::{rename::remap, scope::ScopeKind};
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
use swc_trace_macro::swc_trace;
|
||||
|
||||
use super::operator::{rename, Rename};
|
||||
|
||||
pub(super) fn block_scoped_vars() -> impl VisitMut {
|
||||
BlockScopedVars::default()
|
||||
}
|
||||
@ -62,7 +60,7 @@ impl BlockScopedVars {
|
||||
/// - For third, we rename all declarations which may conflict.
|
||||
fn handle_program<N>(&mut self, n: &mut N)
|
||||
where
|
||||
N: VisitMutWith<Self> + VisitMutWith<Rename>,
|
||||
N: VisitMutWith<Self> + for<'aa> VisitMutWith<dyn 'aa + VisitMut>,
|
||||
{
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
@ -81,7 +79,7 @@ impl BlockScopedVars {
|
||||
|
||||
// dbg!(&rename_map);
|
||||
|
||||
n.visit_mut_with(&mut rename(rename_map))
|
||||
n.visit_mut_with(&mut remap(&rename_map, Default::default()) as &mut dyn VisitMut);
|
||||
}
|
||||
|
||||
fn with_scope(&mut self, kind: ScopeKind, op: impl FnOnce(&mut Self)) {
|
||||
|
@ -54,6 +54,14 @@ pub fn rename_with_config(map: &AHashMap<Id, JsWord>, config: Config) -> impl '_
|
||||
})
|
||||
}
|
||||
|
||||
pub fn remap(map: &AHashMap<Id, Id>, config: Config) -> impl '_ + Fold + VisitMut {
|
||||
as_folder(Operator {
|
||||
rename: map,
|
||||
config,
|
||||
extra: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn renamer<R>(config: Config, renamer: R) -> impl Fold + VisitMut
|
||||
where
|
||||
R: Renamer,
|
||||
|
@ -1,10 +1,10 @@
|
||||
use swc_atoms::JsWord;
|
||||
use swc_common::{
|
||||
collections::AHashMap,
|
||||
util::{move_map::MoveMap, take::Take},
|
||||
Spanned, SyntaxContext, DUMMY_SP,
|
||||
};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::ident::IdentLike;
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
|
||||
use crate::{
|
||||
@ -12,14 +12,20 @@ use crate::{
|
||||
perf::{cpu_count, ParExplode, Parallel, ParallelExt},
|
||||
};
|
||||
|
||||
pub(super) struct Operator<'a> {
|
||||
pub rename: &'a AHashMap<Id, JsWord>,
|
||||
pub(super) struct Operator<'a, I>
|
||||
where
|
||||
I: IdentLike,
|
||||
{
|
||||
pub rename: &'a AHashMap<Id, I>,
|
||||
pub config: Config,
|
||||
|
||||
pub extra: Vec<ModuleItem>,
|
||||
}
|
||||
|
||||
impl Operator<'_> {
|
||||
impl<I> Operator<'_, I>
|
||||
where
|
||||
I: IdentLike,
|
||||
{
|
||||
fn keep_class_name(&mut self, ident: &mut Ident, class: &mut Class) -> Option<ClassExpr> {
|
||||
if !self.config.keep_class_names {
|
||||
return None;
|
||||
@ -55,7 +61,10 @@ impl Operator<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Parallel for Operator<'_> {
|
||||
impl<I> Parallel for Operator<'_, I>
|
||||
where
|
||||
I: IdentLike,
|
||||
{
|
||||
fn create(&self) -> Self {
|
||||
Self {
|
||||
rename: self.rename,
|
||||
@ -73,7 +82,10 @@ impl Parallel for Operator<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl ParExplode for Operator<'_> {
|
||||
impl<I> ParExplode for Operator<'_, I>
|
||||
where
|
||||
I: IdentLike,
|
||||
{
|
||||
fn after_one_stmt(&mut self, _: &mut Vec<Stmt>) {}
|
||||
|
||||
fn after_one_module_item(&mut self, stmts: &mut Vec<ModuleItem>) {
|
||||
@ -81,7 +93,10 @@ impl ParExplode for Operator<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VisitMut for Operator<'a> {
|
||||
impl<'a, I> VisitMut for Operator<'a, I>
|
||||
where
|
||||
I: IdentLike,
|
||||
{
|
||||
noop_visit_mut_type!();
|
||||
|
||||
/// Preserve key of properties.
|
||||
@ -576,12 +591,18 @@ impl<'a> VisitMut for Operator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
struct VarFolder<'a, 'b> {
|
||||
orig: &'a mut Operator<'b>,
|
||||
struct VarFolder<'a, 'b, I>
|
||||
where
|
||||
I: IdentLike,
|
||||
{
|
||||
orig: &'a mut Operator<'b, I>,
|
||||
renamed: &'a mut Vec<ExportSpecifier>,
|
||||
}
|
||||
|
||||
impl VisitMut for VarFolder<'_, '_> {
|
||||
impl<I> VisitMut for VarFolder<'_, '_, I>
|
||||
where
|
||||
I: IdentLike,
|
||||
{
|
||||
noop_visit_mut_type!();
|
||||
|
||||
#[inline]
|
||||
@ -601,16 +622,21 @@ impl VisitMut for VarFolder<'_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Operator<'a> {
|
||||
impl<'a, I> Operator<'a, I>
|
||||
where
|
||||
I: IdentLike,
|
||||
{
|
||||
/// Returns `Ok(renamed_ident)` if ident should be renamed.
|
||||
fn rename_ident(&mut self, ident: &mut Ident) -> Result<(), ()> {
|
||||
if let Some(sym) = self.rename.get(&ident.to_id()) {
|
||||
if *sym == ident.sym {
|
||||
if let Some(new_id) = self.rename.get(&ident.to_id()) {
|
||||
let (new_sym, new_ctxt) = new_id.to_id();
|
||||
|
||||
if new_sym == ident.sym {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
ident.span = ident.span.with_ctxt(SyntaxContext::empty());
|
||||
ident.sym = sym.clone();
|
||||
ident.span = ident.span.with_ctxt(new_ctxt);
|
||||
ident.sym = new_sym;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
|
||||
function _toBeMocked() {
|
||||
return "I am the original function";
|
||||
}
|
||||
|
||||
export let toBeMocked = _toBeMocked
|
||||
|
||||
export const mock = {
|
||||
get toBeMocked() { return toBeMocked; },
|
||||
set toBeMocked(mock) { toBeMocked = mock; }
|
||||
}
|
@ -2,12 +2,26 @@ use swc_atoms::JsWord;
|
||||
use swc_common::{Span, SyntaxContext};
|
||||
use swc_ecma_ast::{BindingIdent, Id, Ident};
|
||||
|
||||
pub trait IdentLike: Sized {
|
||||
pub trait IdentLike: Sized + Send + Sync + 'static {
|
||||
fn from_ident(i: &Ident) -> Self;
|
||||
fn to_id(&self) -> Id;
|
||||
fn into_id(self) -> Id;
|
||||
}
|
||||
|
||||
impl IdentLike for JsWord {
|
||||
fn from_ident(i: &Ident) -> Self {
|
||||
i.sym.clone()
|
||||
}
|
||||
|
||||
fn to_id(&self) -> Id {
|
||||
(self.clone(), Default::default())
|
||||
}
|
||||
|
||||
fn into_id(self) -> Id {
|
||||
(self, Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl IdentLike for BindingIdent {
|
||||
fn from_ident(i: &Ident) -> Self {
|
||||
i.clone().into()
|
||||
|
Loading…
Reference in New Issue
Block a user