提交 d2dc7c96 编写于 作者: J Jamie 提交者: Devon Govett

Parcel 2 RFC (#1952)

上级 e86ddf45
# Parcel 2 RFC
This RFC is written as documentation for the project, our documentation should
be detailed enough to work as a spec, and it's easier to write it once this way
than to approve a spec and start again on the documentation from scratch.
Everything in Parcel should be documented here, if it is not documented it will
not be part of Parcel 2.
---
## Introduction
Parcel is a compiler for all your code, regardless of the language or toolchain.
Parcel takes all of your files and dependencies, transforms them, and merges
them together into a smaller set of output files that can be used to run your
code.
Parcel supports many different languages and file types out of the box, from
web technologies like HTML, CSS, and JavaScript, to lower level languages like
Rust, and anything that compiles to WebAssembly (WASM), to assets like images,
fonts, videos, and more.
Parcel makes your code portable, you can build your code for different
environments, for the web for your server, or for an app. You can even build
multiple targets at once and have them live update as you make changes.
Parcel is fast and predictable. It compiles all of your files in isolation in
parallel inside workers, caching all of them as it goes along. Caches are
stable across machines and are only affected by the files and configs within
your project (unless you want to pass specific environment variables).
## Getting Started
Before we get started, you'll need to install Node and Yarn (or npm) and create
a `package.json` for your project if you haven't already.
```sh
yarn init
```
Then with Yarn you can install `parcel` into your app:
```sh
yarn add --dev parcel
```
From there you just need to point Parcel at some of your entry files. Like if
you're building a website, an `index.html` file:
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>My First Parcel App</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
```
Now if you just run:
```sh
yarn parcel --serve
```
You should get a URL that looks something like: `http://localhost:1234/`
Next you can start adding dependencies by specifying them in your code (however
your language specifies other assets). So for HTML we could create a
`styles.css` file next to our `index.html` file and include it with a `<link>`
tag.
```css
h1 {
color: hotpink;
font-family: cursive;
}
```
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>My First Parcel App</title>
<link rel="stylesheet" href="./styles.css"/>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
```
As we make the change you should see the website update with your changes
without even refreshing the page.
## Parcel CLI
The Parcel CLI is built into the main `parcel` package. While you can install
it globally and run it, it is much better to install it locally into your
project as a dev dependency.
```sh
yarn add --dev parcel
```
You should also add some "scripts" to your `package.json` to run it easier.
```json
{
"name": "my-project",
"scripts": {
"build": "parcel --production",
"start": "parcel --serve"
},
"devDependencies": {
"parcel": "latest"
}
}
```
Now you can run `yarn build` to bundle your project for production and
`yarn start` to dev on your project.
### CLI Args & Flags
Usage:
```sh
$ parcel [...entries] [...flags]
```
#### `[...entries]`
Entry files to start bundling, these will be preserved as entry points in the
output. Defaults to `package.json#source`, falling back to `src/index.*` or
`index.*`. See [#Entries](#entries-sources-targets-environment).
#### `--serve, -s`
Serve assets on a local server
#### `--watch, -w`
Watch and rebuild code on file changes.
#### `--open, -o [browser]`
Open your local server in a browser. You can optionally pass the name of the
browser you want to open, otherwise it will use your default browser.
#### `--port <port>`
Configure the port to serve assets on. Alternatively you can use the `$PORT`
environment variable.
#### `--https`
This will generate a local certificate (which will be untrusted by your
browser, you'll need to approve it) and serve your assets over `https://`
##### `--cert <path>`
Specify the filepath to your SSL certificate when using `--https`.
##### `--key <path>`
Specify the filepath to your SSL key when using `--https`.
#### `--cache <dir>`, `--no-cache`
Configure the cache directory with `--cache <dir>` or disable it altogether
with `--no-cache`.
#### `--hot`, `--no-hot`
Turn hot reloading on or off.
##### `--hot-hostname <hostname>`
Configure the hot reloading hostname.
##### `--hot-port <port>`
Configure the hot reloading port.
#### `--[no-]source-maps`
Turn source maps on or off. Source maps are turned on by default except in
production builds.
#### `--autoinstall [npm/yarn], --no-autoinstall`
When enabled, whenever Parcel discovers a dependency that isn't installed it
will attempt to install it with either npm or Yarn (defaults to npm unless a
`yarn.lock` exists).
#### `--mode <mode>`
Override the environment mode, including those manually configured to something
else (ex. `--mode production` or `--mode development`). Defaults to the
`NODE_ENV` environment variable.
##### `--development, --dev`
Aliases for `--mode development`.
##### `--production, --prod`
Aliases for `--mode production`.
##### `--test`
Aliases for `--mode test`.
#### `--public-url <url>`
[todo]
#### `--log-level <level>`
Set the log level, either "0" (no output), "1" (errors), "2" (warnings +
errors) or "3" (all). (Default: 2)
#### `--version, -v, -V`
Return the current version of Parcel.
#### `--help, -h`
Get help with the CLI.
## Parcel Config
Parcel has always and will always work out of the box for many projects with
zero configuration. It should always be extremely simple to get started. But if
you do want more control, we give you the tools to do so.
### Configuring external tools
A huge part of what Parcel does is run other tools over your code. Instead of
pulling all that configuration into Parcel, we make use of their own
configuration systems. So if you're using Babel, you should just use `.babelrc`
files to configure it.
When we do need to introduce config, we create tool specific config files in
order to do so.
### Configuring Parcel
When you do need to configure Parcel, it will be in one of 3 places.
- If you need to configure the CLI, it will be a CLI flag
- If you need to configure your package, it will be in the `package.json`
- If you need to configure something with your files or the Parcel asset
pipeline, it will be in `.parcelrc`
### `package.json`
[todo]
```json
{
"name": "foo",
"main": "dist/main/index.js",
"module": "dist/module/index.js",
"browser": "dist/browser/index.js",
"browserslist": ["> 1%", "not dead"],
"engines": {
"node": ">=4.x"
},
"source": "src/index.js",
"targets": {
"main": {
"node": ["^4.0.0"]
},
"module": {
"node": ["^8.0.0"]
},
"browser": {
"browsers": ["> 1%", "not dead"]
}
},
"alias": {
"react": "preact-compat",
"react-dom": "preact-compat"
}
}
```
#### `package.json#name`
**(Required)** The name of the package is always required in order to be
considered a valid `package.json`.
```json
{
"name": "my-package"
}
```
#### `package.json#version`
**(Required)** All packages inside `node_modules` must have a `package.json#version`.
```json
{
"version": "1.0.0"
}
```
#### `package.json#main`
**(Required)** This is the "main" target's entry point for the package.
```json
{
"main": "dist/main/index.js"
}
```
See [Targets](#targets)
#### `package.json#module`
This is the "module" target's entry point for the package.
```json
{
"module": "dist/module/index.js"
}
```
See [Targets](#targets)
#### `package.json#browser`
This is the "browser" target's entry point for the package.
```json
{
"browser": "distflinesof/browser/index.js"
}
```
See [Targets](#targets)
#### `package.json#source`
Specify the entry points for your source code which gets mapped to your
targets.
```json
{
"source": "src/index.js",
"source": ["src/index.js", "src/index.html"]
}
```
See [Sources](#sources)
#### `package.json#browserslist`
As specified by Browserslist, this field is for specifying which transforms
should be applied to browser bundles.
```json
{
"browserslist": ["> 0.2%", "not dead"]
}
```
See [Environments](#environments)
#### `package.json#engines`
Specify what versions of what engines you want to support.
```json
{
"engines": {
"node": ">=4.x",
"electron": ">=2.x"
}
}
```
See [Environments](#environments)
#### `package.json#targets`
Configuration for individual targets.
```json
{
"targets": {
"main": {
"engines": {
"node": ">=4.x",
"electron": ">=2.x"
},
"browsers": ["> 1%", "not dead"]
}
}
}
```
See [Targets](#targets)
#### `package.json#alias`
Aliases asset names/paths to other assets.
```json
{
"alias": {
"react": "preact-compat",
"react-dom": "preact-compat"
}
}
```
See [Aliases](#aliases)
### `.parcelrc`
Your `.parcelrc` file will likely contain just a few fields (if you have one at
all), but here's an example of a `.parcelrc` file that contains every field:
```json
{
"extends": ["@parcel/config-default"],
"resolvers": ["@parcel/resolver-default"],
"bundler": "@parcel/bundler-default",
"transforms": {
"*.vue": ["@parcel/transform-vue"],
"*.scss": ["@parcel/transform-sass"],
"*.js": ["@parcel/transform-babel"],
"*.css": ["@parcel/transform-postcss"],
"*.html": ["@parcel/transform-posthtml"],
},
"packagers": {
"*.js": "@parcel/packager-js",
"*.css": "@parcel/packager-css",
"*.html": "@parcel/packager-html",
"*.wasm": "@parcel/packager-wasm",
"*.raw": "@parcel/packager-raw"
},
"optimizers": {
"*.js": ["@parcel/optimizer-uglify"],
"*.css": ["@parcel/optimizer-cssnano"],
"*.html": ["@parcel/optimizer-htmlnano"],
"*.{png,jpg,jpeg,svg,...}": ["@parcel/optimizer-imagemin"]
},
"loaders": {
"*.js": "@parcel/loader-js",
"*.wasm": "@parcel/loader-wasm"
},
"reporters": ["@parcel/reporter-detailed"]
}
```
#### Glob maps in `.parcelrc`
Many config properties like `transforms` or `packagers` use objects as maps of
globs to package names. While objects in JSON are technically unordered, Parcel
does use the order to give globs priority when a file name is being tested
against them.
```json
{
"transforms": {
"icons/*.svg": ["highest-priority"],
"*.svg": ["lowest-priority"]
}
}
```
Here if we are trying to find a transform for the file `icons/home.svg`, we'll
work our way down the globs until we find a match, which would be
`icons/*.svg`, we never reach `*.svg`.
#### `.parcelrc#extends`
`extends` can either be a string or an array of strings that specify base
configs to extend. That base configuration can be the path to another
`.parcelrc` file or the name of a Parcel config package.
```json
{
"extends": "@parcel/config-default",
"extends": "../.parcelrc",
"extends": ["@parcel/config-default", "@company/parcel-config"]
}
```
When extending a config, Parcel does a shallow merge of the two configs.
#### `.parcelrc#resolvers`
`resolvers` is an array of strings that specifies the name of a Parcel resolver
package.
```json
{
"resolvers": ["@parcel/resolver-default"]
}
```
See [Resolvers](#resolvers)
#### `.parcelrc#transforms`
`transforms` is an object map of globs to arrays of Parcel transform packages.
```json
{
"transforms": {
"*.js": ["@parcel/transform-babel"]
}
}
```
See [Transforms](#transforms)
#### `.parcelrc#bundler`
`bundler` is a string that specifies the name of a Parcel bundler package.
```json
{
"bundler": "@parcel/bundler-v1"
}
```
See [Bundlers](#bundlers)
#### `.parcelrc#packagers`
`packagers` is an object map of globs to Parcel packager packages.
```json
{
"packagers": {
"*.js": ["@parcel/packager-js"]
}
}
```
See [Packagers](#packagers)
#### `.parcelrc#optimizers`
`optimizers` is an object map of globs to arrays of Parcel optimizer packages.
```json
{
"optimizers": {
"*.js": ["@parcel/optimizer-uglify"]
}
}
```
See [Optimizers](#optimizers)
#### `.parcelrc#loaders`
`loaders` is an object map of globs to Parcel loader packages. See
[Loaders](#).
```json
{
"loaders": {
"*.js": "@parcel/loader-js",
"*.wasm": "@parcel/loader-wasm"
}
}
```
See [Loaders](#loaders)
#### `.parcelrc#reporters`
`reporters` is an array of Parcel reporter packages. See [Reporters](#).
```json
{
"reporters": ["@parcel/reporter-detailed"]
}
```
See [Reporters](#reporters)
## Parcel Architecture
Even if you aren't doing anything that complex, if you are going to use Parcel
a lot it makes sense to take some time and understand how it works.
### Phases of Parcel
At a high level Parcel runs through several phases:
- Resolving
- Transforming
- Bundling
- Packaging
- Optimizing
The **resolving** and **transforming** phases work together in parallel to
build a graph of all your assets.
This asset graph gets translated into bundles in the **bundling** phase.
Then the **packaging** phase takes the assets in the calculated bundles and
merges them together into files each containing an entire bundle.
Finally, in the **optimizing** phase, Parcel takes these bundles files and runs
them through optimizing transforms.
### Asset Graph
During the resolving and transforming phases, Parcel discovers all the assets
in your app or program. Every asset can have it's own dependencies on other
assets which Parcel will pull in.
The data structure that represents all of these assets and their dependencies
on one another is known as "The Asset Graph".
| Asset Name | Dependencies |
| ------------ | ------------------- |
| `index.html` | `app.css`, `app.js` |
| `app.css` | N/A |
| `app.js` | `navbar.js` |
| `navbar.js` | etc. |
### Bundles
Once Parcel has built the entire Asset Graph, it begins turning it into
"bundles". These bundles are groupings of assets that get placed together in a
single file.
Bundles will (generally) contain only assets in the same language:
| Bundle Name | Assets |
| ------------ | --------------------------- |
| `index.html` | `index.html` |
| `app.css` | `app.css` |
| `app.js` | `app.js`, `navbar.js`, etc. |
Some assets are considered "entry" points into your app, and will stay as
separate bundles. For example, if your `index.html` file links to an
`about.html` file, they won't be merged together.
| Bundle Name | Assets | Entry URL |
| ------------ | ------------ | --------- |
| `index.html` | `index.html` | `/` |
| `about.html` | `about.html` | `/about` |
### Sources
"Sources" are the files that contain the source code to your app before being
compiled by Parcel.
Parcel discovers these sources by following their dependencies on one another
starting at your "entries".
These entries will be one of:
1. `$ parcel <...entries>`
2. `~/package.json#source`
3. `./src/index.*`
4. `./index.*`
From there, everything those assets depend on will be considered a "source" in
Parcel.