diff --git a/src/basetran/handleMisc.js b/src/basetran/handleMisc.js
index a318326..5f7e398 100644
--- a/src/basetran/handleMisc.js
+++ b/src/basetran/handleMisc.js
@@ -35,12 +35,9 @@ export default function (ast, {isFuncComp}) {
* const x = 1
* export default x
*
- * 函数式组件在后续会被处理为class组件,这里不需要处理
- *
*/
- if (!isFuncComp
- && path.type === 'ExportDefaultDeclaration'
+ if (path.type === 'ExportDefaultDeclaration'
&& path.node.declaration
&& path.node.declaration.type === 'AssignmentExpression'
&& path.node.declaration.left.type === 'Identifier'
diff --git a/src/tran/funcCompToClassComp.js b/src/tran/funcCompToClassComp.js
index 35988ad..7477dcf 100644
--- a/src/tran/funcCompToClassComp.js
+++ b/src/tran/funcCompToClassComp.js
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
*/
-
+
import traverse from "@babel/traverse";
import * as t from "@babel/types";
import {miscNameToJSName} from '../util/util'
@@ -14,7 +14,7 @@ const npath = require('path')
/**
- * 把函数声明的组件, 转化为class
+ * 把函数声明的组件, 转化为FuncComponent 组件
*
* 1. export default () => {}
*
@@ -33,96 +33,141 @@ const npath = require('path')
*
*
* @param ast
- * @param info
* @returns {*}
*/
-export default function (ast, info) {
- let compName = npath.basename(miscNameToJSName(info.filepath), '.js')
- compName = compName.substring(0, 1).toUpperCase() + compName.substring(1)
+export default function funcCompToClassComp(ast, info) {
+
+ let funcPath = null
+ let hasJSXElement = false
traverse(ast, {
+ enter: path => {
+ // function x {}
+ if (path.type === 'FunctionDeclaration' && path.parentPath.type === 'Program') {
+ funcPath = path
+ hasJSXElement = false
+ }
+
+ // const x = () => {}
+ if (path.type === 'ArrowFunctionExpression'
+ && path.parentPath.type === 'VariableDeclarator'
+ && path.parentPath.parentPath.parentPath.type === 'Program'
+ ) {
+ funcPath = path
+ hasJSXElement = false
+ }
+
+ // export const x = () => {}
+ if (path.type === 'ArrowFunctionExpression'
+ && path.parentPath.type === 'VariableDeclarator'
+ && path.parentPath.parentPath.parentPath.type === 'ExportNamedDeclaration'
+ ) {
+ funcPath = path
+ hasJSXElement = false
+ }
+
+ // const b = function (){}
+ if (path.type === 'FunctionExpression'
+ && path.parentPath.type === 'VariableDeclarator'
+ && path.parentPath.parentPath.parentPath.type === 'Program'
+ ) {
+ funcPath = path
+ hasJSXElement = false
+ }
+
+ // export const b = function (){}
+ if (path.type === 'FunctionExpression'
+ && path.parentPath.type === 'VariableDeclarator'
+ && path.parentPath.parentPath.parentPath.type === 'ExportNamedDeclaration'
+ ) {
+ funcPath = path
+ hasJSXElement = false
+ }
+
+ // export default () => {}
+ if (path.type === 'ArrowFunctionExpression'
+ && path.parentPath.type === 'ExportDefaultDeclaration'
+ ) {
+ funcPath = path
+ hasJSXElement = false
+ }
+
+ // export default function () {}
+ if (path.type === 'FunctionDeclaration'
+ && path.parentPath.type === 'ExportDefaultDeclaration'
+ ) {
+ funcPath = path
+ hasJSXElement = false
+ }
+
+ // export default (function () {})
+ if (path.type === 'FunctionExpression'
+ && path.parentPath.type === 'ExportDefaultDeclaration'
+ ) {
+ funcPath = path
+ hasJSXElement = false
+ }
+
+ if (path.type === 'JSXOpeningElement') {
+ hasJSXElement = true
+ }
+ },
+
exit: path => {
- if (path.type === 'ExportDefaultDeclaration') {
- const declaration = path.node.declaration
+ if (path.type === 'ClassDeclaration' || path.type === 'ClassExpression') {
+ // 下面的path.replaceWith 将导致exit 在执行一次
+ return
+ }
+
+ if (path === funcPath && hasJSXElement) {
+ const funcPathNode = funcPath.node
+
+ if (funcPathNode.body.type !== 'BlockStatement') {
+ funcPathNode.body = t.blockStatement([
+ t.returnStatement(funcPathNode.body)
+ ])
+ }
- let renderBody = getRenderBody(declaration)
- path.node.declaration = t.classDeclaration(
- t.identifier(compName),
+ const propsVar = funcPathNode.params[0]
+ if (propsVar) {
+ const propDec = t.variableDeclaration('const', [
+ t.variableDeclarator(propsVar, t.memberExpression(t.thisExpression(), t.identifier('props')))
+ ])
+
+ funcPathNode.body.body.unshift(propDec)
+ }
+
+ const contextVar = funcPathNode.params[1]
+ if (contextVar) {
+ const contextDec = t.variableDeclaration('const', [
+ t.variableDeclarator(contextVar, t.memberExpression(t.thisExpression(), t.identifier('context')))
+ ])
+
+ funcPathNode.body.body.unshift(contextDec)
+ }
+
+
+ const classDec = t.classExpression(
+ path.node.id,
t.memberExpression(t.identifier('React'), t.identifier('FuncComponent')),
t.classBody([
t.classMethod(
'method',
t.identifier('render'),
[],
- t.blockStatement(renderBody)
+ funcPathNode.body
)
])
)
+
+ if (funcPathNode.type === 'FunctionDeclaration') {
+ classDec.type = 'ClassDeclaration'
+ }
+
+ path.replaceWith(classDec)
}
}
})
return ast
}
-
-
-function getRenderBody(declaration) {
- let renderBody = null
- let params = null
- if (declaration.type === 'AssignmentExpression') {
- const { operator, right} = declaration
- if (operator === '=' && (right.type === 'ArrowFunctionExpression' || right.type === 'FunctionExpression')) {
-
- params = right.params
-
- if (right.body.body) {
- renderBody = [...right.body.body]
- } else {
- renderBody = [
- t.returnStatement(right.body)
- ]
- }
- }
- }
-
- if (declaration.type === 'ArrowFunctionExpression') {
- const func = declaration
- params = func.params
- if (func.body.body) {
- renderBody = [...func.body.body]
- } else {
- renderBody = [
- t.returnStatement(func.body)
- ]
- }
- }
-
- if (declaration.type === 'FunctionDeclaration') {
- const func = declaration
- params = func.params
- renderBody = [
- ...func.body.body
- ]
- }
-
-
- const propsVar = params[0]
- if (propsVar) {
- const propDec = t.variableDeclaration('const', [
- t.variableDeclarator(propsVar, t.memberExpression(t.thisExpression(), t.identifier('props')))
- ])
-
- renderBody.unshift(propDec)
- }
-
- const contextVar = params[1]
- if (contextVar) {
- const contextDec = t.variableDeclaration('const', [
- t.variableDeclarator(contextVar, t.memberExpression(t.thisExpression(), t.identifier('context')))
- ])
-
- renderBody.unshift(contextDec)
- }
-
- return renderBody
-}
\ No newline at end of file
diff --git a/test/help/utils.js b/test/help/utils.js
index 4663f4d..77f190b 100644
--- a/test/help/utils.js
+++ b/test/help/utils.js
@@ -6,7 +6,7 @@
*
*/
-import {parseCode, geneCode} from '../../src/util/uast'
+import {parseCode, geneJSXCode} from '../../src/util/uast'
/**
* 通过trans把code转化为newCode
@@ -17,13 +17,13 @@ import {parseCode, geneCode} from '../../src/util/uast'
*/
export function getNewCode(code, info, ...trans) {
- let ast = parseCode(code)
+ let ast = parseCode(code, '.js')
for(let i = 0; i < trans.length; i ++) {
const tranFunc = trans[i]
ast = tranFunc.call(null, ast, info)
}
- return geneCode(ast)
+ return geneJSXCode(ast)
}
export function expectNewCode(code, expectCode, info, ...trans) {
diff --git a/test/tran/funcCompToClassComp.test.js b/test/tran/funcCompToClassComp.test.js
index c6e3a3a..1a1f59b 100644
--- a/test/tran/funcCompToClassComp.test.js
+++ b/test/tran/funcCompToClassComp.test.js
@@ -21,54 +21,137 @@ describe('function component to class component', () => {
}
})
- it('箭头函数组件转化为类组件声明', () => {
+ it('声明变量,变量是函数组件', () => {
const code = `
- export default () =>
+ const f = (props) =>
+
+ const h = function() {
+ return
+ }
+
+ const v = function v() {
+ return
+ }
+
+ const g = (props) => {
+ return
+ }
`
const expectCode = `
- export default class MyFunc extends React.FuncComponent {
+ const f = class extends React.FuncComponent {
render() {
- return ;
+ const props = this.props
+ return
+ }
+ }
+ const h = class extends React.FuncComponent {
+ render() {
+ return
+ }
+ }
+ const v = class v extends React.FuncComponent {
+ render() {
+ return
+ }
+ }
+ const g = class extends React.FuncComponent {
+ render() {
+ const props = this.props
+ return
}
+ }
+ `
+ expectNewCode(code, expectCode, {filepath: '/a/b/MyFunc.js'}, funcCompToClassComp)
+ })
+
+
+ it('导出变量,变量是函数组件', () => {
+ const code = `
+ export const f = (props) =>
+
+ export const h = function() {
+ return
+ }
+
+ export const v = function v() {
+ return
+ }
+ export const g = (props) => {
+ return
+ }
+ `
+ const expectCode = `
+ export const f = class extends React.FuncComponent {
+ render() {
+ const props = this.props
+ return
+ }
+ }
+ export const h = class extends React.FuncComponent {
+ render() {
+ return
+ }
+ }
+ export const v = class v extends React.FuncComponent {
+ render() {
+ return
+ }
+ }
+ export const g = class extends React.FuncComponent {
+ render() {
+ const props = this.props
+ return
+ }
}
`
expectNewCode(code, expectCode, {filepath: '/a/b/MyFunc.js'}, funcCompToClassComp)
})
- it('普通函数组件转化为类组件声明', () => {
+
+ it('默认导出 箭头函数组件', () => {
const code = `
- export default function(props) {
- const {a, b} = props
- return (
-
-
- {a}
- {b}
-
-
- )
- }
+ export default (props) =>
`
const expectCode = `
- export default class MyFunc extends React.FuncComponent {
+ export default (class extends React.FuncComponent {
render() {
const props = this.props
- const {
- a,
- b
- } = props;
- return (
-
- {a}
- {b}
-
- )
+ return
+ }
+ })
+ `
+ expectNewCode(code, expectCode, {filepath: '/a/b/MyFunc.js'}, funcCompToClassComp)
+ })
+
+ it('默认导出 函数组件', () => {
+ const code = `
+ export default function h() {return }
+ `
+ const expectCode = `
+ export default class h extends React.FuncComponent {
+ render() {
+ return
+ }
+ }
+ `
+ expectNewCode(code, expectCode, {filepath: '/a/b/MyFunc.js'}, funcCompToClassComp)
+ })
+
+ it('props, context 声明', () => {
+ const code = `
+ export default function h({a, b}, {c, d}) {return }
+ `
+ const expectCode = `
+ export default class h extends React.FuncComponent {
+ render() {
+ const { c, d } = this.context
+ const { a, b } = this.props
+ return
}
-
}
- `
+ `
expectNewCode(code, expectCode, {filepath: '/a/b/MyFunc.js'}, funcCompToClassComp)
})
+})
-})
\ No newline at end of file