マイグレーション

Parcel 2はParcel 1とほぼ同様に動作しますが、アップグレード時に変更が必要な点がいくつかあります。

はじめに

#

Parcel 1からParcel 2へのアップグレードの基本的な手順をいくつか説明します。

パッケージ名

#

Parcel 1からParcel 2へのアップグレードで最初に注意すべき点は、npmパッケージ名が`parcel-bundler`から`parcel`に変更されたことです。`package.json`の依存関係をそれに応じて更新する必要があります。

package.json:
{
"devDependencies": {
"parcel-bundler": "^1.12.5"
}
}
package.json:
{
"devDependencies": {
"parcel": "^2.0.0"
}
}

npmまたはyarnなどのパッケージマネージャーを使用してこれを行うこともできます。

yarn remove parcel-bundler
yarn add parcel --dev

キャッシュの場所

#

Parcelキャッシュのデフォルトの場所は`.cache`から`.parcel-cache`に変更されました。これに対応するために`.gitignore`などを修正する必要があります。

.gitignore:
.cache
.gitignore:
.parcel-cache

コードの変更

#

<script type="module">

#

Parcel 1では、HTMLファイルの<script>タグから参照されるJavaScriptファイルはモジュールとして扱われ、値のインポートとエクスポートにESモジュールとCommonJSの両方の構文をサポートしていました。しかし、これはブラウザの実際の動作とは一致していません。「従来のスクリプト」はインポートとエクスポートをサポートせず、最上位の変数はグローバル変数として扱われます。

Parcel 2はブラウザの動作に合致しています。従来の<script>タグはインポートまたはエクスポートをサポートしません。モジュールを参照するには<script type="module">要素を使用します。これにより、`browserslist`に応じて、古いブラウザでも自動的に`nomodule`バージョンが生成されます。詳細は差分バンドルを参照してください。

index.html:
<!doctype html>
<html>
<head>
<script src="app.js"></script>
</head>
</html>
index.html:
<!doctype html>
<html>
<head>
<script type="module" src="app.js"></script>
</head>
</html>

注記: `type="module"`属性を追加すると、スクリプトの読み込み動作にも影響します。従来のスクリプトは「レンダリングブロッキング」であり、スクリプトの実行が完了するまでHTMLドキュメントの残りの部分が解析されません。モジュールのスクリプトはレンダリングブロッキングではなく、HTMLが完全に解析されるまで実行が遅延されます。Parcelは、古いブラウザや開発モードでこの動作に合わせるために、`defer`属性を自動的に挿入します。これは、`document.write`などの機能がモジュールのスクリプトでは機能しないことを意味します。これらの機能に依存している場合は、最新のAPIに移行するか、アプリのその部分に従来のスクリプトを引き続き使用してください。モジュールと従来のスクリプトの違いの詳細については、MDNのドキュメントを参照してください。

従来のスクリプトとモジュールのスクリプトの詳細については、従来のスクリプトを参照してください。

JavaScriptからの非コードアセットのインポート

#

Parcel 1では、画像やビデオなどのJavaScript以外のファイルをインポートすると、URLが生成されました。Parcel 2では、画像などの既知のファイルタイプではこれは引き続き機能しますが、デフォルトでサポートされていないその他のファイルタイプではコードの変更が必要です。

JavaScriptでURLを参照するための推奨アプローチは、URLコンストラクタを使用することです。ただし、`import`ステートメントの依存関係指定子の前に`url:`を付けることもできます。

index.js:
import downloadUrl from "./download.zip";

document.body.innerHTML = `<a href="${downloadUrl}">Download</a>`;
const downloadUrl = new URL('download.zip', import.meta.url);

document.body.innerHTML = `<a href="${downloadUrl}">Download</a>`;

あるいは、カスタム`.parcelrc`を使用して古い動作を選択することもできます。必要な拡張子にglobを使用して`@parcel/transformer-raw`プラグインを使用します。

yarn add @parcel/config-default @parcel/transformer-raw --dev
.parcelrc
{
"extends": "@parcel/config-default",
"transformers": {
"*.{zip,tgz}": ["@parcel/transformer-raw"]
}
}

トランスパイル

#

Parcel 1は、デフォルトのブラウザセットをサポートするためにJavaScriptを自動的にトランスパイルしていました。Parcel 2は、デフォルトではトランスパイルを実行しません。つまり、ソースコードで最新のJavaScript構文を使用する場合、Parcelはその構文を出力します。トランスパイルを有効にするには、`package.json`の`browserslist`フィールドを設定して、サポートするブラウザのターゲットを定義します。

package.json
{
"name": "my-project",
"browserslist": "> 0.5%, last 2 versions, not dead",
"scripts": {
"start": "parcel index.html",
"build": "parcel build index.html"
},
"devDependencies": {
"parcel": "latest"
}
}

Babel

#

Parcel 1と同様に、Parcel 2は`.babelrc`およびその他のBabel設定ファイルを自動的に検出します。ただし、`@babel/preset-env`、`@babel/preset-typescript`、および`@babel/preset-react`のみを使用している場合、Babelは不要になる可能性があります。Parcelは、Babelの設定なしでこれらの機能をすべて自動的にサポートしており、ParcelのデフォルトのトランスパイラはBabelよりもはるかに高速です。

上記のプリセットのみを使用する場合は、Babelの設定を完全に削除できます。これにより、代わりにParcelのデフォルトのトランスパイラが使用され、ビルドのパフォーマンスが大幅に向上します。`package.json`で`browserslist`を設定して、以前`@babel/preset-env`で使用されていたターゲットと一致させるようにしてください。

Babelの設定にカスタムプリセットまたはプラグインがある場合は、それらを保持できますが、上記でリストされているプリセットは削除してください。これにより、パフォーマンスも向上します(ただし、それほどではありません)。詳細については、JavaScriptのドキュメントのBabelを参照してください。

この例では、`.babelrc`には`@babel/preset-env`と`@babel/preset-react`のみが含まれているため、削除して`package.json`の`browserslist`キーに置き換えることができます。

.babelrc:
{
"presets": [
["@babel/preset-env", {
"targets": "> 0.25%, not dead"
}],
"@babel/preset-react"
]
}
package.json:
{
"browserslist": "> 0.25%, not dead"
}

TypeScript

#

Parcel 1は`tsc`(公式のTypeScriptコンパイラ)を使用してTypeScriptをトランスパイルしていました。Parcel 2は代わりにSWCを使用するようになり、トランスパイルのパフォーマンスが大幅に向上しました。

ただし、デフォルトのトランスパイラは`tsconfig.json`のサポートが限られています。JSX関連のオプションと`experimentalDecorators`以外のカスタムコンパイラオプションを使用する場合は、`@parcel/transformer-typescript-tsc`を使用してParcelのデフォルトのTypeScriptトランスフォーマーをTSCに置き換えることができます。これを行うには、デフォルトの設定とTSCプラグインをインストールし、プロジェクトのルートに`.parcelrc`ファイルを作成します。

yarn add @parcel/config-default @parcel/transformer-typescript-tsc --dev
.parcelrc
{
"extends": "@parcel/config-default",
"transformers": {
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
}
}

ParcelでのTypeScriptの使用の詳細については、TypeScriptドキュメントを参照してください。

Flow

#

Parcel 1と同様に、Parcel 2は`flow-bin`がインストールされている場合、Flowを自動的にサポートします。これは現在、`@babel/preset-flow`を使用して実装されています。Babelの設定にそのプリセットのみがある場合は、上記のように削除できます。

Parcel 1とは異なり、Babelの設定はParcel 2のデフォルトの設定をオーバーライドするものであり、マージされるものではありません。Flow以外のカスタムBabelプラグインがある場合は、`@babel/preset-flow`も追加する必要があります。

GraphQLのインポート

#

GraphQLファイル(`.gql`)をインポートする場合、インポートは引き続き解決/インライン化されます(`graphql-import-macro`を使用)、ただし、Apollo ASTではなく、処理されたGraphQLクエリが文字列として返されるようになります。

DataComponent.js:
import fetchDataQuery from "./fetchData.gql"; // fetchDataQuery is the parsed AST

const DataComponent = () => {
const { data } = useQuery(fetchDataQuery, {
fetchPolicy: "cache-and-network",
});

// ...
};
DataComponent.js:
import gql from "graphql-tag";
import fetchDataQuery from "./fetchData.gql"; // fetchDataQuery is a string

// Convert to the Apollo Specific Query AST
const parsedFetchDataQuery = gql(fetchDataQuery);

const DataComponent = () => {
const { data } = useQuery(parsedFetchDataQuery, {
fetchPolicy: "cache-and-network",
});

// ...
};

Parcel 2の新しいプラグインアーキテクチャでは、ビルド時に文字列をASTに解析するプラグイン(Parcel 1で行っていたように)を作成するのは非常に簡単です。詳細はトランスフォーマードキュメントを参照してください。

package.json#main

#

多くの`package.json`ファイル(たとえば、`npm init`によって生成されたファイル)には`main`フィールドが含まれていますが、これはほとんどのツール(ライブラリ以外のプロジェクトの場合)によって無視されます。ただし、`main`フィールドがあると、Parcelはプロジェクトがライブラリであると推測し、それを出力パスとして使用します。ほとんどのWebアプリでは、この行を削除する必要があります。

package.json:
{
"main": "index.js",
"scripts": {
"start": "parcel index.html",
"build": "parcel build index.html"
}
}
package.json:
{
"scripts": {
"start": "parcel index.html",
"build": "parcel build index.html"
}
}

`main`フィールドを保持する必要があり、Parcelでそれを無視する場合は、`package.json`に` "targets": { "main": false }`を追加できます。詳細はライブラリターゲットを参照してください。

CLI

#

--target

#

Parcel 1では、`--target` CLIオプションはコードがコンパイルされる環境を制御していました。Parcel 2では、これは代わりに`package.json`で設定されます。たとえば、`engines`フィールドに`node`または`electron`キーを含めることで、ターゲットがそれに応じて変更されます。

parcel build index.js --target node
package.json:
{
"engines": {
"node": "10"
}
}

Parcel 2では、複数のターゲットを同時にビルドすることもできます。詳細はターゲットを参照してください。

--experimental-scope-hoisting

#

Parcel 2では、スコープホーティングがデフォルトで有効になっています。無効にするには、`--no-scope-hoist`を追加します。

parcel build index.js --experimental-scope-hoisting
parcel build index.js
parcel build index.js
parcel build index.js --no-scope-hoist

--bundle-node-modules

#

Node.jsをターゲットとする場合に`node_modules`からパッケージをバンドルするには、ターゲットの設定で指定する必要があります。

parcel build index.js --target node --bundle-node-modules
package.json:
{
"targets": {
"default": {
"includeNodeModules": true
}
},
"engines": {
"node": "10"
}
}

このオプションはCLIパラメーターよりも汎用性があります。たとえば、パッケージを選択的に含めることもできます。詳細は、TargetsドキュメントのincludeNodeModulesを参照してください。

--out-dir

#

CLIオプションの--out-dirは、package.jsondistDirオプションと合わせるため、--dist-dirに名称変更されました。詳細はTargetsを参照してください。

parcel build index.html --out-dir www
parcel build index.html --dist-dir www

--out-file

#

CLIオプションの--out-fileは削除されました。パスはpackage.jsonで指定する必要があります。詳細はMultiple targetsLibrary targetsを参照してください。

parcel build index.js --out-file lib.js
package.json:
{
"name": "my-library",
"version": "1.0.0",
"main": "lib.js"
}

--log-level

#

ログレベルは、数値ではなく名前 (noneerrorwarninfoverbose) を使用するようになりました。

parcel build index.js --log-level 1
parcel build index.js --log-level error

--global

#

このオプションは(現時点では)代替オプションなしで削除されました。

parcel build index.js --global mylib

--no-minify

#

このオプションは--no-optimizeに名称変更されました。

parcel build index.js --no-minify
parcel build index.js --no-optimize

API

#

Parcel 2をプログラム的に使用するには、parcel-bundlerではなく@parcel/coreパッケージを使用します。APIは大幅に変更されています。詳細はParcel APIを参照してください。

バンドルイベントへのフック

#

Parcel 1では、APIを使用してbuildEndbuildErrorなどのイベントにフックしてリスンできました。APIは変更されましたが、次のようにイベントをリスンすることもできます。

index.js:
import Bundler from "parcel-bundler"

const bundler = new Bundler({ /* ... */ })
bundler.bundle()

bundler.on("buildEnd", () => { /* ... */ })
bundler.on("buildError", (error) => { /* ... */ })
import Parcel from "@parcel/core"

const bundler = new Parcel({ /* ... */ })

bundler.watch((err, buildEvent) => {
if (buildEvent.type === "buildSuccess") { /* ... */ }
if (buildEvent.type === "buildFailure") { /* ... */ }
})

プラグイン

#

Parcel 2ではプラグインシステムが完全に変更されました。Parcel 1のプラグインはParcel 2と互換性がありません。Parcel 2プラグインAPIの詳細はプラグインシステムを参照してください。

プラグインの使用

#

Parcel 1では、プロジェクトにプラグインをインストールし、package.jsonの依存関係にリストすることで、プラグインを自動的に有効化できました。Parcel 2では、プラグインは.parcelrcで設定されます。詳細はParcel設定を参照してください。