Skip to content

Commit 3c0b779

Browse files
committedNov 13, 2021
feat: allow to specify icon size
BREAKING CHANGE: using --icon as latest arg now requires "--" Fix #571
1 parent 6ba16a3 commit 3c0b779

File tree

11 files changed

+237
-46
lines changed

11 files changed

+237
-46
lines changed
 

‎packages/babel-plugin-svg-em-dimensions/README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ npm install --save-dev @svgr/babel-plugin-svg-em-dimensions
1212

1313
```json
1414
{
15-
"plugins": ["@svgr/babel-plugin-svg-em-dimensions"]
15+
"plugins": [
16+
["@svgr/babel-plugin-svg-em-dimensions", { "width": 24, "height": 24 }]
17+
]
1618
}
1719
```
1820

Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
11
import { transform } from '@babel/core'
2-
import plugin from '.'
2+
import plugin, { Options } from '.'
33

4-
const testPlugin = (code: string) => {
4+
const testPlugin = (code: string, options?: Options) => {
55
const result = transform(code, {
6-
plugins: ['@babel/plugin-syntax-jsx', plugin],
6+
plugins: ['@babel/plugin-syntax-jsx', [plugin, options]],
77
configFile: false,
88
})
99

1010
return result?.code
1111
}
1212

1313
describe('plugin', () => {
14-
it('should replace width / height attributes', () => {
14+
it('replaces width / height attributes', () => {
1515
expect(
1616
testPlugin('<svg foo="bar" width="100" height="200" />'),
1717
).toMatchInlineSnapshot(
1818
`"<svg foo=\\"bar\\" width=\\"1em\\" height=\\"1em\\" />;"`,
1919
)
2020
})
2121

22-
it('should add theme if they are not present', () => {
22+
it('adds theme if they are not present', () => {
2323
expect(testPlugin('<svg foo="bar" />')).toMatchInlineSnapshot(
2424
`"<svg foo=\\"bar\\" width=\\"1em\\" height=\\"1em\\" />;"`,
2525
)
2626
})
27+
28+
it('accepts numeric values', () => {
29+
expect(
30+
testPlugin('<svg foo="bar" />', { width: 24, height: 24 }),
31+
).toMatchInlineSnapshot(`"<svg foo=\\"bar\\" width={24} height={24} />;"`)
32+
})
33+
34+
it('accepts string values', () => {
35+
expect(
36+
testPlugin('<svg foo="bar" />', { width: '2em', height: '2em' }),
37+
).toMatchInlineSnapshot(
38+
`"<svg foo=\\"bar\\" width=\\"2em\\" height=\\"2em\\" />;"`,
39+
)
40+
})
2741
})

‎packages/babel-plugin-svg-em-dimensions/src/index.ts

+29-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
11
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2-
import { types as t, NodePath } from '@babel/core'
2+
import { types as t, NodePath, ConfigAPI } from '@babel/core'
33

44
const elements = ['svg', 'Svg']
5-
const value = t.stringLiteral('1em')
65

7-
const plugin = () => ({
6+
export interface Options {
7+
width: number | string
8+
height: number | string
9+
}
10+
11+
const getValue = (raw: undefined | number | string) => {
12+
if (raw === undefined) return t.stringLiteral('1em')
13+
switch (typeof raw) {
14+
case 'number':
15+
return t.jsxExpressionContainer(t.numericLiteral(raw))
16+
case 'string':
17+
return t.stringLiteral(raw)
18+
default:
19+
return t.stringLiteral('1em')
20+
}
21+
}
22+
23+
const plugin = (_: ConfigAPI, opts: Options) => ({
824
visitor: {
925
JSXOpeningElement(path: NodePath<t.JSXOpeningElement>) {
1026
if (
@@ -14,7 +30,11 @@ const plugin = () => ({
1430
)
1531
return
1632

17-
const requiredAttributes = ['width', 'height']
33+
const values = {
34+
width: getValue(opts.width),
35+
height: getValue(opts.height),
36+
}
37+
const requiredAttributes = Object.keys(values)
1838

1939
path.get('attributes').forEach((attributePath) => {
2040
if (!attributePath.isJSXAttribute()) return
@@ -25,14 +45,17 @@ const plugin = () => ({
2545
if (index === -1) return
2646

2747
const valuePath = attributePath.get('value')
28-
valuePath.replaceWith(value)
48+
valuePath.replaceWith(values[namePath.node.name as 'width' | 'height'])
2949
requiredAttributes.splice(index, 1)
3050
})
3151

3252
path.pushContainer(
3353
'attributes',
3454
requiredAttributes.map((attr) =>
35-
t.jsxAttribute(t.jsxIdentifier(attr), value),
55+
t.jsxAttribute(
56+
t.jsxIdentifier(attr),
57+
values[attr as 'width' | 'height'],
58+
),
3659
),
3760
)
3861
},

‎packages/babel-preset/src/index.test.ts

+39-5
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const testPreset = (code: string, options: Partial<Options>) => {
1717
}
1818

1919
describe('preset', () => {
20-
it('should handle svgProps', () => {
20+
it('handles svgProps', () => {
2121
expect(
2222
testPreset('<svg />', {
2323
svgProps: {
@@ -34,7 +34,7 @@ describe('preset', () => {
3434
`)
3535
})
3636

37-
it('should handle titleProp', () => {
37+
it('handles titleProp', () => {
3838
expect(
3939
testPreset('<svg></svg>', {
4040
titleProp: true,
@@ -50,7 +50,7 @@ describe('preset', () => {
5050
export default SvgComponent;"
5151
`)
5252
})
53-
it('should handle titleProp and fallback on existing title', () => {
53+
it('handles titleProp and fallback on existing title', () => {
5454
// testing when existing title has string as chilren
5555
expect(
5656
testPreset(`<svg><title>Hello</title></svg>`, {
@@ -83,7 +83,7 @@ describe('preset', () => {
8383
`)
8484
})
8585

86-
it('should handle replaceAttrValues', () => {
86+
it('handles replaceAttrValues', () => {
8787
expect(
8888
testPreset('<svg a="#000" b="#fff" />', {
8989
replaceAttrValues: {
@@ -100,7 +100,7 @@ describe('preset', () => {
100100
`)
101101
})
102102

103-
it('should handle expandProps & icon & dimensions', () => {
103+
it('handles expandProps & icon & dimensions', () => {
104104
expect(
105105
testPreset('<svg a="#000" b="#fff" />', {
106106
expandProps: 'end',
@@ -115,4 +115,38 @@ describe('preset', () => {
115115
export default SvgComponent;"
116116
`)
117117
})
118+
119+
it('handles custom icon size', () => {
120+
expect(
121+
testPreset('<svg a="#000" b="#fff" />', {
122+
expandProps: 'end',
123+
icon: 24,
124+
dimensions: true,
125+
}),
126+
).toMatchInlineSnapshot(`
127+
"import * as React from \\"react\\";
128+
129+
const SvgComponent = props => <svg a=\\"#000\\" b=\\"#fff\\" width={24} height={24} {...props} />;
130+
131+
export default SvgComponent;"
132+
`)
133+
})
134+
135+
it('defaults to 24 on native', () => {
136+
expect(
137+
testPreset('<svg a="#000" b="#fff" />', {
138+
expandProps: 'end',
139+
icon: true,
140+
native: true,
141+
dimensions: true,
142+
}),
143+
).toMatchInlineSnapshot(`
144+
"import * as React from \\"react\\";
145+
import Svg from \\"react-native-svg\\";
146+
147+
const SvgComponent = props => <Svg a=\\"#000\\" b=\\"#fff\\" width={24} height={24} {...props} />;
148+
149+
export default SvgComponent;"
150+
`)
151+
})
118152
})

‎packages/babel-preset/src/index.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export interface Options extends TransformOptions {
2020
titleProp?: boolean
2121
expandProps?: boolean | 'start' | 'end'
2222
dimensions?: boolean
23-
icon?: boolean
23+
icon?: boolean | string | number
2424
native?: boolean
2525
svgProps?: { [key: string]: string }
2626
replaceAttrValues?: { [key: string]: string }
@@ -96,7 +96,18 @@ const plugin = (_: ConfigAPI, opts: Options) => {
9696

9797
const plugins: any[] = [
9898
[transformSvgComponent, opts],
99-
...(opts.icon && opts.dimensions ? [svgEmDimensions] : []),
99+
...(opts.icon !== false && opts.dimensions
100+
? [
101+
[
102+
svgEmDimensions,
103+
opts.icon !== true
104+
? { width: opts.icon, height: opts.icon }
105+
: opts.native
106+
? { width: 24, height: 24 }
107+
: {},
108+
],
109+
]
110+
: []),
100111
[
101112
removeJSXAttribute,
102113
{ elements: ['svg', 'Svg'], attributes: toRemoveAttributes },

‎packages/cli/src/__snapshots__/index.test.ts.snap

+42-2
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,46 @@ export default SvgFile
203203
"
204204
`;
205205

206+
exports[`cli should support various args: --icon 2em 1`] = `
207+
"import * as React from 'react'
208+
209+
const SvgFile = (props) => (
210+
<svg
211+
width=\\"2em\\"
212+
height=\\"2em\\"
213+
viewBox=\\"0 0 48 1\\"
214+
xmlns=\\"http://www.w3.org/2000/svg\\"
215+
{...props}
216+
>
217+
<path d=\\"M0 0h48v1H0z\\" fill=\\"#063855\\" fillRule=\\"evenodd\\" />
218+
</svg>
219+
)
220+
221+
export default SvgFile
222+
223+
"
224+
`;
225+
226+
exports[`cli should support various args: --icon 24 1`] = `
227+
"import * as React from 'react'
228+
229+
const SvgFile = (props) => (
230+
<svg
231+
width={24}
232+
height={24}
233+
viewBox=\\"0 0 48 1\\"
234+
xmlns=\\"http://www.w3.org/2000/svg\\"
235+
{...props}
236+
>
237+
<path d=\\"M0 0h48v1H0z\\" fill=\\"#063855\\" fillRule=\\"evenodd\\" />
238+
</svg>
239+
)
240+
241+
export default SvgFile
242+
243+
"
244+
`;
245+
206246
exports[`cli should support various args: --jsx-runtime automatic 1`] = `
207247
"const SvgFile = (props) => (
208248
<svg width={48} height={1} xmlns=\\"http://www.w3.org/2000/svg\\" {...props}>
@@ -250,8 +290,8 @@ import Svg, { Path } from 'react-native-svg'
250290
251291
const SvgFile = (props) => (
252292
<Svg
253-
width=\\"1em\\"
254-
height=\\"1em\\"
293+
width={24}
294+
height={24}
255295
viewBox=\\"0 0 48 1\\"
256296
xmlns=\\"http://www.w3.org/2000/svg\\"
257297
{...props}

‎packages/cli/src/index.test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ describe('cli', () => {
119119
['--expand-props none'],
120120
['--expand-props start'],
121121
['--icon'],
122+
['--icon 24'],
123+
['--icon 2em'],
122124
['--native'],
123125
['--native --icon'],
124126
['--native --expand-props none'],
@@ -135,7 +137,7 @@ describe('cli', () => {
135137
])(
136138
'should support various args',
137139
async (args) => {
138-
const result = await cli(`${args} __fixtures__/simple/file.svg`)
140+
const result = await cli(`${args} -- __fixtures__/simple/file.svg`)
139141
expect(result).toMatchSnapshot(args)
140142
},
141143
10000,

‎packages/cli/src/index.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ const parseTemplate = (name: string) => (arg: string) => {
6868
}
6969
}
7070

71+
const parseIconSize = (arg: string) => {
72+
const num = Number(arg)
73+
return Number.isNaN(num) ? arg : num
74+
}
75+
7176
export interface Options extends Config {
7277
configFile?: string
7378
runtimeConfig?: boolean
@@ -100,7 +105,11 @@ program
100105
'--filename-case <case>',
101106
'specify filename case ("pascal", "kebab", "camel") (default: "pascal")',
102107
)
103-
.option('--icon', 'use "1em" as width and height')
108+
.option(
109+
'--icon [size]',
110+
'specify width and height (default to "1em" or 24dp (native))',
111+
parseIconSize,
112+
)
104113
.option(
105114
'--jsx-runtime <runtime>',
106115
'specify JSX runtime ("automatic", "classic", "classic-preact") (default: "classic")',

‎packages/core/src/__snapshots__/transform.test.ts.snap

+75-21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`convert config should support options {"dimensions":false} 1`] = `
3+
exports[`convert config accepts options {"dimensions":false} 1`] = `
44
"import * as React from 'react'
55
66
const SvgComponent = (props) => (
@@ -21,7 +21,7 @@ export default SvgComponent
2121
"
2222
`;
2323

24-
exports[`convert config should support options {"expandProps":"start"} 1`] = `
24+
exports[`convert config accepts options {"expandProps":"start"} 1`] = `
2525
"import * as React from 'react'
2626
2727
const SvgComponent = (props) => (
@@ -42,7 +42,7 @@ export default SvgComponent
4242
"
4343
`;
4444

45-
exports[`convert config should support options {"expandProps":false} 1`] = `
45+
exports[`convert config accepts options {"expandProps":false} 1`] = `
4646
"import * as React from 'react'
4747
4848
const SvgComponent = () => (
@@ -63,7 +63,7 @@ export default SvgComponent
6363
"
6464
`;
6565

66-
exports[`convert config should support options {"exportType":"named"} 1`] = `
66+
exports[`convert config accepts options {"exportType":"named"} 1`] = `
6767
"import * as React from 'react'
6868
6969
const SvgComponent = (props) => (
@@ -84,7 +84,61 @@ export { SvgComponent as ReactComponent }
8484
"
8585
`;
8686

87-
exports[`convert config should support options {"icon":true} 1`] = `
87+
exports[`convert config accepts options {"icon":"2em"} 1`] = `
88+
"import * as React from 'react'
89+
90+
const SvgComponent = (props) => (
91+
<svg
92+
width=\\"2em\\"
93+
height=\\"2em\\"
94+
viewBox=\\"0 0 88 88\\"
95+
xmlns=\\"http://www.w3.org/2000/svg\\"
96+
{...props}
97+
>
98+
<g
99+
stroke=\\"#063855\\"
100+
strokeWidth={2}
101+
fill=\\"none\\"
102+
fillRule=\\"evenodd\\"
103+
strokeLinecap=\\"square\\"
104+
>
105+
<path d=\\"M51 37 37 51M51 51 37 37\\" />
106+
</g>
107+
</svg>
108+
)
109+
110+
export default SvgComponent
111+
"
112+
`;
113+
114+
exports[`convert config accepts options {"icon":24} 1`] = `
115+
"import * as React from 'react'
116+
117+
const SvgComponent = (props) => (
118+
<svg
119+
width={24}
120+
height={24}
121+
viewBox=\\"0 0 88 88\\"
122+
xmlns=\\"http://www.w3.org/2000/svg\\"
123+
{...props}
124+
>
125+
<g
126+
stroke=\\"#063855\\"
127+
strokeWidth={2}
128+
fill=\\"none\\"
129+
fillRule=\\"evenodd\\"
130+
strokeLinecap=\\"square\\"
131+
>
132+
<path d=\\"M51 37 37 51M51 51 37 37\\" />
133+
</g>
134+
</svg>
135+
)
136+
137+
export default SvgComponent
138+
"
139+
`;
140+
141+
exports[`convert config accepts options {"icon":true} 1`] = `
88142
"import * as React from 'react'
89143
90144
const SvgComponent = (props) => (
@@ -111,7 +165,7 @@ export default SvgComponent
111165
"
112166
`;
113167

114-
exports[`convert config should support options {"memo":true} 1`] = `
168+
exports[`convert config accepts options {"memo":true} 1`] = `
115169
"import * as React from 'react'
116170
import { memo } from 'react'
117171
@@ -134,7 +188,7 @@ export default Memo
134188
"
135189
`;
136190

137-
exports[`convert config should support options {"namedExport":"Component","state":{"caller":{"previousExport":"export default \\"logo.svg\\";"}}} 1`] = `
191+
exports[`convert config accepts options {"namedExport":"Component","state":{"caller":{"previousExport":"export default \\"logo.svg\\";"}}} 1`] = `
138192
"import * as React from 'react'
139193
140194
const SvgComponent = (props) => (
@@ -155,7 +209,7 @@ export default SvgComponent
155209
"
156210
`;
157211

158-
exports[`convert config should support options {"native":true,"expandProps":false} 1`] = `
212+
exports[`convert config accepts options {"native":true,"expandProps":false} 1`] = `
159213
"import * as React from 'react'
160214
import Svg, { G, Path } from 'react-native-svg'
161215
@@ -177,14 +231,14 @@ export default SvgComponent
177231
"
178232
`;
179233

180-
exports[`convert config should support options {"native":true,"icon":true} 1`] = `
234+
exports[`convert config accepts options {"native":true,"icon":true} 1`] = `
181235
"import * as React from 'react'
182236
import Svg, { G, Path } from 'react-native-svg'
183237
184238
const SvgComponent = (props) => (
185239
<Svg
186-
width=\\"1em\\"
187-
height=\\"1em\\"
240+
width={24}
241+
height={24}
188242
viewBox=\\"0 0 88 88\\"
189243
xmlns=\\"http://www.w3.org/2000/svg\\"
190244
{...props}
@@ -205,7 +259,7 @@ export default SvgComponent
205259
"
206260
`;
207261

208-
exports[`convert config should support options {"native":true,"ref":true} 1`] = `
262+
exports[`convert config accepts options {"native":true,"ref":true} 1`] = `
209263
"import * as React from 'react'
210264
import Svg, { G, Path } from 'react-native-svg'
211265
import { forwardRef } from 'react'
@@ -235,7 +289,7 @@ export default ForwardRef
235289
"
236290
`;
237291

238-
exports[`convert config should support options {"native":true} 1`] = `
292+
exports[`convert config accepts options {"native":true} 1`] = `
239293
"import * as React from 'react'
240294
import Svg, { G, Path } from 'react-native-svg'
241295
@@ -257,15 +311,15 @@ export default SvgComponent
257311
"
258312
`;
259313

260-
exports[`convert config should support options {"prettier":false} 1`] = `
314+
exports[`convert config accepts options {"prettier":false} 1`] = `
261315
"import * as React from \\"react\\";
262316
263317
const SvgComponent = props => <svg width={88} height={88} xmlns=\\"http://www.w3.org/2000/svg\\" {...props}><g stroke=\\"#063855\\" strokeWidth={2} fill=\\"none\\" fillRule=\\"evenodd\\" strokeLinecap=\\"square\\"><path d=\\"M51 37 37 51M51 51 37 37\\" /></g></svg>;
264318
265319
export default SvgComponent;"
266320
`;
267321

268-
exports[`convert config should support options {"ref":true} 1`] = `
322+
exports[`convert config accepts options {"ref":true} 1`] = `
269323
"import * as React from 'react'
270324
import { forwardRef } from 'react'
271325
@@ -294,7 +348,7 @@ export default ForwardRef
294348
"
295349
`;
296350

297-
exports[`convert config should support options {"replaceAttrValues":{"none":"{black}"}} 1`] = `
351+
exports[`convert config accepts options {"replaceAttrValues":{"none":"{black}"}} 1`] = `
298352
"import * as React from 'react'
299353
300354
const SvgComponent = (props) => (
@@ -315,7 +369,7 @@ export default SvgComponent
315369
"
316370
`;
317371

318-
exports[`convert config should support options {"replaceAttrValues":{"none":"black"}} 1`] = `
372+
exports[`convert config accepts options {"replaceAttrValues":{"none":"black"}} 1`] = `
319373
"import * as React from 'react'
320374
321375
const SvgComponent = (props) => (
@@ -336,7 +390,7 @@ export default SvgComponent
336390
"
337391
`;
338392

339-
exports[`convert config should support options {"svgProps":{"a":"b","b":"{props.b}"}} 1`] = `
393+
exports[`convert config accepts options {"svgProps":{"a":"b","b":"{props.b}"}} 1`] = `
340394
"import * as React from 'react'
341395
342396
const SvgComponent = (props) => (
@@ -364,7 +418,7 @@ export default SvgComponent
364418
"
365419
`;
366420

367-
exports[`convert config should support options {"svgo":false} 1`] = `
421+
exports[`convert config accepts options {"svgo":false} 1`] = `
368422
"import * as React from 'react'
369423
370424
const SvgComponent = (props) => (
@@ -399,7 +453,7 @@ export default SvgComponent
399453
"
400454
`;
401455

402-
exports[`convert config should support options {"titleProp":true} 1`] = `
456+
exports[`convert config accepts options {"titleProp":true} 1`] = `
403457
"import * as React from 'react'
404458
405459
const SvgComponent = ({ title, titleId, ...props }) => (
@@ -427,7 +481,7 @@ export default SvgComponent
427481
"
428482
`;
429483

430-
exports[`convert config should support options {} 1`] = `
484+
exports[`convert config accepts options {} 1`] = `
431485
"const noop = () => null
432486
433487
export default noop

‎packages/core/src/config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export interface Config {
1111
titleProp?: boolean
1212
expandProps?: boolean | 'start' | 'end'
1313
dimensions?: boolean
14-
icon?: boolean
14+
icon?: boolean | string | number
1515
native?: boolean
1616
svgProps?: {
1717
[key: string]: string

‎packages/core/src/transform.test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ describe('convert', () => {
300300
{ expandProps: false },
301301
{ expandProps: 'start' },
302302
{ icon: true },
303+
{ icon: 24 },
304+
{ icon: '2em' },
303305
{ native: true },
304306
{ native: true, icon: true },
305307
{ native: true, expandProps: false },
@@ -323,7 +325,7 @@ describe('convert', () => {
323325
{ exportType: 'named' },
324326
]
325327

326-
test.each(configs)('should support options %j', async (config) => {
328+
test.each(configs)('accepts options %j', async (config) => {
327329
const result = await convertWithAllPlugins(svgBaseCode, config)
328330
expect(result).toMatchSnapshot()
329331
})

0 commit comments

Comments
 (0)
Please sign in to comment.