mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-03-01 13:40:41 +00:00
feat: 🎸 优化 Rax 出码时对绑定的表达式的包裹逻辑, 对于一些简单的安全的表达式不做包裹
This commit is contained in:
parent
475534f51f
commit
facfa2afd6
@ -1,5 +1,6 @@
|
||||
import _ from 'lodash';
|
||||
import changeCase from 'change-case';
|
||||
import { Expression, MemberExpression } from '@babel/types';
|
||||
import {
|
||||
BuilderComponentPlugin,
|
||||
BuilderComponentPluginFactory,
|
||||
@ -25,7 +26,11 @@ import { generateExpression } from '../../../utils/jsExpression';
|
||||
import { CustomHandlerSet, generateUnknownType } from '../../../utils/compositeType';
|
||||
|
||||
import { IScopeBindings, ScopeBindings } from '../../../utils/ScopeBindings';
|
||||
import { parseExpressionConvertThis2Context, parseExpressionGetGlobalVariables } from '../../../utils/expressionParser';
|
||||
import {
|
||||
parseExpression,
|
||||
parseExpressionConvertThis2Context,
|
||||
parseExpressionGetGlobalVariables,
|
||||
} from '../../../utils/expressionParser';
|
||||
|
||||
type PluginConfig = {
|
||||
fileType: string;
|
||||
@ -181,7 +186,62 @@ function transformLoopExpr(expr: string, handlers: CustomHandlerSet) {
|
||||
}
|
||||
|
||||
function transformJsExpr(expr: string, handlers: CustomHandlerSet) {
|
||||
return isLiteralAtomicExpr(expr) ? expr : `__$$eval(() => (${transformThis2Context(expr, handlers)}))`;
|
||||
if (!expr) {
|
||||
return 'undefined';
|
||||
}
|
||||
|
||||
if (isLiteralAtomicExpr(expr)) {
|
||||
return expr;
|
||||
}
|
||||
|
||||
const exprAst = parseExpression(expr);
|
||||
switch (exprAst.type) {
|
||||
// 对于下面这些比较安全的字面值,可以直接返回对应的表达式,而非包一层
|
||||
case 'BigIntLiteral':
|
||||
case 'BooleanLiteral':
|
||||
case 'DecimalLiteral':
|
||||
case 'NullLiteral':
|
||||
case 'NumericLiteral':
|
||||
case 'RegExpLiteral':
|
||||
case 'StringLiteral':
|
||||
return expr;
|
||||
|
||||
// 对于直接写个函数的,则不用再包下,因为这样不会抛出异常的
|
||||
case 'ArrowFunctionExpression':
|
||||
case 'FunctionExpression':
|
||||
return transformThis2Context(exprAst, handlers);
|
||||
|
||||
// 对于直接访问 this.xxx, this.utils.xxx, this.state.xxx 的也不用再包下
|
||||
case 'MemberExpression':
|
||||
if (isSimpleDirectlyAccessingThis(exprAst) || isSimpleDirectlyAccessingSafeProperties(exprAst)) {
|
||||
return transformThis2Context(exprAst, handlers);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// 其他的都需要包一层
|
||||
return `__$$eval(() => (${transformThis2Context(exprAst, handlers)}))`;
|
||||
}
|
||||
|
||||
/** this.xxx */
|
||||
function isSimpleDirectlyAccessingThis(exprAst: MemberExpression) {
|
||||
return !exprAst.computed && exprAst.object.type === 'ThisExpression';
|
||||
}
|
||||
|
||||
/** this.state.xxx 和 this.utils.xxx 等安全的肯定应该存在的东东 */
|
||||
function isSimpleDirectlyAccessingSafeProperties(exprAst: MemberExpression): boolean {
|
||||
return (
|
||||
!exprAst.computed &&
|
||||
exprAst.object.type === 'MemberExpression' &&
|
||||
exprAst.object.object.type === 'ThisExpression' &&
|
||||
!exprAst.object.computed &&
|
||||
exprAst.object.property.type === 'Identifier' &&
|
||||
/^(state|utils|constants|i18n)$/.test(exprAst.object.property.name)
|
||||
);
|
||||
}
|
||||
|
||||
function isImportAliasDefineChunk(
|
||||
@ -206,14 +266,14 @@ function isImportAliasDefineChunk(
|
||||
* 判断是否是原子类型的表达式
|
||||
*/
|
||||
function isLiteralAtomicExpr(expr: string): boolean {
|
||||
return expr === 'null' || expr === 'undefined' || expr === 'true' || expr === 'false' || /^\d+$/.test(expr);
|
||||
return expr === 'null' || expr === 'undefined' || expr === 'true' || expr === 'false' || /^-?\d+(\.\d+)?$/.test(expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将所有的 this.xxx 替换为 __$$context.xxx
|
||||
* @param expr
|
||||
*/
|
||||
function transformThis2Context(expr: string, customHandlers: CustomHandlerSet): string {
|
||||
function transformThis2Context(expr: string | Expression, customHandlers: CustomHandlerSet): string {
|
||||
// return expr
|
||||
// .replace(/\bthis\.item\./g, () => 'item.')
|
||||
// .replace(/\bthis\.index\./g, () => 'index.')
|
||||
|
||||
@ -7,8 +7,8 @@ import { isIdentifier, Node } from '@babel/types';
|
||||
import { OrderedSet } from './OrderedSet';
|
||||
|
||||
export class ParseError extends Error {
|
||||
constructor(public readonly expr: string, public readonly detail: unknown) {
|
||||
super(`Failed to parse expression "${expr}"`);
|
||||
constructor(public readonly expr: string | t.Expression, public readonly detail: unknown) {
|
||||
super(`Failed to parse expression "${typeof expr === 'string' ? expr : generate(expr)}"`);
|
||||
Object.setPrototypeOf(this, new.target.prototype);
|
||||
}
|
||||
}
|
||||
@ -159,7 +159,7 @@ export function parseExpressionGetGlobalVariables(
|
||||
}
|
||||
|
||||
export function parseExpressionConvertThis2Context(
|
||||
expr: string,
|
||||
expr: string | t.Expression,
|
||||
contextName: string = '__$$context',
|
||||
localVariables: string[] = [],
|
||||
): string {
|
||||
@ -168,7 +168,7 @@ export function parseExpressionConvertThis2Context(
|
||||
}
|
||||
|
||||
try {
|
||||
const exprAst = parser.parseExpression(expr);
|
||||
const exprAst = typeof expr === 'string' ? parser.parseExpression(expr) : expr;
|
||||
const exprWrapAst = t.expressionStatement(exprAst);
|
||||
const fileAst = t.file(t.program([exprWrapAst]));
|
||||
|
||||
@ -229,11 +229,14 @@ export function parseExpressionConvertThis2Context(
|
||||
const { code } = generate(exprWrapAst.expression, { sourceMaps: false });
|
||||
return code;
|
||||
} catch (e) {
|
||||
// throw new ParseError(expr, e);
|
||||
throw e;
|
||||
throw new ParseError(expr, e);
|
||||
}
|
||||
}
|
||||
|
||||
function indent(level: number) {
|
||||
return ' '.repeat(level);
|
||||
export function parseExpression(expr: string) {
|
||||
try {
|
||||
return parser.parseExpression(expr);
|
||||
} catch (e) {
|
||||
throw new ParseError(expr, e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ class Home$$Page extends Component {
|
||||
<View>
|
||||
<Text>=== User Info: ===</Text>
|
||||
</View>
|
||||
{__$$eval(() => __$$context.state.user) && (
|
||||
{__$$context.state.user && (
|
||||
<View style={{ flexDirection: 'row' }}>
|
||||
<Image
|
||||
source={{ uri: __$$eval(() => __$$context.state.user.avatar) }}
|
||||
@ -128,7 +128,7 @@ class Home$$Page extends Component {
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Text>点击次数:{__$$eval(() => __$$context.state.clickCount)}(点击加 1)</Text>
|
||||
<Text>点击次数:{__$$context.state.clickCount}(点击加 1)</Text>
|
||||
</View>
|
||||
<View>
|
||||
<Text>操作提示:</Text>
|
||||
|
||||
@ -87,7 +87,7 @@
|
||||
isSync: true,
|
||||
},
|
||||
dataHandler: {
|
||||
type: 'JSFunction',
|
||||
type: 'JSExpression',
|
||||
value: 'function (response) {\nif (!response.success){\n throw new Error(response.message);\n }\n return response.data;\n}',
|
||||
},
|
||||
},
|
||||
@ -104,13 +104,13 @@
|
||||
isSync: true,
|
||||
},
|
||||
dataHandler: {
|
||||
type: 'JSFunction',
|
||||
type: 'JSExpression',
|
||||
value: 'function (response) {\nif (!response.success){\n throw new Error(response.message);\n }\n return response.data.result;\n}',
|
||||
},
|
||||
},
|
||||
],
|
||||
dataHandler: {
|
||||
type: 'JSFunction',
|
||||
type: 'JSExpression',
|
||||
value: 'function (dataMap) {\n console.info("All datasources loaded:", dataMap);\n}',
|
||||
},
|
||||
},
|
||||
@ -164,7 +164,7 @@
|
||||
componentName: 'View',
|
||||
props: {
|
||||
onClick: {
|
||||
type: 'JSFunction',
|
||||
type: 'JSExpression',
|
||||
value: 'this.hello',
|
||||
},
|
||||
},
|
||||
@ -210,7 +210,7 @@
|
||||
props: {
|
||||
style: { flexDirection: 'row' },
|
||||
onClick: {
|
||||
type: 'JSFunction',
|
||||
type: 'JSExpression',
|
||||
value: 'function(){ this.utils.recordEvent(`CLICK_ORDER`, this.order.title) }',
|
||||
},
|
||||
},
|
||||
@ -260,7 +260,7 @@
|
||||
componentName: 'View',
|
||||
props: {
|
||||
onClick: {
|
||||
type: 'JSFunction',
|
||||
type: 'JSExpression',
|
||||
value: 'function (){ this.setState({ clickCount: this.state.clickCount + 1 }) }',
|
||||
},
|
||||
},
|
||||
@ -312,7 +312,7 @@
|
||||
name: 'formatPrice',
|
||||
type: 'function',
|
||||
content: {
|
||||
type: 'JSFunction',
|
||||
type: 'JSExpression',
|
||||
value: 'function formatPrice(price, unit) { return Number(price).toFixed(2) + unit; }',
|
||||
},
|
||||
},
|
||||
@ -321,7 +321,7 @@
|
||||
name: 'recordEvent',
|
||||
type: 'function',
|
||||
content: {
|
||||
type: 'JSFunction',
|
||||
type: 'JSExpression',
|
||||
value: 'function recordEvent(eventName, eventDetail) { \n this.utils.Toast.show(`[EVENT]: ${eventName} ${eventDetail}`);\n console.log(`[EVENT]: ${eventName} (detail: %o) (user: %o)`, eventDetail, this.state.user); }',
|
||||
},
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user