From 82907fd23cd2a521c3b6c14316ea4177841ede3d Mon Sep 17 00:00:00 2001
From: Ian Sutherland <ian@iansutherland.ca>
Date: Wed, 17 Jan 2018 12:40:56 -0700
Subject: [PATCH] Import SVGs as React components (#1388) (#3718)

* Import SVGs as React components (#1388)

* Updated webpack production config and fixed tests

* Improved Jest SVG file transform

* Improved SVG tests

* Add a comment

* Update webpack.config.prod.js
---
 .../config/jest/fileTransform.js              | 12 ++++++++-
 .../config/webpack.config.dev.js              | 25 +++++++++++++++++++
 .../config/webpack.config.prod.js             | 25 +++++++++++++++++++
 .../src/features/webpack/SvgComponent.js      | 11 ++++++++
 .../src/features/webpack/SvgComponent.test.js | 18 +++++++++++++
 packages/react-scripts/package.json           |  1 +
 6 files changed, 91 insertions(+), 1 deletion(-)
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgComponent.js
 create mode 100644 packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgComponent.test.js

diff --git a/packages/react-scripts/config/jest/fileTransform.js b/packages/react-scripts/config/jest/fileTransform.js
index 38910e18b..b5aa17e0f 100644
--- a/packages/react-scripts/config/jest/fileTransform.js
+++ b/packages/react-scripts/config/jest/fileTransform.js
@@ -15,6 +15,16 @@ const path = require('path');
 
 module.exports = {
   process(src, filename) {
-    return `module.exports = ${JSON.stringify(path.basename(filename))};`;
+    const assetFilename = JSON.stringify(path.basename(filename));
+
+    if (filename.match(/\.svg$/)) {
+      return `module.exports = {
+        __esModule: true,
+        default: ${assetFilename},
+        ReactComponent: () => ${assetFilename},
+      };`;
+    }
+
+    return `module.exports = ${assetFilename};`;
   },
 };
diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js
index a016510f1..f5ac385ae 100644
--- a/packages/react-scripts/config/webpack.config.dev.js
+++ b/packages/react-scripts/config/webpack.config.dev.js
@@ -262,6 +262,31 @@ module.exports = {
               },
             ],
           },
+          // Allows you to use two kinds of imports for SVG:
+          // import logoUrl from './logo.svg'; gives you the URL.
+          // import { ReactComponent as Logo } from './logo.svg'; gives you a component.
+          {
+            test: /\.svg$/,
+            use: [
+              {
+                loader: require.resolve('babel-loader'),
+                options: {
+                  // @remove-on-eject-begin
+                  babelrc: false,
+                  presets: [require.resolve('babel-preset-react-app')],
+                  // @remove-on-eject-end
+                  cacheDirectory: true,
+                },
+              },
+              require.resolve('svgr/webpack'),
+              {
+                loader: require.resolve('file-loader'),
+                options: {
+                  name: 'static/media/[name].[hash:8].[ext]',
+                },
+              },
+            ],
+          },
           // "file" loader makes sure those assets get served by WebpackDevServer.
           // When you `import` an asset, you get its (virtual) filename.
           // In production, they would get copied to the `build` folder.
diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js
index 578ef0de5..f831a1dd7 100644
--- a/packages/react-scripts/config/webpack.config.prod.js
+++ b/packages/react-scripts/config/webpack.config.prod.js
@@ -304,6 +304,31 @@ module.exports = {
             ),
             // Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
           },
+          // Allows you to use two kinds of imports for SVG:
+          // import logoUrl from './logo.svg'; gives you the URL.
+          // import { ReactComponent as Logo } from './logo.svg'; gives you a component.
+          {
+            test: /\.svg$/,
+            use: [
+              {
+                loader: require.resolve('babel-loader'),
+                options: {
+                  // @remove-on-eject-begin
+                  babelrc: false,
+                  presets: [require.resolve('babel-preset-react-app')],
+                  // @remove-on-eject-end
+                  cacheDirectory: true,
+                },
+              },
+              require.resolve('svgr/webpack'),
+              {
+                loader: require.resolve('file-loader'),
+                options: {
+                  name: 'static/media/[name].[hash:8].[ext]',
+                },
+              },
+            ],
+          },
           // "file" loader makes sure assets end up in the `build` folder.
           // When you `import` an asset, you get its filename.
           // This loader doesn't use a "test" so it will catch all modules
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgComponent.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgComponent.js
new file mode 100644
index 000000000..0eb06a027
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgComponent.js
@@ -0,0 +1,11 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import React from 'react';
+import { ReactComponent as Logo } from './assets/logo.svg';
+
+export default () => <Logo />;
diff --git a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgComponent.test.js b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgComponent.test.js
new file mode 100644
index 000000000..1b63788c7
--- /dev/null
+++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgComponent.test.js
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import React from 'react';
+import ReactDOM from 'react-dom';
+import SvgComponent from './SvgComponent';
+
+describe('svg component', () => {
+  it('renders without crashing', () => {
+    const div = document.createElement('div');
+    ReactDOM.render(<SvgComponent />, div);
+    expect(div.textContent).toBe('logo.svg');
+  });
+});
diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json
index 583fcc346..f19e1d1f2 100644
--- a/packages/react-scripts/package.json
+++ b/packages/react-scripts/package.json
@@ -54,6 +54,7 @@
     "raf": "3.4.0",
     "react-dev-utils": "^5.0.0",
     "style-loader": "0.19.1",
+    "svgr": "1.6.0",
     "sw-precache-webpack-plugin": "0.11.4",
     "thread-loader": "1.1.2",
     "uglifyjs-webpack-plugin": "1.1.6",
-- 
GitLab