Repository: swc-project/swc (rust)

Issue:
`export const` breaks toplevel analysis
### Describe the bug

When a const function depends on an exported const, the toplevel option fails to reduce the function.

### Input code

```typescript
export const envKey = "staging" || "production"

const environmentResolver = () => {
  if (envKey === "production") {
    return "production"
  }
  if (envKey === "staging") {
    return "staging"
  }
  if (envKey === "test") {
    return "test"
  }
  if (envKey === "development") {
    return "development"
  }
  throw new Error(`Unknown environment: ${envKey}`)
}
export const environment = environmentResolver()
```


### Config

```json
{
  "jsc": {
    "parser": {
      "syntax": "ecmascript",
      "jsx": false
    },
    "target": "es5",
    "loose": true,
    "minify": {
      "compress": {
        "arguments": true,
        "arrows": true,
        "booleans": true,
        "booleans_as_integers": true,
        "collapse_vars": false,
        "comparisons": true,
        "computed_props": true,
        "conditionals": true,
        "dead_code": true,
        "directives": true,
        "drop_console": false,
        "drop_debugger": true,
        "evaluate": true,
        "expression": true,
        "hoist_funs": true,
        "hoist_props": true,
        "hoist_vars": false,
        "if_return": true,
        "join_vars": false,
        "keep_classnames": false,
        "keep_fargs": true,
        "keep_fnames": true,
        "keep_infinity": false,
        "loops": true,
        "negate_iife": true,
        "properties": true,
        "reduce_funcs": true,
        "reduce_vars": false,
        "side_effects": true,
        "switches": true,
        "toplevel": true,
        "typeofs": true,
        "unsafe": true,
        "unsafe_arrows": true,
        "unsafe_comps": true,
        "unsafe_Function": true,
        "unsafe_math": true,
        "unsafe_symbols": true,
        "unsafe_methods": true,
        "unsafe_proto": true,
        "unsafe_regexp": true,
        "unsafe_undefined": true,
        "unused": true,
        "const_to_let": false,
        "pristine_globals": true,
        "passes": 0
      },
      "mangle": false
    }
  },
  "module": {
    "type": "es6"
  },
  "minify": true,
  "isModule": true
}
```


### Playground link

https://play.swc.rs/?version=1.3.66&code=H4sIAAAAAAAAA3WRMW%2BDQAyF9%2FsVT6gDTOyR6NapW9XugeCGU8FGPgONkvz3HkmpkiaRbrCe%2FdnPvjzHpN4IoujLEION1ISGlBx996IWBQ4G4vGVdiiQBCu3nrcJDgckvUo9bMwLJ87lOd4bHxDfEKiGCSpCWbU0h1FCTWV9nkCt7zyXM4lqByMNpHOHqfGbZm4h3O7QSwh%2BbuAZ1hBC2RG6OLMl92fMq3BHbG8UpB1Jo8s0Q%2FGMvQP8J9LFfFFcOc5OBYCSDcrXywDHO%2FCy%2B39y0R9gRsFumJP4AKhppFb6eakb7jL3i1ujMoFpwouqaLr%2B4C%2BWiS%2BPs8LT%2FjziuM7c8eZ3l7p4vDsnTTPn3A%2BlfvV%2FLQIAAA%3D%3D&config=H4sIAAAAAAAAA32VS5IbIQyG9z6Fq9dZZJMscoDs5gwUBnUbDyAKCY9dU7571K95iuzc%2Fych9EB%2BPRyPw4Xc8Of4Kj%2Flo9hKUN%2B%2BRaF7ZnsTZQCXLLkaCg8%2FdnqhGY02EizSYyUD2zoBL170azMfIiKBaFwbbFIKOYz3j%2FEcplKB6IMmqhzXEmSmT%2B4bqvii6CfECDb%2FhxhLJmSGCapi5TBGWwjM1S54SfITT1KtQKiFmGFj8KZULCrPPnDALId%2Bpx6sNw49KChUcByuoLlJLHHLJOkpF16wh1ObpqXFX7zhamOzrMSE29IRue13dsZAbMamFWFlnQqssFPbMJoK3KoS8IIh99yeAaQA0RJlm6BrMco0KTda2e6pwZBHmVe%2BKwfLbGtZZpikpCaEUanrXBmoHLR4FXxzMBfW9WmnDBQ8GBhHGRTFl14Cu7MWk7FEuEJUyL0AjoqL9N1qqa266b3NDc%2BvpE%2F%2FSu6sDt1mkCyfu5Du6YTa49qdgc%2Fo%2B1yaw9ilVZbGrXRxyx5kVMBrFo00fX63bBhNXPbm154W2TQsJ5op4kldGkXGfunqz017vG3pZPP0vhLWRX3YDIaEvi1w%2BwuYe70u7t%2FDu9G%2Bp%2FegQ6Cn3XHWDo9%2FptqC000GAAA%3D

### Expected behavior

expected the following output
```
export var environment="staging";
```

### Actual behavior

_No response_

### Version

1.3.66

### Additional context

_No response_

Pull request:
feat(es/minifier): Inline constants even if they are exported
**Description:**

**Related issue:**

 - Closes #7575.

Patch:
diff --git a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs
index ac1ecfa4cf..c476858020 100644
--- a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs
+++ b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs
@@ -281,7 +281,8 @@ impl Optimizer<'_> {
             }
 
             // Single use => inlined
-            if is_inline_enabled
+            if !self.ctx.is_exported
+                && is_inline_enabled
                 && usage.declared
                 && !should_preserve
                 && !usage.reassigned()
diff --git a/crates/swc_ecma_minifier/src/compress/optimize/unused.rs b/crates/swc_ecma_minifier/src/compress/optimize/unused.rs
index 4525871404..a6ae885f44 100644
--- a/crates/swc_ecma_minifier/src/compress/optimize/unused.rs
+++ b/crates/swc_ecma_minifier/src/compress/optimize/unused.rs
@@ -707,6 +707,7 @@ impl Optimizer<'_> {
                     if let Some(var) = self.data.vars.get(&i.to_id()) {
                         // technically this is inline
                         if !var.inline_prevented
+                            && !var.exported
                             && var.usage_count == 0
                             && var.declared
                             && (!var.declared_as_fn_param || !used_arguments || self.ctx.in_strict)
diff --git a/crates/swc_ecma_minifier/src/program_data.rs b/crates/swc_ecma_minifier/src/program_data.rs
index dab2788713..3d44fe6a47 100644
--- a/crates/swc_ecma_minifier/src/program_data.rs
+++ b/crates/swc_ecma_minifier/src/program_data.rs
@@ -509,6 +509,10 @@ impl VarDataLike for VarUsageInfo {
         self.inline_prevented = true;
     }
 
+    fn mark_as_exported(&mut self) {
+        self.exported = true;
+    }
+
     fn mark_initialized_with_safe_value(&mut self) {
         self.no_side_effect_for_member_access = true;
     }
diff --git a/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs b/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs
index 7b8e58ce80..5493af52dd 100644
--- a/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs
+++ b/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs
@@ -523,7 +523,7 @@ where
                 let ids = find_pat_ids(v);
 
                 for id in ids {
-                    self.data.var_or_default(id).prevent_inline();
+                    self.data.var_or_default(id).mark_as_exported();
                 }
             }
             _ => {}
diff --git a/crates/swc_ecma_usage_analyzer/src/analyzer/storage.rs b/crates/swc_ecma_usage_analyzer/src/analyzer/storage.rs
index a262d830aa..7ac126e35c 100644
--- a/crates/swc_ecma_usage_analyzer/src/analyzer/storage.rs
+++ b/crates/swc_ecma_usage_analyzer/src/analyzer/storage.rs
@@ -72,6 +72,8 @@ pub trait VarDataLike: Sized {
 
     fn prevent_inline(&mut self);
 
+    fn mark_as_exported(&mut self);
+
     fn mark_initialized_with_safe_value(&mut self);
 
     fn mark_as_pure_fn(&mut self);


Test patch:
diff --git a/crates/swc/tests/tsc-references/taggedTemplatesWithTypeArguments1.2.minified.js b/crates/swc/tests/tsc-references/taggedTemplatesWithTypeArguments1.2.minified.js
index d7a56bc096..c019f21f2f 100644
--- a/crates/swc/tests/tsc-references/taggedTemplatesWithTypeArguments1.2.minified.js
+++ b/crates/swc/tests/tsc-references/taggedTemplatesWithTypeArguments1.2.minified.js
@@ -18,6 +18,6 @@ export const b = g`
 export let c = obj.prop`${(input)=>({
         ...input
     })}`;
-c.returnedObjProp.x, c.returnedObjProp.y, c.returnedObjProp.z, c = obj.prop`${(input)=>({
+c.returnedObjProp.x, c.returnedObjProp.y, c.returnedObjProp.z, (c = obj.prop`${(input)=>({
         ...input
-    })}`, c.returnedObjProp.x, c.returnedObjProp.y, c.returnedObjProp.z;
+    })}`).returnedObjProp.x, c.returnedObjProp.y, c.returnedObjProp.z;
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/7575/1/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/7575/1/input.js
new file mode 100644
index 0000000000..d29359e54e
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/7575/1/input.js
@@ -0,0 +1,18 @@
+export const envKey = "staging" || "production"
+
+const environmentResolver = () => {
+    if (envKey === "production") {
+        return "production"
+    }
+    if (envKey === "staging") {
+        return "staging"
+    }
+    if (envKey === "test") {
+        return "test"
+    }
+    if (envKey === "development") {
+        return "development"
+    }
+    throw new Error(`Unknown environment: ${envKey}`)
+}
+export const environment = environmentResolver()
\ No newline at end of file
diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/7575/1/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/7575/1/output.js
new file mode 100644
index 0000000000..9a774e4cf0
--- /dev/null
+++ b/crates/swc_ecma_minifier/tests/fixture/issues/7575/1/output.js
@@ -0,0 +1,2 @@
+export const envKey = "staging";
+export const environment = "staging";