Is it possible to work around two dependencies calling `mime.define`?
With a dependency tree (showing mime only) like this:
├─┬ [email protected]
│ └─┬ [email protected]
│ └── [email protected]
├─┬ [email protected]
│ └─┬ [email protected]
│ └── [email protected] deduped
├─┬ [email protected]
│ └─┬ [email protected]
│ └── [email protected]
├─┬ [email protected]
│ └─┬ [email protected]
│ └── [email protected]
└─┬ [email protected]
└─┬ [email protected]
└── [email protected] deduped
I am running into the following problem: as soon as I require supertest as well as africastalking, an error is thrown:
Welcome to Node.js v16.3.0.
Type ".help" for more information.
> require('supertest')
[Function (anonymous)] {
Test: [Function: Test],
agent: [Function: TestAgent]
}
> require('africastalking')
Uncaught:
Error: Attempt to change mapping for "form" extension from "application/x-www-form-urlencoded" to "application/x-www-form-urlencoded". Pass `force=true` to allow this, otherwise remove "form" from the list of extensions for "application/x-www-form-urlencoded".
at Mime.define (/home/frederik/projects/redacted/agave/node_modules/mime/Mime.js:56:15)
at Object.<anonymous> (/home/frederik/projects/redacted/agave/node_modules/unirest/index.js:26:6)
at Module._compile (node:internal/modules/cjs/loader:1109:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1138:10)
at Module.load (node:internal/modules/cjs/loader:989:32)
at Function.Module._load (node:internal/modules/cjs/loader:829:14)
at Module.require (node:internal/modules/cjs/loader:1013:19)
at require (node:internal/modules/cjs/helpers:93:18)
This seems to be due to the fact that unirest will call mime.define here: https://github.com/Kong/unirest-nodejs/blob/a06ba4e6c58fea028d377c170cb4cbf7cf3c6049/index.js#L23-L28 and superagent does the same here: https://github.com/visionmedia/superagent/blob/f99e62499d11d60e9e774942b0a8a98dae28caf7/src/node/index.js#L79-L84 creating a collision on mimes global state (as npm dedupes the package both require calls return the same "module instance").
Being honest I don't really have any idea how this could be solved in here, but maybe you have an idea or others can find this issue before they get sucked into the vortex that I spent a few hours in right now.
perhaps worth mentioning that this is npm version 6
perhaps worth mentioning that this is npm version 6
I think once modules are installed in a certain order (which I guess could happen in all versions of npm), npm isn't concerned with module resolution anymore (but Node), or is it?
probably true
Interesting. The root issue here is that the default Mime instance exposed by this module is shared across all callers, and complains when an attempt is made to redefine a type.
There are a couple possible workarounds:
- Both
superagentandunirestcould setforce = truewhen callingdefine(). If only one does it, there's still a chance the error could occur. This does create a last-one-wins race condition in terms of what the actual mapping ends up being. However both modules actually set the same mapping so it's not a real concern in this particular case. - Either module could instantiate their own
Mimeinstance, effectively breaking the dependency on the shared instance. This is straight-forward. It just requires mimic'ing the way the global instance is created, here. - Duck-patch the exposed
mimeinstance to swallow the error, as below. Not a great solution, but it'll fix the immediate problem.
// Do this before requiring `supertest` or `unirest`
const mime = require('mime');
const _define = mime.define;
mime.define = function(...args) {
try {
_define.apply(mime, args);
} catch (err) {}
};
In terms of fixes that could be implemented here, one stop-gap solution that would address this case is to not throw the error if the define() call doesn't actually change the mappings. (Both superagent and unirest are setting the same mapping, so there isn't a real conflict to be concerned about here.) This is more probably more trouble than it's worth, though, since ...
The Right Solution™ would be to make the shared instance read-only. But that will be a breaking change that requires a major version bump. The problem with this is that I'd really like the next version to (somehow) bring the mime and mime-types modules back together so we can deprecate one or the other.
tl;dr: It's a bug, but I'm not strongly motivated to fix it at this time.

Closing. mime@4 (just published beta) fixes this problem by making the default mime instances read-only. Modules such as unitest or africaistalking that need custom mappings must create their own Mime instance.