diff --git a/dev/playground/a.js b/dev/playground/a.js
index 25c5951..f00ce32 100644
--- a/dev/playground/a.js
+++ b/dev/playground/a.js
@@ -1,4 +1,3 @@
-import {get} from 'lodash'
-import {Dict} from 'interfaces'
-import {Something} from './constants'
-import zlib from 'zlib'
+const A = () => {
+ return
hello
+}
diff --git a/dev/playground/t.ts b/dev/playground/t.ts
index a8c65a5..e69de29 100644
--- a/dev/playground/t.ts
+++ b/dev/playground/t.ts
@@ -1 +0,0 @@
-const z = (a, b) => {return a + b}
diff --git a/src/fixers/__tests__/specs/experimental-use-styled-component.txt b/src/fixers/__tests__/specs/experimental-use-styled-component.txt
index 7c7a70e..1d85109 100644
--- a/src/fixers/__tests__/specs/experimental-use-styled-component.txt
+++ b/src/fixers/__tests__/specs/experimental-use-styled-component.txt
@@ -97,4 +97,36 @@ const A = () => {
return r.div([
r(StyledSpan, 'hello')
])
+}
+===
+name: extract from selfclosing JSX tag
+line: 2
+column: 17
+---
+const A = () => {
+ return
+}
+---
+import { styled } from "styletron-react";
+
+const StyledImg = styled("img", {});
+
+const A = () => {
+ return
+}
+===
+name: extract from JSX tag
+line: 2
+column: 17
+---
+const A = () => {
+ return hello
+}
+---
+import { styled } from "styletron-react";
+
+const StyledDiv = styled("div", {});
+
+const A = () => {
+ return hello
}
\ No newline at end of file
diff --git a/src/fixers/experimental-use-styled-component.ts b/src/fixers/experimental-use-styled-component.ts
index c38b271..0a9fe56 100644
--- a/src/fixers/experimental-use-styled-component.ts
+++ b/src/fixers/experimental-use-styled-component.ts
@@ -8,8 +8,13 @@ import astTypes from 'recast/lib/types'
import {Collection} from 'jscodeshift/src/Collection'
import {capitalize} from '../text-utils'
import {pathExists} from 'fs-extra'
+import {jsxElement} from '@babel/types'
-type Data = Position.t
+type NodeType = 'jsx' | 'rdom'
+type Data = {
+ start: Position.t
+ type: NodeType
+}
function hasStyledImport(j: jscodeshift.JSCodeshift, ast: AstRoot): boolean {
const styletronSpecifiers = getImportSpecifiers(j, ast, 'styletron-react')
@@ -77,6 +82,10 @@ function getTagName(j: jscodeshift.JSCodeshift, node: jscodeshift.CallExpression
: null
}
+function isLowerCase(word: string): boolean {
+ return word === word.toLowerCase()
+}
+
const fixer: Fixer = {
suggestCodeAction(params) {
const {j, ast} = params
@@ -95,31 +104,114 @@ const fixer: Fixer = {
j.Identifier.check(n.callee.property)
)
- if (!node || !node.loc) {
+ if (node && node.loc) {
+ const tag = getTagName(j, node)
+
+ return {
+ title: `Extract "${tag}" to styled component`,
+ data: {start: node.loc.start, type: 'rdom'},
+ }
+ }
+
+ const jsxElement = Ast.findLastNode(
+ ast,
+ j.JSXElement,
+ n =>
+ n.loc !== null &&
+ Range.isInside(params.selection, n.loc) &&
+ j.JSXIdentifier.check(n.openingElement.name) &&
+ isLowerCase(n.openingElement.name.name)
+ )
+
+ if (!jsxElement || !jsxElement.loc) {
return null
}
- const tag = getTagName(j, node)
+ const jsxNode = jsxElement.openingElement
+
+ if (jsxNode && jsxNode.loc && j.JSXIdentifier.check(jsxNode.name)) {
+ const tag = jsxNode.name.name
- return {
- title: `Extract "${tag}" to styled component`,
- data: node.loc.start,
+ return {
+ title: `Extract "${tag}" to styled component`,
+ data: {start: jsxElement.loc.start, type: 'jsx'},
+ }
}
+
+ return null
},
createEdit(params) {
const {data, ast, j} = params
- const node = Ast.findLastNode(ast, j.CallExpression, n => Ast.isOnPosition(n, data))
- if (!node) {
- return null
+ if (data.type === 'rdom') {
+ const node = Ast.findLastNode(ast, j.CallExpression, n => Ast.isOnPosition(n, data.start))
+ if (!node) {
+ return null
+ }
+
+ const rDomVar = getDefaultImportName(j, ast, 'r-dom')
+ if (!rDomVar) {
+ return null
+ }
+
+ const tag = getTagName(j, node)
+
+ if (!tag) {
+ return null
+ }
+
+ const componentName = 'Styled' + capitalize(tag)
+
+ const patches = []
+
+ const newNode = j.callExpression(j.identifier(rDomVar), [
+ j.identifier(componentName),
+ ...node.arguments,
+ ])
+
+ patches.push(Patch.replaceNode(j, node, newNode))
+
+ const lastImport = Ast.findLastNode(ast, j.ImportDeclaration, () => true)
+ const declarationStart =
+ lastImport && lastImport.loc
+ ? Position.create(lastImport.loc.end.line, lastImport.loc.end.column)
+ : Position.create(1, 0)
+
+ const topInserts: any = ['\n']
+
+ const shouldInsertStyledImport = !hasStyledImport(j, ast)
+ if (shouldInsertStyledImport) {
+ const styledImport = j.importDeclaration(
+ [j.importSpecifier(j.identifier('styled'))],
+ j.literal('styletron-react')
+ )
+ topInserts.push(styledImport)
+ topInserts.push('\n')
+ }
+
+ topInserts.push('\n')
+
+ const styledComponentDeclaration = j.variableDeclaration('const', [
+ j.variableDeclarator(
+ j.identifier(componentName),
+ j.callExpression(j.identifier('styled'), [j.literal(tag), j.objectExpression([])])
+ ),
+ ])
+
+ topInserts.push(styledComponentDeclaration)
+
+ patches.push(Patch.insert(j, declarationStart, topInserts))
+
+ return patches
}
- const rDomVar = getDefaultImportName(j, ast, 'r-dom')
- if (!rDomVar) {
+ const elementNode = Ast.findLastNode(ast, j.JSXElement, n => Ast.isOnPosition(n, data.start))
+ if (!elementNode) {
return null
}
+ const node = elementNode.openingElement
- const tag = getTagName(j, node)
+ const tag = j.JSXIdentifier.check(node.name) && node.name.name
if (!tag) {
return null
@@ -129,10 +221,21 @@ const fixer: Fixer = {
const patches = []
- const newNode = j.callExpression(j.identifier(rDomVar), [
- j.identifier(componentName),
- ...node.arguments,
- ])
+ const newNode = Ast.cloneNode(j, node)
+ newNode.name = j.jsxIdentifier(componentName)
+ newNode.attributes = newNode.attributes.filter(
+ attr =>
+ !(
+ j.JSXAttribute.check(attr) &&
+ j.JSXIdentifier.check(attr.name) &&
+ attr.name.name === 'style'
+ )
+ )
+ if (!node.selfClosing && elementNode.closingElement) {
+ const newNode = Ast.cloneNode(j, elementNode.closingElement)
+ newNode.name = j.jsxIdentifier(componentName)
+ patches.push(Patch.replaceNode(j, elementNode.closingElement, newNode))
+ }
patches.push(Patch.replaceNode(j, node, newNode))
@@ -142,7 +245,7 @@ const fixer: Fixer = {
? Position.create(lastImport.loc.end.line, lastImport.loc.end.column)
: Position.create(1, 0)
- const topInserts: any = ['\n']
+ const topInserts: any = lastImport ? ['\n'] : []
const shouldInsertStyledImport = !hasStyledImport(j, ast)
if (shouldInsertStyledImport) {
@@ -165,10 +268,10 @@ const fixer: Fixer = {
topInserts.push(styledComponentDeclaration)
+ topInserts.push('\n')
+ topInserts.push('\n')
- patches.push(
- Patch.insert(j, declarationStart, topInserts)
- )
+ patches.push(Patch.insert(j, declarationStart, topInserts))
return patches
},