Skip to content

[BUG]: npm package cannot be imported from ESM or CJS, the documented README snippet throws #234

Description

@AmrendraTheCoder

Bug Description

I was working through the README to try the library in a small React app, and I got stuck on the very first line of the Advanced Usage > Using npm Package section. That snippet is:

import SocialShareButton from "@aossie-org/social-share-button";

Running exactly that against the published @aossie-org/social-share-button@1.0.4 throws:

SyntaxError: The requested module '@aossie-org/social-share-button' does not provide an export named 'default'

I assumed I had made a mistake, so I checked the other entry points too. As far as I can tell the published package cannot be consumed from either module system, and only the CDN script tag path works. I wanted to write up what I measured rather than guess, so here is the whole picture.

What I measured against the published tarball (not a local build):

Entry point Result
import S from '@aossie-org/social-share-button' SyntaxError, no default export
import * as ns from '...' resolves, but the namespace is empty ([])
require('@aossie-org/social-share-button') returns an empty module namespace, no class
<script src="...jsdelivr..."> works, sets window.SocialShareButton

Why I think it happens

package.json sets "type": "module", so every .js file in the package is an ES module. The only export machinery in src/social-share-button.js is at the bottom of the file:

if (typeof module !== "undefined" && module.exports) {
  module.exports = SocialShareButton;
}

if (typeof window !== "undefined") {
  window.SocialShareButton = SocialShareButton;
}

In an ES module module is not defined, so that first block never runs. There is no export statement anywhere in the file, which is why the module genuinely has zero exports rather than just a missing default. The window assignment still works, which is why the CDN path is fine and the npm path is not.

Two smaller things I noticed while looking:

  1. The files array lists the React wrapper but not src/social-share-button-preact.jsx or src/social-share-button-qwik.tsx, so those two are not in the published tarball. I confirmed this with tar -tzf on both my own npm pack output and the tarball npm actually serves: 7 files, neither wrapper present.
  2. There is no exports map, no module field, and no types field.

Steps to Reproduce

mkdir repro && cd repro
printf '{"name":"repro","version":"1.0.0","type":"module","private":true}\n' > package.json
npm install @aossie-org/social-share-button@1.0.4

# ESM, the exact snippet from the README
printf "import S from '@aossie-org/social-share-button';\nconsole.log(S);\n" > esm.mjs
node esm.mjs

# CJS
printf "const m = require('@aossie-org/social-share-button');\nconsole.log(typeof m, Object.keys(m));\n" > cjs.cjs
node cjs.cjs

# what actually shipped
tar -tzf "$(npm pack @aossie-org/social-share-button@1.0.4 2>/dev/null | tail -1)"

Logs and Screenshots

### STATIC import, published 1.0.4 ###
SyntaxError: The requested module '@aossie-org/social-share-button' does not provide an export named 'default'

### namespace import ###
namespace keys: []

### CJS require ###
typeof: object keys: [] value: [Module: null prototype] {  }

### files shipped ###
social-share-analytics.js
social-share-button-react.jsx
social-share-button.css
social-share-button.js

Environment Details

  • OS: macOS (Darwin 25.6.0)
  • Node.js: v22.21.0
  • npm package: @aossie-org/social-share-button@1.0.4 installed from the npm registry
  • No bundler involved, plain Node, so nothing in my toolchain is papering over or causing the failure

One caveat on the require row: Node 22.21 supports require() of an ES module, which is why it returns an empty namespace instead of erroring. I did not test on a Node version without that support, so I would not want to claim what happens there.

Impact

High - Major feature is broken

The CDN and script tag path is unaffected, so this is not a total outage. It is the npm install path and the README snippet documenting it that do not work.

Proposed fix

I put together a patched build locally and verified it, mostly to be sure I was not proposing something that would not work. Three parts:

  1. Add real ESM exports to src/social-share-button.js:
    export default SocialShareButton;
    export { SocialShareButton };
  2. Add an exports map with import and require conditions, plus named subpaths for the CSS and the framework wrappers.
  3. Add the Preact and Qwik wrappers to files so they actually ship.

With those applied, all three entry points return the real class:

### ESM static default import ###
typeof: function | name: SocialShareButton
### ESM named import ###
typeof: function | name: SocialShareButton
### CJS require ###
typeof default: function | name: SocialShareButton

A few things I would rather ask than decide

These felt like calls that belong to the maintainers, so I did not want to assume:

  1. How should CJS be served? The repo has no build step today. I got require() working with a two line .cjs file that re-exports the ESM one, which keeps things build free. The alternative is a real bundler step producing a proper CJS build. The first is much smaller, the second is more conventional. Do you have a preference, or would you rather the package go ESM only and just document that clearly? Going ESM only would also mean deleting the dead module.exports block, which might be the most honest option.
  2. Should the old deep paths keep working? Adding an exports map is technically a breaking change, because it stops consumers from reaching into src/... directly. The README currently documents import "@aossie-org/social-share-button/src/social-share-button.css". I can keep that path working alongside a nicer /css subpath so nothing breaks, or make the clean break. Happy either way, just did not want to silently break someone.
  3. Is the Qwik wrapper meant to ship as .tsx source? Node cannot load .jsx or .tsx directly, so those subpaths only work for consumers with a bundler. That is completely normal for source shipped components, but it does mean the wrappers are bundler only, and I did not know whether that was the intent.

Relationship to #233

This overlaps with #233 (adding TypeScript definitions) but does not duplicate it, and I am explicitly not asking for that issue. It is @Mansi2007275's and they said in the body that they intend to write the .d.ts files themselves.

The connection is just that the two need each other to be useful. Type definitions are surfaced to consumers through a types field or a types condition in the exports map, and this package currently has neither. So once the .d.ts files from #233 land, they still will not be picked up until the packaging side is sorted out. If it helps, I would be glad to make sure whatever exports map comes out of this leaves a clean types slot for #233 to drop into, so the two pieces meet without either of us having to redo work. Entirely happy to sequence it whichever way suits, including waiting for #233 to land first.

Offer

If this is something you would like fixed, I would be happy to send a PR. I have the change working locally already. I would just want a steer on question 1 and 2 above first, since the answers change the shape of the diff quite a bit. And if you would rather someone else pick this up, or if I have misread the intent and the package is meant to be CDN only, please do say, no problem at all.

AI disclosure

I used Claude to help investigate this and to draft this write up. The reproduction, the measurements in the table, the tarball contents and the patched build were all run and verified by me locally against the published package.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdocumentationChanges to documentation filesenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions