|
1 | 1 | import { |
2 | | - Expression, |
3 | | - JSXElement, JSXText, Program, |
| 2 | + Expression, JSXAttribute, JSXAttrValue, |
| 3 | + JSXElement, JSXExpression, JSXText, Program, |
4 | 4 | } from '@swc/core'; |
5 | 5 | import Visitor from '@swc/core/Visitor'; |
6 | 6 | import { |
7 | | - ExprOrSpread, JSXElementChild |
| 7 | + ExprOrSpread, JSXElementChild, Pattern |
8 | 8 | } from '@swc/core/types'; |
9 | 9 | import { |
10 | 10 | buildArrayExpression, |
11 | | - buildArrowFunctionExpression, buildBooleanLiteral, buildCallExpression, buildIdentifier, buildImportDeclaration, |
| 11 | + buildArrowFunctionExpression, |
| 12 | + buildBooleanLiteral, |
| 13 | + buildCallExpression, |
| 14 | + buildIdentifier, |
| 15 | + buildImportDeclaration, |
12 | 16 | buildJSXElement, |
13 | | - buildJSXExpressionContainer, buildJSXText, buildNamedImportSpecifier, buildNullLiteral, buildStringLiteral |
| 17 | + buildJSXExpressionContainer, |
| 18 | + buildJSXText, |
| 19 | + buildMemberExpression, |
| 20 | + buildNamedImportSpecifier, |
| 21 | + buildNullLiteral, |
| 22 | + buildStringLiteral, buildThisExpression |
14 | 23 | } from './utils'; |
15 | 24 |
|
| 25 | +function JSXListToStandard(n: JSXElement) { |
| 26 | + let openingAttributes = n.opening.attributes; |
| 27 | + |
| 28 | + if (openingAttributes) { |
| 29 | + openingAttributes = openingAttributes.filter((attribute) => { |
| 30 | + if (attribute.type === 'JSXAttribute' && attribute.name.type === 'Identifier' && attribute.name.value === 'x-for') { |
| 31 | + return false; |
| 32 | + } |
| 33 | + return true; |
| 34 | + }); |
| 35 | + } |
| 36 | + return buildJSXElement({ |
| 37 | + ...n.opening, |
| 38 | + attributes: openingAttributes |
| 39 | + }, n.children, n.closing) |
| 40 | +} |
| 41 | + |
16 | 42 | function transformJSXList(n: JSXElement, currentList: JSXElementChild[], currentIndex: number): JSXElement | JSXText { |
| 43 | + n.children = n.children.map((c, i) => { |
| 44 | + if (c.type === 'JSXElement' && isJSXList(c)) { |
| 45 | + return transformJSXList(c, n.children, i); |
| 46 | + } |
| 47 | + return c; |
| 48 | + }); |
| 49 | + |
| 50 | + if (isJSXList(n)) { |
| 51 | + let attrValue = getJSXList(n); |
| 52 | + if (!attrValue || attrValue.type !== 'JSXExpressionContainer') { |
| 53 | + console.warn('ignore x-for due to stynax error.'); |
| 54 | + return n; |
| 55 | + } |
| 56 | + |
| 57 | + // @ts-ignore |
| 58 | + if (n.__listHandled) return n; |
| 59 | + // @ts-ignore |
| 60 | + n.__listHandled = true; |
| 61 | + |
| 62 | + let { expression } = attrValue; |
| 63 | + let params: Pattern[] = []; |
| 64 | + let iterValue: Expression; |
| 65 | + |
| 66 | + if (expression.type === 'BinaryExpression' && expression.operator === 'in') { |
| 67 | + // x-for={(item, index) in value} |
| 68 | + const { left, right } = expression; |
| 69 | + iterValue = right; |
| 70 | + |
| 71 | + if (left.type === 'ParenthesisExpression' && left.expression.type === 'SequenceExpression') { |
| 72 | + // x-for={(item, key) in value} |
| 73 | + params = left.expression.expressions; |
| 74 | + } else if (left.type === 'Identifier') { |
| 75 | + // x-for={item in value} |
| 76 | + params.push(buildIdentifier(left.value)); |
| 77 | + } else { |
| 78 | + // x-for={??? in value} |
| 79 | + throw new Error('Stynax error of x-for.'); |
| 80 | + } |
| 81 | + } else { |
| 82 | + // x-for={value}, x-for={callExp()}, ... |
| 83 | + iterValue = expression; |
| 84 | + } |
| 85 | + |
| 86 | + let callee = buildMemberExpression(buildIdentifier('__create_list__'), buildIdentifier('call')); |
| 87 | + let body = buildCallExpression(callee, [ |
| 88 | + { |
| 89 | + expression: buildThisExpression() |
| 90 | + }, |
| 91 | + { |
| 92 | + expression: iterValue |
| 93 | + }, |
| 94 | + { |
| 95 | + expression: buildArrowFunctionExpression(params, JSXListToStandard(n)) |
| 96 | + } |
| 97 | + ]) as any; |
| 98 | + |
| 99 | + return buildJSXExpressionContainer(body) as any; |
| 100 | + } |
| 101 | + |
17 | 102 | return n; |
18 | 103 | } |
19 | 104 |
|
| 105 | +function getJSXList(n: JSXElement): JSXAttrValue | undefined { |
| 106 | + let opening = n.opening; |
| 107 | + let openingAttributes = opening.attributes; |
| 108 | + |
| 109 | + if (openingAttributes) { |
| 110 | + for (let attribute of openingAttributes) { |
| 111 | + if (attribute.type === 'JSXAttribute' && attribute.name.type === 'Identifier' && attribute.name.value === 'x-for') { |
| 112 | + return attribute.value; |
| 113 | + } |
| 114 | + } |
| 115 | + } |
| 116 | +} |
| 117 | + |
| 118 | +function isJSXList(n: JSXElement): boolean { |
| 119 | + let opening = n.opening; |
| 120 | + let openingAttributes = opening.attributes; |
| 121 | + |
| 122 | + if (openingAttributes) { |
| 123 | + for (let attribute of openingAttributes) { |
| 124 | + if (attribute.type === 'JSXAttribute' && attribute.name.type === 'Identifier' && attribute.name.value === 'x-for') { |
| 125 | + return true; |
| 126 | + } |
| 127 | + } |
| 128 | + } |
| 129 | + return false; |
| 130 | +} |
| 131 | + |
20 | 132 | class JSXListTransformer extends Visitor { |
21 | 133 | visitJSXElement(n: JSXElement): JSXElement { |
22 | | - return n; |
| 134 | + return transformJSXList(n, [], -1) as JSXElement; |
23 | 135 | } |
24 | 136 | } |
25 | 137 |
|
|
0 commit comments