Skip to content

Commit ac8b8ca

Browse files
committed
feat(v4): new architecture
- Replace h2x by rehype + babel - Move jsx, prettier and svgo into separated plugins BREAKING CHANGE: - `template` option must now returns a Babel AST - `@svgr/core` does not include svgo & prettier by default
1 parent a125679 commit ac8b8ca

139 files changed

Lines changed: 3499 additions & 2664 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ module.exports = {
1010
jest: true,
1111
},
1212
rules: {
13+
'no-plusplus': 'off',
1314
'class-methods-use-this': 'off',
1415
'no-param-reassign': 'off',
1516
'no-use-before-define': 'off',
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
src/
2+
.*
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# @svgr/babel-plugin-add-jsx-attribute
2+
3+
## Install
4+
5+
```
6+
npm install --save-dev @svgr/babel-plugin-add-jsx-attribute
7+
```
8+
9+
## Usage
10+
11+
**.babelrc**
12+
13+
```json
14+
{
15+
"plugins": [
16+
[
17+
"@svgr/babel-plugin-add-jsx-attribute",
18+
{
19+
"elements": ["svg"],
20+
"attributes": [
21+
{
22+
"name": "width",
23+
"value": "200",
24+
"spread": false,
25+
"literal": false,
26+
"position": "end"
27+
}
28+
]
29+
}
30+
]
31+
]
32+
}
33+
```
34+
35+
## License
36+
37+
MIT
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "@svgr/babel-plugin-add-jsx-attribute",
3+
"description": "Add JSX attribute",
4+
"version": "3.1.0",
5+
"main": "lib/index.js",
6+
"repository": "git@github.com:smooth-code/svgr.git",
7+
"author": "Greg Bergé <berge.greg@gmail.com>",
8+
"keywords": [
9+
"babel-plugin"
10+
],
11+
"engines": {
12+
"node": ">=8"
13+
},
14+
"license": "MIT",
15+
"scripts": {
16+
"prebuild": "rm -rf lib/",
17+
"build": "babel --config-file ../../babel.config.js -d lib --ignore \"**/*.test.js\" src",
18+
"prepublishOnly": "yarn run build"
19+
}
20+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
const positionMethod = {
2+
start: 'unshiftContainer',
3+
end: 'pushContainer',
4+
}
5+
6+
const addJSXAttribute = ({ types: t, template }, opts) => {
7+
function getAttributeValue({ literal, value }) {
8+
if (typeof value === 'boolean') {
9+
return t.jsxExpressionContainer(t.booleanLiteral(value))
10+
}
11+
12+
if (typeof value === 'number') {
13+
return t.jsxExpressionContainer(t.numericLiteral(value))
14+
}
15+
16+
if (typeof value === 'string' && literal) {
17+
return t.jsxExpressionContainer(template.ast(value).expression)
18+
}
19+
20+
if (typeof value === 'string') {
21+
return t.stringLiteral(value)
22+
}
23+
24+
return null
25+
}
26+
27+
function getAttribute({ spread, name, value, literal }) {
28+
if (spread) {
29+
return t.jsxSpreadAttribute(t.identifier(name))
30+
}
31+
32+
return t.jsxAttribute(
33+
t.jsxIdentifier(name),
34+
getAttributeValue({ value, literal }),
35+
)
36+
}
37+
38+
return {
39+
visitor: {
40+
JSXOpeningElement(path) {
41+
if (!opts.elements.includes(path.node.name.name)) return
42+
43+
opts.attributes.forEach(
44+
({
45+
name,
46+
value = null,
47+
spread = false,
48+
literal = false,
49+
position = 'end',
50+
}) => {
51+
const method = positionMethod[position]
52+
const newAttribute = getAttribute({ spread, name, value, literal })
53+
const attributes = path.get('attributes')
54+
55+
const isEqualAttribute = attribute => {
56+
if (spread) {
57+
return attribute.get('argument').isIdentifier({ name })
58+
}
59+
60+
return attribute.get('name').isJSXIdentifier({ name })
61+
}
62+
63+
const replaced = attributes.some(attribute => {
64+
if (!isEqualAttribute(attribute)) return false
65+
attribute.replaceWith(newAttribute)
66+
return true
67+
})
68+
69+
if (!replaced) {
70+
path[method]('attributes', newAttribute)
71+
}
72+
},
73+
)
74+
},
75+
},
76+
}
77+
}
78+
79+
export default addJSXAttribute
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { transform } from '@babel/core'
2+
import plugin from '.'
3+
4+
const testPlugin = (code, options) => {
5+
const result = transform(code, {
6+
plugins: ['@babel/plugin-syntax-jsx', [plugin, options]],
7+
configFile: false,
8+
})
9+
10+
return result.code
11+
}
12+
13+
describe('plugin', () => {
14+
it('should add simple attribute', () => {
15+
expect(
16+
testPlugin('<div />', {
17+
elements: ['div'],
18+
attributes: [{ name: 'disabled' }],
19+
}),
20+
).toMatchInlineSnapshot(`"<div disabled />;"`)
21+
})
22+
23+
it('should add attribute with value', () => {
24+
expect(
25+
testPlugin('<div />', {
26+
elements: ['div'],
27+
attributes: [{ name: 'disabled', value: true }],
28+
}),
29+
).toMatchInlineSnapshot(`"<div disabled={true} />;"`)
30+
expect(
31+
testPlugin('<div />', {
32+
elements: ['div'],
33+
attributes: [{ name: 'disabled', value: 'true' }],
34+
}),
35+
).toMatchInlineSnapshot(`"<div disabled=\\"true\\" />;"`)
36+
37+
expect(
38+
testPlugin('<div />', {
39+
elements: ['div'],
40+
attributes: [{ name: 'disabled', value: 200 }],
41+
}),
42+
).toMatchInlineSnapshot(`"<div disabled={200} />;"`)
43+
})
44+
45+
it('should add literal attribute', () => {
46+
expect(
47+
testPlugin('<div />', {
48+
elements: ['div'],
49+
attributes: [{ name: 'ref', value: 'ref', literal: true }],
50+
}),
51+
).toMatchInlineSnapshot(`"<div ref={ref} />;"`)
52+
53+
expect(
54+
testPlugin('<div />', {
55+
elements: ['div'],
56+
attributes: [{ name: 'ref', value: 'ref ? ref : null', literal: true }],
57+
}),
58+
).toMatchInlineSnapshot(`"<div ref={ref ? ref : null} />;"`)
59+
})
60+
61+
it('should add spread attribute', () => {
62+
expect(
63+
testPlugin('<div foo><span /></div>', {
64+
elements: ['div'],
65+
attributes: [
66+
{
67+
spread: true,
68+
name: 'props',
69+
position: 'start',
70+
},
71+
],
72+
}),
73+
).toMatchInlineSnapshot(`"<div {...props} foo><span /></div>;"`)
74+
75+
expect(
76+
testPlugin('<div><span foo="bar" /></div>', {
77+
elements: ['span'],
78+
attributes: [
79+
{
80+
spread: true,
81+
name: 'props',
82+
position: 'end',
83+
},
84+
],
85+
}),
86+
).toMatchInlineSnapshot(`"<div><span foo=\\"bar\\" {...props} /></div>;"`)
87+
})
88+
89+
it('should replace attribute', () => {
90+
expect(
91+
testPlugin('<div disabled />', {
92+
elements: ['div'],
93+
attributes: [{ name: 'disabled', value: false }],
94+
}),
95+
).toMatchInlineSnapshot(`"<div disabled={false} />;"`)
96+
})
97+
})
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
src/
2+
.*
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# @svgr/babel-plugin-remove-jsx-attribute
2+
3+
## Install
4+
5+
```
6+
npm install --save-dev @svgr/babel-plugin-remove-jsx-attribute
7+
```
8+
9+
## Usage
10+
11+
**.babelrc**
12+
13+
```json
14+
{
15+
"plugins": [
16+
[
17+
"@svgr/babel-plugin-remove-jsx-attribute",
18+
{
19+
"elements": ["svg"],
20+
"attributes": [{ "name": "width" }, { "name": "height" }]
21+
}
22+
]
23+
]
24+
}
25+
```
26+
27+
## License
28+
29+
MIT
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "@svgr/babel-plugin-remove-jsx-attribute",
3+
"description": "Remove JSX attribute",
4+
"version": "3.1.0",
5+
"main": "lib/index.js",
6+
"repository": "git@github.com:smooth-code/svgr.git",
7+
"author": "Greg Bergé <berge.greg@gmail.com>",
8+
"keywords": [
9+
"babel-plugin"
10+
],
11+
"engines": {
12+
"node": ">=8"
13+
},
14+
"license": "MIT",
15+
"scripts": {
16+
"prebuild": "rm -rf lib/",
17+
"build": "babel --config-file ../../babel.config.js -d lib --ignore \"**/*.test.js\" src",
18+
"prepublishOnly": "yarn run build"
19+
}
20+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const removeJSXAttribute = (api, opts) => ({
2+
visitor: {
3+
JSXOpeningElement(path) {
4+
if (!opts.elements.includes(path.node.name.name)) return
5+
6+
path.get('attributes').forEach(attribute => {
7+
if (opts.attributes.includes(attribute.node.name.name)) {
8+
attribute.remove()
9+
}
10+
})
11+
},
12+
},
13+
})
14+
15+
export default removeJSXAttribute

0 commit comments

Comments
 (0)