npm scriptsで開発をもっと効率的に!実践的自動化テクニック 🚀

開発ツール / 効率化

はじめに:npm scriptsとは?🤔

現代のWeb開発、特にNode.jsエコシステムにおいては、npm (Node Package Manager) は欠かせないツールです。多くの開発者がライブラリのインストールや管理に npm install を利用していますが、npm の真価はそれだけではありません。npm scripts という強力な機能を使うことで、開発ワークフローにおける様々なタスクを自動化し、劇的に効率化できるのです!✨

npm scriptsは、プロジェクトの package.json ファイル内に定義されるカスタムコマンドです。これを使うことで、以下のようなメリットがあります。

  • タスクの標準化: ビルド、テスト、デプロイといった定型的なタスクを、npm run buildnpm test のような簡単なコマンドで誰でも実行できるようになります。これにより、チーム内での手順のばらつきを防ぎ、新しいメンバーもすぐに開発に参加しやすくなります。
  • 依存関係の隠蔽: プロジェクトで利用している特定のツール(例: Babel, webpack, ESLint, Prettier)の実行方法を、npm scripts内に閉じ込めることができます。利用者はツールの詳細な使い方を知らなくても、定義されたスクリプトを実行するだけで済みます。
  • クロスプラットフォーム対応の促進: シェルスクリプトはOSによって挙動が異なることがありますが、npm scriptsと関連ツール(後述する cross-env など)を組み合わせることで、Windows, macOS, Linuxなど、異なる環境でも動作するスクリプトを作成しやすくなります。
  • 複雑なタスクの簡略化: 複数のコマンドを組み合わせたり、特定の順序で実行したりする複雑なワークフローも、一つのnpm scriptとしてまとめることができます。

この記事では、npm scriptsの基本的な使い方から、より実践的で高度なテクニックまで、順を追って詳しく解説していきます。これを読めば、あなたもnpm scriptsを使いこなし、日々の開発をもっと快適で効率的なものにできるはずです!💪

基本的な使い方:package.jsonとnpm run

npm scriptsの心臓部となるのが、プロジェクトルートにある package.json ファイルです。このJSONファイルには、プロジェクト名、バージョン、依存ライブラリなどの情報が含まれていますが、その中に "scripts" というキーがあります。ここに、実行したいタスクの名前(キー)と、そのタスクで実行するコマンド(値)を定義します。

scriptsフィールドの構造

以下は、package.jsonscripts フィールドの簡単な例です。

{
  "name": "my-awesome-project",
  "version": "1.0.0",
  "description": "An awesome project using npm scripts",
  "main": "index.js",
  "scripts": {
    "hello": "echo 'Hello, npm scripts!'",
    "greet": "node greet.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {}
}

この例では、"hello""greet" という2つのスクリプトが定義されています。

  • hello スクリプトは、シェルコマンド echo 'Hello, npm scripts!' を実行します。これは単にコンソールにメッセージを表示するだけの簡単な例です。
  • greet スクリプトは、Node.jsを使って greet.js というファイルを実行します。

npm run でスクリプトを実行

これらのスクリプトを実行するには、ターミナル(コマンドプロンプトやPowerShellなど)で npm run <script-name> というコマンドを使用します。

# hello スクリプトを実行
npm run hello

# greet スクリプトを実行
npm run greet

npm run hello を実行すると、コンソールに Hello, npm scripts! と表示されます。npm run greet を実行すると、greet.js ファイルの内容がNode.jsによって実行されます(事前に greet.js ファイルを作成しておく必要があります)。

💡 Tip: npm run コマンドを引数なしで実行すると、利用可能なスクリプトの一覧が表示されます。これも覚えておくと便利です。

事前定義されたスクリプト名

npmには、いくつかの特別な意味を持つスクリプト名が事前定義されています。これらのスクリプト名は、npm run を省略して実行できる場合があります。

  • start: プロジェクトを開始するためのスクリプトです。通常、開発サーバーの起動などに使われます。npm start で実行できます(run は不要)。
    "scripts": {
      "start": "node server.js"
    }
    npm start
  • test: プロジェクトのテストを実行するためのスクリプトです。npm test (または npm t) で実行できます(run は不要)。
    "scripts": {
      "test": "jest"
    }
    npm test
  • stop: プロジェクトを停止するためのスクリプトです。start と対になることが多いですが、自動で呼び出されることは少なく、明示的に npm run stop で実行する必要があります。
    "scripts": {
      "start": "node server.js --port 3000",
      "stop": "kill $(lsof -t -i:3000)" // ポート3000で動いているプロセスを停止(環境依存あり)
    }
    npm run stop
  • restart: プロジェクトを再起動するためのスクリプトです。stop, restart, start を順に実行する便利なショートカットですが、stopstart スクリプトが定義されている必要があります。npm restart で実行できます(run は不要)。
    npm restart
    ⚠️ 注意: restartstopstart が定義されていないとエラーになります。また、単純に stop -> start を実行するだけなので、複雑な再起動処理には向かない場合があります。

これらの事前定義されたスクリプト名を使うことで、プロジェクトの基本的な操作が直感的になります。特に starttest は多くのプロジェクトで標準的に使われています。

カスタムスクリプトの作成:より複雑なタスクへ

npm scriptsの真価は、独自のカスタムスクリプトを作成して、開発ワークフローを自動化するところにあります。単純なコマンドだけでなく、複数のコマンドを組み合わせることも可能です。

複数のコマンドを連結する

シェルの機能を使って、複数のコマンドを一つのスクリプト内で実行できます。連結方法にはいくつかあり、挙動が異なります。

  • && (AND): 左側のコマンドが成功した場合にのみ、右側のコマンドを実行します。ビルドが成功したらテストを実行する、といった依存関係のあるタスクに適しています。
    "scripts": {
      "build-and-test": "npm run build && npm run test"
    }
    この例では、npm run build が正常に終了(終了コード 0)した場合にのみ、npm run test が実行されます。ビルドに失敗した場合はテストは実行されません。
  • ; (セミコロン): 左側のコマンドの成功・失敗にかかわらず、右側のコマンドを実行します。順番に実行したいが、互いに依存していないタスクに使えます。
    "scripts": {
      "cleanup-and-log": "rm -rf ./dist ; echo 'Cleanup done.'"
    }
    この例では、rm -rf ./dist が失敗したとしても(例えば dist ディレクトリが存在しなくても)、echo 'Cleanup done.' は実行されます。
  • & (バックグラウンド): 左側のコマンドをバックグラウンドで実行し、すぐに右側のコマンドを実行します(並列実行に近いですが、完全な制御は難しいです)。主にサーバー起動と他のタスクを同時に行う場合などに使われますが、挙動が不安定になることもあります。
    "scripts": {
      "dev": "node server.js & webpack --watch" // 環境によってはうまく動かないことも
    }
🚨 注意点: これらのシェル演算子は、OS(特にWindowsのコマンドプロンプト)によって挙動が異なる、あるいはサポートされていない場合があります。クロスプラットフォームなスクリプトを目指す場合は注意が必要です。後述する npm-run-all などのツールを使うのがより堅牢です。

コマンドの並列実行と順次実行:npm-run-all

複数のnpm scriptsを順番に実行したり、並行して実行したりしたい場合、シェルの演算子だけでは限界があります。特にクロスプラットフォーム対応や、複雑な依存関係の管理には、専用のツールを使うのが便利です。その代表格が npm-run-all です。

まず、npm-run-all を開発依存としてインストールします。

npm install --save-dev npm-run-all

npm-run-all を使うと、以下のようにスクリプトを定義できます。

{
  "scripts": {
    "lint": "eslint .",
    "format": "prettier --write .",
    "build:css": "sass styles/main.scss dist/style.css",
    "build:js": "webpack",
    "build": "npm-run-all build:*", // build:css と build:js を並列実行
    "check": "npm-run-all lint format", // lint と format を並列実行
    "ci": "npm-run-all check build test" // check, build, test を順次実行
  }
}
  • npm-run-all <task1> <task2> ...: 指定したタスクを並列で実行します。build:* のようなワイルドカードも使えます。
  • npm-run-all -s <task1> <task2> ... (または run-s): 指定したタスクを順次実行します。-s--sequential の略です。
  • npm-run-all -p <task1> <task2> ... (または run-p): 指定したタスクを並列で実行します。-p--parallel の略です。デフォルトの動作と同じですが、明示的に並列を指定したい場合に便利です。

npm-run-all を使うことで、複雑なタスクの組み合わせも、クロスプラットフォームを意識しながら簡潔に記述できます。

スクリプト内での環境変数利用:cross-env 🌿

npm scripts内で環境変数を使いたい場面はよくあります。例えば、開発モードと本番モードでAPIのエンドポイントを切り替えたり、特定の機能を有効/無効にしたりする場合です。

しかし、環境変数の設定方法はOSによって異なります。

  • Linux / macOS (bash, zshなど): NODE_ENV=production node server.js
  • Windows (コマンドプロンプト): set NODE_ENV=production && node server.js
  • Windows (PowerShell): $env:NODE_ENV="production"; node server.js

このようにOSごとに書き方を変えるのは非常に面倒ですし、package.json が複雑になってしまいます。ここで役立つのが cross-env です。

cross-env は、環境変数を設定するコマンドをクロスプラットフォームで実行できるようにするユーティリティです。まず、開発依存としてインストールします。

npm install --save-dev cross-env

そして、scripts の中で、環境変数を設定したいコマンドの前に cross-env を付けます。

{
  "scripts": {
    "build": "cross-env NODE_ENV=production webpack --mode production",
    "dev": "cross-env NODE_ENV=development webpack serve --mode development",
    "start:prod": "cross-env NODE_ENV=production PORT=8080 node server.js"
  }
}

cross-env が、実行環境(OS)を判別し、適切な方法で環境変数を設定してから、その後のコマンドを実行してくれます。これにより、package.jsonscripts はシンプルで、どの環境でも同じように動作します。これはチーム開発において非常に重要です。💯

スクリプトへの引数の渡し方 ➡️

npm scriptsを実行する際に、追加の引数を渡したい場合があります。例えば、テストスクリプトで特定のファイルだけをテストしたい、ビルドスクリプトで特定のオプションを有効にしたい、といったケースです。

npm scriptsに引数を渡すには、npm run <script-name> -- <arguments> のように、スクリプト名の後に -- を付け、その後に渡したい引数を記述します。-- 以降の部分が、スクリプト内で実行されるコマンドにそのまま渡されます。

例を見てみましょう。

{
  "scripts": {
    "test": "jest",
    "serve": "http-server ./public -p 8080",
    "greet": "node greet.js"
  }
}
  • 特定のテストファイルだけを実行したい場合:
    npm run test -- --watch src/utils.test.js
    これは、内部的に jest --watch src/utils.test.js を実行します。--watchsrc/utils.test.jsjest コマンドに渡されます。
  • serve スクリプトでポート番号を変更したい場合:
    npm run serve -- -p 9000
    これは、内部的に http-server ./public -p 8080 -p 9000 を実行します(http-server が最後の -p を優先する場合)。もしデフォルトのポート指定を上書きしたいなら、スクリプト側での対応が必要かもしれません。より柔軟なのは、スクリプト自体が引数を解釈するように作ることです。
    // greet.js
    const name = process.argv[2] || 'World'; // コマンドライン引数の3番目(node greet.js name の 'name')を取得
    console.log(`Hello, ${name}!`);
    npm run greet -- Alice
    これは、内部的に node greet.js Alice を実行し、コンソールに Hello, Alice! と表示されます。
💡 Tip: npm startnpm test のような run を省略できるコマンドの場合でも、引数を渡すときは npm test -- --watch のように -- を使う必要があります。

スクリプトのフック:preとpost 🎣

npm scriptsには「フック」と呼ばれる仕組みがあります。これは、特定のスクリプトが実行される前 (pre) または後 (post) に、自動的に別のスクリプトを実行する機能です。

フックスクリプトを定義するには、元のスクリプト名の前に pre または post を付けます。例えば、build スクリプトに対するフックは prebuildpostbuild になります。

{
  "scripts": {
    "prebuild": "npm run clean", // build の前に実行
    "build": "webpack",
    "postbuild": "npm run deploy", // build の後に実行 (成功時のみ)

    "pretest": "npm run lint", // test の前に実行
    "test": "jest",
    "posttest": "echo 'Tests finished!'", // test の後に実行 (成功時のみ)

    "clean": "rm -rf ./dist",
    "lint": "eslint .",
    "deploy": "echo 'Deploying build artifacts...'" // 仮のデプロイコマンド
  }
}

この設定で npm run build を実行すると、以下の順序でスクリプトが実行されます。

  1. prebuild (つまり npm run clean) が実行される。
  2. prebuild が成功したら、build (つまり webpack) が実行される。
  3. build が成功したら、postbuild (つまり npm run deploy) が実行される。

同様に、npm test を実行すると、pretest -> test -> posttest の順で実行されます。

⚠️ 注意: pre スクリプトが失敗すると、メインのスクリプトと post スクリプトは実行されません。同様に、メインのスクリプトが失敗すると、post スクリプトは実行されません。これは、一連のタスクが安全に実行されることを保証する上で重要です。

フックは、以下のようなシナリオで非常に役立ちます。

  • テスト実行前にコードのリンティングを行う (pretest)。
  • ビルド前に出力ディレクトリをクリーンアップする (prebuild)。
  • バージョンを上げる前にテストを実行する (preversion – これは npm version コマンドに対するフックです)。
  • npmパッケージを公開する前にビルドとテストを行う (prepublishOnly – これは npm publish に対するフックです)。

フックをうまく使うことで、スクリプト間の依存関係を暗黙的に定義し、ワークフローをより堅牢にすることができます。

npmパッケージの実行可能ファイルを利用する 🛠️

プロジェクトにローカルインストールしたnpmパッケージには、コマンドラインツール(実行可能ファイル)が含まれていることがよくあります。例えば、webpack, eslint, prettier, jest, typescript などです。

これらのツールをnpm scripts内から実行する場合、通常はグローバルにインストールする必要はありません。npm run でスクリプトを実行すると、npmは自動的に ./node_modules/.bin ディレクトリを環境変数 PATH の先頭に追加します。この ./node_modules/.bin には、ローカルインストールされたパッケージの実行可能ファイルへのシンボリックリンク(またはWindowsの場合は .cmd ファイルなど)が配置されています。

これにより、npm scriptsの中では、ローカルインストールされたコマンド名を直接記述するだけで実行できます。

{
  "scripts": {
    "lint": "eslint src/**/*.js", // ローカルのeslintが実行される
    "build": "webpack",         // ローカルのwebpackが実行される
    "test": "jest"              // ローカルのjestが実行される
  },
  "devDependencies": {
    "eslint": "^8.0.0",
    "webpack": "^5.0.0",
    "jest": "^29.0.0"
  }
}

npm run lint を実行すると、グローバルに eslint がインストールされていなくても、プロジェクトの node_modules 内にある eslint が使われます。これにより、プロジェクトごとにツールのバージョンを固定でき、開発者間の環境差異による問題を避けることができます。これはnpm scriptsの非常に大きな利点の一つです。🌟

npx との関係

npx は、npm 5.2.0から同梱されるようになったコマンドラインツールで、ローカルまたはリモートのnpmパッケージのコマンドを実行するのに便利です。

  • ローカルパッケージの実行: npx <command> は、まず node_modules/.bin 内に <command> が存在するか探し、あればそれを実行します。これはnpm scripts内での挙動と似ています。
  • 一時的な実行: ローカルに <command> が見つからない場合、npx はそのパッケージを一時的にダウンロードして実行し、実行後に削除します。これにより、一度しか使わないようなツールをグローバルやローカルにインストールせずに試すことができます。例えば、npx create-react-app my-app のようにプロジェクトの雛形作成ツールを実行する際によく使われます。

npm scriptsの中では、node_modules/.bin が自動的にPATHに追加されるため、通常は npx を使う必要はありません。コマンド名を直接書けばOKです。

// npm scripts 内では、以下はどちらもほぼ同じ意味(ローカルのeslintを使う)
"scripts": {
  "lint": "eslint .",
  "lint-npx": "npx eslint ." // npx を使うこともできるが、冗長な場合が多い
}

ただし、スクリプト内で特定のバージョンのNode.jsを使いたい場合など、npx が便利なケースもあります(例: npx -p node@18 node myscript.js)。

より複雑なスクリプト:シェルスクリプト vs Node.jsスクリプト

npm scriptsが長くなったり、複雑なロジック(条件分岐、ループなど)が必要になったりすると、package.jsonscripts フィールドだけで管理するのは難しくなってきます。可読性も保守性も低下しがちです。

このような場合、2つの主なアプローチがあります。

1. シェルスクリプトファイルを利用する

複雑な処理を別のシェルスクリプトファイル(例: scripts/build.sh)に記述し、npm scriptからはそのファイルを実行するようにします。

// package.json
{
  "scripts": {
    "build": "bash ./scripts/build.sh"
  }
}
#!/bin/bash
# ./scripts/build.sh

set -e # エラーが発生したらスクリプトを終了する

echo "Starting build process..."

# クリーンアップ
echo "Cleaning up dist directory..."
rm -rf ./dist
mkdir ./dist

# CSSのビルド
echo "Building CSS..."
sass styles/main.scss dist/style.css --style compressed

# JavaScriptのビルド (環境変数を使用)
echo "Building JavaScript for $NODE_ENV..."
cross-env BABEL_ENV=$NODE_ENV webpack --mode $NODE_ENV

echo "Build completed successfully!"

メリット:

  • シェルスクリプトに慣れている場合、書きやすい。
  • 簡単な処理なら手早く書ける。

デメリット:

  • Windows環境での互換性に問題が出やすい(bashが必要、コマンドの違いなど)。WSL (Windows Subsystem for Linux) を使えば緩和できますが、開発者全員にそれを要求するのは難しい場合もあります。
  • 複雑なロジック、エラーハンドリング、JSON操作などは苦手。

2. Node.jsスクリプトファイルを利用する

複雑な処理をNode.jsスクリプトファイル(例: scripts/build.js)に記述し、npm scriptからはそれを node コマンドで実行します。

// package.json
{
  "scripts": {
    "build": "node ./scripts/build.js"
    // cross-env を使って環境変数を渡すことも可能
    // "build:prod": "cross-env NODE_ENV=production node ./scripts/build.js"
  }
}
// ./scripts/build.js
const fs = require('fs-extra'); // fs-extra は標準のfsより高機能
const path = require('path');
const { execSync } = require('child_process'); // 同期的に子プロセスを実行

const env = process.env.NODE_ENV || 'development';
const distDir = path.resolve(__dirname, '../dist');

console.log(`Starting build process for ${env}...`);

try {
  // クリーンアップ
  console.log('Cleaning up dist directory...');
  fs.emptyDirSync(distDir); // ディレクトリがなければ作成、あれば中身を空にする

  // CSSのビルド (ここでは例としてシェルコマンドを実行)
  console.log('Building CSS...');
  const sassCommand = `sass ${path.resolve(__dirname, '../styles/main.scss')} ${path.join(distDir, 'style.css')} --style compressed`;
  execSync(sassCommand, { stdio: 'inherit' }); // stdio:'inherit' で子プロセスの出力を表示

  // JavaScriptのビルド (webpackのNode APIを使うことも可能だが、ここではコマンド実行例)
  console.log(`Building JavaScript for ${env}...`);
  const webpackCommand = `webpack --mode ${env}`;
  // 環境変数を渡す場合は execSync のオプションで設定可能
  // execSync(webpackCommand, { stdio: 'inherit', env: { ...process.env, BABEL_ENV: env } });
  execSync(webpackCommand, { stdio: 'inherit' });

  console.log('Build completed successfully!');

} catch (error) {
  console.error('Build failed:', error);
  process.exit(1); // エラーで終了
}

メリット:

  • Node.js (JavaScript) で記述できるため、フロントエンド/バックエンド開発者にとって親しみやすい。
  • ファイルシステム操作、非同期処理、JSON操作、エラーハンドリングなどが得意。
  • npm install で追加したライブラリ(例: fs-extra, chalk)をスクリプト内で利用できる。
  • Node.js自体がクロスプラットフォームなので、OS間の互換性問題が起きにくい(シェルコマンド呼び出し部分を除く)。

デメリット:

  • 簡単な処理でもファイルを作成する必要がある。
  • シェルコマンドの実行には child_process モジュールなどを使う必要があり、少し冗長になることがある。

一般的に、スクリプトが数行を超える場合や、クロスプラットフォーム対応が重要な場合は、Node.jsスクリプトを利用する方が堅牢で保守しやすい傾向にあります。👍

実践的な例:よくあるタスクの自動化 🎯

これまでに学んだことを活かして、実際の開発でよく使われるnpm scriptsの例を見てみましょう。

{
  "name": "practical-npm-scripts-example",
  "version": "1.0.0",
  "scripts": {
    // --- 開発 ---
    "dev": "npm-run-all --parallel dev:*", // 開発サーバーとwatchを並行起動
    "dev:server": "cross-env NODE_ENV=development nodemon server.js", // nodemonでサーバー自動再起動
    "dev:build": "webpack --watch --mode development", // webpackでファイルの変更を監視して自動ビルド

    // --- ビルド ---
    "build": "npm-run-all clean build:*", // クリーンアップ後、各ビルドを並列実行
    "build:css": "sass src/styles:dist/css --style compressed",
    "build:js": "cross-env NODE_ENV=production webpack --mode production",
    "build:assets": "cpx 'src/assets/**/*' dist/assets", // ファイル/ディレクトリコピー

    // --- コード品質 ---
    "lint": "eslint 'src/**/*.js'",
    "lint:fix": "eslint 'src/**/*.js' --fix",
    "format": "prettier --write 'src/**/*.{js,jsx,ts,tsx,json,css,scss,md}'",
    "check-types": "tsc --noEmit", // TypeScriptの型チェック
    "validate": "npm-run-all -p check-types lint format", // 型チェック、Lint、Formatを並行実行

    // --- テスト ---
    "test": "cross-env NODE_ENV=test jest",
    "test:watch": "npm run test -- --watch",
    "test:coverage": "npm run test -- --coverage",
    "pretest": "npm run validate", // テスト前にコード品質チェックを実行

    // --- クリーンアップ ---
    "clean": "rimraf dist coverage", // rimrafはクロスプラットフォームな rm -rf

    // --- CI/デプロイ関連 ---
    "ci": "npm-run-all validate build test", // CIパイプラインで実行する一連のタスク
    "deploy": "node scripts/deploy.js", // デプロイ用のNode.jsスクリプトを実行
    "predeploy": "npm run ci", // デプロイ前にCIタスクを実行

    // --- その他 ---
    "start": "cross-env NODE_ENV=production node server.js", // 本番サーバー起動
    "prepare": "husky install" // husky (Gitフック管理) の設定用 (npm install後に自動実行される)
  },
  "devDependencies": {
    "@babel/core": "...",
    "cross-env": "...",
    "cpx": "...", // ファイルコピー用ユーティリティ
    "eslint": "...",
    "husky": "...", // Gitフック管理
    "jest": "...",
    "nodemon": "...", // ファイル変更監視・自動再起動
    "npm-run-all": "...",
    "prettier": "...",
    "rimraf": "...", // クロスプラットフォーム rm -rf
    "sass": "...",
    "typescript": "...",
    "webpack": "...",
    "webpack-cli": "..."
    // ... その他必要な開発依存パッケージ
  },
  "dependencies": {
    // ... アプリケーションの依存パッケージ
  }
}

この例では、以下のような多くの一般的なタスクがnpm scriptsで定義されています。

  • 開発サーバーの起動とファイル変更の監視 (dev)
  • 本番用のビルド (build)
  • コードの静的解析とフォーマット (lint, format, validate)
  • ユニットテストの実行 (test)
  • 生成されたファイルのクリーンアップ (clean)
  • CI/CDパイプラインで実行される一連のタスク (ci)
  • デプロイスクリプトの実行 (deploy)

このように、関連するタスクをグループ化し (例: build:*)、npm-run-all やフック (pretest, predeploy) を活用することで、非常に整理され、再利用性の高いスクリプト集を構築できます。

ベストプラクティスと注意点 📝

npm scriptsを効果的に使うために、いくつか意識しておきたい点があります。

  • 推奨 命名規則:
    • タスクの内容がわかる、一貫性のある名前を付けましょう(例: build:js, build:css)。
    • コロン (:) を使って、関連するタスクをグルーピングすると見通しが良くなります。
    • pre, post フックを活用し、暗黙的な依存関係を定義します。
  • 推奨 クロスプラットフォーム対応:
    • 環境変数の設定には cross-env を使いましょう。
    • ファイルパスの操作にはNode.jsの path モジュールを使うか、cpxcopyfiles のようなクロスプラットフォームなツールを使いましょう。
    • ファイルの削除には rimrafdel-cli を使いましょう (rm -rf の代わりに)。
    • 複雑な処理やOS依存のコマンドが多い場合は、Node.jsスクリプトの利用を検討しましょう。
  • 推奨 可読性と保守性:
    • スクリプトが長くなりすぎる場合は、シェルスクリプトやNode.jsスクリプトファイルに分割しましょう。
    • npm-run-all を使って、複数のスクリプトの実行フローを明確にしましょう。
    • コメントを活用しましょう (ただし、JSONなのでコメントは書けません。スクリプトファイル内に書くか、package.json の他のフィールドにメモとして残すなどの工夫が必要です)。
  • 推奨 ローカル依存を活用:
    • 必要なCLIツールは devDependencies としてローカルにインストールし、グローバルインストールに依存しないようにしましょう。これにより、プロジェクトの再現性が高まります。
  • 注意 シェルの違い:
    • npm scriptsはデフォルトでシステムのデフォルトシェル (Linux/macOSでは sh、Windowsでは cmd.exe) を使って実行されます。シェルの種類によって使えるコマンドや構文が異なることに注意が必要です。bash を前提としたスクリプトはWindowsでは動かない可能性があります。
    • script-shell 設定でnpmが使うシェルを変更することもできますが、開発者全員の環境でそれを統一するのは難しい場合があります。
  • 注意 セキュリティ:
    • インターネットから取得した信頼できないプロジェクトで、安易に npm installnpm run を実行しないでください。install スクリプトや preinstall / postinstall スクリプトに悪意のあるコードが含まれている可能性があります。npm ciinstall スクリプトを実行しないため、より安全な場合があります。

まとめ 🎉

npm scripts は、Node.js開発におけるタスク自動化のための強力で身近なツールです。基本的な使い方から、cross-env, npm-run-all, フック、Node.jsスクリプトの活用といった応用的なテクニックまで見てきました。

これらを活用することで、

  • 煩雑なコマンド入力を削減し、タイプミスを防ぐ。
  • 開発、ビルド、テスト、デプロイなどのワークフローを標準化・自動化する。
  • チームメンバー間の環境差異を吸収し、共同作業をスムーズにする。
  • プロジェクトの依存関係を明確にし、管理しやすくする。

といった多くのメリットが得られます。

もし、まだ npm install くらいしか npm を使っていなかったなら、ぜひ今日から package.jsonscripts フィールドを活用してみてください。最初は簡単なスクリプトからで構いません。少しずつ自動化を進めていくことで、開発体験は着実に向上していくはずです。Happy Scripting! 💻

コメント

タイトルとURLをコピーしました