PWA: Building and publishing a PWA in 15ish minutes using Angular 5 and Service Worker
Introduction
Here is an express start to your first PWA with service workers and a 100% score from lighthouse. I have taken it as a challenge to be able to present it to you so that you may be able to put your first pwa "hello world" app in 15ish minutes.What is PWA?
Progressive web apps (PWAs) take traditional web sites/applications — with all of the advantages the web brings — and add a number of features that give them many of the user experience advantages of native apps. This set of docs tells you all you need to know. - Source: https://developer.mozilla.org/en-US/Apps/ProgressiveIf the above definition sounds interesting, then this is to get you kickstarted with PWA using Angular 5 and inspire you to build more PWAs. End of this post you should be able to build, host and publish a PWA and get motivated to read more about it. Ever since I read about PWAs It has intrested me like crazy. Before, you get excited about PWA, please look into the supported browsers and the growing trend.
Source: https://caniuse.com/#feat=serviceworkers
The good news is every browser is trying to equip itself with this service workers. That encourages us to build more PWAs.
Prerequisites
1. A laptop/computer that you may be able to do it yourself.2. Have a valid GitHub Account. If you don't have one go to github.com
3. Have the latest nodejs and npm all set to run correctly. You may get the latest nodejs and npm from nodejs.org.
4. A good Text Editor. I am using vscode. You may wish to use anything that you are comfortable.
5. Git command line
6. Last but not the least, have a good Internet speed.
Outline
The clock starts now: Here is the outline of what we will be doing.1. Install an Enterprise App Starter.
2. Generate a new project
3. Make changes to the generated code so that github is happy to host.
4. Build and Deploy
5. Test it with your phone. You are done!
Lets get started
Assuming that you are already having all the prerequisites.Step 1: Install an Enterprise App Starter
Go to the command prompt/terminal and do the following. This installs the generator to your command line.
╭─ ~/Development/blog ╰─$ mkdir first-pwa ╭─ ~/Development/blog ╰─$ cd first-pwa ╭─ ~/Development/blog/first-pwa ╰─$ npm install -g generator-ngx-rocket ~/.npm-global/bin/ngx -> ~/.npm-global/lib/node_modules/generator-ngx-rocket/cli/bin/ngx + [email protected] updated 7 packages in 16.134s ╭─ ~/Development/blog/first-pwa ╰─$ ngx --help __ __ _ _ __ _\ \./ / ____ ____ ____ _ _ ____ ___ | ' \/ _` |> < |--< [__] |___ |-:_ |=== | |_||_\__, /_/°\_\ ENTERPRISE APP STARTER -~*=> |___/ v4.0.0 Usage: ngx [new|update|config|list|<script>] [options] n, new [name] Creates a new app. -a, --addon Creates an add-on instead. --packageManager <yarn|npm> Uses specified package manager. --automate <json_file> Automates prompt answers using JSON file. --tools Generates only the toolchain u, update Updates an existing app or add-on. --tools Updates only the toolchain c, config Configures add-ons to use for new apps. All available add-ons are used by default. l, list Lists available add-ons. -n, --npm Show installable add-ons on NPM <script> Runs specified script from your package.json. Works just like npm run <script>
Step 2: Generate a new project
Lets now generate the project and I assume that you are in a empty directory (first-pwa).
╭─ ~/Development/blog/first-pwa ╰─$ ngx new __ __ _ _ __ _\ \./ / ____ ____ ____ _ _ ____ ___ | ' \/ _` |> < |--< [__] |___ |-:_ |=== | |_||_\__, /_/°\_\ ENTERPRISE APP STARTER -~*=> |___/ v4.0.0 ? What's the name of your app? first pwa ? What kind of app do you want to create? Web app ? Do you want a progressive web app? (with manifest and service worker) Yes ? Which UI framework do you want? Angular Material (more website-oriented) ? Which kind of layout do you want? Side menu with split panels (more app-oriented) ? Do you want authentication? No ? Do you want lazy loading? No create package.json create .editorconfig create .htmlhintrc create .stylelintrc
... create src/app/core/shell/shell.component.scss create src/app/core/shell/shell.component.spec.ts Running npm install, please wait... > [email protected] install ~/Development/blog/first-pwa/node_modules/uws > node-gyp rebuild > build_log.txt 2>&1 || exit 0 > [email protected] install ~/Development/blog/first-pwa/node_modules/node-sass > node scripts/install.js Cached binary found at ~/.npm/node-sass/4.7.2/linux-x64-57_binding.node > [email protected] install ~/Development/blog/first-pwa/node_modules/puppeteer > node install.js Downloading Chromium r536395 - 96.2 Mb [====================] 100% 0.0s Chromium downloaded to ~/Development/blog/first-pwa/node_modules/puppeteer/.local-chromium/linux-536395 > [email protected] postinstall ~/Development/blog/first-pwa/node_modules/webpack/node_modules/uglifyjs-webpack-plugin > node lib/post_install.js > [email protected] postinstall ~/Development/blog/first-pwa/node_modules/node-sass > node scripts/build.js Binary found at ~/Development/blog/first-pwa/node_modules/node-sass/vendor/linux-x64-57/binding.node Testing binary Binary is fine added 1537 packages in 50.038s All done! Get started with these tasks: - $ npm start: start dev server with live reload on http://localhost:4200 - $ npm run build: build web app for production - $ npm test: run unit tests in watch mode for TDD - $ npm run test:ci: lint code and run units tests with coverage - $ npm run e2e: launch e2e tests - $ npm run docs: show docs and coding guides
Step 3: Code Change to enable hosting
Now open your favorite editor and do the following.
i) Remove /dist from .gitignore. We need to be able to publish our .gitignore to github pages.
ii) On index.html replace the base tag with below line .
This is because the web application when running on gh-pages does not always start with root context. Which means / is not going to work on gh-pages.
<base href="./" />
This is because the web application when running on gh-pages does not always start with root context. Which means / is not going to work on gh-pages.
iii) Update the ServiceWorkerModule register on the app.module.ts.
From:
To:
This is also to make sure the worker is working on gh-pages.
From:
ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production }),
To:
ServiceWorkerModule.register('./ngsw-worker.js', { enabled: environment.production }),
This is also to make sure the worker is working on gh-pages.
Step 4: Build and Deploy
Lets now do the build with following command.
Once you have the build ready you may see the files as follows,
╭─ ~/Development/blog/first-pwa ╰─$ npm run build > [email protected] build ~/Development/blog/first-pwa > npm run env -s && ng build --prod Date: 2018-03-17T19:30:45.683Z Hash: a2be90b48555d5ce9393 Time: 39010ms chunk {0} polyfills.6632764bf1554842312c.bundle.js (polyfills) 94.5 kB [initial] [rendered] chunk {1} main.96d63fdea40fe06beccd.bundle.js (main) 782 kB [initial] [rendered] chunk {2} styles.8214eb1655da67c76bfc.bundle.css (styles) 47.8 kB [initial] [rendered] chunk {3} inline.318b50c57b4eba3d437b.bundle.js (inline) 796 bytes [entry] [rendered]
Once you have the build ready you may see the files as follows,
$ tree dist/ dist/ ├── 3rdpartylicenses.txt ├── assets │ └── ngx-rocket-logo.png ├── favicon.ico ├── index.html ├── inline.318b50c57b4eba3d437b.bundle.js ├── main.96d63fdea40fe06beccd.bundle.js ├── manifest.json ├── MaterialIcons-Regular.012cf6a10129e2275d79.woff ├── MaterialIcons-Regular.570eb83859dc23dd0eec.woff2 ├── MaterialIcons-Regular.a37b0c01c0baf1888ca8.ttf ├── MaterialIcons-Regular.e79bfd88537def476913.eot ├── ngsw.json ├── ngsw-worker.js ├── polyfills.6632764bf1554842312c.bundle.js ├── robots.txt └── styles.8214eb1655da67c76bfc.bundle.css 1 directory, 16 files
At this point you have everything ready for hosting the application. Lets do the deployment.
Go to https://github.com/new and create an empty repository.
Create a new repository as follows
Once you have created the repository you will see the below page.
In my case it is as follows.
[email protected]:reflexdemon/first-pwa.git
Now, lets begin our deployment.
i) Initialize git repository on your local
$ git init Initialized empty Git repository in ~/Development/blog/first-pwa/.git/
ii) Add files to local repository
╭─ ~/Development/blog/first-pwa ‹master*› ╰─$ git add . ╭─ ~/Development/blog/first-pwa ‹master*› ╰─$ git commit -m "first commit" [master (root-commit) 84accee] first commit 116 files changed, 21984 insertions(+) create mode 100644 .angular-cli.json create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .htmlhintrc create mode 100644 .stylelintrc create mode 100644 .yo-rc.json create mode 100644 README.md create mode 100644 browserslist create mode 100644 dist/3rdpartylicenses.txt create mode 100644 dist/MaterialIcons-Regular.012cf6a10129e2275d79.woff create mode 100644 dist/MaterialIcons-Regular.570eb83859dc23dd0eec.woff2 create mode 100644 dist/MaterialIcons-Regular.a37b0c01c0baf1888ca8.ttf create mode 100644 dist/MaterialIcons-Regular.e79bfd88537def476913.eot create mode 100644 dist/assets/ngx-rocket-logo.png create mode 100644 dist/favicon.ico create mode 100644 dist/index.html create mode 100644 dist/inline.318b50c57b4eba3d437b.bundle.js create mode 100644 dist/main.96d63fdea40fe06beccd.bundle.js create mode 100644 dist/manifest.json create mode 100644 dist/ngsw-worker.js create mode 100644 dist/ngsw.json create mode 100644 dist/polyfills.6632764bf1554842312c.bundle.js create mode 100644 dist/robots.txt create mode 100644 dist/styles.8214eb1655da67c76bfc.bundle.css create mode 100644 docs/backend-proxy.md create mode 100644 docs/coding-guides/angular.md create mode 100644 docs/coding-guides/e2e-tests.md create mode 100644 docs/coding-guides/html.md create mode 100644 docs/coding-guides/sass.md create mode 100644 docs/coding-guides/typescript.md create mode 100644 docs/coding-guides/unit-tests.md create mode 100644 docs/corporate-proxy.md create mode 100644 docs/i18n.md create mode 100644 docs/readme.md create mode 100644 docs/routing.md create mode 100644 docs/updating.md create mode 100644 e2e/app.e2e-spec.ts create mode 100644 e2e/app.po.ts create mode 100644 e2e/tsconfig.e2e.json create mode 100644 karma.conf.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 protractor.conf.js create mode 100644 proxy.conf.js create mode 100644 src/app/about/about-routing.module.ts create mode 100644 src/app/about/about.component.html create mode 100644 src/app/about/about.component.scss create mode 100644 src/app/about/about.component.spec.ts create mode 100644 src/app/about/about.component.ts create mode 100644 src/app/about/about.module.ts create mode 100644 src/app/app-routing.module.ts create mode 100644 src/app/app.component.html create mode 100644 src/app/app.component.scss create mode 100644 src/app/app.component.spec.ts create mode 100644 src/app/app.component.ts create mode 100644 src/app/app.module.ts create mode 100644 src/app/core/core.module.ts create mode 100644 src/app/core/http/api-prefix.interceptor.spec.ts create mode 100644 src/app/core/http/api-prefix.interceptor.ts create mode 100644 src/app/core/http/cache.interceptor.spec.ts create mode 100644 src/app/core/http/cache.interceptor.ts create mode 100644 src/app/core/http/error-handler.interceptor.spec.ts create mode 100644 src/app/core/http/error-handler.interceptor.ts create mode 100644 src/app/core/http/http-cache.service.spec.ts create mode 100644 src/app/core/http/http-cache.service.ts create mode 100644 src/app/core/http/http.service.spec.ts create mode 100644 src/app/core/http/http.service.ts create mode 100644 src/app/core/i18n.service.spec.ts create mode 100644 src/app/core/i18n.service.ts create mode 100644 src/app/core/index.ts create mode 100644 src/app/core/logger.service.spec.ts create mode 100644 src/app/core/logger.service.ts create mode 100644 src/app/core/route-reusable-strategy.ts create mode 100644 src/app/core/route.service.spec.ts create mode 100644 src/app/core/route.service.ts create mode 100644 src/app/core/shell/shell.component.html create mode 100644 src/app/core/shell/shell.component.scss create mode 100644 src/app/core/shell/shell.component.spec.ts create mode 100644 src/app/core/shell/shell.component.ts create mode 100644 src/app/home/home-routing.module.ts create mode 100644 src/app/home/home.component.html create mode 100644 src/app/home/home.component.scss create mode 100644 src/app/home/home.component.spec.ts create mode 100644 src/app/home/home.component.ts create mode 100644 src/app/home/home.module.ts create mode 100644 src/app/home/quote.service.spec.ts create mode 100644 src/app/home/quote.service.ts create mode 100644 src/app/material.module.ts create mode 100644 src/app/shared/index.ts create mode 100644 src/app/shared/loader/loader.component.html create mode 100644 src/app/shared/loader/loader.component.scss create mode 100644 src/app/shared/loader/loader.component.spec.ts create mode 100644 src/app/shared/loader/loader.component.ts create mode 100644 src/app/shared/shared.module.ts create mode 100644 src/apple-touch-icon.png create mode 100644 src/assets/ngx-rocket-logo.png create mode 100644 src/environments/environment.prod.ts create mode 100644 src/environments/environment.ts create mode 100644 src/favicon.ico create mode 100644 src/index.html create mode 100644 src/main.scss create mode 100644 src/main.ts create mode 100644 src/manifest.json create mode 100644 src/ngsw-config.json create mode 100644 src/polyfills.ts create mode 100755 src/robots.txt create mode 100644 src/test.ts create mode 100644 src/theme/theme-variables.scss create mode 100644 src/theme/theme.scss create mode 100644 src/translations/en-US.json create mode 100644 src/translations/fr-FR.json create mode 100644 src/tsconfig.app.json create mode 100644 src/tsconfig.spec.json create mode 100644 src/typings.d.ts create mode 100644 tsconfig.json create mode 100644 tslint.json
iii) Update your remote and push them.
╭─ ~/Development/blog/first-pwa ‹master› ╰─$ git remote add origin [email protected]:reflexdemon/first-pwa.git ╭─ ~/Development/blog/first-pwa ‹master› ╰─$ git push -u origin master Counting objects: 131, done. Delta compression using up to 4 threads. Compressing objects: 100% (128/128), done. Writing objects: 100% (131/131), 670.72 KiB | 0 bytes/s, done. Total 131 (delta 11), reused 0 (delta 0) remote: Resolving deltas: 100% (11/11), done. To [email protected]:reflexdemon/first-pwa.git * [new branch] master -> master Branch master set up to track remote branch master from origin.
Now, building, packing are shipping are done. Hosting is the only thing pending.
Please use the below commands and you should be able to host your application.
╭─ ~/Development/blog/first-pwa ‹master› ╰─$ git subtree push --prefix dist/ origin gh-pages git push using: origin gh-pages Counting objects: 19, done. Delta compression using up to 4 threads. Compressing objects: 100% (18/18), done. Writing objects: 100% (19/19), 453.30 KiB | 0 bytes/s, done. Total 19 (delta 1), reused 0 (delta 0) remote: Resolving deltas: 100% (1/1), done. To [email protected]:reflexdemon/first-pwa.git * [new branch] 0445901608cce0afa1ea60a9fac1d3ecf908d721 -> gh-pages
Here you go with your first pwa.
https://reflexdemon.github.io/first-pwa/
Your URL might be something like, https://
The clock stops here.
Lighthouse
Lighthouse is an open-source, automated tool for improving the quality of web pages. You can run it against any web page, public or requiring authentication. It has audits for performance, accessibility, progressive web apps, and more.Please visit here to be able to run your lighthouse report.
I have published my lighthouse report here. https://reflexdemon.github.io/first-pwa/reflexdemon.github.io_2018-03-17_16-24-05.report.html
Conclision
Hope you did it inside my challenge of 15ish min to deploy your first PWA. At this point I am happy that you learn something into the new buzz word PWA.
After completing this post, If you are still intrested to read more about PWAs, here is the path I would recommend you to begin your PWA journey.Please visit the links in the order provided below,
1. https://developer.mozilla.org/en-US/Apps/Progressive
2. https://developers.google.com/web/progressive-web-apps/
3. https://angularfirebase.com/lessons/hnpwa-angular-5-progressive-web-app-service-worker-tutorial/#Angular-5-Service-Worker-Everything-you-Wanted-to-Know
4. https://angular.io/guide/service-worker-intro
5. https://blog.vpv.io/2018/03/pwa-building-and-publishing-pwa-in.html
When adding PWA capability to a regular Angular project, the command:
ReplyDelete$ ng add @angular/pwa
automatically adds PWA features to our Angular application, such as a manifest.json file, icons and the ngsw-worker.js service worker.
However, in the Ngx Rocket app generated app, there is no ngsw-worker.js in the src directory, and it only gets build by running npm run build --env=prod.
Why is the file not part of the project, and what if you wanted to add another function to the service worker for example to look for differences in http responses?
Thanks in advance.