From 4d5b9c7e91e55f05dbd23fcc50b52594d0495321 Mon Sep 17 00:00:00 2001 From: flolu Date: Sat, 25 Apr 2020 13:08:10 +0200 Subject: [PATCH] feat(example): add full pwa support --- examples/angular/BUILD.bazel | 2 + examples/angular/README.md | 1 + examples/angular/favicon.ico | Bin 0 -> 15406 bytes examples/angular/package.json | 2 + examples/angular/src/BUILD.bazel | 34 ++++++---- examples/angular/src/app/BUILD.bazel | 1 + examples/angular/src/app/app.module.ts | 4 +- examples/angular/src/assets/BUILD.bazel | 2 +- .../angular/src/assets/icons/icon-128x128.png | Bin 0 -> 2919 bytes .../angular/src/assets/icons/icon-144x144.png | Bin 0 -> 4549 bytes .../angular/src/assets/icons/icon-152x152.png | Bin 0 -> 5105 bytes .../angular/src/assets/icons/icon-192x192.png | Bin 0 -> 5230 bytes .../angular/src/assets/icons/icon-384x384.png | Bin 0 -> 12058 bytes .../angular/src/assets/icons/icon-512x512.png | Bin 0 -> 9343 bytes .../angular/src/assets/icons/icon-72x72.png | Bin 0 -> 2513 bytes .../angular/src/assets/icons/icon-96x96.png | Bin 0 -> 2685 bytes examples/angular/src/example/index.prod.html | 23 +++++-- examples/angular/src/manifest.webmanifest | 59 ++++++++++++++++ examples/angular/src/ngsw-config.json | 35 ++++++++++ examples/angular/src/robots.txt | 2 + examples/angular/src/server.ts | 63 +++++++----------- examples/angular/tools/ngsw_config.bzl | 37 ++++++++++ examples/angular/yarn.lock | 9 ++- 23 files changed, 212 insertions(+), 62 deletions(-) create mode 100644 examples/angular/favicon.ico create mode 100644 examples/angular/src/assets/icons/icon-128x128.png create mode 100644 examples/angular/src/assets/icons/icon-144x144.png create mode 100644 examples/angular/src/assets/icons/icon-152x152.png create mode 100644 examples/angular/src/assets/icons/icon-192x192.png create mode 100644 examples/angular/src/assets/icons/icon-384x384.png create mode 100644 examples/angular/src/assets/icons/icon-512x512.png create mode 100644 examples/angular/src/assets/icons/icon-72x72.png create mode 100644 examples/angular/src/assets/icons/icon-96x96.png create mode 100644 examples/angular/src/manifest.webmanifest create mode 100644 examples/angular/src/ngsw-config.json create mode 100644 examples/angular/src/robots.txt create mode 100644 examples/angular/tools/ngsw_config.bzl diff --git a/examples/angular/BUILD.bazel b/examples/angular/BUILD.bazel index 2a1a039be2..6d239c07e2 100644 --- a/examples/angular/BUILD.bazel +++ b/examples/angular/BUILD.bazel @@ -2,6 +2,8 @@ load("@k8s_deploy//:defaults.bzl", "k8s_deploy") package(default_visibility = ["//:__subpackages__"]) +exports_files(["favicon.ico"]) + # ts_library uses the `//:tsconfig.json` target # by default. This alias allows omitting explicit tsconfig # attribute. diff --git a/examples/angular/README.md b/examples/angular/README.md index c811bfecbc..3ff844ee47 100644 --- a/examples/angular/README.md +++ b/examples/angular/README.md @@ -20,6 +20,7 @@ This example is a monorepo, meant to show many different features and integratio - **Differential loading**: in production mode, we load a pair of ` - + diff --git a/examples/angular/src/manifest.webmanifest b/examples/angular/src/manifest.webmanifest new file mode 100644 index 0000000000..91edd8fbe3 --- /dev/null +++ b/examples/angular/src/manifest.webmanifest @@ -0,0 +1,59 @@ +{ + "name": "Angular Bazel Example", + "short_name": "Angular Bazel", + "theme_color": "#43a047", + "background_color": "#fff", + "display": "standalone", + "scope": "./", + "start_url": "./", + "icons": [ + { + "src": "assets/icons/icon-72x72.png", + "sizes": "72x72", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "assets/icons/icon-96x96.png", + "sizes": "96x96", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "assets/icons/icon-128x128.png", + "sizes": "128x128", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "assets/icons/icon-144x144.png", + "sizes": "144x144", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "assets/icons/icon-152x152.png", + "sizes": "152x152", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "assets/icons/icon-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "assets/icons/icon-384x384.png", + "sizes": "384x384", + "type": "image/png", + "purpose": "maskable any" + }, + { + "src": "assets/icons/icon-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable any" + } + ] +} diff --git a/examples/angular/src/ngsw-config.json b/examples/angular/src/ngsw-config.json new file mode 100644 index 0000000000..0dc5320a64 --- /dev/null +++ b/examples/angular/src/ngsw-config.json @@ -0,0 +1,35 @@ +{ + "$schema": "../node_modules/@angular/service-worker/config/schema.json", + "index": "/index.html", + "assetGroups": [ + { + "name": "app", + "installMode": "prefetch", + "updateMode": "prefetch", + "resources": { + "files": [ + "favicon.ico", + "index.html", + "manifest.webmanifest", + "/**/*.css", + "/**/*.js" + ], + "urls": [ + "https://fonts.googleapis.com/**", + "https://fonts.gstatic.com/s/**" + ] + } + }, + { + "name": "assets", + "installMode": "lazy", + "updateMode": "prefetch", + "resources": { + "files": [ + "/assets/**", + "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)" + ] + } + } + ] +} diff --git a/examples/angular/src/robots.txt b/examples/angular/src/robots.txt new file mode 100644 index 0000000000..14267e9032 --- /dev/null +++ b/examples/angular/src/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Allow: / \ No newline at end of file diff --git a/examples/angular/src/server.ts b/examples/angular/src/server.ts index c512c184d1..82cd01275f 100644 --- a/examples/angular/src/server.ts +++ b/examples/angular/src/server.ts @@ -1,41 +1,28 @@ -/// -import "zone.js/dist/zone-node"; +/// +import 'zone.js/dist/zone-node'; -import { ngExpressEngine } from "@nguniversal/express-engine"; -import * as express from "express"; -import { join } from "path"; +import { ngExpressEngine } from '@nguniversal/express-engine'; +import * as express from 'express'; +import { join } from 'path'; +import * as compression from 'compression'; -const app = express(); - -const PORT = process.env.PORT || 4000; -const DIST_FOLDER = join(process.cwd(), "src/prodapp"); - -import { AppServerModule } from "./app/app.server.module"; - -app.engine( - "html", - ngExpressEngine({ - bootstrap: AppServerModule, - providers: [ - // TODO add support for lazy loading with server side rendering - // provideModuleMap(LAZY_MODULE_MAP) - ] - }) as any -); - -app.set("view engine", "html"); -app.set("views", DIST_FOLDER); +import { AppServerModule } from './app/app.server.module'; -app.get("*.*", express.static(DIST_FOLDER, { maxAge: "1y" })); - -// catch /favicon.ico route to prevent the following server error: -// Error: Cannot match any routes. URL Segment: 'favicon.ico' -app.get("/favicon.ico", (req, res) => res.send("")); - -app.get("*", (req, res) => { - res.render("example/index", { req }); -}); - -app.listen(PORT, () => { - console.log(`Node Express server listening on http://localhost:${PORT}`); -}); +const app = express(); +const port = process.env.PORT || 4000; +const DIST_FOLDER = join(process.cwd(), 'src/pwa'); + +/** + * text compression for smaller download sizes and thus faster load times + * without compression: 1.4 MB + * with compresion: 321 kB + */ +app.use(compression()); + +app.engine('html', ngExpressEngine({ bootstrap: AppServerModule }) as any); +app.set('view engine', 'html'); +app.set('views', DIST_FOLDER); + +app.get('*.*', express.static(DIST_FOLDER, { maxAge: '1y' })); +app.get('*', (req, res) => res.render('example/index', { req })); +app.listen(port, () => console.log(`Server listening http://localhost:${port}`)); diff --git a/examples/angular/tools/ngsw_config.bzl b/examples/angular/tools/ngsw_config.bzl new file mode 100644 index 0000000000..b99d56397b --- /dev/null +++ b/examples/angular/tools/ngsw_config.bzl @@ -0,0 +1,37 @@ +"Angular service worker support (credits: https://github.com/marcus-sa)" + +load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary") + +def ngsw_config(name, config, index_html, src, out = None, **kwargs): + if not out: + out = name + + ngsw_config_name = "%s_bin" % name + + nodejs_binary( + name = ngsw_config_name, + data = ["@npm//@angular/service-worker", index_html, config, src], + visibility = ["//visibility:private"], + entry_point = "@npm//:node_modules/@angular/service-worker/ngsw-config.js", + ) + + cmd = """ +mkdir -p $@ +cp -R $(locations {TMPL_src})/. $@/ +cp $(location {TMPL_index}) $@/index.html +$(location :{TMPL_bin}) $@ $(location {TMPL_conf}) + """.format( + TMPL_src = src, + TMPL_bin = ngsw_config_name, + TMPL_index = index_html, + TMPL_conf = config, + ) + + native.genrule( + name = name, + outs = [out], + srcs = [src, config, index_html], + tools = [":" + ngsw_config_name], + cmd = cmd, + **kwargs + ) diff --git a/examples/angular/yarn.lock b/examples/angular/yarn.lock index de3515e452..33ce2d028b 100644 --- a/examples/angular/yarn.lock +++ b/examples/angular/yarn.lock @@ -175,6 +175,13 @@ dependencies: tslib "^2.0.0" +"@angular/service-worker@10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@angular/service-worker/-/service-worker-10.0.2.tgz#1414f4b7721a45c97c9a3efc6b7516453643d55d" + integrity sha512-PXbh5k7yba+X18o2nNqST92Mcsq+5CXDaYIwUcUkYhx1omAMHhYF9W+FFJSY/EhW2yx5x4aRoh9ZmOqldsQB9A== + dependencies: + tslib "^2.0.0" + "@babel/cli@^7.6.0": version "7.6.2" resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.6.2.tgz#4ce8b5b4b2e4b4c1b7bd841cec62085e2dfc4465" @@ -2078,7 +2085,7 @@ compressible@~2.0.16: dependencies: mime-db ">= 1.38.0 < 2" -compression@^1.7.0: +compression@^1.7.0, compression@^1.7.4: version "1.7.4" resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==