🚚 Migration

Some tips for upgrading from Parcel 1 to Parcel 2

For the most part, you shouldn't have to change much when upgrading to Parcel 2:

ΒΆ Code Changes

ΒΆ Importing non-code assets from Javascript

If you want import the url to an image (or a soundfile, etc.) from Javascript, you need to prepend url: to the module specifier (read more about named pipelines in Plugin Configuration)

index.js:
import logo from "./logo.svg";

document.body.innerHTML = `<img src="${logo}">`;
import logo from "url:./logo.svg";

document.body.innerHTML = `<img src="${logo}">`;

Alternatively, you can use a custom .parcelrc to opt into the old behaviour (add more asset extensions if you use them):

.parcelrc:
{
"extends": "@parcel/config-default",
"transformers": {
"*.{jpg,png,svg}": ["@parcel/transformer-raw"]
}
}

ΒΆ Typescript

Parcel 1 transpiled TypeScript using tsc (the official TypeScript compiler). Parcel 2 instead uses Babel (using @babel/preset-env) by default. This has two notable consequences:

(The TypeScript page contains more informations - and limitations - of Parcel's TypeScript handling.)

ΒΆ @babel/preset-typescript Isn't inserted Automatically into a Custom .babelrc.

For most use cases, transpiling using Babel is enough, so Parcel includes @babel/preset-typescript in its default Babel config for TypeScript assets. You need to specify it manually however if you are using a custom .babelrc:

.babelrc:
{
"presets": ["@babel/preset-env"],
"plugins": ["babel-plugin-foo"]
}
.babelrc:
{
"presets": ["@babel/preset-env", "@babel/preset-typescript"],
"plugins": ["babel-plugin-foo"]
}
ΒΆ Babel Doesn't Read tsconfig.json

In case Babel doesn't work for you (e.g. because of an advanced tsconfig.json), you can use tsc:

.parcelrc:
{
"extends": "@parcel/config-default",
"transformers": {
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
}
}
This is expected to be slightly slower for large builds/assets, so transpiling using Babel is the default approach.

ΒΆ Importing GraphQL

When import GraphQL files (.gql), imports are still resolved/inlined (using graphql-import-macro), but you now get the processed GraphQL query as a string instead of an Apollo AST.

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

const DataComponent = () => {
const { data } = useQuery(test, {
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",
});

// ...
};
With Parcel 2's new plugin architecture, creating a plugin that parses the string into an AST at build time (as Parcel 1 did) is very easy.

ΒΆ Hooking into Bundle Events

Parcel 1 let you hook in and listen to events like buildEnd or buildError. The API has changed but you can still listen for events like so:

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") { /* ... */ }
})

ΒΆ Configuration/CLI

ΒΆ Transpilation

By default, Parcel 2 doesn't downlevel your source code anymore. If you want this to happen, you should specify a browserslist to only target the browsers you want to support.

ΒΆ package.json#main

Many package.jsons (e.g. the one generated by npm init) contain main: "index.js" which is ignored by most tools (for non-library projects). Parcel 2 will however use that value as the output path; for most web apps, this line should simply be removed. If you really do need it, you can add "targets": { "main": false } to your package.json. See Configuration#main

ΒΆ --target

This CLI flag is now inferred from your package.json, one of these three properties is enough (number denotes priority).

parcel build index.js --target node
package.json:
{
"targets": {
"default": {
"context": "node", // <--- (1)
"engines": {
"node": "10", // <------ (2)
},
},
},
"engines": {
"node": "10", // <---------- (3)
},
}

ΒΆ --experimental-scope-hoisting

Parcel 2 has scope hoisting enabled by default; to disable it, add --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

To bundle packages from node_modules when targetting Node.js, you now should specify that in the target configuration:

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

This option is more versatile that the CLI parameter (you can also selectively include packages), see Configuration#includeNodeModules for all details.

ΒΆ --out-dir

To align --out-dir with the options in package.json#targets, that option was renamed to --dist-dir.

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

ΒΆ --out-file

This flag, was removed and the path should instead be be specified in package.json (see Configuration).

parcel build index.js --out-file lib.js
package.json:
{
"default": "lib.js",
// ...
}

ΒΆ --log-level

The log levels now have names instead of numbers (none, error, warn, info, verbose)

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

ΒΆ --global

This option has been removed without a replacement (for now).

parcel build index.js --global mylib

ΒΆ --no-minify

This option has been renamed to --no-optimize

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