Skip to content

Add browser shim for node:events module#9141

Open
alxndrsn wants to merge 5 commits intoapache:masterfrom
alxndrsn:add-explicit-events-dep
Open

Add browser shim for node:events module#9141
alxndrsn wants to merge 5 commits intoapache:masterfrom
alxndrsn:add-explicit-events-dep

Conversation

@alxndrsn
Copy link
Copy Markdown
Contributor

@alxndrsn alxndrsn commented Nov 20, 2025

TODO

  • add a test which fails against existing codebase similar to how linked issues fail, and passes with the updates in this PR
  • add a test for node-js builds to show that import 'pouchdb-events' === import 'node:events'?

This seems to be a common issue:

I think it was introduced by 404346b, which upgrade from webpack 1 to webpack 5. From the webpack docs:

Webpack 5 no longer polyfills Node.js core modules automatically which means if you use them in your code running in browsers or alike, you will have to install compatible modules from npm and include them yourself.

Alternatively, it may come from rollup.

Hopefully:

Closes #8968
Closes #8985
Closes #8989
Closes #9072
Closes #9078

@alxndrsn
Copy link
Copy Markdown
Contributor Author

alxndrsn commented Nov 20, 2025

Less convinced now:

PR has been updated to handle events dependency like other browser-specific polyfills via new pouchdb-events package.

> e1 = require('node:events'); e2 = require('./node_modules/events');
<ref *1> [Function: EventEmitter] {
  once: [Function: once],
  EventEmitter: [Circular *1],
  defaultMaxListeners: [Getter/Setter],
  init: [Function (anonymous)],
  listenerCount: [Function (anonymous)]
}
> e1
<ref *1> [Function: EventEmitter] {
  addAbortListener: [Function: addAbortListener],
  once: [AsyncFunction: once],
  on: [Function: on],
  getEventListeners: [Function: getEventListeners],
  getMaxListeners: [Function: getMaxListeners],
  EventEmitter: [Circular *1],
  usingDomains: true,
  captureRejectionSymbol: Symbol(nodejs.rejection),
  captureRejections: [Getter/Setter],
  EventEmitterAsyncResource: [Getter],
  errorMonitor: Symbol(events.errorMonitor),
  defaultMaxListeners: [Getter/Setter],
  setMaxListeners: [Function (anonymous)],
  init: [Function (anonymous)],
  listenerCount: [Function (anonymous)]
}
> e2
<ref *1> [Function: EventEmitter] {
  once: [Function: once],
  EventEmitter: [Circular *1],
  defaultMaxListeners: [Getter/Setter],
  init: [Function (anonymous)],
  listenerCount: [Function (anonymous)]
}
> e1.EventEmitter === e2.EventEmitter
false

@alxndrsn alxndrsn marked this pull request as draft November 20, 2025 11:15
@alxndrsn alxndrsn marked this pull request as ready for review November 20, 2025 12:37
@alxndrsn alxndrsn changed the title Add explicit events dependency Add browser shim for NodeJS events module Jan 12, 2026
@alxndrsn alxndrsn changed the title Add browser shim for NodeJS events module Add browser shim for node:events module Jan 12, 2026
@espy
Copy link
Copy Markdown
Contributor

espy commented Apr 15, 2026

Hello! 👋

We’ve extensively looked at your PR and manually tested it, and it turned out that, surprisingly, events is still not included as a dependency in npm i pouchdb-browser. We eventually found that the events dependency is actively removed by PouchDB’s build process, because it has the same name as a node.js builtin (via builtin-modules), even if it is explicitly a dependency in package.json and not just a reference to the builtin.

You can test this via npm run test-webpack (which composes the individual modules’ dependency field in their package.jsons), and inspect the files in packages/node_modules/pouchdb-browser:

  • the lib/index* files do some form of import events from 'events';, so all imports of pouchdb-events in the various modules have been transformed into imports for events. However:
  • the generated package.json depends on neither events nor pouchdb-events, so the events package is still not present in the browser.

We came up with a very hardcoded fix, left for your consideration in this branch. This allows events to be added to the dependency field in package.json files, effectively that means it gets added to pouchdb-events and pouchdb-browser. After some additional testing, we found that node will prefer its builtin, even if events is a proper dependency, so this approach seems fine to us. To test this specific behaviour:

mkdir tmp
cd tmp
mkdir -p node_modules/events
# edit node_modules/events/index.js to print/return something
# edit index.js to contain `const e = require('events') ; console.log(e)`
node index.js

This is also true for index.mjs and import e from 'events'.

To test that the fix itself actually works and events is now installed with npm i pouchdb-browser (npm pack is the closest equivalent for local testing, npm i <FILEPATH> and npm link both don’t do anything useful here):

# in your pouchdb repo with our change:
$ npm run test-webpack
$ cd packages/node_modules/pouchdb-browser
$ npm pack
# copy resulting tarball to some other empty directory and cd there
$ npm init # and yes all the things
$ npm i < TARBALL_FILENAME >
# inspect the `node_modules` folder, `events` is present

Aside from the fix, the branch also contains another commit with a small cleanup change.

We hope this helps to land this fix in the next release! Thanks for your continued work on PouchDB 🙏

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