依存関係解決
Parcelがソースコードをビルドする際、それは依存関係を検出します。これにより、コードを別々のファイルに分割し、複数の場所で再利用することが可能になります。依存関係は、依存するコードを含むファイルの場所と、そのビルド方法に関するメタデータを示します。
依存関係指定子
#依存関係指定子とは、インポートするファイルからの相対的な依存関係の場所を示す文字列です。例えば、JavaScriptでは、import文またはrequire関数が依存関係の作成に使用されます。CSSでは、@importとurl()が使用される場合があります。通常、これらの依存関係は完全な絶対パスを指定するのではなく、Parcelやその他のツールによって絶対パスに解決される短い指定子を使用します。
Parcelは、Nodeモジュール解決アルゴリズムの拡張バージョンを実装しています。これは、依存関係指定子をファイルシステムからロードできる絶対パスに変換する役割を担います。多くのツールでサポートされている標準的な依存関係指定子に加えて、Parcelはいくつかの追加の指定子タイプと機能もサポートしています。
相対指定子
#相対指定子は.または..で始まり、インポートするファイルからの相対パスでファイルを解決します。
import './utils.js';
import '../constants.js'; 上記の例では、最初のインポートは/path/to/project/src/utils.jsに、2番目は/path/to/project/constants.jsに解決されます。
ファイル拡張子
#すべてのインポート指定子に完全なファイル拡張子を含めることをお勧めします。これにより、依存関係解決のパフォーマンスが向上し、あいまいさが減少します。
ただし、NodeのCommonJSとの互換性、およびTypeScriptとの互換性のために、Parcelは特定のファイルタイプについてはファイル拡張子を省略することを許可しています。省略できるファイル拡張子には、.ts、.tsx、.mjs、.js、.jsx、.cjs、および.jsonが含まれます。他のすべてのファイルタイプをインポートするには、ファイル拡張子が必須です。
次の例は、上記と同じファイルに解決されます。
import './utils';
import '../constants'; これらは、JavaScriptまたはTypeScriptファイルからのインポートの場合にのみ省略できることに注意してください。HTMLやCSSなどの他のファイルタイプで定義された依存関係には、常にファイル拡張子が必須です。
TypeScriptファイルでは、Parcelは.js、.jsx、.mjs、.cjsなどのJavaScript拡張子を、それらのTypeScript相当物(.ts、.tsx、.mts、.cts)に置き換えることも試みます。例えば、./foo.jsへの依存関係は./foo.tsに解決されます。これはTSCの動作と一致します。ただし、TSCとは異なり、元の./foo.jsが存在する場合は、TSバージョンではなく、元のファイルが使用されます。これはNodeや他のバンドラーの動作と一致します。
ディレクトリインデックスファイル
#JavaScript、TypeScript、その他のJSベースの言語では、依存関係指定子はファイルではなくディレクトリに解決される場合があります。ディレクトリにpackage.jsonファイルが含まれている場合、「パッケージエントリ」セクションで説明されているように、メインエントリが解決されます。package.jsonが存在しない場合、ディレクトリ内のインデックスファイル(index.jsまたはindex.tsなど)に解決しようとします。上記のすべての拡張子がインデックスファイルでサポートされています。
import './client'; 例えば、/path/to/project/src/clientがディレクトリの場合、上記の指定子は/path/to/project/src/client/index.jsに解決される可能性があります。
ベア指定子
#ベア指定子は.、/、~、または#以外の文字で始まります。JavaScript、TypeScript、およびその他のJSベースの言語では、それらはnode_modules内のパッケージに解決されます。HTMLやCSSなどの他のタイプのファイルでは、ベア指定子は相対指定子と同じように扱われます。
import 'react'; 上記の例では、reactは/path/to/project/node_modules/react/index.jsのようなものになる可能性があります。正確な場所は、node_modulesディレクトリの場所と、パッケージ内の設定によって異なります。
node_modulesディレクトリは、インポートするファイルから上方に検索されます。検索はプロジェクトルートディレクトリで停止します。例えば、インポートするファイルが/path/to/project/src/client/index.jsにあった場合、次の場所が検索されます。
/path/to/project/src/client/node_modules/react/path/to/project/src/node_modules/react/path/to/project/node_modules/react
モジュールディレクトリが見つかると、パッケージエントリが解決されます。パッケージエントリでこのプロセスの詳細を参照してください。
パッケージサブパス
#ベア指定子は、パッケージ内のサブパスも指定できます。例えば、パッケージは単一のエントリポイントではなく、複数のエントリポイントを公開する場合があります。
import 'lodash/clone'; 上記の例では、上記のようにnode_modulesディレクトリ内でlodashを解決し、そのメインエントリポイントではなく、パッケージ内のcloneモジュールを解決します。これは、例えばnode_modules/lodash/clone.jsファイルである可能性があります。
組み込みモジュール
#Parcelには、pathやurlなど、多くの組み込みNode.jsモジュールのシムが含まれています。依存関係指定子がこれらのモジュール名のいずれかを参照する場合、同じ名前でnode_modulesにインストールされているモジュールよりも、組み込みモジュールが優先されます。Node環境向けにビルドする場合、組み込みモジュールはバンドルから除外されます。それ以外の場合は、シムが含まれます。組み込みモジュールの完全なリストについては、Nodeドキュメントを参照してください。
Electron環境向けにビルドする場合、electronモジュールも組み込みと見なされ、バンドルから除外されます。
絶対指定子
#絶対指定子は/で始まり、プロジェクトルートからの相対パスでファイルを解決します。プロジェクトルートはプロジェクトのベースディレクトリであり、通常はパッケージマネージャーのロックファイル(yarn.lockやpackage-lock.jsonなど)、またはソース管理ディレクトリ(.gitなど)が含まれています。絶対指定子は、深くネストされた階層で非常に長い相対パスを回避するために役立ちます。
import '/src/client.js'; 上記の例は、プロジェクトのディレクトリ構造内の任意のファイル、任意の場所に配置でき、常に/path/to/project/src/client.jsに解決されます。
チルダ指定子
#チルダ指定子は~で始まり、インポートするファイルから最も近いパッケージルートからの相対パスで解決されます。パッケージルートとは、package.jsonファイルのあるディレクトリであり、通常はnode_modules内、またはモノレポのパッケージのルートにあります。チルダ指定子は、絶対指定子と同様の目的で使用されますが、複数のパッケージがある場合に特に便利です。
import '~/src/utils.js'; 上記の例は/path/to/project/packages/frontend/src/utils.jsに解決されます。
ハッシュ指定子
#ハッシュ指定子は#文字で始まり、その動作は含まれるファイルタイプによって異なります。JavaScriptおよびTypeScriptファイルでは、ハッシュ指定子は以下で説明する内部パッケージインポートとして扱われます。その他のファイルでは、これらは相対URLハッシュとして扱われます。
package.jsonの"imports"フィールドを使用して、パッケージ内のJavaScriptまたはTypeScriptファイルのインポート指定子に適用されるプライベートマッピングを定義できます。これにより、パッケージは環境に応じて条件付きインポートを定義できます。これは、下記とNode.jsドキュメントで説明されています。
{
"imports": {
"#dep": {
"node": "dep-node",
"browser": "dep-browser"
}
}
} import '#dep'; クエリパラメータ
#依存関係指定子には、解決されたファイルの変換オプションを指定するクエリパラメータを含めることもできます。たとえば、イメージをロードするときに幅と高さを指定してサイズ変更することができます。
.logo {
background: url(logo.png?width=400&height=400);
} イメージの詳細については、イメージトランスフォーマーのドキュメントを参照してください。カスタムトランスフォーマープラグインでもクエリパラメータを使用できます。
注記: クエリパラメータは、CommonJS指定子(require関数によって作成される)ではサポートされていません。
URLスキーム
#依存関係指定子は、URLスキームを使用して名前付きパイプラインをターゲットにすることができます。これにより、デフォルトのパイプラインとは異なるパイプラインでファイルをコンパイルするように指定できます。たとえば、bundle-text:スキームを使用して、コンパイルされたバンドルをテキストとしてインライン化できます。バンドルインライン化の詳細を参照してください。
名前付きパイプラインには使用できず、組み込みの動作を持つ予約済みのURLスキームがいくつかあります。
node:– 組み込みNodeモジュールを指定する別の方法です。組み込みモジュールを参照してください。npm:– URL依存関係(HTML、CSS、またはWebワーカーなど)がnode_modulesパッケージからファイルを取り込むための方法です。http:およびhttps:– 完全修飾URL依存関係。これらは実行時に解決され、Parcelによって変更されません。data:– 依存関係のソースコードをインラインで含むデータURL。現在Parcelでは実装されていませんが、将来の使用のために予約されています。file:– ファイルURL。将来の使用のために予約されています。mailto:およびtel:- 一般的に使用されるURLスキーム。これらはParcelによって変更されません。
Glob指定子
#ParcelはGlobを使用して一度に複数のファイルをインポートすることをサポートしますが、Globインポートは非標準であるため、デフォルトのParcel設定には含まれていません。有効にするには、@parcel/resolver-globを.parcelrcに追加します。
{
"extends": "@parcel/config-default",
"resolvers": ["@parcel/resolver-glob", "..."]
} 有効にすると、./files/*.jsのような指定子を使用して複数のファイルをインポートできます。これは、ファイル名に対応するキーを持つオブジェクトを返します。
import * as files from './files/*.js'; は次のものと同等です
import * as foo from './files/foo.js';
import * as bar from './files/bar.js';
let files = {
foo,
bar
}; 具体的には、globパターンの動的な部分はオブジェクトのキーになります。動的な部分が複数ある場合は、ネストされたオブジェクトが返されます。例えば、pages/profile/index.jsファイルが存在する場合、以下のようにマッチします。
import * as pages from './pages/*/*.js';
console.log(pages.profile.index); これは、bundle-text:のようなURLスキームにも、動的インポートにも有効です。動的インポートを使用する場合、結果のオブジェクトにはファイル名と関数のマッピングが含まれます。各関数は、解決されたモジュールをロードするために呼び出すことができます。つまり、各ファイルはすべて事前にロードするのではなく、オンデマンドでロードされます。
let files = import('./files/*.js');
async function doSomething() {
let foo = await files.foo();
let bar = await files.bar();
return foo + bar;
} Globはnpmパッケージからファイルを取り込むためにも使用できます。
import * as locales from '@company/pkg/i18n/*.js';
console.log(locales.en.message); GlobインポートはCSSでも機能します。
@import "./components/*.css"; は次のものと同等です
@import "./components/button.css";
@import "./components/dropdown.css"; パッケージエントリ
#パッケージディレクトリを解決する際、package.jsonファイルを参照してパッケージエントリを決定します。Parcelは次のフィールドを(順に)確認します。
source– モジュールがシンボリックリンクの背後にある場合(例:モノレポ内、またはnpm link経由)、Parcelはsourceフィールドを使用してソースからモジュールをコンパイルします。sourceフィールドは、パッケージに複数のエントリポイントがある場合のエイリアスマッピングとしても使用できます。詳細は下記のエイリアスを参照してください。exports– パッケージエクスポート、詳細は下記を参照してください。browser– パッケージのブラウザ固有バージョン。ブラウザ環境向けにビルドする場合、browserフィールドは他のフィールドよりも優先されます。browserフィールドは、パッケージに複数のエントリポイントがある場合のエイリアスマッピングとしても使用できます。詳細は下記のエイリアスを参照してください。module– パッケージのESモジュールバージョン。main– パッケージのCommonJSバージョン。
これらのフィールドのいずれも設定されていない場合、またはそれらが指すファイルが存在しない場合、解決はインデックスファイルにフォールバックします。詳細はディレクトリインデックスファイルを参照してください。
パッケージエクスポート
#package.jsonの"exports"フィールドを使用して、パッケージのパブリックにアクセス可能なエントリポイントを定義できます。これらは、環境に基づいて条件付きの動作を定義することもでき、モジュールがどこからインポートされるか(例:ノードまたはブラウザ)によって解決が変わるようにすることができます。
パッケージエクスポートの有効化
#パッケージエクスポートは、考慮されていない既存のプロジェクトを壊す可能性があるため、デフォルトで無効になっています。プロジェクトルートディレクトリのpackage.jsonファイルに以下を追加することで、サポートを有効にできます。
{
"@parcel/resolver-default": {
"packageExports": true
}
} 単一エクスポート
#パッケージにエクスポートされるモジュールが1つしかない場合、"exports"フィールドは文字列として定義できます。
{
"name": "foo",
"exports": "./dist/index.js"
} 上記の例でユーザーが"foo"パッケージをインポートすると、node_modules/foo/dist/index.jsモジュールが解決されます。
複数エクスポート
#パッケージが複数のモジュールをエクスポートする場合、"exports"フィールドは、これらの各エクスポートを見つける場所を定義するマッピングを提供できます。"."エクスポートはメインエントリポイントを定義し、他のエントリはサブパスとして定義されます。
{
"name": "foo",
"exports": {
".": "./dist/index.js",
"./bar": "./dist/bar.js"
}
} 上記のパッケージでは、ユーザーは"foo"をインポートできます(これはnode_modules/foo/dist/index.jsに解決されます)。または"foo/bar"をインポートできます(これはnode_modules/foo/dist/bar.jsに解決されます)。
"exports"フィールドを含むパッケージによって明示的にエクスポートされていないサブパスは、コンシューマーからアクセスできません。たとえば、上記のパッケージで"foo/abc"をインポートしようとすると、ビルド時のエラーが発生します。
*文字は、エクスポートマッピング内でワイルドカードとして使用できます。マッピングの左辺には1つの*しか表示できず、一致した文字列は右辺の各インスタンスに置き換えられます。これにより、エクスポートするすべてのファイルを個別にリストする必要がなくなります。
{
"name": "foo",
"exports": {
"./*": "./dist/*.js"
}
} 上記の例では、distディレクトリ内のすべての.jsファイルが、拡張子なしでパッケージからエクスポートされます。たとえば、"foo/bar"をインポートすると、node_modules/foo/dist/bar.jsに解決されます。
条件付きエクスポート
#"exports"フィールドは、異なる環境またはその他の条件で同じ指定子に対して異なるマッピングを定義することもできます。たとえば、パッケージはESモジュールとCommonJS、またはNodeとブラウザに対して異なるエントリポイントを提供できます。
{
"name": "foo",
"exports": {
"node": "./dist/node.js",
"default": "./dist/default.js"
}
} 上記の例では、コンシューマーがNode環境から"foo"モジュールをインポートした場合、node_modules/foo/dist/node.jsに解決されます。それ以外の場合は、node_modules/foo/default.jsに解決されます。
条件付きエクスポートは、サブパスマッピング内にネストすることもできます。
{
"name": "foo",
"exports": {
"./bar": {
"node": "./dist/bar-node.js",
"default": "./dist/bar-default.js"
}
}
} これにより、"foo/bar"をインポートして、Nodeとその他の環境で異なるファイルに解決できます。
条件付きエクスポートは、互いにネストしてより複雑なロジックを作成することもできます。
{
"name": "foo",
"exports": {
"node": {
"import": "./dist/node.mjs",
"require": "./dist/node.cjs"
},
"default": "./dist/default.js"
}
} 上記の例では、パッケージがNode環境内でESM importまたはCommonJS requireを介してロードされるかどうかによって、モジュールの異なるバージョンが定義されています。
Parcelは次のエクスポート条件をサポートしています。
"import"– パッケージはESMimport構文を使用して参照されました。"require"– パッケージはCommonJSrequire関数を使用して参照されました。"module"– パッケージはESMimport構文またはCommonJSrequire関数のいずれかから参照されました。"sass"– パッケージはSassスタイルシートから参照されました。"less"– パッケージはLessスタイルシートから参照されました。"stylus"– パッケージはStylusスタイルシートから参照されました。"style"– パッケージはスタイルシート(例:CSS、Sass、Stylusなど)から参照されました。"node"– 出力はNode環境で実行されます。"browser"– 出力はブラウザ環境で実行されます。"worker"– 出力はWebワーカーまたはサービスワーカー環境で実行されます。"worklet"– 出力はworklet環境で実行されます。"electron"– 出力はElectron環境で実行されます。"development"– パッケージは開発ビルドでロードされました。"production"– パッケージは本番ビルドでロードされました。"default"– 他の条件が一致しなかった場合のデフォルトの条件。
エクスポート条件が解決される順序は、上記にリストされている順序ではなく、package.jsonで定義されている順序に従います。
その他の例
#package.jsonエクスポートの詳細については、Node.jsドキュメントを参照してください。
エイリアス
#エイリアスを使用して、依存関係の通常の解決をオーバーライドできます。たとえば、API互換の異なる代替でモジュールをオーバーライドしたり、CDNからロードされたライブラリによって定義されたグローバル変数に依存関係をマッピングしたりする場合があります。
エイリアスは、package.jsonのaliasフィールドを介して定義されます。依存関係を含むソースファイルに最も近いpackage.jsonでローカルに定義することも、プロジェクトルートディレクトリのpackage.jsonでグローバルに定義することもできます。グローバルエイリアスは、node_modules内のファイルやパッケージを含む、プロジェクト内のすべてのファイルやパッケージに適用されます。
パッケージエイリアス
#パッケージエイリアスは、node_modules依存関係を異なるパッケージ、またはプロジェクト内のローカルファイルにマッピングします。たとえば、プロジェクト内のファイルとnode_modules内の他のライブラリの両方でreactとreact-domをPreactで置き換えるには、プロジェクトルートディレクトリのpackage.jsonにグローバルエイリアスを定義できます。
{
"alias": {
"react": "preact/compat",
"react-dom": "preact/compat"
}
} エイリアスが定義されているpackage.jsonからの相対パスを使用して、モジュールをプロジェクト内のファイルにマッピングすることもできます。
{
"alias": {
"react": "./my-react.js"
}
} モジュールの特定のサブパスのみをエイリアスすることもサポートされています。この例では、lodash/cloneをtiny-cloneにエイリアスします。lodashパッケージ内の他のインポートは影響を受けません。
{
"alias": {
"lodash/clone": "tiny-clone"
}
} これは逆にも機能します。モジュール全体がエイリアスされている場合、そのパッケージのサブパスインポートはすべて、エイリアスされたモジュール内で解決されます。たとえば、lodashをmy-lodashにエイリアスしてlodash/cloneをインポートした場合、これはmy-lodash/cloneに解決されます。
ファイルエイリアス
#エイリアスは、パッケージ内の特定のファイルを別のファイルで置き換える相対パスとして定義することもできます。これは、aliasフィールドを使用してファイルを無条件に置き換えるか、sourceまたはbrowserフィールドを使用して条件付きで置き換えることができます。これらのフィールドの詳細については、上記のパッケージエントリを参照してください。
たとえば、特定のファイルをブラウザ固有のバージョンで置き換えるには、browserフィールドを使用できます。
{
"browser": {
"./fs.js": "./fs-browser.js"
}
} これで、ブラウザ環境でmy-module/fs.jsがインポートされると、実際にはmy-module/fs-browser.jsが取得されます。これは、外部からのインポート(例:パッケージサブパス)と、モジュール内の内部の両方にも適用されます。
Globエイリアス
#ファイルエイリアスは、globを使用して定義することもできます。これにより、単一のパターンを使用して多くのファイルを置き換えることができます。置換には、キャプチャされたglobマッチにアクセスするための$1などのパターンを含めることができます。これは、aliasフィールドを使用してファイルを無条件に置き換えるか、sourceまたはbrowserフィールドを使用して条件付きで置き換えることができます。これらのフィールドの詳細については、上記のパッケージエントリを参照してください。
たとえば、sourceフィールドを使用して、パッケージ内のコンパイル済みコードと元のソースコード間のマッピングを提供できます。モジュールがシンボリックリンクされている場合、またはモノレポ内にある場合、これにより、Parcelは事前にコンパイルされたバージョンを使用するのではなく、ソースからモジュールをコンパイルできます。
{
"source": {
"./dist/*": "./src/$1"
}
} これで、distディレクトリ内のファイルがインポートされるたびに、代わりにsrcフォルダー内の対応するファイルがロードされます。
Shimエイリアス
#ファイルまたはパッケージは、ビルドから除外され、空のモジュールで置き換えられるようにfalseにエイリアスできます。これは、たとえばNode.jsでのみ動作する特定のモジュールをブラウザビルドから除外する場合に役立ちます。
{
"alias": {
"fs": false
}
} グローバルエイリアス
#ファイルまたはパッケージは、ランタイムで定義されるグローバル変数にエイリアスすることもできます。たとえば、特定のライブラリはCDNからロードできます。それをバンドルする代わりに、そのライブラリへの依存関係が解決されるたびに、バンドルされる代わりに、そのグローバル変数への参照で置き換えられます。
これは、globalプロパティを持つオブジェクトへのエイリアスを作成することで実現できます。次の例では、jquery依存関係指定子をグローバル変数$にエイリアスしています。
{
"alias": {
"jquery": {
"global": "$"
}
}
} TSConfig
#Parcelは、TypeScriptのtsconfig.json設定ファイルで定義されているいくつかの設定をサポートしています。これには、baseUrl、paths、moduleSuffixesが含まれます。Parcelは、依存関係を含むファイルから上方向に検索して、最も近いtsconfig.jsonファイルを見つけます。また、extendsオプションを使用して、複数のtsconfigの設定をマージすることもサポートしています。詳細は、TypeScriptドキュメントを参照してください。
baseUrl
#baseUrlフィールドは、ベア指定子を解決するためのベースディレクトリを定義します。
{
"compilerOptions": {
"baseUrl": "./src"
}
} import 'Home'; 上記の例では、Homeは存在する場合はsrc/Home.jsに解決されます。存在しない場合は、例えばnode_modules/Homeにフォールバックします。
paths
#pathsフィールドを使用して、ベア指定子からファイルパスへのマッピングを定義できます。*文字を使用してワイルドカードパターンを定義することもできます。
pathsフィールドで参照されるファイルパスは、baseUrlが定義されている場合はbaseUrlを基準に、そうでない場合はtsconfig.jsonファイルを含むディレクトリを基準にします。
{
"compilerOptions": {
"paths": {
"jquery": ["./vendor/jquery/dist/jquery"],
"app/*": ["./src/app/*"]
}
}
} import 'jquery';
import 'app/foo'; 上記の例では、jqueryは./vendor/jquery/dist/jquery.jsに、app/fooは./src/app/foo.jsに解決されます。
moduleSuffixes
#moduleSuffixesフィールドを使用すると、モジュールを解決する際に検索するファイル名のサフィックスを指定できます。
{
"compilerOptions": {
"moduleSuffixes": [".ios", ".native", ""]
}
} import './foo'; 上記の例では、Parcelは./foo.ios.ts、./foo.native.ts、./foo.ts(.tsx、.jsなどの他の拡張子の他に)を検索します。
その他のツールの設定
#このセクションでは、Nodeの解決アルゴリズムに対するParcelの拡張機能と連携するように、その他のツールを設定する方法について説明します。
TypeScript
#絶対パス指定子、チルダ依存関係指定子、エイリアスなどのParcel機能をサポートするには、TypeScriptの設定が必要です。これは、tsconfig.jsonのpathsオプションを使用して行うことができます。詳細は、TypeScriptモジュール解決ドキュメントを参照してください。
例えば、チルダパスをルートディレクトリにマップするには、この設定を使用できます。
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~*": ["./*"]
}
}
} URLスキームのサポートは、プロジェクトに環境モジュール宣言を作成することでも有効にできます。例えば、bundle-text:スキームで読み込まれた依存関係を文字列にマップするには、次の宣言を使用できます。これは、プロジェクト内の任意の場所にparcel.d.tsなどのファイルに配置できます。
declare module 'bundle-text:*' {
const value: string;
export default value;
} Flow
#絶対パス指定子、チルダ指定子、エイリアスをサポートするには、Flowの設定が必要です。これは、.flowconfigのmodule.name_mapper機能を使用して行うことができます。
例えば、絶対パス指定子をプロジェクトルートから解決するようにマップするには、この設定を使用できます。
[options]
module.name_mapper='^\/\(.*\)$' -> '<PROJECT_ROOT>/\1'
URLスキームを有効にするには、期待される型をエクスポートする.flow宣言ファイルへのマッピングを作成する必要があります。例えば、bundle-text:スキームで読み込まれた依存関係を文字列にマップするには、bundle-text.js.flowというファイルを作成し、そのスキームを参照するすべての依存関係をそれにマップできます。
// @flow
declare var value: string;
export default value; [options]
module.name_mapper='^bundle-text:.*$' -> '<PROJECT_ROOT>/bundle-text.js'