From f53b63832386a4fd86c90edb75ea382689da0ac2 Mon Sep 17 00:00:00 2001 From: Ian Sutherland 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 +++++++++++++ package.json | 1 + 6 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 fixtures/kitchensink/src/features/webpack/SvgComponent.js create mode 100644 fixtures/kitchensink/src/features/webpack/SvgComponent.test.js diff --git a/config/jest/fileTransform.js b/config/jest/fileTransform.js index 38910e18b19..b5aa17e0f7b 100644 --- a/config/jest/fileTransform.js +++ b/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/config/webpack.config.dev.js b/config/webpack.config.dev.js index a016510f1fa..f5ac385aea3 100644 --- a/config/webpack.config.dev.js +++ b/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/config/webpack.config.prod.js b/config/webpack.config.prod.js index 578ef0de54a..f831a1dd766 100644 --- a/config/webpack.config.prod.js +++ b/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/fixtures/kitchensink/src/features/webpack/SvgComponent.js b/fixtures/kitchensink/src/features/webpack/SvgComponent.js new file mode 100644 index 00000000000..0eb06a027e3 --- /dev/null +++ b/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/fixtures/kitchensink/src/features/webpack/SvgComponent.test.js b/fixtures/kitchensink/src/features/webpack/SvgComponent.test.js new file mode 100644 index 00000000000..1b63788c730 --- /dev/null +++ b/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(, div); + expect(div.textContent).toBe('logo.svg'); + }); +}); diff --git a/package.json b/package.json index 583fcc346a7..f19e1d1f2a8 100644 --- a/package.json +++ b/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",