Skip to content

Commit

Permalink
feat: allow to specify icon size
Browse files Browse the repository at this point in the history
BREAKING CHANGE: using --icon as latest arg now requires "--"

Fix #571
  • Loading branch information
gregberge committed Nov 13, 2021
1 parent 6ba16a3 commit 3c0b779
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 46 deletions.
4 changes: 3 additions & 1 deletion packages/babel-plugin-svg-em-dimensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ npm install --save-dev @svgr/babel-plugin-svg-em-dimensions

```json
{
"plugins": ["@svgr/babel-plugin-svg-em-dimensions"]
"plugins": [
["@svgr/babel-plugin-svg-em-dimensions", { "width": 24, "height": 24 }]
]
}
```

Expand Down
24 changes: 19 additions & 5 deletions packages/babel-plugin-svg-em-dimensions/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,41 @@
import { transform } from '@babel/core'
import plugin from '.'
import plugin, { Options } from '.'

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

return result?.code
}

describe('plugin', () => {
it('should replace width / height attributes', () => {
it('replaces width / height attributes', () => {
expect(
testPlugin('<svg foo="bar" width="100" height="200" />'),
).toMatchInlineSnapshot(
`"<svg foo=\\"bar\\" width=\\"1em\\" height=\\"1em\\" />;"`,
)
})

it('should add theme if they are not present', () => {
it('adds theme if they are not present', () => {
expect(testPlugin('<svg foo="bar" />')).toMatchInlineSnapshot(
`"<svg foo=\\"bar\\" width=\\"1em\\" height=\\"1em\\" />;"`,
)
})

it('accepts numeric values', () => {
expect(
testPlugin('<svg foo="bar" />', { width: 24, height: 24 }),
).toMatchInlineSnapshot(`"<svg foo=\\"bar\\" width={24} height={24} />;"`)
})

it('accepts string values', () => {
expect(
testPlugin('<svg foo="bar" />', { width: '2em', height: '2em' }),
).toMatchInlineSnapshot(
`"<svg foo=\\"bar\\" width=\\"2em\\" height=\\"2em\\" />;"`,
)
})
})
35 changes: 29 additions & 6 deletions packages/babel-plugin-svg-em-dimensions/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { types as t, NodePath } from '@babel/core'
import { types as t, NodePath, ConfigAPI } from '@babel/core'

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

const plugin = () => ({
export interface Options {
width: number | string
height: number | string
}

const getValue = (raw: undefined | number | string) => {
if (raw === undefined) return t.stringLiteral('1em')
switch (typeof raw) {
case 'number':
return t.jsxExpressionContainer(t.numericLiteral(raw))
case 'string':
return t.stringLiteral(raw)
default:
return t.stringLiteral('1em')
}
}

const plugin = (_: ConfigAPI, opts: Options) => ({
visitor: {
JSXOpeningElement(path: NodePath<t.JSXOpeningElement>) {
if (
Expand All @@ -14,7 +30,11 @@ const plugin = () => ({
)
return

const requiredAttributes = ['width', 'height']
const values = {
width: getValue(opts.width),
height: getValue(opts.height),
}
const requiredAttributes = Object.keys(values)

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

const valuePath = attributePath.get('value')
valuePath.replaceWith(value)
valuePath.replaceWith(values[namePath.node.name as 'width' | 'height'])
requiredAttributes.splice(index, 1)
})

path.pushContainer(
'attributes',
requiredAttributes.map((attr) =>
t.jsxAttribute(t.jsxIdentifier(attr), value),
t.jsxAttribute(
t.jsxIdentifier(attr),
values[attr as 'width' | 'height'],
),
),
)
},
Expand Down
44 changes: 39 additions & 5 deletions packages/babel-preset/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const testPreset = (code: string, options: Partial<Options>) => {
}

describe('preset', () => {
it('should handle svgProps', () => {
it('handles svgProps', () => {
expect(
testPreset('<svg />', {
svgProps: {
Expand All @@ -34,7 +34,7 @@ describe('preset', () => {
`)
})

it('should handle titleProp', () => {
it('handles titleProp', () => {
expect(
testPreset('<svg></svg>', {
titleProp: true,
Expand All @@ -50,7 +50,7 @@ describe('preset', () => {
export default SvgComponent;"
`)
})
it('should handle titleProp and fallback on existing title', () => {
it('handles titleProp and fallback on existing title', () => {
// testing when existing title has string as chilren
expect(
testPreset(`<svg><title>Hello</title></svg>`, {
Expand Down Expand Up @@ -83,7 +83,7 @@ describe('preset', () => {
`)
})

it('should handle replaceAttrValues', () => {
it('handles replaceAttrValues', () => {
expect(
testPreset('<svg a="#000" b="#fff" />', {
replaceAttrValues: {
Expand All @@ -100,7 +100,7 @@ describe('preset', () => {
`)
})

it('should handle expandProps & icon & dimensions', () => {
it('handles expandProps & icon & dimensions', () => {
expect(
testPreset('<svg a="#000" b="#fff" />', {
expandProps: 'end',
Expand All @@ -115,4 +115,38 @@ describe('preset', () => {
export default SvgComponent;"
`)
})

it('handles custom icon size', () => {
expect(
testPreset('<svg a="#000" b="#fff" />', {
expandProps: 'end',
icon: 24,
dimensions: true,
}),
).toMatchInlineSnapshot(`
"import * as React from \\"react\\";
const SvgComponent = props => <svg a=\\"#000\\" b=\\"#fff\\" width={24} height={24} {...props} />;
export default SvgComponent;"
`)
})

it('defaults to 24 on native', () => {
expect(
testPreset('<svg a="#000" b="#fff" />', {
expandProps: 'end',
icon: true,
native: true,
dimensions: true,
}),
).toMatchInlineSnapshot(`
"import * as React from \\"react\\";
import Svg from \\"react-native-svg\\";
const SvgComponent = props => <Svg a=\\"#000\\" b=\\"#fff\\" width={24} height={24} {...props} />;
export default SvgComponent;"
`)
})
})
15 changes: 13 additions & 2 deletions packages/babel-preset/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface Options extends TransformOptions {
titleProp?: boolean
expandProps?: boolean | 'start' | 'end'
dimensions?: boolean
icon?: boolean
icon?: boolean | string | number
native?: boolean
svgProps?: { [key: string]: string }
replaceAttrValues?: { [key: string]: string }
Expand Down Expand Up @@ -96,7 +96,18 @@ const plugin = (_: ConfigAPI, opts: Options) => {

const plugins: any[] = [
[transformSvgComponent, opts],
...(opts.icon && opts.dimensions ? [svgEmDimensions] : []),
...(opts.icon !== false && opts.dimensions
? [
[
svgEmDimensions,
opts.icon !== true
? { width: opts.icon, height: opts.icon }
: opts.native
? { width: 24, height: 24 }
: {},
],
]
: []),
[
removeJSXAttribute,
{ elements: ['svg', 'Svg'], attributes: toRemoveAttributes },
Expand Down
44 changes: 42 additions & 2 deletions packages/cli/src/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,46 @@ export default SvgFile
"
`;

exports[`cli should support various args: --icon 2em 1`] = `
"import * as React from 'react'
const SvgFile = (props) => (
<svg
width=\\"2em\\"
height=\\"2em\\"
viewBox=\\"0 0 48 1\\"
xmlns=\\"http://www.w3.org/2000/svg\\"
{...props}
>
<path d=\\"M0 0h48v1H0z\\" fill=\\"#063855\\" fillRule=\\"evenodd\\" />
</svg>
)
export default SvgFile
"
`;

exports[`cli should support various args: --icon 24 1`] = `
"import * as React from 'react'
const SvgFile = (props) => (
<svg
width={24}
height={24}
viewBox=\\"0 0 48 1\\"
xmlns=\\"http://www.w3.org/2000/svg\\"
{...props}
>
<path d=\\"M0 0h48v1H0z\\" fill=\\"#063855\\" fillRule=\\"evenodd\\" />
</svg>
)
export default SvgFile
"
`;

exports[`cli should support various args: --jsx-runtime automatic 1`] = `
"const SvgFile = (props) => (
<svg width={48} height={1} xmlns=\\"http://www.w3.org/2000/svg\\" {...props}>
Expand Down Expand Up @@ -250,8 +290,8 @@ import Svg, { Path } from 'react-native-svg'
const SvgFile = (props) => (
<Svg
width=\\"1em\\"
height=\\"1em\\"
width={24}
height={24}
viewBox=\\"0 0 48 1\\"
xmlns=\\"http://www.w3.org/2000/svg\\"
{...props}
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ describe('cli', () => {
['--expand-props none'],
['--expand-props start'],
['--icon'],
['--icon 24'],
['--icon 2em'],
['--native'],
['--native --icon'],
['--native --expand-props none'],
Expand All @@ -135,7 +137,7 @@ describe('cli', () => {
])(
'should support various args',
async (args) => {
const result = await cli(`${args} __fixtures__/simple/file.svg`)
const result = await cli(`${args} -- __fixtures__/simple/file.svg`)
expect(result).toMatchSnapshot(args)
},
10000,
Expand Down
11 changes: 10 additions & 1 deletion packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ const parseTemplate = (name: string) => (arg: string) => {
}
}

const parseIconSize = (arg: string) => {
const num = Number(arg)
return Number.isNaN(num) ? arg : num
}

export interface Options extends Config {
configFile?: string
runtimeConfig?: boolean
Expand Down Expand Up @@ -100,7 +105,11 @@ program
'--filename-case <case>',
'specify filename case ("pascal", "kebab", "camel") (default: "pascal")',
)
.option('--icon', 'use "1em" as width and height')
.option(
'--icon [size]',
'specify width and height (default to "1em" or 24dp (native))',
parseIconSize,
)
.option(
'--jsx-runtime <runtime>',
'specify JSX runtime ("automatic", "classic", "classic-preact") (default: "classic")',
Expand Down
Loading

0 comments on commit 3c0b779

Please sign in to comment.