node icon indicating copy to clipboard operation
node copied to clipboard

Ability to suppress warnings by type (or just experimental warnings)

Open coreyfarrell opened this issue 5 years ago β€’ 14 comments

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.

coreyfarrell avatar Dec 05 '19 18:12 coreyfarrell

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 avatar Mar 06 '20 21:03 JoshMcCullough

@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.

coreyfarrell avatar Mar 07 '20 21:03 coreyfarrell

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.

JoshMcCullough avatar Mar 09 '20 16:03 JoshMcCullough

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.

coreyfarrell avatar Mar 09 '20 16:03 coreyfarrell

this would have been cool, especially for those of us using experimental loader (which I presume will be experimental for quite some time?)

noahehall avatar Feb 12 '22 04:02 noahehall

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 avatar Apr 15 '22 07:04 ChuanyuanLiu

@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

kachkaev avatar Apr 24 '22 13:04 kachkaev

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 avatar Apr 24 '22 15:04 JakobJingleheimer

@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).

kachkaev avatar Apr 24 '22 16:04 kachkaev

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).

JakobJingleheimer avatar May 03 '22 20:05 JakobJingleheimer

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)
}

aral avatar May 26 '22 17:05 aral

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

renhiyama avatar Jul 18 '22 06:07 renhiyama

(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!)

renhiyama avatar Jul 18 '22 06:07 renhiyama

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']).

Xunnamius avatar Sep 20 '22 21:09 Xunnamius

I wrote a package that seems to do it for me atm: suppress-experimental-warnings. CI-tested with many different node versions + operating systems.

dword-design avatar Dec 03 '22 17:12 dword-design

Vicious circle here:

  1. 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
  2. if users never dare throw real-life usages at those features, then those features will never be sufficiently "proven" by real life usage
  3. 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.

rulatir avatar Dec 18 '22 22:12 rulatir

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.

rulatir avatar Dec 18 '22 22:12 rulatir

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...

replete avatar Dec 31 '22 16:12 replete

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);
};

karlhorky avatar Jan 15 '23 15:01 karlhorky

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.

o-az avatar Feb 17 '23 01:02 o-az

    error.message.includes('The Fetch API is an experimental feature.')

Only works in English locale.

rulatir avatar Feb 25 '23 12:02 rulatir

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.

bnoordhuis avatar Feb 27 '23 10:02 bnoordhuis

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 avatar Feb 27 '23 11:02 karlhorky

@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.

bnoordhuis avatar Feb 27 '23 11:02 bnoordhuis

Ok, opened an issue here:

  • https://github.com/nodejs/node/issues/47478

karlhorky avatar Apr 08 '23 10:04 karlhorky

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);

Cojad avatar Jul 04 '23 13:07 Cojad

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.

jeffs avatar Jul 04 '23 15:07 jeffs

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

liudonghua123 avatar Jan 16 '24 05:01 liudonghua123

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 avatar Jan 16 '24 12:01 egasimus

@egasimus Yes, I also find this option is necessary when running on linux.

see also https://github.com/liudonghua123/node-sea/commit/04fa6ec5e4336ea1e2e2adc8884849d35c63e6f5.

liudonghua123 avatar Jan 16 '24 13:01 liudonghua123