Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/sweet-plums-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@emotion/eslint-plugin': patch
---

implement automatic adding of jsxImportSource pragma definition
60 changes: 59 additions & 1 deletion packages/eslint-plugin/src/rules/jsx-import.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/
const JSX_IMPORT_SOURCE_REGEX = /\*?\s*@jsxImportSource\s+([^\s]+)/

// TODO: handling this case
// <div css={`color:hotpink;`} />
Expand All @@ -7,9 +8,66 @@ const JSX_ANNOTATION_REGEX = /\*?\s*@jsx\s+([^\s]+)/

export default {
meta: {
fixable: 'code'
fixable: 'code',
schema: {
type: 'array',
items: {
oneOf: [
{
type: 'string'
},
{
type: 'object',
properties: {
runtime: { type: 'string' },
importSource: { type: 'string' }
},
required: ['runtime'],
additionalProperties: false
}
]
},
uniqueItems: true,
minItems: 0
}
},
create(context) {
const jsxRuntimeMode = context.options.find(
option => option && option.runtime === 'automatic'
)

if (jsxRuntimeMode) {
return {
JSXAttribute(node) {
if (node.name.name !== 'css') {
return
}
let hasJsxImportSource = false
let sourceCode = context.getSourceCode()
let pragma = sourceCode
.getAllComments()
.find(node => JSX_IMPORT_SOURCE_REGEX.test(node.value))
hasJsxImportSource =
pragma && pragma.value.match(JSX_IMPORT_SOURCE_REGEX)
if (!hasJsxImportSource) {
context.report({
node,
message:
'The css prop can only be used if you set @jsxImportSource pragma',
fix(fixer) {
return fixer.insertTextBefore(
sourceCode.ast.body[0],
`/** @jsxImportSource ${
(jsxRuntimeMode || {}).importSource || '@emotion/react'
} */\n`
)
}
})
}
}
}
}

return {
JSXAttribute(node) {
if (node.name.name !== 'css') {
Expand Down
88 changes: 88 additions & 0 deletions packages/eslint-plugin/test/rules/jsx-import.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,45 @@ ruleTester.run('emotion jsx', rule, {
let ele = <div css={{}} />
`
},
{
options: [{ runtime: 'classic' }],
code: `
// @jsx jsx
import { jsx } from '@emotion/react'
let ele = <div css={{}} />
`
},
{
options: [{ runtime: 'invalidRuntime' }],
code: `
// @jsx jsx
import { jsx } from '@emotion/react'
let ele = <div css={{}} />
`
},
{
options: [{ runtime: 'automatic' }],
code: `
/** @jsxImportSource @emotion/react */

let ele = <div css={{}} />
`
},
{
options: [{ runtime: 'automatic' }],
code: `
// no css prop usage for test coverage
let ele = <div nonecss={{}} />
`
},
{
options: [{ runtime: 'automatic', importSource: '@emotion/react' }],
code: `
/** @jsxImportSource @emotion/react */

let ele = <div css={{}} />
`
},
{
code: `

Expand All @@ -52,6 +91,55 @@ let ele = <div css={{}} />
output: `
// @jsx jsx
import { jsx } from '@emotion/react'
let ele = <div css={{}} />
`.trim()
},
{
options: [{ runtime: 'automatic' }],
code: `
let ele = <div css={{}} />
`.trim(),
errors: [
{
message:
'The css prop can only be used if you set @jsxImportSource pragma'
}
],
output: `
/** @jsxImportSource @emotion/react */
let ele = <div css={{}} />
`.trim()
},
{
options: [{ runtime: 'automatic', importSource: '@iChenLei/react' }],
code: `
let ele = <div css={{}} />
`.trim(),
errors: [
{
message:
'The css prop can only be used if you set @jsxImportSource pragma'
}
],
output: `
/** @jsxImportSource @iChenLei/react */
let ele = <div css={{}} />
`.trim()
},
{
options: [{ runtime: 'classic' }],
code: `
let ele = <div css={{}} />
`.trim(),
errors: [
{
message:
'The css prop can only be used if jsx from @emotion/react is imported and it is set as the jsx pragma'
}
],
output: `
/** @jsx jsx */
import { jsx } from '@emotion/react'
let ele = <div css={{}} />
`.trim()
},
Expand Down