My goal is to get a cli I can execute every where.
Seem the better way is global install.
But when I try, it raise an error like if a required module was missing.
Can't resolve this even after an npm install -g <missing_module>
I am using nvm with several node version so I think it can be a root cause. But when I check in node_modules of the current node version used, I find missing module.
I do not understand.
I put the steps to reproduce here with a little exemple test2 project. Note than my issue come from another big project where I encounter issue on dotenv-safe/config too.
If I resolve for this given exemple I think I will be able to resolve for the others missing modules.
$ mkdir -p test2
$ cd test2
$ npm init -y
$ echo '[{"key": "1"}]' > jsonFile.json
$ touch index.js
$ chmod +x index.js
Add this in package.json :
"type": "module",
"bin": {
"test2-cli": "./index.js"
},
$ cat index.js
#!/usr/bin/env node
import jsonContent from './jsonFile.json' assert { type: "json"}
console.log(jsonContent)
$ ./index.js
[ { key: '1' } ]
(node:5362) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
Following João Pimentel advice, I add --experimental-modules
tag.
And I do not want the warning, so I add --require=suppress-experimental-warnings
option.
So I replace in index.js
#!/usr/bin/env node
by
#!/usr/bin/env -S node --experimental-modules --require=suppress-experimental-warnings
$ ./index.js
node:internal/modules/cjs/loader:1042
throw err;
^
Error: Cannot find module 'suppress-experimental-warnings'
Require stack:
- internal/preload
at Module._resolveFilename (node:internal/modules/cjs/loader:1039:15)
at Module._load (node:internal/modules/cjs/loader:885:27)
at Module.require (node:internal/modules/cjs/loader:1105:19)
at Module._preloadModules (node:internal/modules/cjs/loader:1395:12)
at loadPreloadModules (node:internal/process/pre_execution:621:5)
at setupUserModules (node:internal/process/pre_execution:125:3)
at prepareExecution (node:internal/process/pre_execution:116:5)
at prepareMainThreadExecution (node:internal/process/pre_execution:36:3)
at node:internal/main/run_main_module:10:1 {
code: 'MODULE_NOT_FOUND',
requireStack: [ 'internal/preload' ]
}
Node.js v18.13.0
$ npm install suppress-experimental-warnings
added 1 package in 536ms
1 package is looking for funding
run `npm fund` for details
$ ./index.js
[ { key: '1' } ]
Ok, now I want to access to this cli every where in my terminal.
$ npm install -g .
added 2 packages in 1s
1 package is looking for funding
run `npm fund` for details
$ cd ../
$ test2-cli
node:internal/modules/cjs/loader:1042
throw err;
^
Error: Cannot find module 'suppress-experimental-warnings'
Require stack:
- internal/preload
at Module._resolveFilename (node:internal/modules/cjs/loader:1039:15)
at Module._load (node:internal/modules/cjs/loader:885:27)
at Module.require (node:internal/modules/cjs/loader:1105:19)
at Module._preloadModules (node:internal/modules/cjs/loader:1395:12)
at loadPreloadModules (node:internal/process/pre_execution:621:5)
at setupUserModules (node:internal/process/pre_execution:125:3)
at prepareExecution (node:internal/process/pre_execution:116:5)
at prepareMainThreadExecution (node:internal/process/pre_execution:36:3)
at node:internal/main/run_main_module:10:1 {
code: 'MODULE_NOT_FOUND',
requireStack: [ 'internal/preload' ]
}
Node.js v18.13.0
$ npm install -g suppress-experimental-warnings
added 1 package in 534ms
1 package is looking for funding
run `npm fund` for details
$ test2-cli
node:internal/modules/cjs/loader:1042
throw err;
^
Error: Cannot find module 'suppress-experimental-warnings'
Require stack:
- internal/preload
at Module._resolveFilename (node:internal/modules/cjs/loader:1039:15)
at Module._load (node:internal/modules/cjs/loader:885:27)
at Module.require (node:internal/modules/cjs/loader:1105:19)
at Module._preloadModules (node:internal/modules/cjs/loader:1395:12)
at loadPreloadModules (node:internal/process/pre_execution:621:5)
at setupUserModules (node:internal/process/pre_execution:125:3)
at prepareExecution (node:internal/process/pre_execution:116:5)
at prepareMainThreadExecution (node:internal/process/pre_execution:36:3)
at node:internal/main/run_main_module:10:1 {
code: 'MODULE_NOT_FOUND',
requireStack: [ 'internal/preload' ]
}
Node.js v18.13.0
$ nvm ls
v6.14.4
v14.15.5
v16.10.0
v16.17.0
-> v18.13.0
v19.5.0
default -> lts/* (-> v18.13.0)
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v19.5.0) (default)
stable -> 19.5 (-> v19.5.0) (default)
lts/* -> lts/hydrogen (-> v18.13.0)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.24.1 (-> N/A)
lts/erbium -> v12.22.12 (-> N/A)
lts/fermium -> v14.21.2 (-> N/A)
lts/gallium -> v16.19.0 (-> N/A)
lts/hydrogen -> v18.13.0
$ npm prefix -g
/home/bjam/.nvm/versions/node/v18.13.0
$ ls -la /home/bjam/.nvm/versions/node/v18.13.0/lib/node_modules/ | grep suppress-
drwxr-xr-x 3 bjam bjam 4096 Mar 17 11:00 suppress-experimental-warnings
I found a way to resolve it by replacing experimental json import by readFileSync
combined to URL
and import.meta.url
to resolve relative path from the project.
#!/usr/bin/env -S node
import { readFileSync } from 'fs'
const filePath = new URL('jsonFile.json', import.meta.url)
const jsonContent = JSON.parse(readFileSync(filePath, 'utf-8'))
console.log(jsonContent)
With readFileSync
, have to think than a relative path is relative to the current working directory (where the script is called) and not relative to the project (where the package.json
is stored).
Relative path to the current working directory :
readFileSync('jsonFile.json', 'utf-8')
Relative path to the current file :
new URL('jsonFile.json', import.meta.url)