英語608453 views
中学社会667231 views
いろは2988729 views
高校生物549999 views
中学理科1626729 views
高校物理158371 views
小学理科717454 views
高校化学2914160 views
ヒストリア284663 views
高校日本史189916 views
Help
Tools

English

CommonJS から ES Modules への移行

多くの Node.js プロジェクトが CommonJS から ES Modules への移行を進めています。この記事では、移行の手順と注意点を解説します。

移行前の準備

まず、現在のプロジェクトの依存関係を確認します。使用しているパッケージが ES Modules に対応しているかを調べましょう。

# 依存パッケージのESM対応状況を確認
npm ls

基本的な移行手順

package.json に “type”: “module” を追加

require() を import に書き換え

module.exports を export に書き換え

__dirname / __filename を修正

Step 1: package.json の変更

{
  "name": "my-project",
  "type": "module",
  "version": "1.0.0"
}

この変更により、すべての .js ファイルが ES Modules として扱われます。

Step 2: require → import

// Before (CommonJS)
const express = require('express');
const { readFile } = require('fs');
const path = require('path');
const utils = require('./utils');

// After (ES Modules)
import express from 'express';
import { readFile } from 'fs';
import path from 'path';
import utils from './utils.js'; // 拡張子が必要

Step 3: exports → export

// Before (CommonJS)
function greet(name) {
  return `Hello, ${name}!`;
}
module.exports = { greet };
module.exports.VERSION = '1.0';

// After (ES Modules)
export function greet(name) {
  return `Hello, ${name}!`;
}
export const VERSION = '1.0';

Step 4: __dirname と __filename の修正

ES Modules では __dirname と __filename が使えません。

// Before (CommonJS)
const configPath = path.join(__dirname, 'config.json');

// After (ES Modules)
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const configPath = join(__dirname, 'config.json');

JSON ファイルの読み込み

ES Modules では JSON を直接 import できない環境もあります。

// Before (CommonJS)
const config = require('./config.json');

// After (ES Modules) - 方法1: assert を使用(Node.js 17.5+)
import config from './config.json' with { type: 'json' };

// After (ES Modules) - 方法2: fs で読み込む
import { readFileSync } from 'fs';
const config = JSON.parse(readFileSync('./config.json', 'utf-8'));

動的 require の移行

// Before (CommonJS)
const plugin = require(`./plugins/${name}`);

// After (ES Modules)
const plugin = await import(`./plugins/${name}.js`);

条件付き require の移行

// Before (CommonJS)
let db;
if (process.env.NODE_ENV === 'production') {
  db = require('./db-prod');
} else {
  db = require('./db-dev');
}

// After (ES Modules)
const dbModule = process.env.NODE_ENV === 'production'
  ? './db-prod.js'
  : './db-dev.js';
const db = await import(dbModule);

段階的な移行

一度にすべてを移行するのが難しい場合、段階的に進められます。

方法1: .mjs を使用

新しいファイルは .mjs で作成し、既存の .js は CommonJS のまま維持。

方法2: サブディレクトリごとに移行

移行済みのディレクトリに package.json を置いて “type”: “module” を設定。

互換性維持パターン

ESM と CJS の両方から使えるライブラリを公開する場合は、デュアルパッケージとして構成します。

{
  "name": "my-library",
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    }
  }
}

よくある問題と解決策

問題解決策
拡張子エラーimport に .js を追加
__dirname が未定義import.meta.url から導出
JSON インポートエラーwith { type: 'json' } または fs.readFileSync
CJS パッケージが動かないdefault インポートを確認

移行は一度に完了させる必要はありません。プロジェクトの状況に応じて、段階的に進めていくのが現実的です。