Skip to content

Commit f3dfe22

Browse files
ndcunninghamFrozenPandaz
authored andcommitted
feat(react): Add SvgOptions for NxReactWebpackPlugin and WithNx (#23283)
This PR adds the ability to now override our svg options by providing them either using `NxReactWebpackPlugin` for react apps or `withNx` for Next.js apps ``` new NxReactWebpackPlugin({ svgr: { svgo: true, titleProp: true, ref: true, } }), ``` This now gives you control on customizing how the svg is handled. Should you need to enable svgo you can provide the config using `svgr.config.js` https://react-svgr.com/docs/options/#svgo closes: #9487 (cherry picked from commit 9cd0b42)
1 parent 4d03625 commit f3dfe22

File tree

5 files changed

+182
-15
lines changed

5 files changed

+182
-15
lines changed

e2e/next/src/next-svgr.test.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,67 @@ describe('NextJs SVGR support', () => {
7777
);
7878
expect(svgFile).toBeTruthy();
7979
});
80+
81+
it('should allow both SVG asset and SVGR component to be used (using SvgrOptions)', () => {
82+
const appName = uniq('app');
83+
runCLI(
84+
`generate @nx/next:app ${appName} --no-interactive --appDir=true --src=true`
85+
);
86+
createFile(
87+
`apps/${appName}/src/app/nx.svg`,
88+
`
89+
<svg version="1.1" width="300" height="200" xmlns="http://www.w3.org/2000/svg">
90+
<text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG for app</text>
91+
</svg>
92+
`
93+
);
94+
updateFile(
95+
`apps/${appName}/src/app/page.tsx`,
96+
`
97+
import Image from 'next/image';
98+
import svgImg, { ReactComponent as Logo } from './nx.svg';
99+
export default async function Index() {
100+
return (
101+
<>
102+
<Image src={svgImg} alt="Alt for SVG img tag" />
103+
<Logo />
104+
</>
105+
);
106+
}
107+
`
108+
);
109+
updateFile(
110+
`apps/${appName}/next.config.js`,
111+
`
112+
const { composePlugins, withNx } = require('@nx/next');
113+
const nextConfig = {
114+
nx: {
115+
svgr: {
116+
svgo: false,
117+
titleProp: true,
118+
ref: true,
119+
},
120+
},
121+
};
122+
const plugins = [
123+
withNx,
124+
];
125+
module.exports = composePlugins(...plugins)(nextConfig);
126+
`
127+
);
128+
129+
runCLI(`build ${appName}`);
130+
131+
const pageFile = readFile(`apps/${appName}/.next/server/app/page.js`);
132+
const svgFile = listFiles(`apps/${appName}/.next/static/media`).find((f) =>
133+
/nx\.[a-z0-9]+\.svg$/.test(f)
134+
);
135+
expect(`apps/${appName}/.next/static/chunks/app/${pageFile}`).toMatch(
136+
/SVG for app/
137+
);
138+
expect(`apps/${appName}/.next/static/chunks/app/${pageFile}`).toMatch(
139+
/Alt for SVG img tag/
140+
);
141+
expect(svgFile).toBeTruthy();
142+
});
80143
});

e2e/react/src/react-webpack.test.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,87 @@ describe('Build React applications and libraries with Vite', () => {
6262
expect(mainContent).toMatch(/Alt for SVG img tag/);
6363
expect(svgFile).toBeTruthy();
6464
}, 300_000);
65+
66+
it('should support SVGR and SVG asset in the same project (using SvgrOptions)', async () => {
67+
const appName = uniq('app');
68+
69+
runCLI(
70+
`generate @nx/react:app ${appName} --bundler=webpack --compiler=babel --unitTestRunner=none --no-interactive`
71+
);
72+
createFile(
73+
`apps/${appName}/src/app/nx.svg`,
74+
`
75+
<svg version="1.1" width="300" height="200" xmlns="http://www.w3.org/2000/svg">
76+
<text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG for app</text>
77+
</svg>
78+
`
79+
);
80+
updateFile(
81+
`apps/${appName}/src/app/app.tsx`,
82+
`
83+
import svgImg, { ReactComponent as Logo } from './nx.svg';
84+
export function App() {
85+
return (
86+
<>
87+
<img src={svgImg} alt="Alt for SVG img tag" />
88+
<Logo />
89+
</>
90+
);
91+
}
92+
export default App;
93+
`
94+
);
95+
96+
updateFile(
97+
`apps/${appName}/webpack.config.js`,
98+
`
99+
const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin');
100+
const { NxReactWebpackPlugin } = require('@nx/react/webpack-plugin');
101+
const { join } = require('path');
102+
103+
module.exports = {
104+
output: {
105+
path: join(__dirname, '../../dist/apps/${appName}'),
106+
},
107+
devServer: {
108+
port: 4201,
109+
},
110+
plugins: [
111+
new NxAppWebpackPlugin({
112+
tsConfig: './tsconfig.app.json',
113+
compiler: 'babel',
114+
main: './src/main.tsx',
115+
index: './src/index.html',
116+
baseHref: '/',
117+
assets: ['./src/favicon.ico', './src/assets'],
118+
styles: ['./src/styles.css'],
119+
outputHashing: process.env['NODE_ENV'] === 'production' ? 'all' : 'none',
120+
optimization: process.env['NODE_ENV'] === 'production',
121+
}),
122+
new NxReactWebpackPlugin({
123+
// Uncomment this line if you don't want to use SVGR
124+
// See: https://react-svgr.com/
125+
svgr: {
126+
svgo: false,
127+
titleProp: true,
128+
ref: true,
129+
},
130+
}),
131+
],
132+
};
133+
134+
135+
`
136+
);
137+
138+
await runCLIAsync(`build ${appName}`);
139+
140+
const outFiles = listFiles(`dist/apps/${appName}`);
141+
const mainFile = outFiles.find((f) => f.startsWith('main.'));
142+
const mainContent = readFile(`dist/apps/${appName}/${mainFile}`);
143+
const svgFile = outFiles.find((f) => f.endsWith('.svg'));
144+
expect(mainContent).toMatch(/SVG for app/);
145+
expect(mainContent).toMatch(/Alt for SVG img tag/);
146+
expect(svgFile).toBeTruthy();
147+
}, 300_000);
65148
});

packages/next/plugins/with-nx.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,15 @@ import {
1212
type Target,
1313
} from '@nx/devkit';
1414

15+
export interface SvgrOptions {
16+
svgo?: boolean;
17+
titleProp?: boolean;
18+
ref?: boolean;
19+
}
20+
1521
export interface WithNxOptions extends NextConfig {
1622
nx?: {
17-
svgr?: boolean;
23+
svgr?: boolean | SvgrOptions;
1824
babelUpwardRootMode?: boolean;
1925
};
2026
}
@@ -331,7 +337,15 @@ export function getNextConfig(
331337
*/
332338

333339
// Default SVGR support to be on for projects.
334-
if (nx?.svgr !== false) {
340+
if (nx?.svgr !== false || typeof nx?.svgr === 'object') {
341+
const defaultSvgrOptions = {
342+
svgo: false,
343+
titleProp: true,
344+
ref: true,
345+
};
346+
347+
const svgrOptions =
348+
typeof nx?.svgr === 'object' ? nx.svgr : defaultSvgrOptions;
335349
// TODO(v20): Remove file-loader and use `?react` querystring to differentiate between asset and SVGR.
336350
// It should be:
337351
// use: [{
@@ -365,11 +379,7 @@ export function getNextConfig(
365379
use: [
366380
{
367381
loader: require.resolve('@svgr/webpack'),
368-
options: {
369-
svgo: false,
370-
titleProp: true,
371-
ref: true,
372-
},
382+
options: svgrOptions,
373383
},
374384
{
375385
loader: require.resolve('file-loader'),

packages/react/plugins/nx-react-webpack-plugin/lib/apply-react-config.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
import { Configuration, WebpackOptionsNormalized } from 'webpack';
2+
import { SvgrOptions } from '../../with-react';
23

34
export function applyReactConfig(
4-
options: { svgr?: boolean },
5+
options: { svgr?: boolean | SvgrOptions },
56
config: Partial<WebpackOptionsNormalized | Configuration> = {}
67
): void {
78
if (!process.env['NX_TASK_TARGET_PROJECT']) return;
89

910
addHotReload(config);
1011

11-
if (options.svgr !== false) {
12+
if (options.svgr !== false || typeof options.svgr === 'object') {
1213
removeSvgLoaderIfPresent(config);
1314

15+
const defaultSvgrOptions = {
16+
svgo: false,
17+
titleProp: true,
18+
ref: true,
19+
};
20+
21+
const svgrOptions =
22+
typeof options.svgr === 'object' ? options.svgr : defaultSvgrOptions;
23+
1424
// TODO(v20): Remove file-loader and use `?react` querystring to differentiate between asset and SVGR.
1525
// It should be:
1626
// use: [{
@@ -37,11 +47,7 @@ export function applyReactConfig(
3747
use: [
3848
{
3949
loader: require.resolve('@svgr/webpack'),
40-
options: {
41-
svgo: false,
42-
titleProp: true,
43-
ref: true,
44-
},
50+
options: svgrOptions,
4551
},
4652
{
4753
loader: require.resolve('file-loader'),

packages/react/plugins/with-react.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@ import { applyReactConfig } from './nx-react-webpack-plugin/lib/apply-react-conf
44

55
const processed = new Set();
66

7+
export interface SvgrOptions {
8+
svgo?: boolean;
9+
titleProp?: boolean;
10+
ref?: boolean;
11+
}
712
export interface WithReactOptions extends WithWebOptions {
8-
svgr?: false;
13+
svgr?: boolean | SvgrOptions;
914
}
1015

1116
/**

0 commit comments

Comments
 (0)