mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-21 08:28:16 +00:00
refactor: 💡 node2jsx function
This commit is contained in:
parent
95d67c1d99
commit
e689d7f341
@ -13,7 +13,7 @@ import {
|
|||||||
import { RAX_CHUNK_NAME } from './const';
|
import { RAX_CHUNK_NAME } from './const';
|
||||||
import { COMMON_CHUNK_NAME } from '../../../const/generator';
|
import { COMMON_CHUNK_NAME } from '../../../const/generator';
|
||||||
|
|
||||||
import { createNodeGenerator, generateReactCtrlLine, generateString } from '../../../utils/nodeToJSX';
|
import { createNodeGenerator, generateReactCtrlLine } from '../../../utils/nodeToJSX';
|
||||||
import { generateExpression } from '../../../utils/jsExpression';
|
import { generateExpression } from '../../../utils/jsExpression';
|
||||||
|
|
||||||
type PluginConfig = {
|
type PluginConfig = {
|
||||||
@ -62,20 +62,15 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
|
|||||||
next.chunks = next.chunks.filter((chunk) => !isImportAliasDefineChunk(chunk));
|
next.chunks = next.chunks.filter((chunk) => !isImportAliasDefineChunk(chunk));
|
||||||
|
|
||||||
// 创建代码生成器
|
// 创建代码生成器
|
||||||
const generator = createNodeGenerator(
|
const generator = createNodeGenerator({
|
||||||
{
|
handlers: {
|
||||||
string: generateString,
|
|
||||||
expression: (input) => [handlers.expression(input)],
|
|
||||||
function: (input) => [handlers.function(input)],
|
|
||||||
},
|
|
||||||
[generateReactCtrlLine],
|
|
||||||
{
|
|
||||||
expression: (input: JSExpression) => (isJSExpression(input) ? handlers.expression(input) : ''),
|
expression: (input: JSExpression) => (isJSExpression(input) ? handlers.expression(input) : ''),
|
||||||
function: (input: JSFunction) => (isJSFunction(input) ? handlers.function(input) : ''),
|
function: (input: JSFunction) => (isJSFunction(input) ? handlers.function(input) : ''),
|
||||||
loopDataExpr: (input: string) => (typeof input === 'string' ? transformers.transformLoopExpr(input) : ''),
|
loopDataExpr: (input: string) => (typeof input === 'string' ? transformers.transformLoopExpr(input) : ''),
|
||||||
tagName: mapComponentNameToAliasOrKeepIt,
|
tagName: mapComponentNameToAliasOrKeepIt,
|
||||||
},
|
},
|
||||||
);
|
plugins: [generateReactCtrlLine],
|
||||||
|
});
|
||||||
|
|
||||||
// 生成 JSX 代码
|
// 生成 JSX 代码
|
||||||
const jsxContent = generator(ir);
|
const jsxContent = generator(ir);
|
||||||
|
|||||||
@ -23,7 +23,13 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
|
|||||||
...config,
|
...config,
|
||||||
};
|
};
|
||||||
|
|
||||||
const generator = createReactNodeGenerator({ nodeTypeMapping: cfg.nodeTypeMapping });
|
const { nodeTypeMapping } = cfg;
|
||||||
|
|
||||||
|
const generator = createReactNodeGenerator({
|
||||||
|
handlers: {
|
||||||
|
tagName: (v) => nodeTypeMapping[v] || v,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
|
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
|
||||||
const next: ICodeStruct = {
|
const next: ICodeStruct = {
|
||||||
|
|||||||
@ -12,8 +12,7 @@ import {
|
|||||||
} from '../../../types';
|
} from '../../../types';
|
||||||
import { COMMON_CHUNK_NAME } from '../../../const/generator';
|
import { COMMON_CHUNK_NAME } from '../../../const/generator';
|
||||||
|
|
||||||
import { createNodeGenerator, generateString } from '../../../utils/nodeToJSX';
|
import { createNodeGenerator } from '../../../utils/nodeToJSX';
|
||||||
import { generateExpression } from '../../../utils/jsExpression';
|
|
||||||
import { generateCompositeType } from '../../../utils/compositeType';
|
import { generateCompositeType } from '../../../utils/compositeType';
|
||||||
|
|
||||||
const generateGlobalProps = (ctx: INodeGeneratorContext, nodeItem: NodeSchema): CodePiece[] => {
|
const generateGlobalProps = (ctx: INodeGeneratorContext, nodeItem: NodeSchema): CodePiece[] => {
|
||||||
@ -53,13 +52,9 @@ const generateCtrlLine = (ctx: INodeGeneratorContext, nodeItem: NodeSchema): Cod
|
|||||||
};
|
};
|
||||||
|
|
||||||
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
|
const pluginFactory: BuilderComponentPluginFactory<unknown> = () => {
|
||||||
const generator = createNodeGenerator(
|
const generator = createNodeGenerator({
|
||||||
{
|
plugins: [generateGlobalProps, generateCtrlLine],
|
||||||
string: generateString,
|
});
|
||||||
expression: (input) => [generateExpression(input)],
|
|
||||||
},
|
|
||||||
[generateGlobalProps, generateCtrlLine],
|
|
||||||
);
|
|
||||||
|
|
||||||
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
|
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
|
||||||
const next: ICodeStruct = {
|
const next: ICodeStruct = {
|
||||||
|
|||||||
@ -1,15 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
ResultDir,
|
ResultDir,
|
||||||
ResultFile,
|
ResultFile,
|
||||||
NodeData,
|
NodeDataType,
|
||||||
NodeSchema,
|
NodeSchema,
|
||||||
ProjectSchema,
|
ProjectSchema,
|
||||||
JSExpression,
|
JSExpression,
|
||||||
JSFunction,
|
JSFunction,
|
||||||
CompositeArray,
|
|
||||||
CompositeObject,
|
|
||||||
JSONArray,
|
|
||||||
JSONObject,
|
|
||||||
JSSlot,
|
JSSlot,
|
||||||
} from '@ali/lowcode-types';
|
} from '@ali/lowcode-types';
|
||||||
|
|
||||||
@ -149,45 +145,38 @@ export interface CodePiece {
|
|||||||
type: PIECE_TYPE;
|
type: PIECE_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 这个 HandlerSet 和 CustomHandlerSet 为啥定义还不一样?
|
|
||||||
export interface HandlerSet<T> {
|
export interface HandlerSet<T> {
|
||||||
string?: (input: string) => T[];
|
string?: (input: string) => T;
|
||||||
expression?: (input: JSExpression) => T[];
|
boolean?: (input: boolean) => T;
|
||||||
function?: (input: JSFunction) => T[];
|
number?: (input: number) => T;
|
||||||
node?: (input: NodeSchema) => T[];
|
expression?: (input: JSExpression) => T;
|
||||||
common?: (input: unknown) => T[];
|
function?: (input: JSFunction) => T;
|
||||||
|
slot?: (input: JSSlot) => T;
|
||||||
|
node?: (input: NodeSchema) => T;
|
||||||
|
array?: (input: any[]) => T;
|
||||||
|
object?: (input: object) => T;
|
||||||
|
common?: (input: unknown) => T;
|
||||||
|
tagName?: (input: string) => T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ExtGeneratorPlugin = (
|
export type ExtGeneratorPlugin = (ctx: INodeGeneratorContext, nodeItem: NodeSchema) => CodePiece[];
|
||||||
ctx: INodeGeneratorContext,
|
|
||||||
nodeItem: NodeSchema,
|
|
||||||
handlers: CustomHandlerSet,
|
|
||||||
) => CodePiece[];
|
|
||||||
|
|
||||||
export interface INodeGeneratorConfig {
|
export type NodeGeneratorConfig = {
|
||||||
nodeTypeMapping?: Record<string, string>;
|
handlers?: HandlerSet<string>;
|
||||||
}
|
plugins?: ExtGeneratorPlugin[];
|
||||||
|
};
|
||||||
|
|
||||||
export type NodeGenerator = (nodeItem: NodeData) => string;
|
export type NodeGenerator = (nodeItem: NodeDataType) => string;
|
||||||
|
|
||||||
export interface INodeGeneratorContext {
|
export interface INodeGeneratorContext {
|
||||||
|
handlers: HandlerSet<string>;
|
||||||
generator: NodeGenerator;
|
generator: NodeGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CompositeValueCustomHandler = (data: unknown) => string;
|
export type CompositeValueCustomHandler = (data: unknown) => string;
|
||||||
export type CompositeTypeContainerHandler = (value: string) => string;
|
|
||||||
export interface CompositeValueCustomHandlerSet {
|
|
||||||
boolean?: CompositeValueCustomHandler;
|
|
||||||
number?: CompositeValueCustomHandler;
|
|
||||||
string?: CompositeValueCustomHandler;
|
|
||||||
array?: CompositeValueCustomHandler;
|
|
||||||
object?: CompositeValueCustomHandler;
|
|
||||||
expression?: CompositeValueCustomHandler;
|
|
||||||
function?: CompositeValueCustomHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CompositeValueGeneratorOptions = {
|
export type CompositeValueGeneratorOptions = {
|
||||||
handlers?: CompositeValueCustomHandlerSet;
|
handlers?: HandlerSet<string>;
|
||||||
containerHandler?: (value: string, isString: boolean, valStr: string) => string;
|
containerHandler?: (value: string, isString: boolean, valStr: string) => string;
|
||||||
nodeGenerator?: NodeGenerator;
|
nodeGenerator?: NodeGenerator;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { generateJsSlot } from './jsSlot';
|
|||||||
import { isValidIdentifier } from './validate';
|
import { isValidIdentifier } from './validate';
|
||||||
import { camelize } from './common';
|
import { camelize } from './common';
|
||||||
|
|
||||||
import { CompositeValueGeneratorOptions, CompositeTypeContainerHandler, CodeGeneratorError } from '../types';
|
import { CompositeValueGeneratorOptions, CodeGeneratorError } from '../types';
|
||||||
|
|
||||||
function generateArray(value: CompositeArray, options: CompositeValueGeneratorOptions = {}): string {
|
function generateArray(value: CompositeArray, options: CompositeValueGeneratorOptions = {}): string {
|
||||||
const body = value.map((v) => generateUnknownType(v, options)).join(',');
|
const body = value.map((v) => generateUnknownType(v, options)).join(',');
|
||||||
@ -110,12 +110,12 @@ function generateUnknownType(value: CompositeValue, options: CompositeValueGener
|
|||||||
return JSON.stringify(value);
|
return JSON.stringify(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultContainer: CompositeTypeContainerHandler = (v: string) => v;
|
const defaultContainer = (v: string) => v;
|
||||||
|
|
||||||
export function generateCompositeType(value: CompositeValue, options: CompositeValueGeneratorOptions = {}): string {
|
export function generateCompositeType(value: CompositeValue, options: CompositeValueGeneratorOptions = {}): string {
|
||||||
const isStringType = _.isString(value);
|
const isStringType = _.isString(value);
|
||||||
const result = generateUnknownType(value, options);
|
const result = generateUnknownType(value, options);
|
||||||
const handler: CompositeTypeContainerHandler = options.containerHandler || defaultContainer;
|
const handler = options.containerHandler || defaultContainer;
|
||||||
|
|
||||||
if (isStringType && result.length >= 2) {
|
if (isStringType && result.length >= 2) {
|
||||||
return handler(result, true, result.substring(1, result.length - 1));
|
return handler(result, true, result.substring(1, result.length - 1));
|
||||||
|
|||||||
@ -4,10 +4,12 @@ import {
|
|||||||
JSExpression,
|
JSExpression,
|
||||||
NodeData,
|
NodeData,
|
||||||
NodeSchema,
|
NodeSchema,
|
||||||
|
isNodeSchema,
|
||||||
PropsMap,
|
PropsMap,
|
||||||
isJSExpression,
|
isJSExpression,
|
||||||
isJSSlot,
|
isJSSlot,
|
||||||
isDOMText,
|
isDOMText,
|
||||||
|
NodeDataType,
|
||||||
} from '@ali/lowcode-types';
|
} from '@ali/lowcode-types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -18,11 +20,10 @@ import {
|
|||||||
NodeGenerator,
|
NodeGenerator,
|
||||||
ExtGeneratorPlugin,
|
ExtGeneratorPlugin,
|
||||||
INodeGeneratorContext,
|
INodeGeneratorContext,
|
||||||
INodeGeneratorConfig,
|
NodeGeneratorConfig,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
import { generateCompositeType } from './compositeType';
|
import { generateCompositeType } from './compositeType';
|
||||||
import { generateExpression } from './jsExpression';
|
|
||||||
|
|
||||||
// tslint:disable-next-line: no-empty
|
// tslint:disable-next-line: no-empty
|
||||||
const noop = () => [];
|
const noop = () => [];
|
||||||
@ -75,40 +76,6 @@ export function handleSubNodes<T>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleChildren<T>(
|
|
||||||
ctx: INodeGeneratorContext,
|
|
||||||
children: unknown,
|
|
||||||
handlers: HandlerSet<T>,
|
|
||||||
options?: {
|
|
||||||
rerun?: boolean;
|
|
||||||
},
|
|
||||||
): T[] {
|
|
||||||
const opt = {
|
|
||||||
...handleChildrenDefaultOptions,
|
|
||||||
...(options || {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Array.isArray(children)) {
|
|
||||||
const list: NodeData[] = children as NodeData[];
|
|
||||||
return list.map((child) => handleChildren(ctx, child, handlers, opt)).reduce((p, c) => p.concat(c), []);
|
|
||||||
} else if (isDOMText(children)) {
|
|
||||||
const handler = handlers.string || handlers.common || noop;
|
|
||||||
return handler(children as string);
|
|
||||||
} else if (isJSExpression(children)) {
|
|
||||||
const handler = handlers.expression || handlers.common || noop;
|
|
||||||
return handler(children as JSExpression);
|
|
||||||
} else {
|
|
||||||
const handler = handlers.node || handlers.common || noop;
|
|
||||||
const child = children as NodeSchema;
|
|
||||||
let curRes = handler(child);
|
|
||||||
if (opt.rerun && child.children) {
|
|
||||||
const childRes = handleChildren(ctx, child.children, handlers, opt);
|
|
||||||
curRes = curRes.concat(childRes || []);
|
|
||||||
}
|
|
||||||
return curRes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateAttr(ctx: INodeGeneratorContext, attrName: string, attrValue: any): CodePiece[] {
|
export function generateAttr(ctx: INodeGeneratorContext, attrName: string, attrValue: any): CodePiece[] {
|
||||||
if (attrName === 'initValue') {
|
if (attrName === 'initValue') {
|
||||||
return [];
|
return [];
|
||||||
@ -149,102 +116,24 @@ export function generateAttrs(ctx: INodeGeneratorContext, nodeItem: NodeSchema):
|
|||||||
return pieces;
|
return pieces;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapNodeName(src: string, mapping: Record<string, string>): string {
|
export function generateBasicNode(ctx: INodeGeneratorContext, nodeItem: NodeSchema): CodePiece[] {
|
||||||
if (mapping[src]) {
|
|
||||||
return mapping[src];
|
|
||||||
}
|
|
||||||
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateBasicNode(
|
|
||||||
ctx: INodeGeneratorContext,
|
|
||||||
nodeItem: NodeSchema,
|
|
||||||
mapping: Record<string, string>,
|
|
||||||
): CodePiece[] {
|
|
||||||
const pieces: CodePiece[] = [];
|
const pieces: CodePiece[] = [];
|
||||||
|
const tagName = (ctx.handlers.tagName || _.identity)(nodeItem.componentName);
|
||||||
|
|
||||||
pieces.push({
|
pieces.push({
|
||||||
value: mapNodeName(nodeItem.componentName, mapping),
|
value: tagName || '', // FIXME: type detection error
|
||||||
type: PIECE_TYPE.TAG,
|
type: PIECE_TYPE.TAG,
|
||||||
});
|
});
|
||||||
|
|
||||||
return pieces;
|
return pieces;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: 生成文档
|
export function linkPieces(pieces: CodePiece[]): string {
|
||||||
// 为包裹的代码片段生成子上下文,集成父级上下文,并传入子级上下文新增内容。(如果存在多级上下文怎么处理?)
|
|
||||||
// 创建新的上下文,并从作用域中取对应同名变量塞到作用域里面?
|
|
||||||
// export function createSubContext() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSX 生成逻辑插件。在 React 代码模式下生成 loop 与 condition 相关的逻辑代码
|
|
||||||
*
|
|
||||||
* @export
|
|
||||||
* @param {NodeSchema} nodeItem 当前 UI 节点
|
|
||||||
* @returns {CodePiece[]} 实现功能的相关代码片段
|
|
||||||
*/
|
|
||||||
export function generateReactCtrlLine(ctx: INodeGeneratorContext, nodeItem: NodeSchema): CodePiece[] {
|
|
||||||
const pieces: CodePiece[] = [];
|
|
||||||
|
|
||||||
if (nodeItem.loop) {
|
|
||||||
const loopItemName = nodeItem.loopArgs?.[0] || 'item';
|
|
||||||
const loopIndexName = nodeItem.loopArgs?.[1] || 'index';
|
|
||||||
|
|
||||||
const loopDataExpr = (handlers.loopDataExpr || _.identity)(
|
|
||||||
isJSExpression(nodeItem.loop) ? `(${nodeItem.loop.value})` : `(${JSON.stringify(nodeItem.loop)})`,
|
|
||||||
);
|
|
||||||
|
|
||||||
pieces.unshift({
|
|
||||||
value: `${loopDataExpr}.map((${loopItemName}, ${loopIndexName}) => (`,
|
|
||||||
type: PIECE_TYPE.BEFORE,
|
|
||||||
});
|
|
||||||
|
|
||||||
pieces.push({
|
|
||||||
value: '))',
|
|
||||||
type: PIECE_TYPE.AFTER,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nodeItem.condition) {
|
|
||||||
const value = generateCompositeType(nodeItem.condition, {
|
|
||||||
nodeGenerator: ctx.generator,
|
|
||||||
});
|
|
||||||
|
|
||||||
const conditionExpr = (handlers.conditionExpr || _.identity)(value);
|
|
||||||
|
|
||||||
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);
|
const tagsPieces = pieces.filter((p) => p.type === PIECE_TYPE.TAG);
|
||||||
if (tagsPieces.length !== 1) {
|
if (tagsPieces.length !== 1) {
|
||||||
throw new CodeGeneratorError('One node only need one tag define');
|
throw new CodeGeneratorError('One node only need one tag define');
|
||||||
}
|
}
|
||||||
|
const tagName = tagsPieces[0].value;
|
||||||
const tagName = (handlers.tagName || _.identity)(tagsPieces[0].value);
|
|
||||||
|
|
||||||
const beforeParts = pieces
|
const beforeParts = pieces
|
||||||
.filter((p) => p.type === PIECE_TYPE.BEFORE)
|
.filter((p) => p.type === PIECE_TYPE.BEFORE)
|
||||||
@ -275,53 +164,133 @@ export function linkPieces(pieces: CodePiece[], handlers: CustomHandlerSet): str
|
|||||||
return `${beforeParts}<${tagName}${attrsParts} />${afterParts}`;
|
return `${beforeParts}<${tagName}${attrsParts} />${afterParts}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createNodeGenerator(
|
export function generateNodeSchema(nodeItem: NodeSchema, ctx: INodeGeneratorContext): string {
|
||||||
handlers: HandlerSet<string>,
|
let pieces: CodePiece[] = [];
|
||||||
plugins: ExtGeneratorPlugin[],
|
|
||||||
cfg?: INodeGeneratorConfig,
|
plugins.forEach((p) => {
|
||||||
): NodeGenerator {
|
pieces = pieces.concat(p(ctx, nodeItem));
|
||||||
let nodeTypeMapping: Record<string, string> = {};
|
});
|
||||||
if (cfg && cfg.nodeTypeMapping) {
|
pieces = pieces.concat(generateBasicNode(ctx, nodeItem));
|
||||||
nodeTypeMapping = cfg.nodeTypeMapping;
|
pieces = pieces.concat(generateAttrs(ctx, nodeItem));
|
||||||
|
if (nodeItem.children && (nodeItem.children as unknown[]).length > 0) {
|
||||||
|
pieces = pieces.concat(
|
||||||
|
handleChildren<string>(ctx, nodeItem.children, handlers).map((l) => ({
|
||||||
|
type: PIECE_TYPE.CHILDREN,
|
||||||
|
value: l,
|
||||||
|
})),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateNode = (nodeItem: NodeSchema): string => {
|
return linkPieces(pieces);
|
||||||
let pieces: CodePiece[] = [];
|
}
|
||||||
const ctx: INodeGeneratorContext = {
|
|
||||||
generator: generateNode,
|
|
||||||
};
|
|
||||||
|
|
||||||
plugins.forEach((p) => {
|
// TODO: 生成文档
|
||||||
pieces = pieces.concat(p(ctx, nodeItem));
|
// 为包裹的代码片段生成子上下文,集成父级上下文,并传入子级上下文新增内容。(如果存在多级上下文怎么处理?)
|
||||||
|
// 创建新的上下文,并从作用域中取对应同名变量塞到作用域里面?
|
||||||
|
// export function createSubContext() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSX 生成逻辑插件。在 React 代码模式下生成 loop 与 condition 相关的逻辑代码
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @param {NodeSchema} nodeItem 当前 UI 节点
|
||||||
|
* @returns {CodePiece[]} 实现功能的相关代码片段
|
||||||
|
*/
|
||||||
|
export function generateReactCtrlLine(ctx: INodeGeneratorContext, nodeItem: NodeSchema): CodePiece[] {
|
||||||
|
const pieces: CodePiece[] = [];
|
||||||
|
|
||||||
|
if (nodeItem.loop) {
|
||||||
|
const loopItemName = nodeItem.loopArgs?.[0] || 'item';
|
||||||
|
const loopIndexName = nodeItem.loopArgs?.[1] || 'index';
|
||||||
|
|
||||||
|
const loopDataExpr = (ctx.handlers.loopDataExpr || _.identity)(
|
||||||
|
isJSExpression(nodeItem.loop) ? `(${nodeItem.loop.value})` : `(${JSON.stringify(nodeItem.loop)})`,
|
||||||
|
);
|
||||||
|
|
||||||
|
pieces.unshift({
|
||||||
|
value: `${loopDataExpr}.map((${loopItemName}, ${loopIndexName}) => (`,
|
||||||
|
type: PIECE_TYPE.BEFORE,
|
||||||
});
|
});
|
||||||
pieces = pieces.concat(generateBasicNode(ctx, nodeItem, nodeTypeMapping));
|
|
||||||
pieces = pieces.concat(generateAttrs(ctx, nodeItem));
|
pieces.push({
|
||||||
if (nodeItem.children && (nodeItem.children as unknown[]).length > 0) {
|
value: '))',
|
||||||
pieces = pieces.concat(
|
type: PIECE_TYPE.AFTER,
|
||||||
handleChildren<string>(ctx, nodeItem.children, handlers).map((l) => ({
|
});
|
||||||
type: PIECE_TYPE.CHILDREN,
|
}
|
||||||
value: l,
|
|
||||||
})),
|
if (nodeItem.condition) {
|
||||||
);
|
const value = generateCompositeType(nodeItem.condition, {
|
||||||
|
nodeGenerator: ctx.generator,
|
||||||
|
});
|
||||||
|
|
||||||
|
const conditionExpr = (ctx.handlers.conditionExpr || _.identity)(value);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleArray = (v: string[]) => v.join('');
|
||||||
|
|
||||||
|
export function createNodeGenerator(cfg: NodeGeneratorConfig = {}): NodeGenerator {
|
||||||
|
let ctx: INodeGeneratorContext = { handlers: {}, generator: () => '' };
|
||||||
|
|
||||||
|
const generateNode = (nodeItem: NodeDataType): string => {
|
||||||
|
if (_.isArray(nodeItem)) {
|
||||||
|
const resList = nodeItem.map((n) => generateNode(n));
|
||||||
|
return (cfg?.handlers?.array || handleArray)(resList);
|
||||||
}
|
}
|
||||||
|
|
||||||
return linkPieces(pieces, customHandlers);
|
if (isNodeSchema(nodeItem)) {
|
||||||
|
if (cfg?.handlers?.node) {
|
||||||
|
// TODO: children 的处理是否拆出来作为公用
|
||||||
|
return cfg.handlers.node(nodeItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateNodeSchema(nodeItem, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateCompositeType(nodeItem, {
|
||||||
|
handlers: cfg.handlers,
|
||||||
|
// FIXME: 这里和 children 类型的嵌套逻辑需要再思考一下
|
||||||
|
// containerHandler: (value: string, isString: boolean, valStr: string) => (isString ? valStr : value),
|
||||||
|
nodeGenerator: generateNode,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handlers.node = (input: NodeSchema) => [generateNode(input)];
|
ctx = {
|
||||||
|
handlers: cfg?.handlers || {},
|
||||||
|
generator: generateNode,
|
||||||
|
};
|
||||||
|
|
||||||
return generateNode;
|
return generateNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateString = (input: string) => [input];
|
// TODO: 需要一个 merge config 的方法。
|
||||||
|
export function createReactNodeGenerator(cfg?: NodeGeneratorConfig): NodeGenerator {
|
||||||
export function createReactNodeGenerator(cfg?: INodeGeneratorConfig): NodeGenerator {
|
return createNodeGenerator({
|
||||||
return createNodeGenerator(
|
plugins: [generateReactCtrlLine],
|
||||||
{
|
...cfg,
|
||||||
string: generateString,
|
});
|
||||||
expression: (input) => [generateExpression(input)],
|
|
||||||
},
|
|
||||||
[generateReactCtrlLine],
|
|
||||||
cfg,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user