├── .gitignore ├── index.js ├── docs ├── favicon.ico ├── manifest.json ├── precache-manifest.f353fe2dcce57fdbfff016a75e042fe3.js ├── asset-manifest.json ├── service-worker.js ├── static │ ├── css │ │ ├── main.0f84021f.chunk.css │ │ └── main.0f84021f.chunk.css.map │ └── js │ │ ├── runtime~main.46fcdfae.js │ │ ├── runtime~main.46fcdfae.js.map │ │ ├── main.066f72df.chunk.js │ │ └── main.066f72df.chunk.js.map └── index.html ├── prettier.config.js ├── demo-app ├── public │ ├── favicon.ico │ ├── manifest.json │ └── index.html ├── README.md ├── src │ ├── App.test.js │ ├── index.js │ ├── App.css │ ├── components │ │ ├── NoPrefetchApp │ │ │ └── index.js │ │ ├── OnRenderPrefetchApp │ │ │ └── index.js │ │ └── NetworkInspector │ │ │ └── index.js │ ├── index.css │ ├── App.js │ └── serviceWorker.js ├── .gitignore └── package.json ├── .babelrc ├── src ├── index.js └── prefetcher.js ├── LICENSE ├── package.json ├── README.md └── test └── prefetcher.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | export * from './dist/index'; 2 | export { default } from './dist/index'; 3 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manojVivek/react-prefetcher/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | trailingComma: 'es5', 4 | }; 5 | -------------------------------------------------------------------------------- /demo-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manojVivek/react-prefetcher/HEAD/demo-app/public/favicon.ico -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-react", "@babel/preset-env"], 3 | "plugins": ["@babel/plugin-proposal-class-properties"] 4 | } -------------------------------------------------------------------------------- /demo-app/README.md: -------------------------------------------------------------------------------- 1 | ## React-prefetcher Demo App 2 | 3 | This app can be previewed here: https://manojvivek.github.io/react-prefetcher/ 4 | -------------------------------------------------------------------------------- /demo-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /docs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /demo-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /demo-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Prefetcher from './prefetcher'; 3 | 4 | export default Prefetcher; 5 | 6 | export const OnRenderPrefetcher = ({ link, ...other }) => ( 7 | 8 | ); 9 | 10 | export const OnHoverPrefetcher = ({ link, ...other }) => ( 11 | 12 | ); 13 | 14 | export const OnClickPrefetcher = ({ link, ...other }) => ( 15 | 16 | ); 17 | -------------------------------------------------------------------------------- /demo-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /demo-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 40vmin; 8 | pointer-events: none; 9 | } 10 | 11 | .App-header { 12 | background-color: #282c34; 13 | min-height: 100vh; 14 | display: flex; 15 | flex-direction: column; 16 | align-items: center; 17 | justify-content: center; 18 | font-size: calc(10px + 2vmin); 19 | color: white; 20 | } 21 | 22 | .App-link { 23 | color: #61dafb; 24 | } 25 | 26 | @keyframes App-logo-spin { 27 | from { 28 | transform: rotate(0deg); 29 | } 30 | to { 31 | transform: rotate(360deg); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /demo-app/src/components/NoPrefetchApp/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class NoPrefetchApp extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | this.state = { clicked: false }; 7 | } 8 | 9 | render() { 10 | return ( 11 |
12 |
this.setState({ clicked: true })} 15 | > 16 | Click to show image 17 |
18 | {this.state.clicked ? ( 19 | 26 | ) : null} 27 |
28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /demo-app/src/components/OnRenderPrefetchApp/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Prefetcher from 'react-prefetcher'; 3 | 4 | export default class OnRenderPrefetchApp extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { clicked: false }; 8 | this.imageUrl = 9 | 'https://assets.imgix.net/examples/kingfisher.jpg?w=200&rand=' + 10 | this.props.rand; 11 | } 12 | 13 | render() { 14 | return ( 15 |
16 | 17 |
this.setState({ clicked: true })} 20 | > 21 | Click to show image 22 |
23 |
24 | {this.state.clicked ? : null} 25 |
26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs/precache-manifest.f353fe2dcce57fdbfff016a75e042fe3.js: -------------------------------------------------------------------------------- 1 | self.__precacheManifest = (self.__precacheManifest || []).concat([ 2 | { 3 | revision: '45e6beacb7f569ac0c559ef2e02caac9', 4 | url: 'https://manojvivek.github.io/react-prefetcher/index.html', 5 | }, 6 | { 7 | revision: 'f47b8a7839495d1ac2ff', 8 | url: 9 | 'https://manojvivek.github.io/react-prefetcher/static/css/main.0f84021f.chunk.css', 10 | }, 11 | { 12 | revision: 'f295713939b9d62a5fc3', 13 | url: 14 | 'https://manojvivek.github.io/react-prefetcher/static/js/2.dcc8df9a.chunk.js', 15 | }, 16 | { 17 | revision: 'f47b8a7839495d1ac2ff', 18 | url: 19 | 'https://manojvivek.github.io/react-prefetcher/static/js/main.066f72df.chunk.js', 20 | }, 21 | { 22 | revision: 'aca3f9a7930a1c37a08b', 23 | url: 24 | 'https://manojvivek.github.io/react-prefetcher/static/js/runtime~main.46fcdfae.js', 25 | }, 26 | ]); 27 | -------------------------------------------------------------------------------- /demo-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-prefetcher-demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "homepage": "https://manojvivek.github.io/react-prefetcher/", 6 | "dependencies": { 7 | "react": "^16.8.6", 8 | "react-dom": "^16.8.6", 9 | "react-prefetcher": "^1.0.3", 10 | "react-scripts": "3.0.1" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "PUBLIC_URL=https://manojvivek.github.io/react-prefetcher react-scripts build && rm -rf ../docs && mv build ../docs", 15 | "test": "react-scripts test", 16 | "eject": "react-scripts eject" 17 | }, 18 | "eslintConfig": { 19 | "extends": "react-app" 20 | }, 21 | "browserslist": { 22 | "production": [ 23 | ">0.2%", 24 | "not dead", 25 | "not op_mini all" 26 | ], 27 | "development": [ 28 | "last 1 chrome version", 29 | "last 1 firefox version", 30 | "last 1 safari version" 31 | ] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Manoj Vivek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/asset-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": { 3 | "main.css": "https://manojvivek.github.io/react-prefetcher/static/css/main.0f84021f.chunk.css", 4 | "main.js": "https://manojvivek.github.io/react-prefetcher/static/js/main.066f72df.chunk.js", 5 | "main.js.map": "https://manojvivek.github.io/react-prefetcher/static/js/main.066f72df.chunk.js.map", 6 | "runtime~main.js": "https://manojvivek.github.io/react-prefetcher/static/js/runtime~main.46fcdfae.js", 7 | "runtime~main.js.map": "https://manojvivek.github.io/react-prefetcher/static/js/runtime~main.46fcdfae.js.map", 8 | "static/js/2.dcc8df9a.chunk.js": "https://manojvivek.github.io/react-prefetcher/static/js/2.dcc8df9a.chunk.js", 9 | "static/js/2.dcc8df9a.chunk.js.map": "https://manojvivek.github.io/react-prefetcher/static/js/2.dcc8df9a.chunk.js.map", 10 | "index.html": "https://manojvivek.github.io/react-prefetcher/index.html", 11 | "precache-manifest.f353fe2dcce57fdbfff016a75e042fe3.js": "https://manojvivek.github.io/react-prefetcher/precache-manifest.f353fe2dcce57fdbfff016a75e042fe3.js", 12 | "service-worker.js": "https://manojvivek.github.io/react-prefetcher/service-worker.js", 13 | "static/css/main.0f84021f.chunk.css.map": "https://manojvivek.github.io/react-prefetcher/static/css/main.0f84021f.chunk.css.map" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /demo-app/src/components/NetworkInspector/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class NetworkInspector extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | this.state = { requests: [] }; 7 | } 8 | 9 | componentDidMount() { 10 | this.intervalHandle = setInterval(this._refreshList, 200); 11 | } 12 | 13 | componentWillUnmount() { 14 | clearInterval(this.intervalHandle); 15 | } 16 | 17 | _refreshList = () => { 18 | this.setState({ 19 | requests: performance 20 | .getEntriesByType('resource') 21 | .filter(entry => entry.name.indexOf(this.props.filter) > -1), 22 | }); 23 | }; 24 | 25 | render() { 26 | return ( 27 |
28 |

Network Inspector:

29 | 30 | 31 | 32 | 33 | 34 | 35 | {this.state.requests.map(request => ( 36 | 37 | 38 | 39 | 43 | 44 | ))} 45 |
Time from Page load(ms)Resource URLTime Taken to Download(ms)
{Math.round(request.startTime)}{request.name} 40 | {Math.round(request.duration)} 41 | {request.duration < 10 && '(from cache)'} 42 |
46 |
47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /docs/service-worker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome to your Workbox-powered service worker! 3 | * 4 | * You'll need to register this file in your web app and you should 5 | * disable HTTP caching for this file too. 6 | * See https://goo.gl/nhQhGp 7 | * 8 | * The rest of the code is auto-generated. Please don't update this file 9 | * directly; instead, make changes to your Workbox build configuration 10 | * and re-run your build process. 11 | * See https://goo.gl/2aRDsh 12 | */ 13 | 14 | importScripts( 15 | 'https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js' 16 | ); 17 | 18 | importScripts( 19 | 'https://manojvivek.github.io/react-prefetcher/precache-manifest.f353fe2dcce57fdbfff016a75e042fe3.js' 20 | ); 21 | 22 | self.addEventListener('message', event => { 23 | if (event.data && event.data.type === 'SKIP_WAITING') { 24 | self.skipWaiting(); 25 | } 26 | }); 27 | 28 | workbox.core.clientsClaim(); 29 | 30 | /** 31 | * The workboxSW.precacheAndRoute() method efficiently caches and responds to 32 | * requests for URLs in the manifest. 33 | * See https://goo.gl/S9QRab 34 | */ 35 | self.__precacheManifest = [].concat(self.__precacheManifest || []); 36 | workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); 37 | 38 | workbox.routing.registerNavigationRoute( 39 | workbox.precaching.getCacheKeyForURL( 40 | 'https://manojvivek.github.io/react-prefetcher/index.html' 41 | ), 42 | { 43 | blacklist: [/^\/_/, /\/[^\/]+\.[^\/]+$/], 44 | } 45 | ); 46 | -------------------------------------------------------------------------------- /demo-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #3e3e3e; 3 | margin: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 5 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 13 | monospace; 14 | } 15 | 16 | .wrapper { 17 | display: flex; 18 | } 19 | 20 | .left { 21 | flex: 0 0 30%; 22 | } 23 | 24 | .right { 25 | flex: 1; 26 | } 27 | 28 | table { 29 | border-collapse: collapse; 30 | width: 100%; 31 | } 32 | 33 | th, 34 | td { 35 | text-align: left; 36 | padding: 8px; 37 | } 38 | 39 | tr:nth-child(even) { 40 | background-color: #f2f2f2; 41 | } 42 | 43 | .cta-button { 44 | padding: 10px 20px; 45 | background: cornflowerblue; 46 | width: fit-content; 47 | margin: 0 auto 10px; 48 | text-transform: uppercase; 49 | color: white; 50 | cursor: pointer; 51 | } 52 | 53 | .section { 54 | border: 2px solid grey; 55 | margin: 20px; 56 | padding: 20px; 57 | min-height: 200px; 58 | min-width: 244px; 59 | } 60 | 61 | .header { 62 | text-align: left; 63 | margin: 20px; 64 | } 65 | 66 | .title { 67 | font-size: 30px; 68 | font-weight: bold; 69 | } 70 | 71 | .code-link { 72 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 73 | monospace; 74 | color: inherit; 75 | text-decoration: none; 76 | } 77 | -------------------------------------------------------------------------------- /demo-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | React-prefetcher Demo App 23 | 24 | 25 | 26 |
27 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /demo-app/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './App.css'; 3 | import NetworkInspector from './components/NetworkInspector'; 4 | import NoPrefetchApp from './components/NoPrefetchApp'; 5 | import OnRenderPrefetchApp from './components/OnRenderPrefetchApp'; 6 | 7 | class App extends React.Component { 8 | noPrefetchRand = Math.random(); 9 | onRenderPrefetchRand = Math.random(); 10 | 11 | render() { 12 | return ( 13 |
14 |
15 | Component without any Prefetching 16 | 22 | (see code) 23 | 24 |
25 |
26 |
27 | 28 |
29 |
30 | 31 |
32 |
33 |
34 | Component with OnRender Prefetching 35 | 41 | (see code) 42 | 43 |
44 |
45 |
46 | 47 |
48 |
49 | 50 |
51 |
52 |
53 | ); 54 | } 55 | } 56 | 57 | export default App; 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-prefetcher", 3 | "version": "1.1.2", 4 | "description": "A react library providing components that help with interaction-based asset pre-fetching.", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "test": "nyc mocha --require @babel/register --require jsdom-global/register", 8 | "test-dev": "mocha --require @babel/register --require @babel/polyfill --require jsdom-global/register --watch", 9 | "build": "babel src --out-dir dist", 10 | "dev": "babel src --out-dir dist --watch", 11 | "prebuild": "rm -rf dist/*", 12 | "prepublish": "npm run build", 13 | "release": "np" 14 | }, 15 | "files": [ 16 | "dist" 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/manojVivek/react-prefetcher.git" 21 | }, 22 | "keywords": [ 23 | "prefetch", 24 | "performance", 25 | "preload", 26 | "resource", 27 | "hint", 28 | "web", 29 | "performance", 30 | "speed", 31 | "link", 32 | "cache", 33 | "react" 34 | ], 35 | "author": "Manoj Vivek (https://github.com/manojVivek)", 36 | "license": "MIT", 37 | "bugs": { 38 | "url": "https://github.com/manojVivek/react-prefetcher/issues" 39 | }, 40 | "homepage": "https://github.com/manojVivek/react-prefetcher#readme", 41 | "devDependencies": { 42 | "@babel/cli": "^7.4.4", 43 | "@babel/core": "^7.4.5", 44 | "@babel/plugin-proposal-class-properties": "^7.4.4", 45 | "@babel/polyfill": "^7.4.4", 46 | "@babel/preset-env": "^7.4.5", 47 | "@babel/preset-react": "^7.0.0", 48 | "@babel/register": "^7.4.4", 49 | "chai": "^4.2.0", 50 | "chai-enzyme": "^1.0.0-beta.1", 51 | "chai-spies": "^1.0.0", 52 | "enzyme": "^3.10.0", 53 | "enzyme-adapter-react-16": "^1.14.0", 54 | "husky": "^2.4.0", 55 | "jsdom": "15.1.1", 56 | "jsdom-global": "3.0.2", 57 | "mocha": "^6.1.4", 58 | "np": "^5.0.3", 59 | "nyc": "^14.1.1", 60 | "prettier": "^1.18.2", 61 | "pretty-quick": "^1.11.0", 62 | "react": "^16.8.6", 63 | "react-dom": "^16.8.6" 64 | }, 65 | "peerDependencies": { 66 | "react": "^16.8.6", 67 | "react-dom": "^16.8.6" 68 | }, 69 | "husky": { 70 | "hooks": { 71 | "pre-commit": "pretty-quick --staged" 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/prefetcher.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | 3 | const loadedUrls = {}; 4 | export default class Prefetcher extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | rendered: false, 9 | hovered: false, 10 | clicked: false, 11 | }; 12 | } 13 | 14 | _delay = this.props.delayMs || 500; 15 | 16 | timeoutHandles = []; 17 | 18 | _eventHappened = event => 19 | this.timeoutHandles.push( 20 | setTimeout(() => this.setState({ [event + 'ed']: true }), this._delay) 21 | ); 22 | 23 | componentDidMount() { 24 | this._eventHappened('render'); 25 | } 26 | 27 | componentWillUnmount() { 28 | for (const handle of this.timeoutHandles) { 29 | clearTimeout(handle); 30 | } 31 | } 32 | 33 | render() { 34 | return ( 35 | 36 | {this.props.children && ( 37 | this._eventHappened('hover')} 39 | onClick={() => this._eventHappened('click')} 40 | className={this.props.className} 41 | style={{ width: 'max-content', ...this.props.style }} 42 | > 43 | {this.props.children} 44 | 45 | )} 46 | {this.state.rendered && this._prefetchAssets(this.props.onRenderAssets)} 47 | {this.state.hovered && this._prefetchAssets(this.props.onHoverAssets)} 48 | {this.state.clicked && this._prefetchAssets(this.props.onClickAssets)} 49 | 50 | ); 51 | } 52 | 53 | _prefetchAssets = assets => { 54 | if (!assets) { 55 | return null; 56 | } 57 | if (assets.constructor !== Array) { 58 | return this._renderPrefetch(assets); 59 | } 60 | return assets.map(this._renderPrefetch).filter(Boolean); 61 | }; 62 | 63 | _renderPrefetch = href => { 64 | if (typeof href === 'object') { 65 | this._processCustomFetcher(href); 66 | return null; 67 | } 68 | 69 | if (!href || loadedUrls[href]) { 70 | return; 71 | } 72 | return ( 73 | (loadedUrls[href] = true)} 78 | /> 79 | ); 80 | }; 81 | 82 | _processCustomFetcher = config => { 83 | if (typeof config.fetcher !== 'function') { 84 | console.warn('Invalid config to prefetch:', config); 85 | return; 86 | } 87 | if (loadedUrls[config.href]) { 88 | return; 89 | } 90 | config.fetcher(); 91 | loadedUrls[config.href] = true; 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /docs/static/css/main.0f84021f.chunk.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #3e3e3e; 3 | margin: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 5 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | code { 10 | font-family: source-code-pro, Menlo, Monaco, Consolas, Courier New, monospace; 11 | } 12 | .wrapper { 13 | display: flex; 14 | } 15 | .left { 16 | flex: 0 0 30%; 17 | } 18 | .right { 19 | flex: 1 1; 20 | } 21 | table { 22 | border-collapse: collapse; 23 | width: 100%; 24 | } 25 | td, 26 | th { 27 | text-align: left; 28 | padding: 8px; 29 | } 30 | tr:nth-child(2n) { 31 | background-color: #f2f2f2; 32 | } 33 | .cta-button { 34 | padding: 10px 20px; 35 | background: #6495ed; 36 | width: -webkit-fit-content; 37 | width: -moz-fit-content; 38 | width: fit-content; 39 | margin: 0 auto 10px; 40 | text-transform: uppercase; 41 | color: #fff; 42 | cursor: pointer; 43 | } 44 | .section { 45 | border: 2px solid grey; 46 | margin: 20px; 47 | padding: 20px; 48 | min-height: 200px; 49 | min-width: 244px; 50 | } 51 | .header { 52 | text-align: left; 53 | margin: 20px; 54 | } 55 | .title { 56 | font-size: 30px; 57 | font-weight: 700; 58 | } 59 | .code-link { 60 | font-family: source-code-pro, Menlo, Monaco, Consolas, Courier New, monospace; 61 | color: inherit; 62 | text-decoration: none; 63 | } 64 | .App { 65 | text-align: center; 66 | } 67 | .App-logo { 68 | -webkit-animation: App-logo-spin 20s linear infinite; 69 | animation: App-logo-spin 20s linear infinite; 70 | height: 40vmin; 71 | pointer-events: none; 72 | } 73 | .App-header { 74 | background-color: #282c34; 75 | min-height: 100vh; 76 | display: flex; 77 | flex-direction: column; 78 | align-items: center; 79 | justify-content: center; 80 | font-size: calc(10px + 2vmin); 81 | color: #fff; 82 | } 83 | .App-link { 84 | color: #61dafb; 85 | } 86 | @-webkit-keyframes App-logo-spin { 87 | 0% { 88 | -webkit-transform: rotate(0deg); 89 | transform: rotate(0deg); 90 | } 91 | to { 92 | -webkit-transform: rotate(1turn); 93 | transform: rotate(1turn); 94 | } 95 | } 96 | @keyframes App-logo-spin { 97 | 0% { 98 | -webkit-transform: rotate(0deg); 99 | transform: rotate(0deg); 100 | } 101 | to { 102 | -webkit-transform: rotate(1turn); 103 | transform: rotate(1turn); 104 | } 105 | } 106 | /*# sourceMappingURL=main.0f84021f.chunk.css.map */ 107 | -------------------------------------------------------------------------------- /docs/static/css/main.0f84021f.chunk.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["index.css","App.css"],"names":[],"mappings":"AAAA,KACE,aAAc,CACd,QAAS,CACT,mIAEY,CACZ,kCAAmC,CACnC,iCACF,CAEA,KACE,uEAEF,CAEA,SACE,YACF,CAEA,MACE,YACF,CAEA,OACE,QACF,CAEA,MACE,wBAAyB,CACzB,UACF,CAEA,MAEE,eAAgB,CAChB,WACF,CAEA,iBACE,wBACF,CAEA,YACE,iBAAkB,CAClB,kBAA0B,CAC1B,yBAAkB,CAAlB,sBAAkB,CAAlB,iBAAkB,CAClB,kBAAmB,CACnB,wBAAyB,CACzB,UAAY,CACZ,cACF,CAEA,SACE,qBAAsB,CACtB,WAAY,CACZ,YAAa,CACb,gBAAiB,CACjB,eACF,CAEA,QACE,eAAgB,CAChB,WACF,CAEA,OACE,cAAe,CACf,eACF,CAEA,WACE,uEACW,CACX,aAAc,CACd,oBACF,CC3EA,KACE,iBACF,CAEA,UACE,mDAA4C,CAA5C,2CAA4C,CAC5C,aAAc,CACd,mBACF,CAEA,YACE,wBAAyB,CACzB,gBAAiB,CACjB,YAAa,CACb,qBAAsB,CACtB,kBAAmB,CACnB,sBAAuB,CACvB,4BAA6B,CAC7B,UACF,CAEA,UACE,aACF,CAEA,iCACE,GACE,8BAAuB,CAAvB,sBACF,CACA,GACE,+BAAyB,CAAzB,uBACF,CACF,CAPA,yBACE,GACE,8BAAuB,CAAvB,sBACF,CACA,GACE,+BAAyB,CAAzB,uBACF,CACF","file":"main.0f84021f.chunk.css","sourcesContent":["body {\n color: #3e3e3e;\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n\n.wrapper {\n display: flex;\n}\n\n.left {\n flex: 0 0 30%;\n}\n\n.right {\n flex: 1;\n}\n\ntable {\n border-collapse: collapse;\n width: 100%;\n}\n\nth,\ntd {\n text-align: left;\n padding: 8px;\n}\n\ntr:nth-child(even) {\n background-color: #f2f2f2;\n}\n\n.cta-button {\n padding: 10px 20px;\n background: cornflowerblue;\n width: fit-content;\n margin: 0 auto 10px;\n text-transform: uppercase;\n color: white;\n cursor: pointer;\n}\n\n.section {\n border: 2px solid grey;\n margin: 20px;\n padding: 20px;\n min-height: 200px;\n min-width: 244px;\n}\n\n.header {\n text-align: left;\n margin: 20px;\n}\n\n.title {\n font-size: 30px;\n font-weight: bold;\n}\n\n.code-link {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n color: inherit;\n text-decoration: none;\n}\n",".App {\n text-align: center;\n}\n\n.App-logo {\n animation: App-logo-spin infinite 20s linear;\n height: 40vmin;\n pointer-events: none;\n}\n\n.App-header {\n background-color: #282c34;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n font-size: calc(10px + 2vmin);\n color: white;\n}\n\n.App-link {\n color: #61dafb;\n}\n\n@keyframes App-logo-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n"]} -------------------------------------------------------------------------------- /docs/static/js/runtime~main.46fcdfae.js: -------------------------------------------------------------------------------- 1 | !(function(e) { 2 | function r(r) { 3 | for ( 4 | var n, i, f = r[0], l = r[1], a = r[2], c = 0, s = []; 5 | c < f.length; 6 | c++ 7 | ) 8 | (i = f[c]), o[i] && s.push(o[i][0]), (o[i] = 0); 9 | for (n in l) Object.prototype.hasOwnProperty.call(l, n) && (e[n] = l[n]); 10 | for (p && p(r); s.length; ) s.shift()(); 11 | return u.push.apply(u, a || []), t(); 12 | } 13 | function t() { 14 | for (var e, r = 0; r < u.length; r++) { 15 | for (var t = u[r], n = !0, f = 1; f < t.length; f++) { 16 | var l = t[f]; 17 | 0 !== o[l] && (n = !1); 18 | } 19 | n && (u.splice(r--, 1), (e = i((i.s = t[0])))); 20 | } 21 | return e; 22 | } 23 | var n = {}, 24 | o = { 1: 0 }, 25 | u = []; 26 | function i(r) { 27 | if (n[r]) return n[r].exports; 28 | var t = (n[r] = { i: r, l: !1, exports: {} }); 29 | return e[r].call(t.exports, t, t.exports, i), (t.l = !0), t.exports; 30 | } 31 | (i.m = e), 32 | (i.c = n), 33 | (i.d = function(e, r, t) { 34 | i.o(e, r) || Object.defineProperty(e, r, { enumerable: !0, get: t }); 35 | }), 36 | (i.r = function(e) { 37 | 'undefined' !== typeof Symbol && 38 | Symbol.toStringTag && 39 | Object.defineProperty(e, Symbol.toStringTag, { value: 'Module' }), 40 | Object.defineProperty(e, '__esModule', { value: !0 }); 41 | }), 42 | (i.t = function(e, r) { 43 | if ((1 & r && (e = i(e)), 8 & r)) return e; 44 | if (4 & r && 'object' === typeof e && e && e.__esModule) return e; 45 | var t = Object.create(null); 46 | if ( 47 | (i.r(t), 48 | Object.defineProperty(t, 'default', { enumerable: !0, value: e }), 49 | 2 & r && 'string' != typeof e) 50 | ) 51 | for (var n in e) 52 | i.d( 53 | t, 54 | n, 55 | function(r) { 56 | return e[r]; 57 | }.bind(null, n) 58 | ); 59 | return t; 60 | }), 61 | (i.n = function(e) { 62 | var r = 63 | e && e.__esModule 64 | ? function() { 65 | return e.default; 66 | } 67 | : function() { 68 | return e; 69 | }; 70 | return i.d(r, 'a', r), r; 71 | }), 72 | (i.o = function(e, r) { 73 | return Object.prototype.hasOwnProperty.call(e, r); 74 | }), 75 | (i.p = 'https://manojvivek.github.io/react-prefetcher/'); 76 | var f = (window.webpackJsonp = window.webpackJsonp || []), 77 | l = f.push.bind(f); 78 | (f.push = r), (f = f.slice()); 79 | for (var a = 0; a < f.length; a++) r(f[a]); 80 | var p = l; 81 | t(); 82 | })([]); 83 | //# sourceMappingURL=runtime~main.46fcdfae.js.map 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-Prefetcher ![GitHub](https://img.shields.io/github/license/manojVivek/react-prefetcher.svg) ![npm](https://img.shields.io/npm/v/react-prefetcher.svg) 2 | 3 | A react library providing components that help with interaction-based asset pre-fetching. 4 | 5 | ## Installation 6 | 7 | Using npm: 8 | 9 | ```bash 10 | npm install react-prefetcher 11 | ``` 12 | 13 | Using yarn: 14 | 15 | ```bash 16 | yarn add react-prefetcher 17 | ``` 18 | 19 | ## Usage 20 | 21 | ### OnRenderPrefetcher 22 | 23 | ```javascript 24 | import React, {Fragment} from 'react'; 25 | import {OnRenderPrefetcher} from 'react-prefetcher'; 26 | 27 | export default () => ( 28 | 29 | 30 |

Asset prefetching happens when this `

` tag is rendered

31 |
32 |
33 | ); 34 | ``` 35 | 36 | ### OnHoverPrefetcher 37 | 38 | ```javascript 39 | import React, {Fragment} from 'react'; 40 | import {OnHoverPrefetcher} from 'react-prefetcher'; 41 | 42 | export default () => ( 43 | 44 | 45 |

Asset prefetching happens when the user hovers this `

` tag

46 |
47 |
48 | ); 49 | ``` 50 | 51 | ### OnClickPrefetcher 52 | 53 | ```javascript 54 | import React, {Fragment} from 'react'; 55 | import {OnClickPrefetcher} from 'react-prefetcher'; 56 | 57 | export default () => ( 58 | 59 | 60 |

Asset prefetching happens when the user clicks this `

` tag

61 |
62 |
63 | ); 64 | ``` 65 | 66 | ### Prefetcher 67 | 68 | Customize prefetching by combining multiple interactions. 69 | 70 | ```javascript 71 | import React, {Fragment} from 'react'; 72 | import Prefetcher from 'react-prefetcher'; 73 | 74 | export default () => ( 75 | 76 | 81 |

82 | 1. Prefetches https://example.com/on-render-asset-url on render of this `

` tag. 83 | 2. Prefetches https://example.com/on-hover-asset-url when the user hovers this `

` tag. 84 | 3. Prefetches https://example.com/on-click-asset-url when the user clicks this `

` tag. 85 |

86 |
87 |
88 | ); 89 | ``` 90 | 91 | ### Possible values for assets 92 | 93 | 1. URL 94 | 2. Array of URLs 95 | 3. Object with custom fetcher 96 | 97 | #### URL as asset: 98 | 99 | ```javascript 100 | 101 | ``` 102 | 103 | #### Array of URLs: 104 | 105 | ```javascript 106 | 112 | ``` 113 | 114 | #### Custom fetcher function: 115 | 116 | ```javascript 117 | { 121 | /*custom fetcher that gets invoked on render*/ 122 | }, 123 | }} 124 | /> 125 | ``` 126 | 127 | ## Contributing 128 | 129 | Pull requests are welcome. 130 | 131 | ## License 132 | 133 | [MIT](https://github.com/manojVivek/react-prefetcher/blob/master/LICENSE) 134 | -------------------------------------------------------------------------------- /test/prefetcher.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import chai, { expect } from 'chai'; 3 | import chaiEnzyme from 'chai-enzyme'; 4 | import spies from 'chai-spies'; 5 | import Enzyme, { shallow } from 'enzyme'; 6 | import Adapter from 'enzyme-adapter-react-16'; 7 | 8 | import Prefetcher from '../index'; 9 | 10 | chai.use(chaiEnzyme()); 11 | chai.use(spies); 12 | Enzyme.configure({ adapter: new Adapter() }); 13 | 14 | const children =
test
; 15 | const prefetchLink1 = `https://prefetch-link1.com`; 16 | const prefetchLink2 = `https://prefetch-link2.com`; 17 | const customPrefetcher1 = () => {}; 18 | const customPrefetcher2 = () => {}; 19 | 20 | describe('Children tests', () => { 21 | it('renders nothing when no children and no onRenderAssets', () => { 22 | const wrapper = shallow(); 23 | expect(wrapper.children()).to.have.lengthOf(0); 24 | }); 25 | 26 | it('renders the children when no prefetching assets', () => { 27 | const wrapper = shallow({children}); 28 | expect(wrapper).to.contain(children); 29 | }); 30 | }); 31 | 32 | describe('onRenderAssets tests', () => { 33 | it('renders prefetch statements for single url for onRenderAssets props', () => { 34 | const wrapper = shallow( 35 | {children} 36 | ); 37 | expect(wrapper).to.contain(children); 38 | // TODO: find a way with simulated events to avoid this manual setState() 39 | wrapper.setState({ rendered: true }); 40 | expect(wrapper.find(`link[href='${prefetchLink1}']`)).to.have.lengthOf(1); 41 | }); 42 | 43 | it('renders prefetch statements for an array of urls for onRenderAssets props', () => { 44 | const prefetchAssets = [prefetchLink1, prefetchLink2]; 45 | const wrapper = shallow( 46 | {children} 47 | ); 48 | expect(wrapper).to.contain(children); 49 | wrapper.setState({ rendered: true }); 50 | for (const asset of prefetchAssets) { 51 | expect(wrapper.find(`link[href='${asset}']`)).to.have.lengthOf(1); 52 | } 53 | }); 54 | 55 | it('invokes custom prefetcher for an single of prefetcher function for onRenderAssets props', () => { 56 | const prefetchAsset = chai.spy(customPrefetcher1); 57 | const wrapper = shallow( 58 | 59 | {children} 60 | 61 | ); 62 | expect(wrapper).to.contain(children); 63 | wrapper.setState({ rendered: true }); 64 | expect(prefetchAsset).to.have.been.called(); 65 | }); 66 | 67 | it('invokes custom prefetchers for an array of custom prefetchers for onRenderAssets props', () => { 68 | const prefetchAssets = [ 69 | chai.spy(customPrefetcher1), 70 | chai.spy(customPrefetcher2), 71 | ]; 72 | const wrapper = shallow( 73 | ({ 75 | href: idx, 76 | fetcher, 77 | }))} 78 | > 79 | {children} 80 | 81 | ); 82 | expect(wrapper).to.contain(children); 83 | wrapper.setState({ rendered: true }); 84 | for (const asset of prefetchAssets) { 85 | expect(asset).to.have.been.called(); 86 | } 87 | }); 88 | 89 | it('works fine with a mix of urls and custom fetcher for onRenderAssets props', () => { 90 | const customFetcher = chai.spy(customPrefetcher1); 91 | const wrapper = shallow( 92 | 98 | {children} 99 | 100 | ); 101 | expect(wrapper).to.contain(children); 102 | wrapper.setState({ rendered: true }); 103 | expect(wrapper.find(`link[href='${prefetchLink1}']`)).to.have.lengthOf(1); 104 | expect(customFetcher).to.have.been.called(); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 15 | React-prefetcher Demo App 16 | 20 | 21 | 22 | 23 |
24 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /demo-app/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /docs/static/js/runtime~main.46fcdfae.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../webpack/bootstrap"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","executeModules","i","resolves","length","installedChunks","push","Object","prototype","hasOwnProperty","call","modules","parentJsonpFunction","shift","deferredModules","apply","checkDeferredModules","result","deferredModule","fulfilled","j","depId","splice","__webpack_require__","s","installedModules","1","exports","module","l","m","c","d","name","getter","o","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","p","jsonpArray","window","oldJsonpFunction","slice"],"mappings":"aACA,SAAAA,EAAAC,GAQA,IAPA,IAMAC,EAAAC,EANAC,EAAAH,EAAA,GACAI,EAAAJ,EAAA,GACAK,EAAAL,EAAA,GAIAM,EAAA,EAAAC,EAAA,GACQD,EAAAH,EAAAK,OAAoBF,IAC5BJ,EAAAC,EAAAG,GACAG,EAAAP,IACAK,EAAAG,KAAAD,EAAAP,GAAA,IAEAO,EAAAP,GAAA,EAEA,IAAAD,KAAAG,EACAO,OAAAC,UAAAC,eAAAC,KAAAV,EAAAH,KACAc,EAAAd,GAAAG,EAAAH,IAKA,IAFAe,KAAAhB,GAEAO,EAAAC,QACAD,EAAAU,OAAAV,GAOA,OAHAW,EAAAR,KAAAS,MAAAD,EAAAb,GAAA,IAGAe,IAEA,SAAAA,IAEA,IADA,IAAAC,EACAf,EAAA,EAAiBA,EAAAY,EAAAV,OAA4BF,IAAA,CAG7C,IAFA,IAAAgB,EAAAJ,EAAAZ,GACAiB,GAAA,EACAC,EAAA,EAAkBA,EAAAF,EAAAd,OAA2BgB,IAAA,CAC7C,IAAAC,EAAAH,EAAAE,GACA,IAAAf,EAAAgB,KAAAF,GAAA,GAEAA,IACAL,EAAAQ,OAAApB,IAAA,GACAe,EAAAM,IAAAC,EAAAN,EAAA,KAGA,OAAAD,EAIA,IAAAQ,EAAA,GAKApB,EAAA,CACAqB,EAAA,GAGAZ,EAAA,GAGA,SAAAS,EAAA1B,GAGA,GAAA4B,EAAA5B,GACA,OAAA4B,EAAA5B,GAAA8B,QAGA,IAAAC,EAAAH,EAAA5B,GAAA,CACAK,EAAAL,EACAgC,GAAA,EACAF,QAAA,IAUA,OANAhB,EAAAd,GAAAa,KAAAkB,EAAAD,QAAAC,IAAAD,QAAAJ,GAGAK,EAAAC,GAAA,EAGAD,EAAAD,QAKAJ,EAAAO,EAAAnB,EAGAY,EAAAQ,EAAAN,EAGAF,EAAAS,EAAA,SAAAL,EAAAM,EAAAC,GACAX,EAAAY,EAAAR,EAAAM,IACA1B,OAAA6B,eAAAT,EAAAM,EAAA,CAA0CI,YAAA,EAAAC,IAAAJ,KAK1CX,EAAAgB,EAAA,SAAAZ,GACA,qBAAAa,eAAAC,aACAlC,OAAA6B,eAAAT,EAAAa,OAAAC,YAAA,CAAwDC,MAAA,WAExDnC,OAAA6B,eAAAT,EAAA,cAAiDe,OAAA,KAQjDnB,EAAAoB,EAAA,SAAAD,EAAAE,GAEA,GADA,EAAAA,IAAAF,EAAAnB,EAAAmB,IACA,EAAAE,EAAA,OAAAF,EACA,KAAAE,GAAA,kBAAAF,QAAAG,WAAA,OAAAH,EACA,IAAAI,EAAAvC,OAAAwC,OAAA,MAGA,GAFAxB,EAAAgB,EAAAO,GACAvC,OAAA6B,eAAAU,EAAA,WAAyCT,YAAA,EAAAK,UACzC,EAAAE,GAAA,iBAAAF,EAAA,QAAAM,KAAAN,EAAAnB,EAAAS,EAAAc,EAAAE,EAAA,SAAAA,GAAgH,OAAAN,EAAAM,IAAqBC,KAAA,KAAAD,IACrI,OAAAF,GAIAvB,EAAA2B,EAAA,SAAAtB,GACA,IAAAM,EAAAN,KAAAiB,WACA,WAA2B,OAAAjB,EAAA,SAC3B,WAAiC,OAAAA,GAEjC,OADAL,EAAAS,EAAAE,EAAA,IAAAA,GACAA,GAIAX,EAAAY,EAAA,SAAAgB,EAAAC,GAAsD,OAAA7C,OAAAC,UAAAC,eAAAC,KAAAyC,EAAAC,IAGtD7B,EAAA8B,EAAA,iDAEA,IAAAC,EAAAC,OAAA,aAAAA,OAAA,iBACAC,EAAAF,EAAAhD,KAAA2C,KAAAK,GACAA,EAAAhD,KAAAX,EACA2D,IAAAG,QACA,QAAAvD,EAAA,EAAgBA,EAAAoD,EAAAlD,OAAuBF,IAAAP,EAAA2D,EAAApD,IACvC,IAAAU,EAAA4C,EAIAxC","file":"static/js/runtime~main.46fcdfae.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n \t\tvar executeModules = data[2];\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t\t// add entry modules from loaded chunk to deferred list\n \t\tdeferredModules.push.apply(deferredModules, executeModules || []);\n\n \t\t// run deferred modules when all chunks ready\n \t\treturn checkDeferredModules();\n \t};\n \tfunction checkDeferredModules() {\n \t\tvar result;\n \t\tfor(var i = 0; i < deferredModules.length; i++) {\n \t\t\tvar deferredModule = deferredModules[i];\n \t\t\tvar fulfilled = true;\n \t\t\tfor(var j = 1; j < deferredModule.length; j++) {\n \t\t\t\tvar depId = deferredModule[j];\n \t\t\t\tif(installedChunks[depId] !== 0) fulfilled = false;\n \t\t\t}\n \t\t\tif(fulfilled) {\n \t\t\t\tdeferredModules.splice(i--, 1);\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = deferredModule[0]);\n \t\t\t}\n \t\t}\n \t\treturn result;\n \t}\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t1: 0\n \t};\n\n \tvar deferredModules = [];\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"https://manojvivek.github.io/react-prefetcher/\";\n\n \tvar jsonpArray = window[\"webpackJsonp\"] = window[\"webpackJsonp\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// run deferred modules from other chunks\n \tcheckDeferredModules();\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /docs/static/js/main.066f72df.chunk.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp = window.webpackJsonp || []).push([ 2 | [0], 3 | { 4 | 10: function(e, t, a) { 5 | e.exports = a(18); 6 | }, 7 | 16: function(e, t, a) {}, 8 | 17: function(e, t, a) {}, 9 | 18: function(e, t, a) { 10 | 'use strict'; 11 | a.r(t); 12 | var n = a(0), 13 | r = a.n(n), 14 | c = a(8), 15 | l = a.n(c), 16 | s = (a(16), a(1)), 17 | i = a(2), 18 | o = a(4), 19 | m = a(3), 20 | u = a(5), 21 | d = 22 | (a(17), 23 | (function(e) { 24 | function t(e) { 25 | var a; 26 | return ( 27 | Object(s.a)(this, t), 28 | ((a = Object(o.a)( 29 | this, 30 | Object(m.a)(t).call(this, e) 31 | ))._refreshList = function() { 32 | a.setState({ 33 | requests: performance 34 | .getEntriesByType('resource') 35 | .filter(function(e) { 36 | return e.name.indexOf(a.props.filter) > -1; 37 | }), 38 | }); 39 | }), 40 | (a.state = { requests: [] }), 41 | a 42 | ); 43 | } 44 | return ( 45 | Object(u.a)(t, e), 46 | Object(i.a)(t, [ 47 | { 48 | key: 'componentDidMount', 49 | value: function() { 50 | this.intervalHandle = setInterval(this._refreshList, 200); 51 | }, 52 | }, 53 | { 54 | key: 'componentWillUnmount', 55 | value: function() { 56 | clearInterval(this.intervalHandle); 57 | }, 58 | }, 59 | { 60 | key: 'render', 61 | value: function() { 62 | return r.a.createElement( 63 | 'div', 64 | { className: 'section' }, 65 | r.a.createElement('h3', null, 'Network Inspector:'), 66 | r.a.createElement( 67 | 'table', 68 | null, 69 | r.a.createElement( 70 | 'tr', 71 | null, 72 | r.a.createElement( 73 | 'th', 74 | null, 75 | 'Time from Page load(ms)' 76 | ), 77 | r.a.createElement('th', null, 'Resource URL'), 78 | r.a.createElement( 79 | 'th', 80 | null, 81 | 'Time Taken to Download(ms)' 82 | ) 83 | ), 84 | this.state.requests.map(function(e) { 85 | return r.a.createElement( 86 | 'tr', 87 | null, 88 | r.a.createElement( 89 | 'td', 90 | null, 91 | Math.round(e.startTime) 92 | ), 93 | r.a.createElement('td', null, e.name), 94 | r.a.createElement( 95 | 'td', 96 | null, 97 | Math.round(e.duration) 98 | ) 99 | ); 100 | }) 101 | ) 102 | ); 103 | }, 104 | }, 105 | ]), 106 | t 107 | ); 108 | })(r.a.Component)), 109 | h = (function(e) { 110 | function t(e) { 111 | var a; 112 | return ( 113 | Object(s.a)(this, t), 114 | ((a = Object(o.a)(this, Object(m.a)(t).call(this, e))).state = { 115 | clicked: !1, 116 | }), 117 | a 118 | ); 119 | } 120 | return ( 121 | Object(u.a)(t, e), 122 | Object(i.a)(t, [ 123 | { 124 | key: 'render', 125 | value: function() { 126 | var e = this; 127 | return r.a.createElement( 128 | 'div', 129 | { class: 'section' }, 130 | r.a.createElement( 131 | 'div', 132 | { 133 | class: 'cta-button', 134 | onClick: function() { 135 | return e.setState({ clicked: !0 }); 136 | }, 137 | }, 138 | 'Click to show image' 139 | ), 140 | this.state.clicked 141 | ? r.a.createElement('img', { 142 | src: 143 | 'https://assets.imgix.net/examples/kingfisher.jpg?w=200&rand=' + 144 | this.props.rand, 145 | alt: '', 146 | }) 147 | : null 148 | ); 149 | }, 150 | }, 151 | ]), 152 | t 153 | ); 154 | })(r.a.Component), 155 | f = a(9), 156 | p = (function(e) { 157 | function t(e) { 158 | var a; 159 | return ( 160 | Object(s.a)(this, t), 161 | ((a = Object(o.a)(this, Object(m.a)(t).call(this, e))).state = { 162 | clicked: !1, 163 | }), 164 | (a.imageUrl = 165 | 'https://assets.imgix.net/examples/kingfisher.jpg?w=200&rand=' + 166 | a.props.rand), 167 | a 168 | ); 169 | } 170 | return ( 171 | Object(u.a)(t, e), 172 | Object(i.a)(t, [ 173 | { 174 | key: 'render', 175 | value: function() { 176 | var e = this; 177 | return r.a.createElement( 178 | 'div', 179 | { class: 'section' }, 180 | r.a.createElement( 181 | f.a, 182 | { onRenderAssets: [this.imageUrl] }, 183 | r.a.createElement( 184 | 'div', 185 | { 186 | class: 'cta-button', 187 | onClick: function() { 188 | return e.setState({ clicked: !0 }); 189 | }, 190 | }, 191 | 'Click to show image' 192 | ) 193 | ), 194 | this.state.clicked 195 | ? r.a.createElement('img', { 196 | src: this.imageUrl, 197 | alt: '', 198 | }) 199 | : null 200 | ); 201 | }, 202 | }, 203 | ]), 204 | t 205 | ); 206 | })(r.a.Component), 207 | v = (function(e) { 208 | function t() { 209 | var e, a; 210 | Object(s.a)(this, t); 211 | for (var n = arguments.length, r = new Array(n), c = 0; c < n; c++) 212 | r[c] = arguments[c]; 213 | return ( 214 | ((a = Object(o.a)( 215 | this, 216 | (e = Object(m.a)(t)).call.apply(e, [this].concat(r)) 217 | )).noPrefetchRand = Math.random()), 218 | (a.onRenderPrefetchRand = Math.random()), 219 | a 220 | ); 221 | } 222 | return ( 223 | Object(u.a)(t, e), 224 | Object(i.a)(t, [ 225 | { 226 | key: 'render', 227 | value: function() { 228 | return r.a.createElement( 229 | 'div', 230 | { className: 'App' }, 231 | r.a.createElement( 232 | 'div', 233 | { className: 'header' }, 234 | r.a.createElement( 235 | 'span', 236 | { className: 'title' }, 237 | 'Component without any Prefetching' 238 | ), 239 | r.a.createElement( 240 | 'a', 241 | { 242 | className: 'code-link', 243 | href: 244 | 'https://github.com/manojVivek/react-prefetcher/blob/master/demo/src/components/NoPrefetchApp/index.js', 245 | target: '_blank', 246 | rel: 'noopener noreferrer', 247 | }, 248 | '(see code)' 249 | ) 250 | ), 251 | r.a.createElement( 252 | 'div', 253 | { className: 'wrapper' }, 254 | r.a.createElement( 255 | 'div', 256 | { className: 'left' }, 257 | r.a.createElement(h, { rand: this.noPrefetchRand }) 258 | ), 259 | r.a.createElement( 260 | 'div', 261 | { className: 'right' }, 262 | r.a.createElement(d, { filter: this.noPrefetchRand }) 263 | ) 264 | ), 265 | r.a.createElement( 266 | 'div', 267 | { className: 'header' }, 268 | r.a.createElement( 269 | 'span', 270 | { className: 'title' }, 271 | 'Component with OnRender Prefetching' 272 | ), 273 | r.a.createElement( 274 | 'a', 275 | { 276 | className: 'code-link', 277 | href: 278 | 'https://github.com/manojVivek/react-prefetcher/blob/master/demo/src/components/OnRenderPrefetchApp/index.js', 279 | target: '_blank', 280 | rel: 'noopener noreferrer', 281 | }, 282 | '(see code)' 283 | ) 284 | ), 285 | r.a.createElement( 286 | 'div', 287 | { className: 'wrapper' }, 288 | r.a.createElement( 289 | 'div', 290 | { className: 'left' }, 291 | r.a.createElement(p, { 292 | rand: this.onRenderPrefetchRand, 293 | }) 294 | ), 295 | r.a.createElement( 296 | 'div', 297 | { className: 'right' }, 298 | r.a.createElement(d, { 299 | filter: this.onRenderPrefetchRand, 300 | }) 301 | ) 302 | ) 303 | ); 304 | }, 305 | }, 306 | ]), 307 | t 308 | ); 309 | })(r.a.Component); 310 | Boolean( 311 | 'localhost' === window.location.hostname || 312 | '[::1]' === window.location.hostname || 313 | window.location.hostname.match( 314 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 315 | ) 316 | ); 317 | l.a.render(r.a.createElement(v, null), document.getElementById('root')), 318 | 'serviceWorker' in navigator && 319 | navigator.serviceWorker.ready.then(function(e) { 320 | e.unregister(); 321 | }); 322 | }, 323 | }, 324 | [[10, 1, 2]], 325 | ]); 326 | //# sourceMappingURL=main.066f72df.chunk.js.map 327 | -------------------------------------------------------------------------------- /docs/static/js/main.066f72df.chunk.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["components/NetworkRequestLister/index.js","components/NoPrefetchApp/index.js","components/OnRenderPrefetchApp/index.js","App.js","serviceWorker.js","index.js"],"names":["NetworkReqestLister","props","_this","Object","classCallCheck","this","possibleConstructorReturn","getPrototypeOf","call","_refreshList","setState","requests","performance","getEntriesByType","filter","entry","name","indexOf","state","intervalHandle","setInterval","clearInterval","react_default","a","createElement","className","map","request","Math","round","startTime","duration","React","Component","NoPrefetchApp","clicked","_this2","class","onClick","src","rand","alt","OnRenderPrefetchApp","imageUrl","react_prefetcher","onRenderAssets","App","noPrefetchRand","random","onRenderPrefetchRand","href","target","rel","NoPrefetchApp_NoPrefetchApp","NetworkRequestLister_NetworkReqestLister","OnRenderPrefetchApp_OnRenderPrefetchApp","Boolean","window","location","hostname","match","ReactDOM","render","src_App_0","document","getElementById","navigator","serviceWorker","ready","then","registration","unregister"],"mappings":"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","file":"static/js/main.066f72df.chunk.js","sourcesContent":["import React from 'react';\n\nexport default class NetworkReqestLister extends React.Component {\n constructor(props) {\n super(props);\n this.state = { requests: [] };\n }\n\n componentDidMount() {\n this.intervalHandle = setInterval(this._refreshList, 200);\n }\n\n componentWillUnmount() {\n clearInterval(this.intervalHandle);\n }\n\n _refreshList = () => {\n this.setState({\n requests: performance\n .getEntriesByType('resource')\n .filter(entry => entry.name.indexOf(this.props.filter) > -1),\n });\n };\n\n render() {\n return (\n
\n

Network Inspector:

\n \n \n \n \n \n \n {this.state.requests.map(request => (\n \n \n \n \n \n ))}\n
Time from Page load(ms)Resource URLTime Taken to Download(ms)
{Math.round(request.startTime)}{request.name}{Math.round(request.duration)}
\n
\n );\n }\n}\n","import React from 'react';\n\nexport default class NoPrefetchApp extends React.Component {\n constructor(props) {\n super(props);\n this.state = { clicked: false };\n }\n\n render() {\n return (\n
\n this.setState({ clicked: true })}\n >\n Click to show image\n
\n {this.state.clicked ? (\n \n ) : null}\n \n );\n }\n}\n","import React from 'react';\nimport Prefetcher from 'react-prefetcher';\n\nexport default class OnRenderPrefetchApp extends React.Component {\n constructor(props) {\n super(props);\n this.state = { clicked: false };\n this.imageUrl =\n 'https://assets.imgix.net/examples/kingfisher.jpg?w=200&rand=' +\n this.props.rand;\n }\n\n render() {\n return (\n
\n \n this.setState({ clicked: true })}\n >\n Click to show image\n
\n
\n {this.state.clicked ? \"\" : null}\n \n );\n }\n}\n","import React from 'react';\nimport './App.css';\nimport NetworkRequestLister from './components/NetworkRequestLister';\nimport NoPrefetchApp from './components/NoPrefetchApp';\nimport OnRenderPrefetchApp from './components/OnRenderPrefetchApp';\n\nclass App extends React.Component {\n noPrefetchRand = Math.random();\n onRenderPrefetchRand = Math.random();\n\n render() {\n return (\n
\n
\n Component without any Prefetching\n \n (see code)\n \n
\n
\n
\n \n
\n
\n \n
\n
\n
\n Component with OnRender Prefetching\n \n (see code)\n \n
\n
\n
\n \n
\n
\n \n
\n
\n
\n );\n }\n}\n\nexport default App;\n","// This optional code is used to register a service worker.\n// register() is not called by default.\n\n// This lets the app load faster on subsequent visits in production, and gives\n// it offline capabilities. However, it also means that developers (and users)\n// will only see deployed updates on subsequent visits to a page, after all the\n// existing tabs open on the page have been closed, since previously cached\n// resources are updated in the background.\n\n// To learn more about the benefits of this model and instructions on how to\n// opt-in, read https://bit.ly/CRA-PWA\n\nconst isLocalhost = Boolean(\n window.location.hostname === 'localhost' ||\n // [::1] is the IPv6 localhost address.\n window.location.hostname === '[::1]' ||\n // 127.0.0.1/8 is considered localhost for IPv4.\n window.location.hostname.match(\n /^127(?:\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/\n )\n);\n\nexport function register(config) {\n if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {\n // The URL constructor is available in all browsers that support SW.\n const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);\n if (publicUrl.origin !== window.location.origin) {\n // Our service worker won't work if PUBLIC_URL is on a different origin\n // from what our page is served on. This might happen if a CDN is used to\n // serve assets; see https://github.com/facebook/create-react-app/issues/2374\n return;\n }\n\n window.addEventListener('load', () => {\n const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;\n\n if (isLocalhost) {\n // This is running on localhost. Let's check if a service worker still exists or not.\n checkValidServiceWorker(swUrl, config);\n\n // Add some additional logging to localhost, pointing developers to the\n // service worker/PWA documentation.\n navigator.serviceWorker.ready.then(() => {\n console.log(\n 'This web app is being served cache-first by a service ' +\n 'worker. To learn more, visit https://bit.ly/CRA-PWA'\n );\n });\n } else {\n // Is not localhost. Just register service worker\n registerValidSW(swUrl, config);\n }\n });\n }\n}\n\nfunction registerValidSW(swUrl, config) {\n navigator.serviceWorker\n .register(swUrl)\n .then(registration => {\n registration.onupdatefound = () => {\n const installingWorker = registration.installing;\n if (installingWorker == null) {\n return;\n }\n installingWorker.onstatechange = () => {\n if (installingWorker.state === 'installed') {\n if (navigator.serviceWorker.controller) {\n // At this point, the updated precached content has been fetched,\n // but the previous service worker will still serve the older\n // content until all client tabs are closed.\n console.log(\n 'New content is available and will be used when all ' +\n 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'\n );\n\n // Execute callback\n if (config && config.onUpdate) {\n config.onUpdate(registration);\n }\n } else {\n // At this point, everything has been precached.\n // It's the perfect time to display a\n // \"Content is cached for offline use.\" message.\n console.log('Content is cached for offline use.');\n\n // Execute callback\n if (config && config.onSuccess) {\n config.onSuccess(registration);\n }\n }\n }\n };\n };\n })\n .catch(error => {\n console.error('Error during service worker registration:', error);\n });\n}\n\nfunction checkValidServiceWorker(swUrl, config) {\n // Check if the service worker can be found. If it can't reload the page.\n fetch(swUrl)\n .then(response => {\n // Ensure service worker exists, and that we really are getting a JS file.\n const contentType = response.headers.get('content-type');\n if (\n response.status === 404 ||\n (contentType != null && contentType.indexOf('javascript') === -1)\n ) {\n // No service worker found. Probably a different app. Reload the page.\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister().then(() => {\n window.location.reload();\n });\n });\n } else {\n // Service worker found. Proceed as normal.\n registerValidSW(swUrl, config);\n }\n })\n .catch(() => {\n console.log(\n 'No internet connection found. App is running in offline mode.'\n );\n });\n}\n\nexport function unregister() {\n if ('serviceWorker' in navigator) {\n navigator.serviceWorker.ready.then(registration => {\n registration.unregister();\n });\n }\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\n\nReactDOM.render(, document.getElementById('root'));\n\n// If you want your app to work offline and load faster, you can change\n// unregister() to register() below. Note this comes with some pitfalls.\n// Learn more about service workers: https://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"],"sourceRoot":""} --------------------------------------------------------------------------------