Summary
A Meteor application fails to resolve the eta/core module during the build process, despite the eta package being correctly installed in node_modules. The error originates from the oidc-provider package, which attempts to import eta/core in its view handling code. This failure occurs because Meteor’s build tool does not automatically include the CommonJS sub-path export eta/core, causing the build to abort with a “missing module” error. The core issue is a mismatch between Node.js module resolution behavior and Meteor’s static analysis/bundling strategy.
Root Cause
The root cause is Meteor’s static dependency analysis failing to recognize the sub-path export eta/core.
- Sub-path Exports: The
oidc-providerpackage uses a modern Node.js feature called “sub-path exports” (defined inpackage.jsonofeta). It importseta/coreexpecting Node to resolve this to an internal file likenode_modules/eta/core.js. - Missing Dependency Declaration:
oidc-providerlistsetaas a peer dependency. It assumes the consuming application (your Meteor app) will installeta. However,oidc-providerdoes not explicitly list the internal fileeta/corein its own static dependency list for Meteor to scan. - Meteor Build Failure: Meteor reads
oidc-provider/lib/views/index.js, seesrequire("eta/core"), but cannot find a static reference toetaoreta/coreinoidc-provider‘spackage.jsondependencies or a compatibleimportstatement. Consequently, it omitsetafrom the client-side or server-side bundle, resulting in the “Unable to resolve some modules” error.
Why This Happens in Real Systems
This scenario is common when integrating legacy CommonJS packages with modern ESM features.
- Hybrid Node.js Modules: Many libraries like
oidc-providerare evolving to support both CommonJS and ESM, often utilizing package exports maps. When an older CommonJSrequiretries to access an alias defined inexports(likeeta/core), build tools that rely on static analysis (like Meteor) can get confused if thepackage.jsonstructure isn’t perfectly traversed. - Peer Dependency Ambiguity:
oidc-providertreatsetaas a “peer dependency” because it supports multiple view engines. It doesn’t forceetato be installed, expecting the user to do so. However, Meteor relies on explicit imports to bundle dependencies. If the link between therequirestatement and thepackage.jsonpeer dependency isn’t detected, the module is treated as “missing.” - Build Tool Heuristics: Meteor’s build linker parses code to determine what to include. It looks for
importstatements orrequirecalls where the string is a direct path to a package. It may skip deep imports or aliased exports likeeta/coreif it cannot find thepackage.jsonentry for that specific string, assuming it’s an external file not meant for bundling.
Real-World Impact
- Build/Deploy Breakage: The application cannot build or deploy. This is a blocking error that stops development pipelines immediately.
- Third-Party Library Lock-in: You are effectively blocked from using the specific version of
oidc-providerthat requires this structure until the build configuration is fixed. - Developer Productivity Loss: Developers spend significant time debugging
node_modulesinconsistencies, runningnpm install, and trying to force module resolution, rather than focusing on business logic.
Example or Code
If oidc-provider requires eta/core internally, the fix usually involves “shimming” the dependency or forcing Meteor to include it.
// Example of a shim file (e.g., imports/fix-eta.js) that can be imported early in the app
// This forces the module into the bundle.
import eta from 'eta';
export default eta;
However, the specific code causing the issue looks like this within oidc-provider:
// node_modules/oidc-provider/lib/views/index.js
// This line fails in Meteor because "eta/core" is not statically resolvable
const { compile, render } = require('eta/core');
How Senior Engineers Fix It
Senior engineers resolve this by bridging the gap between the build tool and the module system.
- Check
package.jsonExports: They inspectnode_modules/eta/package.jsonto see howeta/coreis mapped. If it points to./core.js, they ensure the file exists. - Explicit Import/Re-export: The most robust fix is to manually import the dependency in a startup file. By adding
import 'eta';(orimport { compile } from 'eta';) in a top-level application file (e.g.,server/main.js), Meteor is forced to parse and includeetain the bundle. - Runtime Compatibility Layer: If
oidc-provideris trying to loadeta/coreat runtime (dynamic require), the Senior Engineer might useMeteor.npmRequireor a dynamic import wrapper if supported by the specific Meteor version, but usually, static inclusion is safer. - Meteor Settings: They might configure
meteor-main-moduleor build plugins if the project is complex, but usually, adding the missingetaimport to the application code is the immediate fix.
Why Juniors Miss It
Junior engineers often lack the experience with bundler internals and Node.js ESM/CJS interop.
- Blind Re-installation: They often run
npm installrepeatedly (as suggested by the error) thinking the package is missing, when in reality, the package is there, but the bundler cannot see it. - Ignoring Peer Dependencies: They might not realize that
oidc-providerrequiresetato be explicitly installed by them (the app developer), not just the library. - Misinterpreting “Missing Module”: They interpret “Unable to resolve” as a file not found error on disk, rather than a bundling/scope resolution error.
- Lack of Debugging Tools: They don’t know how to inspect the Meteor build process (e.g., looking at
.meteor/local/buildor usingmeteor shellto test requires) to verify ifetais actually bundled.