Skip to content

Commit

Permalink
Add handling of the css prop in syntax-preference rule
Browse files Browse the repository at this point in the history
  • Loading branch information
Andarist committed Nov 28, 2019
1 parent 0b817da commit 767ccfc
Show file tree
Hide file tree
Showing 12 changed files with 1,842 additions and 882 deletions.
25 changes: 12 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -204,19 +204,19 @@
"enzyme": "^3.7.0",
"enzyme-adapter-react-16": "^1.6.0",
"enzyme-to-json": "^3.2.1",
"eslint": "^4.5.0",
"eslint-config-prettier": "^2.3.0",
"eslint": "^6.7.1",
"eslint-config-prettier": "^6.7.0",
"eslint-config-react": "^1.1.7",
"eslint-config-standard": "^10.2.1",
"eslint-config-standard-react": "^5.0.0",
"eslint-plugin-flowtype": "^2.50.0",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-node": "^5.1.1",
"eslint-plugin-prettier": "^2.2.0",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^7.3.0",
"eslint-plugin-react-hooks": "^1.0.1",
"eslint-plugin-standard": "^3.0.1",
"eslint-config-standard": "^14.1.0",
"eslint-config-standard-react": "^9.2.0",
"eslint-plugin-flowtype": "^4.5.2",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-node": "^10.0.0",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-react": "^7.16.0",
"eslint-plugin-react-hooks": "^2.3.0",
"eslint-plugin-standard": "^4.0.1",
"flow-bin": "^0.110.0",
"html-tag-names": "^1.1.2",
"husky": "^3.0.9",
Expand All @@ -237,7 +237,6 @@
"prettier": "1.14.3",
"puppeteer": "^1.6.0",
"raf": "^3.4.0",
"razzle": "^2.4.0",
"react": "^16.11.0",
"react-art": "^16.5.2",
"react-dom": "^16.11.0",
Expand Down
5 changes: 0 additions & 5 deletions packages/eslint-plugin-emotion/.eslintrc

This file was deleted.

10 changes: 8 additions & 2 deletions packages/eslint-plugin-emotion/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,11 @@
"engines": {
"node": ">=6"
},
"license": "ISC"
}
"license": "ISC",
"peerDependencies": {
"eslint": "6"
},
"devDependencies": {
"eslint": "^6.7.1"
}
}
219 changes: 160 additions & 59 deletions packages/eslint-plugin-emotion/src/rules/syntax-preference.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,45 @@
*/

function isStringStyle(node) {
if (node.type === 'TaggedTemplateExpression') {
// shorthand notation
// eg: styled.h1` color: red; `
if (
node.tag.type === 'MemberExpression' &&
node.tag.object.name === 'styled'
) {
// string syntax used
return true
}
// shorthand notation
// eg: styled.h1` color: red; `
if (
node.tag.type === 'MemberExpression' &&
node.tag.object.name === 'styled'
) {
// string syntax used
return true
}

// full notation
// eg: styled('h1')` color: red; `
if (
node.tag.type === 'CallExpression' &&
node.tag.callee.name === 'styled'
) {
// string syntax used
return true
}
// full notation
// eg: styled('h1')` color: red; `
if (node.tag.type === 'CallExpression' && node.tag.callee.name === 'styled') {
// string syntax used
return true
}

return false
}

function isObjectStyle(node) {
if (node.type === 'CallExpression') {
// shorthand notation
// eg: styled.h1({ color: 'red' })
if (
node.callee.type === 'MemberExpression' &&
node.callee.object.name === 'styled'
) {
// object syntax used
return true
}
// shorthand notation
// eg: styled.h1({ color: 'red' })
if (
node.callee.type === 'MemberExpression' &&
node.callee.object.name === 'styled'
) {
// object syntax used
return true
}

// full notation
// eg: styled('h1')({ color: 'red' })
if (
node.callee.type === 'CallExpression' &&
node.callee.callee.name === 'styled'
) {
// object syntax used
return true
}
// full notation
// eg: styled('h1')({ color: 'red' })
if (
node.callee.type === 'CallExpression' &&
node.callee.callee.name === 'styled'
) {
// object syntax used
return true
}

return false
Expand All @@ -61,6 +54,127 @@ function isObjectStyle(node) {

const MSG_PREFER_STRING_STYLE = 'Styles should be written using strings.'
const MSG_PREFER_OBJECT_STYLE = 'Styles should be written using objects.'
const MSG_PREFER_WRAPPING_WITH_CSS =
'Prefer wrapping your string styles with `css` call.'

const checkCssPropExpressionPreferringObject = (context, node) => {
switch (node.type) {
case 'ArrayExpression':
node.elements.forEach(element =>
checkCssPropExpressionPreferringObject(context, element)
)
return
case 'CallExpression':
context.report({
node,
message: MSG_PREFER_OBJECT_STYLE
})
return
case 'TaggedTemplateExpression':
context.report({
node,
message: MSG_PREFER_OBJECT_STYLE
})
return
case 'Literal':
// validating other literal types seems out of scope of this plugin
if (typeof node.value !== 'string') {
return
}
context.report({
node,
message: MSG_PREFER_OBJECT_STYLE
})
}
}

const createPreferredObjectVisitor = context => ({
TaggedTemplateExpression(node) {
if (isStringStyle(node)) {
context.report({
node,
message: MSG_PREFER_OBJECT_STYLE
})
}
},
JSXAttribute(node) {
if (node.name.name !== 'css') {
return
}

switch (node.value.type) {
case 'Literal':
// validating other literal types seems out of scope of this plugin
if (typeof node.value.value !== 'string') {
return
}
context.report({
node: node.value,
message: MSG_PREFER_OBJECT_STYLE
})
return
case 'JSXExpressionContainer':
checkCssPropExpressionPreferringObject(context, node.value.expression)
}
}
})

const checkCssPropExpressionPreferringString = (context, node) => {
switch (node.type) {
case 'ArrayExpression':
node.elements.forEach(element =>
checkCssPropExpressionPreferringString(context, element)
)
return
case 'ObjectExpression':
context.report({
node,
message: MSG_PREFER_STRING_STYLE
})
return
case 'Literal':
// validating other literal types seems out of scope of this plugin
if (typeof node.value !== 'string') {
return
}
context.report({
node,
message: MSG_PREFER_WRAPPING_WITH_CSS
})
}
}

const createPreferredStringVisitor = context => ({
CallExpression(node) {
if (isObjectStyle(node)) {
context.report({
node,
message: MSG_PREFER_STRING_STYLE
})
}
},

JSXAttribute(node) {
if (node.name.name !== 'css') {
return
}

switch (node.value.type) {
case 'Literal':
// validating other literal types seems out of scope of this plugin
if (typeof node.value.value !== 'string') {
return
}
context.report({
node: node.value,
message: MSG_PREFER_WRAPPING_WITH_CSS
})
return
case 'JSXExpressionContainer':
checkCssPropExpressionPreferringString(context, node.value.expression)
}
}
})

export default {
meta: {
Expand All @@ -78,28 +192,15 @@ export default {
},

create(context) {
return {
TaggedTemplateExpression(node) {
const preferedSyntax = context.options[0]

if (isStringStyle(node) && preferedSyntax === 'object') {
context.report({
node,
message: MSG_PREFER_OBJECT_STYLE
})
}
},

CallExpression(node) {
const preferedSyntax = context.options[0]
const preferredSyntax = context.options[0]

if (isObjectStyle(node) && preferedSyntax === 'string') {
context.report({
node,
message: MSG_PREFER_STRING_STYLE
})
}
}
switch (preferredSyntax) {
case 'object':
return createPreferredObjectVisitor(context)
case 'string':
return createPreferredStringVisitor(context)
default:
return {}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
* @jest-environment node
*/

const { RuleTester } = require('eslint')
const rule = require('eslint-plugin-emotion').rules['import-from-emotion']
import { RuleTester } from 'eslint'
import { rules as emotionRules } from 'eslint-plugin-emotion'

const rule = emotionRules['import-from-emotion']

RuleTester.setDefaultConfig({
parserOptions: {
Expand All @@ -20,9 +22,7 @@ const ruleTester = new RuleTester()
ruleTester.run('emotion jsx', rule, {
valid: [
{
code: `
import { css } from 'emotion'
`
code: `import { css } from 'emotion'`
}
],

Expand Down
6 changes: 4 additions & 2 deletions packages/eslint-plugin-emotion/test/rules/jsx-import.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
* @jest-environment node
*/

const { RuleTester } = require('eslint')
const rule = require('eslint-plugin-emotion').rules['jsx-import']
import { RuleTester } from 'eslint'
import { rules as emotionRules } from 'eslint-plugin-emotion'

const rule = emotionRules['jsx-import']

RuleTester.setDefaultConfig({
parserOptions: {
Expand Down
6 changes: 4 additions & 2 deletions packages/eslint-plugin-emotion/test/rules/no-vanilla.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
* @jest-environment node
*/

const { RuleTester } = require('eslint')
const rule = require('eslint-plugin-emotion').rules['no-vanilla']
import { RuleTester } from 'eslint'
import { rules as emotionRules } from 'eslint-plugin-emotion'

const rule = emotionRules['no-vanilla']

RuleTester.setDefaultConfig({
parserOptions: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
* @jest-environment node
*/

const { RuleTester } = require('eslint')
const rule = require('eslint-plugin-emotion').rules['styled-import']
import { RuleTester } from 'eslint'
import { rules as emotionRules } from 'eslint-plugin-emotion'

const rule = emotionRules['styled-import']

RuleTester.setDefaultConfig({
parserOptions: {
Expand Down
Loading

0 comments on commit 767ccfc

Please sign in to comment.