Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions crates/swc_ecma_minifier/src/compress/pure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,121 @@ impl Repeated for Pure<'_> {
}

impl Pure<'_> {
fn merge_duplicate_imports(&mut self, items: &mut Vec<ModuleItem>) {
use std::collections::HashMap;

let mut import_map: HashMap<String, Vec<usize>> = HashMap::new();

// First pass: group imports by source
for (idx, item) in items.iter().enumerate() {
if let ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)) = item {
let src = import_decl.src.value.to_string();
import_map.entry(src).or_default().push(idx);
}
}

let mut indices_to_remove = Vec::new();

// Second pass: merge imports for each source
for (_, mut indices) in import_map {
if indices.len() > 1 {
// Sort indices to keep the first one and merge others into it
indices.sort();
let first_idx = indices[0];

let mut merged_specifiers = Vec::new();

// Collect all specifiers from all imports with this source
for &idx in &indices {
if let ModuleItem::ModuleDecl(ModuleDecl::Import(import_decl)) = &items[idx] {
for spec in &import_decl.specifiers {
// Check if we already have this specifier
let mut should_add = true;

match spec {
ImportSpecifier::Named(named) => {
// Check for duplicates based on imported name
for existing_spec in &merged_specifiers {
if let ImportSpecifier::Named(existing_named) =
existing_spec
{
let named_imported = match &named.imported {
Some(ModuleExportName::Ident(ident)) => &ident.sym,
None => &named.local.sym,
_ => continue,
};
let existing_imported = match &existing_named.imported {
Some(ModuleExportName::Ident(ident)) => &ident.sym,
None => &existing_named.local.sym,
_ => continue,
};
if named_imported == existing_imported {
should_add = false;
break;
}
}
}
}
ImportSpecifier::Default(_) => {
// Check if we already have a default import
for existing_spec in &merged_specifiers {
if matches!(existing_spec, ImportSpecifier::Default(_)) {
should_add = false;
break;
}
}
}
ImportSpecifier::Namespace(_) => {
// Don't merge namespace imports with others if other types
// exist
for existing_spec in &merged_specifiers {
if !matches!(existing_spec, ImportSpecifier::Namespace(_)) {
should_add = false;
break;
}
}
// Also don't add other types if namespace exists
if !merged_specifiers.is_empty()
&& !matches!(
merged_specifiers[0],
ImportSpecifier::Namespace(_)
)
{
should_add = false;
}
}
}

if should_add {
merged_specifiers.push(spec.clone());
}
}
}
}

// Update the first import with merged specifiers
if let ModuleItem::ModuleDecl(ModuleDecl::Import(ref mut import_decl)) =
&mut items[first_idx]
{
import_decl.specifiers = merged_specifiers;
}

// Mark other imports for removal
for &idx in &indices[1..] {
indices_to_remove.push(idx);
}

self.changed = true;
}
}

// Third pass: remove duplicate imports (in reverse order to maintain indices)
indices_to_remove.sort_by(|a, b| b.cmp(a));
for idx in indices_to_remove {
items.remove(idx);
}
}

fn handle_stmt_likes<T>(&mut self, stmts: &mut Vec<T>)
where
T: ModuleItemExt + Take,
Expand Down Expand Up @@ -808,6 +923,8 @@ impl VisitMut for Pure<'_> {
fn visit_mut_module_items(&mut self, items: &mut Vec<ModuleItem>) {
self.visit_par(1, items);

self.merge_duplicate_imports(items);

self.handle_stmt_likes(items);
}

Expand Down
1 change: 1 addition & 0 deletions crates/swc_ecma_minifier/src/option/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ pub struct CompressOptions {

#[cfg_attr(feature = "extra-serde", serde(default))]
pub experimental: CompressExperimentalOptions,

}

impl CompressOptions {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"compress": {}
}
10 changes: 10 additions & 0 deletions crates/swc_ecma_minifier/tests/fixture/issues/11133/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { add } from 'math-library';
import { subtract, multiply } from 'math-library';
import { divide } from 'math-library';
import { add as addAgain } from 'math-library';

console.log(add(1, 2));
console.log(subtract(5, 3));
console.log(multiply(2, 4));
console.log(divide(10, 2));
console.log(addAgain(3, 4));
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { add, subtract, multiply, divide } from 'math-library';
console.log(add(1, 2));
console.log(subtract(5, 3));
console.log(multiply(2, 4));
console.log(divide(10, 2));
console.log(add(3, 4));
Loading