Skip to content

Commit

Permalink
feat: add rollup-plugin-workbox (#378)
Browse files Browse the repository at this point in the history
  • Loading branch information
thepassle authored Aug 21, 2020
1 parent d4fd21c commit 4b473cf
Show file tree
Hide file tree
Showing 16 changed files with 435 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/five-peaches-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@web/rollup-plugin-workbox': patch
---

moved rollup-plugin-workbox to @web
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@typescript-eslint/parser": "^3.7.0",
"chai": "^4.2.0",
"concurrently": "^5.2.0",
"deepmerge": "^4.2.2",
"eslint": "^7.5.0",
"eslint-config-prettier": "^6.11.0",
"husky": "^1.0.0",
Expand Down
166 changes: 166 additions & 0 deletions packages/rollup-plugin-workbox/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# rollup-plugin-workbox

[![Published on npm](https://img.shields.io/npm/v/rollup-plugin-workbox.svg)](https://www.npmjs.com/package/rollup-plugin-workbox)

Rollup plugin that builds a service worker with workbox as part of your rollup build

## Usage

This package provides two rollup plugins: one that generates a complete service worker for you and one that generates a list of assets to precache that is injected into a service worker file.

The plugins are implemented as two function in the rollup-plugin-workbox module, named `generateSW` and `injectManifest`.

### `generateSW`

Import the `generateSW` plugin from `rollup-plugin-workbox`, and add it to your `plugins` array in your `rollup.config.js`. The plugin takes a workbox config object, and an optional render function as the second argument.

You can find a detailed list of supported properties for the workbox config object [here](https://developers.google.com/web/tools/workbox/modules/workbox-build#generatesw_mode).

```js
const { generateSW } = require('@web/rollup-plugin-workbox');

module.exports {
input: 'main.js',
output: {
file: 'dist/bundle.js',
format: 'esm',
},
plugins: [
generateSW({
swDest: '/dist/sw.js',
globDirectory: 'demo/dist/',
})
],
};
```

You can also `require` your `workbox-config.js` file and pass it to the plugin.

```js
const { generateSW } = require('@web/rollup-plugin-workbox');

const workboxConfig = require('./workbox-config.js')

module.exports {
// ...
plugins: [
generateSW(workboxConfig)
],
};
```

You can also customize the console output after workbox has generated your service worker by passing an optional render function as the second argument to the plugin:

```js
const { generateSW } = require('@web/rollup-plugin-workbox');

const workboxConfig = require('./workbox-config.js')

module.exports {
// ...
plugins: [
generateSW(
workboxConfig,
function render({ swDest, count, size }) {
console.log(
'📦', swDest,
'#️⃣', count,
'🐘', size,
);
}),
)
],
};
```

### `injectManifest`

Import the `injectManifest` plugin from `rollup-plugin-workbox`, and add it to your `plugins` array in your `rollup.config.js`. The plugin takes a workbox config object, and an optional render function as the second argument.

You can find a detailed list of supported properties for the workbox config object [here](https://developers.google.com/web/tools/workbox/modules/workbox-build#injectmanifest_mode).

```js
const { injectManifest } = require('@web/rollup-plugin-workbox');

module.exports {
input: 'main.js',
output: {
file: 'dist/bundle.js',
format: 'esm',
},
plugins: [
injectManifest({
swSrc: 'sw.js',
swDest: '/dist/sw.js',
globDirectory: 'demo/dist/',
})
],
};
```

You can also `require` your `workbox-config.js` file and pass it to the plugin.

```js
const { injectManifest } = require('@web/rollup-plugin-workbox');

const workboxConfig = require('./workbox-config.js')

module.exports {
// ...
plugins: [
injectManifest(workboxConfig)
],
};
```

You can also customize the console output after workbox has created your service worker by passing an optional render function as the second argument to the plugin:

```js
const { injectManifest } = require('@web/rollup-plugin-workbox');

const workboxConfig = require('./workbox-config.js')

module.exports {
// ...
plugins: [
injectManifest(
workboxConfig,
function render({ swDest, count, size }) {
console.log(
'📦', swDest,
'#️⃣', count,
'🐘', size,
);
}),
)
],
};
```

### A note on the `mode` config property

The `generateSW` mode of Workbox supports a `mode` property, that when set to `'production'` will bundle your generated service worker, and get rid of any `process.env.NODE_ENV` variables that are internally used in the Workbox libraries.

Unfortunately this got [wrongfully documented](https://github.com/GoogleChrome/workbox/issues/2427) for `injectManifest`, and this means that `injectManifest` does not actually support the `mode` property. There is a feature request on the [Workbox repo](https://github.com/GoogleChrome/workbox/issues/2588) to support this feature for `injectManifest` as well.

Until this gets fixed in `workbox-build`, `rollup-plugin-workbox` **does** support the `mode` property in the Workbox configuration for `injectManifest`, and when set to `'production'` will output a production optimized service worker for you.

```diff
const { injectManifest } = require('@web/rollup-plugin-workbox');

module.exports {
input: 'main.js',
output: {
file: 'dist/bundle.js',
format: 'esm',
},
plugins: [
injectManifest({
swSrc: 'sw.js',
swDest: '/dist/sw.js',
globDirectory: 'demo/dist/',
+ mode: 'production',
})
],
};
```
4 changes: 4 additions & 0 deletions packages/rollup-plugin-workbox/demo/injectManifestSwSrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
if (process.env.NODE_ENV === 'production') {
console.log('foo');
}
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);
1 change: 1 addition & 0 deletions packages/rollup-plugin-workbox/demo/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('hello workbox');
27 changes: 27 additions & 0 deletions packages/rollup-plugin-workbox/demo/rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { generateSW, injectManifest } from '../dist/index.js';

export default {
input: 'demo/main.js',
output: {
file: 'demo/dist/bundle.js',
format: 'esm',
},
plugins: [
generateSW({
swDest: 'demo/dist/generateSW_sw.js',
globDirectory: 'demo/dist/',
globIgnores: ['injectManifest_sw.js'],
},
function render({ swDest, count, size }) {
console.log(`\nCustom render! ${swDest}`);
console.log(`Custom render! The service worker will precache ${count} URLs, totaling ${size}.\n`);
}),
injectManifest({
swSrc: 'demo/injectManifestSwSrc.js',
swDest: 'demo/dist/injectManifest_sw.js',
globDirectory: 'demo/dist/',
globIgnores: ['generateSW_sw.js'],
mode: 'production',
}),
],
};
37 changes: 37 additions & 0 deletions packages/rollup-plugin-workbox/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@web/rollup-plugin-workbox",
"version": "1.0.0",
"description": "Rollup plugin that builds a service worker with workbox as part of your rollup build",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/modernweb-dev/web.git",
"directory": "packages/rollup-plugin-workbox"
},
"author": "modern-web",
"homepage": "https://github.com/modernweb-dev/web/tree/master/packages/rollup-plugin-workbox",
"bugs": {
"url": "https://github.com/modernweb-dev/web/issues"
},
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"demo": "rollup -c demo/rollup.config.js"
},
"keywords": [
"rollup",
"service-worker",
"workbox"
],
"dependencies": {
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-replace": "^2.3.3",
"pretty-bytes": "^5.3.0",
"rollup": "^2.20.0",
"rollup-plugin-terser": "^6.1.0",
"workbox-build": "^5.0.0"
},
"devDependencies": {
"@types/workbox-build": "^5.0.0"
}
}
93 changes: 93 additions & 0 deletions packages/rollup-plugin-workbox/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {
generateSW as _generateSw,
injectManifest as _injectManifest,
GenerateSWConfig,
InjectManifestConfig,
} from 'workbox-build';
import * as prettyBytes from 'pretty-bytes';
import * as rollup from 'rollup';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';
import resolve from '@rollup/plugin-node-resolve';

const name = 'workbox';

const report = ({ swDest, count, size }: { swDest: string; count: number; size: number }) => {
const prettySize = prettyBytes.default(size);

console.log(`\nThe service worker file was written to ${swDest}`);
console.log(`The service worker will precache ${count} URLs, totaling ${prettySize}.\n`);
};

export function generateSW(generateSWConfig: GenerateSWConfig, render = report) {
const { swDest, globDirectory } = generateSWConfig;

if (!swDest) throw new Error('No service worker destination specified');
if (!globDirectory) throw new Error('No globDirectory specified');

const doRender = ({
count,
size,
}: {
count: number;
filePaths: string[];
size: number;
warnings: string[];
}) => render({ swDest, count, size });

return {
name,
writeBundle() {
return _generateSw(generateSWConfig).then(doRender).catch(console.error);
},
};
}

export function injectManifest(injectManifestConfig: InjectManifestConfig, render = report) {
const { swSrc, swDest, globDirectory, mode } = injectManifestConfig;

if (!swSrc) throw new Error('No service worker source specified');
if (!swDest) throw new Error('No service worker destination specified');
if (!globDirectory) throw new Error('No globDirectory specified');

const doRender = ({
count,
size,
}: {
count: number;
filePaths: string[];
size: number;
warnings: string[];
}) => render({ swDest, count, size });

return {
name,
writeBundle() {
return _injectManifest(injectManifestConfig)
.then(doRender)
.then(async () => mode === 'production' && (await processBundle({ swDest })))
.catch(console.error);
},
};
}

/**
* @TODO
* This is a hack to be able to support the `mode` property for `injectManifest` until Workbox decides to support it.
* Feature is tracked here: https://github.com/GoogleChrome/workbox/issues/2588
* Once Workbox's `injectManifest` supports this out of the box, we should remove this.
*/
const processBundle = async ({ swDest }: { swDest: string }) => {
const bundle = await rollup.rollup({
input: swDest,
plugins: [
replace({ 'process.env.NODE_ENV': '"production"' }),
resolve(),
terser({ output: { comments: false } }),
],
});
await bundle.write({
file: swDest,
format: 'iife',
});
};
26 changes: 26 additions & 0 deletions packages/rollup-plugin-workbox/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Don't edit this file directly. It is generated by /scripts/update-package-configs.ts

{
"extends": "../../tsconfig.node-base.json",
"compilerOptions": {
"module": "es2015",
"outDir": "./dist",
"rootDir": "./src",
"composite": true,
"allowJs": true,
"lib": [
"webworker"
],
"types": [
"workbox-build"
]
},
"references": [],
"include": [
"src"
],
"exclude": [
"dist",
"demo"
]
}
7 changes: 7 additions & 0 deletions packages/rollup-plugin-workbox/tsconfig.override.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"lib": ["webworker"],
"types": ["workbox-build"]
},
"exclude": ["dist", "demo"]
}
Loading

0 comments on commit 4b473cf

Please sign in to comment.