node
node copied to clipboard
Ability to suppress warnings by type (or just experimental warnings)
Is your feature request related to a problem? Please describe. I'd like to suppress experimental warnings while still seeing any other errors. In particular when I am using native ES modules I do not want the experimental warning printed for every process, but I do want unrelated warnings to still be printed.
Describe the solution you'd like
Allow --no-warnings to optionally accept an option string such as --no-warnings=type1,type2. Using --no-warnings without any option would continue to disable all warnings. This would allow --no-warnings=ExperimentalWarning to suppress ExperimentalWarning only.
Describe alternatives you've considered
--no-experimental-warnings or a similarly named new flag could be created. This has the drawback that node --no-experimental-warnings on node.js 13.3.0 exit with an error where --no-warnings=ExperimentalWarnings will not currently error (it causes all warnings to be ignored).
In my own repo which uses ES modules I've created suppress-experimental.cjs which gets loaded with NODE_OPTIONS='--require=./suppress-experimental.cjs':
'use strict';
const {emitWarning} = process;
process.emitWarning = (warning, ...args) => {
if (args[0] === 'ExperimentalWarning') {
return;
}
if (args[0] && typeof args[0] === 'object' && args[0].type === 'ExperimentalWarning') {
return;
}
return emitWarning(warning, ...args);
};
Obviously patching node.js internals like this is undesirable but it accomplishes my goal.
args[0] would point to a string, since the arguments to emitWarning are warning: string | Error, name?: string, ctor?: Function. Are you sure this code is working correctly?
@JoshMcCullough Yes the code works, note that the parameters for my replacement function are (warning, ...args). I can see how that looks odd but it definitely works.
I was suggesting that your args[0] will always be a string since it maps to the 2nd parameter of emitWarning, which is (if the types are correct), name?: string. So the second if block would never be entered -- unless I'm totally missing something here.
https://nodejs.org/dist/latest/docs/api/process.html#process_process_emitwarning_warning_options shows that process.emitWarning can also take an options object as the second argument. I don't think the ESM warning uses that style call but I still check for it as I want my code to continue working if the ESM warning switches to the options object.
this would have been cool, especially for those of us using experimental loader (which I presume will be experimental for quite some time?)
The proposed hack with suppress-experimental.js did not work for me.
It works when I trigger process.emitWarning manually but it appears that NodeJS's call on Experimental Warning did not go through the redefined process.emitWarning.
My cmd arguments are node --experimental-fetch --require=./suppress-experimental.js index.js
@ChuanyuanLiu which version of Node are you using? Overriding process.emitWarning as suggested above worked for me on Node 16, but when I tried Node 18, it did not. It indeed feels like (node:43369) ExperimentalWarning: ... comes from some internals of Node, so cannot be silenced.
UPD I found a patch that works both in Node 16 and 18 π https://github.com/yarnpkg/berry/blob/2cf0a8fe3e4d4bd7d4d344245d24a85a45d4c5c9/packages/yarnpkg-pnp/sources/loader/applyPatch.ts#L414-L435
A recent change (#42314) converted some of ESMLoader's experimental warning emissions from using process.emitWarning() directly to an internal utility (emitExperimentalWarning()) that uses it under the hood. Since it still uses process.emitWarning() under the hood, if overwriting process.emitWarning() ever worked, that should continue to work.
@JakobJingleheimer looks like in Node 18 uses process.emit instead of process.emitWarning. Judging by a recent change in Yarn Berry: https://github.com/yarnpkg/berry/pull/4338 (diff).
Could you cite a problematic place where that happens? A quick search of the node codebase suggests it's used only sparingly and in very specific scenarios. process.emitWarning does call process.emit():
emitWarning β doEmitWarning β emit
https://github.com/nodejs/node/blob/c3aa86d6784af77121e5e98e75c6dc12e5bb39ec/lib/internal/process/warning.js#L155-L160
but it has done so for ~2 years (according to git history).
I'm not aware of a policy change and I see no code docs recommending such a switch (and if it did happen, it was apparently incomplete). It's quite possible one of the ~100 active maintainers used something different recently, either because it was better suited or because they didn't know process.emitWarning() exists.
P.S. Regarding the original topic of this issue, I don't foresee suppressing certain warnings being supported (and it would take a significant amount of work to facilitate as there is currently no authoritative list of feature names, only strings that happen to be consistent because humans care to check).
UPD I found a patch that works both in Node 16 and 18 tada https://github.com/yarnpkg/berry/blob/2cf0a8fe3e4d4bd7d4d344245d24a85a45d4c5c9/packages/yarnpkg-pnp/sources/loader/applyPatch.ts#L414-L435
And hereβs a plain old JavaScript version of it that also silences a few other experimental warnings (JSON modules, and Node.js specifier resolution):
const originalEmit = process.emit;
process.emit = function (name, data, ...args) {
if (
name === 'warning' &&
typeof data === 'object' &&
data.name === 'ExperimentalWarning' &&
(data.message.includes('--experimental-loader') ||
data.message.includes('Custom ESM Loaders is an experimental feature') ||
data.message.includes('The Node.js specifier resolution flag is experimental') ||
data.message.includes('Importing JSON modules is an experimental feature'))
)
return false
return originalEmit.apply(process, arguments)
}
I happened to use a smaller version since your code isnt compatible if I use env instead of passing through nodejs' args
const originalEmit = process.emit;
process.emit = function (name, data, ...args) {
if (
name === `warning` &&
typeof data === `object` &&
data.name === `ExperimentalWarning`
)
return false;
return originalEmit.apply(process, arguments);
};
Heres the bash script that runs mine:
NODE_OPTIONS=--experimental-vm-modules node test
(I bet you can add extra conditions to check env, but hey, your code would be not as performant as before then and will be 0.001% slower than before!)
The previous solutions didn't work for me in all cases, though process is an EventEmitter so the following coarse-grain one-liner worked for me:
process.removeAllListeners('warning');
Alternatively, a safer more fine-grained approach to only disable a specific type of warning (ExperimentalWarning in this case):
const warningListeners = process.listeners('warning');
if (warningListeners.length != 1) {
console.warn(
`expected 1 listener on the process "warning" event, saw ${warningListeners.length}`
);
}
if (warningListeners[0]) {
const originalWarningListener = warningListeners[0];
process.removeAllListeners('warning');
process.prependListener('warning', (warning) => {
if (warning.name != 'ExperimentalWarning') {
originalWarningListener(warning);
}
});
}
UPDATE: I went ahead and published suppress-node-warnings, which lets you safely disable only certain warnings like so: suppressWarnings(['ExperimentalWarning', 'DeprecationWarning']).
I wrote a package that seems to do it for me atm: suppress-experimental-warnings. CI-tested with many different node versions + operating systems.
Vicious circle here:
- if experimental warnings are impossible to be silenced as long as the features remain experimental, users will never dare throw real-life usages at those features
- if users never dare throw real-life usages at those features, then those features will never be sufficiently "proven" by real life usage
- if those features will never be sufficiently "proven" by real life usage, they will remain experimental forever
Something must yield in order to unstick this deadlock, and this feature request expresses the position that it is the warnings that should yield.
I wrote a package that seems to do it for me atm: suppress-experimental-warnings. CI-tested with many different node versions + operating systems.
With localizations too?
EDIT: just read your code above and indeed it doesn't seem to rely on output parsing. I'll give it a try.
I wrote a package that seems to do it for me atm: suppress-experimental-warnings. CI-tested with many different node versions + operating systems.
This seems to work pretty well FYI...
To suppress the experimental fetch warnings like ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time, this is what we used:
package.json
{
"scripts": {
"start": "node --require ./suppress-experimental-fetch-warnings.js index.js"
}
}
suppress-experimental-fetch-warnings.js
// Suppress Node.js warning about experimental fetch API
// Ref: https://github.com/nodejs/node/issues/30810#issuecomment-1383184769
const originalEmit = process.emit;
process.emit = function (event, error) {
if (
event === 'warning' &&
error.name === 'ExperimentalWarning' &&
error.message.includes('The Fetch API is an experimental feature.')
) {
return false;
}
return originalEmit.apply(process, arguments);
};
You can also use NODE_NO_WARNINGS=1 to suppress all warnings. E.g.,
NODE_NO_WARNINGS=1 node <...>
Alternatively, adding to @karlhorky, to suppress all experimental warnings (not just fetch):
// suppress-node-warnings.cjs
// inspired by
// https://github.com/nodejs/node/issues/30810#issuecomment-1383184769
const { emit: originalEmit } = process;
function suppresser(event, error) {
return event === 'warning' && error.name === 'ExperimentalWarning'
? false
: originalEmit.apply(process, arguments);
}
process.emit = suppresser;
node --require ./suppress-node-warnings.cjs
I'm using "type": "module" in package.json so I needed to use .cjs.
I'm also using ts-node so to run TypeScript files, I have the following script:
"scripts": {
"run-ts": "node --loader ts-node/esm --require ./scripts/suppress-node-warnings.cjs"
}
now I can run ts file with npm run run-ts filename.ts.
error.message.includes('The Fetch API is an experimental feature.')
Only works in English locale.
Node doesn't localize error messages, they're always in English.
I'm going to close this issue because OP's requested feature exists. You can (for example) disable experimental warnings like so:
$ node --no-warnings=ExperimentalWarning app.js
I'll open a new issue to discuss documentation because this feature seems to be completely undocumented right now.
Thanks @bnoordhuis, didn't know about this --no-warnings flag! π€ What versions is this available in?
I guess this does not allow for disabling warnings of only a specific topic? Eg. disabling only experimental fetch warnings - but no other experimental warnings - like in my comment above.
Maybe there should be a followup issue created for disabling warnings by their specific topic? I guess that's what the PR https://github.com/nodejs/node/pull/36137 was meant to address.
@karlhorky not 100% sure but it's been around for a while so probably all supported release lines.
You're welcome to open a new issue but the previous attempt to implement that functionality stalled out. You'll probably have to drive it yourself if you want to see it happen.
Ok, opened an issue here:
- https://github.com/nodejs/node/issues/47478
I want a solution which is simple yet easy to port to any project using fetch. So I shorten it into just 2 lines of code in my entry script.
// Using only 2 line at begining of your entry script(ex: index.js).
// Or any place before using fetch to suppress warnings for ExperimentalWarning(just once before calling fetch).
// inspired by
// https://github.com/nodejs/node/issues/30810#issuecomment-1433950987
const { emit: originalEmit } = process;
process.emit = (event, error) => event === 'warning' && error.name === 'ExperimentalWarning' ? false : originalEmit.apply(process, arguments);
const { emit: originalEmit } = process; process.emit = (event, error) => event === 'warning' && error.name === 'ExperimentalWarning' ? false : originalEmit.apply(process, arguments);
That arguments object isn't from your wrapper function; it's a free variable. The arguments object isn't defined by fat arrow functions. You're accidentally passing originalEmit an unrelated arguments object that Node apparently puts in the prelude for scripts (though not for the REPL), and dropping your actual parameters. Consider using a rest parameter instead. See also MDN.
Maybe you could share a GitHub repo or NPM package complete with unit and integration tests. It would be nice if your solution also type-checked properly. I'm sure folks would be happy to review your code.
I add a shebang like this to make the bin script working without showing the warnings. And it works for windows too.
#!/usr/bin/env node --no-warnings=ExperimentalWarning
I add a shebang like this to make the bin script working without showing the warnings. And it works for windows too.
#!/usr/bin/env node --no-warnings=ExperimentalWarning
On my Linux box, this needs to be:
#!/usr/bin/env -S node --no-warnings=ExperimentalWarning
The -S flag is needed to split the command line arguments. Without it, the script just hangs, doesn't even start. Does macOS env support the -S flag?
I saw somebody do an executable Dockerfile with env -S recently - but does it work out of the box on all platforms today?
The other thing you gotta keep in mind when starting a script with a shebang is that it becomes unimportable in <script type="module">. Which is mostly fine for a CLI entrypoint unless you want to write an entire isomorphic app in 1 file, in which case, no dice :sweat_smile:
@egasimus Yes, I also find this option is necessary when running on linux.
see also https://github.com/liudonghua123/node-sea/commit/04fa6ec5e4336ea1e2e2adc8884849d35c63e6f5.