Skip to content

Commit 978db3e

Browse files
edpagetgregberge
authored andcommitted
feat: add expo option (#289)
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.`.
1 parent f476c8e commit 978db3e

9 files changed

Lines changed: 136 additions & 28 deletions

File tree

packages/babel-plugin-transform-react-native-svg/src/index.js

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,30 @@ const elementToComponent = {
2323
image: 'Image',
2424
}
2525

26-
const plugin = ({ types: t }) => {
26+
const expoPrefix = (component, expo) => {
27+
// Prefix with 'Svg.' in the case we're transforming for Expo
28+
if (!expo) {
29+
return component
30+
}
31+
return (component !== 'Svg' ? 'Svg.' : '') + component
32+
}
33+
34+
const plugin = ({ types: t }, { expo }) => {
2735
function replaceElement(path, state) {
2836
const { name } = path.node.openingElement.name
2937

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

3341
if (component) {
42+
const prefixedComponent = expoPrefix(component, expo)
3443
const openingElementName = path.get('openingElement.name')
35-
openingElementName.replaceWith(t.jsxIdentifier(component))
44+
openingElementName.replaceWith(t.jsxIdentifier(prefixedComponent))
3645
if (path.has('closingElement')) {
3746
const closingElementName = path.get('closingElement.name')
38-
closingElementName.replaceWith(t.jsxIdentifier(component))
47+
closingElementName.replaceWith(t.jsxIdentifier(prefixedComponent))
3948
}
40-
state.replacedComponents.add(component)
49+
state.replacedComponents.add(prefixedComponent)
4150
return
4251
}
4352

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

6675
const importDeclarationVisitor = {
6776
ImportDeclaration(path, state) {
68-
if (!path.get('source').isStringLiteral({ value: 'react-native-svg' })) {
69-
return
70-
}
71-
72-
state.replacedComponents.forEach(component => {
73-
if (
74-
path
75-
.get('specifiers')
76-
.some(specifier =>
77-
specifier.get('local').isIdentifier({ name: component }),
78-
)
79-
) {
80-
return
81-
}
77+
if (path.get('source').isStringLiteral({ value: 'react-native-svg' })) {
78+
state.replacedComponents.forEach(component => {
79+
if (
80+
path
81+
.get('specifiers')
82+
.some(specifier =>
83+
specifier.get('local').isIdentifier({ name: component }),
84+
)
85+
) {
86+
return
87+
}
8288

89+
path.pushContainer(
90+
'specifiers',
91+
t.importSpecifier(t.identifier(component), t.identifier(component)),
92+
)
93+
})
94+
} else if (path.get('source').isStringLiteral({ value: 'expo' })) {
8395
path.pushContainer(
8496
'specifiers',
85-
t.importSpecifier(t.identifier(component), t.identifier(component)),
97+
t.importSpecifier(t.identifier('Svg'), t.identifier('Svg')),
8698
)
87-
})
99+
} else {
100+
return
101+
}
88102

89103
if (state.unsupportedComponents.size && !path.has('trailingComments')) {
90104
const componentList = [...state.unsupportedComponents].join(', ')

packages/babel-plugin-transform-react-native-svg/src/index.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@ describe('plugin', () => {
2525
/* SVGR has dropped some elements not supported by react-native-svg: div */
2626
2727
<Svg><G /></Svg>;"
28+
`)
29+
})
30+
31+
it('should transform for expo import', () => {
32+
const { code } = testPlugin(`import 'expo'; <svg><g /><div /></svg>;`, {
33+
expo: true,
34+
})
35+
36+
expect(code).toMatchInlineSnapshot(`
37+
"import { Svg } from 'expo';
38+
/* SVGR has dropped some elements not supported by react-native-svg: div */
39+
40+
<Svg><Svg.G /></Svg>;"
2841
`)
2942
})
3043
})

packages/babel-plugin-transform-svg-component/src/index.test.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,36 @@ describe('plugin', () => {
2020
2121
const SvgComponent = () => <svg><div /></svg>;
2222
23+
export default SvgComponent;"
24+
`)
25+
})
26+
27+
it('should add import for react-native-svg', () => {
28+
const { code } = testPlugin('<svg><div /></svg>', {
29+
state: { componentName: 'SvgComponent' },
30+
native: true,
31+
})
32+
expect(code).toMatchInlineSnapshot(`
33+
"import React from \\"react\\";
34+
import Svg from \\"react-native-svg\\";
35+
36+
const SvgComponent = () => <svg><div /></svg>;
37+
38+
export default SvgComponent;"
39+
`)
40+
})
41+
42+
it('should import for expo', () => {
43+
const { code } = testPlugin('<svg><div /></svg>', {
44+
state: { componentName: 'SvgComponent' },
45+
native: { expo: true },
46+
})
47+
expect(code).toMatchInlineSnapshot(`
48+
"import React from \\"react\\";
49+
import \\"expo\\";
50+
51+
const SvgComponent = () => <svg><div /></svg>;
52+
2353
export default SvgComponent;"
2454
`)
2555
})

packages/babel-plugin-transform-svg-component/src/util.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,16 @@ export const getImport = ({ types: t }, opts) => {
4747
]
4848

4949
if (opts.native) {
50-
importDeclarations.push(
51-
t.importDeclaration(
52-
[t.importDefaultSpecifier(t.identifier('Svg'))],
53-
t.stringLiteral('react-native-svg'),
54-
),
55-
)
50+
if (opts.native.expo) {
51+
importDeclarations.push(t.importDeclaration([], t.stringLiteral('expo')))
52+
} else {
53+
importDeclarations.push(
54+
t.importDeclaration(
55+
[t.importDefaultSpecifier(t.identifier('Svg'))],
56+
t.stringLiteral('react-native-svg'),
57+
),
58+
)
59+
}
5660
}
5761

5862
return importDeclarations

packages/babel-preset/src/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,11 @@ const plugin = (api, opts) => {
9090
}
9191

9292
if (opts.native) {
93-
plugins.push(transformReactNativeSVG)
93+
if (opts.native.expo) {
94+
plugins.push([transformReactNativeSVG, opts.native])
95+
} else {
96+
plugins.push(transformReactNativeSVG)
97+
}
9498
}
9599

96100
return { plugins }

packages/babel-preset/src/index.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,24 @@ describe('preset', () => {
2929
3030
const SvgComponent = () => <svg foo=\\"bar\\" x={y} />;
3131
32+
export default SvgComponent;"
33+
`)
34+
})
35+
36+
it('should handle native expo option', () => {
37+
expect(
38+
testPreset('<svg><g><path d="M0 0h48v1H0z" /></g></svg>', {
39+
native: { expo: true },
40+
state: {
41+
componentName: 'SvgComponent',
42+
},
43+
}),
44+
).toMatchInlineSnapshot(`
45+
"import React from \\"react\\";
46+
import { Svg } from \\"expo\\";
47+
48+
const SvgComponent = () => <Svg><Svg.G><Svg.Path d=\\"M0 0h48v1H0z\\" /></Svg.G></Svg>;
49+
3250
export default SvgComponent;"
3351
`)
3452
})

packages/core/src/__snapshots__/convert.test.js.snap

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,28 @@ export default SvgComponent
106106
"
107107
`;
108108

109+
exports[`convert config should support options { native: { expo: true } } 1`] = `
110+
"import React from 'react'
111+
import { Svg } from 'expo'
112+
113+
const SvgComponent = props => (
114+
<Svg width={88} height={88} {...props}>
115+
<Svg.G
116+
stroke=\\"#063855\\"
117+
strokeWidth={2}
118+
fill=\\"none\\"
119+
fillRule=\\"evenodd\\"
120+
strokeLinecap=\\"square\\"
121+
>
122+
<Svg.Path d=\\"M51 37L37 51M51 51L37 37\\" />
123+
</Svg.G>
124+
</Svg>
125+
)
126+
127+
export default SvgComponent
128+
"
129+
`;
130+
109131
exports[`convert config should support options { native: true, expandProps: false } 1`] = `
110132
"import React from 'react'
111133
import Svg, { G, Path } from 'react-native-svg'

packages/core/src/convert.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ describe('convert', () => {
293293
{ expandProps: 'start' },
294294
{ icon: true },
295295
{ native: true },
296+
{ native: { expo: true } },
296297
{ native: true, icon: true },
297298
{ native: true, expandProps: false },
298299
{ native: true, ref: true },

website/src/pages/docs/options.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,11 @@ inherits from text size.
4747
Modify all SVG nodes with uppercase and use a specific template with
4848
[`react-native-svg`](https://github.com/react-native-community/react-native-svg) imports. **All unsupported nodes will be removed.**
4949

50+
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.
51+
5052
| Default | CLI Override | API Override |
5153
| ------- | ------------ | ---------------- |
52-
| `false` | `--native` | `native: <bool>` |
54+
| `false` | `--native` | `native: <bool>` or `native: { expo: true }` |
5355

5456
## Dimensions
5557

0 commit comments

Comments
 (0)