Skip to content

Commit 6940ad4

Browse files
Karen BaneyKaren Baney
authored andcommitted
Initial commit
0 parents  commit 6940ad4

File tree

8,179 files changed

+1002904
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

8,179 files changed

+1002904
-0
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Auto detect text files and perform LF normalization
2+
* text=auto

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Karen Baney
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# vue-cli-plugin-workbox-pwa
2+
A Vue CLI Plugin with advanced support for workbox 6.x and Vue 3. Includes the ability to debug the service worker locally.
3+
4+
How is this different from the vue-cli-plugin-pwa?
5+
- vue-cli-plugin-pwa depends on register-service-worker but this does not. The register-service-worker makes several assumptions about how the PWA should work, but this does not meet advanced use cases like cached apis, cache first, etc.
6+
- vue-cli-plugin-pwa does not provide a way to easily debug the service worker. This plugin does. It makes use of a new environment variable to enable NODE_ENV=development and injecting the manifest with customizations.
7+
- We started with some of the manifest features of vue-cli-plugin-pwa. Because this is a more fully featured pwa, we start with the assumption that InjectManifest mode of Workbox is needed.
8+
9+
## Set up
10+
The scripts commands expects a global install of `npm serve`.
11+
12+
Run: `npm install -g serve`.
13+
14+
## Configuration
15+
16+
Configuration is handled via the `pwa` property of either the `vue.config.js`
17+
file, or the `"vue"` field in `package.json`.
18+
19+
- **pwa.workboxPluginMode**
20+
21+
This allows you to choose between the two modes supported by the underlying
22+
[`workbox-webpack-plugin`](https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin).
23+
24+
- `'InjectManifest'` (default), allows you to start with an existing service worker file,
25+
and creates a copy of that file with a "precache manifest" injected into it.
26+
27+
- `'GenerateSW'` will lead to a new service worker file being created
28+
each time you rebuild your web app.
29+
30+
The "[Which Plugin to Use?](https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin#which_plugin_to_use)"
31+
guide can help you choose between the two modes.
32+
33+
- **pwa.workboxOptions**
34+
35+
These options are passed on through to the underlying `workbox-webpack-plugin`.
36+
37+
For more information on what values are supported, please see the guide for
38+
[`GenerateSW`](https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin#full_generatesw_config)
39+
or for [`InjectManifest`](https://developers.google.com/web/tools/workbox/modules/workbox-webpack-plugin#full_injectmanifest_config).
40+
41+
- **pwa.name**
42+
43+
- Default: "name" field in `package.json`
44+
45+
Used as the value for the `apple-mobile-web-app-title` meta tag in the generated HTML. Note you will need to edit `public/manifest.json` to match this.
46+
47+
- **pwa.themeColor**
48+
49+
- Default: `'#4DBA87'`
50+
51+
- **pwa.msTileColor**
52+
53+
- Default: `'#000000'`
54+
55+
- **pwa.appleMobileWebAppCapable**
56+
57+
- Default: `'no'`
58+
59+
This defaults to `'no'` because iOS before 11.3 does not have proper PWA support. See [this article](https://medium.com/@firt/dont-use-ios-web-app-meta-tag-irresponsibly-in-your-progressive-web-apps-85d70f4438cb) for more details.
60+
61+
- **pwa.appleMobileWebAppStatusBarStyle**
62+
63+
- Default: `'default'`
64+
65+
- **pwa.assetsVersion**
66+
67+
- Default: `''`
68+
69+
This option is used if you need to add a version to your icons and manifest, against browser’s cache. This will append `?v=<pwa.assetsVersion>` to the URLs of the icons and manifest.
70+
71+
- **pwa.manifestPath**
72+
73+
- Default: `'manifest.json'`
74+
75+
The path of app’s manifest. If the path is an URL, the plugin won't generate a manifest.json in the dist directory during the build.
76+
77+
- **pwa.manifestOptions**
78+
79+
- Default: `{}`
80+
81+
The object will be used to generate the `manifest.json`
82+
83+
If the following attributes are not defined in the object, the options of `pwa` or default options will be used instead.
84+
- name: `pwa.name`
85+
- short_name: `pwa.name`
86+
- display: `'standalone'`
87+
- theme_color: `pwa.themeColor`
88+
- start_url: `'/'`
89+
90+
By default, the start url is set to '/'. If it is set to '.', it will work fine in a web browser or when installed on Windows, but will result in a blank when installed on iOS or Android.
91+
92+
The router will alse need to have an entry for `path: '/'`. This will meet the requirements for a good `start_url` in Lighthouse.
93+
94+
- **pwa.manifestCrossorigin**
95+
96+
- Default: `undefined`
97+
98+
Value for `crossorigin` attribute in manifest link tag in the generated HTML. You may need to set this if your PWA is behind an authenticated proxy. See [cross-origin values](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-crossorigin) for more details.
99+
100+
- **pwa.iconPaths**
101+
102+
- Defaults:
103+
104+
```js
105+
{
106+
faviconSVG: 'img/icons/favicon.svg',
107+
favicon32: 'img/icons/favicon-32x32.png',
108+
favicon16: 'img/icons/favicon-16x16.png',
109+
appleTouchIcon: 'img/icons/apple-touch-icon-152x152.png',
110+
maskIcon: 'img/icons/safari-pinned-tab.svg',
111+
msTileImage: 'img/icons/msapplication-icon-144x144.png'
112+
}
113+
```
114+
115+
Change these values to use different paths for your icons. You can use `null` as a value and that icon will not be included.
116+
117+
### Example Configuration
118+
119+
```js
120+
// Inside vue.config.js
121+
module.exports = {
122+
// ...other vue-cli plugin options...
123+
pwa: {
124+
name: 'My App',
125+
themeColor: '#4DBA87',
126+
msTileColor: '#000000',
127+
appleMobileWebAppCapable: 'yes',
128+
appleMobileWebAppStatusBarStyle: 'black',
129+
130+
manifestOptions: {
131+
start_url: '/'
132+
},
133+
// configure the workbox plugin
134+
workboxPluginMode: 'InjectManifest',
135+
workboxOptions: {
136+
// swSrc is required in InjectManifest mode.
137+
swSrc: './src/sw.js',
138+
swDest: 'service-worker.js',
139+
// ...other workbox options
140+
}
141+
}
142+
}
143+
```
144+
145+
## Installing in an Already Created Project
146+
147+
``` sh
148+
vue add workbox-pwa
149+
```
150+
151+
## Injected dependencies and devDependencies
152+
153+
```
154+
"dependencies": {
155+
"workbox-cacheable-response": "^6.1.5",
156+
"workbox-core": "^6.1.5",
157+
"workbox-expiration": "^6.1.5",
158+
"workbox-routing": "^6.1.5",
159+
"workbox-strategies": "^6.1.5",
160+
"workbox-window": "^6.1.5"
161+
},
162+
"devDependencies": {
163+
"workbox-webpack-plugin": "^6.1.5"
164+
}
165+
```
166+
167+
## Injected webpack-chain Rules
168+
169+
- `config.plugin('workbox')`
170+
- `config.plugin('html')` title attribute is added to match the pwa.name
171+
172+
## Injected files
173+
174+
- **public/img/**
175+
Default app icons are added.
176+
- **public/**
177+
Default robots.txt is added.
178+
- **src/sw.js**
179+
Default service worker file is added with:
180+
- common caching scenarios for index.html, css, js, images.
181+
- Workbox's syntax for injecting the manifest. `precacheAndRoute(self.__WB_MANIFEST)`
182+
- Workbox clear cache for outdated versions of Workbox. `cleanupOutdatedCaches()`
183+
-
184+
185+
186+
187+
## License
188+
MIT

generator/index.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
module.exports = (api, options, rootOptions) => {
2+
api.extendPackage({
3+
scripts: {
4+
'pwa-build': 'vue-cli-service build --mode pwalocalserve',
5+
'pwa-serve': 'npm run pwa-build && serve -s dist -l 8080'
6+
},
7+
dependencies: {
8+
'workbox-cacheable-response': '^6.1.5',
9+
'workbox-core': '^6.1.5',
10+
'workbox-expiration': '^6.1.5',
11+
'workbox-routing': '^6.1.5',
12+
'workbox-strategies': '^6.1.5',
13+
'workbox-window': '^6.1.5'
14+
},
15+
devDependencies: {
16+
'workbox-webpack-plugin': '^6.1.5'
17+
}
18+
})
19+
20+
api.render('./template')
21+
22+
// Inject service worker registration into main.js/.ts
23+
const importRegister = `import register from './service-worker/register-service-worker'`
24+
api.onCreateComplete(() => {
25+
// inject to main.js
26+
const fs = require('fs')
27+
const ext = api.hasPlugin('typescript') ? 'ts' : 'js'
28+
const mainPath = api.resolve(`./src/main.${ext}`)
29+
30+
// get existing content
31+
let contentMain = fs.readFileSync(mainPath, { encoding: 'utf-8' })
32+
const lines = contentMain.split(/\r?\n/g).reverse()
33+
34+
// inject import
35+
const lastImportIndex = lines.findIndex(line => line.match(/^import/))
36+
lines[lastImportIndex] += importRegister
37+
38+
// modify add to content
39+
const addedContent = `register()`
40+
contentMain = lines.reverse().join(`\n${addedContent}\n`)
41+
fs.writeFileSync(mainPath, contentMain, { encoding: 'utf-8' })
42+
})
43+
}

generator/template/_env

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
extend: '@vue/cli-service/generator/template/_env'
3+
---
4+
5+
<%# REPLACE %>
6+
VUE_APP_PWA_LOCAL_SERVE=false
7+
<%# END_REPLACE %>

generator/template/_env.pwalocalserve

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
NODE_ENV=development
2+
VUE_APP_DEBUG=true
3+
VUE_APP_PWA_LOCAL_SERVE=true

generator/template/main.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import register from './service-worker/register-service-worker' // registers service worker
2+
register()
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Lines changed: 3 additions & 0 deletions
Loading

generator/template/public/robots.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
User-agent: *
2+
Disallow:
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { Workbox } from 'workbox-window'
2+
3+
// const autoUpdate = async (registration: ServiceWorkerRegistration | undefined) => {
4+
// setInterval(async () => {
5+
// try {
6+
// /* eslint-disable-next-line no-unused-expressions */
7+
// await registration?.update()
8+
// } catch (err) {
9+
// /* eslint-disable-next-line no-console */
10+
// console.log(`sw: registration autoUpdate failed: ${err}`)
11+
// }
12+
// }, 1000 * 60 * 60)
13+
// }
14+
15+
// const manualUpdateAvailable = (registration: ServiceWorkerRegistration | undefined) => {
16+
// // Wires up an event that we can listen to in the app. Example: listen for available update and prompt user to update.
17+
// document.dispatchEvent(
18+
// new CustomEvent('swUpdated', { detail: registration }))
19+
// }
20+
21+
const register = async () => {
22+
if ('serviceWorker' in navigator) {
23+
// Workbox combines the ./src/sw.js file and injected manifest into the servicer-worker.js file in /dist
24+
// Uses vue.config.js workboxOptions.swSrc for the location of sw.js and swDest for the output location of 'service-worker.js'.
25+
// You can override the file names and locations by changing the values of workboxOptions in vue.config.js.
26+
const wb = new Workbox(`${process.env.BASE_URL}service-worker.js`)
27+
28+
// wire up instance of registration so we can take further action on it.
29+
const registration = await wb.register()
30+
console.log(`sw: waiting: ${registration.waiting}`)
31+
32+
// autoUpdate(registration)
33+
34+
// wb.addEventListener('activated', async (event) => {
35+
// if (event.isUpdate) {
36+
// // event.isUpdate=true means the service worker was already registered and there is a new version available.
37+
38+
// // this only triggers self.skipWaiting. It still doesn't force the app to update. See /composables/use-service-worker.ts for updating app.
39+
// wb.messageSkipWaiting()
40+
41+
// // Wires up an event that we can listen to in the app for manual updates. Example: listen for available update and prompt user to update.
42+
// manualUpdateAvailable(registration)
43+
// } else {
44+
// // first time use when event.isUpdate = false
45+
// // service worker should claim the client immediately since its the first install.
46+
// wb.messageSW({ type: 'CLIENTS_CLAIM' })
47+
// /* eslint-disable-next-line no-console */
48+
// console.log('sw: clientsClaim called.')
49+
// }
50+
// })
51+
52+
// // This is the code piece that GenerateSW mode can't provide for us.
53+
// // This code listens for the user's confirmation to update the app.
54+
// wb.addEventListener('message', (event) => {
55+
// /* eslint-disable-next-line no-console */
56+
// console.log('sw: message event listener hit.')
57+
// if (event.data && event.data.type === 'SKIP_WAITING') {
58+
// /* eslint-disable-next-line no-console */
59+
// console.log('sw: message SKIP_WAITING called.')
60+
// }
61+
// })
62+
}
63+
}
64+
65+
export default register

0 commit comments

Comments
 (0)