Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues with default export and node.js resolution algorithm when using ESM #719

Open
blutorange opened this issue Feb 4, 2025 · 1 comment

Comments

@blutorange
Copy link

blutorange commented Feb 4, 2025

See also #505.

simplebar uses default exports, which are notorious for causing issues with regards to common JS and ESM interoperability with various tools and environments, see e.g. The default export can be error-prone
for an explanation.

To illustrate, see this sample project: simplebar-modules.zip

It's an ESM module (type: "module" in the package.json). It contains a single index.ts file that imports from simplebar, logs the imports, and tries to create a new simplebar instance.

import SimpleBar from "simplebar";

console.log(SimpleBar);

function init(): void {
  const simpleBar = new SimpleBar(document.createElement("div"));
}

First run npm install, then

# Run directly via node. Node picks up the common js build, which is an object with the property 'default'
# { default: function SimpleBar() ... }
node --experimental-strip-types index.ts

# esbuild picks up the ESM version
npm run esbuild
# function SimpleBar() { ... }
node dist/esbuild.js

# TypeScript considers simplebar as CJS and produces an error, since it thinks SimpleBar is not a function
# (but an object with a default property)
#
# Note that this behavior is related to the verbatimModuleSyntax option, see
# https://www.typescriptlang.org/tsconfig/#verbatimModuleSyntax
# (which effectively requires modue: "node16" or "nodenext")
#
# index.ts:6:25 - error TS2351: This expression is not constructable.
#   Type 'typeof import("/home/madgaksha/tmp/simplear-modules/node_modules/simplebar/dist/index")' has no construct signatures.
# 
# 6   const simpleBar = new SimpleBar(document.createElement("div"));
npm run typescript

# I'm not using it, but for reference, e.g. rollup seems to treat it as ESM
npx rollup --config rollup.config.js
# function SimpleBar() { ... }
node dist/ts/rollup.js

Solutions

As mentioned, the easiest (for you) would be to either drop the common JS build or to not use default exports.

Another solution I just noticed, that fixes all of the above cases, is to add the new exports field to the package.json that tells node.js which entry point is CJS and which is ESM:

  "exports": {".": {"import": "./dist/index.mjs", "require": "./dist/index.cjs"}},

Try adding the above line to node_modules/simplebar/package.json in the sample project. Then

# No default property anymore
# function SimpleBar() { ... }
node --experimental-strip-types index.ts

# No compile error anymore
npm run typescript

Note: The exports fields must be exhaustive, i.e. you must list everything that people are allowed to import (include e.g. CSS). Otherwise people will get error messages when they try to import something not listed explicitly in exports.

@Grsmto
Copy link
Owner

Grsmto commented Feb 4, 2025

Hey @blutorange thank you very much for opening the new issue and for taking the time to provide all those details.

You're right using the exports property seems like a good solution since most modern tools support it and it shouldn't affect legacy tools behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants