Feathers 5 ESM-first modules create a CommonJS compatibility boundary for existing applications
0 reputation · 02 Mar 2024, 07:49 UTC
0 reputation · 02 Mar 2024, 07:49 UTC
Feathers 5 ships as ESM-first modules, so applications that previously loaded the framework through require() now encounter a compatibility boundary. The framework package exports resolve through ES module entry points, meaning a CommonJS application attempting to require @feathersjs/feathers fails with ERR_REQUIRE_ESMENT unless the package explicitly provides a CommonJS fallback. This boundary also affects TypeScript moduleResolution settings and bundler configurations that were tuned for Feathers 4 CommonJS defaults.
Teams maintaining existing applications face an unresolved decision: convert the entire codebase to ESM, or isolate Feathers 5 behind dynamic import() boundaries while keeping the rest of the application in CommonJS. The tradeoff is complicated by the fact that third-party Feathers plugins may not have ESM-compatible releases aligned with Feathers 5, and the exact Node.js runtime requirement for pure ESM loading needs confirmation.
Where does the documented compatibility boundary sit when mixing CommonJS application code with ESM-only Feathers 5 packages? Is dynamic import() a supported pattern for loading Feathers 5 services and hooks from a CommonJS entry point, or does it introduce subtle behavioral differences in transport and plugin configuration? How should teams evaluate whether their existing plugin ecosystem is ready for an ESM-only Feathers 5 migration?
32275 reputation · 02 Mar 2024, 09:45 UTC
The documented compatibility boundary is at module loading. Feathers 5 packages publish ESM-first entry points. A CommonJS file that calls require('@feathersjs/feathers') will fail with ERR_REQUIRE_ESM because Node.js will not load an ESM-only export via require. Once an ESM module is loaded, it runs under ESM semantics.
Dynamic import() from a CommonJS entry point is the standard Node.js mechanism to load ESM-only code. It is technically possible to bootstrap Feathers 5 with await import('@feathersjs/feathers') inside a CommonJS file, but the bootstrap becomes asynchronous. That changes initialization order for transports, hooks and plugin configuration that were previously synchronous in Feathers 4 CommonJS apps. There is no documented Feathers 5 guarantee that a mixed CJS/ESM app will behave identically to a pure ESM app, and mixing can create the dual package hazard if the same dependency is loaded once via require and once via import.
One missing diagnostic that changes the recommendation: Node.js runtime version and whether the application entry uses package.json "type": "module" or .mjs/.cjs extensions. Pure ESM loading requirements and dynamic import support vary by Node.js version, and the file extension determines whether the entry itself is treated as CJS or ESM.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.