import _ from 'lodash'; import { PIECE_TYPE, CodeGeneratorError, CodePiece, HandlerSet, ExtGeneratorPlugin, isJSExpression, isJSFunction, NodeData, CompositeValue, NodeSchema, } from '../types'; import { CustomHandlerSet, generateCompositeType, generateUnknownType } from './compositeType'; import { generateExpression } from './jsExpression'; // tslint:disable-next-line: no-empty const noop = () => []; export function handleChildren(children: NodeData | NodeData[], handlers: HandlerSet): string[] { if (Array.isArray(children)) { return children.map((child) => handleChildren(child, handlers)).reduce((p, c) => p.concat(c), []); } else if (typeof children === 'string') { const handler = handlers.string || handlers.common || noop; return handler(children); } else if (isJSExpression(children)) { const handler = handlers.expression || handlers.common || noop; return ['{', ...handler(children), '}']; } else if (isJSFunction(children)) { const handler = handlers.function || handlers.common || noop; return handler(children); } else { const handler = handlers.node || handlers.common || noop; return handler(children); } } export function generateAttr( attrName: string, attrValue: CompositeValue, customHandlers: CustomHandlerSet, ): CodePiece[] { if (customHandlers.nodeAttr) { return customHandlers.nodeAttr(attrName, attrValue); } // 为了规避一个上游 schema 引入的 bug if (attrName === 'initValue' || attrName === 'labelCol') { return []; } if (typeof attrValue === 'string') { return [ { value: `${attrName}=${JSON.stringify(attrValue)}`, type: PIECE_TYPE.ATTR, }, ]; } return [ { value: `${attrName}={${generateUnknownType(attrValue, customHandlers)}}`, type: PIECE_TYPE.ATTR, }, ]; } export function generateAttrs(nodeItem: NodeSchema, customHandlers: CustomHandlerSet): CodePiece[] { if (customHandlers.nodeAttrs) { return customHandlers.nodeAttrs(nodeItem); } const { props } = nodeItem; let pieces: CodePiece[] = []; if (props) { if (!Array.isArray(props)) { Object.keys(props).forEach((propName: string) => { pieces = pieces.concat(generateAttr(propName, props[propName], customHandlers)); }); } else { props.forEach((prop) => { if (prop.name && !prop.spread) { pieces = pieces.concat(generateAttr(prop.name, prop.value, customHandlers)); } // TODO: 处理 spread 场景() }); } } return pieces; } export function mapNodeName(src: string): string { if (src === 'Div') { return 'div'; } return src; } export function generateBasicNode(nodeItem: NodeSchema): CodePiece[] { const pieces: CodePiece[] = []; pieces.push({ value: mapNodeName(nodeItem.componentName), type: PIECE_TYPE.TAG, }); return pieces; } export function generateReactCtrlLine(nodeItem: NodeSchema, handlers: CustomHandlerSet): CodePiece[] { const pieces: CodePiece[] = []; if (nodeItem.loop) { const loopItemName = nodeItem.loopArgs?.[0] || 'item'; const loopIndexName = nodeItem.loopArgs?.[1] || 'index'; const rawLoopDataExpr = isJSExpression(nodeItem.loop) ? `(${nodeItem.loop.value})` : `(${JSON.stringify(nodeItem.loop)})`; const loopDataExpr = handlers.loopDataExpr ? handlers.loopDataExpr(rawLoopDataExpr) : rawLoopDataExpr; pieces.unshift({ value: `${loopDataExpr}.map((${loopItemName}, ${loopIndexName}) => (`, type: PIECE_TYPE.BEFORE, }); pieces.push({ value: '))', type: PIECE_TYPE.AFTER, }); } if (nodeItem.condition) { const [isString, value] = generateCompositeType(nodeItem.condition, handlers); const rawConditionExpr = isString ? `'${value}'` : value; const conditionExpr = handlers.conditionExpr ? handlers.conditionExpr(rawConditionExpr) : rawConditionExpr; pieces.unshift({ value: `(${conditionExpr}) && (`, type: PIECE_TYPE.BEFORE, }); pieces.push({ value: ')', type: PIECE_TYPE.AFTER, }); } if (nodeItem.condition || nodeItem.loop) { pieces.unshift({ value: '{', type: PIECE_TYPE.BEFORE, }); pieces.push({ value: '}', type: PIECE_TYPE.AFTER, }); } return pieces; } export function linkPieces(pieces: CodePiece[], handlers: CustomHandlerSet): string { const tagsPieces = pieces.filter((p) => p.type === PIECE_TYPE.TAG); if (tagsPieces.length !== 1) { throw new CodeGeneratorError('One node only need one tag define'); } const rawTagName = tagsPieces[0].value; const tagName = handlers.tagName ? handlers.tagName(rawTagName) : rawTagName; const beforeParts = pieces .filter((p) => p.type === PIECE_TYPE.BEFORE) .map((p) => p.value) .join(''); const afterParts = pieces .filter((p) => p.type === PIECE_TYPE.AFTER) .map((p) => p.value) .join(''); const childrenParts = pieces .filter((p) => p.type === PIECE_TYPE.CHILDREN) .map((p) => p.value) .join(''); let attrsParts = pieces .filter((p) => p.type === PIECE_TYPE.ATTR) .map((p) => p.value) .join(' '); attrsParts = !!attrsParts ? ` ${attrsParts}` : ''; if (childrenParts) { return `${beforeParts}<${tagName}${attrsParts}>${childrenParts}${afterParts}`; } return `${beforeParts}<${tagName}${attrsParts} />${afterParts}`; } export function createNodeGenerator( handlers: HandlerSet, plugins: ExtGeneratorPlugin[], customHandlers: CustomHandlerSet = {}, ) { const generateNode = (nodeItem: NodeSchema): string => { let pieces: CodePiece[] = []; plugins.forEach((p) => { pieces = pieces.concat(p(nodeItem, customHandlers)); }); pieces = pieces.concat(generateBasicNode(nodeItem)); pieces = pieces.concat(generateAttrs(nodeItem, customHandlers)); if (nodeItem.children && !_.isEmpty(nodeItem.children)) { pieces = pieces.concat( handleChildren(nodeItem.children, handlers).map((l) => ({ type: PIECE_TYPE.CHILDREN, value: l, })), ); } return linkPieces(pieces, customHandlers); }; handlers.node = (node: NodeSchema) => [generateNode(node)]; customHandlers.node = customHandlers.node || generateNode; return generateNode; } export const generateString = (input: string) => [input]; export function createReactNodeGenerator() { return createNodeGenerator( { string: generateString, expression: (input) => [generateExpression(input)], }, [generateReactCtrlLine], ); }