Skip to content

Commit

Permalink
updated to es6 syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
Red-misst committed Apr 7, 2024
1 parent 5ecbd92 commit 9dd3738
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 332 deletions.
63 changes: 3 additions & 60 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# Zero Config [PWA](https://web.dev/learn/pwa/) Plugin for [Next.js](https://nextjs.org/)

This plugin is powered by [workbox](https://developer.chrome.com/docs/workbox/). A mantained version of the next-pwa

[license](https://img.shields.io/npm/l/imuigai-next-pwa.svg)
pwa library for creating webapps in [Next.js](https://nextjs.org/) . Powered by [workbox](https://developer.chrome.com/docs/workbox/).


**Features**
Expand Down Expand Up @@ -35,6 +33,8 @@ This plugin is powered by [workbox](https://developer.chrome.com/docs/workbox/).
```bash
yarn add imuigai-next-pwa
or
npm i imuigai-next-pwa
```

## Basic Usage
Expand Down Expand Up @@ -229,63 +229,6 @@ module.exports = withPWA({
})
```

### Available Options

- disable: boolean - whether to disable pwa feature as a whole
- default: `false`
- set `disable: false`, so that it will generate service worker in both `dev` and `prod`
- set `disable: true` to completely disable PWA
- if you don't need to debug service worker in `dev`, you can set `disable: process.env.NODE_ENV === 'development'`
- register: boolean - whether to let this plugin register service worker for you
- default to `true`
- set to `false` when you want to handle register service worker yourself, this could be done in `componentDidMount` of your root app. you can consider the [register.js](https://github.com/Red-misst/imuigai-next-pwa/blob/master/register.js) as an example.
- scope: string - url scope for pwa
- default: [`basePath`](https://nextjs.org/docs/api-reference/next.config.js/basepath) in `next.config.js` or `/`
- set to `/app` so that path under `/app` will be PWA while others are not
- sw: string - service worker script file name
- default: `/sw.js`
- set to another file name if you want to customize the output file name
- runtimeCaching - caching strategies (array or callback function)
- default: see the **Runtime Caching** section for the default configuration
- accepts an array of cache entry objects, [please follow the structure here](https://developer.chrome.com/docs/workbox/reference/workbox-build/#type-RuntimeCaching)
- Note: the order of the array matters. The first rule that matches is effective. Therefore, please **ALWAYS** put rules with larger scope behind the rules with a smaller and specific scope.
- publicExcludes - an array of glob pattern strings to exclude files in the `public` folder from being precached.
- default: `['!noprecache/**/*']` - this means that the default behavior will precache all the files inside your `public` folder but files inside `/public/noprecache` folder. You can simply put files inside that folder to not precache them without config this.
- example: `['!img/super-large-image.jpg', '!fonts/not-used-fonts.otf']`
- buildExcludes - an array of extra pattern or function to exclude files from being precached in `.next/static` (or your custom build) folder
- default: `[]`
- example: `[/chunks\/images\/.*$/]` - Don't precache files under `.next/static/chunks/images` (Highly recommend this to work with `next-optimized-images` plugin)
- doc: Array of (string, RegExp, or function()). One or more specifiers used to exclude assets from the precache manifest. This is interpreted following the same rules as Webpack's standard exclude option.
- cacheStartUrl - whether to cache start url
- default: `true`
- [discussion of use case to not cache start url at all](https://github.com/Red-misst/imuigai-next-pwa/pull/296#issuecomment-1094167025)
- dynamicStartUrl - if your start url returns different HTML document under different state (such as logged in vs. not logged in), this should be set to true.
- default: `true`
- effective when `cacheStartUrl` set to `true`
- recommend: set to **false** if your start url always returns same HTML document, then start url will be precached, this will help to speed up first load.
- dynamicStartUrlRedirect - if your start url redirect to another route such as `/login`, it's recommended to setup this redirected url for the best user experience.
- default: `undefined`
- effective when `dynamicStartUrlRedirect` set to `true`
- fallbacks - config precached routes to fallback when both cache and network not available to serve resources.
- **if you just need a offline fallback page, simply create a `/_offline` page such as `pages/_offline.js` and you are all set, no configuration necessary**
- default: `object`
- `fallbacks.document` - fallback route for document (page), default to `/_offline` if you created that page
- `fallbacks.image` - fallback route for image, default to none
- `fallbacks.audio` - fallback route for audio, default to none
- `fallbacks.video` - fallback route for video, default to none
- `fallbacks.font` - fallback route for font, default to none
- cacheOnFrontEndNav - enable additional route cache when navigate between pages with `next/link` on front end. Checkout this [example](https://github.com/Red-misst/imuigai-next-pwa/tree/master/examples/cache-on-front-end-nav) for some context about why this is implemented.
- default: `false`
- note: this improve user experience on special use cases but it also adds some overhead because additional network call, I suggest you consider this as a trade off.
- ~~subdomainPrefix: string - url prefix to allow hosting static files on a subdomain~~
- ~~default: `""` - i.e. default with no prefix~~
- ~~example: `/subdomain` if the app is hosted on `example.com/subdomain`~~
- deprecated, use [basePath](https://nextjs.org/docs/api-reference/next.config.js/basepath) instead
- reloadOnOnline - changes the behaviour of the app when the device detects that it has gone back "online" and has a network connection. Indicate if the app should call `location.reload()` to refresh the app.
- default: `true`
- customWorkerDir - customize the directory where `imuigai-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/Red-misst/imuigai-next-pwa/tree/master/examples/custom-ts-worker).
- default: `worker`

### Other Options

`imuigai-next-pwa` uses `workbox-webpack-plugin`, other options which could also be put in `pwa` object can be found [**ON THE DOCUMENTATION**](https://developer.chrome.com/docs/workbox/modules/workbox-webpack-plugin) for [GenerateSW](https://developer.chrome.com/docs/workbox/modules/workbox-webpack-plugin/#generatesw-plugin) and [InjectManifest](https://developer.chrome.com/docs/workbox/modules/workbox-webpack-plugin/#injectmanifest-plugin). If you specify `swSrc`, `InjectManifest` plugin will be used, otherwise `GenerateSW` will be used to generate service worker.
Expand Down
50 changes: 25 additions & 25 deletions build-custom-worker.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
'use strict'
'use strict';

const path = require('path')
const fs = require('fs')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
import path from 'path';
import fs from 'fs';
import webpack from 'webpack';
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
import TerserPlugin from 'terser-webpack-plugin';

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

if (fs.existsSync(path.join(basedir, customWorkerDir))) {
workerDir = path.join(basedir, customWorkerDir)
workerDir = path.join(basedir, customWorkerDir);
} else if (fs.existsSync(path.join(basedir, 'src', customWorkerDir))) {
workerDir = path.join(basedir, 'src', customWorkerDir)
workerDir = path.join(basedir, 'src', customWorkerDir);
}

if (!workerDir) return
if (!workerDir) return;

const name = `worker-${id}.js`
const name = `worker-${id}.js`;
const customWorkerEntries = ['ts', 'js']
.map(ext => path.join(workerDir, `index.${ext}`))
.filter(entry => fs.existsSync(entry))
.filter(entry => fs.existsSync(entry));

if (customWorkerEntries.length === 0) return
if (customWorkerEntries.length === 0) return;

if (customWorkerEntries.length > 1) {
console.warn(
`> [PWA] WARNING: More than one custom worker found (${customWorkerEntries.join(
','
)}), not building a custom worker`
)
return
);
return;
}

const customWorkerEntry = customWorkerEntries[0]
console.log(`> [PWA] Custom worker found: ${customWorkerEntry}`)
console.log(`> [PWA] Build custom worker: ${path.join(destdir, name)}`)
const customWorkerEntry = customWorkerEntries[0];
console.log(`> [PWA] Custom worker found: ${customWorkerEntry}`);
console.log(`> [PWA] Build custom worker: ${path.join(destdir, name)}`);
webpack({
mode: 'none',
target: 'webworker',
Expand Down Expand Up @@ -108,13 +108,13 @@ const buildCustomWorker = ({ id, basedir, customWorkerDir, destdir, plugins, min
: undefined
}).run((error, status) => {
if (error || status.hasErrors()) {
console.error(`> [PWA] Failed to build custom worker`)
console.error(status.toString({ colors: true }))
process.exit(-1)
console.error(`> [PWA] Failed to build custom worker`);
console.error(status.toString({ colors: true }));
process.exit(-1);
}
})
});

return name
}
return name;
};

module.exports = buildCustomWorker
export default buildCustomWorker;
112 changes: 56 additions & 56 deletions build-fallback-worker.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
'use strict'
'use strict';

const path = require('path')
const fs = require('fs')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
import path from 'path';
import fs from 'fs';
import webpack from 'webpack';
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
import TerserPlugin from 'terser-webpack-plugin';

const getFallbackEnvs = ({ fallbacks, basedir, id, pageExtensions }) => {
let { document, data } = fallbacks
let { document, data } = fallbacks;

if (!document) {
let pagesDir = undefined
let pagesDir = undefined;

if (fs.existsSync(path.join(basedir, 'pages'))) {
pagesDir = path.join(basedir, 'pages')
pagesDir = path.join(basedir, 'pages');
} else if (fs.existsSync(path.join(basedir, 'src', 'pages'))) {
pagesDir = path.join(basedir, 'src', 'pages')
pagesDir = path.join(basedir, 'src', 'pages');
}

if (!pagesDir) return
if (!pagesDir) return;

const offlines = pageExtensions
.map(ext => path.join(pagesDir, `_offline.${ext}`))
.filter(entry => fs.existsSync(entry))
.filter(entry => fs.existsSync(entry));
if (offlines.length === 1) {
document = '/_offline'
document = '/_offline';
}
}

if (data && data.endsWith('.json')) {
data = path.posix.join('/_next/data', id, data)
data = path.posix.join('/_next/data', id, data);
}

const envs = {
Expand All @@ -38,34 +38,34 @@ const getFallbackEnvs = ({ fallbacks, basedir, id, pageExtensions }) => {
__PWA_FALLBACK_AUDIO__: fallbacks.audio || false,
__PWA_FALLBACK_VIDEO__: fallbacks.video || false,
__PWA_FALLBACK_FONT__: fallbacks.font || false,
__PWA_FALLBACK_DATA__: data || false
}
__PWA_FALLBACK_DATA__: data || false,
};

if (Object.values(envs).filter(v => !!v).length === 0) return
if (Object.values(envs).filter(v => !!v).length === 0) return;

console.log('> [PWA] Fallback to precache routes when fetch failed from cache or network:')
if (envs.__PWA_FALLBACK_DOCUMENT__) console.log(`> [PWA] document (page): ${envs.__PWA_FALLBACK_DOCUMENT__}`)
if (envs.__PWA_FALLBACK_IMAGE__) console.log(`> [PWA] image: ${envs.__PWA_FALLBACK_IMAGE__}`)
if (envs.__PWA_FALLBACK_AUDIO__) console.log(`> [PWA] audio: ${envs.__PWA_FALLBACK_AUDIO__}`)
if (envs.__PWA_FALLBACK_VIDEO__) console.log(`> [PWA] video: ${envs.__PWA_FALLBACK_VIDEO__}`)
if (envs.__PWA_FALLBACK_FONT__) console.log(`> [PWA] font: ${envs.__PWA_FALLBACK_FONT__}`)
if (envs.__PWA_FALLBACK_DATA__) console.log(`> [PWA] data (/_next/data/**/*.json): ${envs.__PWA_FALLBACK_DATA__}`)
console.log('> [PWA] Fallback to precache routes when fetch failed from cache or network:');
if (envs.__PWA_FALLBACK_DOCUMENT__) console.log(`> [PWA] document (page): ${envs.__PWA_FALLBACK_DOCUMENT__}`);
if (envs.__PWA_FALLBACK_IMAGE__) console.log(`> [PWA] image: ${envs.__PWA_FALLBACK_IMAGE__}`);
if (envs.__PWA_FALLBACK_AUDIO__) console.log(`> [PWA] audio: ${envs.__PWA_FALLBACK_AUDIO__}`);
if (envs.__PWA_FALLBACK_VIDEO__) console.log(`> [PWA] video: ${envs.__PWA_FALLBACK_VIDEO__}`);
if (envs.__PWA_FALLBACK_FONT__) console.log(`> [PWA] font: ${envs.__PWA_FALLBACK_FONT__}`);
if (envs.__PWA_FALLBACK_DATA__) console.log(`> [PWA] data (/_next/data/**/*.json): ${envs.__PWA_FALLBACK_DATA__}`);

return envs
}
return envs;
};

const buildFallbackWorker = ({ id, fallbacks, basedir, destdir, minify, pageExtensions }) => {
const envs = getFallbackEnvs({ fallbacks, basedir, id, pageExtensions })
if (!envs) return
const envs = getFallbackEnvs({ fallbacks, basedir, id, pageExtensions });
if (!envs) return;

const name = `fallback-${id}.js`
const fallbackJs = path.join(__dirname, `fallback.js`)
const name = `fallback-${id}.js`;
const fallbackJs = path.join(__dirname, `fallback.js`);

webpack({
mode: 'none',
target: 'webworker',
entry: {
main: fallbackJs
main: fallbackJs,
},
resolve: {
extensions: ['.js'],
Expand All @@ -82,8 +82,8 @@ const buildFallbackWorker = ({ id, fallbacks, basedir, destdir, minify, pageExte
net: false,
tls: false,
zlib: false,
child_process: false
}
child_process: false,
},
},
module: {
rules: [
Expand All @@ -101,46 +101,46 @@ const buildFallbackWorker = ({ id, fallbacks, basedir, destdir, minify, pageExte
corejs: false,
helpers: true,
regenerator: false,
useESModules: true
useESModules: true,
},
'preset-env': {
modules: false,
targets: 'chrome >= 56'
}
}
]
]
}
}
]
}
]
targets: 'chrome >= 56',
},
},
],
],
},
},
],
},
],
},
output: {
path: destdir,
filename: name
filename: name,
},
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: [path.join(destdir, 'fallback-*.js'), path.join(destdir, 'fallback-*.js.map')]
cleanOnceBeforeBuildPatterns: [path.join(destdir, 'fallback-*.js'), path.join(destdir, 'fallback-*.js.map')],
}),
new webpack.EnvironmentPlugin(envs)
new webpack.EnvironmentPlugin(envs),
],
optimization: minify
? {
minimize: true,
minimizer: [new TerserPlugin()]
minimizer: [new TerserPlugin()],
}
: undefined
: undefined,
}).run((error, status) => {
if (error || status.hasErrors()) {
console.error(`> [PWA] Failed to build fallback worker`)
console.error(status.toString({ colors: true }))
process.exit(-1)
console.error(`> [PWA] Failed to build fallback worker`);
console.error(status.toString({ colors: true }));
process.exit(-1);
}
})
});

return { fallbacks, name, precaches: Object.values(envs).filter(v => !!v) }
}
return { fallbacks, name, precaches: Object.values(envs).filter(v => !!v) };
};

module.exports = buildFallbackWorker
export default buildFallbackWorker;
Loading

0 comments on commit 9dd3738

Please sign in to comment.