ClaudeでNode.js CLIツールを作る方法|package.json自動生成からテンプレートまで
Node.js CLIツール開発がClaudeで加速する理由
Node.jsでコマンドラインツール(CLI)を作るとき、いくつもの決定を迫られます。package.jsonの設定、CommonJSとESMのどちらにするか、エラーハンドリングの実装、そしてテンプレート化による再利用性。これらの作業は定型的でありながら、細かいミスが起きやすい領域です。
ClaudeはこうしたNode.js CLI開発の自動化に最適です。単にコードを生成するだけでなく、package.jsonの構造設計、ESMとCommonJSの違いを理解したうえでのエラー診断、そして複数プロジェクトで再利用できるテンプレート化まで、一貫した開発フローをサポートします。
本記事では、ClaudeでNode.js CLIツール開発を効率化する3つの実践的な方法を紹介します。
必要な環境と前提知識
Node.js CLI開発にClaudeを使う場合、以下の環境を整えておくと効率的です。
- Node.js 14.0以上(CLIプロジェクトの実行環境)
- npm 6.0以上(パッケージ管理)
- Claude API アクセス(有料プランまたは無料版)
- テキストエディタ(VSCode推奨)
- Git(バージョン管理とテンプレート保存用)
前提知識としては、Node.jsの基本的なモジュール構造、package.jsonの役割、npm scriptの書き方に関する初級程度の理解があると、Claudeからの出力を効果的に活用できます。
方法1: package.json自動生成とCLI設定
Node.js CLIを作る際、package.jsonの設定は非常に重要です。プロジェクトメタデータ、スクリプト、依存関係、ESMの有無、binフィールドによるコマンド登録など、複数の要素が関連します。
Claudeへのプロンプト例
Claudeに以下のような具体的な指示を与えることで、プロジェクトに最適なpackage.jsonを生成できます。
以下の仕様でNode.js CLIツール用のpackage.jsonを生成してください:
- ツール名: markdown-to-json
- 説明: Markdown形式のファイルをJSON形式に変換するCLIツール
- メインファイル: src/index.js
- コマンド名: md2json
- メジャーバージョン: 1、マイナーバージョン: 0、パッチバージョン: 0
- 依存パッケージ: marked (Markdown解析), yargs (コマンドラインパース)
- 開発時のみ必要: eslint (コード品質チェック)
- モジュール形式: CommonJS(古い環境対応)
- author: your-name
- license: MIT
- binフィールドを使用して、グローバルコマンドとして実行可能にする設定を含めてください
Claudeの出力例と解説
Claudeはこうした指示から、以下のようなpackage.jsonを生成します。
{
"name": "markdown-to-json",
"version": "1.0.0",
"description": "Markdown形式のファイルをJSON形式に変換するCLIツール",
"main": "src/index.js",
"type": "commonjs",
"bin": {
"md2json": "./src/cli.js"
},
"scripts": {
"start": "node src/cli.js",
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "eslint src/"
},
"keywords": [
"markdown",
"json",
"cli",
"converter"
],
"author": "your-name",
"license": "MIT",
"dependencies": {
"marked": "^11.1.0",
"yargs": "^17.7.2"
},
"devDependencies": {
"eslint": "^8.50.0"
}
}
このpackage.jsonの重要なポイントを説明します。
binフィールド: "bin": { "md2json": "./src/cli.js" } により、npm install -g でグローバルにインストールした際に、md2json コマンドが使用可能になります。
typeフィールド: "type": "commonjs" でCommonJS形式を明示しています。ESMを使う場合は "type": "module" に変更します。
scripts: start でローカルテスト実行、lint でコード品質チェックが可能です。
実装手順
- プロジェクトフォルダを作成
mkdir markdown-to-json
cd markdown-to-json
- Claudeで生成したpackage.jsonを保存
cat > package.json << 'EOF'
{生成されたpackage.jsonの内容}
EOF
- 依存パッケージをインストール
npm install
- srcフォルダを作成してCLIスクリプトを配置
mkdir src
- CLIファイルのパーミッションを設定(Unixシステムの場合)
chmod +x src/cli.js
方法2: Node.js CommonJS vs ESM エラー診断と解決
Node.jsの開発で頻出するエラーが、CommonJSとESMの混在による問題です。代表的なエラーメッセージ「ERR_REQUIRE_ESM」や「ERR_MODULE_NOT_FOUND」に直面したとき、原因を素早く特定し解決することが重要です。
よく発生するエラーとその原因
ERR_REQUIRE_ESM エラー
このエラーは、ESM形式(import/export)でのみ提供されているパッケージに対して、CommonJS形式(require)でアクセスしようとしたときに発生します。
例えば、以下のようなコードでエラーが起きます。
// src/index.js (CommonJS形式)
const marked = require('marked'); // ← エラーが発生する可能性
module.exports = { marked };
ERR_MODULE_NOT_FOUND エラー
このエラーは、ESM形式で記述されたファイルが、存在しないモジュールを import しているときに発生します。
// src/index.js (ESM形式)
import axios from 'axios-esm'; // ← このパッケージが存在しない場合
export { axios };
Claudeを使った診断フロー
エラー診断では、以下のプロンプトをClaudeに送信してください。
以下のエラーメッセージが発生しています:
エラーメッセージ:
Error [ERR_REQUIRE_ESM]: require() of ES Module /node_modules/marked/lib/marked.esm.js not supported.
発生時のコード:
const marked = require('marked');
const yargs = require('yargs');
package.json:
{
"name": "markdown-to-json",
"version": "1.0.0",
"type": "commonjs",
"dependencies": {
"marked": "^11.1.0",
"yargs": "^17.7.2"
}
}
このエラーの原因と解決方法を教えてください。複数の解決パターンを示してください。
Claudeの診断結果と解決策
Claudeは以下のような診断と複数の解決策を提示します。
原因: markedパッケージのバージョン11.x以降はESM形式でのみ提供されており、CommonJS形式での require() は対応していません。
解決策1: package.jsonをESM形式に変更
{
"name": "markdown-to-json",
"version": "1.0.0",
"type": "module",
"main": "src/index.js",
"bin": {
"md2json": "./src/cli.js"
}
}
その後、コードをESM形式に書き換えます。
// src/index.js (ESM形式)
import { marked } from 'marked';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
export { marked, yargs };
解決策2: CommonJS互換性を保つため、古いバージョンのmarkedを使用
npm install marked@10.0.0
これにより、CommonJS形式で対応したmarkedを使用できます。
// src/index.js (CommonJS形式のまま)
const { marked } = require('marked');
const yargs = require('yargs');
module.exports = { marked, yargs };
解決策3: 動的importを使用
CommonJS形式のプロジェクト設定を保ちながら、ESMパッケージを使用したい場合は、動的importを使います。
// src/index.js (CommonJS形式、動的import使用)
async function initializeModules() {
const { marked } = await import('marked');
const yargs = await import('yargs');
return { marked, yargs };
}
module.exports = { initializeModules };
診断プロンプトテンプレート
エラーが発生した際に、Claudeに以下の情報を提供することで、迅速な診断が可能になります。
【エラー診断用プロンプトテンプレート】
1. エラーメッセージ(全文)
2. エラーが発生したコードファイル(該当部分)
3. package.json の全内容
4. Node.jsのバージョン(node -v で確認)
5. npm のバージョン(npm -v で確認)
6. 使用しているパッケージのバージョン(package-lock.json から確認)
実際の例:
エラーメッセージ:
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/user/project/src/utils' imported from /home/user/project/src/cli.js
コード:
import utils from './utils';
package.json より:
"type": "module"
Node.jsバージョン: v18.17.0
npm バージョン: 9.6.7
方法3: 再利用可能なAI副業テンプレートの構築
複数のNode.js CLIツールを開発する場合、テンプレート化することで大幅な開発時間短縮が可能になります。Claudeはテンプレート構造の設計から、実装例までサポートします。
テンプレートの基本構成
AI副業テンプレートとは、以下の要素を組み込んだ再利用可能なプロジェクト構造です。
cli-template/
├── src/
│ ├── cli.js # メインのCLIエントリーポイント
│ ├── index.js # コア処理ロジック
│ └── utils/
│ ├── file.js # ファイル操作ユーティリティ
│ ├── parser.js # データパース処理
│ └── error.js # カスタムエラーハンドリング
├── package.json # プロジェクト設定
├── package-lock.json # 依存パッケージのロック
├── .gitignore # Git無視設定
├── README.md # ドキュメント
└── examples/
├── input.txt # 入力サンプル
└── output.json # 出力サンプル
Claudeへのテンプレート設計プロンプト
Node.js CLIツール開発を効率化するためのリユーザブルなテンプレートを設計してください。
要件:
- CommonJS形式
- 複数のコマンドに対応(yargs使用)
- ファイル入出力機能を含む
- エラーハンドリングが充実している
- 開発時とプロダクション環境を区別
- npm scriptに test, lint, build を含む
- グローバルコマンドとして機能する
テンプレートの構成、各ファイルの内容、初期化スクリプトを提供してください。
テンプレートの実装例
package.json
{
"name": "cli-template",
"version": "1.0.0",
"description": "Reusable Node.js CLI Tool Template",
"main": "src/index.js",
"type": "commonjs",
"bin": {
"cli-template": "./src/cli.js"
},
"scripts": {
"start": "node src/cli.js",
"dev": "NODE_ENV=development node src/cli.js",
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "eslint src/",
"build": "echo Build complete"
},
"keywords": ["cli", "tool", "template"],
"author": "",
"license": "MIT",
"dependencies": {
"yargs": "^17.7.2"
},
"devDependencies": {
"eslint": "^8.50.0"
}
}
src/cli.js
#!/usr/bin/env node
const yargs = require('yargs');
const { hideBin } = require('yargs/helpers');
const { processFile } = require('./index');
const { handleError } = require('./utils/error');
const argv = yargs(hideBin(process.argv))
.command(
'process <input>',
'Process input file',
(yargs) => {
return yargs.positional('input', {
describe: 'Input file path',
type: 'string'
}).option('output', {
alias: 'o',
describe: 'Output file path',
type: 'string'
});
},
async (argv) => {
try {
const result = await processFile(argv.input, argv.output);
console.log('Processing complete:', result);
} catch (error) {
handleError(error);
}
}
)
.option('verbose', {
alias: 'v',
describe: 'Verbose output',
type: 'boolean'
})
.help()
.alias('help', 'h')
.argv;
src/index.js
const fs = require('fs').promises;
const path = require('path');
const { readFile, writeFile } = require('./utils/file');
async function processFile(inputPath, outputPath) {
try {
const input = await readFile(inputPath);
const processed = JSON.stringify(input, null, 2);
if (outputPath) {
await writeFile(outputPath, processed);
return { success: true, output: outputPath };
} else {
console.log(processed);
return { success: true, output: 'stdout' };
}
} catch (error) {
throw new Error(`File processing failed: ${error.message}`);
}
}
module.exports = { processFile };
src/utils/error.js
function handleError(error) {
const isDev = process.env.NODE_ENV === 'development';
if (isDev) {
console.error('Error Stack:', error.stack);
} else {
console.error('Error:', error.message);
}
process.exit(1);
}
module.exports = { handleError };
src/utils/file.js
const fs = require('fs').promises;
async function readFile(filePath) {
try {
const content = await fs.readFile(filePath, 'utf-8');
return JSON.parse(content);
} catch (error) {
throw new Error(`Failed to read file: ${filePath}`);
}
}
async function writeFile(filePath, content) {
try {
await fs.writeFile(filePath, content, 'utf-8');
} catch (error) {
throw new Error(`Failed to write file: ${filePath}`);
}
}
module.exports = { readFile, writeFile };
テンプレートの初期化スクリプト
新しいプロジェクトを素早く開始するための初期化スクリプトです。
#!/bin/bash
# init-template.sh
PROJECT_NAME=$1
if [ -z "$PROJECT_NAME" ]; then
echo "Usage: ./init-template.sh <project-name>"
exit 1
fi
mkdir -p "$PROJECT_NAME"
cd "$PROJECT_NAME"
# フォルダ構造作成
mkdir -p src/utils examples
# package.json を生成(プロジェクト名を置換)
cp package.json.template package.json
sed -i "s/\"name\": \"cli-template\"/\"name\": \"$PROJECT_NAME\"/" package.json
# その他ファイルをコピー
cp ../src/cli.js src/
cp ../src/index.js src/
cp ../src/utils/*.js src/utils/
# 依存パッケージをインストール
npm install
# Gitリポジトリ初期化
git init
echo "Template initialized: $PROJECT_NAME"
つまずきやすいポイントと解決方法
問題1: binフィールドの設定忘れ
CLIコマンドがグローバルで認識されない場合、package.jsonの bin フィールドが未設定の可能性があります。
{
"bin": {
"my-cli": "./src/cli.js"
}
}
加えて、CLIファイルの先頭にShebang行が必要です。
#!/usr/bin/env node
問題2: ESMとCommonJSの混在
新しいパッケージの多くはESM形式を採用しており、古いCommonJS形式のプロジェクトとの互換性が問題になります。
解決方法: package.jsonの type フィールドを明確に設定し、すべてのコードをいずれかのフォーマットに統一することが推奨されます。
問題3: パッケージのバージョン互換性
複数のパッケージを組み合わせる際、バージョンの不整合でエラーが発生することがあります。
解決方法: Claudeに「このパッケージの組み合わせで動作確認済みの安定したバージョン」を質問し、package.jsonに明示的なバージョン番号を記載することで回避できます。
応用: AI副業案件での活用方法
構築したテンプレートと知識は、以下のようなAI副業案件の効率化に直結します。
- データ変換ツール: CSVやXML、JSON形式のデータを別の形式に自動変換するCLIツール
- 定期実行スクリプト: cron/スケジューラーと連携して定期的に実行するバッチプロセス
- API連携ツール: 外部APIと連携し、取得したデータを整形・保存するツール
- コード生成ツール: テンプレートやプロンプトから自動でコードスニペットを生成
- ログ分析ツール: ログファイルを解析し、レポートを生成するツール
これらはいずれも、Claudeでの迅速な設計・実装が可能であり、テンプレート化することで複数案件への展開が容易になります。
次のステップ
node.js CLIツール開発の基盤ができたら、以下の拡張を検討してください。
- 単体テスト: jestやmochaを導入し、テストカバレッジを高める
- CI/CD: GitHubActionsやGitLabCIで自動テスト・デプロイを実現
- ドキュメント生成: JSDocからAPIドキュメントを自動生成
- パッケージング: npm registryに公開し、他の開発者が使用可能に
- パフォーマンス最適化: Node.js Profilerで処理時間のボトルネック分析
Claudeはこれらの高度なタスクでもサポート可能なため、段階的なスキル向上も見込めます。
あわせて読みたい
- Claude Codeの使い方|5つの実践テクニック&よくあるエラー対処法
- Claude Code でPython・シェルスクリプトを実行する方法【3ステップ解説】
- Claude Code初心者向けセットアップ【macOS/Windows手順】