mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-03-04 00:37:08 +00:00
feat: 🎸 优化 Rax 出码时对绑定的表达式的包裹逻辑, 对于一些简单的安全的表达式不做包裹
This commit is contained in:
parent
475534f51f
commit
facfa2afd6
@ -1,5 +1,6 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import changeCase from 'change-case';
|
import changeCase from 'change-case';
|
||||||
|
import { Expression, MemberExpression } from '@babel/types';
|
||||||
import {
|
import {
|
||||||
BuilderComponentPlugin,
|
BuilderComponentPlugin,
|
||||||
BuilderComponentPluginFactory,
|
BuilderComponentPluginFactory,
|
||||||
@ -25,7 +26,11 @@ import { generateExpression } from '../../../utils/jsExpression';
|
|||||||
import { CustomHandlerSet, generateUnknownType } from '../../../utils/compositeType';
|
import { CustomHandlerSet, generateUnknownType } from '../../../utils/compositeType';
|
||||||
|
|
||||||
import { IScopeBindings, ScopeBindings } from '../../../utils/ScopeBindings';
|
import { IScopeBindings, ScopeBindings } from '../../../utils/ScopeBindings';
|
||||||
import { parseExpressionConvertThis2Context, parseExpressionGetGlobalVariables } from '../../../utils/expressionParser';
|
import {
|
||||||
|
parseExpression,
|
||||||
|
parseExpressionConvertThis2Context,
|
||||||
|
parseExpressionGetGlobalVariables,
|
||||||
|
} from '../../../utils/expressionParser';
|
||||||
|
|
||||||
type PluginConfig = {
|
type PluginConfig = {
|
||||||
fileType: string;
|
fileType: string;
|
||||||
@ -181,7 +186,62 @@ function transformLoopExpr(expr: string, handlers: CustomHandlerSet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function transformJsExpr(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(
|
function isImportAliasDefineChunk(
|
||||||
@ -206,14 +266,14 @@ function isImportAliasDefineChunk(
|
|||||||
* 判断是否是原子类型的表达式
|
* 判断是否是原子类型的表达式
|
||||||
*/
|
*/
|
||||||
function isLiteralAtomicExpr(expr: string): boolean {
|
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
|
* 将所有的 this.xxx 替换为 __$$context.xxx
|
||||||
* @param expr
|
* @param expr
|
||||||
*/
|
*/
|
||||||
function transformThis2Context(expr: string, customHandlers: CustomHandlerSet): string {
|
function transformThis2Context(expr: string | Expression, customHandlers: CustomHandlerSet): string {
|
||||||
// return expr
|
// return expr
|
||||||
// .replace(/\bthis\.item\./g, () => 'item.')
|
// .replace(/\bthis\.item\./g, () => 'item.')
|
||||||
// .replace(/\bthis\.index\./g, () => 'index.')
|
// .replace(/\bthis\.index\./g, () => 'index.')
|
||||||
|
|||||||
@ -7,8 +7,8 @@ import { isIdentifier, Node } from '@babel/types';
|
|||||||
import { OrderedSet } from './OrderedSet';
|
import { OrderedSet } from './OrderedSet';
|
||||||
|
|
||||||
export class ParseError extends Error {
|
export class ParseError extends Error {
|
||||||
constructor(public readonly expr: string, public readonly detail: unknown) {
|
constructor(public readonly expr: string | t.Expression, public readonly detail: unknown) {
|
||||||
super(`Failed to parse expression "${expr}"`);
|
super(`Failed to parse expression "${typeof expr === 'string' ? expr : generate(expr)}"`);
|
||||||
Object.setPrototypeOf(this, new.target.prototype);
|
Object.setPrototypeOf(this, new.target.prototype);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,7 +159,7 @@ export function parseExpressionGetGlobalVariables(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function parseExpressionConvertThis2Context(
|
export function parseExpressionConvertThis2Context(
|
||||||
expr: string,
|
expr: string | t.Expression,
|
||||||
contextName: string = '__$$context',
|
contextName: string = '__$$context',
|
||||||
localVariables: string[] = [],
|
localVariables: string[] = [],
|
||||||
): string {
|
): string {
|
||||||
@ -168,7 +168,7 @@ export function parseExpressionConvertThis2Context(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const exprAst = parser.parseExpression(expr);
|
const exprAst = typeof expr === 'string' ? parser.parseExpression(expr) : expr;
|
||||||
const exprWrapAst = t.expressionStatement(exprAst);
|
const exprWrapAst = t.expressionStatement(exprAst);
|
||||||
const fileAst = t.file(t.program([exprWrapAst]));
|
const fileAst = t.file(t.program([exprWrapAst]));
|
||||||
|
|
||||||
@ -229,11 +229,14 @@ export function parseExpressionConvertThis2Context(
|
|||||||
const { code } = generate(exprWrapAst.expression, { sourceMaps: false });
|
const { code } = generate(exprWrapAst.expression, { sourceMaps: false });
|
||||||
return code;
|
return code;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// throw new ParseError(expr, e);
|
throw new ParseError(expr, e);
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function indent(level: number) {
|
export function parseExpression(expr: string) {
|
||||||
return ' '.repeat(level);
|
try {
|
||||||
|
return parser.parseExpression(expr);
|
||||||
|
} catch (e) {
|
||||||
|
throw new ParseError(expr, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,7 +79,7 @@ class Home$$Page extends Component {
|
|||||||
<View>
|
<View>
|
||||||
<Text>=== User Info: ===</Text>
|
<Text>=== User Info: ===</Text>
|
||||||
</View>
|
</View>
|
||||||
{__$$eval(() => __$$context.state.user) && (
|
{__$$context.state.user && (
|
||||||
<View style={{ flexDirection: 'row' }}>
|
<View style={{ flexDirection: 'row' }}>
|
||||||
<Image
|
<Image
|
||||||
source={{ uri: __$$eval(() => __$$context.state.user.avatar) }}
|
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>
|
||||||
<View>
|
<View>
|
||||||
<Text>操作提示:</Text>
|
<Text>操作提示:</Text>
|
||||||
|
|||||||
@ -87,7 +87,7 @@
|
|||||||
isSync: true,
|
isSync: true,
|
||||||
},
|
},
|
||||||
dataHandler: {
|
dataHandler: {
|
||||||
type: 'JSFunction',
|
type: 'JSExpression',
|
||||||
value: 'function (response) {\nif (!response.success){\n throw new Error(response.message);\n }\n return response.data;\n}',
|
value: 'function (response) {\nif (!response.success){\n throw new Error(response.message);\n }\n return response.data;\n}',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -104,13 +104,13 @@
|
|||||||
isSync: true,
|
isSync: true,
|
||||||
},
|
},
|
||||||
dataHandler: {
|
dataHandler: {
|
||||||
type: 'JSFunction',
|
type: 'JSExpression',
|
||||||
value: 'function (response) {\nif (!response.success){\n throw new Error(response.message);\n }\n return response.data.result;\n}',
|
value: 'function (response) {\nif (!response.success){\n throw new Error(response.message);\n }\n return response.data.result;\n}',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
dataHandler: {
|
dataHandler: {
|
||||||
type: 'JSFunction',
|
type: 'JSExpression',
|
||||||
value: 'function (dataMap) {\n console.info("All datasources loaded:", dataMap);\n}',
|
value: 'function (dataMap) {\n console.info("All datasources loaded:", dataMap);\n}',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -164,7 +164,7 @@
|
|||||||
componentName: 'View',
|
componentName: 'View',
|
||||||
props: {
|
props: {
|
||||||
onClick: {
|
onClick: {
|
||||||
type: 'JSFunction',
|
type: 'JSExpression',
|
||||||
value: 'this.hello',
|
value: 'this.hello',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -210,7 +210,7 @@
|
|||||||
props: {
|
props: {
|
||||||
style: { flexDirection: 'row' },
|
style: { flexDirection: 'row' },
|
||||||
onClick: {
|
onClick: {
|
||||||
type: 'JSFunction',
|
type: 'JSExpression',
|
||||||
value: 'function(){ this.utils.recordEvent(`CLICK_ORDER`, this.order.title) }',
|
value: 'function(){ this.utils.recordEvent(`CLICK_ORDER`, this.order.title) }',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -260,7 +260,7 @@
|
|||||||
componentName: 'View',
|
componentName: 'View',
|
||||||
props: {
|
props: {
|
||||||
onClick: {
|
onClick: {
|
||||||
type: 'JSFunction',
|
type: 'JSExpression',
|
||||||
value: 'function (){ this.setState({ clickCount: this.state.clickCount + 1 }) }',
|
value: 'function (){ this.setState({ clickCount: this.state.clickCount + 1 }) }',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -312,7 +312,7 @@
|
|||||||
name: 'formatPrice',
|
name: 'formatPrice',
|
||||||
type: 'function',
|
type: 'function',
|
||||||
content: {
|
content: {
|
||||||
type: 'JSFunction',
|
type: 'JSExpression',
|
||||||
value: 'function formatPrice(price, unit) { return Number(price).toFixed(2) + unit; }',
|
value: 'function formatPrice(price, unit) { return Number(price).toFixed(2) + unit; }',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -321,7 +321,7 @@
|
|||||||
name: 'recordEvent',
|
name: 'recordEvent',
|
||||||
type: 'function',
|
type: 'function',
|
||||||
content: {
|
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); }',
|
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