'detect whether ES Module is run from command line in Node
When using CommonJS modules in Node, you can detect whether a script is being run from the command line using require.main === module
.
What is an equivalent way to detect whether a script is being run from the command line when using ES Modules in Node (with the --experimental-modules
flag)?
Solution 1:[1]
Use
if (import.meta.url === `file://${process.argv[1]}`) {
// module was not imported but called directly
}
See the MDN docs on import.meta
for details.
Update Sep 27, 2021
Perhaps more robust, but involving an extra import (via Rich Harris)
import url from 'url'
if (import.meta.url === url.pathToFileURL(process.argv[1]).href) {
// module was not imported but called directly
}
Solution 2:[2]
There is none - yet (it's still experimental!). Although the prevailing opinion is that such a check is a bad practice anyway and you should just provide separate scripts for the library and the executable, there is an idea to provide a boolean import.meta.main
property for this purpose.
Solution 3:[3]
The module
global variable will be defined in CommonJS, but won’t exist at
all in an ES module. Yes, there is an inconsistency there, that ES modules are
the things that don’t have module
variables.
You can check for an undefined variable by seeing if typeof v
is the string
(not value!) 'undefined'
.
That turns into:
const inCommonJs = typeof module !== 'undefined';
console.log(`inCommonJs = ${inCommonJs}`);
If we put that exact code into both .cjs
and .mjs
files, we get the correct answers:
$ node foo.mjs
inCommonJs = false
$ cp foo.mjs foo.cjs
$ node foo.cjs
inCommonJs = true
Solution 4:[4]
The other answers get close, but will miss the mark for a pretty typical usecase - cli scripts exposed by the bin
property in package.json
files.
These scripts will be symlinked in the node_modules/.bin
folder. These can be invoked through npx
or as scripts defined in the scripts
-object in package.json
. process.argv[1]
will in that case be the symlink and not the actual file referenced by import.meta.url
Furthermore, we need to convert the file path to an actual file://
-url otherwise it will not work correctly on different platforms.
import { realpathSync } from "fs";
import { pathToFileURL } from "url";
function wasCalledAsScript() {
// We use realpathSync to resolve symlinks, as cli scripts will often
// be executed from symlinks in the `node_modules/.bin`-folder
const realPath = realpathSync(process.argv[1]);
// Convert the file-path to a file-url before comparing it
const realPathAsUrl = pathToFileURL(realPath).href;
return import.meta.url === realPathAsUrl;
}
if (wasCalledAsScript()) {
// module was executed and imported by another file.
}
I would have posted this as a comment on the accepted answer, but apparently I'm not allowed to comment with a fresh account.
Solution 5:[5]
I like import.meta.url === `file://${process.argv[1]}`
, but it does not work in Windows inside bash shell. This is the alternative that is only checking the basename:
const runningAsScript = import.meta.url.endsWith(path.basename(process.argv[1]));
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Bergi |
Solution 3 | andrewdotn |
Solution 4 | gustavnikolaj |
Solution 5 | Slawa |