├── .commitlintrc.js ├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .husky ├── .gitignore ├── commit-msg ├── pre-commit └── pre-push ├── .huskyrc ├── .npmignore ├── .prettierignore ├── .prettierrc.js ├── .releaserc ├── .template-lintrc.js ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── addon ├── instance-initializers │ └── sw.js └── services │ └── service-worker.js ├── app ├── instance-initializers │ └── sw.js └── services │ └── service-worker.js ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── jsconfig.json ├── lib └── broccoli-workbox.js ├── node-tests ├── .eslintrc ├── default-config.js ├── default-ember-cli-build.js └── index.js ├── package.json ├── testem.js ├── tests ├── acceptance │ ├── auto-register-test.js │ ├── configuration-test.js │ └── simple-test.js ├── dummy │ ├── app │ │ ├── app.js │ │ ├── index.html │ │ ├── router.js │ │ ├── styles │ │ │ └── style.css │ │ └── templates │ │ │ └── application.hbs │ ├── config │ │ ├── coverage.js │ │ ├── ember-cli-update.json │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ └── public │ │ ├── assets │ │ └── service-workers │ │ │ └── skip-waiting.js │ │ ├── engines-dist │ │ └── my-engine │ │ │ └── assets │ │ │ └── service-workers │ │ │ └── engine.js │ │ └── robots.txt ├── helpers │ └── .gitkeep ├── index.html └── test-helper.js └── yarn.lock /.commitlintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | }; 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | .*/ 17 | .eslintcache 18 | 19 | # ember-try 20 | /.node_modules.ember-try/ 21 | /bower.json.ember-try 22 | /package.json.ember-try 23 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | ecmaFeatures: { 10 | legacyDecorators: true, 11 | }, 12 | }, 13 | plugins: ['ember'], 14 | extends: [ 15 | 'eslint:recommended', 16 | 'plugin:ember/recommended', 17 | 'plugin:prettier/recommended', 18 | ], 19 | env: { 20 | browser: true, 21 | }, 22 | rules: {}, 23 | overrides: [ 24 | // node files 25 | { 26 | files: [ 27 | './.commitlintrc.js', 28 | './.eslintrc.js', 29 | './.prettierrc.js', 30 | './.template-lintrc.js', 31 | './ember-cli-build.js', 32 | './index.js', 33 | './testem.js', 34 | './blueprints/*/index.js', 35 | './config/**/*.js', 36 | './lib/**/*.js', 37 | './node-tests/**/*.js', 38 | './tests/dummy/config/**/*.js', 39 | ], 40 | parserOptions: { 41 | sourceType: 'script', 42 | }, 43 | env: { 44 | browser: false, 45 | node: true, 46 | }, 47 | plugins: ['node'], 48 | extends: ['plugin:node/recommended'], 49 | }, 50 | { 51 | // Test files: 52 | files: ['tests/**/*-test.{js,ts}'], 53 | extends: ['plugin:qunit/recommended'], 54 | }, 55 | ], 56 | }; 57 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | name: Test 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@v2 10 | - name: Set up node 11 | uses: actions/setup-node@v2 12 | with: 13 | node-version: 16 14 | cache: yarn 15 | - name: Install dependencies 16 | run: yarn install 17 | - name: Test 18 | run: yarn test 19 | - name: Release dry-run 20 | run: yarn test:release 21 | env: 22 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 23 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 24 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 25 | - name: Upload codecov 26 | uses: codecov/codecov-action@v1 27 | with: 28 | token: ${{ secrets.CODECOV_TOKEN }} 29 | files: ./coverage/ember/lcov.info,./coverage/node/lcov.info 30 | fail_ci_if_error: true 31 | regression: 32 | if: github.ref != 'refs/heads/master' 33 | needs: test 34 | runs-on: ubuntu-latest 35 | name: Regression test - ${{ matrix.ember_try_scenario }} - Experimental ${{ matrix.experimental }} 36 | strategy: 37 | matrix: 38 | ember_try_scenario: 39 | - ember-lts-3.24 40 | - ember-lts-3.28 41 | - ember-release 42 | - ember-classic 43 | experimental: [false] 44 | include: 45 | - ember_try_scenario: ember-beta 46 | experimental: true 47 | - ember_try_scenario: ember-canary 48 | experimental: true 49 | - ember_try_scenario: embroider-safe 50 | experimental: true 51 | - ember_try_scenario: embroider-optimized 52 | experimental: true 53 | continue-on-error: ${{ matrix.experimental }} 54 | timeout-minutes: 5 55 | steps: 56 | - name: Checkout 57 | uses: actions/checkout@v2 58 | - name: Set up node 59 | uses: actions/setup-node@v2 60 | with: 61 | node-version: 16 62 | cache: yarn 63 | - name: Install dependencies 64 | run: yarn install 65 | - name: Test 66 | run: yarn ember try:one ${{ matrix.ember_try_scenario }} 67 | release: 68 | if: github.ref == 'refs/heads/master' 69 | needs: [test] 70 | runs-on: ubuntu-latest 71 | name: Release 72 | steps: 73 | - name: Checkout 74 | uses: actions/checkout@v2 75 | with: 76 | fetch-depth: 0 77 | token: ${{ secrets.GH_TOKEN }} 78 | - name: Set up node 79 | uses: actions/setup-node@v2 80 | with: 81 | node-version: 16 82 | registry-url: 'https://registry.npmjs.org' 83 | - name: Configure CI Git User 84 | run: | 85 | git config --global user.email adrigzr@users.noreply.github.com 86 | git config --global user.name adrigzr 87 | - name: Install dependencies 88 | run: yarn install 89 | - name: Release 90 | run: yarn semantic-release 91 | env: 92 | HUSKY: 0 93 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 94 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 95 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist/ 5 | /tmp/ 6 | 7 | # dependencies 8 | /bower_components/ 9 | /node_modules/ 10 | 11 | # misc 12 | .DS_Store 13 | /.env* 14 | /.pnp* 15 | /.sass-cache 16 | /.eslintcache 17 | /.nyc_output/ 18 | /connect.lock 19 | /coverage/ 20 | /libpeerconnection.log 21 | /npm-debug.log* 22 | /testem.log 23 | /yarn-error.log 24 | 25 | # ember-try 26 | /.node_modules.ember-try/ 27 | /bower.json.ember-try 28 | /package.json.ember-try 29 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn commitlint --edit "$1" 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn lint-staged 5 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn test 5 | -------------------------------------------------------------------------------- /.huskyrc: -------------------------------------------------------------------------------- 1 | # This loads nvm.sh and sets the correct PATH before running hook 2 | export NVM_DIR="$HOME/.nvm" 3 | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # dependencies 6 | /bower_components/ 7 | 8 | # misc 9 | /.bowerrc 10 | /.editorconfig 11 | /.ember-cli 12 | /.env* 13 | /.eslintcache 14 | /.eslintignore 15 | /.eslintrc.js 16 | /.git/ 17 | /.gitignore 18 | /.prettierignore 19 | /.prettierrc.js 20 | /.template-lintrc.js 21 | /.travis.yml 22 | /.watchmanconfig 23 | /bower.json 24 | /config/ember-try.js 25 | /CONTRIBUTING.md 26 | /ember-cli-build.js 27 | /node-tests/ 28 | /testem.js 29 | /tests/ 30 | /yarn-error.log 31 | /yarn.lock 32 | .gitkeep 33 | 34 | # ember-try 35 | /.node_modules.ember-try/ 36 | /bower.json.ember-try 37 | /package.json.ember-try 38 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | /.nyc_output/ 16 | !.* 17 | .eslintcache 18 | *.html 19 | *.hbs 20 | CHANGELOG.md 21 | 22 | # ember-try 23 | /.node_modules.ember-try/ 24 | /bower.json.ember-try 25 | /package.json.ember-try 26 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | }; 6 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@semantic-release/commit-analyzer", 4 | "@semantic-release/release-notes-generator", 5 | "@semantic-release/changelog", 6 | "@semantic-release/npm", 7 | "@semantic-release/git", 8 | "@semantic-release/github" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended', 5 | }; 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [3.3.0](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v3.2.0...v3.3.0) (2023-06-14) 2 | 3 | 4 | ### Features 5 | 6 | * remove console log ([686ef73](https://github.com/BBVAEngineering/ember-cli-workbox/commit/686ef739297025a804429ed88e97798e7e4f8c54)) 7 | 8 | # [3.2.0](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v3.1.2...v3.2.0) (2023-06-13) 9 | 10 | 11 | ### Features 12 | 13 | * movemos el escenario ember-beta a experimentales ([d3459e8](https://github.com/BBVAEngineering/ember-cli-workbox/commit/d3459e87de44eb16617af6e4da893f84db0fee12)) 14 | * update workbox ([371c1bd](https://github.com/BBVAEngineering/ember-cli-workbox/commit/371c1bdcb2f60710819c79141b4f4920524bf47a)) 15 | * update workbox version ([2281419](https://github.com/BBVAEngineering/ember-cli-workbox/commit/2281419a001dae280be9f84d4484dab3bc1d9b5b)) 16 | * update workbox version ([de1974b](https://github.com/BBVAEngineering/ember-cli-workbox/commit/de1974b6a75be4f77c688ef820c7cd249d60630a)) 17 | * update yarn.lock ([19df250](https://github.com/BBVAEngineering/ember-cli-workbox/commit/19df2503f87dcddadd660f82594e389b40390640)) 18 | 19 | ## [3.1.2](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v3.1.1...v3.1.2) (2022-02-03) 20 | 21 | 22 | ### Bug Fixes 23 | 24 | * remove public tree ([29e499c](https://github.com/BBVAEngineering/ember-cli-workbox/commit/29e499cf600bf20fc65d33be83c873bb4c52b365)) 25 | * upgrade dependencies ([59c207d](https://github.com/BBVAEngineering/ember-cli-workbox/commit/59c207d4a1d4e89bdc8167ea71391714936363c2)) 26 | 27 | ## [3.1.1](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v3.1.0...v3.1.1) (2022-02-02) 28 | 29 | 30 | ### Bug Fixes 31 | 32 | * allow override default settings ([679ab32](https://github.com/BBVAEngineering/ember-cli-workbox/commit/679ab320ac87bd2ada286fd6c817613e55833e51)), closes [#108](https://github.com/BBVAEngineering/ember-cli-workbox/issues/108) 33 | 34 | # [3.1.0](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v3.0.0...v3.1.0) (2022-02-01) 35 | 36 | 37 | ### Features 38 | 39 | * update to ember 4.1 ([35d6c01](https://github.com/BBVAEngineering/ember-cli-workbox/commit/35d6c011b24b8cce92fe5267ca9145761c9b3ee4)), closes [#106](https://github.com/BBVAEngineering/ember-cli-workbox/issues/106) 40 | 41 | # [3.0.0](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v2.0.0...v3.0.0) (2021-06-08) 42 | 43 | ### Features 44 | 45 | - **config:** move workbox config from environment to ember-cli-build ([44f8fdf](https://github.com/BBVAEngineering/ember-cli-workbox/commit/44f8fdf433db6fad5e8ef125a017653bf8ca7fc8)) 46 | 47 | ### BREAKING CHANGES 48 | 49 | - **config:** workbox config has been moved to ember-cli-build.js 50 | 51 | # [2.0.0](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v1.2.0...v2.0.0) (2020-09-02) 52 | 53 | ### Bug Fixes 54 | 55 | - fix pr issues ([ed60cb4](https://github.com/BBVAEngineering/ember-cli-workbox/commit/ed60cb4b9c577c0d62841a63699e84eae8862222)) 56 | - **build:** add warnings to broccoli-workbox stdout ([d5b4362](https://github.com/BBVAEngineering/ember-cli-workbox/commit/d5b43625fe576d2fd41985a19a927c24d3e78623)), closes [#91](https://github.com/BBVAEngineering/ember-cli-workbox/issues/91) 57 | - remove getWithDefault deprecation ([e5fe855](https://github.com/BBVAEngineering/ember-cli-workbox/commit/e5fe855a2e647be415522a259b0b78f5c691fd10)), closes [#101](https://github.com/BBVAEngineering/ember-cli-workbox/issues/101) 58 | 59 | ### Features 60 | 61 | - use rootURL when registering service worker ([5eb0af6](https://github.com/BBVAEngineering/ember-cli-workbox/commit/5eb0af60eea03308b623e336231dc97ae84238e0)), closes [#102](https://github.com/BBVAEngineering/ember-cli-workbox/issues/102) [#100](https://github.com/BBVAEngineering/ember-cli-workbox/issues/100) 62 | 63 | ### BREAKING CHANGES 64 | 65 | - appending rootURL to service worker could break some apps 66 | 67 | # [1.2.0](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v1.1.0...v1.2.0) (2020-07-07) 68 | 69 | ### Features 70 | 71 | - move files to the addon directory ([1e4f39a](https://github.com/BBVAEngineering/ember-cli-workbox/commit/1e4f39a9310490eb279fb44a9816e257535e5ea1)) 72 | 73 | # [1.1.0](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v1.0.1...v1.1.0) (2020-06-16) 74 | 75 | ### Features 76 | 77 | - **config:** new option 'importScriptsGlobPatterns' ([722c038](https://github.com/BBVAEngineering/ember-cli-workbox/commit/722c03840fe8bb924ab973ecbe00b9a32c249ef2)) 78 | 79 | ## [1.0.1](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v1.0.0...v1.0.1) (2020-02-10) 80 | 81 | ### Bug Fixes 82 | 83 | - **build:** do not ignore .gitkeep from npm ([59994a3](https://github.com/BBVAEngineering/ember-cli-workbox/commit/59994a3ef879cc7ea752fd25755074b5e065be54)) 84 | - **build:** ensure dir is created ([1ee3d00](https://github.com/BBVAEngineering/ember-cli-workbox/commit/1ee3d00b5f9339ccd061be08238a62b0327319ac)) 85 | 86 | # [1.0.0](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v0.6.1...v1.0.0) (2020-02-10) 87 | 88 | ### Features 89 | 90 | - **package:** upgrade deps ([d1a4fad](https://github.com/BBVAEngineering/ember-cli-workbox/commit/d1a4fad20ff8ae5d6e334cfe7a26061cdc75d9ce)) 91 | - **service:** change events ([18ca8fa](https://github.com/BBVAEngineering/ember-cli-workbox/commit/18ca8fa19dee5e4dcc38bb319113ef028a7734aa)) 92 | - **service:** es6 classes ([d4a20bf](https://github.com/BBVAEngineering/ember-cli-workbox/commit/d4a20bf8c24fcfe0f4cea7c4c0d4e5db0b5d2535)) 93 | - **sw:** new life cycle ([81932ac](https://github.com/BBVAEngineering/ember-cli-workbox/commit/81932acf7954c3711af33606dad131f68cc02f4c)) 94 | 95 | ### Styles 96 | 97 | - **sw:** empty line ([6047fa8](https://github.com/BBVAEngineering/ember-cli-workbox/commit/6047fa8f2b0e695b4f6dbdf33a716394050a629e)) 98 | 99 | ### BREAKING CHANGES 100 | 101 | - **sw:** Update workbox to v5 102 | 103 | ## [0.6.1](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v0.6.0...v0.6.1) (2020-01-14) 104 | 105 | ### Bug Fixes 106 | 107 | - **sw:** do not create empty SW when disabled ([af1cf2d](https://github.com/BBVAEngineering/ember-cli-workbox/commit/af1cf2d)), closes [#78](https://github.com/BBVAEngineering/ember-cli-workbox/issues/78) 108 | 109 | # [0.6.0](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v0.5.2...v0.6.0) (2019-12-16) 110 | 111 | ### Features 112 | 113 | - add importScriptsTransform option ([440f1f1](https://github.com/BBVAEngineering/ember-cli-workbox/commit/440f1f1)) 114 | 115 | ## [0.5.2](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v0.5.1...v0.5.2) (2019-03-08) 116 | 117 | ### Bug Fixes 118 | 119 | - **package:** update workbox-build to version 4.1.0 ([84f6f95](https://github.com/BBVAEngineering/ember-cli-workbox/commit/84f6f95)), closes [#26](https://github.com/BBVAEngineering/ember-cli-workbox/issues/26) 120 | 121 | ## [0.5.1](https://github.com/BBVAEngineering/ember-cli-workbox/compare/v0.5.0...v0.5.1) (2019-01-09) 122 | 123 | ### Bug Fixes 124 | 125 | - force publish patch ([a1a2e0d](https://github.com/BBVAEngineering/ember-cli-workbox/commit/a1a2e0d)) 126 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | ## Installation 4 | 5 | - `git clone ` 6 | - `cd my-addon` 7 | - `yarn install` 8 | 9 | ## Linting 10 | 11 | - `yarn lint:hbs` 12 | - `yarn lint:js` 13 | - `yarn lint:js -- --fix` 14 | 15 | ## Running tests 16 | 17 | - `ember test` – Runs the test suite on the current Ember version 18 | - `ember test --server` – Runs the test suite in "watch mode" 19 | - `ember try:each` – Runs the test suite against multiple Ember versions 20 | 21 | ## Running the dummy application 22 | 23 | - `ember serve` 24 | - Visit the dummy application at [http://localhost:4200](http://localhost:4200). 25 | 26 | For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/). 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ember-cli-workbox 2 | 3 | [![Build Status](https://travis-ci.org/BBVAEngineering/ember-cli-workbox.svg?branch=master)](https://travis-ci.org/BBVAEngineering/ember-cli-workbox) 4 | [![GitHub version](https://badge.fury.io/gh/BBVAEngineering%2Fember-cli-workbox.svg)](https://badge.fury.io/gh/BBVAEngineering%2Fember-cli-workbox) 5 | [![NPM version](https://badge.fury.io/js/ember-cli-workbox.svg)](https://badge.fury.io/js/ember-cli-workbox) 6 | [![Dependency Status](https://david-dm.org/BBVAEngineering/ember-cli-workbox.svg)](https://david-dm.org/BBVAEngineering/ember-cli-workbox) 7 | [![codecov](https://codecov.io/gh/BBVAEngineering/ember-cli-workbox/branch/master/graph/badge.svg)](https://codecov.io/gh/BBVAEngineering/ember-cli-workbox) 8 | [![Greenkeeper badge](https://badges.greenkeeper.io/BBVAEngineering/ember-cli-workbox.svg)](https://greenkeeper.io/) 9 | [![Ember Observer Score](https://emberobserver.com/badges/ember-cli-workbox.svg)](https://emberobserver.com/addons/ember-cli-workbox) 10 | 11 | ## Information 12 | 13 | [![NPM](https://nodei.co/npm/ember-cli-workbox.png?downloads=true&downloadRank=true)](https://nodei.co/npm/ember-cli-workbox/) 14 | 15 | A plugin for your Ember-cli build process, giving your app offline caching as a progressive enhancement, using service workers. Ember-cli-workbox will add a service worker to your Ember App registering it on initial page load. 16 | 17 | This addon simplify service worker registration and caching, powered by [workbox-build](https://www.npmjs.com/package/workbox-build). Workbox library automate precaching of static resources (HTML, JavaScript, CSS, and images) and handle runtime caching and fallback strategies. It allowed us to implement a performant strategy in which a static content is always served directly from the cache, and dynamic or remote resources are served from the network, with fallbacks to cached or static responses when needed. 18 | 19 | For more details on Workbox check out: 20 | 21 | - [Workbox Google Developers](https://developers.google.com/web/tools/workbox/) 22 | - [Service Workers cookbook](https://serviceworke.rs/) 23 | 24 | ## Installation 25 | 26 | `ember install ember-cli-workbox` 27 | 28 | ## Dependencies 29 | 30 | "ember-cli-uglify": "^2.0.0", 31 | 32 | ## Configuration 33 | 34 | If you need to customize **ember-cli-workbox configuration** you can do it like this: 35 | 36 | ```javascript 37 | // app/config/environment.js 38 | 39 | ENV['ember-cli-workbox'] = { 40 | enabled: environment !== 'test', 41 | debug: true, 42 | autoRegister: true, 43 | importScriptsGlobPatterns: ['assets/service-workers/*.js'], 44 | }; 45 | ``` 46 | 47 | ```javascript 48 | // ember-cli-build.js 49 | const app = new EmberAddon(defaults, { 50 | 'ember-cli-workbox': { 51 | importScriptsTransform(importScripts) { 52 | return importScripts.map( 53 | (importScript) => `https://example-cdn.com/${importScript}` 54 | ); 55 | }, 56 | }, 57 | }); 58 | ``` 59 | 60 | | Property | Type | Description | 61 | | :-------------------------: | :--------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------: | 62 | | `enabled` | `Boolean` | Addon is enabled. Defaults `true` for production builds | 63 | | `debug` | `Boolean` | Log serviceworker states (registering, updating, etc) | 64 | | `autoRegister` | `Boolean` | Enable the sw registration before initializing the application | 65 | | `importScriptsTransform` | `Function` | Allows for transformation of array sent to workbox [importScripts](https://developers.google.com/web/tools/workbox/modules/workbox-build#generateSW-importScripts) | 66 | | `importScriptsGlobPatterns` | `Array` | Define files that are going to be imported using [importScripts](https://developers.google.com/web/tools/workbox/modules/workbox-build#generateSW-importScripts) | 67 | 68 | You can further customize ember-cli-workbox by setting **workbox configurations** in your `config/environment.js` on in your `ember-cli-build.js` 69 | 70 | _Note_: `importScriptsTransform` and `workbox` must be defined in your `ember-cli-build.js`. 71 | 72 | ```javascript 73 | //ember-cli-build.js 74 | 75 | const app = new EmberAddon(defaults, { 76 | workbox: { 77 | globPatterns: ['**/*.{html,js,css}'], 78 | 79 | globDirectory: './', 80 | 81 | globIgnores: [], 82 | // ... 83 | }, 84 | }); 85 | ``` 86 | 87 | | Property | Description | 88 | | :-----------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | 89 | | `swDest` | The path to the final service worker file that will be created by the build process, relative to the build directory. Default path: `./sw.js` | 90 | | `globPatterns` | Files matching against any of these glob patterns will be included in the precache manifest. By default sw precaches all our ember application assets that match `**/*.{json,css,js,png,svg,eot,ttf,woff,jpg,gif,ico,xml,html,txt}` | 91 | | `globDirectory` | The base directory you wish to match globPatterns against, related to the build directory. Default './' | 92 | | `globIgnores` | Files matching against any of these glob patterns will be excluded from the file manifest, overriding any matches from globPatterns (E.g. `globIgnores: ['**\/ignored.html']`) | 93 | | `templatedUrls` | If a URL is rendered generated based on some server-side logic, its contents may depend on multiple files or on some other unique string value. | 94 | | `cacheId` | An optional ID to be prepended to caches used by workbox-sw. This is primarily useful for local development where multiple sites may be served from the same http://localhost origin. Defaults to your app name (config.APP.name). | 95 | | `maximumFileSizeToCacheInBytes` | This value can be used to determine the maximum size of files that will be precached | 96 | | `runtimeCaching` | Passing in an array of objects containing urlPatterns, handlers, and potentially options that will add the appropriate code to the generated service worker to handle runtime caching. The handler values correspond the names of the [strategies](https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-sw.Strategies) supported by workbox-sw (cacheFirst, cacheOnly, networkFirst, networkOnly, staleWhileRevalidate) | 97 | 98 | ```javascript 99 | runtimeCaching: [ 100 | { 101 | // You can use a RegExp as the pattern: 102 | urlPattern: /https://api.example.com/, 103 | handler: 'cacheFirst', 104 | // Any options provided will be used when 105 | // creating the caching strategy. 106 | options: { 107 | cacheName: 'my-api-cache', 108 | cacheExpiration: { 109 | maxEntries: 10, 110 | }, 111 | }, 112 | }, 113 | // ... 114 | ] 115 | ``` 116 | 117 | Note that `importScripts` parameter is overriden by this addon to include all js files on `/public/assets/service-workers/*` folder. If you want to change this path use `importScriptsGlobPatterns` option. 118 | 119 | > For more details on Workbox configuration take a look at: [Workbox Google Developers](https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build). 120 | 121 | ## Ember service 122 | 123 | This addon bundles a new Ember service called `service-worker`. 124 | This service will register/unregister the workers when necessary. 125 | 126 | | Property | Type | Description | 127 | | :-----------: | :-------: | :------------------------------: | 128 | | `sw` | `Object` | The navigator SW API | 129 | | `isSupported` | `Boolean` | Navigator is complatible with SW | 130 | 131 | **Methods:** 132 | 133 | - `register(swFile)`: Registers new service worker given a file path. 134 | - `unregisterAll()`: Unregisters all service workers. 135 | 136 | ## Subscribing to events 137 | 138 | If you are using workbox with `clientsClaim: true` and `skipWaiting: true` your serviceWorker will became active automaticatly. 139 | In this case, if you want to force reload simply do this: 140 | 141 | ```JavaScript 142 | // "this.serviceWorker" means the Ember service 143 | this.serviceWorker.on('activated', () => { 144 | console.log('New service worker controlling the page. Forcing reload to apply new changes.'); 145 | window.location.reload(); 146 | }); 147 | ``` 148 | 149 | But if you want to take control of what is the state of serviceWorker, do not activate `clientsClaim` and `skipWaiting`. 150 | The recomendation is using the Ember's `service-worker` service (bundled with this addon) that triggers the following events: 151 | 152 | - `error`: SW not registered. 153 | - `waiting`: New SW waiting for controlling page. 154 | - `activated`: The new SW is ready to respond. 155 | - `registrationComplete`: SW successfully registered. 156 | - `unregistrationComplete`: All SW are unregistered. 157 | 158 | ### Why and how to use this events? 159 | 160 | By default, users have to close all tabs to a site in order to update a Service Worker (the Refresh button is not enough). 161 | If you make a mistake here, **users will see an outdated version of your site** even after refreshing. 162 | Service Workers break the Refresh button because they behave like “apps,” refusing to update while the app is still running, in order to maintain code consistency and client-side data consistency. We can write code to notify users when a new version is available. 163 | 164 | This addon make it easy for you and implements [google recommendation](https://developers.google.com/web/tools/workbox/guides/advanced-recipes#offer_a_page_reload_for_users). 165 | Basically, what you have to do is subscribing to the event `waiting`. When event is triggered, send a message to sw in order to launch `skipWaiting + clients.claim` on it to turn it active (you can do this just calling forceActivate method on serviceWorkerService). When service worker became active it will send a message "reload-window" and "newSWActive" will be triggered. 166 | 167 | **Example:** 168 | 169 | ```javascript 170 | // /mixins/service-worker-states.js 171 | 172 | import Route from '@ember/routing/route'; 173 | import { inject as service } from '@ember/service'; 174 | 175 | export default class MyRoute extends Route { 176 | @service serviceWorker; 177 | 178 | async beforeModel() { 179 | await super.beforeModel(...arguments); 180 | 181 | // Do not call this event twice! 182 | this.subscribeToSWEvents(); 183 | } 184 | 185 | subscribeToSWEvents() { 186 | this.serviceWorker.on('activated', (reg) => { 187 | window.alert('Content is now available offline!'); 188 | }); 189 | 190 | this.serviceWorker.on('waiting', (reg) => { 191 | if (window.confirm('New version available! Refresh?')) { 192 | sw.forceActivate(reg); 193 | } 194 | }); 195 | 196 | this.serviceWorker.on('updated', () => { 197 | window.location.reload(); 198 | }); 199 | } 200 | } 201 | ``` 202 | 203 | ## Prevent caching lazy engines 204 | 205 | By default, this addons precaches everything, that means all the lazy engines will be precached in your service worker. 206 | To prevent precaching it, just exclude the `engine-dist` in the addon config: 207 | 208 | ```javascript 209 | var ENV = { 210 | workbox: { 211 | globIgnores: ['engines-dist/**/*'], 212 | 213 | runtimeCaching: [ 214 | { 215 | urlPattern: /engines-dist/, 216 | handler: 'networkFirst', 217 | }, 218 | ], 219 | }, 220 | }; 221 | ``` 222 | 223 | ## Debugging serviceWorker generation on build 224 | 225 | ```bash 226 | $ DEBUG=ember-cli:workbox ember s 227 | ``` 228 | 229 | ## Future improvements 230 | 231 | `ember-cli-workbox` currently do not implement `workboxBuild.injectManifest()` feature, only works generating a new serviceworker. 232 | 233 | **What is injectManifest feature?** 234 | 235 | If you have an existing serviceWorker, `workbox-build` can modify it to inject the manifest file to precache our static files. 236 | Basically you should have a placeholder array which is populated automatically by `workboxBuild.injectManifest()`. 237 | 238 | ```javascript 239 | // my existing service worker 240 | workboxSW.precache([]); 241 | ``` 242 | 243 | Sometimes you'll need more control over what is cached, strategies used and custom code inside the server worker. You can do this by setting your own service worker and using the `WorkboxSW` object directly. 244 | 245 | ## Contributing 246 | 247 | We're thankful to the community for contributing any improvements. 248 | 249 | Do not forget to follow our `eslint` rules and make test for the new functionalities/fixes. 250 | -------------------------------------------------------------------------------- /addon/instance-initializers/sw.js: -------------------------------------------------------------------------------- 1 | import { debug } from '@ember/debug'; 2 | import { get } from '@ember/object'; 3 | 4 | function getWithDefault(obj, path, def) { 5 | const value = get(obj, path); 6 | 7 | return typeof value !== 'undefined' ? value : def; 8 | } 9 | 10 | export function getConfig(appInstance) { 11 | const config = appInstance.resolveRegistration('config:environment'); 12 | const isProdBuild = config.environment === 'production'; 13 | 14 | return { 15 | isEnabled: getWithDefault(config, 'ember-cli-workbox.enabled', isProdBuild), 16 | debugAddon: getWithDefault(config, 'ember-cli-workbox.debug', !isProdBuild), 17 | swDestFile: getWithDefault(config, 'ember-cli-workbox.swDest', 'sw.js'), 18 | autoRegister: getWithDefault( 19 | config, 20 | 'ember-cli-workbox.autoRegister', 21 | true 22 | ), 23 | }; 24 | } 25 | 26 | export function initialize(appInstance) { 27 | const swService = appInstance.lookup('service:service-worker'); 28 | const { isEnabled, debugAddon, swDestFile } = getConfig(appInstance); 29 | 30 | swService.set('debug', debugAddon); 31 | 32 | // first checks whether the browser supports service workers 33 | if (swService.get('isSupported')) { 34 | // Load and register pre-caching Service Worker 35 | if (isEnabled) { 36 | swService.register(swDestFile); 37 | } else { 38 | swService.unregisterAll(); 39 | } 40 | } else { 41 | debug('Service workers are not supported in this browser.'); 42 | } 43 | } 44 | 45 | export default { 46 | name: 'ember-cli-workbox', 47 | initialize(appInstance) { 48 | const { autoRegister } = getConfig(appInstance); 49 | 50 | /* istanbul ignore else */ 51 | if (autoRegister) { 52 | initialize(appInstance); 53 | } 54 | }, 55 | }; 56 | -------------------------------------------------------------------------------- /addon/services/service-worker.js: -------------------------------------------------------------------------------- 1 | import Service from '@ember/service'; 2 | import Evented from '@ember/object/evented'; 3 | import { debug } from '@ember/debug'; 4 | import { getOwner } from '@ember/application'; 5 | 6 | const EventedService = Service.extend(Evented); 7 | 8 | /* 9 | * 10 | * Service worker states: 11 | * "installing" - the install event has fired, but is not yet completed 12 | * "installed" - install completed 13 | * "activating" - the activate event has fired, but is not yet completed 14 | * "activated" - fully active 15 | * "redundant" - discarded. Either failed install, or it's been replaced by a newer version 16 | * 17 | * Events triggered: 18 | * "error" - sw not registered 19 | * "waiting" - new sw waiting for controlling page 20 | * "activated" - the new sw is ready to respond 21 | * "registrationComplete" - sw successfully registered 22 | * "unregistrationComplete" - all sw are unregistered 23 | */ 24 | export default class ServiceWorker extends EventedService { 25 | get config() { 26 | return getOwner(this).resolveRegistration('config:environment'); 27 | } 28 | 29 | constructor() { 30 | super(...arguments); 31 | 32 | const sw = window.navigator.serviceWorker; 33 | let isSupported = false; 34 | 35 | /* istanbul ignore else */ 36 | if (sw) { 37 | isSupported = ['getRegistrations', 'register'].every( 38 | (func) => func in sw 39 | ); 40 | } 41 | 42 | this.sw = sw; 43 | this.isSupported = isSupported; 44 | } 45 | 46 | _log(message) { 47 | /* istanbul ignore else */ 48 | if (this.debug) { 49 | debug(`ember-cli-workbox: ${message}`); 50 | } 51 | } 52 | 53 | async register(swFile) { 54 | try { 55 | const registration = await this.sw.register( 56 | `${this.config.rootURL}${swFile}` 57 | ); 58 | 59 | return this._onRegistration(registration); 60 | } catch (error) { 61 | this.trigger('error', error); 62 | this._log('Service Worker registration failed: ', error); 63 | 64 | throw error; 65 | } 66 | } 67 | 68 | /* 69 | * Utility function that unregisters SW, but you still need to reload to see SW removed completely 70 | * This does not delete items in Cache 71 | */ 72 | async unregisterAll() { 73 | const registrations = await this.sw.getRegistrations(); 74 | 75 | await Promise.all( 76 | registrations.map(async (reg) => { 77 | const boolean = await reg.unregister(); 78 | 79 | if (boolean) { 80 | this._log(`${reg} unregistered`); 81 | } else { 82 | this._log(`Error unregistering ${reg}`); 83 | } 84 | }) 85 | ); 86 | 87 | this.trigger('unregistrationComplete'); 88 | this._log('Unregistrations complete'); 89 | } 90 | 91 | _onRegistration(registration) { 92 | this._log(`Registration succeeded. Scope is ${registration.scope}`); 93 | this.trigger('registrationComplete'); 94 | 95 | /* istanbul ignore else */ 96 | if (!registration) { 97 | return; 98 | } 99 | 100 | /* istanbul ignore else */ 101 | if (registration.waiting) { 102 | // SW is waiting to activate. Can occur if multiple clients open and 103 | // one of the clients is refreshed. 104 | this._waiting(registration); 105 | } 106 | 107 | // We are currently controlled so a new SW may be found... 108 | // Add a listener in case a new SW is found, 109 | registration.addEventListener('updatefound', () => { 110 | const installingWorker = registration.installing; 111 | 112 | /* istanbul ignore else */ 113 | if (!installingWorker) { 114 | return; 115 | } 116 | 117 | if (installingWorker.state === 'installed') { 118 | this._checkSWInstalled(installingWorker, registration); 119 | } else { 120 | installingWorker.addEventListener('statechange', () => { 121 | this._checkSWInstalled(installingWorker, registration); 122 | }); 123 | } 124 | }); 125 | } 126 | 127 | _checkSWInstalled(installingWorker, registration) { 128 | switch (installingWorker.state) { 129 | case 'installed': 130 | if (navigator.serviceWorker.controller) { 131 | // At this point, the updated precached content has been fetched, 132 | // but the previous service worker will still serve the older 133 | // content until all client tabs are closed. 134 | this._log( 135 | 'New content is available and will be used when all tabs for this page are closed.' 136 | ); 137 | 138 | // Execute callback 139 | this._waiting(registration); 140 | } else { 141 | // At this point, everything has been precached. 142 | // It's the perfect time to display a "Content is cached for offline use." message. 143 | this._log( 144 | 'New serviceworker is controlling page. Content is now available offline!' 145 | ); 146 | } 147 | break; 148 | default: 149 | break; 150 | } 151 | } 152 | 153 | _waiting(registration) { 154 | this._log( 155 | 'New serviceworker is waiting to activate. New or updated content is available.' 156 | ); 157 | this.trigger('waiting', registration); 158 | 159 | registration.waiting.addEventListener('statechange', (event) => { 160 | if (event.target.state === 'activated') { 161 | this.trigger('activated', registration); 162 | } 163 | }); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /app/instance-initializers/sw.js: -------------------------------------------------------------------------------- 1 | export { 2 | default, 3 | initialize, 4 | } from 'ember-cli-workbox/instance-initializers/sw'; 5 | -------------------------------------------------------------------------------- /app/services/service-worker.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-cli-workbox/services/service-worker'; 2 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup'); 5 | 6 | module.exports = async function () { 7 | return { 8 | useYarn: true, 9 | scenarios: [ 10 | { 11 | name: 'ember-lts-3.24', 12 | npm: { 13 | devDependencies: { 14 | 'ember-source': '~3.24.3', 15 | }, 16 | }, 17 | }, 18 | { 19 | name: 'ember-lts-3.28', 20 | npm: { 21 | devDependencies: { 22 | 'ember-source': '~3.28.0', 23 | }, 24 | }, 25 | }, 26 | { 27 | name: 'ember-release', 28 | npm: { 29 | devDependencies: { 30 | 'ember-source': await getChannelURL('release'), 31 | }, 32 | }, 33 | }, 34 | { 35 | name: 'ember-beta', 36 | npm: { 37 | devDependencies: { 38 | 'ember-source': await getChannelURL('beta'), 39 | }, 40 | }, 41 | }, 42 | { 43 | name: 'ember-canary', 44 | npm: { 45 | devDependencies: { 46 | 'ember-source': await getChannelURL('canary'), 47 | }, 48 | }, 49 | }, 50 | { 51 | name: 'ember-classic', 52 | env: { 53 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 54 | 'application-template-wrapper': true, 55 | 'default-async-observers': false, 56 | 'template-only-glimmer-components': false, 57 | }), 58 | }, 59 | npm: { 60 | devDependencies: { 61 | 'ember-source': '~3.28.0', 62 | }, 63 | ember: { 64 | edition: 'classic', 65 | }, 66 | }, 67 | }, 68 | embroiderSafe(), 69 | embroiderOptimized(), 70 | ], 71 | }; 72 | }; 73 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (/* environment, appConfig */) { 4 | return {}; 5 | }; 6 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function (defaults) { 6 | let app = new EmberAddon(defaults, { 7 | workbox: { 8 | swDest: 'sw.js', 9 | }, 10 | }); 11 | 12 | // Use `app.import` to add additional libraries to the generated 13 | // output files. 14 | // 15 | // If you need to use different assets in different 16 | // environments, specify an object as the first parameter. That 17 | // object's keys should be the environment name and the values 18 | // should be the asset to use in that environment. 19 | // 20 | // If the library that you are including contains AMD or ES6 21 | // modules that you would like to import into your application 22 | // please specify an object with the list of modules as keys 23 | // along with the exports of each module as its value. 24 | 25 | const { maybeEmbroider } = require('@embroider/test-setup'); 26 | return maybeEmbroider(app, { 27 | skipBabel: [ 28 | { 29 | package: 'qunit', 30 | }, 31 | ], 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mergeTrees = require('broccoli-merge-trees'); 4 | const BroccoliWorkbox = require('./lib/broccoli-workbox'); 5 | 6 | module.exports = { 7 | name: require('./package').name, 8 | 9 | isDevelopingAddon: () => true, 10 | 11 | config(env, baseConfig) { 12 | const emberCliWorkboxOptions = baseConfig['ember-cli-workbox']; 13 | const appOptions = 14 | (this.app && this.app.options['ember-cli-workbox']) || {}; 15 | const projectName = (baseConfig.APP && baseConfig.APP.name) || 'app'; 16 | let workboxOptions = (this.app && this.app.options.workbox) || {}; 17 | let options = emberCliWorkboxOptions || {}; 18 | 19 | workboxOptions = Object.assign( 20 | { 21 | swDest: 'sw.js', 22 | globDirectory: './', 23 | globPatterns: [ 24 | '**/*.{json,css,js,png,svg,eot,ttf,woff,jpg,gif,ico,xml,html,txt}', 25 | ], 26 | skipWaiting: false, 27 | clientsClaim: false, 28 | cacheId: projectName, 29 | }, 30 | workboxOptions 31 | ); 32 | 33 | env = env || process.env.EMBER_ENV; 34 | 35 | // Do nothing if no ENV. For example, when running an ember generator. 36 | /* istanbul ignore else */ 37 | if (!env) { 38 | return; 39 | } 40 | 41 | const isProdBuild = Boolean(env.match('prod')); 42 | 43 | options = Object.assign( 44 | { 45 | enabled: isProdBuild, 46 | debug: !isProdBuild, 47 | importScriptsGlobPatterns: ['assets/service-workers/*.js'], 48 | }, 49 | options, 50 | appOptions 51 | ); 52 | 53 | this._options = options; 54 | this.workboxOptions = workboxOptions; 55 | 56 | // Append "workbox" config to "ember-cli-workbox" config 57 | if (emberCliWorkboxOptions) { 58 | emberCliWorkboxOptions.swDest = workboxOptions.swDest; 59 | } 60 | }, 61 | 62 | postprocessTree(type, tree) { 63 | if (type !== 'all') { 64 | return tree; 65 | } 66 | 67 | const workboxFunnel = new BroccoliWorkbox([tree], { 68 | options: this._options, 69 | workboxOptions: this.workboxOptions, 70 | }); 71 | 72 | return mergeTrees([tree, workboxFunnel], { 73 | overwrite: true, 74 | }); 75 | }, 76 | }; 77 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true 4 | }, 5 | "exclude": ["node_modules", "dist", "coverage", "recordings"] 6 | } 7 | -------------------------------------------------------------------------------- /lib/broccoli-workbox.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | /* eslint-disable no-sync */ 3 | const glob = require('glob'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const debug = require('debug')('ember-cli:workbox'); 7 | const Plugin = require('broccoli-plugin'); 8 | const prettyBytes = require('pretty-bytes'); 9 | const workboxBuild = require('workbox-build'); 10 | const workboxBuildPkg = require('workbox-build/package.json'); 11 | const rimraf = require('rimraf'); 12 | const chalk = require('chalk'); 13 | const { red, green, yellow } = chalk; 14 | 15 | function cleanDir(directory) { 16 | return new Promise((resolve, reject) => 17 | rimraf(directory, (err, result) => { 18 | if (err) { 19 | reject(err); 20 | } 21 | resolve(result); 22 | }) 23 | ); 24 | } 25 | 26 | class BroccoliWorkbox extends Plugin { 27 | constructor(inputNodes, options = {}) { 28 | super(inputNodes, { 29 | annotation: options.annotation || 'broccoli-workbox', 30 | // see `options` in the below README to see a full list of constructor options 31 | }); 32 | 33 | this.options = options.options; 34 | this.workboxOptions = options.workboxOptions; 35 | } 36 | 37 | removeServiceWorker(filePath) { 38 | /* istanbul ignore else */ 39 | if (fs.existsSync(filePath)) { 40 | debug(yellow('Addon disabled. Service worker exist, remove it')); 41 | fs.unlinkSync(filePath); 42 | } 43 | } 44 | 45 | async _generateSW(cleanPromise, workboxOptions) { 46 | try { 47 | await cleanPromise; 48 | 49 | const { count, size, warnings } = await workboxBuild.generateSW( 50 | workboxOptions 51 | ); 52 | 53 | debug(green('Service worker successfully generated.')); 54 | debug( 55 | green( 56 | `${count} files will be precached, totalling ${prettyBytes(size)}.` 57 | ) 58 | ); 59 | 60 | /* istanbul ignore else */ 61 | if (warnings) { 62 | debug(yellow(warnings)); 63 | } 64 | } catch (e) { 65 | debug(red(`Could not generate service Worker ${e.name}`)); 66 | 67 | throw Error(e); 68 | } 69 | } 70 | 71 | async build() { 72 | const workboxOptions = Object.assign({}, this.workboxOptions); 73 | const directory = this.inputPaths[0]; 74 | 75 | workboxOptions.swDest = path.join(directory, workboxOptions.swDest); 76 | 77 | /* istanbul ignore else */ 78 | if (!this.options.enabled) { 79 | debug(yellow('Addon disabled. Disable and remove service worker...')); 80 | 81 | this.removeServiceWorker(workboxOptions.swDest); 82 | 83 | return false; 84 | } 85 | 86 | workboxOptions.globDirectory = path.join( 87 | directory, 88 | workboxOptions.globDirectory 89 | ); 90 | 91 | let cleanPromise = Promise.resolve(); 92 | const workboxDirectory = path.join( 93 | directory, 94 | `workbox-v${workboxBuildPkg.version}` 95 | ); 96 | 97 | // Remove workbox libraries directory to prevent exception on recopying it. 98 | /* istanbul ignore else */ 99 | if ( 100 | !workboxOptions.importWorkboxFromCDN && 101 | fs.existsSync(workboxDirectory) 102 | ) { 103 | cleanPromise = cleanDir(workboxDirectory); 104 | } 105 | 106 | const filesToIncludeInSW = this.options.importScriptsGlobPatterns.reduce( 107 | (acc, pattern) => { 108 | const patterns = glob.sync(pattern, { 109 | cwd: workboxOptions.globDirectory, 110 | }); 111 | 112 | return [...acc, ...patterns]; 113 | }, 114 | [] 115 | ); 116 | 117 | workboxOptions.importScripts = filesToIncludeInSW; 118 | /* istanbul ignore else */ 119 | if (this.options.importScriptsTransform) { 120 | workboxOptions.importScripts = this.options.importScriptsTransform( 121 | workboxOptions.importScripts 122 | ); 123 | } 124 | 125 | return this._generateSW(cleanPromise, workboxOptions); 126 | } 127 | } 128 | 129 | module.exports = BroccoliWorkbox; 130 | -------------------------------------------------------------------------------- /node-tests/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /node-tests/default-config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment) { 4 | const ENV = { 5 | modulePrefix: 'simple-app', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. 'with-controller': true 13 | }, 14 | EXTEND_PROTOTYPES: { 15 | // Prevent Ember Data from overriding Date.parse. 16 | Date: false, 17 | }, 18 | }, 19 | 20 | APP: { 21 | // Here you can pass flags/options to your application instance 22 | // when it is created 23 | }, 24 | }; 25 | 26 | if (environment === 'development') { 27 | // ENV.APP.LOG_RESOLVER = true; 28 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 29 | // ENV.APP.LOG_TRANSITIONS = true; 30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 31 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 32 | } 33 | 34 | if (environment === 'test') { 35 | // Testem prefers this... 36 | ENV.locationType = 'none'; 37 | 38 | // keep test console output quieter 39 | ENV.APP.LOG_ACTIVE_GENERATION = false; 40 | ENV.APP.LOG_VIEW_LOOKUPS = false; 41 | 42 | ENV.APP.rootElement = '#ember-testing'; 43 | ENV.APP.autoboot = false; 44 | } 45 | 46 | if (environment === 'production') { 47 | // here you can enable a production-specific feature 48 | } 49 | 50 | return ENV; 51 | }; 52 | -------------------------------------------------------------------------------- /node-tests/default-ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function (defaults) { 6 | const app = new EmberAddon(defaults, { 7 | 'ember-cli-workbox': { 8 | importScriptsTransform(importScripts) { 9 | return importScripts.map( 10 | (importScript) => process.env.IMPORT_SCRIPTS_PREFIX + importScript 11 | ); 12 | }, 13 | }, 14 | }); 15 | 16 | // Use `app.import` to add additional libraries to the generated 17 | // output files. 18 | // 19 | // If you need to use different assets in different 20 | // environments, specify an object as the first parameter. That 21 | // object's keys should be the environment name and the values 22 | // should be the asset to use in that environment. 23 | // 24 | // If the library that you are including contains AMD or ES6 25 | // modules that you would like to import into your application 26 | // please specify an object with the list of modules as keys 27 | // along with the exports of each module as its value. 28 | 29 | return app.toTree(); 30 | }; 31 | -------------------------------------------------------------------------------- /node-tests/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-sync */ 2 | const assert = require('chai').assert; 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const rimraf = require('rimraf').sync; 6 | const exec = require('child_process').exec; 7 | 8 | const TEST_TIMEOUT = 120000; 9 | const MOCK_CONFIG = path.resolve(__dirname, 'default-config.js'); 10 | const MOCK_BUILD_CONFIG = path.resolve(__dirname, 'default-ember-cli-build.js'); 11 | const emberCLIPath = path.resolve( 12 | __dirname, 13 | '../node_modules/ember-cli/bin/ember' 14 | ); 15 | const fixturePath = path.resolve(__dirname, '..'); 16 | const outputSWPath = outputFilePath('sw.js'); 17 | const configPath = path.resolve(fixturePath, 'tests/dummy/config'); 18 | 19 | // Using process.env to be accessible independent of file location 20 | process.env.IMPORT_SCRIPTS_PREFIX = '2d57274b-581a-4017-9b88-7f0f04b2d1a1'; 21 | 22 | function runEmberCommand(packagePath, command) { 23 | return new Promise((resolve, reject) => 24 | exec( 25 | `${emberCLIPath} ${command}`, 26 | { 27 | cwd: packagePath, 28 | }, 29 | (err, result) => { 30 | if (err) { 31 | reject(err); 32 | } 33 | 34 | resolve(result); 35 | } 36 | ) 37 | ); 38 | } 39 | 40 | function cleanup(packagePath) { 41 | rimraf(path.join(packagePath, 'dist')); 42 | rimraf(path.join(packagePath, 'tmp')); 43 | } 44 | 45 | function outputFilePath(file) { 46 | return path.join(fixturePath, 'dist', file); 47 | } 48 | 49 | function assertFileExists(filePath) { 50 | assert.ok(fs.existsSync(filePath), `${filePath} exists`); 51 | } 52 | 53 | function assertFileDoesNotExist(filePath) { 54 | assert.isNotOk(fs.existsSync(filePath), `${filePath} does not exist`); 55 | } 56 | 57 | function assertContains(filePath, regexp) { 58 | const fileContent = fs.readFileSync(filePath, 'utf8'); 59 | 60 | assert.ok(fileContent.match(regexp), `${filePath} contains ${regexp}`); 61 | } 62 | 63 | function mockConfig() { 64 | // config enviroment 65 | fs.renameSync( 66 | path.resolve(configPath, 'environment.js'), 67 | path.resolve(configPath, 'tmp.js') 68 | ); 69 | fs.renameSync(MOCK_CONFIG, path.resolve(configPath, 'environment.js')); 70 | 71 | // ember cli build 72 | fs.renameSync( 73 | path.resolve(fixturePath, 'ember-cli-build.js'), 74 | path.resolve(fixturePath, 'tmp.js') 75 | ); 76 | fs.renameSync( 77 | MOCK_BUILD_CONFIG, 78 | path.resolve(fixturePath, 'ember-cli-build.js') 79 | ); 80 | } 81 | 82 | function restoreConfig() { 83 | // config enviroment 84 | fs.renameSync(path.resolve(configPath, 'environment.js'), MOCK_CONFIG); 85 | fs.renameSync( 86 | path.resolve(configPath, 'tmp.js'), 87 | path.resolve(configPath, 'environment.js') 88 | ); 89 | 90 | // ember cli build 91 | fs.renameSync( 92 | path.resolve(fixturePath, 'ember-cli-build.js'), 93 | MOCK_BUILD_CONFIG 94 | ); 95 | fs.renameSync( 96 | path.resolve(fixturePath, 'tmp.js'), 97 | path.resolve(fixturePath, 'ember-cli-build.js') 98 | ); 99 | } 100 | 101 | describe('Addon is enabled for production build', function () { 102 | this.timeout(TEST_TIMEOUT); 103 | 104 | context('Precaches and register serviceworker', () => { 105 | before(async () => { 106 | mockConfig(); 107 | 108 | await runEmberCommand(fixturePath, 'build --prod'); 109 | }); 110 | 111 | after(async () => { 112 | restoreConfig(); 113 | 114 | await cleanup(fixturePath); 115 | }); 116 | 117 | it('produces a sw.js file', () => { 118 | assertFileExists(outputSWPath); 119 | }); 120 | 121 | it('precaches assets', () => { 122 | assertContains( 123 | outputFilePath('sw.js'), 124 | /engines-dist\/my-engine\/assets\/service-workers\/engine.js/ 125 | ); 126 | assertContains( 127 | outputFilePath('sw.js'), 128 | /assets\/service-workers\/skip-waiting.js/ 129 | ); 130 | assertContains(outputFilePath('sw.js'), /assets\/dummy\.[cjs|]/); 131 | assertContains(outputFilePath('sw.js'), /vendor\.[cjs|]/); 132 | assertContains(outputFilePath('sw.js'), /index\.html/); 133 | assertContains(outputFilePath('sw.js'), /robots\.txt/); 134 | }); 135 | 136 | it('produces a sw skip waiting file, which is imported on sw.js', () => { 137 | assertFileExists( 138 | outputFilePath('assets/service-workers/skip-waiting.js') 139 | ); 140 | assertContains( 141 | outputSWPath, 142 | /"assets\/service-workers\/skip-waiting.js"/ 143 | ); 144 | }); 145 | 146 | it('produces a sw engine file, which is imported on sw.js', () => { 147 | assertFileExists( 148 | outputFilePath( 149 | 'engines-dist/my-engine/assets/service-workers/engine.js' 150 | ) 151 | ); 152 | assertContains( 153 | outputSWPath, 154 | /"engines-dist\/my-engine\/assets\/service-workers\/engine.js"/ 155 | ); 156 | }); 157 | 158 | it('applies importScriptsTransform', () => { 159 | assertContains(outputSWPath, process.env.IMPORT_SCRIPTS_PREFIX); 160 | }); 161 | }); 162 | }); 163 | 164 | describe('Addon is disabled for development', function () { 165 | this.timeout(TEST_TIMEOUT); 166 | 167 | context('Precaches nothing and does not register serviceworker', () => { 168 | before(async () => { 169 | mockConfig(); 170 | 171 | await runEmberCommand(fixturePath, 'build --prod'); 172 | await runEmberCommand(fixturePath, 'build'); 173 | }); 174 | 175 | after(async () => { 176 | restoreConfig(); 177 | 178 | await cleanup(fixturePath); 179 | }); 180 | 181 | it('does not produce a sw.js file', () => { 182 | assertFileDoesNotExist(outputSWPath); 183 | }); 184 | }); 185 | 186 | context('Addon was enabled before and then disabled', () => { 187 | before(async () => { 188 | mockConfig(); 189 | 190 | await runEmberCommand(fixturePath, 'build --prod'); 191 | }); 192 | 193 | after(async () => { 194 | restoreConfig(); 195 | 196 | await cleanup(fixturePath); 197 | }); 198 | 199 | it('removes sw.js file', async () => { 200 | assertFileExists(outputSWPath); 201 | 202 | await runEmberCommand(fixturePath, 'build'); 203 | 204 | assertFileDoesNotExist(outputSWPath); 205 | }); 206 | }); 207 | }); 208 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-workbox", 3 | "version": "3.3.0", 4 | "description": "Service worker generator with precaching and some basic configurable options using workbox-build", 5 | "keywords": [ 6 | "service-worker", 7 | "sw", 8 | "workbox", 9 | "manifest", 10 | "ember-addon" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/BBVAEngineering/ember-cli-workbox.git" 15 | }, 16 | "license": "MIT", 17 | "author": "BBVAEngineering", 18 | "directories": { 19 | "doc": "doc", 20 | "test": "tests" 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/BBVAEngineering/ember-cli-workbox/issues" 24 | }, 25 | "homepage": "https://github.com/BBVAEngineering/ember-cli-workbox", 26 | "scripts": { 27 | "build": "ember build --environment=production", 28 | "check-coverage": "istanbul check-coverage coverage/coverage-final.json --statements 60 --branches 60 --functions 60 --lines 60", 29 | "clean-coverage": "rm -rf coverage", 30 | "commit": "git-cz", 31 | "lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"", 32 | "lint:fix": "npm-run-all --continue-on-error lint:*:fix", 33 | "lint:hbs": "ember-template-lint .", 34 | "lint:hbs:fix": "ember-template-lint . --fix", 35 | "lint:js": "eslint . --cache --max-warnings 0", 36 | "lint:js:fix": "eslint . --fix", 37 | "lint:style": "prettier --check .", 38 | "lint:style:fix": "prettier --write .", 39 | "merge-coverage": "cp coverage/ember/coverage-final.json coverage/ember.json && cp coverage/node/coverage-final.json coverage/node.json && nyc merge coverage coverage/coverage-final.json", 40 | "posttest": "npm run merge-coverage && npm run report-coverage && npm run check-coverage && npm run validate-coverage", 41 | "prepare": "husky install", 42 | "pretest": "npm run clean-coverage", 43 | "report-coverage": "istanbul report --include=coverage/coverage-final.json text", 44 | "semantic-release": "semantic-release", 45 | "start": "ember serve", 46 | "test": "npm-run-all lint test:ember test:node", 47 | "test:ember": "COVERAGE=true ember test", 48 | "test:ember-compatibility": "ember try:each", 49 | "test:node": "nyc --reporter=html --reporter=json --reporter=json-summary --reporter=lcov --report-dir coverage/node --exclude node-tests --exclude **/config/* mocha node-tests", 50 | "test:release": "semantic-release --branches=$(git rev-parse --abbrev-ref HEAD) --dry-run", 51 | "validate-coverage": "test \"$(cat coverage/ember/coverage-summary.json | json total.lines.total)\" -gt 0" 52 | }, 53 | "dependencies": { 54 | "broccoli-merge-trees": "^4.1.0", 55 | "broccoli-plugin": "^4.0.7", 56 | "chalk": "^4.1.0", 57 | "debug": "^4.3.3", 58 | "ember-cli-babel": "^7.26.11", 59 | "ember-cli-htmlbars": "^6.0.1", 60 | "glob": "^7.2.0", 61 | "pretty-bytes": "^5.6.0", 62 | "rimraf": "^3.0.1", 63 | "workbox-build": "^6.5.4" 64 | }, 65 | "devDependencies": { 66 | "@commitlint/cli": "^16.1.0", 67 | "@commitlint/config-conventional": "^16.0.0", 68 | "@ember/optional-features": "^2.0.0", 69 | "@ember/test-helpers": "^2.6.0", 70 | "@embroider/test-setup": "^1.0.0", 71 | "@glimmer/component": "^1.0.4", 72 | "@glimmer/tracking": "^1.0.4", 73 | "@semantic-release/changelog": "^6.0.1", 74 | "@semantic-release/git": "^10.0.1", 75 | "babel-eslint": "^10.1.0", 76 | "broccoli-test-helpers": "0.0.9", 77 | "chai": "^4.3.6", 78 | "commitizen": "^4.2.4", 79 | "cz-conventional-changelog": "^3.3.0", 80 | "ember-auto-import": "^2.2.4", 81 | "ember-cli": "~4.1.1", 82 | "ember-cli-code-coverage": "^1.0.3", 83 | "ember-cli-dependency-checker": "^3.2.0", 84 | "ember-cli-inject-live-reload": "^2.1.0", 85 | "ember-cli-terser": "^4.0.2", 86 | "ember-disable-prototype-extensions": "^1.1.3", 87 | "ember-engines": "^0.8.20", 88 | "ember-export-application-global": "^2.0.1", 89 | "ember-page-title": "^7.0.0", 90 | "ember-qunit": "^5.1.5", 91 | "ember-resolver": "^8.0.3", 92 | "ember-sinon": "^5.0.0", 93 | "ember-source": "^4.1.0", 94 | "ember-source-channel-url": "^3.0.0", 95 | "ember-template-lint": "^4.0.0", 96 | "ember-try": "~2.0.0", 97 | "eslint": "^7.32.0", 98 | "eslint-config-prettier": "^8.3.0", 99 | "eslint-plugin-ember": "^10.5.8", 100 | "eslint-plugin-node": "^11.1.0", 101 | "eslint-plugin-prettier": "^4.0.0", 102 | "eslint-plugin-qunit": "^7.2.0", 103 | "husky": "^7.0.4", 104 | "istanbul": "^0.4.5", 105 | "json": "^11.0.0", 106 | "lint-staged": "^12.3.3", 107 | "loader.js": "^4.7.0", 108 | "mocha": "^9.2.0", 109 | "npm-run-all": "^4.1.5", 110 | "nyc": "^15.0.0", 111 | "prettier": "^2.5.1", 112 | "qunit": "^2.17.2", 113 | "qunit-dom": "^2.0.0", 114 | "semantic-release": "^19.0.2", 115 | "webpack": "^5.68.0" 116 | }, 117 | "engines": { 118 | "node": "12.* || 14.* || >= 16" 119 | }, 120 | "ember": { 121 | "edition": "octane" 122 | }, 123 | "ember-addon": { 124 | "configPath": "tests/dummy/config" 125 | }, 126 | "config": { 127 | "commitizen": { 128 | "path": "./node_modules/cz-conventional-changelog" 129 | } 130 | }, 131 | "lint-staged": { 132 | "*.{js,ts}": "eslint --fix", 133 | "*.{md,json,yaml,yml}": "prettier -w" 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test_page: 'tests/index.html?hidepassed', 5 | disable_watching: true, 6 | launch_in_ci: ['Chrome'], 7 | launch_in_dev: ['Chrome'], 8 | browser_start_timeout: 120, 9 | browser_args: { 10 | Chrome: [ 11 | // --no-sandbox is needed when running Chrome inside a container 12 | process.env.CI ? '--no-sandbox' : null, 13 | '--headless', 14 | '--disable-dev-shm-usage', 15 | '--disable-software-rasterizer', 16 | '--mute-audio', 17 | '--remote-debugging-port=0', 18 | '--window-size=1440,900', 19 | ].filter(Boolean), 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /tests/acceptance/auto-register-test.js: -------------------------------------------------------------------------------- 1 | import { visit } from '@ember/test-helpers'; 2 | import { module, test } from 'qunit'; 3 | import { setupApplicationTest } from 'ember-qunit'; 4 | import { set } from '@ember/object'; 5 | import swInitializer from 'dummy/instance-initializers/sw'; 6 | 7 | module('Acceptance | Auto register', (hooks) => { 8 | setupApplicationTest(hooks); 9 | 10 | hooks.beforeEach(function () { 11 | this.events = []; 12 | this.swService = this.owner.lookup('service:service-worker'); 13 | }); 14 | 15 | hooks.afterEach(async function () { 16 | await this.swService.unregisterAll(); 17 | }); 18 | 19 | test('its registration is made automatically', async function (assert) { 20 | swInitializer.initialize(this.owner); 21 | 22 | const promise = new Promise((resolve) => { 23 | setTimeout(() => { 24 | resolve(); 25 | }, 500); 26 | }); 27 | 28 | await promise; 29 | await visit('/'); 30 | 31 | const registrations = 32 | await window.navigator.serviceWorker.getRegistrations(); 33 | 34 | assert.ok(registrations.length); 35 | }); 36 | 37 | test('its registration can be disabled', async function (assert) { 38 | const config = this.owner.resolveRegistration('config:environment'); 39 | 40 | set(config, 'ember-cli-workbox.autoRegister', false); 41 | 42 | this.swService.set('autoRegister', false); 43 | 44 | swInitializer.initialize(this.owner); 45 | 46 | await visit('/'); 47 | 48 | const registrations = 49 | await window.navigator.serviceWorker.getRegistrations(); 50 | 51 | assert.notOk(registrations.length, 'there is no registrations'); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /tests/acceptance/configuration-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupApplicationTest } from 'ember-qunit'; 3 | 4 | module('Acceptance | Simple Acceptance Test', (hooks) => { 5 | setupApplicationTest(hooks); 6 | 7 | test('"workbox" configuration is not in environment config', async function (assert) { 8 | const config = this.owner.resolveRegistration('config:environment'); 9 | 10 | assert.notOk(config.workbox); 11 | }); 12 | 13 | test('"workbox" configuration is setted in "ember-cli-workbox" config object', async function (assert) { 14 | const config = this.owner.resolveRegistration('config:environment'); 15 | 16 | assert.strictEqual(config['ember-cli-workbox'].swDest, 'sw.js'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /tests/acceptance/simple-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupApplicationTest } from 'ember-qunit'; 3 | import sinon from 'sinon'; 4 | 5 | module('Acceptance | Simple Acceptance Test', (hooks) => { 6 | setupApplicationTest(hooks); 7 | 8 | hooks.beforeEach(function () { 9 | this.events = []; 10 | this.swService = this.owner.lookup('service:service-worker'); 11 | // Prevent running initializer 12 | this.swService.set('isSupported', false); 13 | 14 | this.swService.on('registrationComplete', () => 15 | this.events.push('registrationComplete') 16 | ); 17 | this.swService.on('unregistrationComplete', () => 18 | this.events.push('unregistrationComplete') 19 | ); 20 | this.swService.on('error', () => this.events.push('error')); 21 | // this.swService.on('newSWwaiting', () => this.events.push('newSWwaiting')); 22 | // this.swService.on('newSWActive', () => this.events.push('newSWActive')); 23 | }); 24 | 25 | hooks.afterEach(async function () { 26 | await this.swService.unregisterAll(); 27 | }); 28 | 29 | test('its registration is rejected if sw file does not exist', async function (assert) { 30 | assert.expect(2); 31 | 32 | try { 33 | await this.swService.register('foo'); 34 | } catch (error) { 35 | assert.ok(error, 'Service worker triggers "error" event'); 36 | assert.deepEqual(this.events, ['error']); 37 | } 38 | }); 39 | 40 | test('its registration is resolved if file exist', async function (assert) { 41 | await this.swService.register('sw.js'); 42 | 43 | assert.deepEqual( 44 | this.events, 45 | ['registrationComplete'], 46 | 'Event triggered: registrationComplete' 47 | ); 48 | 49 | const registrations = 50 | await window.navigator.serviceWorker.getRegistrations(); 51 | 52 | assert.ok(registrations.length); 53 | }); 54 | 55 | test('it unregisters sw', async function (assert) { 56 | await this.swService.register('sw.js'); 57 | 58 | assert.deepEqual( 59 | this.events, 60 | ['registrationComplete'], 61 | 'Event triggered: registrationComplete' 62 | ); 63 | 64 | await this.swService.unregisterAll(); 65 | 66 | assert.deepEqual( 67 | this.events, 68 | ['registrationComplete', 'unregistrationComplete'], 69 | 'Service worker does not exists' 70 | ); 71 | 72 | const registrations = 73 | await window.navigator.serviceWorker.getRegistrations(); 74 | 75 | assert.notOk(registrations.length); 76 | }); 77 | 78 | test('it unregisters sw but fails', async function (assert) { 79 | await this.swService.register('sw.js'); 80 | 81 | const registrations = await this.swService.sw.getRegistrations(); 82 | const stubs = registrations.map((reg) => { 83 | return sinon.stub(reg, 'unregister').callsFake(() => { 84 | return Promise.resolve(false); 85 | }); 86 | }); 87 | 88 | const stubLog = sinon.stub(this.swService, '_log'); 89 | 90 | assert.deepEqual( 91 | this.events, 92 | ['registrationComplete'], 93 | 'Event triggered: registrationComplete' 94 | ); 95 | 96 | await this.swService.unregisterAll(); 97 | 98 | assert.ok(stubLog.called); 99 | assert.ok(registrations.length); 100 | 101 | assert.deepEqual( 102 | this.events, 103 | ['registrationComplete', 'unregistrationComplete'], 104 | 'Service worker does not exists' 105 | ); 106 | 107 | stubs.forEach((stubReg) => stubReg.restore()); 108 | stubLog.restore(); 109 | }); 110 | 111 | test.skip('it triggers "update" event on sw response', async function (assert) { 112 | const _reload = window.location.reload; 113 | let called = 0; 114 | 115 | Object.defineProperty(window.location, 'reload', { 116 | value() { 117 | called++; 118 | }, 119 | }); 120 | 121 | await this.swService._watchUpdates(); 122 | 123 | const event = new Event('message'); 124 | 125 | event.data = 'reload-window'; 126 | 127 | this.swService.sw.dispatchEvent(event); 128 | 129 | assert.equal(called, 1, 'window.location.reload() called once'); 130 | 131 | window.location.reload = _reload; // eslint-disable-line require-atomic-updates 132 | }); 133 | }); 134 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import config from './config/environment'; 4 | 5 | export default class App extends Application { 6 | modulePrefix = config.modulePrefix; 7 | podModulePrefix = config.podModulePrefix; 8 | Resolver = Resolver; 9 | } 10 | -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from 'dummy/config/environment'; 3 | 4 | export default class Router extends EmberRouter { 5 | location = config.locationType; 6 | rootURL = config.rootURL; 7 | } 8 | 9 | Router.map(function () {}); 10 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BBVAEngineering/ember-cli-workbox/ce650413bb0344d58caf7fba41f24214b68779d8/tests/dummy/app/styles/style.css -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | 6 | 7 | {{outlet}} 8 | -------------------------------------------------------------------------------- /tests/dummy/config/coverage.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | reporters: ['html', 'json', 'json-summary', 'lcov'], 3 | coverageFolder: 'coverage/ember', 4 | excludes: ['tests/dummy/**/*'], 5 | }; 6 | -------------------------------------------------------------------------------- /tests/dummy/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "4.1.1", 7 | "blueprints": [ 8 | { 9 | "name": "addon", 10 | "outputRepo": "https://github.com/ember-cli/ember-addon-output", 11 | "codemodsSource": "ember-addon-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": ["--yarn", "--ci-provider=github"] 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment) { 4 | const ENV = { 5 | modulePrefix: 'dummy', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | 'ember-cli-workbox': { 10 | enabled: true, 11 | }, 12 | EmberENV: { 13 | FEATURES: { 14 | // Here you can enable experimental features on an ember canary build 15 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 16 | }, 17 | EXTEND_PROTOTYPES: { 18 | // Prevent Ember Data from overriding Date.parse. 19 | Date: false, 20 | }, 21 | }, 22 | 23 | APP: { 24 | // Here you can pass flags/options to your application instance 25 | // when it is created 26 | }, 27 | }; 28 | 29 | if (environment === 'development') { 30 | // ENV.APP.LOG_RESOLVER = true; 31 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 32 | // ENV.APP.LOG_TRANSITIONS = true; 33 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 34 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 35 | } 36 | 37 | if (environment === 'test') { 38 | // Testem prefers this... 39 | ENV.locationType = 'none'; 40 | 41 | // keep test console output quieter 42 | ENV.APP.LOG_ACTIVE_GENERATION = false; 43 | ENV.APP.LOG_VIEW_LOOKUPS = false; 44 | 45 | ENV.APP.rootElement = '#ember-testing'; 46 | ENV.APP.autoboot = false; 47 | } 48 | 49 | if (environment === 'production') { 50 | // here you can enable a production-specific feature 51 | } 52 | 53 | return ENV; 54 | }; 55 | -------------------------------------------------------------------------------- /tests/dummy/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "template-only-glimmer-components": true 6 | } 7 | -------------------------------------------------------------------------------- /tests/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions', 7 | ]; 8 | 9 | module.exports = { 10 | browsers, 11 | }; 12 | -------------------------------------------------------------------------------- /tests/dummy/public/assets/service-workers/skip-waiting.js: -------------------------------------------------------------------------------- 1 | console.log('foo'); 2 | -------------------------------------------------------------------------------- /tests/dummy/public/engines-dist/my-engine/assets/service-workers/engine.js: -------------------------------------------------------------------------------- 1 | console.log('bar'); 2 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BBVAEngineering/ember-cli-workbox/ce650413bb0344d58caf7fba41f24214b68779d8/tests/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {{content-for "body-footer"}} 38 | {{content-for "test-body-footer"}} 39 | 40 | 41 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from 'dummy/app'; 2 | import config from 'dummy/config/environment'; 3 | import * as QUnit from 'qunit'; 4 | import { setApplication } from '@ember/test-helpers'; 5 | import { setup } from 'qunit-dom'; 6 | import { start } from 'ember-qunit'; 7 | 8 | setApplication(Application.create(config.APP)); 9 | 10 | setup(QUnit.assert); 11 | 12 | start(); 13 | --------------------------------------------------------------------------------