diff --git a/packages/wx-to-taro/package.json b/packages/wx-to-taro/package.json index f180d0d82fbe..35ce8da176e5 100644 --- a/packages/wx-to-taro/package.json +++ b/packages/wx-to-taro/package.json @@ -15,6 +15,7 @@ "author": "yuche", "license": "MIT", "dependencies": { + "@babel/code-frame": "^7.0.0", "babel-core": "^6.26.3", "babel-generator": "^6.26.1", "babel-template": "^6.26.0", diff --git a/packages/wx-to-taro/src/lifecycle.ts b/packages/wx-to-taro/src/lifecycle.ts new file mode 100644 index 000000000000..6ee42801facf --- /dev/null +++ b/packages/wx-to-taro/src/lifecycle.ts @@ -0,0 +1,29 @@ +const enum Lifecycle { + constructor = 'constructor', + componentWillMount = 'componentWillMount', + componentDidMount = 'componentDidMount', + componentWillUpdate = 'componentWillUpdate', + componentDidUpdate = 'componentDidUpdate', + componentWillUnmount = 'componentWillUnmount', + componentDidCatch = 'componentDidCatch', + componentDidShow = 'componentDidShow', + componentDidHide = 'componentDidHide', + componentDidAttached = 'componentDidAttached', + componentDidMoved = 'componentDidMoved', + shouldComponentUpdate = 'shouldComponentUpdate', + componentWillReceiveProps = 'componentWillReceiveProps' +} + +export const PageLifecycle = new Map() +PageLifecycle.set('onLoad', Lifecycle.componentWillMount) +PageLifecycle.set('onShow', Lifecycle.componentDidShow) +PageLifecycle.set('onReady ', Lifecycle.componentDidMount) +PageLifecycle.set('onHide ', Lifecycle.componentDidHide) +PageLifecycle.set('onUnload', Lifecycle.componentWillUnmount) + +export const ComponentLifeCycle = new Map() +ComponentLifeCycle.set('created', Lifecycle.componentWillMount) +ComponentLifeCycle.set('created', Lifecycle.componentWillMount) +ComponentLifeCycle.set('created', Lifecycle.componentWillMount) +ComponentLifeCycle.set('created', Lifecycle.componentWillMount) +ComponentLifeCycle.set('created', Lifecycle.componentWillMount) diff --git a/packages/wx-to-taro/src/script.ts b/packages/wx-to-taro/src/script.ts index 20a324697d46..16f8cc6cbeb1 100644 --- a/packages/wx-to-taro/src/script.ts +++ b/packages/wx-to-taro/src/script.ts @@ -1,10 +1,11 @@ import * as t from 'babel-types' import traverse, { NodePath } from 'babel-traverse' import { transform } from 'babel-core' -import { buildImportStatement } from './utils' +import { buildImportStatement, codeFrameError } from './utils' import { usedComponents } from './wxml' +import { PageLifecycle } from './lifecycle' -function parseScript (script: string) { +export function parseScript (script: string) { const { ast } = transform(script, { parserOpts: { sourceType: 'module', @@ -22,12 +23,18 @@ function parseScript (script: string) { 'dynamicImport' ] } - }) + }) as { ast: t.File } + let classDecl!: t.ClassDeclaration traverse(ast, { + Program (path) { + path.scope.rename('wx', 'Taro') + }, CallExpression (path) { const callee = path.get('callee') if (callee.isIdentifier({ name: 'Page' })) { - parsePage(path) + classDecl = parsePage(path)! + path.insertAfter(classDecl) + path.remove() path.stop() } } @@ -35,19 +42,58 @@ function parseScript (script: string) { const taroComponentsImport = buildImportStatement('@tarojs/components', [...usedComponents]) const taroImport = buildImportStatement('@tarojs/taro', [], 'Taro') + ast.program.body.unshift(taroComponentsImport, taroImport) } +const defaultClassName = 'C' + function parsePage (path: NodePath) { -} + const arg = path.get('arguments')[0] + if (!arg || !arg.isObjectExpression()) { + return + } + const props = arg.get('properties') + const properties = props.filter(p => !p.isSpreadProperty()) as NodePath[] + if (properties.length !== props.length) { + throw new Error('不支持编译在 Page 对象中使用解构(`...` spread property)语法') + } -function renameSetData (path: NodePath, guard?: (node: NodePath) => node is NodePath) { - const callee = path.get('callee') - if (callee.isMemberExpression() && callee.get('property').isIdentifier({ name: 'setData' })) { - const obj = callee.get('object') - const property = callee.get('property') - if (obj.isThisExpression()) { - property.replaceWith(t.identifier('setState')) - return + const classBody = properties.map(prop => { + const key = prop.get('key') + const value = prop.get('value') + if (!key.isIdentifier()) { + throw codeFrameError(key.node, 'Page 对象的键值只能是字符串') } - } + const name = key.node.name + if (name === 'data') { + return t.classProperty(t.identifier('state'), value.node) + } + if (PageLifecycle.has(name)) { + const lifecycle = PageLifecycle.get(name)! + return t.classMethod('method', t.identifier(lifecycle), [], value.node as any) + } + if (prop.isObjectMethod()) { + const body = prop.get('body') + return t.classProperty(t.identifier(name), t.arrowFunctionExpression([], body.node)) + } + return t.classProperty(t.identifier(name), value.isFunctionExpression() ? t.arrowFunctionExpression(value.node.params, value.node.body) : value.node) + }) + + return t.classDeclaration( + t.identifier(defaultClassName), + t.memberExpression(t.identifier('Taro'), t.identifier('Component')), + t.classBody(classBody) + ) } + +// function renameSetData (path: NodePath, guard?: (node: NodePath) => node is NodePath) { +// const callee = path.get('callee') +// if (callee.isMemberExpression() && callee.get('property').isIdentifier({ name: 'setData' })) { +// const obj = callee.get('object') +// const property = callee.get('property') +// if (obj.isThisExpression()) { +// property.replaceWith(t.identifier('setState')) +// return +// } +// } +// } diff --git a/packages/wx-to-taro/src/utils.ts b/packages/wx-to-taro/src/utils.ts index ac8867c759ef..12492e73c0d2 100644 --- a/packages/wx-to-taro/src/utils.ts +++ b/packages/wx-to-taro/src/utils.ts @@ -1,5 +1,6 @@ import * as template from 'babel-template' import * as t from 'babel-types' +import { codeFrameColumns } from '@babel/code-frame' export const buildTemplate = (str: string) => template(str)().expression as t.Expression @@ -15,6 +16,22 @@ export function buildImportStatement (source: string, specifiers: string[] = [], ) } +export function codeFrameError (node, msg: string) { + let errMsg = '' + try { + errMsg = codeFrameColumns(setting.sourceCode, node && node.type && node.loc ? node.loc : node) + } catch (error) { + errMsg = 'failed to locate source' + } + return new Error(`${msg} + ----- + ${errMsg}`) +} + +export const setting = { + sourceCode: '' +} + // tslint:disable-next-line export const DEFAULT_Component_SET = new Set([ 'View', diff --git a/packages/wx-to-taro/yarn.lock b/packages/wx-to-taro/yarn.lock index bafb4c500f4b..7ac3ead71b95 100644 --- a/packages/wx-to-taro/yarn.lock +++ b/packages/wx-to-taro/yarn.lock @@ -2,6 +2,12 @@ # yarn lockfile v1 +"@babel/code-frame@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" + dependencies: + "@babel/highlight" "^7.0.0" + "@babel/code-frame@^7.0.0-beta.35": version "7.0.0-rc.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-rc.2.tgz#12b6daeb408238360744649d16c0e9fa7ab3859e" @@ -16,6 +22,14 @@ esutils "^2.0.2" js-tokens "^4.0.0" +"@babel/highlight@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + "@types/babel-generator@^6.25.2": version "6.25.2" resolved "https://registry.yarnpkg.com/@types/babel-generator/-/babel-generator-6.25.2.tgz#fa13653ec2d34a4037be9c34dec32ae75bea04cc"