[
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# production\n/build\n\n# misc\n.DS_Store\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Temporary env files\n/public/env-config.js\nenv-config.js"
  },
  {
    "path": "Dockerfile",
    "content": "# => Build container\nFROM node:alpine as builder\nWORKDIR /app\nCOPY package.json .\nCOPY yarn.lock .\nRUN yarn\nCOPY . .\nRUN yarn build\n\n# => Run container\nFROM nginx:1.15.2-alpine\n\n# Nginx config\nRUN rm -rf /etc/nginx/conf.d\nCOPY conf /etc/nginx\n\n# Static build\nCOPY --from=builder /app/build /usr/share/nginx/html/\n\n# Default port exposure\nEXPOSE 80\n\n# Copy .env file and shell script to container\nWORKDIR /usr/share/nginx/html\nCOPY ./env.sh .\nCOPY .env .\n\n# Make our shell script executable\nRUN chmod +x env.sh\n\n# Start Nginx server\nCMD [\"/bin/sh\", \"-c\", \"/usr/share/nginx/html/env.sh && nginx -g \\\"daemon off;\\\"\"]"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) Facebook, Inc. and its affiliates.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Runtime Environment Variables with Create React App, Docker (and Nginx)\n\nThis repository shows how to implement **runtime** environment variables. Unlike traditional solutions, this allows you to configure your React application via environment variables without need to build once again.\n\nThis repository is explained deeply within Medium blog post:\n\nhttps://medium.com/free-code-camp/how-to-implement-runtime-environment-variables-with-create-react-app-docker-and-nginx-7f9d42a91d70\n\n---\n\nThere are many ways to configure your React application, in this post I will aim\nto show you approach which respects [Twelve-Factor App\nmethodology](https://en.wikipedia.org/wiki/Twelve-Factor_App_methodology),\nmeaning that it enforces reconfiguration during runtime, therefore no build per\nenvironment would be required.\n\n![](https://cdn-images-1.medium.com/max/1600/0*X2czIkbrJQuKpgM5.jpeg)\n\n### 🤔 What do we want to achieve?\n\nWe want to be able to run our React application as Docker container that is\nbuilt once and runs everywhere. We want to reconfigure our container **during\nruntime. **The output should be lightweight and performant container which\nserves our React application as static content, which we achieve by using Ngnix\nAlpine. Our application should allow configuration within docker-compose file\nsuch as this:\n\n```\nversion: \"3.2\"\nservices:\n  my-react-app:\n    image: my-react-app\n    ports:\n      - \"3000:80\"\n    environment:\n      - \"API_URL=production.example.com\"\n```\n\nWe should be able configure our React application using ` -e`` flag (environment variables) when using `Docker run` command.\n\n> Basic users might not need this approach and can be satisfied with buildtime\n> configuration which is easier to reason about on the short run, but if you are\n> targeting dynamic environments that might change or you are using some kind of\n> orchestration system, this approach is something that you might consider.\n\n### 🧐 The problem\n\nFirst of all, it must be clear that there is no such thing as environment\nvariables inside browser environment. Whichever solution we use nowadays, is\nnothing but a fake abstraction. But, then you might ask, what about `.env` files\nand `REACT_APP` prefixed environment variables which come [straight from\ndocumentation](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables)?\nEven inside source code these are used as `process.env` just like we use\nenvironment variables inside Node.js.\n\nIn reality, object `process` does not exist inside browser environment, it's\nNode specific. CRA by default doesn't do server-side rendering, so it can't\ninject environment variables during content serving (like\n[Next.js](https://github.com/zeit/next.js) does), it doesn't include server as\nsuch, so in this case, ** during transpiling**, Webpack process replaces all\noccurrences of `process.env` with string value that was given. This means **it\ncan only be configured during build time**.\n\n### 👌 Solution\n\nThere is specific moment when it is still possible to inject environment\nvariables, it happens when we start our container. Then we can read environment\nvariables from inside container and write them into file which can be served via\nNginx (which also serves our React app) and imported into our application using\n`<script>` tag inside head section of `index.html`. So at that moment we run\nbash script which creates JavaScript file with environment variables assigned as\nproperties of the global `window`object. Injected to be globally available\nwithin our application the browser way.\n\n### 🐢 Step by step guide\n\nLet's start with simple `create-react-app` project and create `.env` file with our first\nenvironment variable that we want to expose.\n\n```\n# Generate React App\ncreate-react-app cra-runtime-environment-variables\ncd cra-runtime-environment-variables\n\n# Create default environment variables that we want to use\ntouch .env\necho \"API_URL=https//default.dev.api.com\" >> .env\n```\n\nThen let's write a small bash script which will read`.env` file and extract\nenvironment variables that will be written into file. If you set environment\nvariable inside the container, its value will be used, otherwise it will\nfallback to default value from .env file. It will create JavaScript file which\nputs environment variable values as object which is assigned as property of\n`window` object.\n\n```\n#!/bin/sh\n# line endings must be \\n, not \\r\\n !\necho \"window._env_ = {\" > ./env-config.js\nawk -F '=' '{ print $1 \": \\\"\" (ENVIRON[$1] ? ENVIRON[$1] : $2) \"\\\",\" }' ./.env >> ./env-config.js\necho \"}\" >> ./env-config.js\n```\n\n> env.sh\n\nWe need to add following line to `<head>` element inside `index.html` which then\nimports file created by our bash script.\n\n```\n<script src=\"%PUBLIC_URL%/env-config.js\"></script>\n```\n\n> index.html\n\nLet's display our environment variable within application:\n\n```\n<p>API_URL: {window._env_.API_URL}</p>\n```\n\n> src/App\n\n#### 🛠 Development\n\nDuring development, if we don't want to use Docker, we can run bash script via\nnpm script runner by modifying package.json:\n\n```\n  \"scripts\": {\n    \"dev\": \"chmod +x ./env.sh && ./env.sh && cp env-config.js ./public/ && react-scripts start\",\n    \"test\": \"react-scripts test\",\n    \"eject\": \"react-scripts eject\",\n    \"build\": \"react-scripts build'\"\n  },\n```\n\n> package.json\n\nAnd if we run `yarn dev` we should see output like this:\n\n![](https://cdn-images-1.medium.com/max/1600/1*e4ugnbph1YnN3uVbH2QNZA.png)\n\nThere are two ways to reconfigure environment variables within development;\neither change default value inside `.env` file or override defaults by running\n`yarn dev`command with environment variables prepended:\n\n```\nAPI_URL=https://my.new.dev.api.com yarn dev\n```\n\n![](https://cdn-images-1.medium.com/max/1600/1*MHnRJn_JkV33mmK6Yh1raw.png)\n\nAnd finally edit `.gitignore` so that we exclude environment configurations out\nof source code:\n\n```\n# Temporary env files\n/public/env-config.js\nenv-config.js\n```\n\nAs for development environment, that's it! We are half-way there. However, We\ndidn't make a huge difference at this point compared to what CRA offered by\ndefault for development environment, however, the true potential of this\napproach shines in production.\n\n#### 🌎 Production\n\nNow we are going to create minimal Nginx configuration so that we can build\noptimized image which serves production-ready application.\n\n```\n# Create directory for Ngnix configuration\nmkdir -p conf/conf.d\ntouch conf/conf.d/default.conf conf/conf.d/gzip.conf\n```\n\nMain configuration file should look somewhat like this:\n\n```\nserver {\n  listen 80;\n  location / {\n    root   /usr/share/nginx/html;\n    index  index.html index.htm;\n    try_files $uri $uri/ /index.html;\n    expires -1; # Set it to different value depending on your standard requirements\n  }\n  error_page   500 502 503 504  /50x.html;\n  location = /50x.html {\n    root   /usr/share/nginx/html;\n  }\n}\n```\n\n> conf/conf.d/default.conf\n\nIt's also useful to enable gzip compression so that our assets are more\nlightweight during network transition:\n\n```\ngzip on;\ngzip_http_version  1.0;\ngzip_comp_level    5; # 1-9\ngzip_min_length    256;\ngzip_proxied       any;\ngzip_vary          on;\n\n# MIME-types\ngzip_types\n  application/atom+xml\n  application/javascript\n  application/json\n  application/rss+xml\n  application/vnd.ms-fontobject\n  application/x-font-ttf\n  application/x-web-app-manifest+json\n  application/xhtml+xml\n  application/xml\n  font/opentype\n  image/svg+xml\n  image/x-icon\n  text/css\n  text/plain\n  text/x-component;\n```\n\n> conf/conf.d/gzip.conf\n\nNow that our Nginx configuration is ready, we can finally create Dockerfile and\ndocker-compose files:\n\n```\ntouch Dockerfile docker-compose.yml\n```\n\nInitially, we use `node:alpine` image to create optimized production build of\nour application. Then, we build runtime image on top of `nginx:alpine` .\n\n```\n# => Build container\nFROM node:alpine as builder\nWORKDIR /app\nCOPY package.json .\nCOPY yarn.lock .\nRUN yarn\nCOPY . .\nRUN yarn build\n\n# => Run container\nFROM nginx:1.15.2-alpine\n\n# Nginx config\nRUN rm -rf /etc/nginx/conf.d\nCOPY conf /etc/nginx\n\n# Static build\nCOPY --from=builder /app/build /usr/share/nginx/html/\n\n# Default port exposure\nEXPOSE 80\n\n# Copy .env file and shell script to container\nWORKDIR /usr/share/nginx/html\nCOPY ./env.sh .\nCOPY .env .\n\n# Make our shell script executable\nRUN chmod +x env.sh\n\n# Start Nginx server\nCMD [\"/bin/sh\", \"-c\", \"/usr/share/nginx/html/env.sh && nginx -g \\\"daemon off;\\\"\"]\n```\n\nNow our container is ready. We can do all standard stuff with it. We can build\ncontainer, run it with inline configurations and push it to repository provided\nby services such as [Dockerhub](https://hub.docker.com/).\n\n```\ndocker build . -t kunokdev/cra-runtime-environment-variables\ndocker run -p 3000:80 -e API_URL=https://staging.api.com -t kunokdev/cra-runtime-environment-variables\ndocker push -t kunokdev/cra-runtime-environment-variables\n```\n\nAbove `docker run` command should output application like so:\n\n![](https://cdn-images-1.medium.com/max/1600/1*kK7Ss5ODlukXgsLNuYh0Lg.png)\n\nLastly, let's create our docker-compose file. You will usually have different\ndocker-compose files depending on environment and you will use `-f` flag to\nselect which file to use.\n\n```\nversion: \"3.2\"\nservices:\n  cra-runtime-environment-variables:\n    image: kunokdev/cra-runtime-environment-variables\n    ports:\n      - \"5000:80\"\n    environment:\n      - \"API_URL=production.example.com\"\n```\n\nand if we do `docker-compose up` we should see output like so:\n\n![](https://cdn-images-1.medium.com/max/1600/1*7TBDwzS_otshjMhQqvycmg.png)\n\nGreat! We have now achieved our goal, we can reconfigure our application easily\nin both development and production environments in a very convenient way. We can\nnow finally build only once and run everywhere!\n\n#### 💅 Next steps\n\nCurrent implementation of shell script will print all variables included within\n.env file, but most of the time we don't really want to expose all of them. You\ncould implement filters for variables you don't want to expose using prefixes or\nsimilar technique.\n\n#### 🧩 TypeScript\n\nYou may run into [Type errors as mentioned in this issue](https://github.com/kunokdev/cra-runtime-environment-variables/issues/12). To solve this, extend `window` object or rewrite global window type as suggested in issue comments.\n\n#### 🐓 Alternative solutions\n\nAs noted above, buildtime configuration will satisfy most use cases and you can\nrely on default approach using .env file per environment and build container for\neach environment, inject values via CRA Webpack provided environment variables.\n\nYou could also have a look at [CRA Github repository\nissue](https://github.com/facebook/create-react-app/issues/2353) which covers\nthis problem. By now, there should be more posts and issues which cover this topic and each offers similar solution as above, it's up to you to decide how are you going to implement specific details, you as well might use Node.js to serve your application which means that you can also replace shells script with Node.js script, but note that Nginx is more convenient to serve static content.\n"
  },
  {
    "path": "conf/conf.d/default.conf",
    "content": "server {\n  listen 80;\n  add_header Cache-Control no-cache;\n  location / {\n    root   /usr/share/nginx/html;\n    index  index.html index.htm;\n    try_files $uri $uri/ /index.html;\n    expires -1;\n  }\n  error_page   500 502 503 504  /50x.html;\n  location = /50x.html {\n    root   /usr/share/nginx/html;\n  }\n}\n"
  },
  {
    "path": "conf/conf.d/gzip.conf",
    "content": "# Enable Gzip compressed.\n  gzip on;\n\n  # Enable compression both for HTTP/1.0 and HTTP/1.1 (required for CloudFront).\n  gzip_http_version  1.0;\n\n  # Compression level (1-9).\n  # 5 is a perfect compromise between size and cpu usage, offering about\n  # 75% reduction for most ascii files (almost identical to level 9).\n  gzip_comp_level    5;\n\n  # Don't compress anything that's already small and unlikely to shrink much\n  # if at all (the default is 20 bytes, which is bad as that usually leads to\n  # larger files after gzipping).\n  gzip_min_length    256;\n\n  # Compress data even for clients that are connecting to us via proxies,\n  # identified by the \"Via\" header (required for CloudFront).\n  gzip_proxied       any;\n\n  # Tell proxies to cache both the gzipped and regular version of a resource\n  # whenever the client's Accept-Encoding capabilities header varies;\n  # Avoids the issue where a non-gzip capable client (which is extremely rare\n  # today) would display gibberish if their proxy gave them the gzipped version.\n  gzip_vary          on;\n\n  # Compress all output labeled with one of the following MIME-types.\n  gzip_types\n    application/atom+xml\n    application/javascript\n    application/json\n    application/rss+xml\n    application/vnd.ms-fontobject\n    application/x-font-ttf\n    application/x-web-app-manifest+json\n    application/xhtml+xml\n    application/xml\n    font/opentype\n    image/svg+xml\n    image/x-icon\n    text/css\n    text/plain\n    text/x-component;\n  # text/html is always compressed by HttpGzipModule"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: \"3.2\"\nservices:\n  cra-runtime-environment-variables:\n    image: kunokdev/cra-runtime-environment-variables\n    ports:\n      - \"5000:80\"\n    environment:\n      - \"API_URL=production.example.com\"\n"
  },
  {
    "path": "env.sh",
    "content": "#!/bin/sh\n# line endings must be \\n, not \\r\\n !\necho \"window._env_ = {\" > ./env-config.js\nawk -F '=' '{ print $1 \": \\\"\" (ENVIRON[$1] ? ENVIRON[$1] : $2) \"\\\",\" }' ./.env >> ./env-config.js\necho \"}\" >> ./env-config.js"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"cra-runtime-environment-variables\",\n  \"version\": \"0.1.0\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"react\": \"^16.6.3\",\n    \"react-dom\": \"^16.6.3\",\n    \"react-scripts\": \"2.1.1\"\n  },\n  \"scripts\": {\n    \"dev\": \"chmod +x ./env.sh && ./env.sh && cp env-config.js ./public/ && react-scripts start\",\n    \"test\": \"react-scripts test\",\n    \"eject\": \"react-scripts eject\",\n    \"build\": \"sh -ac '. ./.env; react-scripts build'\"\n  },\n  \"eslintConfig\": {\n    \"extends\": \"react-app\"\n  },\n  \"browserslist\": [\n    \">0.2%\",\n    \"not dead\",\n    \"not ie <= 11\",\n    \"not op_mini all\"\n  ]\n}\n"
  },
  {
    "path": "public/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <link rel=\"shortcut icon\" href=\"%PUBLIC_URL%/favicon.ico\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n    <meta name=\"theme-color\" content=\"#000000\">\n    <!--\n      manifest.json provides metadata used when your web app is added to the\n      homescreen on Android. See https://developers.google.com/web/fundamentals/web-app-manifest/\n    -->\n    <link rel=\"manifest\" href=\"%PUBLIC_URL%/manifest.json\">\n    <!--\n      Notice the use of %PUBLIC_URL% in the tags above.\n      It will be replaced with the URL of the `public` folder during the build.\n      Only files inside the `public` folder can be referenced from the HTML.\n\n      Unlike \"/favicon.ico\" or \"favicon.ico\", \"%PUBLIC_URL%/favicon.ico\" will\n      work correctly both with client-side routing and a non-root public URL.\n      Learn how to configure a non-root public URL by running `npm run build`.\n    -->\n    <title>React App</title>\n    <script src=\"%PUBLIC_URL%/env-config.js\"></script>\n  </head>\n  <body>\n    <noscript>\n      You need to enable JavaScript to run this app.\n    </noscript>\n    <div id=\"root\"></div>\n    <!--\n      This HTML file is a template.\n      If you open it directly in the browser, you will see an empty page.\n\n      You can add webfonts, meta tags, or analytics to this file.\n      The build step will place the bundled scripts into the <body> tag.\n\n      To begin the development, run `npm start` or `yarn start`.\n      To create a production bundle, use `npm run build` or `yarn build`.\n    -->\n  </body>\n</html>\n"
  },
  {
    "path": "public/manifest.json",
    "content": "{\n  \"short_name\": \"React App\",\n  \"name\": \"Create React App Sample\",\n  \"icons\": [\n    {\n      \"src\": \"favicon.ico\",\n      \"sizes\": \"64x64 32x32 24x24 16x16\",\n      \"type\": \"image/x-icon\"\n    }\n  ],\n  \"start_url\": \".\",\n  \"display\": \"standalone\",\n  \"theme_color\": \"#000000\",\n  \"background_color\": \"#ffffff\"\n}\n"
  },
  {
    "path": "src/App.css",
    "content": ".App {\n  text-align: center;\n}\n\n.App-logo {\n  animation: App-logo-spin infinite 20s linear;\n  height: 40vmin;\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"
  },
  {
    "path": "src/App.js",
    "content": "import React, { Component } from \"react\";\nimport logo from \"./logo.svg\";\nimport \"./App.css\";\n\nclass App extends Component {\n  render() {\n    return (\n      <div className=\"App\">\n        <header className=\"App-header\">\n          <img src={logo} className=\"App-logo\" alt=\"logo\" />\n          <p>API_URL: {window._env_.API_URL}</p>\n        </header>\n      </div>\n    );\n  }\n}\n\nexport default App;\n"
  },
  {
    "path": "src/App.test.js",
    "content": "import React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport App from \"./App\";\n\nconst window = global;\nwindow._env_ = {\n  API_URL: \"https://github.com\"\n};\n\nit(\"renders without crashing\", () => {\n  const div = document.createElement(\"div\");\n  ReactDOM.render(<App />, div);\n  ReactDOM.unmountComponentAtNode(div);\n});\n"
  },
  {
    "path": "src/index.css",
    "content": "body {\n  margin: 0;\n  padding: 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"
  },
  {
    "path": "src/index.js",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport * as serviceWorker from './serviceWorker';\n\nReactDOM.render(<App />, 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: http://bit.ly/CRA-PWA\nserviceWorker.unregister();\n"
  },
  {
    "path": "src/serviceWorker.js",
    "content": "// 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 http://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 http://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 http://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"
  }
]