Skip to content

Allow modification of webpack config for custom workers #441

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ module.exports = withPWA({
- default: `true`
- customWorkerDir - customize the directory where `next-pwa` looks for a custom worker implementation to add to the service worker generated by workbox. For more information, check out the [custom worker example](https://github.com/shadowwalker/next-pwa/tree/master/examples/custom-ts-worker).
- default: `worker`
- customWorkerWebpack - Function to customize the webpack configuration for bundling custom workers. Receives the configuration object with default settings and must return the modified one.
- default: `undefined`

### Other Options

Expand Down
14 changes: 11 additions & 3 deletions build-custom-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')

const buildCustomWorker = ({ id, basedir, customWorkerDir, destdir, plugins, minify }) => {
const buildCustomWorker = ({ id, basedir, customWorkerDir, destdir, plugins, minify, webpack: customWebpack }) => {
let workerDir = undefined

if (fs.existsSync(path.join(basedir, customWorkerDir))) {
Expand Down Expand Up @@ -36,7 +36,7 @@ const buildCustomWorker = ({ id, basedir, customWorkerDir, destdir, plugins, min
const customWorkerEntry = customWorkerEntries[0]
console.log(`> [PWA] Custom worker found: ${customWorkerEntry}`)
console.log(`> [PWA] Build custom worker: ${path.join(destdir, name)}`)
webpack({
const baseConfig = {
mode: 'none',
target: 'webworker',
entry: {
Expand Down Expand Up @@ -106,7 +106,15 @@ const buildCustomWorker = ({ id, basedir, customWorkerDir, destdir, plugins, min
minimizer: [new TerserPlugin()]
}
: undefined
}).run((error, status) => {
};

let config = baseConfig;
if (typeof customWebpack === 'function') {
console.log('> [PWA] Using provided webpack config to build custom worker')
config = customWebpack(baseConfig);
}

webpack(config).run((error, status) => {
if (error || status.hasErrors()) {
console.error(`> [PWA] Failed to build custom worker`)
console.error(status.toString({ colors: true }))
Expand Down
3 changes: 3 additions & 0 deletions examples/custom-worker-webpack/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
68 changes: 68 additions & 0 deletions examples/custom-worker-webpack/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# next-pwa - custom worker example

[TOC]

This example demonstrates how to use `next-pwa` plugin to turn a `next.js` based web application into a progressive web application easily. It demonstrates how to add custom worker code to the service worker generated by workbox.

## New Method

Simply create a `worker/index.js` and start implementing your service worker. `next-pwa` will detect this file automatically, and bundle the file into `dest` as `worker-*.js` using `webpack`. It's also automatically injected into `sw.js` generated.

In this way, you get benefit of code splitting and size minimization automatically. Yes! `require` modules works! Yes! you can share codes between web app and the service worker!

> - Typescript support for `worker/index.ts` current not supported.
>
> - In dev mode, `worker/index.js` is not watch, so it will not hot reload.

### Custom Worker Directory

You can customize the directory of your custom worker file by setting the `customWorkerDir` relative to the `basedir` in the `pwa` section of your `next.config.js`:

```javascript
const withPWA = require('next-pwa')({
customWorkerDir: 'serviceworker'
...
})

module.exports = withPWA({
// next.js config
})
```

In this example, `next-pwa` would look for `serviceworker/index.js`.

## Old Method (Still Works)

Basically you need to create a file such as `worker.js` in `public` folder, then add an option `importScripts` to `pwa` object in `next.config.js`:

```javascript
const withPWA = require('next-pwa')({
dest: 'public',
importScripts: ['/worker.js']
})

module.exports = withPWA({
// next.js config
})
```

Then service worker generated will automatically import your code and run it before other workbox code.

## Usage

[![Open in Gitpod](https://img.shields.io/badge/Open%20In-Gitpod.io-%231966D2?style=for-the-badge&logo=gitpod)](https://gitpod.io/#https://github.com/shadowwalker/next-pwa/)

```bash
cd examples/custom-server
yarn install
yarn build
yarn start
```

## Recommend `.gitignore`

```
**/public/workbox-*.js
**/public/sw.js
**/public/worker-*.js
```
17 changes: 17 additions & 0 deletions examples/custom-worker-webpack/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const withPWA = require('next-pwa')({
dest: 'public',
customWorkerWebpack(config) {
return {
...config,
resolve: {
...config.resolve,
alias: {
...config.resolve.alias,
'@util': path.resolve(__dirname, './src'),
}
}
};
}
})

module.exports = withPWA()
23 changes: 23 additions & 0 deletions examples/custom-worker-webpack/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "next-pwa-example",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"babel-loader": "^8.2.5",
"next": "^12.2.5",
"next-pwa": "latest",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"eslint": "^8.22.0",
"eslint-config-next": "12.2.5"
}
}
50 changes: 50 additions & 0 deletions examples/custom-worker-webpack/pages/_document.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Document, { Html, Head, Main, NextScript } from 'next/document'

const APP_NAME = 'next-pwa example'
const APP_DESCRIPTION = 'This is an example of using next-pwa plugin'

class _Document extends Document {
static async getInitialProps(ctx) {
return await Document.getInitialProps(ctx)
}

render() {
return (
<Html lang='en' dir='ltr'>
<Head>
<meta name='application-name' content={APP_NAME} />
<meta name='apple-mobile-web-app-capable' content='yes' />
<meta name='apple-mobile-web-app-status-bar-style' content='default' />
<meta name='apple-mobile-web-app-title' content={APP_NAME} />
<meta name='description' content={APP_DESCRIPTION} />
<meta name='format-detection' content='telephone=no' />
<meta name='mobile-web-app-capable' content='yes' />
<meta name='theme-color' content='#FFFFFF' />
{/* TIP: set viewport head meta tag in _app.js, otherwise it will show a warning */}
{/* <meta name='viewport' content='minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no, viewport-fit=cover' /> */}

<link rel='apple-touch-icon' sizes='180x180' href='/icons/apple-touch-icon.png' />
<link rel='manifest' href='/manifest.json' />
<link rel='shortcut icon' href='/favicon.ico' />
<style>{`
html, body, #__next {
height: 100%;
}
#__next {
margin: 0 auto;
}
h1 {
text-align: center;
}
`}</style>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}

export default _Document
12 changes: 12 additions & 0 deletions examples/custom-worker-webpack/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Head from 'next/head'

const Index = () => (
<>
<Head>
<title>next-pwa example</title>
</Head>
<h1>Next.js + PWA = AWESOME!</h1>
</>
)

export default Index
Binary file added examples/custom-worker-webpack/public/favicon.ico
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions examples/custom-worker-webpack/public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "next-pwa",
"short_name": "next-pwa",
"display": "standalone",
"orientation": "portrait",
"theme_color": "#FFFFFF",
"background_color": "#FFFFFF",
"start_url": "/",
"icons": [
{
"src": "/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
8 changes: 8 additions & 0 deletions examples/custom-worker-webpack/util/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict'

module.exports = () => {
console.log('Hello from util.')
console.log('es6+ syntax test:')
let foo = { message: "working" }
console.log(foo?.message)
}
20 changes: 20 additions & 0 deletions examples/custom-worker-webpack/worker/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict'

// To disable all workbox logging during development, you can set self.__WB_DISABLE_DEV_LOGS to true
// https://developers.google.com/web/tools/workbox/guides/configure-workbox#disable_logging
//
// self.__WB_DISABLE_DEV_LOGS = true

const util = require('@util')

util()

// listen to message event from window
self.addEventListener('message', event => {
// HOW TO TEST THIS?
// Run this in your browser console:
// window.navigator.serviceWorker.controller.postMessage({command: 'log', message: 'hello world'})
// OR use next-pwa injected workbox object
// window.workbox.messageSW({command: 'log', message: 'hello world'})
console.log(event.data)
})
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ module.exports =
scope = basePath,
customWorkerDir = 'worker',
subdomainPrefix, // deprecated, use basePath in next.config.js instead
customWorkerWebpack = undefined,
...workbox
} = pluginOptions

Expand Down Expand Up @@ -107,7 +108,8 @@ module.exports =
customWorkerDir,
destdir: _dest,
plugins: config.plugins.filter(plugin => plugin instanceof webpack.DefinePlugin),
minify: !dev
minify: !dev,
webpack: customWorkerWebpack
})

if (!!customWorkerScriptName) {
Expand Down