rollup-route-manifest
v1.0.0
Published
A Rollup plugin to generate an asset manifest, keyed by route patterns!
Downloads
8,425
Maintainers
Readme
rollup-route-manifest
A Rollup plugin to generate an asset manifest, keyed by route patterns!
The Context
Modern applications (should!) take advantage of route-based code splitting. This enables an application to be compartmentalized into smaller pieces, and to only load those pieces when needed. The common/immediate benefit is that your clients' first page-load requires dramatically less code, which results in a faster experience.
The Problem
While amazing, this isn't (yet) a perfect solution. The client will need to navigate to other pages!
Let's assume the client checks out the /blog
page.
In most configurations, the blog's assets only start downloading after the click has been made. Typically, the main "entrypoint" for /blog
will load, but only then will the additional assets it requires be requested. This cascade of "oh yeah, we need that too" can easily get out of hand.
What this means is that despite our super speedy, well-optimized application, the client is still waiting for assets. Our application is at the mercy of the client's network connection.
Until all the assets for /blog
have loaded, our client may be staring at a loading screen/spinner, or – worse – a split-second flash of the loader.
The Solution
With this plugin, you regain control of your application's assets and how they're loaded. :muscle:
You are given the knowledge of exactly which files are going to be requested for each route of your application.
In turn, this means you can preemptively load all the assets for /blog
before the client clicks – or begin prefetching everything /blog
needs immediately after the click – skipping the "oh yeah"-cascade and decreasing wait time(s).
Further Reading
- https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content
- https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/
- https://w3c.github.io/preload/#x2.link-type-preload
Install
$ npm install rollup-route-manifest --save-dev
Usage
// rollup.config.js
import Manifest from 'rollup-route-manifest';
export default {
// ...
plugins: [
// ...
Manifest({
merge: true,
minify: true,
routes(file) {
// Assume all "routes" in "/path/to/src/pages/" directory
file = file.replace('/path/to/src', '').replace(/\.[tj]sx?$/, '');
if (!file.includes('/pages/') return '*'; // commons
let name = '/' + file.replace('/pages/', '');
if (name === '/error') return false; // ignore
if (name === '/article') return '/blog/:title';
return name === '/home' ? '/' : name;
}
})
]
}
Options
options.routes
Type: Function
or Object
Required: true
Map absolute file paths to the URL route patterns that represent them.
Important: This is the only required option.
When routes
is a function, it receives absolute paths (string
) and expects a pattern (string) to be returned. You may return a falsey value to ignore the file, which will not create a new key in the route manifest.
route(file) {
if (file.includes('/error.js')) return false; // skip
if (!file.includes('/routes/')) return '*'; // commons chunk
let name = file.replace('/path/to/routes/', '').replace(/\.[tj]sx?$/, '');
if (name === 'article') return '/blog/:slug';
return name === 'home' ? '/' : name;
}
When routes
is an object, its keys must match the abolsute path and its values must be the pattern strings. Any unmatched absolute paths are ignored – as are any falsey values. Ignored files will not create a new key in the route manifest.
routes: {
'/path/to/src/index.js': '*',
'/path/to/routes/home.js': '/',
'/path/to/routes/article.js': '/blog/:slug',
// falsey and/or no match ~> ignore
// '/path/to/routes/error.js': false
}
options.assets
Type: Function
or Object
Customize the type
or as
value of an asset by looking at its filename.
Important: You may also return a falsey value to exclude the asset from the manifest.
The assets
option receives the assets' filenames, which are used to return a valid resource "destination" value. You may also return a falsey value which will not include the asset inside the manifest.
Below is the default assets
parser:
assets(filename) {
if (/\.js$/i.test(filename)) return 'script';
if (/\.(svg|jpe?g|png)$/i.test(filename)) return 'image';
if (/\.(woff2?|otf|ttf|eot)$/i.test(filename)) return 'font';
if (/\.css$/i.test(filename)) return 'style';
return false;
}
When assets
is an object, the assets' filenames are used as key lookups. Unmatched filenames are ignored and not included in the manifest. Because this could get very verbose, the function
-based approach is strongly recommended.
options.format
Type: Function
Customize the Asset
values.
You may use this function to modify the contents of the route chunks' Asset
list. This also runs before the headers
are produced.
Important: The
assets
andfilemap
youroptions.headers
function receives are affected by anyoptions.format
changes.
For example, if we wanted to drop the type
information from our Asset
list:
format(assets) {
return assets.map(x => x.href);
}
options.headers
Type: true
or Function
Optionally include (and customize) a "headers" section per manifest entry.
Important: When enabled, the output format of your manifest file will change! See Manifest Contents for details.
When true
, the default/internal function is used, which produces a HTTP Link
header per pattern, pointing to the pattern's assets.
You may also provide a function to define your own Link
header and/or add additional headers per route.
This function will receive:
assets
– theAsset[]
files for the current route chunkpattern
– the current route pattern stringfilemap
– the entire manifest file mapping ({ [pattern]: Asset[] }
)
Note: An
Asset
is defined as{ type: string, href: string }
shape.
options.filename
Type: String
or false
Default: rmanifest.json
The output filename for the route manifest. This file is written to disk, relative to your Rollup configuration's output directory/file.
When a falsey value (eg, ''
, false
, null
), no file is emitted.
options.publicPath
Type: String
Default: '/'
A prefix to append to all Asset
paths. This affects all files and headers, when enabled.
Important: A
publicPath
must end with a trailing slash.
// rollup.config.js
Manifest({
inline: true,
publicPath: '/foobar/'
// ...
})
// in browser:
console.log(window.__rmanifest['/']);
//=> [
//=> { type: 'script', href: '/foobar/index.b0b86791.js' },
//=> { type: 'script', href: '/foobar/index.1c5aebde.js' },
//=> ...
//=> ]
options.merge
Type: Boolean
Default: false
When enabled, and when a "*"
chunk exists, the "*"
contents are merged into all other route chunks.
Note: Any headers are merged too, if/when
options.headers
is enabled.
After merging, the "*"
chunk is removed from the final manifest. This is because its contents are already accounted for, reducing the amount of route-manifest
(runtime) work.
options.minify
Type: Boolean
Default: false
Minify the manifest's file contents.
options.sort
Type: Boolean
Default: true
If route patterns should be sorted by specificity. By default, this is true
as to ensure the consumer/runtime (eg, route-manifest
) can find the correct entry for a URL path.
Note: See Specificity from
route-sort
documentation.
options.inline
Type: Boolean
Default: true
Attempts to inline the manifest file directly into your main entry file (eg; bundle.xxxxx.js
).When successful, the manifest will be available globally as window.__rmanifest
.
While not required, it is strongly recommended that this option remains enabled so that the manifest contents are available to your Application immediately upon loading. This saves a network request and the trouble of coordinating subsequent prefetches.
Note: A
rmanifest.json
will still be written to disk for easier developer analysis. You must define a falseyoption.filename
to prevent a disk write.
Route Patterns
The supported route pattern types are:
- static –
/users
- named parameters –
/users/:id
- nested parameters –
/users/:id/books/:title
- optional parameters –
/users/:id?/books/:title?
- suffixed parameters –
/movies/:title.mp4
,/movies/:title.(mp4|mov)
- wildcards –
/users/*
Manifest Contents
The manifest file contains a JSON object whose keys are the route patterns you've defined for your application via the options.routes
mapping.
Note: There will often be a
"*"
key, which signifies your common/catch-all route. This typically contains yourbundle.(js|css)
files, and maybe some images that your main stylesheet requires.
Each key will point to an "Entry" item whose data type will vary depending on your options.headers
configuration. Either way, this Entry will always contain an "Asset" array, so let's define that first:
interface Asset {
type: string;
href: string;
}
Now, without options.headers
(default), the manifest pairs patterns directly to its list of Assets:
type Entry = Asset[];
// keys are `[pattern: string]`
type Manifest = Record<string, Entry>;
// Example:
//=> {
//=> "/": [
//=> { "type": "script", "href": "/index.abc123.js" },
//=> { "type": "style", "href": "/index.d10eg4.css" },
//=> // ...
//=> ],
//=> "/:slug": [...]
//=> }
With options.headers
configured, each manifest Entry becomes object containing "files" and "headers" keys:
interface Entry {
files: Asset[];
headers: any[]; // you decide its shape
}
// keys are `[pattern: string]`
type Manifest = Record<string, Entry>;
// Example:
//=> {
//=> "/": {
//=> "files": [
//=> { "type": "script", "href": "/index.abc123.js" },
//=> { "type": "style", "href": "/index.d10eg4.css" },
//=> // ...
//=> ],
//=> "headers": [
//=> // you decide
//=> ]
//=> }
//=> "/:slug": [...]
//=> }
Lastly, if options.headers
is true
, the default function runs, providing you with this format:
interface Header {
key: string;
value: string;
}
interface Entry {
files: Asset[];
headers: Header[];
}
// keys are `[pattern: string]`
type Manifest = Record<string, Entry>;
// Example:
//=> {
//=> "/": {
//=> "files": [
//=> { "type": "script", "href": "/index.abc123.js" },
//=> { "type": "style", "href": "/index.d10eg4.css" },
//=> // ...
//=> ],
//=> "headers": [
//=> {
//=> "key": "Link",
//=> "value": "</index.abc123.js>; rel=preload; as=script; crossorigin=anonymous, ..."
//=> }
//=> ]
//=> }
//=> "/:slug": [...]
//=> }
Related
webpack-route-manifest
– The webpack variant of this plugin.route-manifest
– A tiny (412B) runtime to retrieve the correct entry from a Route Manifest file.route-sort
– A tiny (200B) utility to sort route patterns by specificityquicklink
– A 900B library to achieve faster subsequent page-loads by prefetching in-viewport links during idle time.
License
MIT © Luke Edwards