Skip to content

Commit

Permalink
feat(wx-to-taro): 支持复杂的 if 的表达式
Browse files Browse the repository at this point in the history
  • Loading branch information
yuche authored and luckyadam committed Nov 19, 2018
1 parent 525e5dc commit 718663e
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 12 deletions.
1 change: 1 addition & 0 deletions packages/wx-to-taro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@types/node": "^9.6.2",
"jest": "^23.0.1",
"jest-cli": "^22.1.4",
"prettier": "^1.14.2",
"ts-jest": "^22.4.6",
"tslint": "^5.10.0",
"tslint-config-prettier": "^1.10.0",
Expand Down
122 changes: 110 additions & 12 deletions packages/wx-to-taro/src/wxml.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { parse } from 'himalaya'
import * as t from 'babel-types'
import { camelCase } from 'lodash'
import traverse from 'babel-traverse'
import { camelCase, cloneDeep } from 'lodash'
import traverse, { NodePath } from 'babel-traverse'
import * as template from 'babel-template'
// const template = require('@babel/template')
import generate from 'babel-generator'

const buildTemplate = (str: string) => template(str)().expression as t.Expression
const allCamelCase = (str: string) => str.charAt(0).toUpperCase() + camelCase(str.substr(1))
Expand Down Expand Up @@ -38,39 +38,137 @@ interface Text {

type AllKindNode = Element | Comment | Text
type Node = Element | Text
interface Condition {
condition: string,
path: NodePath<t.JSXElement>,
tester: t.JSXExpressionContainer
}

export function parseWXML (wxml: string) {
const nodes = (parse(wxml.trim()) as AllKindNode[]).filter(node => node.type !== NodeType.Comment) as Node[]
const ast = nodes.map(parseNode).find(node => t.isJSXElement(node))
type Tester = t.StringLiteral | t.JSXElement | t.JSXExpressionContainer | null

const WX_IF = 'wx:if'
const WX_ELSE = 'wx:else'
const WX_ELSE_IF = 'wx:elif'

traverse(t.file(t.program([t.expressionStatement(ast as t.Expression)], [])), {
export function parseWXML (wxml: string) {
const nodes = (parse(wxml.trim()) as AllKindNode[]).filter(removEmptyText).filter(node => node.type !== NodeType.Comment) as Node[]
const ast = t.file(t.program([t.expressionStatement(nodes.map(parseNode).find(node => t.isJSXElement(node)) as t.Expression)], []))
traverse(ast, {
JSXAttribute (path) {
// path.node.name = t.jSXIdentifier('fuck')
const name = path.node.name as t.JSXIdentifier
const jsx = path.findParent(p => p.isJSXElement()) as NodePath<t.JSXElement>
const tester = cloneDeep(path.get('value').node)
const conditions: Condition[] = []
if (name.name === WX_IF) {
const siblings = jsx.getAllNextSiblings()
conditions.push({
condition: WX_IF,
path: jsx,
tester: tester as t.JSXExpressionContainer
})
path.remove()
for (const [ index, sibling ] of siblings.entries()) {
const next = siblings[index + 1]
const currMatches = findWXIfProps(sibling)
const nextMatches = findWXIfProps(next)
if (currMatches === null) {
break
}
conditions.push({
condition: currMatches.reg.input as string,
path: sibling as any,
tester: currMatches.tester as t.JSXExpressionContainer
})
if (nextMatches === null) {
break
}
}
}
handleConditions(conditions)
}
})

return ast
}

function parseNode (node: Node, index: number, nodes: Node[]) {
function handleConditions (conditions: Condition[]) {
if (conditions.length === 1) {
const ct = conditions[0]
ct.path.replaceWith(
t.jSXExpressionContainer(
t.logicalExpression('&&', ct.tester.expression, cloneDeep(ct.path.node))
)
)
}
if (conditions.length > 1) {
const lastLength = conditions.length - 1
const lastCon = conditions[lastLength]
let lastAlternate: t.Expression = cloneDeep(lastCon.path.node)
if (lastCon.condition === WX_ELSE_IF) {
lastAlternate = t.logicalExpression('&&', lastCon.tester.expression, lastAlternate)
}
const node = conditions.slice(0, lastLength).reduceRight((acc: t.Expression, condition) => {
return t.conditionalExpression(condition.tester.expression, cloneDeep(condition.path.node), acc)
}, lastAlternate)
conditions[0].path.replaceWith(t.jSXExpressionContainer(node))
conditions.slice(1).forEach(c => c.path.remove())
}
}

function findWXIfProps (jsx: NodePath<t.Node>): { reg: RegExpMatchArray, tester: Tester } | null {
let matches: { reg: RegExpMatchArray, tester: Tester } | null = null
jsx && jsx.isJSXElement() && jsx
.get('openingElement')
.get('attributes')
.some(path => {
const attr = path.node
if (t.isJSXIdentifier(attr.name)) {
const name = attr.name.name
if (name === WX_IF) {
return true
}
const match = name.match(/wx:else|wx:elif/)
if (match) {
path.remove()
matches = {
reg: match,
tester: attr.value
}
return true
}
}
return false
})

return matches
}

function parseNode (node: Node) {
if (node.type === NodeType.Text) {
return parseText(node)
}
return parseElement(node, nodes[index + 1])
return parseElement(node)
}

function parseElement (element: Element, nextElement?: Node): t.JSXElement {
function parseElement (element: Element): t.JSXElement {
const tagName = t.jSXIdentifier(
allCamelCase(element.tagName)
)
return t.jSXElement(
t.jSXOpeningElement(tagName, element.attributes.map(parseAttribute)),
t.jSXClosingElement(tagName),
element.children.map(parseNode),
element.children.filter(removEmptyText).map(parseNode),
false
)
}

function removEmptyText (node: Node) {
if (node.type === NodeType.Text && node.content.trim().length === 0) {
return false
}
return true
}

function parseText (node: Text) {
const { type, content } = parseContent(node.content)
if (type === 'raw') {
Expand Down
4 changes: 4 additions & 0 deletions packages/wx-to-taro/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2813,6 +2813,10 @@ preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"

prettier@^1.14.2:
version "1.14.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.2.tgz#0ac1c6e1a90baa22a62925f41963c841983282f9"

pretty-format@^22.4.0, pretty-format@^22.4.3:
version "22.4.3"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.3.tgz#f873d780839a9c02e9664c8a082e9ee79eaac16f"
Expand Down

0 comments on commit 718663e

Please sign in to comment.