◆ npm run などで実行したときは環境変数に色々な情報が入ってる

npm-scripts でコマンドを実行するときに実行したコマンドの名前を取得したいです

{
...
"scripts": {
"c1": "command1",
},
...
}

で 「yarn run c1」 を実行した場合は command1 が実行されます
command1 の中で c1 という名前が実行されたことを知りたいです

というのも

{
...
"scripts": {
"c1": "command1",
"c2": "command1",
"c3": "command1",
},
...
}

のように全部同じコマンドを呼び出すようにしておいて command1 の中で c1 として呼び出したのか c2 として呼び出したのかで処理を分岐したいです
しかしコマンドライン引数では node コマンドのパスと command1 のパスしか受け取れません
取れないものだろうなと諦めた結果 以前 npm-scripts で使うツールを作ったときは自分でコマンドを指定するようにしていました

ですが wireit の使用例を見ていると取れそうな感じです
どうやってるのかソースコードを見てみました

https://github.com/google/wireit/blob/main/src/cli-options.ts#L62
  // This environment variable is set by npm, yarn, and pnpm, and tells us which
// script is running.
const scriptName = process.env.npm_lifecycle_event;

環境変数に入ってるみたいですね

環境変数

command1 で実行するファイルを

#!/usr/bin/env node

console.log(process.env)

package.json を

{
"name": "a",
"version": "1.0.0",
"scripts": {
"c1": "command1",
"c2": "command1"
}
}

にして実行します

root@abc0c5fc3c92:/mnt# npm run c1 | grep lifecycle
npm_lifecycle_script: 'command1',
npm_lifecycle_event: 'c1',

root@abc0c5fc3c92:/mnt# yarn run c1 | grep lifecycle
warning package.json: No license field
npm_lifecycle_script: 'command1',
npm_lifecycle_event: 'c1',

root@abc0c5fc3c92:/mnt# pnpm run c1 | grep lifecycle
npm_lifecycle_script: 'command1',
npm_lifecycle_event: 'c1',

npm/yarn/pnpm どれでも npm_lifecycle_event という名前で取得できていますね
他にもパッケージマネージャーが追加する環境変数は色々ありそうなので一覧を取得してみました

npm run c1 > npm-env.txt
yarn run c1 > yarn-env.txt
pnpm run c1 > pnpm-env.txt

npm

{
npm_config_user_agent: 'npm/8.19.3 node/v18.13.0 linux x64 workspaces/false',
NODE_VERSION: '18.13.0',
HOSTNAME: 'abc0c5fc3c92',
YARN_VERSION: '1.22.19',
npm_node_execpath: '/usr/local/bin/node',
SHLVL: '1',
npm_config_noproxy: '',
HOME: '/root',
OLDPWD: '/mnt',
npm_package_json: '/opt/9/package.json',
npm_config_userconfig: '/root/.npmrc',
npm_config_local_prefix: '/opt/9',
container: 'podman',
COLOR: '0',
npm_config_metrics_registry: 'https://registry.npmjs.org/',
_: '/usr/local/bin/npm',
npm_config_prefix: '/usr/local',
TERM: 'xterm',
npm_config_cache: '/root/.npm',
npm_config_node_gyp: '/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js',
PATH: '/opt/9/node_modules/.bin:/opt/node_modules/.bin:/node_modules/.bin:/usr/local/lib/node_modules/npm/node_modules/@npmcli/run-script/lib/node-gyp-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
NODE: '/usr/local/bin/node',
npm_package_name: 'a',
npm_lifecycle_script: 'command1',
npm_package_version: '1.0.0',
npm_lifecycle_event: 'c1',
npm_config_globalconfig: '/usr/local/etc/npmrc',
npm_config_init_module: '/root/.npm-init.js',
PWD: '/opt/9',
npm_execpath: '/usr/local/lib/node_modules/npm/bin/npm-cli.js',
npm_config_global_prefix: '/usr/local',
npm_command: 'run-script',
INIT_CWD: '/opt/9',
EDITOR: 'vi'
}

yarn

{
npm_package_scripts_c2: 'command1',
npm_config_version_commit_hooks: 'true',
npm_config_user_agent: 'yarn/1.22.19 npm/? node/v18.13.0 linux x64',
NODE_VERSION: '18.13.0',
npm_config_bin_links: 'true',
HOSTNAME: 'abc0c5fc3c92',
YARN_VERSION: '1.22.19',
npm_node_execpath: '/usr/local/bin/node',
npm_config_init_version: '1.0.0',
SHLVL: '1',
HOME: '/root',
OLDPWD: '/mnt',
COREPACK_ROOT: '/usr/local/lib/node_modules/corepack',
npm_config_init_license: 'MIT',
YARN_WRAP_OUTPUT: 'false',
npm_config_version_tag_prefix: 'v',
container: 'podman',
_: '/usr/local/bin/yarn',
npm_config_registry: 'https://registry.yarnpkg.com',
TERM: 'xterm',
npm_config_ignore_scripts: '',
npm_config_version: '1.22.19',
PATH: '/tmp/yarn--1693043178969-0.9753192302193248:/opt/9/node_modules/.bin:/usr/local/share/.config/yarn/link/node_modules/.bin:/usr/local/libexec/lib/node_modules/npm/bin/node-gyp-bin:/usr/local/lib/node_modules/npm/bin/node-gyp-bin:/usr/local/bin/node_modules/npm/bin/node-gyp-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
NODE: '/usr/local/bin/node',
npm_package_name: 'a',
npm_lifecycle_script: 'command1',
npm_config_version_git_message: 'v%s',
npm_lifecycle_event: 'c1',
npm_package_version: '1.0.0',
npm_config_argv: '{"remain":[],"cooked":["run","c1"],"original":["run","c1"]}',
npm_config_version_git_tag: 'true',
npm_config_version_git_sign: '',
npm_config_strict_ssl: 'true',
PWD: '/opt/9',
npm_execpath: '/root/.cache/node/corepack/yarn/1.22.19/bin/yarn.js',
npm_config_save_prefix: '^',
npm_config_ignore_optional: '',
INIT_CWD: '/opt/9',
npm_package_scripts_c1: 'command1'
}

pnpm

{
npm_package_scripts_c2: 'command1',
npm_config_user_agent: 'pnpm/8.6.12 npm/? node/v18.13.0 linux x64',
NODE_VERSION: '18.13.0',
HOSTNAME: 'abc0c5fc3c92',
YARN_VERSION: '1.22.19',
npm_node_execpath: '/usr/local/bin/node',
SHLVL: '1',
HOME: '/root',
OLDPWD: '/mnt',
COREPACK_ROOT: '/usr/local/lib/node_modules/corepack',
container: 'podman',
_: '/usr/local/bin/pnpm',
npm_config_registry: 'https://registry.npmjs.org/',
TERM: 'xterm',
npm_config_node_gyp: '/root/.cache/node/corepack/pnpm/8.6.12/dist/node_modules/node-gyp/bin/node-gyp.js',
PATH: '/opt/9/node_modules/.bin:/root/.cache/node/corepack/pnpm/8.6.12/dist/node-gyp-bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
npm_package_name: 'a',
NODE: '/usr/local/bin/node',
npm_config_frozen_lockfile: '',
npm_lifecycle_script: 'command1',
npm_package_version: '1.0.0',
npm_lifecycle_event: 'c1',
PWD: '/opt/9',
npm_execpath: '/root/.cache/node/corepack/pnpm/8.6.12/bin/pnpm.cjs',
npm_command: 'run-script',
PNPM_SCRIPT_SRC_DIR: '/opt/9',
npm_package_scripts_c1: 'command1',
INIT_CWD: '/opt/9'
}

違い

似てるようで違いも多いです
環境変数に含まれるキーからどのパッケージマネージャーかの判断もできますが その用途だと npm_config_user_agent を見るのが良さそうです
またパッケージマネージャー関連では _ と npm_execpath にパスが入ってます
_ は /usr/local/bin の中を指していて実行した対象みたいです
node_execpath の方は corepack で管理されている yarn.js など node コマンドで内部的に実行される実体のパスになっているようです

環境変数の数は yarn が一番が多く pnpm が一番が少ないです
と言っても使わなそうなのが多いと思うのであまり気にするほどでもないと思います

使いそうな部分で言えば yarn と pnpm は package.json の中身を環境変数に展開してくれています
例えば npm_package_scripts_c2 に scripts.c2 の内容が含まれています
これは自分で追加した独自キーも対象になります

{
...
"foo": {
"bar": {
"baz": "value"
}
},
...
}

という値を package.json に追加した場合は

  npm_package_foo_bar_baz: 'value'

のようになります

npm だとそういう情報はなく 代わりに package.json のパスが npm_package_json というキーで含まれているので 自分で JSON ファイルを読み取る必要があります

これまで気にしたことがなかったですが npm-scripts では環境変数でも色々な情報が取得できるのですね