diff --git a/docusaurus/docs/adding-images-fonts-and-files.md b/docusaurus/docs/adding-images-fonts-and-files.md index 67cc4b53d22..c76ff5401e3 100644 --- a/docusaurus/docs/adding-images-fonts-and-files.md +++ b/docusaurus/docs/adding-images-fonts-and-files.md @@ -47,21 +47,24 @@ An alternative way of handling static assets is described in the next section. > Note: this feature is available with `react-scripts@2.0.0` and higher, and `react@16.3.0` and higher. -One way to add SVG files was described in the section above. You can also import SVGs directly as React components. You can use either of the two approaches. In your code it would look like this: +One way to add SVG files was described in the section above, but you need to add `?url` as query string to import it as URL. You can also import SVGs directly as React components. You can use either of the two approaches. In your code it would look like this: ```js -import { ReactComponent as Logo } from './logo.svg'; +import Logo from './logo.svg'; +import logo from './logo.svg?url'; function App() { return (
{/* Logo is an actual React component */} + {/* logo as url */} + logo
); } ``` -This is handy if you don't want to load SVG as a separate file. Don't forget the curly braces in the import! The `ReactComponent` import name is significant and tells Create React App that you want a React component that renders an SVG, rather than its filename. +This is handy if you don't want to load SVG as a separate file. > **Tip:** The imported SVG React Component accepts a `title` prop along with other props that a `svg` element accepts. Use this prop to add an accessible title to your svg component. diff --git a/package-lock.json b/package-lock.json index ea5b4f049f7..d485380fcc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29793,21 +29793,21 @@ } }, "packages/cra-template": { - "version": "1.1.3", + "version": "1.2.0", "license": "MIT", "engines": { "node": ">=14" } }, "packages/cra-template-typescript": { - "version": "1.1.3", + "version": "1.2.0", "license": "MIT", "engines": { "node": ">=14" } }, "packages/create-react-app": { - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "dependencies": { "chalk": "^4.1.2", @@ -29848,7 +29848,7 @@ } }, "packages/eslint-config-react-app": { - "version": "7.0.0", + "version": "7.0.1", "license": "MIT", "dependencies": { "@babel/core": "^7.16.0", @@ -29889,7 +29889,7 @@ } }, "packages/react-dev-utils": { - "version": "12.0.0", + "version": "12.0.1", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.16.0", @@ -29911,7 +29911,7 @@ "open": "^8.4.0", "pkg-up": "^3.1.0", "prompts": "^2.4.2", - "react-error-overlay": "^6.0.10", + "react-error-overlay": "^6.0.11", "recursive-readdir": "^2.2.2", "shell-quote": "^1.7.3", "strip-ansi": "^6.0.1", @@ -29934,7 +29934,7 @@ } }, "packages/react-error-overlay": { - "version": "6.0.10", + "version": "6.0.11", "license": "MIT", "devDependencies": { "@babel/code-frame": "^7.16.0", @@ -29946,17 +29946,6 @@ "chalk": "^4.1.2", "chokidar": "^3.5.2", "cross-env": "^7.0.3", - "eslint": "^8.3.0", - "eslint-config-react-app": "^7.0.0", -<<<<<<< HEAD -======= -======= -<<<<<<< HEAD ->>>>>>> f21e2137 (Publish) -======= ->>>>>>> 9f8d75e5 (chore(lint): lint all files) ->>>>>>> fb003998 (chore(lint): lint all files) ->>>>>>> f301bfe4 (chore(lint): lint all files) "flow-bin": "^0.116.0", "html-entities": "^2.3.2", "jest": "^27.4.3", @@ -29983,7 +29972,7 @@ } }, "packages/react-scripts": { - "version": "5.0.0", + "version": "5.0.1", "license": "MIT", "dependencies": { "@babel/core": "^7.16.0", @@ -30002,7 +29991,7 @@ "dotenv": "^10.0.0", "dotenv-expand": "^5.1.0", "eslint": "^8.3.0", - "eslint-config-react-app": "^7.0.0", + "eslint-config-react-app": "^7.0.1", "eslint-webpack-plugin": "^3.1.1", "file-loader": "^6.2.0", "fs-extra": "^10.0.0", @@ -30019,7 +30008,7 @@ "postcss-preset-env": "^7.0.1", "prompts": "^2.4.2", "react-app-polyfill": "^3.0.0", - "react-dev-utils": "^12.0.0", + "react-dev-utils": "^12.0.1", "react-refresh": "^0.11.0", "resolve": "^1.20.0", "resolve-url-loader": "^4.0.0", @@ -47301,7 +47290,7 @@ "open": "^8.4.0", "pkg-up": "^3.1.0", "prompts": "^2.4.2", - "react-error-overlay": "^6.0.10", + "react-error-overlay": "^6.0.11", "recursive-readdir": "^2.2.2", "shell-quote": "^1.7.3", "strip-ansi": "^6.0.1", @@ -47337,19 +47326,6 @@ "chalk": "^4.1.2", "chokidar": "^3.5.2", "cross-env": "^7.0.3", -<<<<<<< HEAD - "eslint": "^8.3.0", - "eslint-config-react-app": "^7.0.0", -======= -<<<<<<< HEAD -======= -<<<<<<< HEAD - "eslint": "^8.3.0", - "eslint-config-react-app": "^7.0.0", -======= ->>>>>>> 9f8d75e5 (chore(lint): lint all files) ->>>>>>> fb003998 (chore(lint): lint all files) ->>>>>>> f301bfe4 (chore(lint): lint all files) "flow-bin": "^0.116.0", "html-entities": "^2.3.2", "jest": "^27.4.3", @@ -47506,7 +47482,7 @@ "dotenv": "^10.0.0", "dotenv-expand": "^5.1.0", "eslint": "^8.3.0", - "eslint-config-react-app": "^7.0.0", + "eslint-config-react-app": "^7.0.1", "eslint-webpack-plugin": "^3.1.1", "file-loader": "^6.2.0", "fs-extra": "^10.0.0", @@ -47525,7 +47501,7 @@ "prompts": "^2.4.2", "react": "^18.0.0", "react-app-polyfill": "^3.0.0", - "react-dev-utils": "^12.0.0", + "react-dev-utils": "^12.0.1", "react-dom": "^18.0.0", "react-refresh": "^0.11.0", "resolve": "^1.20.0", diff --git a/packages/babel-plugin-named-asset-import/index.test.js b/packages/babel-plugin-named-asset-import/index.test.js index fff2c28894e..55e96acefa0 100644 --- a/packages/babel-plugin-named-asset-import/index.test.js +++ b/packages/babel-plugin-named-asset-import/index.test.js @@ -8,7 +8,7 @@ pluginTester.default({ pluginOptions: { loaderMap: { svg: { - ReactComponent: '@svgr/webpack?-svgo![path]', + ReactComponent: '[path]', }, }, }, @@ -28,24 +28,24 @@ pluginTester.default({ output: 'import { Url as logo1 } from "logo";', }, svgDefaultImport: { - code: 'import logo from "logo.svg";', - output: 'import logo from "logo.svg";', + code: 'import Logo from "logo.svg";', + output: 'import Logo from "logo.svg";', }, svgNamedImport: { code: 'import { logo } from "logo.svg";', output: 'import { logo } from "logo.svg";', }, svgReactComponentNamedImport: { - code: 'import { ReactComponent as logo } from "logo.svg";', - output: - 'import { ReactComponent as logo } from "@svgr/webpack?-svgo!logo.svg";', + code: 'import { ReactComponent as Logo } from "logo.svg";', + output: 'import { ReactComponent as Logo } from "logo.svg";', }, svgMultipleImport: { - code: 'import logo, { logoUrl , ReactComponent as Logo } from "logo.svg";', + code: + 'import logoUrl from "logo.svg?url";\n' + + 'import Logo from "logo.svg";', output: - 'import logo from "logo.svg";\n' + - 'import { logoUrl } from "logo.svg";\n' + - 'import { ReactComponent as Logo } from "@svgr/webpack?-svgo!logo.svg";', + 'import logoUrl from "logo.svg?url";\n' + + 'import Logo from "logo.svg";', }, defaultExport: { code: 'export default logo;', @@ -81,18 +81,19 @@ pluginTester.default({ }, svgReactComponentNamedExport: { code: 'export { ReactComponent as Logo } from "logo.svg";', - output: - 'export { ReactComponent as Logo } from "@svgr/webpack?-svgo!logo.svg";', + output: 'export { ReactComponent as Logo } from "logo.svg";', }, svgReactComponentExport: { code: 'export { ReactComponent } from "logo.svg";', - output: 'export { ReactComponent } from "@svgr/webpack?-svgo!logo.svg";', + output: 'export { ReactComponent } from "logo.svg";', }, svgMultipleExport: { - code: 'export { logoUrl , ReactComponent as Logo } from "logo.svg";', + code: + 'export { logoUrl } from "logo.svg?url";\n' + + 'export { ReactComponent as Logo } from "logo.svg";', output: - 'export { logoUrl } from "logo.svg";\n' + - 'export { ReactComponent as Logo } from "@svgr/webpack?-svgo!logo.svg";', + 'export { logoUrl } from "logo.svg?url";\n' + + 'export { ReactComponent as Logo } from "logo.svg";', }, }, }); diff --git a/packages/cra-template-typescript/template/src/App.tsx b/packages/cra-template-typescript/template/src/App.tsx index a53698aab3c..94c986a3c93 100644 --- a/packages/cra-template-typescript/template/src/App.tsx +++ b/packages/cra-template-typescript/template/src/App.tsx @@ -1,12 +1,12 @@ import React from 'react'; -import logo from './logo.svg'; +import Logo from './logo.svg'; import './App.css'; function App() { return (
- logo +

Edit src/App.tsx and save to reload.

diff --git a/packages/cra-template/template/src/App.js b/packages/cra-template/template/src/App.js index 37845757234..f8e04508180 100644 --- a/packages/cra-template/template/src/App.js +++ b/packages/cra-template/template/src/App.js @@ -1,11 +1,11 @@ -import logo from './logo.svg'; +import Logo from './logo.svg'; import './App.css'; function App() { return (
- logo +

Edit src/App.js and save to reload.

diff --git a/packages/react-scripts/config/jest/fileTransform.js b/packages/react-scripts/config/jest/fileTransform.js index aab67618c38..3facbe0a2ba 100644 --- a/packages/react-scripts/config/jest/fileTransform.js +++ b/packages/react-scripts/config/jest/fileTransform.js @@ -16,7 +16,7 @@ module.exports = { const pascalCaseFilename = camelcase(path.parse(filename).name, { pascalCase: true, }); - const componentName = `Svg${pascalCaseFilename}`; + const componentName = `${pascalCaseFilename}`; return `const React = require('react'); module.exports = { __esModule: true, diff --git a/packages/react-scripts/config/webpack.config.js b/packages/react-scripts/config/webpack.config.js index e465d8e7a00..e0b5751caa4 100644 --- a/packages/react-scripts/config/webpack.config.js +++ b/packages/react-scripts/config/webpack.config.js @@ -386,30 +386,31 @@ module.exports = function (webpackEnv) { }, }, { - test: /\.svg$/, - use: [ - { - loader: require.resolve('@svgr/webpack'), - options: { - prettier: false, - svgo: false, - svgoConfig: { - plugins: [{ removeViewBox: false }], - }, - titleProp: true, - ref: true, - }, - }, - { - loader: require.resolve('file-loader'), - options: { - name: 'static/media/[name].[hash].[ext]', - }, - }, - ], + test: /\.svg$/i, + issuer: { + and: [/\.(ts|tsx|js|jsx|md|mdx)$/], + }, + type: 'asset', + resourceQuery: /url/, // *.svg?url + }, + { + test: /\.svg$/i, issuer: { and: [/\.(ts|tsx|js|jsx|md|mdx)$/], }, + resourceQuery: { not: [/url/] }, // exclude react component if *.svg?url + use: { + loader: require.resolve('@svgr/webpack'), + options: { + prettier: false, + svgo: false, + svgoConfig: { + plugins: [{ removeViewBox: false }], + }, + titleProp: true, + ref: true, + }, + }, }, // Process application JS with Babel. // The preset includes JSX, Flow, TypeScript, and some ESnext features. diff --git a/packages/react-scripts/fixtures/kitchensink/template/integration/webpack.test.js b/packages/react-scripts/fixtures/kitchensink/template/integration/webpack.test.js index 537c8456051..eb2ddd51ab9 100644 --- a/packages/react-scripts/fixtures/kitchensink/template/integration/webpack.test.js +++ b/packages/react-scripts/fixtures/kitchensink/template/integration/webpack.test.js @@ -117,9 +117,7 @@ describe('Integration', () => { it('svg inclusion', async () => { doc = await initDOM('svg-inclusion'); - expect(doc.getElementById('feature-svg-inclusion').src).toMatch( - /\/static\/media\/logo\..+\.svg$/ - ); + expect(doc.getElementById('feature-svg-inclusion').src).toBeDefined(); }); it('svg component', async () => { diff --git a/packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/SvgComponent.js b/packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/SvgComponent.js index d7d6dafeb33..16e5e89ad6a 100644 --- a/packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/SvgComponent.js +++ b/packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/SvgComponent.js @@ -6,7 +6,7 @@ */ import React from 'react'; -import { ReactComponent as Logo } from './assets/logo.svg'; +import Logo from './assets/logo.svg'; const SvgComponent = () => { return ; diff --git a/packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/SvgComponent.test.js b/packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/SvgComponent.test.js index 493a6bc87ba..19811ef5d75 100644 --- a/packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/SvgComponent.test.js +++ b/packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/SvgComponent.test.js @@ -13,14 +13,16 @@ describe('svg component', () => { it('renders without crashing', () => { const div = document.createElement('div'); ReactDOM.render(, div); - expect(div.textContent).toBe('logo.svg'); + expect(div.innerHTML).toBe( + '' + ); }); it('svg root element equals the passed ref', () => { const div = document.createElement('div'); const someRef = React.createRef(); ReactDOM.render(, div); - const svgElement = div.getElementsByTagName('svg'); + const svgElement = div.getElementsByTagName('logo.svg'); expect(svgElement).toHaveLength(1); expect(svgElement[0]).toBe(someRef.current); }); diff --git a/packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/assets/svg.css b/packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/assets/svg.css index 383743f4bfe..a7e9ccf14e0 100644 --- a/packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/assets/svg.css +++ b/packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/assets/svg.css @@ -1,3 +1,3 @@ #feature-svg-in-css { - background-image: url('./logo.svg'); + background-image: url('./logo.svg?url'); } diff --git a/packages/react-scripts/lib/react-app.d.ts b/packages/react-scripts/lib/react-app.d.ts index 780c321229c..b17fa8a2c20 100644 --- a/packages/react-scripts/lib/react-app.d.ts +++ b/packages/react-scripts/lib/react-app.d.ts @@ -44,15 +44,16 @@ declare module '*.webp' { export default src; } -declare module '*.svg' { - import * as React from 'react'; +declare module '*.svg?url' { + const content: string; + export default content; +} - export const ReactComponent: React.FunctionComponent< +declare module '*.svg' { + const ReactComponent: React.FunctionComponent< React.SVGProps & { title?: string } >; - - const src: string; - export default src; + export default ReactComponent; } declare module '*.module.css' { diff --git a/packages/react-scripts/scripts/utils/createJestConfig.js b/packages/react-scripts/scripts/utils/createJestConfig.js index ff1c5811025..be6b00f3340 100644 --- a/packages/react-scripts/scripts/utils/createJestConfig.js +++ b/packages/react-scripts/scripts/utils/createJestConfig.js @@ -56,6 +56,7 @@ module.exports = (resolve, rootDir, isEjecting) => { moduleNameMapper: { '^react-native$': 'react-native-web', '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', + '^@/(.*svg?url)$': '/src/$1', ...(modules.jestAliases || {}), }, moduleFileExtensions: [...paths.moduleFileExtensions, 'node'].filter( diff --git a/tasks/e2e-simple.sh b/tasks/e2e-simple.sh index d2858f0715f..e4727d2889b 100755 --- a/tasks/e2e-simple.sh +++ b/tasks/e2e-simple.sh @@ -102,7 +102,7 @@ npm run build exists build/*.html exists build/static/js/*.js exists build/static/css/*.css -exists build/static/media/*.svg +# TODO: 'exists build/static/media/*.svg' if size higher than IMAGE_INLINE_SIZE_LIMIT exists build/favicon.ico # Run tests with CI flag @@ -210,7 +210,7 @@ npm run build exists build/*.html exists build/static/js/*.js exists build/static/css/*.css -exists build/static/media/*.svg +# TODO: 'exists build/static/media/*.svg' if size higher than IMAGE_INLINE_SIZE_LIMIT exists build/favicon.ico # Run tests with CI flag @@ -243,7 +243,7 @@ npm run build exists build/*.html exists build/static/js/*.js exists build/static/css/*.css -exists build/static/media/*.svg +# TODO: 'exists build/static/media/*.svg' if size higher than IMAGE_INLINE_SIZE_LIMIT exists build/favicon.ico # Run tests, overriding the watch option to disable it. diff --git a/test/fixtures/global-scss-asset-resolution/src/index.scss b/test/fixtures/global-scss-asset-resolution/src/index.scss index 81ade6705a1..6c0d8dfde53 100644 --- a/test/fixtures/global-scss-asset-resolution/src/index.scss +++ b/test/fixtures/global-scss-asset-resolution/src/index.scss @@ -1,5 +1,5 @@ #root { width: 300px; height: 300px; - background: url(/images/logo.svg) center/cover no-repeat; + background: url(/images/logo.svg?url) center/cover no-repeat; } diff --git a/test/fixtures/relative-paths/src/index.css b/test/fixtures/relative-paths/src/index.css index 244889b10aa..d48acce9e84 100644 --- a/test/fixtures/relative-paths/src/index.css +++ b/test/fixtures/relative-paths/src/index.css @@ -1,7 +1,7 @@ .RootSvg:before { display: block; content: ' '; - background-image: url(./logo.svg); + background-image: url(./logo.svg?url); background-size: 28px 28px; height: 28px; width: 28px;