Skip to content

Conversation

@grantjoy
Copy link
Contributor

Summary

This PR fixes ESM interoperability issues where named exports (particularly ExpressReceiver, SocketModeReceiver, HTTPReceiver, and AwsLambdaReceiver) weren't accessible when importing from ESM, as identified by @arethetypeswrong/cli.

Problem

The current export pattern:

export { default as ExpressReceiver, ExpressReceiverOptions } from './receivers/ExpressReceiver';

Generates CommonJS code like:

Object.defineProperty(exports, "ExpressReceiver", { 
  enumerable: true, 
  get: function () { return __importDefault(ExpressReceiver_1).default; } 
});

This dynamic property access pattern isn't statically analyzable by Node.js's ESM-CJS interop layer, so these exports don't get exposed when importing from ESM:

// ❌ Before: undefined when imported from ESM
import { ExpressReceiver } from '@slack/bolt';

Solution

Refactored to use an import-then-export pattern:

import ExpressReceiver from './receivers/ExpressReceiver';
export { ExpressReceiver };
export type { ExpressReceiverOptions } from './receivers/ExpressReceiver';

This generates simpler, statically analyzable CommonJS code that Node.js can properly expose as named exports in ESM.

Additionally:

  • Separated type-only exports using export type for better tree-shaking
  • Fixed Logger and OAuth type re-exports to be type-only (they're interfaces, not runtime values)

Verification

Before:

$ bunx @arethetypeswrong/cli --from-npm @slack/bolt -f json
{
  "problems": {
    "NamedExports": [
      {
        "missing": [
          "Logger",
          "ExpressReceiver",
          "SocketModeReceiver",
          "HTTPReceiver",
          "AwsLambdaReceiver",
          "Installation",
          "InstallURLOptions",
          "InstallationQuery",
          "InstallationStore",
          "StateStore",
          "InstallProviderOptions"
        ]
      }
    ]
  }
}

After:

$ bunx @arethetypeswrong/cli --pack . -f json
{
  "problems": {}
}

All NamedExports issues resolved!

ESM Import Test:

import { ExpressReceiver, App } from '@slack/bolt';
console.log(typeof ExpressReceiver); // ✅ "function" (was "undefined")
console.log(typeof App); // ✅ "function"

CommonJS Compatibility:

const { ExpressReceiver, App } = require('@slack/bolt');
console.log(typeof ExpressReceiver); // ✅ "function"
console.log(typeof App); // ✅ "function"

Tests

All existing tests pass:

94.52% code coverage

Related Issues

Migration Guide

This change is fully backward compatible. No code changes required for consumers.

Both import styles continue to work:

// ✅ Works before and after
import { App } from '@slack/bolt';

// ✅ Now works with ESM (was broken before)
import { ExpressReceiver } from '@slack/bolt';

This commit fixes ESM interoperability issues identified by @arethetypeswrong/cli
where named exports weren't accessible when importing from ESM.

Changes:
- Refactor receiver exports to use import-then-export pattern instead of
  `export { default as X }` syntax
- Separate type-only exports using `export type` for better tree-shaking
- Fix Logger and OAuth type re-exports to be type-only

The previous pattern `export { default as ExpressReceiver } from './receivers/ExpressReceiver'`
generated code with `__importDefault().default` which isn't statically analyzable
by Node.js's ESM-CJS interop layer.

The new pattern imports first then re-exports:
```typescript
import ExpressReceiver from './receivers/ExpressReceiver';
export { ExpressReceiver };
export type { ExpressReceiverOptions } from './receivers/ExpressReceiver';
```

This generates simpler CommonJS code that Node.js can properly expose as named
exports when imported from ESM.

Verified with @arethetypeswrong/cli - NamedExports errors are now resolved.

Relates to #2565, #2632, #2644
@grantjoy grantjoy requested a review from a team as a code owner November 23, 2025 20:25
@salesforce-cla
Copy link

Thanks for the contribution! Before we can merge this, we need @grantjoy to sign the Salesforce Inc. Contributor License Agreement.

@mwbrooks mwbrooks added enhancement M-T: A feature request for new functionality semver:patch labels Nov 24, 2025
@mwbrooks mwbrooks added this to the 4.7.0 milestone Nov 24, 2025
@mwbrooks
Copy link
Member

Hi @grantjoy 👋🏻 Thanks for taking the time to craft an in-depth and quality pull request! 👌🏻

The PR looks safe to run our GitHub Workflows, so I've approved it. It looks like there are some minor failures due to biomjs formatting:

> @slack/[email protected] lint
> npx @biomejs/biome check docs src test examples

src/index.ts format ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  × Formatter would have printed the following content:
  
    94 94 │   export * as webApi from '@slack/web-api';
Checked 121 files in 202ms. No fixes applied.
Found 1 error.
    95 95 │   
    96    │ - 

❓ When you have a moment, would you mind pushing a commit to fix the formatting error? It looks like the newline on L95 should be removed.

🔍 I'll ping a few teammates to look over your pull request, but on first review it's look good to move forward! 👍🏻

@codecov
Copy link

codecov bot commented Nov 28, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.44%. Comparing base (716a207) to head (6158b4c).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2724   +/-   ##
=======================================
  Coverage   93.44%   93.44%           
=======================================
  Files          37       37           
  Lines        7675     7675           
  Branches      669      669           
=======================================
  Hits         7172     7172           
  Misses        498      498           
  Partials        5        5           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@zimeg

This comment was marked as duplicate.

Copy link
Member

@zimeg zimeg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grantjoy LGTM! Thanks for bringing this enhancement to exports - I agree this is a nice pattern to use 🙏 ✨

@WilliamBergamin WilliamBergamin merged commit 8b5a3db into slackapi:main Dec 10, 2025
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla:signed enhancement M-T: A feature request for new functionality semver:patch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Why Common JS and not ESM?

4 participants