Skip to content

Commit

Permalink
Omit svg static imports if custom webpack config is defined (#26281)
Browse files Browse the repository at this point in the history
This PR does a couple things:

1. Omit svg static imports if the user has defined custom webpack config with svg rule
2. Change TS type to `any` for svg imports to avoid conflicts with other plugins

The idea is that some users want to use `next/image` with static imports for most image types but not for svg and instead inline those images with a plugin.

- Fixes #25950  
- Fixes #26130 
- Fixes #26176 
- Fixes #26196 
- Fixes #26067 


## Bug

- [x] Related issues linked using Fixes #26130 
- [x] Integration tests added
  • Loading branch information
styfle authored Jun 18, 2021
1 parent 14f01a1 commit c35b01a
Show file tree
Hide file tree
Showing 12 changed files with 11,359 additions and 241 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@babel/preset-react": "7.12.10",
"@fullhuman/postcss-purgecss": "1.3.0",
"@mdx-js/loader": "0.18.0",
"@svgr/webpack": "5.5.0",
"@testing-library/react": "11.2.5",
"@types/cheerio": "0.22.16",
"@types/fs-extra": "8.1.0",
Expand Down
34 changes: 24 additions & 10 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1030,16 +1030,6 @@ export default async function getBaseWebpackConfig(
]
: defaultLoaders.babel,
},
...(!config.images.disableStaticImages && isWebpack5
? [
{
test: /\.(png|svg|jpg|jpeg|gif|webp|ico|bmp)$/i,
loader: 'next-image-loader',
issuer: { not: regexLikeCss },
dependency: { not: ['url'] },
},
]
: []),
].filter(Boolean),
},
plugins: [
Expand Down Expand Up @@ -1468,6 +1458,30 @@ export default async function getBaseWebpackConfig(
}
}

if (!config.images.disableStaticImages && isWebpack5) {
if (!webpackConfig.module) {
webpackConfig.module = { rules: [] }
}

const hasSvg = webpackConfig.module.rules.some(
(rule) =>
'test' in rule && rule.test instanceof RegExp && rule.test.test('.svg')
)

// Exclude svg if the user already defined it in custom
// webpack config such as `@svgr/webpack` plugin or
// the `babel-plugin-inline-react-svg` plugin.
webpackConfig.module.rules.push({
test: hasSvg
? /\.(png|jpg|jpeg|gif|webp|ico|bmp)$/i
: /\.(png|svg|jpg|jpeg|gif|webp|ico|bmp)$/i,
loader: 'next-image-loader',
dependency: { not: ['url'] },
// @ts-ignore
issuer: { not: regexLikeCss },
})
}

if (
config.experimental.craCompat &&
webpackConfig.module?.rules &&
Expand Down
10,446 changes: 10,221 additions & 225 deletions packages/next/compiled/babel/bundle.js

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion packages/next/types/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ declare module '*.png' {
}

declare module '*.svg' {
const content: StaticImageData
/**
* Use `any` to avoid conflicts with
* `@svgr/webpack` plugin or
* `babel-plugin-inline-react-svg` plugin.
*/
const content: any

export default content
}
Expand Down
10 changes: 10 additions & 0 deletions test/integration/image-component/svgo-webpack/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = {
webpack(config, options) {
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack'],
})

return config
},
}
5 changes: 5 additions & 0 deletions test/integration/image-component/svgo-webpack/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Icon from '../public/test.svg'

export default function Home() {
return <Icon />
}
10 changes: 10 additions & 0 deletions test/integration/image-component/svgo-webpack/public/test.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions test/integration/image-component/svgo-webpack/test/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* eslint-env jest */

import { join } from 'path'
import {
renderViaHTTP,
findPort,
launchApp,
nextBuild,
killApp,
} from 'next-test-utils'

jest.setTimeout(1000 * 60 * 2)

const appDir = join(__dirname, '..')
let appPort
let app
let devOutput

describe('svgo-webpack with Image Component', () => {
describe('next build', () => {
it('should not fail to build invalid usage of the Image component', async () => {
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
expect(stderr).toBeFalsy()
expect(code).toBe(0)
})
})

describe('next dev', () => {
beforeAll(async () => {
devOutput = { stdout: '', stderr: '' }
appPort = await findPort()
app = await launchApp(appDir, appPort, {
onStdout: (msg) => {
devOutput.stdout += msg
},
onStderr: (msg) => {
devOutput.stderr += msg
},
})
})
afterAll(() => killApp(app))

it('should print error when invalid Image usage', async () => {
await renderViaHTTP(appPort, '/', {})
expect(devOutput.stderr).toBeFalsy()
})
})
})
19 changes: 19 additions & 0 deletions test/integration/image-component/svgo-webpack/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
2 changes: 2 additions & 0 deletions test/integration/image-component/typescript/pages/valid.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react'
import Image from 'next/image'
import testTall from '../public/tall.png'
import svg from '../public/test.svg'

const Page = () => {
return (
Expand Down Expand Up @@ -73,6 +74,7 @@ const Page = () => {
src={testTall}
placeholder="blur"
/>
<Image id="object-src-with-svg" src={svg} />
<p id="stubtext">This is valid usage of the Image component</p>
</div>
)
Expand Down
10 changes: 10 additions & 0 deletions test/integration/image-component/typescript/public/test.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit c35b01a

Please sign in to comment.