diff --git a/.changeset/dry-queens-clean.md b/.changeset/dry-queens-clean.md new file mode 100644 index 000000000000..3127269308c8 --- /dev/null +++ b/.changeset/dry-queens-clean.md @@ -0,0 +1,6 @@ +--- +swc_ecma_minifier: patch +swc_core: patch +--- + +fix(es/minifier): Preserve `__proto__` shorthand property behavior diff --git a/crates/swc_ecma_minifier/src/compress/optimize/util.rs b/crates/swc_ecma_minifier/src/compress/optimize/util.rs index f07e6b56b1e5..7d2d1f0d3614 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/util.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/util.rs @@ -469,8 +469,9 @@ impl VisitMut for Finalizer<'_> { if let Prop::Shorthand(i) = n { if let Some(expr) = self.lits.get(&i.to_id()) { + let key = prop_name_from_ident(i.take()); *n = Prop::KeyValue(KeyValueProp { - key: i.take().into(), + key, value: expr.clone(), }); self.changed = true; @@ -566,10 +567,8 @@ impl VisitMut for NormalMultiReplacer<'_> { debug!("multi-replacer: Replaced `{}` as shorthand", i); self.changed = true; - *p = Prop::KeyValue(KeyValueProp { - key: PropName::Ident(IdentName::new(i.sym.clone(), i.span)), - value, - }); + let key = prop_name_from_ident(i.take()); + *p = Prop::KeyValue(KeyValueProp { key, value }); } } } @@ -642,10 +641,8 @@ impl VisitMut for ExprReplacer { } else { unreachable!("`{}` is already taken", i) }; - *p = Prop::KeyValue(KeyValueProp { - key: PropName::Ident(i.clone().into()), - value, - }); + let key = prop_name_from_ident(i.take()); + *p = Prop::KeyValue(KeyValueProp { key, value }); } } } @@ -820,3 +817,21 @@ pub fn get_ids_of_pat(pat: &Pat) -> Vec { append(pat, &mut idents); idents } + +/// Creates a PropName for a shorthand property, handling the special case of +/// `__proto__`. When the property name is `__proto__`, it must be converted to +/// a computed property to preserve JavaScript semantics. +fn prop_name_from_ident(ident: Ident) -> PropName { + if ident.sym == "__proto__" { + PropName::Computed(ComputedPropName { + span: ident.span, + expr: Box::new(Expr::Lit(Lit::Str(Str { + span: ident.span, + value: ident.sym.clone(), + raw: None, + }))), + }) + } else { + ident.into() + } +} diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/11105/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/11105/input.js new file mode 100644 index 000000000000..4f08d4ebff6d --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/11105/input.js @@ -0,0 +1 @@ +var __proto__ = []; console.log({ __proto__ } instanceof Array) // false diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/11105/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/11105/output.js new file mode 100644 index 000000000000..f8f64ff4ff0e --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/11105/output.js @@ -0,0 +1,3 @@ +console.log(({ + ["__proto__"]: [] +}) instanceof Array); // false