From d0cb3fb28c3e8d467e9dc5ba5aaee44c24a37be1 Mon Sep 17 00:00:00 2001 From: DuCanhGH <75556609+DuCanhGH@users.noreply.github.com> Date: Wed, 30 Nov 2022 21:20:15 +0700 Subject: [PATCH] feat: support for appDir --- CHANGELOG.md | 6 +- README.md | 91 ++++--------------- examples/cache-on-front-end-nav/README.md | 4 +- examples/custom-ts-worker/README.md | 8 +- examples/custom-worker/README.md | 8 +- examples/lifecycle/README.md | 2 +- examples/lifecycle/pages/index.js | 8 +- examples/next-9/README.md | 2 +- examples/next-i18next/README.md | 2 +- examples/next-image/README.md | 2 +- examples/offline-fallback/README.md | 2 +- package.json | 12 +-- pnpm-lock.yaml | 92 +++++++++++++++----- rollup.config.js | 8 +- src/build-custom-worker.ts | 2 +- src/index.ts | 68 +++++++++------ src/types.ts | 101 +++++++++++++++++++++- tsconfig.json | 2 +- 18 files changed, 266 insertions(+), 154 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48427978..ef2bcad5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,14 +103,14 @@ ### Fix 1. (the real) Fix for not precache server js -2. Fix service worker register url edge case +2. Fix Service Worker register url edge case ## 5.4.7 ### Fix 1. (the real) Fix for not precache server js -2. Fix service worker register url edge case +2. Fix Service Worker register url edge case ## 5.4.6 @@ -368,7 +368,7 @@ ### Fix -- Remove POST api runtime cache from default cache configuration as it's not supported in service worker +- Remove POST api runtime cache from default cache configuration as it's not supported in Service Worker ## 3.1.0 diff --git a/README.md b/README.md index 25d09ff2..c5726fd0 100644 --- a/README.md +++ b/README.md @@ -9,19 +9,19 @@ This plugin is powered by [Workbox](https://developer.chrome.com/docs/workbox/) **Features** -- 0️⃣ Zero config for registering and generating service worker +- 0️⃣ Zero config for registering and generating Service Worker - ✨ Optimized precache and runtime cache -- 💯 Maximize lighthouse score +- 💯 Maximize Lighthouse score - 🎈 Easy to understand examples - 📴 Completely offline support with fallbacks [example](https://github.com/DuCanhGH/next-pwa/tree/master/examples/offline-fallback-v2) 🆕 -- 📦 Use [workbox](https://developer.chrome.com/docs/workbox/) and [workbox-window](https://developer.chrome.com/docs/workbox/modules/workbox-window) v6 +- 📦 Use [Workbox](https://developer.chrome.com/docs/workbox/) and [workbox-window](https://developer.chrome.com/docs/workbox/modules/workbox-window) v6 - 🍪 Work with cookies out of the box - 🔉 Default range requests for audios and videos - ☕ No custom server needed for Next.js 9+ [example](https://github.com/DuCanhGH/next-pwa/tree/master/examples/next-9) - 🔧 Handle PWA lifecycle events opt-in [example](https://github.com/DuCanhGH/next-pwa/tree/master/examples/lifecycle) -- 📐 Custom worker to run extra code with code splitting and **typescript** support [example](https://github.com/DuCanhGH/next-pwa/tree/master/examples/custom-ts-worker) +- 📐 Custom worker to run extra code with code splitting and **Typescript** support [example](https://github.com/DuCanhGH/next-pwa/tree/master/examples/custom-ts-worker) - 📜 [Public environment variables](https://nextjs.org/docs/basic-features/environment-variables#exposing-environment-variables-to-the-browser) available in custom worker as usual -- 🐞 Debug service worker with confidence in development mode without caching +- 🐞 Debug Service Worker with confidence in development mode without caching - 🌏 Internationalization (a.k.a I18N) with `next-i18next` [example](https://github.com/DuCanhGH/next-pwa/tree/master/examples/next-i18next) - 🛠 Configurable by the same [workbox configuration options](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) - 🚀 Spin up a [GitPod](https://gitpod.io/#https://github.com/DuCanhGH/next-pwa/) and try out examples in rocket speed @@ -29,7 +29,7 @@ This plugin is powered by [Workbox](https://developer.chrome.com/docs/workbox/) > **NOTE 1** - `next-pwa` version 2.0.0+ should only work with `Next.js` 9.1+, and static files should only be served through `public` directory. This will make things simpler. > -> **NOTE 2** - If you encounter error `TypeError: Cannot read property **'javascript' of undefined**` during build, [please consider upgrade to Webpack 5 in `next.config.js`](https://github.com/shadowwalker/next-pwa/issues/198#issuecomment-817205700). +> **NOTE 2** - If you encounter error `TypeError: Cannot read property **'javascript' of undefined**` during build, [please consider upgrading to Webpack 5 in `next.config.js`](https://github.com/shadowwalker/next-pwa/issues/198#issuecomment-817205700). --- @@ -85,7 +85,7 @@ Copy files to your static file hosting server, so that they are accessible from One example is using Firebase hosting service to host those files statically. You can automate the copy step using scripts in your deployment workflow. -> For security reasons, you must host these files directly from your domain. If the content is delivered using a redirect, the browser will refuse to run the service worker. +> For security reasons, you must host these files directly from your domain. If the content is delivered using a redirect, the browser will refuse to run the Service Worker. ### Option 2: Use Custom Server @@ -304,85 +304,32 @@ export default 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/DuCanhGH/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/shadowwalker/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/DuCanhGH/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 `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/DuCanhGH/next-pwa/tree/master/examples/custom-ts-worker). - - default: `worker` +See [PluginOptions](https://github.com/DuCanhGH/next-pwa/blob/master/src/types.ts?plain=1#L3) ### Other Options -`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. +`next-pwa` uses `workbox-webpack-plugin`, other options can be found in [Google's documentation for workbox-webpack-plugin](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). ### Runtime Caching -`next-pwa` uses a default runtime [cache.js](https://github.com/DuCanhGH/next-pwa/blob/master/cache.js) +`next-pwa` specifies a default runtime caching [cache.js](https://github.com/DuCanhGH/next-pwa/blob/master/src/cache.ts) -There is a great chance you may want to customize your own runtime caching rules. Please feel free to copy the default `cache.js` file and customize the rules as you like. Don't forget to inject the configurations into your `pwa` config in `next.config.js`. +There is a chance you may want to have your own runtime caching rules. Please feel free to copy the default `cache.ts` file and customize the rules as you like. Don't forget to inject the configurations into your `withPWAInit`'s config in `next.config.js`. -Here is the [document on how to write runtime caching configurations](https://developer.chrome.com/docs/workbox/reference/workbox-build/#type-RuntimeCaching), including background sync and broadcast update features and more! +Here is the [document on how to write a runtime caching configuration](https://developer.chrome.com/docs/workbox/reference/workbox-build/#type-RuntimeCaching), including background sync and broadcast update and more! ## Tips -1. [Common UX pattern to ask user to reload when new service worker is installed](https://github.com/DuCanhGH/next-pwa/blob/master/examples/lifecycle/pages/index.js#L26-L38) -2. Use a convention like `{command: 'doSomething', message: ''}` object when `postMessage` to service worker. So that on the listener, it could do multiple different tasks using `if...else...`. -3. When you are debugging service worker, constantly `clean application cache` to reduce some flaky errors. +1. [Common UX pattern to ask user to reload when new Service Worker is installed](https://github.com/DuCanhGH/next-pwa/blob/master/examples/lifecycle/pages/index.js#L26-L38) +2. Use a convention like `{command: 'doSomething', message: ''}` object when `postMessage` to Service Worker. So that on the listener, it could do multiple different tasks using `if...else...`. +3. When you are debugging Service Worker, constantly `clean application cache` to reduce some flaky errors. 4. If you are redirecting the user to another route, please note [workbox by default only cache response with 200 HTTP status](https://developer.chrome.com/docs/workbox/modules/workbox-cacheable-response#what_are_the_defaults), if you really want to cache redirected page for the route, you can specify it in `runtimeCaching` such as `options.cacheableResponse.statuses=[200,302]`. 5. When debugging issues, you may want to format your generated `sw.js` file to figure out what's really going on. -6. Force `next-pwa` to generate worker box production build by specify the option `mode: 'production'` in your `pwa` section of `next.config.js`. Though `next-pwa` automatically generate the worker box development build during development (by running `next`) and worker box production build during production (by running `next build` and `next start`). You may still want to force it to production build even during development of your web app for following reason: - 1. Reduce logging noise due to production build doesn't include logging. - 2. Improve performance a bit due to production build is optimized and minified. +6. Force `next-pwa` to generate worker box production build by specify the option `mode: 'production'` in your `withPWAInit`'s config in `next.config.js`. Though `next-pwa` automatically generate the worker box development build during development (by running `next`) and worker box production build during production (by running `next build` and `next start`). You may still want to force it to build for production even in development mode of your web app for following reason: + 1. Reduce logging noise as the production build doesn't include logging. + 2. Improve performance a bit as the production build is better optimized. 7. If you just want to disable worker box logging while keeping development build during development, [simply put `self.__WB_DISABLE_DEV_LOGS = true` in your `worker/index.js` (create one if you don't have one)](https://github.com/DuCanhGH/next-pwa/blob/c48ef110360d0138ad2dacd82ab96964e3da2daf/examples/custom-worker/worker/index.js#L6). -8. It is common developers have to use `userAgent` string to determine if users are using Safari/iOS/MacOS or some other platform, [ua-parser-js](https://www.npmjs.com/package/ua-parser-js) library is a good friend for that purpose. +8. It is common for developers to have to use `userAgent` string to determine if users are using Safari/iOS/MacOS or some other platform, [ua-parser-js](https://www.npmjs.com/package/ua-parser-js) library is a good friend for that purpose. ## Reference diff --git a/examples/cache-on-front-end-nav/README.md b/examples/cache-on-front-end-nav/README.md index 33031965..605da7e2 100644 --- a/examples/cache-on-front-end-nav/README.md +++ b/examples/cache-on-front-end-nav/README.md @@ -8,9 +8,9 @@ This example demonstrates how to use `next-pwa` plugin to solve the issue when u For context, `next.js` embraces both SSR and front end routing (typical SPA) to deliver smooth users experience. However, when a user navigate on the web app through `next/router` or `next/link` (Link component), the navigation is made through front end routing. Which means there is no HTTP GET request made to the server for that route, it only swap the react component to the new page and change the url showed on the url bar. This "fake" navigation is usually desired because it means users do not have to wait for network delay. -However the problem appears when it comes to caching, because there is not request that service worker could intercept, nothing is cached. Users may landing on your home page and navigate to different pages without doing any caching. When the network is lost and users reload these pages using refresh button or re-open the browser, a network lost page from the browser is present, bummer! This behavior could leave users very confused as they could navigated the web app without problem in online and offline, when it come back offline, the web app stopped working. +However the problem appears when it comes to caching, because there is not request that Service Worker could intercept, nothing is cached. Users may landing on your home page and navigate to different pages without doing any caching. When the network is lost and users reload these pages using refresh button or re-open the browser, a network lost page from the browser is present, bummer! This behavior could leave users very confused as they could navigated the web app without problem in online and offline, when it come back offline, the web app stopped working. -So we have to enforce a cache for each front-end caching. Yes, it adds additional network traffic which seems conflict of what we are trying to achieve with front end routing. But since the cache happens in service worker, it should have trivial impact on user experience or more specifically, page load. +So we have to enforce a cache for each front-end caching. Yes, it adds additional network traffic which seems conflict of what we are trying to achieve with front end routing. But since the cache happens in Service Worker, it should have trivial impact on user experience or more specifically, page load. I personally feel it's a trade off for you to decide whether you want to improve this user experience. diff --git a/examples/custom-ts-worker/README.md b/examples/custom-ts-worker/README.md index c71e6355..b41758e8 100644 --- a/examples/custom-ts-worker/README.md +++ b/examples/custom-ts-worker/README.md @@ -2,13 +2,13 @@ [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. +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.ts` 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. +Simply create a `worker/index.ts` 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! +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! > - In dev mode, `worker/index.ts` is not watched, so it will not hot reload. @@ -44,7 +44,7 @@ module.exports = withPWA({ }); ``` -Then service worker generated will automatically import your code and run it before other workbox code. +Then Service Worker generated will automatically import your code and run it before other workbox code. ## Usage diff --git a/examples/custom-worker/README.md b/examples/custom-worker/README.md index ce635478..f2aa0104 100644 --- a/examples/custom-worker/README.md +++ b/examples/custom-worker/README.md @@ -2,13 +2,13 @@ [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. +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. +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! +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. > @@ -46,7 +46,7 @@ module.exports = withPWA({ }); ``` -Then service worker generated will automatically import your code and run it before other workbox code. +Then Service Worker generated will automatically import your code and run it before other workbox code. ## Usage diff --git a/examples/lifecycle/README.md b/examples/lifecycle/README.md index 3aeb3592..58da3157 100644 --- a/examples/lifecycle/README.md +++ b/examples/lifecycle/README.md @@ -4,7 +4,7 @@ This example demonstrates how to use the `next-pwa` plugin to turn a `next.js` based web application into a progressive web application (PWA) painlessly. -This example demonstrates how to control the service worker registration workflow (instead of automatically registering the service worker) and add an event listener to handle the lifecycle events. It gives you more control through the PWA lifecycle. The key here is to set the `register` option in `next.config.js` to `false` then call `window.workbox.register()` to register the service worker on your own. +This example demonstrates how to control the Service Worker registration workflow (instead of automatically registering the Service Worker) and add an event listener to handle the lifecycle events. It gives you more control through the PWA lifecycle. The key here is to set the `register` option in `next.config.js` to `false` then call `window.workbox.register()` to register the Service Worker on your own. **UPDATE** diff --git a/examples/lifecycle/pages/index.js b/examples/lifecycle/pages/index.js index 45f4e6b5..73827ae8 100644 --- a/examples/lifecycle/pages/index.js +++ b/examples/lifecycle/pages/index.js @@ -28,12 +28,12 @@ const Index = () => { console.log(event); }); - // A common UX pattern for progressive web apps is to show a banner when a service worker has updated and waiting to install. + // A common UX pattern for progressive web apps is to show a banner when a Service Worker has updated and waiting to install. // NOTE: MUST set skipWaiting to false in next.config.js pwa object // https://developers.google.com/web/tools/workbox/guides/advanced-recipes#offer_a_page_reload_for_users const promptNewVersionAvailable = (event) => { - // `event.wasWaitingBeforeRegister` will be false if this is the first time the updated service worker is waiting. - // When `event.wasWaitingBeforeRegister` is true, a previously updated service worker is still waiting. + // `event.wasWaitingBeforeRegister` will be false if this is the first time the updated Service Worker is waiting. + // When `event.wasWaitingBeforeRegister` is true, a previously updated Service Worker is still waiting. // You may want to customize the UI prompt accordingly. if ( confirm( @@ -44,7 +44,7 @@ const Index = () => { window.location.reload(); }); - // Send a message to the waiting service worker, instructing it to activate. + // Send a message to the waiting Service Worker, instructing it to activate. wb.messageSkipWaiting(); } else { console.log( diff --git a/examples/next-9/README.md b/examples/next-9/README.md index f8a62315..0938399f 100644 --- a/examples/next-9/README.md +++ b/examples/next-9/README.md @@ -4,7 +4,7 @@ This example demonstrates how to use `next-pwa` plugin to turn a `next.js` based web application into a progressive web application easily. -Thanks to **Next.js 9+**, we can use `public` folder to serve static files from root url path. It cuts the need to write custom server only to serve those files. Therefore the setup is more easy and concise. We can use `next.config.js` to config `next-pwa` to generates service worker and precache files into `public` folder. +Thanks to **Next.js 9+**, we can use `public` folder to serve static files from root url path. It cuts the need to write custom server only to serve those files. Therefore the setup is more easy and concise. We can use `next.config.js` to config `next-pwa` to generates Service Worker and precache files into `public` folder. > [Check out the lighthouse summary](https://github.com/shadowwalker/next-pwa/blob/master/examples/next-9/lighthouse.pdf), or run the test your self. diff --git a/examples/next-i18next/README.md b/examples/next-i18next/README.md index 423c74c0..bc8dd5ec 100644 --- a/examples/next-i18next/README.md +++ b/examples/next-i18next/README.md @@ -8,7 +8,7 @@ It uses `express` to build a custom server and use [`next-i18next`](https://gith > The express middleware `i18next-express-middleware` is not compatible with `fastify` right not unfortunately. -Because service worker `sw.js` must be served directly without any redirection, make sure it's route is excluded from the i18n middleware is a bit tricky. Please see `index.js` for more details. +Because Service Worker `sw.js` must be served directly without any redirection, make sure it's route is excluded from the i18n middleware is a bit tricky. Please see `index.js` for more details. ## Usage diff --git a/examples/next-image/README.md b/examples/next-image/README.md index 96f2de5e..941b819e 100644 --- a/examples/next-image/README.md +++ b/examples/next-image/README.md @@ -4,7 +4,7 @@ This example demonstrates best practices to serve your images through `next.js` built-in image serving feature. -For best performance, put images in it's own folder other than `public`. This will prevent duplicate precaching entries in the `sw.js` service worker script. Then `import Image from 'next/image'` to use the `Image` component provided from `next.js` in your app. +For best performance, put images in it's own folder other than `public`. This will prevent duplicate precaching entries in the `sw.js` Service Worker script. Then `import Image from 'next/image'` to use the `Image` component provided from `next.js` in your app. ## Usage diff --git a/examples/offline-fallback/README.md b/examples/offline-fallback/README.md index c0e9f647..ca3fa1d8 100644 --- a/examples/offline-fallback/README.md +++ b/examples/offline-fallback/README.md @@ -8,7 +8,7 @@ This example demonstrates how to use `next-pwa` to implement fallback route, image or font when fetch error. Fetch error usually happens when **offline**. (Note fetch is successful even when server returns error codes `404, 400, 500, ...`) -This example uses **Inject Manifest** module from `workbox`. The advantage of using this module is you get more control over your service worker. The disadvantage is that it's more complicated and needs to write more code. +This example uses **Inject Manifest** module from `workbox`. The advantage of using this module is you get more control over your Service Worker. The disadvantage is that it's more complicated and needs to write more code. The idea of implementing comprehensive fallbacks can be found [here](https://developers.google.com/web/tools/workbox/guides/advanced-recipes#comprehensive_fallbacks). diff --git a/package.json b/package.json index c20ded18..0dcc85b5 100644 --- a/package.json +++ b/package.json @@ -14,11 +14,11 @@ "exports": { ".": { "types": "./dist/index.d.ts", - "require": "./dist/ducanh2912-next-pwa.js", + "require": "./dist/ducanh2912-next-pwa.cjs", "default": "./dist/ducanh2912-next-pwa.modern.mjs" } }, - "main": "./dist/ducanh2912-next-pwa.js", + "main": "./dist/ducanh2912-next-pwa.cjs", "module": "./dist/ducanh2912-next-pwa.module.js", "types": "./dist/index.d.ts", "keywords": [ @@ -29,8 +29,8 @@ "service-worker" ], "scripts": { - "dev": "microbundle watch", - "build": "rimraf dist && npm run lint && rollup -c --compact", + "dev": "rimraf dist && rollup -c -w", + "build": "rimraf dist && npm run lint && cross-env NODE_ENV=production rollup -c --compact", "lint": "eslint src --cache --cache-location ./node_modules/.cache/eslint --ext ts,tsx,js,jsx,cjs,mjs", "typecheck": "tsc", "format": "prettier --write .", @@ -40,7 +40,6 @@ "dependencies": { "babel-loader": "9.1.0", "clean-webpack-plugin": "4.0.0", - "globby": "13.1.2", "terser-webpack-plugin": "5.3.6", "workbox-build": "6.5.4", "workbox-webpack-plugin": "6.5.4", @@ -48,6 +47,7 @@ }, "devDependencies": { "@babel/core": "7.20.5", + "@rollup/plugin-node-resolve": "15.0.1", "@rollup/plugin-terser": "0.1.0", "@rollup/plugin-typescript": "10.0.1", "@semantic-release/changelog": "6.0.2", @@ -59,10 +59,12 @@ "@types/node": "18.11.9", "@typescript-eslint/eslint-plugin": "5.45.0", "@typescript-eslint/parser": "5.45.0", + "cross-env": "7.0.3", "eslint": "8.28.0", "eslint-config-prettier": "8.5.0", "eslint-plugin-prettier": "4.2.1", "eslint-plugin-simple-import-sort": "8.0.0", + "fast-glob": "3.2.12", "husky": "8.0.2", "next": "13.0.5", "prettier": "2.8.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 59dd517b..bc049ae7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2,6 +2,7 @@ lockfileVersion: 5.4 specifiers: '@babel/core': 7.20.5 + '@rollup/plugin-node-resolve': 15.0.1 '@rollup/plugin-terser': 0.1.0 '@rollup/plugin-typescript': 10.0.1 '@semantic-release/changelog': 6.0.2 @@ -15,12 +16,15 @@ specifiers: '@typescript-eslint/parser': 5.45.0 babel-loader: 9.1.0 clean-webpack-plugin: 4.0.0 + cross-env: 7.0.3 eslint: 8.28.0 eslint-config-prettier: 8.5.0 eslint-plugin-prettier: 4.2.1 eslint-plugin-simple-import-sort: 8.0.0 - globby: 13.1.2 + fast-glob: 3.2.12 + globby: 11.1.0 husky: 8.0.2 + lodash-es: 4.17.21 next: 13.0.5 prettier: 2.8.0 prettier-plugin-jsdoc: 0.4.2 @@ -40,7 +44,6 @@ specifiers: dependencies: babel-loader: 9.1.0_ztqwsvkb6z73luspkai6ilstpu clean-webpack-plugin: 4.0.0_webpack@5.75.0 - globby: 13.1.2 terser-webpack-plugin: 5.3.6_webpack@5.75.0 workbox-build: 6.5.4 workbox-webpack-plugin: 6.5.4_webpack@5.75.0 @@ -48,6 +51,7 @@ dependencies: devDependencies: '@babel/core': 7.20.5 + '@rollup/plugin-node-resolve': 15.0.1_rollup@3.5.0 '@rollup/plugin-terser': 0.1.0_rollup@3.5.0 '@rollup/plugin-typescript': 10.0.1_3qldpvhx2vwhgdtnpkk4u5tuly '@semantic-release/changelog': 6.0.2_semantic-release@19.0.5 @@ -59,11 +63,15 @@ devDependencies: '@types/node': 18.11.9 '@typescript-eslint/eslint-plugin': 5.45.0_czs5uoqkd3podpy6vgtsxfc7au '@typescript-eslint/parser': 5.45.0_hsf322ms6xhhd4b5ne6lb74y4a + cross-env: 7.0.3 eslint: 8.28.0 eslint-config-prettier: 8.5.0_eslint@8.28.0 eslint-plugin-prettier: 4.2.1_cwlo2dingkvfydnaculr42urve eslint-plugin-simple-import-sort: 8.0.0_eslint@8.28.0 + fast-glob: 3.2.12 + globby: 11.1.0 husky: 8.0.2 + lodash-es: 4.17.21 next: 13.0.5_672uxklweod7ene3nqtsh262ca prettier: 2.8.0 prettier-plugin-jsdoc: 0.4.2_prettier@2.8.0 @@ -1422,10 +1430,12 @@ packages: dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 + dev: true /@nodelib/fs.stat/2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} + dev: true /@nodelib/fs.walk/1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} @@ -1433,6 +1443,7 @@ packages: dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.13.0 + dev: true /@octokit/auth-token/3.0.2: resolution: {integrity: sha512-pq7CwIMV1kmzkFTimdwjAINCXKTajZErLB4wMLYapR2nuB/Jpr66+05wOTZMSCBXP6n4DdDWT2W19Bm17vU69Q==} @@ -1582,6 +1593,24 @@ packages: rollup: 2.79.1 dev: false + /@rollup/plugin-node-resolve/15.0.1_rollup@3.5.0: + resolution: {integrity: sha512-ReY88T7JhJjeRVbfCyNj+NXAG3IIsVMsX9b5/9jC98dRP8/yxlZdz7mHZbHk5zHr24wZZICS5AcXsFZAXYUQEg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.0.2_rollup@3.5.0 + '@types/resolve': 1.20.2 + deepmerge: 4.2.2 + is-builtin-module: 3.2.0 + is-module: 1.0.0 + resolve: 1.22.1 + rollup: 3.5.0 + dev: true + /@rollup/plugin-replace/2.4.2_rollup@2.79.1: resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} peerDependencies: @@ -1871,6 +1900,10 @@ packages: '@types/node': 18.11.9 dev: false + /@types/resolve/1.20.2: + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + dev: true + /@types/retry/0.12.0: resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} dev: true @@ -2352,6 +2385,7 @@ packages: engines: {node: '>=8'} dependencies: fill-range: 7.0.1 + dev: true /browserslist/4.21.4: resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==} @@ -2369,7 +2403,6 @@ packages: /builtin-modules/3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} - dev: false /call-bind/1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} @@ -2585,6 +2618,14 @@ packages: yaml: 1.10.2 dev: true + /cross-env/7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + dependencies: + cross-spawn: 7.0.3 + dev: true + /cross-spawn/7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -2644,7 +2685,6 @@ packages: /deepmerge/4.2.2: resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==} engines: {node: '>=0.10.0'} - dev: false /define-properties/1.1.4: resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} @@ -2700,6 +2740,7 @@ packages: engines: {node: '>=8'} dependencies: path-type: 4.0.0 + dev: true /doctrine/3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} @@ -3013,6 +3054,7 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.5 + dev: true /fast-json-stable-stringify/2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -3025,6 +3067,7 @@ packages: resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} dependencies: reusify: 1.0.4 + dev: true /figures/2.0.0: resolution: {integrity: sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==} @@ -3058,6 +3101,7 @@ packages: engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 + dev: true /find-cache-dir/3.3.2: resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} @@ -3225,6 +3269,7 @@ packages: engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 + dev: true /glob-parent/6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} @@ -3269,17 +3314,6 @@ packages: slash: 3.0.0 dev: true - /globby/13.1.2: - resolution: {integrity: sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - dir-glob: 3.0.1 - fast-glob: 3.2.12 - ignore: 5.2.1 - merge2: 1.4.1 - slash: 4.0.0 - dev: false - /globby/6.1.0: resolution: {integrity: sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==} engines: {node: '>=0.10.0'} @@ -3407,6 +3441,7 @@ packages: /ignore/5.2.1: resolution: {integrity: sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==} engines: {node: '>= 4'} + dev: true /import-fresh/3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -3479,6 +3514,13 @@ packages: has-tostringtag: 1.0.0 dev: false + /is-builtin-module/3.2.0: + resolution: {integrity: sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==} + engines: {node: '>=6'} + dependencies: + builtin-modules: 3.3.0 + dev: true + /is-callable/1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -3499,6 +3541,7 @@ packages: /is-extglob/2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + dev: true /is-fullwidth-code-point/3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} @@ -3510,10 +3553,10 @@ packages: engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 + dev: true /is-module/1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - dev: false /is-negative-zero/2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} @@ -3530,6 +3573,7 @@ packages: /is-number/7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + dev: true /is-obj/1.0.1: resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} @@ -3810,6 +3854,10 @@ packages: p-locate: 5.0.0 dev: true + /lodash-es/4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: true + /lodash.capitalize/4.2.1: resolution: {integrity: sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==} dev: true @@ -3953,6 +4001,7 @@ packages: /merge2/1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + dev: true /micromark-core-commonmark/1.0.6: resolution: {integrity: sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==} @@ -4136,6 +4185,7 @@ packages: dependencies: braces: 3.0.2 picomatch: 2.3.1 + dev: true /mime-db/1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} @@ -4589,6 +4639,7 @@ packages: /path-type/4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + dev: true /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} @@ -4700,6 +4751,7 @@ packages: /queue-microtask/1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true /quick-lru/4.0.1: resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} @@ -4893,6 +4945,7 @@ packages: /reusify/1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true /rimraf/2.7.1: resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} @@ -4941,6 +4994,7 @@ packages: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 + dev: true /sade/1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} @@ -5104,11 +5158,6 @@ packages: engines: {node: '>=8'} dev: true - /slash/4.0.0: - resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} - engines: {node: '>=12'} - dev: false - /source-list-map/2.0.1: resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} dev: false @@ -5433,6 +5482,7 @@ packages: engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 + dev: true /tr46/0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} diff --git a/rollup.config.js b/rollup.config.js index 57c40166..baf5f21b 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -8,7 +8,7 @@ export default [ input: "src/index.ts", output: [ { - file: "dist/ducanh2912-next-pwa.js", + file: "dist/ducanh2912-next-pwa.cjs", format: "cjs", exports: "named", }, @@ -29,7 +29,7 @@ export default [ declaration: true, noEmit: false, }), - terser(), + ...[process.env.NODE_ENV === "production" ? [terser()] : []], ], external: [ "clean-webpack-plugin", @@ -40,7 +40,7 @@ export default [ "fs", "path", "url", - "globby", + "fast-glob", "workbox-window", ], }), @@ -50,7 +50,7 @@ export default [ file: "dist/register.js", format: "esm", }, - plugins: [terser()], + plugins: [...[process.env.NODE_ENV === "production" ? [terser()] : []]], external: ["workbox-window"], }), ]; diff --git a/src/build-custom-worker.ts b/src/build-custom-worker.ts index 1f16bf09..814a9906 100644 --- a/src/build-custom-worker.ts +++ b/src/build-custom-worker.ts @@ -40,7 +40,7 @@ const buildCustomWorker = ({ console.warn( `> [PWA] WARNING: More than one custom worker found (${customWorkerEntries.join( "," - )}), not building a custom worker` + )}), a custom worker will not be built.` ); return; } diff --git a/src/index.ts b/src/index.ts index ed881a4e..4cb682a4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import { CleanWebpackPlugin } from "clean-webpack-plugin"; import crypto from "crypto"; +import fg from "fast-glob"; import fs from "fs"; -import { globbySync } from "globby"; import type { NextConfig } from "next"; import path from "path"; import { fileURLToPath } from "url"; @@ -105,7 +105,9 @@ const withPWAInit = ( ); const registerJs = path.join(__dirname, "register.js"); - const entry = config.entry; + const entry = config.entry as () => Promise< + Record + >; config.entry = () => entry().then((entries: any) => { if ( @@ -114,6 +116,16 @@ const withPWAInit = ( ) { entries["main.js"].unshift(registerJs); } + if ( + entries["main-app"] && + !entries["main-app"].includes(registerJs) + ) { + if (Array.isArray(entries["main-app"])) { + entries["main-app"].unshift(registerJs); + } else if (typeof entries["main-app"] === "string") { + entries["main-app"] = [registerJs, entries["main-app"]]; + } + } return entries; }); @@ -136,18 +148,18 @@ const withPWAInit = ( if (register) { console.log( - `> [PWA] Auto register service worker with: ${path.resolve( + `> [PWA] Auto register Service Worker with: ${path.resolve( registerJs )}` ); } else { console.log( - `> [PWA] Auto register service worker is disabled, please call following code in componentDidMount callback or useEffect hook` + `> [PWA] Auto register Service Worker is disabled, please call following code in componentDidMount callback or useEffect hook` ); console.log(`> [PWA] window.workbox.register()`); } - console.log(`> [PWA] Service worker: ${path.join(_dest, sw)}`); + console.log(`> [PWA] Service Worker: ${path.join(_dest, sw)}`); console.log(`> [PWA] url: ${_sw}`); console.log(`> [PWA] scope: ${_scope}`); @@ -165,26 +177,28 @@ const withPWAInit = ( // precache files in public folder let manifestEntries = additionalManifestEntries ?? []; if (!manifestEntries) { - manifestEntries = globbySync( - [ - "**/*", - "!workbox-*.js", - "!workbox-*.js.map", - "!worker-*.js", - "!worker-*.js.map", - "!fallback-*.js", - "!fallback-*.js.map", - `!${sw.replace(/^\/+/, "")}`, - `!${sw.replace(/^\/+/, "")}.map`, - ...publicExcludes, - ], - { - cwd: "public", - } - ).map((f) => ({ - url: path.posix.join(basePath, `/${f}`), - revision: getRevision(`public/${f}`), - })); + manifestEntries = fg + .sync( + [ + "**/*", + "!workbox-*.js", + "!workbox-*.js.map", + "!worker-*.js", + "!worker-*.js.map", + "!fallback-*.js", + "!fallback-*.js.map", + `!${sw.replace(/^\/+/, "")}`, + `!${sw.replace(/^\/+/, "")}.map`, + ...publicExcludes, + ], + { + cwd: "public", + } + ) + .map((f) => ({ + url: path.posix.join(basePath, `/${f}`), + revision: getRevision(`public/${f}`), + })); } if (cacheStartUrl) { @@ -246,7 +260,7 @@ const withPWAInit = ( if ( asset.name.startsWith("server/") || asset.name.match( - /^(build-manifest\.json|react-loadable-manifest\.json)$/ + /^((app-|^)build-manifest\.json|react-loadable-manifest\.json)$/ ) ) { return true; @@ -302,7 +316,7 @@ const withPWAInit = ( } else { if (dev) { console.log( - "> [PWA] Build in develop mode, cache and precache are mostly disabled. This means offline support is disabled, but you can continue developing other functions in service worker." + "> [PWA] Build in develop mode, cache and precache are mostly disabled. This means that offline support is disabled, but you can continue developing other functions in Service Worker." ); ignoreURLParametersMatching.push(/ts/); diff --git a/src/types.ts b/src/types.ts index 2bf455ed..34e688ce 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,22 +1,121 @@ import { GenerateSWConfig } from "workbox-webpack-plugin"; export interface PluginOptions extends GenerateSWConfig { + /** + * Disable PWA. Set to `true` to completely disable PWA, set to `false` to + * generate Service Worker in both dev and prod. + * + * @default false + */ disable?: boolean; + /** + * Allow this plugin to automatically register Service Worker for you. Set + * this to `false` when you want to handle register Service Worker yourself + * (this can be done in `componentDidMount` of your root app). You can + * consider + * [register.js](https://github.com/DuCanhGH/next-pwa/blob/master/src/register.js) + * an example. + * + * @default true + */ register?: boolean; + /** + * Set output directory for Service Worker. Relative to Next.js's root + * directory. Defaults to `.next`. + */ dest?: string; + /** + * Service Worker script's filename. Defaults to `/sw.js`. Set to a string if + * you want to customize the output filename. + */ sw?: string; + /** + * Service Worker's source file location. Relative to Next.js's root + * directory. Provide this to enable InjectManifest. See more at [Google's + * Workbox + * documentation](https://developer.chrome.com/docs/workbox/reference/workbox-webpack-plugin/#type-InjectManifest). + * If this is not specified then `GenerateSW` will be used to generate Service + * Worker. + */ swSrc?: string; + /** + * Turn on caching for start URL. [Discussion of use cases for this + * option](https://github.com/shadowwalker/next-pwa/pull/296#issuecomment-1094167025) + */ cacheStartUrl?: boolean; + /** + * If your start URL returns different HTML document under different states + * (such as logged in vs. not logged in), this should be set to true. + * Effective only when `cacheStartUrl` is set to `true`. Set to `false` if + * your start URL always returns same HTML document, then start URL will be + * precached and as such help speed up first load time. + * + * @default true + */ dynamicStartUrl?: boolean; + /** + * If your start URL redirect to another route such as `/login`, it's + * recommended to setup this redirected URL for the best user experience. + * Effective when `dynamicStartUrl` is set to `true` + * + * @default undefined + */ dynamicStartUrlRedirect?: string; publicExcludes?: string[]; buildExcludes?: GenerateSWConfig["exclude"]; + /** + * Config precached routes to fallback when both cache and network are 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. + * + * - `fallbacks.document` - fallback route for document (page), defaults to + * `/_offline` if you have created that page. + * - `fallbacks.image` - fallback route for images, defaults to none. + * - `fallbacks.audio` - fallback route for audios, defaults to none. + * - `fallbacks.video` - fallback route for videos, defaults to none. + * - `fallbacks.font` - fallback route for fonts, defaults to none. + */ fallbacks?: Fallbacks; + /** + * Enable additional route caching when navigating between pages with + * `next/link` on frontend. Check this + * [example](https://github.com/DuCanhGH/next-pwa/tree/master/examples/cache-on-front-end-nav) + * for some context on why this is implemented. This improve user experience + * in some special cases but it also adds some overhead because of additional + * network calls, you can consider this a tradeoff. + * + * @default false + */ cacheOnFrontEndNav?: boolean; + /** + * URL scope for PWA. Defaults to `basePath` in `next.config.js`. Set to + * `/foo` so that paths under `/foo` are PWA while others are not + */ scope?: string; + /** + * Customize the directory where `next-pwa` looks for a custom workerc + * implementation to add to the Service Worker generated by Workbox. For more + * information, check out the [custom worker + * example](https://github.com/DuCanhGH/next-pwa/tree/master/examples/custom-ts-worker). + * The plugin will look into root and `src` directory for this directory. + * Relative to Next.js's root directory. Defaults to `worker`. + */ customWorkerDir?: string; + /** + * Reload the app when it detects that it has gone back online. Indicate if + * the app should call `location.reload()` to refresh the app. + * + * @default true + */ reloadOnOnline?: boolean; - /** @deprecated Use `basePath` in `next.config.js` instead. */ + /** + * URL prefix to allow hosting static files on a subdomain. Defaults to root + * URL. For example, use `/subdomain` if the app is hosted on + * `https://www.example.com/subdomain`. + * + * @deprecated Use `basePath` in `next.config.js` instead. + */ subdomainPrefix?: string; } diff --git a/tsconfig.json b/tsconfig.json index efe057d6..18e4ae5f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,5 +20,5 @@ "noEmit": true }, "include": ["**/*.ts", "**/*.tsx"], - "exclude": ["node_modules", "examples"] + "exclude": ["node_modules", "dist", "examples"] }