From 0eba03938d7bac28c017bd88c7054f70b7d305cc Mon Sep 17 00:00:00 2001 From: Ian Sutherland Date: Mon, 8 Jan 2018 16:44:10 -0700 Subject: [PATCH 1/6] Import SVGs as React components (#1388) --- .../config/webpack.config.dev.js | 25 +++++++++++++++++++ packages/react-scripts/package.json | 1 + 2 files changed, 26 insertions(+) diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index 9f3131b0660..9f2090b4206 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -213,6 +213,31 @@ module.exports = { }, ], }, + { + 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 + // This is a feature of `babel-loader` for webpack (not Babel itself). + // It enables caching results in ./node_modules/.cache/babel-loader/ + // directory for faster rebuilds. + 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/package.json b/packages/react-scripts/package.json index dcac53cf49f..08705986e92 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -51,6 +51,7 @@ "raf": "3.4.0", "react-dev-utils": "^4.2.1", "style-loader": "0.19.0", + "svgr": "1.6.0", "sw-precache-webpack-plugin": "0.11.4", "url-loader": "0.6.2", "webpack": "3.8.1", From 81f31acacb05c78ca2ad623be2b3b1dc9d34c5f2 Mon Sep 17 00:00:00 2001 From: Ian Sutherland Date: Wed, 17 Jan 2018 09:33:33 -0700 Subject: [PATCH 2/6] Updated webpack production config and fixed tests --- .../config/jest/fileTransform.js | 4 ++++ .../config/webpack.config.dev.js | 3 --- .../config/webpack.config.prod.js | 22 +++++++++++++++++++ .../src/features/webpack/SvgComponent.js | 11 ++++++++++ .../src/features/webpack/SvgComponent.test.js | 17 ++++++++++++++ 5 files changed, 54 insertions(+), 3 deletions(-) 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 38910e18b19..855645e1e45 100644 --- a/packages/react-scripts/config/jest/fileTransform.js +++ b/packages/react-scripts/config/jest/fileTransform.js @@ -15,6 +15,10 @@ const path = require('path'); module.exports = { process(src, filename) { + if (filename.match(/\.svg$/)) { + return `module.exports = {ReactComponent: () => ('')};`; + } + return `module.exports = ${JSON.stringify(path.basename(filename))};`; }, }; diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index 2c5c6d87ef4..209afab7a80 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -245,9 +245,6 @@ module.exports = { babelrc: false, presets: [require.resolve('babel-preset-react-app')], // @remove-on-eject-end - // This is a feature of `babel-loader` for webpack (not Babel itself). - // It enables caching results in ./node_modules/.cache/babel-loader/ - // directory for faster rebuilds. cacheDirectory: true, }, }, diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 769016ed1b6..48a20258135 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -262,6 +262,28 @@ module.exports = { ), // Note: this won't work without `new ExtractTextPlugin()` in `plugins`. }, + { + 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 00000000000..0eb06a027e3 --- /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 () => ; 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 00000000000..9904982fe98 --- /dev/null +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgComponent.test.js @@ -0,0 +1,17 @@ +/** + * 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(, div); + }); +}); From 8593bdd3022c1f5616a2e90f3c72dea17f2e4f14 Mon Sep 17 00:00:00 2001 From: Ian Sutherland Date: Wed, 17 Jan 2018 10:03:48 -0700 Subject: [PATCH 3/6] Improved Jest SVG file transform --- packages/react-scripts/config/jest/fileTransform.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/react-scripts/config/jest/fileTransform.js b/packages/react-scripts/config/jest/fileTransform.js index 855645e1e45..0e194c81b1f 100644 --- a/packages/react-scripts/config/jest/fileTransform.js +++ b/packages/react-scripts/config/jest/fileTransform.js @@ -16,7 +16,11 @@ const path = require('path'); module.exports = { process(src, filename) { if (filename.match(/\.svg$/)) { - return `module.exports = {ReactComponent: () => ('')};`; + return `module.exports = { + __esModule: true, + default: ${JSON.stringify(path.basename(filename))}, + ReactComponent: () => null + };`; } return `module.exports = ${JSON.stringify(path.basename(filename))};`; From c385b18cb486554d27f789f660e4f249d78296b7 Mon Sep 17 00:00:00 2001 From: Ian Sutherland Date: Wed, 17 Jan 2018 12:08:44 -0700 Subject: [PATCH 4/6] Improved SVG tests --- packages/react-scripts/config/jest/fileTransform.js | 8 +++++--- .../kitchensink/src/features/webpack/SvgComponent.test.js | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/react-scripts/config/jest/fileTransform.js b/packages/react-scripts/config/jest/fileTransform.js index 0e194c81b1f..b5aa17e0f7b 100644 --- a/packages/react-scripts/config/jest/fileTransform.js +++ b/packages/react-scripts/config/jest/fileTransform.js @@ -15,14 +15,16 @@ const path = require('path'); module.exports = { process(src, filename) { + const assetFilename = JSON.stringify(path.basename(filename)); + if (filename.match(/\.svg$/)) { return `module.exports = { __esModule: true, - default: ${JSON.stringify(path.basename(filename))}, - ReactComponent: () => null + default: ${assetFilename}, + ReactComponent: () => ${assetFilename}, };`; } - return `module.exports = ${JSON.stringify(path.basename(filename))};`; + return `module.exports = ${assetFilename};`; }, }; 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 index 9904982fe98..1b63788c730 100644 --- a/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgComponent.test.js +++ b/packages/react-scripts/fixtures/kitchensink/src/features/webpack/SvgComponent.test.js @@ -13,5 +13,6 @@ describe('svg component', () => { it('renders without crashing', () => { const div = document.createElement('div'); ReactDOM.render(, div); + expect(div.textContent).toBe('logo.svg'); }); }); From d400d198dd2e1518d84bd42a4b0586f8de0d36e6 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 17 Jan 2018 19:40:08 +0000 Subject: [PATCH 5/6] Add a comment --- packages/react-scripts/config/webpack.config.dev.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index 209afab7a80..785853b80cb 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -235,6 +235,9 @@ 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: [ From 38728857fc2dabf003bdb596c87c1a3e5d4bfe95 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 17 Jan 2018 19:40:24 +0000 Subject: [PATCH 6/6] Update webpack.config.prod.js --- packages/react-scripts/config/webpack.config.prod.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 48a20258135..41bae378d67 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -262,6 +262,9 @@ 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: [