Skip to content

Commit 3c0b779

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

11 files changed

Lines changed: 237 additions & 46 deletions

File tree

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

Lines changed: 3 additions & 1 deletion
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

Lines changed: 19 additions & 5 deletions
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

Lines changed: 29 additions & 6 deletions
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

Lines changed: 39 additions & 5 deletions
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

Lines changed: 13 additions & 2 deletions
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

Lines changed: 42 additions & 2 deletions
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

Lines changed: 3 additions & 1 deletion
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

Lines changed: 10 additions & 1 deletion
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")',

0 commit comments

Comments
 (0)