diff options
author | Javier Calvarro Nelson <jacalvar@microsoft.com> | 2021-09-09 19:11:27 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-09 19:11:27 +0300 |
commit | 796e4363976fbd719468dc9466aa333ae82f7ae3 (patch) | |
tree | 935f188c15fb759974cff237704898b1596ba95f | |
parent | f9741d3c3fa9f82627409a542700e3996655260b (diff) |
[React] Aligns service worker code with create-react-app PWA template (#19)
Aligns the template with the create-react-app template (cra-template-pwa)
9 files changed, 274 insertions, 131 deletions
diff --git a/src/content/Angular-CSharp/ClientApp/package-lock.json b/src/content/Angular-CSharp/ClientApp/package-lock.json index 6702420..deca65c 100644 --- a/src/content/Angular-CSharp/ClientApp/package-lock.json +++ b/src/content/Angular-CSharp/ClientApp/package-lock.json @@ -19,7 +19,7 @@ "@angular/router": "~12.2.2", "bootstrap": "^5.1.0", "jquery": "^3.5.1", - "oidc-client": "^1.11.3", + "oidc-client": "^1.11.5", "popper.js": "^1.16.0", "run-script-os": "^1.1.6", "rxjs": "~6.6.0", diff --git a/src/content/Angular-CSharp/ClientApp/package.json b/src/content/Angular-CSharp/ClientApp/package.json index e186888..cc7245f 100644 --- a/src/content/Angular-CSharp/ClientApp/package.json +++ b/src/content/Angular-CSharp/ClientApp/package.json @@ -29,7 +29,7 @@ "@angular/router": "~12.2.2", "bootstrap": "^5.1.0", "jquery": "^3.5.1", - "oidc-client": "^1.11.3", + "oidc-client": "^1.11.5", "popper.js": "^1.16.0", "run-script-os": "^1.1.6", "rxjs": "~6.6.0", diff --git a/src/content/React-CSharp/ClientApp/package-lock.json b/src/content/React-CSharp/ClientApp/package-lock.json index b50bf37..b004893 100644 --- a/src/content/React-CSharp/ClientApp/package-lock.json +++ b/src/content/React-CSharp/ClientApp/package-lock.json @@ -12,14 +12,27 @@ "http-proxy-middleware": "^0.19.1", "jquery": "^3.5.1", "merge": "^2.1.1", - "oidc-client": "^1.9.0", + "oidc-client": "^1.11.5", "react": "^17.0.2", "react-dom": "^17.0.2", "react-router-bootstrap": "^0.25.0", "react-router-dom": "^5.2.0", "react-scripts": "^4.0.3", "reactstrap": "^8.9.0", - "rimraf": "^2.6.2" + "rimraf": "^2.6.2", + "web-vitals": "^0.2.4", + "workbox-background-sync": "^5.1.3", + "workbox-broadcast-update": "^5.1.3", + "workbox-cacheable-response": "^5.1.3", + "workbox-core": "^5.1.3", + "workbox-expiration": "^5.1.3", + "workbox-google-analytics": "^5.1.3", + "workbox-navigation-preload": "^5.1.3", + "workbox-precaching": "^5.1.3", + "workbox-range-requests": "^5.1.3", + "workbox-routing": "^5.1.3", + "workbox-strategies": "^5.1.3", + "workbox-streams": "^5.1.3" }, "devDependencies": { "ajv": "^6.9.1", @@ -20401,6 +20414,11 @@ "minimalistic-assert": "^1.0.0" } }, + "node_modules/web-vitals": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-0.2.4.tgz", + "integrity": "sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg==" + }, "node_modules/webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", @@ -37535,6 +37553,11 @@ "minimalistic-assert": "^1.0.0" } }, + "web-vitals": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-0.2.4.tgz", + "integrity": "sha512-6BjspCO9VriYy12z356nL6JBS0GYeEcA457YyRzD+dD6XYCQ75NKhcOHUMHentOE7OcVCIXXDvOm0jKFfQG2Gg==" + }, "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", diff --git a/src/content/React-CSharp/ClientApp/package.json b/src/content/React-CSharp/ClientApp/package.json index 5243014..f3fc81b 100644 --- a/src/content/React-CSharp/ClientApp/package.json +++ b/src/content/React-CSharp/ClientApp/package.json @@ -7,14 +7,27 @@ "http-proxy-middleware": "^0.19.1", "jquery": "^3.5.1", "merge": "^2.1.1", - "oidc-client": "^1.9.0", + "oidc-client": "^1.11.5", "react": "^17.0.2", "react-dom": "^17.0.2", "react-router-bootstrap": "^0.25.0", "react-router-dom": "^5.2.0", "react-scripts": "^4.0.3", "reactstrap": "^8.9.0", - "rimraf": "^2.6.2" + "rimraf": "^2.6.2", + "web-vitals": "^0.2.4", + "workbox-background-sync": "^5.1.3", + "workbox-broadcast-update": "^5.1.3", + "workbox-cacheable-response": "^5.1.3", + "workbox-core": "^5.1.3", + "workbox-expiration": "^5.1.3", + "workbox-google-analytics": "^5.1.3", + "workbox-navigation-preload": "^5.1.3", + "workbox-precaching": "^5.1.3", + "workbox-range-requests": "^5.1.3", + "workbox-routing": "^5.1.3", + "workbox-strategies": "^5.1.3", + "workbox-streams": "^5.1.3" }, "devDependencies": { "ajv": "^6.9.1", diff --git a/src/content/React-CSharp/ClientApp/src/index.js b/src/content/React-CSharp/ClientApp/src/index.js index a4cc5fe..fbb3bcc 100644 --- a/src/content/React-CSharp/ClientApp/src/index.js +++ b/src/content/React-CSharp/ClientApp/src/index.js @@ -3,11 +3,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter } from 'react-router-dom'; import App from './App'; -////#if (IndividualLocalAuth) -////import registerServiceWorker from './registerServiceWorker'; -////#else -import registerServiceWorker from './registerServiceWorker'; -////#endif +import * as serviceWorkerRegistration from './serviceWorkerRegistration'; +import reportWebVitals from './reportWebVitals'; const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href'); const rootElement = document.getElementById('root'); @@ -18,16 +15,12 @@ ReactDOM.render( </BrowserRouter>, rootElement); -////#if (IndividualLocalAuth) -//// Uncomment the line above that imports the registerServiceWorker function -//// and the line below to register the generated service worker. -//// By default create-react-app includes a service worker to improve the -//// performance of the application by caching static assets. This service -//// worker can interfere with the Identity UI, so it is -//// disabled by default when Identity is being used. -//// -////registerServiceWorker(); -////#else -registerServiceWorker(); -////#endif +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: https://cra.link/PWA +serviceWorkerRegistration.unregister(); +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/src/content/React-CSharp/ClientApp/src/registerServiceWorker.js b/src/content/React-CSharp/ClientApp/src/registerServiceWorker.js deleted file mode 100644 index 10b0baf..0000000 --- a/src/content/React-CSharp/ClientApp/src/registerServiceWorker.js +++ /dev/null @@ -1,108 +0,0 @@ -// In production, we register a service worker to serve assets from local cache. - -// This lets the app load faster on subsequent visits in production, and gives -// it offline capabilities. However, it also means that developers (and users) -// will only see deployed updates on the "N+1" visit to a page, since previously -// cached resources are updated in the background. - -// To learn more about the benefits of this model, read https://goo.gl/KwvDNy. -// This link also includes instructions on opting out of this behavior. - -const isLocalhost = Boolean( - window.location.hostname === 'localhost' || - // [::1] is the IPv6 localhost address. - window.location.hostname === '[::1]' || - // 127.0.0.1/8 is considered localhost for IPv4. - window.location.hostname.match( - /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ - ) -); - -export default function register () { - if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { - // The URL constructor is available in all browsers that support SW. - const publicUrl = new URL(process.env.PUBLIC_URL, window.location); - if (publicUrl.origin !== window.location.origin) { - // Our service worker won't work if PUBLIC_URL is on a different origin - // from what our page is served on. This might happen if a CDN is used to - // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 - return; - } - - window.addEventListener('load', () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; - - if (isLocalhost) { - // This is running on localhost. Lets check if a service worker still exists or not. - checkValidServiceWorker(swUrl); - } else { - // Is not local host. Just register service worker - registerValidSW(swUrl); - } - }); - } -} - -function registerValidSW (swUrl) { - navigator.serviceWorker - .register(swUrl) - .then(registration => { - registration.onupdatefound = () => { - const installingWorker = registration.installing; - installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { - if (navigator.serviceWorker.controller) { - // At this point, the old content will have been purged and - // the fresh content will have been added to the cache. - // It's the perfect time to display a "New content is - // available; please refresh." message in your web app. - console.log('New content is available; please refresh.'); - } else { - // At this point, everything has been precached. - // It's the perfect time to display a - // "Content is cached for offline use." message. - console.log('Content is cached for offline use.'); - } - } - }; - }; - }) - .catch(error => { - console.error('Error during service worker registration:', error); - }); -} - -function checkValidServiceWorker (swUrl) { - // Check if the service worker can be found. If it can't reload the page. - fetch(swUrl) - .then(response => { - // Ensure service worker exists, and that we really are getting a JS file. - if ( - response.status === 404 || - response.headers.get('content-type').indexOf('javascript') === -1 - ) { - // No service worker found. Probably a different app. Reload the page. - navigator.serviceWorker.ready.then(registration => { - registration.unregister().then(() => { - window.location.reload(); - }); - }); - } else { - // Service worker found. Proceed as normal. - registerValidSW(swUrl); - } - }) - .catch(() => { - console.log( - 'No internet connection found. App is running in offline mode.' - ); - }); -} - -export function unregister () { - if ('serviceWorker' in navigator) { - navigator.serviceWorker.ready.then(registration => { - registration.unregister(); - }); - } -} diff --git a/src/content/React-CSharp/ClientApp/src/reportWebVitals.js b/src/content/React-CSharp/ClientApp/src/reportWebVitals.js new file mode 100644 index 0000000..532f29b --- /dev/null +++ b/src/content/React-CSharp/ClientApp/src/reportWebVitals.js @@ -0,0 +1,13 @@ +const reportWebVitals = (onPerfEntry) => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/src/content/React-CSharp/ClientApp/src/service-worker.js b/src/content/React-CSharp/ClientApp/src/service-worker.js new file mode 100644 index 0000000..0f1e0ce --- /dev/null +++ b/src/content/React-CSharp/ClientApp/src/service-worker.js @@ -0,0 +1,72 @@ +/* eslint-disable no-restricted-globals */ + +// This service worker can be customized! +// See https://developers.google.com/web/tools/workbox/modules +// for the list of available Workbox modules, or add any other +// code you'd like. +// You can also remove this file if you'd prefer not to use a +// service worker, and the Workbox build step will be skipped. + +import { clientsClaim } from 'workbox-core'; +import { ExpirationPlugin } from 'workbox-expiration'; +import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching'; +import { registerRoute } from 'workbox-routing'; +import { StaleWhileRevalidate } from 'workbox-strategies'; + +clientsClaim(); + +// Precache all of the assets generated by your build process. +// Their URLs are injected into the manifest variable below. +// This variable must be present somewhere in your service worker file, +// even if you decide not to use precaching. See https://cra.link/PWA +precacheAndRoute(self.__WB_MANIFEST); + +// Set up App Shell-style routing, so that all navigation requests +// are fulfilled with your index.html shell. Learn more at +// https://developers.google.com/web/fundamentals/architecture/app-shell +const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$'); +registerRoute( + // Return false to exempt requests from being fulfilled by index.html. + ({ request, url }) => { + // If this isn't a navigation, skip. + if (request.mode !== 'navigate') { + return false; + } // If this is a URL that starts with /_, skip. + + if (url.pathname.startsWith('/_')) { + return false; + } // If this looks like a URL for a resource, because it contains // a file extension, skip. + + if (url.pathname.match(fileExtensionRegexp)) { + return false; + } // Return true to signal that we want to use the handler. + + return true; + }, + createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html') +); + +// An example runtime caching route for requests that aren't handled by the +// precache, in this case same-origin .png requests like those from in public/ +registerRoute( + // Add in any other file extensions or routing criteria as needed. + ({ url }) => url.origin === self.location.origin && url.pathname.endsWith('.png'), // Customize this strategy as needed, e.g., by changing to CacheFirst. + new StaleWhileRevalidate({ + cacheName: 'images', + plugins: [ + // Ensure that once this runtime cache reaches a maximum size the + // least-recently used images are removed. + new ExpirationPlugin({ maxEntries: 50 }), + ], + }) +); + +// This allows the web app to trigger skipWaiting via +// registration.waiting.postMessage({type: 'SKIP_WAITING'}) +self.addEventListener('message', (event) => { + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting(); + } +}); + +// Any other custom service worker logic can go here. diff --git a/src/content/React-CSharp/ClientApp/src/serviceWorkerRegistration.js b/src/content/React-CSharp/ClientApp/src/serviceWorkerRegistration.js new file mode 100644 index 0000000..2262ecd --- /dev/null +++ b/src/content/React-CSharp/ClientApp/src/serviceWorkerRegistration.js @@ -0,0 +1,137 @@ +// This optional code is used to register a service worker. +// register() is not called by default. + +// This lets the app load faster on subsequent visits in production, and gives +// it offline capabilities. However, it also means that developers (and users) +// will only see deployed updates on subsequent visits to a page, after all the +// existing tabs open on the page have been closed, since previously cached +// resources are updated in the background. + +// To learn more about the benefits of this model and instructions on how to +// opt-in, read https://cra.link/PWA + +const isLocalhost = Boolean( + window.location.hostname === 'localhost' || + // [::1] is the IPv6 localhost address. + window.location.hostname === '[::1]' || + // 127.0.0.0/8 are considered localhost for IPv4. + window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) +); + +export function register(config) { + if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + // The URL constructor is available in all browsers that support SW. + const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); + if (publicUrl.origin !== window.location.origin) { + // Our service worker won't work if PUBLIC_URL is on a different origin + // from what our page is served on. This might happen if a CDN is used to + // serve assets; see https://github.com/facebook/create-react-app/issues/2374 + return; + } + + window.addEventListener('load', () => { + const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; + + if (isLocalhost) { + // This is running on localhost. Let's check if a service worker still exists or not. + checkValidServiceWorker(swUrl, config); + + // Add some additional logging to localhost, pointing developers to the + // service worker/PWA documentation. + navigator.serviceWorker.ready.then(() => { + console.log( + 'This web app is being served cache-first by a service ' + + 'worker. To learn more, visit https://cra.link/PWA' + ); + }); + } else { + // Is not localhost. Just register service worker + registerValidSW(swUrl, config); + } + }); + } +} + +function registerValidSW(swUrl, config) { + navigator.serviceWorker + .register(swUrl) + .then((registration) => { + registration.onupdatefound = () => { + const installingWorker = registration.installing; + if (installingWorker == null) { + return; + } + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // At this point, the updated precached content has been fetched, + // but the previous service worker will still serve the older + // content until all client tabs are closed. + console.log( + 'New content is available and will be used when all ' + + 'tabs for this page are closed. See https://cra.link/PWA.' + ); + + // Execute callback + if (config && config.onUpdate) { + config.onUpdate(registration); + } + } else { + // At this point, everything has been precached. + // It's the perfect time to display a + // "Content is cached for offline use." message. + console.log('Content is cached for offline use.'); + + // Execute callback + if (config && config.onSuccess) { + config.onSuccess(registration); + } + } + } + }; + }; + }) + .catch((error) => { + console.error('Error during service worker registration:', error); + }); +} + +function checkValidServiceWorker(swUrl, config) { + // Check if the service worker can be found. If it can't reload the page. + fetch(swUrl, { + headers: { 'Service-Worker': 'script' }, + }) + .then((response) => { + // Ensure service worker exists, and that we really are getting a JS file. + const contentType = response.headers.get('content-type'); + if ( + response.status === 404 || + (contentType != null && contentType.indexOf('javascript') === -1) + ) { + // No service worker found. Probably a different app. Reload the page. + navigator.serviceWorker.ready.then((registration) => { + registration.unregister().then(() => { + window.location.reload(); + }); + }); + } else { + // Service worker found. Proceed as normal. + registerValidSW(swUrl, config); + } + }) + .catch(() => { + console.log('No internet connection found. App is running in offline mode.'); + }); +} + +export function unregister() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.ready + .then((registration) => { + registration.unregister(); + }) + .catch((error) => { + console.error(error.message); + }); + } +} |