From bc6392afafdb544cbfed76e622948f46c158432b Mon Sep 17 00:00:00 2001
From: Dan Abramov <dan.abramov@gmail.com>
Date: Thu, 22 Sep 2016 21:25:34 +0100
Subject: [PATCH] Add support for public/ folder (#703)

---
 packages/react-scripts/config/env.js          |  26 --------
 packages/react-scripts/config/paths.js        |   9 ++-
 .../config/webpack.config.dev.js              |  44 ++++++------
 .../config/webpack.config.prod.js             |  63 ++++++++++--------
 packages/react-scripts/package.json           |   1 -
 packages/react-scripts/scripts/build.js       |  12 +++-
 packages/react-scripts/scripts/eject.js       |   2 +
 packages/react-scripts/scripts/start.js       |  32 +++++----
 packages/react-scripts/scripts/test.js        |   1 +
 .../scripts/utils/InterpolateHtmlPlugin.js    |  43 ++++++++++++
 .../scripts/utils/getClientEnvironment.js     |  38 +++++++++++
 packages/react-scripts/template/index.html    |  22 ------
 .../template/{src => public}/favicon.ico      | Bin
 .../react-scripts/template/public/index.html  |  31 +++++++++
 14 files changed, 202 insertions(+), 122 deletions(-)
 delete mode 100644 packages/react-scripts/config/env.js
 create mode 100644 packages/react-scripts/scripts/utils/InterpolateHtmlPlugin.js
 create mode 100644 packages/react-scripts/scripts/utils/getClientEnvironment.js
 delete mode 100644 packages/react-scripts/template/index.html
 rename packages/react-scripts/template/{src => public}/favicon.ico (100%)
 create mode 100644 packages/react-scripts/template/public/index.html

diff --git a/packages/react-scripts/config/env.js b/packages/react-scripts/config/env.js
deleted file mode 100644
index 66acf119b..000000000
--- a/packages/react-scripts/config/env.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// @remove-on-eject-begin
-/**
- * Copyright (c) 2015-present, Facebook, Inc.
- * All rights reserved.
- *
- * This source code is licensed under the BSD-style license found in the
- * LICENSE file in the root directory of this source tree. An additional grant
- * of patent rights can be found in the PATENTS file in the same directory.
- */
-// @remove-on-eject-end
-
-// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
-// injected into the application via DefinePlugin in Webpack configuration.
-
-var REACT_APP = /^REACT_APP_/i;
-var NODE_ENV = JSON.stringify(process.env.NODE_ENV || 'development');
-
-module.exports = Object
-  .keys(process.env)
-  .filter(key => REACT_APP.test(key))
-  .reduce((env, key) => {
-    env['process.env.' + key] = JSON.stringify(process.env[key]);
-    return env;
-  }, {
-    'process.env.NODE_ENV': NODE_ENV
-  });
diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js
index 314bcc205..1d50c38b9 100644
--- a/packages/react-scripts/config/paths.js
+++ b/packages/react-scripts/config/paths.js
@@ -38,7 +38,8 @@ var nodePaths = (process.env.NODE_PATH || '')
 // config after eject: we're in ./config/
 module.exports = {
   appBuild: resolveApp('build'),
-  appHtml: resolveApp('index.html'),
+  appPublic: resolveApp('public'),
+  appHtml: resolveApp('public/index.html'),
   appIndexJs: resolveApp('src/index.js'),
   appPackageJson: resolveApp('package.json'),
   appSrc: resolveApp('src'),
@@ -56,7 +57,8 @@ function resolveOwn(relativePath) {
 // config before eject: we're in ./node_modules/react-scripts/config/
 module.exports = {
   appBuild: resolveApp('build'),
-  appHtml: resolveApp('index.html'),
+  appPublic: resolveApp('public'),
+  appHtml: resolveApp('public/index.html'),
   appIndexJs: resolveApp('src/index.js'),
   appPackageJson: resolveApp('package.json'),
   appSrc: resolveApp('src'),
@@ -71,7 +73,8 @@ module.exports = {
 // @remove-on-publish-begin
 module.exports = {
   appBuild: resolveOwn('../../../build'),
-  appHtml: resolveOwn('../template/index.html'),
+  appPublic: resolveOwn('../template/public'),
+  appHtml: resolveOwn('../template/public/index.html'),
   appIndexJs: resolveOwn('../template/src/index.js'),
   appPackageJson: resolveOwn('../package.json'),
   appSrc: resolveOwn('../template/src'),
diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js
index 8d1de1c32..f08fcdcb7 100644
--- a/packages/react-scripts/config/webpack.config.dev.js
+++ b/packages/react-scripts/config/webpack.config.dev.js
@@ -14,9 +14,20 @@ var autoprefixer = require('autoprefixer');
 var webpack = require('webpack');
 var HtmlWebpackPlugin = require('html-webpack-plugin');
 var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
+var InterpolateHtmlPlugin = require('../scripts/utils/InterpolateHtmlPlugin');
 var WatchMissingNodeModulesPlugin = require('../scripts/utils/WatchMissingNodeModulesPlugin');
+var getClientEnvironment = require('../scripts/utils/getClientEnvironment');
 var paths = require('./paths');
-var env = require('./env');
+
+// Webpack uses `publicPath` to determine where the app is being served from.
+// In development, we always serve from the root. This makes config easier.
+var publicPath = '/';
+// `publicUrl` is just like `publicPath`, but we will provide it to our app
+// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
+// Omit trailing shlash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
+var publicUrl = '';
+// Get enrivonment variables to inject into our app.
+var env = getClientEnvironment(publicUrl);
 
 // This is the development configuration.
 // It is focused on developer experience and fast rebuilds.
@@ -63,8 +74,8 @@ module.exports = {
     // served by WebpackDevServer in development. This is the JS bundle
     // containing code from all our entry points, and the Webpack runtime.
     filename: 'static/js/bundle.js',
-    // In development, we always serve from the root. This makes config easier.
-    publicPath: '/'
+    // This is the URL that app is served from. We use "/" in development.
+    publicPath: publicPath
   },
   resolve: {
     // This allows you to set a fallback for where Webpack should look for modules.
@@ -129,21 +140,11 @@ module.exports = {
       // In production, they would get copied to the `build` folder.
       {
         test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
-        exclude: /\/favicon.ico$/,
         loader: 'file',
         query: {
           name: 'static/media/[name].[hash:8].[ext]'
         }
       },
-      // A special case for favicon.ico to place it into build root directory.
-      {
-        test: /\/favicon.ico$/,
-        include: [paths.appSrc],
-        loader: 'file',
-        query: {
-          name: 'favicon.ico?[hash:8]'
-        }
-      },
       // "url" loader works just like "file" loader but it also embeds
       // assets smaller than specified size as data URLs to avoid requests.
       {
@@ -153,15 +154,6 @@ module.exports = {
           limit: 10000,
           name: 'static/media/[name].[hash:8].[ext]'
         }
-      },
-      // "html" loader is used to process template page (index.html) to resolve
-      // resources linked with <link href="./relative/path"> HTML tags.
-      {
-        test: /\.html$/,
-        loader: 'html',
-        query: {
-          attrs: ['link:href'],
-        }
       }
     ]
   },
@@ -186,13 +178,19 @@ module.exports = {
     ];
   },
   plugins: [
+    // Makes the public URL available as %PUBLIC_URL% in index.html, e.g.:
+    // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
+    // In development, this will be an empty string.
+    new InterpolateHtmlPlugin({
+      PUBLIC_URL: publicUrl
+    }),
     // Generates an `index.html` file with the <script> injected.
     new HtmlWebpackPlugin({
       inject: true,
       template: paths.appHtml,
     }),
     // Makes some environment variables available to the JS code, for example:
-    // if (process.env.NODE_ENV === 'development') { ... }. See `env.js`.
+    // if (process.env.NODE_ENV === 'development') { ... }.
     new webpack.DefinePlugin(env),
     // This is necessary to emit hot updates (currently CSS only):
     new webpack.HotModuleReplacementPlugin(),
diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js
index 5790e8e95..3db7831be 100644
--- a/packages/react-scripts/config/webpack.config.prod.js
+++ b/packages/react-scripts/config/webpack.config.prod.js
@@ -16,12 +16,18 @@ var HtmlWebpackPlugin = require('html-webpack-plugin');
 var ExtractTextPlugin = require('extract-text-webpack-plugin');
 var url = require('url');
 var paths = require('./paths');
-var env = require('./env');
+var InterpolateHtmlPlugin = require('../scripts/utils/InterpolateHtmlPlugin');
+var getClientEnvironment = require('../scripts/utils/getClientEnvironment');
 
-// Assert this just to be safe.
-// Development builds of React are slow and not intended for production.
-if (env['process.env.NODE_ENV'] !== '"production"') {
-  throw new Error('Production builds must have NODE_ENV=production.');
+function ensureSlash(path, needsSlash) {
+  var hasSlash = path.endsWith('/');
+  if (hasSlash && !needsSlash) {
+    return path.substr(path, path.length - 1);
+  } else if (!hasSlash && needsSlash) {
+    return path + '/';
+  } else {
+    return path;
+  }
 }
 
 // We use "homepage" field to infer "public path" at which the app is served.
@@ -30,10 +36,21 @@ if (env['process.env.NODE_ENV'] !== '"production"') {
 // We can't use a relative path in HTML because we don't want to load something
 // like /todos/42/static/js/bundle.7289d.js. We have to know the root.
 var homepagePath = require(paths.appPackageJson).homepage;
-var publicPath = homepagePath ? url.parse(homepagePath).pathname : '/';
-if (!publicPath.endsWith('/')) {
-  // If we don't do this, file assets will get incorrect paths.
-  publicPath += '/';
+var homepagePathname = homepagePath ? url.parse(homepagePath).pathname : '/';
+// Webpack uses `publicPath` to determine where the app is being served from.
+// It requires a trailing slash, or the file assets will get an incorrect path.
+var publicPath = ensureSlash(homepagePathname, true);
+// `publicUrl` is just like `publicPath`, but we will provide it to our app
+// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
+// Omit trailing shlash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz.
+var publicUrl = ensureSlash(homepagePathname, false);
+// Get enrivonment variables to inject into our app.
+var env = getClientEnvironment(publicUrl);
+
+// Assert this just to be safe.
+// Development builds of React are slow and not intended for production.
+if (env['process.env.NODE_ENV'] !== '"production"') {
+  throw new Error('Production builds must have NODE_ENV=production.');
 }
 
 // This is the production configuration.
@@ -139,21 +156,11 @@ module.exports = {
       // When you `import` an asset, you get its filename.
       {
         test: /\.(ico|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
-        exclude: /\/favicon.ico$/,
         loader: 'file',
         query: {
           name: 'static/media/[name].[hash:8].[ext]'
         }
       },
-      // A special case for favicon.ico to place it into build root directory.
-      {
-        test: /\/favicon.ico$/,
-        include: [paths.appSrc],
-        loader: 'file',
-        query: {
-          name: 'favicon.ico?[hash:8]'
-        }
-      },
       // "url" loader works just like "file" loader but it also embeds
       // assets smaller than specified size as data URLs to avoid requests.
       {
@@ -163,15 +170,6 @@ module.exports = {
           limit: 10000,
           name: 'static/media/[name].[hash:8].[ext]'
         }
-      },
-      // "html" loader is used to process template page (index.html) to resolve
-      // resources linked with <link href="./relative/path"> HTML tags.
-      {
-        test: /\.html$/,
-        loader: 'html',
-        query: {
-          attrs: ['link:href'],
-        }
       }
     ]
   },
@@ -198,6 +196,13 @@ module.exports = {
     ];
   },
   plugins: [
+    // Makes the public URL available as %PUBLIC_URL% in index.html, e.g.:
+    // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
+    // In production, it will be an empty string unless you specify "homepage"
+    // in `package.json`, in which case it will be the pathname of that URL.
+    new InterpolateHtmlPlugin({
+      PUBLIC_URL: publicUrl
+    }),
     // Generates an `index.html` file with the <script> injected.
     new HtmlWebpackPlugin({
       inject: true,
@@ -216,7 +221,7 @@ module.exports = {
       }
     }),
     // Makes some environment variables available to the JS code, for example:
-    // if (process.env.NODE_ENV === 'production') { ... }. See `env.js`.
+    // if (process.env.NODE_ENV === 'production') { ... }.
     // It is absolutely essential that NODE_ENV was set to production here.
     // Otherwise React will be compiled in the very slow development mode.
     new webpack.DefinePlugin(env),
diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json
index 00be129a0..bc0fe3270 100644
--- a/packages/react-scripts/package.json
+++ b/packages/react-scripts/package.json
@@ -46,7 +46,6 @@
     "find-cache-dir": "0.1.1",
     "fs-extra": "0.30.0",
     "gzip-size": "3.0.0",
-    "html-loader": "0.4.3",
     "html-webpack-plugin": "2.22.0",
     "http-proxy-middleware": "0.17.1",
     "jest": "15.1.1",
diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js
index 71dcc798b..a0805e601 100644
--- a/packages/react-scripts/scripts/build.js
+++ b/packages/react-scripts/scripts/build.js
@@ -13,7 +13,7 @@
 process.env.NODE_ENV = 'production';
 
 var chalk = require('chalk');
-var fs = require('fs');
+var fs = require('fs-extra');
 var path = require('path');
 var filesize = require('filesize');
 var gzipSize = require('gzip-size').sync;
@@ -70,6 +70,9 @@ recursive(paths.appBuild, (err, fileNames) => {
 
   // Start the webpack build
   build(previousSizeMap);
+
+  // Merge with the public folder
+  copyPublicFolder();
 });
 
 // Print a detailed summary of build files.
@@ -175,3 +178,10 @@ function build(previousSizeMap) {
     }
   });
 }
+
+function copyPublicFolder() {
+  fs.copySync(paths.appPublic, paths.appBuild, {
+    dereference: true,
+    filter: file => file !== paths.appHtml
+  });
+}
diff --git a/packages/react-scripts/scripts/eject.js b/packages/react-scripts/scripts/eject.js
index a36e1d196..dd18cfb6a 100644
--- a/packages/react-scripts/scripts/eject.js
+++ b/packages/react-scripts/scripts/eject.js
@@ -46,6 +46,8 @@ prompt(
     path.join('scripts', 'start.js'),
     path.join('scripts', 'utils', 'checkRequiredFiles.js'),
     path.join('scripts', 'utils', 'chrome.applescript'),
+    path.join('scripts', 'utils', 'getClientEnvironment.js'),
+    path.join('scripts', 'utils', 'InterpolateHtmlPlugin.js'),
     path.join('scripts', 'utils', 'prompt.js'),
     path.join('scripts', 'utils', 'WatchMissingNodeModulesPlugin.js')
   ];
diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js
index b0290bfc4..191631694 100644
--- a/packages/react-scripts/scripts/start.js
+++ b/packages/react-scripts/scripts/start.js
@@ -255,23 +255,21 @@ function runDevServer(port, protocol) {
     // Silence WebpackDevServer's own logs since they're generally not useful.
     // It will still show compile warnings and errors with this setting.
     clientLogLevel: 'none',
-    // By default WebpackDevServer also serves files from the current directory.
-    // This might be useful in legacy apps. However we already encourage people
-    // to use Webpack for importing assets in the code, so we don't need to
-    // additionally serve files by their filenames. Otherwise, even if it
-    // works in development, those files will be missing in production, unless
-    // we explicitly copy them. But even if we copy all the files into
-    // the build output (which doesn't seem to be wise because it may contain
-    // private information such as files with API keys, for example), we would
-    // still have a problem. Since the filenames would be the same every time,
-    // browsers would cache their content, and updating file content would not
-    // work correctly. This is easily solved by importing assets through Webpack
-    // because if it can then append content hashes to filenames in production,
-    // just like it does for JS and CSS. And because we configured "html" loader
-    // to be used for HTML files, even <link href="./src/something.png"> would
-    // get resolved correctly by Webpack and handled both in development and
-    // in production without actually serving it by that path.
-    contentBase: [],
+    // By default WebpackDevServer serves physical files from current directory
+    // in addition to all the virtual build products that it serves from memory.
+    // This is confusing because those files won’t automatically be available in
+    // production build folder unless we copy them. However, copying the whole
+    // project directory is dangerous because we may expose sensitive files.
+    // Instead, we establish a convention that only files in `public` directory
+    // get served. Our build script will copy `public` into the `build` folder.
+    // In `index.html`, you can get URL of `public` folder with %PUBLIC_PATH%:
+    // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
+    // In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
+    // Note that we only recommend to use `public` folder as an escape hatch
+    // for files like `favicon.ico`, `manifest.json`, and libraries that are
+    // for some reason broken when imported through Webpack. If you just want to
+    // use an image, put it in `src` and `import` it from JavaScript instead.
+    contentBase: paths.appPublic,
     // Enable hot reloading server. It will provide /sockjs-node/ endpoint
     // for the WebpackDevServer client so it can learn when the files were
     // updated. The WebpackDevServer client is included as an entry point
diff --git a/packages/react-scripts/scripts/test.js b/packages/react-scripts/scripts/test.js
index d13aff380..9ff834bcd 100644
--- a/packages/react-scripts/scripts/test.js
+++ b/packages/react-scripts/scripts/test.js
@@ -8,6 +8,7 @@
  */
 
 process.env.NODE_ENV = 'test';
+process.env.PUBLIC_URL = '';
 
 const createJestConfig = require('./utils/createJestConfig');
 const jest = require('jest');
diff --git a/packages/react-scripts/scripts/utils/InterpolateHtmlPlugin.js b/packages/react-scripts/scripts/utils/InterpolateHtmlPlugin.js
new file mode 100644
index 000000000..bcb3a78a7
--- /dev/null
+++ b/packages/react-scripts/scripts/utils/InterpolateHtmlPlugin.js
@@ -0,0 +1,43 @@
+// @remove-on-eject-begin
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+// @remove-on-eject-end
+
+// This Webpack plugin lets us interpolate custom variables into `index.html`.
+// Usage: `new InterpolateHtmlPlugin({ 'MY_VARIABLE': 42 })`
+// Then, you can use %MY_VARIABLE% in your `index.html`.
+
+// It works in tandem with HtmlWebpackPlugin.
+// Learn more about creating plugins like this:
+// https://github.com/ampedandwired/html-webpack-plugin#events
+
+'use strict';
+
+class InterpolateHtmlPlugin {
+  constructor(replacements) {
+    this.replacements = replacements;
+  }
+
+  apply(compiler) {
+    compiler.plugin('compilation', compilation => {
+      compilation.plugin('html-webpack-plugin-before-html-processing',
+        (data, callback) => {
+          // Run HTML through a series of user-specified string replacements.
+          Object.keys(this.replacements).forEach(key => {
+            const value = this.replacements[key];
+            data.html = data.html.replace('%' + key + '%', value);
+          });
+          callback(null, data);
+        }
+      );
+    });
+  }
+}
+
+module.exports = InterpolateHtmlPlugin;
diff --git a/packages/react-scripts/scripts/utils/getClientEnvironment.js b/packages/react-scripts/scripts/utils/getClientEnvironment.js
new file mode 100644
index 000000000..2bdc65afa
--- /dev/null
+++ b/packages/react-scripts/scripts/utils/getClientEnvironment.js
@@ -0,0 +1,38 @@
+// @remove-on-eject-begin
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+// @remove-on-eject-end
+
+// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
+// injected into the application via DefinePlugin in Webpack configuration.
+
+var REACT_APP = /^REACT_APP_/i;
+
+function getClientEnvironment(publicUrl) {
+  return Object
+    .keys(process.env)
+    .filter(key => REACT_APP.test(key))
+    .reduce((env, key) => {
+      env['process.env.' + key] = JSON.stringify(process.env[key]);
+      return env;
+    }, {
+      // Useful for determining whether we’re running in production mode.
+      // Most importantly, it switches React into the correct mode.
+      'process.env.NODE_ENV': JSON.stringify(
+        process.env.NODE_ENV || 'development'
+      ),
+      // Useful for resolving the correct path to static assets in `public`.
+      // For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
+      // This should only be used as an escape hatch. Normally you would put
+      // images into the `src` and `import` them in code to get their paths.
+      'process.env.PUBLIC_URL': JSON.stringify(publicUrl)
+    });
+}
+
+module.exports = getClientEnvironment;
diff --git a/packages/react-scripts/template/index.html b/packages/react-scripts/template/index.html
deleted file mode 100644
index f89671553..000000000
--- a/packages/react-scripts/template/index.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!doctype html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1">
-    <link rel="shortcut icon" href="./src/favicon.ico">
-    <title>React App</title>
-  </head>
-  <body>
-    <div id="root"></div>
-    <!--
-      This HTML file is a template.
-      If you open it directly in the browser, you will see an empty page.
-
-      You can add webfonts, meta tags, or analytics to this file.
-      The build step will place the bundled scripts into the <body> tag.
-
-      To begin the development, run `npm start` in this folder.
-      To create a production bundle, use `npm run build`.
-    -->
-  </body>
-</html>
diff --git a/packages/react-scripts/template/src/favicon.ico b/packages/react-scripts/template/public/favicon.ico
similarity index 100%
rename from packages/react-scripts/template/src/favicon.ico
rename to packages/react-scripts/template/public/favicon.ico
diff --git a/packages/react-scripts/template/public/index.html b/packages/react-scripts/template/public/index.html
new file mode 100644
index 000000000..c89bbf37a
--- /dev/null
+++ b/packages/react-scripts/template/public/index.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
+    <!--
+      Notice the use of %PUBLIC_URL% in the tag above.
+      It will be replaced with the URL of the `public` folder during the build.
+      Only files inside the `public` folder can be referenced from the HTML.
+
+      Unlike "/favicon.ico" or "favico.ico", "%PUBLIC_URL%/favicon.ico" will
+      work correctly both with client-side routing and a non-root public URL.
+      Learn how to configure a non-root public URL by running `npm run build`.
+    -->
+    <title>React App</title>
+  </head>
+  <body>
+    <div id="root"></div>
+    <!--
+      This HTML file is a template.
+      If you open it directly in the browser, you will see an empty page.
+
+      You can add webfonts, meta tags, or analytics to this file.
+      The build step will place the bundled scripts into the <body> tag.
+
+      To begin the development, run `npm start`.
+      To create a production bundle, use `npm run build`.
+    -->
+  </body>
+</html>
-- 
GitLab