Skip to content

Commit

Permalink
feat: add expo option (#289)
Browse files Browse the repository at this point in the history
Using an ejected ExpoKit after version 32 prevents `import Svg from
'react-native-svg';` from working, and also prevents the developer from adding
'react-native-svg' as a direct dependency since ExpoKit already provides the
library just behind the ExpoKit svg namespace. See
kristerkari/react-native-svg-transformer#13

This handles that case by allowing a `expo: true` flag to be passed instead of
the `native` option and will create the correct import statement to use Expo's
SVG library as well as prefix all non `<Svg />` components with `Svg.`.
  • Loading branch information
edpaget authored and gregberge committed Apr 10, 2019
1 parent f476c8e commit 978db3e
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 28 deletions.
54 changes: 34 additions & 20 deletions packages/babel-plugin-transform-react-native-svg/src/index.js
Expand Up @@ -23,21 +23,30 @@ const elementToComponent = {
image: 'Image',
}

const plugin = ({ types: t }) => {
const expoPrefix = (component, expo) => {
// Prefix with 'Svg.' in the case we're transforming for Expo
if (!expo) {
return component
}
return (component !== 'Svg' ? 'Svg.' : '') + component
}

const plugin = ({ types: t }, { expo }) => {
function replaceElement(path, state) {
const { name } = path.node.openingElement.name

// Replace element by react-native-svg components
const component = elementToComponent[name]

if (component) {
const prefixedComponent = expoPrefix(component, expo)
const openingElementName = path.get('openingElement.name')
openingElementName.replaceWith(t.jsxIdentifier(component))
openingElementName.replaceWith(t.jsxIdentifier(prefixedComponent))
if (path.has('closingElement')) {
const closingElementName = path.get('closingElement.name')
closingElementName.replaceWith(t.jsxIdentifier(component))
closingElementName.replaceWith(t.jsxIdentifier(prefixedComponent))
}
state.replacedComponents.add(component)
state.replacedComponents.add(prefixedComponent)
return
}

Expand Down Expand Up @@ -65,26 +74,31 @@ const plugin = ({ types: t }) => {

const importDeclarationVisitor = {
ImportDeclaration(path, state) {
if (!path.get('source').isStringLiteral({ value: 'react-native-svg' })) {
return
}

state.replacedComponents.forEach(component => {
if (
path
.get('specifiers')
.some(specifier =>
specifier.get('local').isIdentifier({ name: component }),
)
) {
return
}
if (path.get('source').isStringLiteral({ value: 'react-native-svg' })) {
state.replacedComponents.forEach(component => {
if (
path
.get('specifiers')
.some(specifier =>
specifier.get('local').isIdentifier({ name: component }),
)
) {
return
}

path.pushContainer(
'specifiers',
t.importSpecifier(t.identifier(component), t.identifier(component)),
)
})
} else if (path.get('source').isStringLiteral({ value: 'expo' })) {
path.pushContainer(
'specifiers',
t.importSpecifier(t.identifier(component), t.identifier(component)),
t.importSpecifier(t.identifier('Svg'), t.identifier('Svg')),
)
})
} else {
return
}

if (state.unsupportedComponents.size && !path.has('trailingComments')) {
const componentList = [...state.unsupportedComponents].join(', ')
Expand Down
13 changes: 13 additions & 0 deletions packages/babel-plugin-transform-react-native-svg/src/index.test.js
Expand Up @@ -25,6 +25,19 @@ describe('plugin', () => {
/* SVGR has dropped some elements not supported by react-native-svg: div */
<Svg><G /></Svg>;"
`)
})

it('should transform for expo import', () => {
const { code } = testPlugin(`import 'expo'; <svg><g /><div /></svg>;`, {
expo: true,
})

expect(code).toMatchInlineSnapshot(`
"import { Svg } from 'expo';
/* SVGR has dropped some elements not supported by react-native-svg: div */
<Svg><Svg.G /></Svg>;"
`)
})
})
30 changes: 30 additions & 0 deletions packages/babel-plugin-transform-svg-component/src/index.test.js
Expand Up @@ -20,6 +20,36 @@ describe('plugin', () => {
const SvgComponent = () => <svg><div /></svg>;
export default SvgComponent;"
`)
})

it('should add import for react-native-svg', () => {
const { code } = testPlugin('<svg><div /></svg>', {
state: { componentName: 'SvgComponent' },
native: true,
})
expect(code).toMatchInlineSnapshot(`
"import React from \\"react\\";
import Svg from \\"react-native-svg\\";
const SvgComponent = () => <svg><div /></svg>;
export default SvgComponent;"
`)
})

it('should import for expo', () => {
const { code } = testPlugin('<svg><div /></svg>', {
state: { componentName: 'SvgComponent' },
native: { expo: true },
})
expect(code).toMatchInlineSnapshot(`
"import React from \\"react\\";
import \\"expo\\";
const SvgComponent = () => <svg><div /></svg>;
export default SvgComponent;"
`)
})
Expand Down
16 changes: 10 additions & 6 deletions packages/babel-plugin-transform-svg-component/src/util.js
Expand Up @@ -47,12 +47,16 @@ export const getImport = ({ types: t }, opts) => {
]

if (opts.native) {
importDeclarations.push(
t.importDeclaration(
[t.importDefaultSpecifier(t.identifier('Svg'))],
t.stringLiteral('react-native-svg'),
),
)
if (opts.native.expo) {
importDeclarations.push(t.importDeclaration([], t.stringLiteral('expo')))
} else {
importDeclarations.push(
t.importDeclaration(
[t.importDefaultSpecifier(t.identifier('Svg'))],
t.stringLiteral('react-native-svg'),
),
)
}
}

return importDeclarations
Expand Down
6 changes: 5 additions & 1 deletion packages/babel-preset/src/index.js
Expand Up @@ -90,7 +90,11 @@ const plugin = (api, opts) => {
}

if (opts.native) {
plugins.push(transformReactNativeSVG)
if (opts.native.expo) {
plugins.push([transformReactNativeSVG, opts.native])
} else {
plugins.push(transformReactNativeSVG)
}
}

return { plugins }
Expand Down
18 changes: 18 additions & 0 deletions packages/babel-preset/src/index.test.js
Expand Up @@ -29,6 +29,24 @@ describe('preset', () => {
const SvgComponent = () => <svg foo=\\"bar\\" x={y} />;
export default SvgComponent;"
`)
})

it('should handle native expo option', () => {
expect(
testPreset('<svg><g><path d="M0 0h48v1H0z" /></g></svg>', {
native: { expo: true },
state: {
componentName: 'SvgComponent',
},
}),
).toMatchInlineSnapshot(`
"import React from \\"react\\";
import { Svg } from \\"expo\\";
const SvgComponent = () => <Svg><Svg.G><Svg.Path d=\\"M0 0h48v1H0z\\" /></Svg.G></Svg>;
export default SvgComponent;"
`)
})
Expand Down
22 changes: 22 additions & 0 deletions packages/core/src/__snapshots__/convert.test.js.snap
Expand Up @@ -106,6 +106,28 @@ export default SvgComponent
"
`;

exports[`convert config should support options { native: { expo: true } } 1`] = `
"import React from 'react'
import { Svg } from 'expo'
const SvgComponent = props => (
<Svg width={88} height={88} {...props}>
<Svg.G
stroke=\\"#063855\\"
strokeWidth={2}
fill=\\"none\\"
fillRule=\\"evenodd\\"
strokeLinecap=\\"square\\"
>
<Svg.Path d=\\"M51 37L37 51M51 51L37 37\\" />
</Svg.G>
</Svg>
)
export default SvgComponent
"
`;

exports[`convert config should support options { native: true, expandProps: false } 1`] = `
"import React from 'react'
import Svg, { G, Path } from 'react-native-svg'
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/convert.test.js
Expand Up @@ -293,6 +293,7 @@ describe('convert', () => {
{ expandProps: 'start' },
{ icon: true },
{ native: true },
{ native: { expo: true } },
{ native: true, icon: true },
{ native: true, expandProps: false },
{ native: true, ref: true },
Expand Down
4 changes: 3 additions & 1 deletion website/src/pages/docs/options.mdx
Expand Up @@ -47,9 +47,11 @@ inherits from text size.
Modify all SVG nodes with uppercase and use a specific template with
[`react-native-svg`](https://github.com/react-native-community/react-native-svg) imports. **All unsupported nodes will be removed.**

Override using the API with `native: { expo: true }` to template SVG nodes with the [ExpoKit SVG package](https://docs.expo.io/versions/latest/sdk/svg/). This is only necessary for 'ejected' ExpoKit projects where `import 'react-native-svg'` results in an error.

| Default | CLI Override | API Override |
| ------- | ------------ | ---------------- |
| `false` | `--native` | `native: <bool>` |
| `false` | `--native` | `native: <bool>` or `native: { expo: true }` |

## Dimensions

Expand Down

1 comment on commit 978db3e

@vercel
Copy link

@vercel vercel bot commented on 978db3e Apr 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully aliased the URL https://svgr-gedmarakux.now.sh to the following alias.

Please sign in to comment.