【JavaScript】拡張子とは?.js/.mjs/.cjsの違いを初心者向けにわかりやすく解説
はじめに
皆さんはJavaScriptファイルの拡張子について、どのように考えていますか。
多くの方は拡張子が「.js」であるファイルを見かけることがほとんどだと思います。 ところが、プロジェクトによっては「.mjs」や「.cjs」といった拡張子を使うケースがありますね。
こうした複数の拡張子を使い分けるのには、それぞれのモジュール形式や処理系に由来する理由があります。 実務の現場でも、チームの方針やプロジェクトの構成によって拡張子を使い分けることがよくあります。
本記事では、JavaScriptの代表的な拡張子「.js」「.mjs」「.cjs」の違いを解説し、実際の開発現場で役立つ具体的な使い方を紹介します。 初心者の方にもわかりやすい言葉を使いながら、なるべく具体例と結びつけてお伝えします。
この記事を読むとわかること
- JavaScript拡張子の種類と特徴
- 実務でどのように拡張子を使い分けるか
- 各拡張子を使ったコード例や活用シーン
- 開発環境やチームでの運用ポイント
ここからは順を追って、JavaScriptで利用される拡張子について掘り下げていきます。
JavaScriptにおける拡張子の概要
JavaScriptでは、主に「.js」「.mjs」「.cjs」という拡張子が使われます。 初心者の方でも一度は「.js」のファイルに触れたことがあるのではないでしょうか。
しかし、プロジェクトによっては「.mjs」や「.cjs」を使うケースも増えています。 これは「モジュール」の扱い方が関係しており、ブラウザやNode.jsなどで少しずつ扱い方が変化しているからです。
ここではざっくりとした概要を示しておきます。
.js
伝統的にJavaScriptファイルはこの拡張子を使います。
ES ModulesにもCommonJSにも利用される、最も一般的な拡張子です。
.mjs
ECMAScriptモジュール(ES Modules)を明確に指定したい場合に使用します。
import/exportを使うモダンな書き方と相性が良いです。
.cjs
CommonJSモジュールを明示するための拡張子です。
require()/module.exportsを中心に使うレガシー形式を示すことができます。
この違いを理解すると、チーム全体で拡張子に関する混乱を防ぎやすくなります。 また、特定のツールやランタイムを使うときに「この拡張子に限り自動でESモジュールを解釈してくれる」といった挙動が変わることもあるので、覚えておくと便利です。
.jsファイルの基本
.jsファイルの特徴
.jsファイルはJavaScriptを扱う上で最も一般的な拡張子です。 Webブラウザでスクリプトを読み込むときは、通常「.js」を指定します。 Node.jsでもデフォルトで「.js」を利用しており、昔から使われている伝統的な形式だといえます。
ただし「.js」拡張子であっても、ES Modulesとして書かれているのかCommonJSとして書かれているのかは、ファイル内部の記述や設定によって決まります。
ブラウザ側ではscriptタグにtype="module"
を付与して読み込むことで、ES Modulesとして扱われるケースがあります。
一方、Node.jsの世界では設定ファイル(たとえば package.json の "type" フィールド)の指定によって、ファイル拡張子「.js」がES Modulesとして解釈されるのか、CommonJSとして解釈されるのかが変わることがあります。
.jsファイルの実務での活用例
フロントエンド開発 (ブラウザ向け)
HTMLの中で<script src="script.js"></script>
のように読み込むことで、DOM操作やイベント処理などを行います。
ES Modulesとして読み込みたい場合は、<script type="module" src="script.js"></script>
のように書くことが多いでしょう。
Node.jsを使ったサーバーサイド開発
Node.jsではrequire()を使うCommonJS形式が長らく標準でした。
しかし、モダンなES Modules形式を使う場合も、拡張子を「.js」のままにしておいて、package.jsonの "type" を "module" に設定するだけでOKな場合があります。
ビルドツールでのまとめ
BabelやWebpackなどのビルドツールを用いる場合、最終的に「.js」という1つのファイルにまとめることがあります。
拡張子は一律「.js」になることが多いですが、モジュール構成をビルド段階で解決してくれるため、開発者はあまり拡張子を意識せずに書ける利点もあります。
.mjsファイルの基本
.mjsファイルの特徴
.mjsファイルは、特にES Modulesを明示的に利用するときに使われる拡張子です。 Node.jsでは、package.jsonの "type" が "commonjs" になっている場合にES Modulesを使いたいとき、ファイルを「.mjs」にしておけばES Modulesとして認識してくれます。
たとえば、import fs from 'fs'
のようなモダンな構文を使いたい場合、拡張子を「.mjs」としておけば、自動でES Modulesとして解釈されます。
逆に、拡張子が「.js」のままだと、"type" フィールドが "module" でない限りES Modulesは動作しないケースがあります。
.mjsファイルの実務での活用例
Node.jsでES Modulesを使用するプロジェクト
CommonJSとES Modulesを混在させる必要があるとき、"type": "commonjs"
と指定しつつ一部ファイルをES Modulesで書きたい場合に、「.mjs」を付ける方法は実務でもよく見られます。
ES Modules形式を明確に区別したい場合
チームで「このファイルはES Modulesだよ」と拡張子でわかるようにしたいときに「.mjs」を積極的に採用することがあります。
シンプルに「この拡張子がついているならimport/export文が書かれている」と即座に理解できる点はメリットです。
ブラウザとの連携
ブラウザでスクリプトを読み込むときは、拡張子自体はあまり重要ではありません。
とはいえ「.mjs」を使うことで「これはES Modules形式のスクリプトですよ」という意図がはっきりする場合もあり、プロジェクトの管理がしやすくなることがあります。
.mjsファイルを使ったコード例
下記はNode.jsでES Modulesを明確に使う場合のサンプルです。
拡張子を「.mjs」にし、ファイル名を app.mjs
とします。
// app.mjs import fs from "fs"; const data = "Hello from .mjs!"; fs.writeFileSync("message.txt", data); console.log("ファイルに書き込みが完了しました。");
ここでは import fs from "fs";
のようなES Modulesの構文を使っています。
Node.js側で "type" を "commonjs" にしていても、拡張子が「.mjs」ならばES Modulesとして動作することが多いです。
.cjsファイルの基本
.cjsファイルの特徴
.cjsファイルは、CommonJS形式を強制したい場合に用いられる拡張子です。 もしプロジェクト全体が "type": "module" となっていると、拡張子「.js」のファイルはES Modulesとして扱われます。 しかし、一部のファイルだけどうしてもCommonJS形式を使いたい場面があります。
その場合、拡張子を「.cjs」にしておくと、Node.jsがそのファイルを強制的にCommonJSとして読み込むようにしてくれます。
.cjsファイルの実務での活用例
一部ライブラリがCommonJSのみに対応している
実務では古いライブラリや自作のツールなどがCommonJS形式しか提供していない場合があるかもしれません。
そういったライブラリと共存するために、特定の処理だけCommonJSで書く必要が出てきます。
ビルドツールの設定ファイル
一部のビルドツールでは、設定ファイルをCommonJS形式で書かなければいけないことがあります。
例として、Node.js製のツールの設定ファイルがCommonJS想定だったりすると、.cjs 形式を指定しておくとトラブルを回避できます。
レガシーコードの部分的再利用
過去のコードをすべて書き換えるには時間がかかることが多いです。
新しいプロジェクトをES Modulesベースにしたいが、一部だけ既存のCommonJSファイルを再利用したい場合に「.cjs」ファイルを導入することで、徐々に移行が進めやすくなります。
.cjsファイルを使ったコード例
下記は app.cjs
というファイル名で、CommonJSの書き方をしています。
// app.cjs const fs = require("fs"); const data = "Hello from .cjs!"; fs.writeFileSync("message-cjs.txt", data); console.log("ファイルに書き込みが完了しました。");
require()
を使ってモジュールを読み込んでいるのが、CommonJS形式の大きな特徴です。
実務での使い分けポイント
チームでの合意形成
複数人で開発する場合、拡張子が混在すると混乱が生まれやすいです。 特に初心者が多い環境では「.js」「.mjs」「.cjs」の使い分けがわかりにくくなることもあるでしょう。
そのため、プロジェクト開始時に「なるべく. js と .mjs どちらを使うのか」「CommonJSはどう扱うのか」といった方針を決めておくとスムーズです。 チームで「すべて. js で書く。ただしES Modulesで統一する」「どうしてもCommonJSが必要な箇所だけ. cjs にする」など、明確に取り決めることをおすすめします。
package.jsonのtype設定
Node.jsでは package.json に "type"
フィールドを設定することで、デフォルトのモジュールタイプを指定できます。
"type": "module"
と書くと、拡張子が「.js」でもES Modulesとして扱う"type": "commonjs"
と書くと、拡張子が「.js」はCommonJSとして扱う
このような挙動をベースにして、例外的に「.mjs」や「.cjs」を使えば、特定のファイルだけ異なるモジュール方式に切り替えることが可能です。 チーム開発では、この設定と拡張子の使い方をしっかり共有しておきましょう。
ブラウザ向けとNode.js向けの違い
ブラウザ向けの開発では、拡張子はそれほど厳密に扱われない場合があります。
scriptタグに type="module"
を付与したり、ビルドツールがトランスパイルやバンドルを行うため、ローカルファイルの拡張子がどうであれ、最終的な出力が「.js」ファイルになることが多いです。
一方、Node.jsでは拡張子が直接モジュール解析に影響を与えることもあるため、より意識して設定しなければいけません。 特にサーバーサイド開発では、複数のツールやライブラリが混在するため、どこでCommonJSなのか、どこでES Modulesなのかを意識しておく必要があります。
バンドラやトランスパイラの設定
WebpackやRollup、Viteなどのビルドツールを使うときは、内部的に「ファイル拡張子」や「package.jsonのtype」をもとにモジュールを解釈する場合があります。 トランスパイル結果がどうなるか、バンドラがどう解釈するかはツールごとに細かな違いがあるので、プロジェクトの設定ファイルをよく確認しましょう。
ただし、初心者向けにそこまで深く突っ込むのは難しいこともあります。 実際には「ビルドツールがうまく動くように設定ファイルを整備する」「拡張子をプロジェクト方針に従う」という2点を押さえておけば困ることは少ないです。
拡張子ごとのメリット・デメリット
.js のメリット・デメリット
メリット
- 伝統的かつ最も一般的な拡張子なので、初心者にもわかりやすい
- ブラウザ、Node.jsともに対応しており、あまり意識せずに使える
デメリット
- そのままだとES ModulesなのかCommonJSなのかわかりにくい
- Node.jsでモジュール形式を切り替える際に、package.jsonのtype設定が必要になることもある
.mjs のメリット・デメリット
メリット
- 拡張子を見れば「これはES Modulesだな」とひと目でわかる
- Node.jsで "type": "commonjs" の場合でも、ES Modulesを使える
デメリット
- チーム内で. js と .mjs が混在すると混乱が生まれやすい
- CommonJSしか理解していない初心者にとっては、新しい拡張子というだけで戸惑うケースもある
.cjs のメリット・デメリット
メリット
- Node.jsで "type": "module" にしていても、ここだけCommonJSが使える
- レガシーなモジュールやライブラリを部分的に運用しやすい
デメリット
- 基本的にCommonJSをもうあまり使わないチームでは存在意義が薄い
- ES Modulesが主流になりつつある中で、拡張子が増えると学習コストが高まる
実務に役立つ運用上の注意点
拡張子の混在を最小限にする
可能であれば、プロジェクト全体の方針をES Modulesで統一してしまい、「.js」と「.mjs」のいずれか1種類に絞り込むのも手です。 CommonJSが必要なライブラリだけ「.cjs」で書くなど、例外を明確に定義しておくとトラブルが減ります。
LinterやFormatterの設定を整える
ESLintやPrettierなどを使う場合、拡張子ごとに設定を変えなければいけないことがあります。 たとえば「.mjs」ファイルもES Modulesとしてチェックさせるようにするなど、チーム内でルールを統一しておきましょう。
バージョン管理システムにおけるディレクトリ構造
実際の開発現場では、フォルダ構成やバージョン管理の方針と組み合わせて、「拡張子をどう使い分けるか」が決まる場合があります。
たとえば、src
ディレクトリ以下はES Modules(.js/.mjs)、scripts
ディレクトリはUtilityスクリプト(CommonJS形式)など、わかりやすくまとめると混乱を減らせます。
自動テスト時の注意
JestやMochaなどのテストフレームワークは、内部で特定のモジュール解釈を行っている場合があります。 チームのルールで「.js はES Modules」としているのに、テストフレームワークがCommonJSを想定しているケースがあり得るため、設定ファイルを確認しておきましょう。
テストやビルド時にエラーが発生した場合、まずは拡張子やモジュール形式の設定を疑ってみるとよいです。
具体的なコード例:複数拡張子を混在させる場合
ここでは「.js」「.mjs」「.cjs」を混在させるプロジェクトの一例を紹介します。 ディレクトリ構造は以下のようにしてみます。
my-project ┣ package.json ┣ index.js ┣ module.mjs ┗ legacy.cjs
package.json
{ "name": "my-project", "type": "module", "scripts": { "start": "node index.js" } }
"type": "module"
としているので、拡張子が「.js」のファイルはES Modulesとして扱われます。
しかし、CommonJS形式のファイルが必要であれば「.cjs」を使うことで強制的にCommonJS扱いができます。
index.js(ES Modulesとして扱う)
// index.js import { greet } from "./module.mjs"; import legacy from "./legacy.cjs"; console.log(greet("ES Modules")); console.log("legacy.cjsからの戻り値:", legacy);
ここでは import
文を使い、legacy.cjs
を読み込んでいます。
Node.jsは、legacy.cjs
をCommonJSとして解釈してくれるため、問題なく呼び出すことができます。
module.mjs(ES Modulesであることを明示)
// module.mjs export function greet(name) { return `こんにちは、${name}の世界!`; }
.mjs
拡張子なので、当然ES Modulesの構文を問題なく使えます。
legacy.cjs(CommonJSとして強制的に扱う)
// legacy.cjs module.exports = "これはCommonJSのエクスポートです。";
require
ではなく module.exports
しか書いていませんが、Node.jsはこのファイルをCommonJSとして読み込みます。
import legacy from "./legacy.cjs";
の呼び出し側はES Modulesで書かれていますが、Node.jsが内部的に変換してくれるため、スムーズに利用できるのです。
デバッグ時のポイント
JavaScriptの拡張子に関連した不具合が出た場合、エラーメッセージが「Unexpected token 'import'」や「Cannot use import statement outside a module」などと表示されることがあります。
- ES Modulesとして読み込みたいのに、拡張子が「.js」で "type": "commonjs" になっている
- CommonJSとして読み込みたいのに、拡張子が「.js」だけど "type": "module" になっている
こうした設定の食い違いが原因であることが多いです。 エラー内容に「import文が許可されていない」「requireは使えない」といった記述があれば、拡張子や "type" フィールドの設定を見直すと解決する場合がよくあります。
この記事のポイントを活用するシーン
新規プロジェクトの設計
新しくNode.jsのプロジェクトを始めるときに、どの拡張子を使うか最初に決めておくと混乱を減らせます。
既存プロジェクトのリファクタリング
CommonJSとES Modulesが混在しているケースでは、適切な拡張子を使い分けることでコードの可読性を向上させられます。
ライブラリ開発
ライブラリ開発者は、配布物をCommonJS版とES Modules版の両方提供する場合があります。
その際に、拡張子の扱いを明確にしておけば、利用者が混乱しにくくなります。
まとめ
JavaScriptの拡張子「.js」「.mjs」「.cjs」は、モジュール形式を明確に示したり、開発環境やツールの動きを制御したりする大切な要素です。
.js
最も一般的で、ブラウザでもNode.jsでもそのまま使える。
ただし、ES ModulesとCommonJSが混在するときは、設定ファイルやチーム方針を合わせて管理する必要がある。
.mjs
ES Modulesであることをはっきり示す拡張子。
"type": "commonjs" にしていてもES Modulesを利用しやすいというメリットがある。
.cjs
CommonJSであることをはっきり示す拡張子。
レガシーなモジュールや一部ライブラリを使う場合に、確実にCommonJSを維持したいときに有効。
実務では、最初にチームやプロジェクト全体で拡張子の運用ルールを決めておけば、混乱やトラブルを大きく減らせます。 皆さんもぜひ、プロジェクトのルール設定やエラー対応の際に、本記事の内容を参考にしてみてください。