Skip to content

Commit

Permalink
feat(v4): new architecture
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
gregberge committed Nov 4, 2018
1 parent a125679 commit ac8b8ca
Show file tree
Hide file tree
Showing 139 changed files with 3,499 additions and 2,664 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = {
jest: true,
},
rules: {
'no-plusplus': 'off',
'class-methods-use-this': 'off',
'no-param-reassign': 'off',
'no-use-before-define': 'off',
Expand Down
2 changes: 2 additions & 0 deletions packages/babel-plugin-add-jsx-attribute/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
src/
.*
37 changes: 37 additions & 0 deletions packages/babel-plugin-add-jsx-attribute/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# @svgr/babel-plugin-add-jsx-attribute

## Install

```
npm install --save-dev @svgr/babel-plugin-add-jsx-attribute
```

## Usage

**.babelrc**

```json
{
"plugins": [
[
"@svgr/babel-plugin-add-jsx-attribute",
{
"elements": ["svg"],
"attributes": [
{
"name": "width",
"value": "200",
"spread": false,
"literal": false,
"position": "end"
}
]
}
]
]
}
```

## License

MIT
20 changes: 20 additions & 0 deletions packages/babel-plugin-add-jsx-attribute/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@svgr/babel-plugin-add-jsx-attribute",
"description": "Add JSX attribute",
"version": "3.1.0",
"main": "lib/index.js",
"repository": "git@github.com:smooth-code/svgr.git",
"author": "Greg Bergé <berge.greg@gmail.com>",
"keywords": [
"babel-plugin"
],
"engines": {
"node": ">=8"
},
"license": "MIT",
"scripts": {
"prebuild": "rm -rf lib/",
"build": "babel --config-file ../../babel.config.js -d lib --ignore \"**/*.test.js\" src",
"prepublishOnly": "yarn run build"
}
}
79 changes: 79 additions & 0 deletions packages/babel-plugin-add-jsx-attribute/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const positionMethod = {
start: 'unshiftContainer',
end: 'pushContainer',
}

const addJSXAttribute = ({ types: t, template }, opts) => {
function getAttributeValue({ literal, value }) {
if (typeof value === 'boolean') {
return t.jsxExpressionContainer(t.booleanLiteral(value))
}

if (typeof value === 'number') {
return t.jsxExpressionContainer(t.numericLiteral(value))
}

if (typeof value === 'string' && literal) {
return t.jsxExpressionContainer(template.ast(value).expression)
}

if (typeof value === 'string') {
return t.stringLiteral(value)
}

return null
}

function getAttribute({ spread, name, value, literal }) {
if (spread) {
return t.jsxSpreadAttribute(t.identifier(name))
}

return t.jsxAttribute(
t.jsxIdentifier(name),
getAttributeValue({ value, literal }),
)
}

return {
visitor: {
JSXOpeningElement(path) {
if (!opts.elements.includes(path.node.name.name)) return

opts.attributes.forEach(
({
name,
value = null,
spread = false,
literal = false,
position = 'end',
}) => {
const method = positionMethod[position]
const newAttribute = getAttribute({ spread, name, value, literal })
const attributes = path.get('attributes')

const isEqualAttribute = attribute => {
if (spread) {
return attribute.get('argument').isIdentifier({ name })
}

return attribute.get('name').isJSXIdentifier({ name })
}

const replaced = attributes.some(attribute => {
if (!isEqualAttribute(attribute)) return false
attribute.replaceWith(newAttribute)
return true
})

if (!replaced) {
path[method]('attributes', newAttribute)
}
},
)
},
},
}
}

export default addJSXAttribute
97 changes: 97 additions & 0 deletions packages/babel-plugin-add-jsx-attribute/src/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { transform } from '@babel/core'
import plugin from '.'

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

return result.code
}

describe('plugin', () => {
it('should add simple attribute', () => {
expect(
testPlugin('<div />', {
elements: ['div'],
attributes: [{ name: 'disabled' }],
}),
).toMatchInlineSnapshot(`"<div disabled />;"`)
})

it('should add attribute with value', () => {
expect(
testPlugin('<div />', {
elements: ['div'],
attributes: [{ name: 'disabled', value: true }],
}),
).toMatchInlineSnapshot(`"<div disabled={true} />;"`)
expect(
testPlugin('<div />', {
elements: ['div'],
attributes: [{ name: 'disabled', value: 'true' }],
}),
).toMatchInlineSnapshot(`"<div disabled=\\"true\\" />;"`)

expect(
testPlugin('<div />', {
elements: ['div'],
attributes: [{ name: 'disabled', value: 200 }],
}),
).toMatchInlineSnapshot(`"<div disabled={200} />;"`)
})

it('should add literal attribute', () => {
expect(
testPlugin('<div />', {
elements: ['div'],
attributes: [{ name: 'ref', value: 'ref', literal: true }],
}),
).toMatchInlineSnapshot(`"<div ref={ref} />;"`)

expect(
testPlugin('<div />', {
elements: ['div'],
attributes: [{ name: 'ref', value: 'ref ? ref : null', literal: true }],
}),
).toMatchInlineSnapshot(`"<div ref={ref ? ref : null} />;"`)
})

it('should add spread attribute', () => {
expect(
testPlugin('<div foo><span /></div>', {
elements: ['div'],
attributes: [
{
spread: true,
name: 'props',
position: 'start',
},
],
}),
).toMatchInlineSnapshot(`"<div {...props} foo><span /></div>;"`)

expect(
testPlugin('<div><span foo="bar" /></div>', {
elements: ['span'],
attributes: [
{
spread: true,
name: 'props',
position: 'end',
},
],
}),
).toMatchInlineSnapshot(`"<div><span foo=\\"bar\\" {...props} /></div>;"`)
})

it('should replace attribute', () => {
expect(
testPlugin('<div disabled />', {
elements: ['div'],
attributes: [{ name: 'disabled', value: false }],
}),
).toMatchInlineSnapshot(`"<div disabled={false} />;"`)
})
})
2 changes: 2 additions & 0 deletions packages/babel-plugin-remove-jsx-attribute/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
src/
.*
29 changes: 29 additions & 0 deletions packages/babel-plugin-remove-jsx-attribute/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# @svgr/babel-plugin-remove-jsx-attribute

## Install

```
npm install --save-dev @svgr/babel-plugin-remove-jsx-attribute
```

## Usage

**.babelrc**

```json
{
"plugins": [
[
"@svgr/babel-plugin-remove-jsx-attribute",
{
"elements": ["svg"],
"attributes": [{ "name": "width" }, { "name": "height" }]
}
]
]
}
```

## License

MIT
20 changes: 20 additions & 0 deletions packages/babel-plugin-remove-jsx-attribute/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@svgr/babel-plugin-remove-jsx-attribute",
"description": "Remove JSX attribute",
"version": "3.1.0",
"main": "lib/index.js",
"repository": "git@github.com:smooth-code/svgr.git",
"author": "Greg Bergé <berge.greg@gmail.com>",
"keywords": [
"babel-plugin"
],
"engines": {
"node": ">=8"
},
"license": "MIT",
"scripts": {
"prebuild": "rm -rf lib/",
"build": "babel --config-file ../../babel.config.js -d lib --ignore \"**/*.test.js\" src",
"prepublishOnly": "yarn run build"
}
}
15 changes: 15 additions & 0 deletions packages/babel-plugin-remove-jsx-attribute/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const removeJSXAttribute = (api, opts) => ({
visitor: {
JSXOpeningElement(path) {
if (!opts.elements.includes(path.node.name.name)) return

path.get('attributes').forEach(attribute => {
if (opts.attributes.includes(attribute.node.name.name)) {
attribute.remove()
}
})
},
},
})

export default removeJSXAttribute
22 changes: 22 additions & 0 deletions packages/babel-plugin-remove-jsx-attribute/src/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { transform } from '@babel/core'
import plugin from '.'

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

return result.code
}

describe('plugin', () => {
it('should remove attributes from an element', () => {
expect(
testPlugin('<div foo><span foo /></div>', {
elements: ['span'],
attributes: ['foo'],
}),
).toMatchInlineSnapshot(`"<div foo><span /></div>;"`)
})
})
2 changes: 2 additions & 0 deletions packages/babel-plugin-remove-jsx-empty-expression/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
src/
.*
21 changes: 21 additions & 0 deletions packages/babel-plugin-remove-jsx-empty-expression/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# @svgr/babel-plugin-remove-jsx-empty-expression

## Install

```
npm install --save-dev @svgr/babel-plugin-remove-jsx-empty-expression
```

## Usage

**.babelrc**

```json
{
"plugins": ["@svgr/babel-plugin-remove-jsx-empty-expression"]
}
```

## License

MIT
20 changes: 20 additions & 0 deletions packages/babel-plugin-remove-jsx-empty-expression/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@svgr/babel-plugin-remove-jsx-empty-expression",
"description": "Remove JSX empty expression",
"version": "3.1.0",
"main": "lib/index.js",
"repository": "git@github.com:smooth-code/svgr.git",
"author": "Greg Bergé <berge.greg@gmail.com>",
"keywords": [
"babel-plugin"
],
"engines": {
"node": ">=8"
},
"license": "MIT",
"scripts": {
"prebuild": "rm -rf lib/",
"build": "babel --config-file ../../babel.config.js -d lib --ignore \"**/*.test.js\" src",
"prepublishOnly": "yarn run build"
}
}
10 changes: 10 additions & 0 deletions packages/babel-plugin-remove-jsx-empty-expression/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const removeJSXEmptyExpression = () => ({
visitor: {
JSXExpressionContainer(path) {
if (!path.get('expression').isJSXEmptyExpression()) return
path.remove()
},
},
})

export default removeJSXEmptyExpression
Loading

0 comments on commit ac8b8ca

Please sign in to comment.