Home > Back-end >  "Error: ENOENT: no such file or directory" after installing global npm package
"Error: ENOENT: no such file or directory" after installing global npm package

Time:10-30

I just published my first Node.js CLI tool package youtube-playlist-export to npm, but when I tried to test my package by installing it to my local computer, it gave the "Error: ENOENT: no such file or directory" warning.

Steps to Reproduce

First, open Terminal and the present working directly should be the home folder.

Next, install the package globally:

$ npm install -g youtube-playlist-export

added 397 packages, and audited 398 packages in 18s

67 packages are looking for funding
  run `npm fund` for details

10 moderate severity vulnerabilities

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

Lastly, run the ytpl-export command to start running the CLI app. It will give the "Error: ENOENT: no such file or directory" warning.

$ ytpl-export
(node:4632) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open 'C:\Users\User\package.json'
(Use `node --trace-warnings ...` to show where the warning was created)
(node:4632) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:4632) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Additional Information

Here's the brief structure of the project repository:

.
├── source/
│   ├── cli.js
│   └── ...
├── tests/
└── package.json

In package.json, I defined the "bin" field to be an object with a "ytpl-export" property with its field being "./source/cli.js". This registers the ytpl-export command in the Terminal such that it will run my CLI app.

{
  "name": "youtube-playlist-export",
  "version": "1.0.2",
  "engines": {
    "node": ">=12"
  },
  "files": [
    "source"
  ],
  "bin": {
    "ytpl-export": "./source/cli.js"
  }
}

And here's the source code source/cli.js:

#!/usr/bin/env node

import c from "chalk";
import { program } from "commander";
import { readPackage } from "read-pkg";
import updateNotifier from "update-notifier";
import idActionHandler from "./commands/id.js";
import keyActionHandler from "./commands/key.js";
import configActionHandler from "./commands/config.js";

(async () => {
  const pkg = await readPackage();

  updateNotifier({ pkg }).notify();

  program.name("ytpl-export").version(pkg.version);

  program.addHelpText("before", "Exports video data from a YouTube playlist to JSON/CSV file.\n");

  program
    .command("id")
    .description("Export video metadata of a playlist by its playlist ID.")
    .argument(
      "<playlistId>",
      // prettier-ignore
      `The value of the "list" parameter in the the playlist homepage URL (https://www.youtube.com/playlist?list=${c.greenBright("[playlistId]")})`
    )
    .option("-d, --default", "Skip all questions and use the default config")
    .addHelpText(
      "after",
      `
Example:
 $ ytpl-export id PLBCF2DAC6FFB574DE 
    `
    )
    .action(idActionHandler);

  program.command("key").description("Manage your YouTube API key.").action(keyActionHandler);

  program
    .command("config")
    .description("Edit configurations of this app.")
    .option("-p, --path", "show the path of the config file")
    .option("-r, --reset", "reset all configurations to default")
    .action(configActionHandler);

  program.parse(process.argv);
})();

CodePudding user response:

Cause of Problem

Thanks to Darth's comment, this problem is caused by the line const pkg = await readPackage(); in cli.js:

#!/usr/bin/env node

import { readPackage } from "read-pkg";
/* ... */

(async () => {
  const pkg = await readPackage();  // THIS LINE
  /* ... */
})();

According to read-pkg's documentation, if we don't supply the cwd option to readPackage(), the default value of cwd is process.cwd() (the current working directory). Since I run ytpl-export in the home directory, readPackage() will try to read package.json in the home directory, which does not exist.

Solution

  1. Install get-installed-path, a package that returns the install path of the module, to the project:
$ yarn add get-installed-path
  1. Edit cli.js such that readPackage() will read the package.json file from the installation path:
#!/usr/bin/env node

import { getInstalledPath } from "get-installed-path";
import { readPackage } from "read-pkg";
/* ... */

(async () => {
  const installedPath = await getInstalledPath("youtube-playlist-export");
  const pkg = await readPackage({ cwd: installedPath });
  /* ... */
})();
  • Related