129 lines
3.4 KiB
TypeScript

import * as parser from '@babel/parser';
import generate from '@babel/generator';
import traverse from '@babel/traverse';
import * as t from '@babel/types';
import { JSExpression, JSFunction, isJSExpression, isJSFunction } from '@alilc/lowcode-types';
import { CodeGeneratorError, IScope } from '../types';
import { transformExpressionLocalRef, ParseError } from './expressionParser';
function parseFunction(content: string): t.FunctionExpression | null {
try {
const ast = parser.parse(`(${content});`);
let resultNode: t.FunctionExpression | null = null;
traverse(ast, {
FunctionExpression(path) {
resultNode = path.node;
path.stop();
},
});
return resultNode;
} catch (e) {
throw new ParseError(content, e);
}
}
function transformFuncExpr2MethodMember(methodName: string, content: string): string {
const funcNode = parseFunction(content);
if (funcNode) {
const targetNode = t.classMethod(
'method',
(methodName && t.identifier(methodName)) || funcNode.id || t.identifier('notDefineName'),
funcNode.params,
funcNode.body,
undefined,
undefined,
undefined,
funcNode.async || undefined,
);
const { code: resultCode } = generate(targetNode, { sourceMaps: false });
return resultCode;
}
throw new Error('Can not find Function Statement');
}
function getArrowFunction(content: string) {
const funcNode = parseFunction(content);
if (funcNode) {
const targetNode = t.arrowFunctionExpression(
funcNode.params,
funcNode.body,
funcNode.async || undefined,
);
const { code: resultCode } = generate(targetNode, { sourceMaps: false });
return resultCode;
}
throw new Error('Can not find Function Statement');
}
function getBodyStatements(content: string) {
const funcNode = parseFunction(content);
if (funcNode) {
const statements: t.Statement[] = funcNode.body.body;
const targetNode = t.program(statements, undefined, 'module', undefined);
const { code: resultCode } = generate(targetNode, { sourceMaps: false });
return resultCode;
}
throw new Error('Can not find Function Statement');
}
export function isJsCode(value: unknown): boolean {
return isJSExpression(value) || isJSFunction(value);
}
export function generateExpression(value: any, scope: IScope): string {
if (isJSExpression(value)) {
const exprVal = (value as JSExpression).value.trim();
if (!exprVal) {
return 'null';
}
const afterProcessWithLocals = transformExpressionLocalRef(exprVal, scope);
return afterProcessWithLocals;
}
throw new CodeGeneratorError('Not a JSExpression');
}
export function generateFunction(
value: any,
config: {
name?: string;
isMember?: boolean;
isBlock?: boolean;
isArrow?: boolean;
isBindExpr?: boolean;
} = {
name: undefined,
isMember: false,
isBlock: false,
isArrow: false,
isBindExpr: false,
},
) {
if (isJsCode(value)) {
const functionCfg = value as JSFunction;
if (config.isMember) {
return transformFuncExpr2MethodMember(config.name || '', functionCfg.value);
}
if (config.isBlock) {
return getBodyStatements(functionCfg.value);
}
if (config.isArrow) {
return getArrowFunction(functionCfg.value);
}
if (config.isBindExpr) {
return `(${functionCfg.value}).bind(this)`;
}
return functionCfg.value;
}
throw new CodeGeneratorError('Not a JSFunction or JSExpression');
}