Skip to content

Commit eb4cc69

Browse files
committed
fix: 🐛 解决 react 中 jsx 出码的时候对于循环数据漏包 __$evalArray 的问题
1 parent c18bc02 commit eb4cc69

File tree

20 files changed

+337
-36
lines changed

20 files changed

+337
-36
lines changed

modules/code-generator/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
"change-case": "^3.1.0",
7676
"commander": "^6.1.0",
7777
"debug": "^4.3.2",
78+
"fp-ts": "^2.11.9",
7879
"fs-extra": "9.x",
7980
"glob": "^7.2.0",
8081
"html-entities": "^2.3.2",

modules/code-generator/src/plugins/component/react/containerInjectUtils.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,23 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
8383
`,
8484
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]],
8585
});
86+
} else {
87+
// useRef 为 false 的时候是指没有组件在 props 中配置 ref 属性,但这个时候其实也可能有代码访问 this.$/$$ 所以还是加上个空的代码
88+
next.chunks.push({
89+
type: ChunkType.STRING,
90+
fileType: cfg.fileType,
91+
name: CLASS_DEFINE_CHUNK_NAME.InsMethod,
92+
content: ` $ = () => null; `,
93+
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]],
94+
});
95+
96+
next.chunks.push({
97+
type: ChunkType.STRING,
98+
fileType: cfg.fileType,
99+
name: CLASS_DEFINE_CHUNK_NAME.InsMethod,
100+
content: ` $$ = () => []; `,
101+
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]],
102+
});
86103
}
87104

88105
return next;

modules/code-generator/src/plugins/component/react/jsx.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
4747
// 这里会将内部的一些子上下文的访问(this.xxx)转换为 __$$context.xxx 的形式
4848
// 与 Rax 所不同的是,这里不会将最顶层的 this 转换掉
4949
const customHandlers: HandlerSet<string> = {
50-
expression(input: JSExpression, scope: IScope) {
50+
expression(input: JSExpression, scope: IScope, config) {
5151
return transformJsExpr(generateExpression(input, scope), scope, {
52-
dontWrapEval: !tolerateEvalErrors,
52+
dontWrapEval: !(config?.tolerateEvalErrors ?? tolerateEvalErrors),
5353
dontTransformThis2ContextAtRootScope: true,
5454
});
5555
},
@@ -71,6 +71,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
7171
const generatorPlugins: NodeGeneratorConfig = {
7272
handlers: customHandlers,
7373
tagMapping: (v) => nodeTypeMapping[v] || v,
74+
tolerateEvalErrors,
7475
};
7576

7677
if (next.contextData.useRefApi) {

modules/code-generator/src/types/core.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ export interface HandlerSet<T> {
217217
export interface CompositeValueGeneratorOptions {
218218
handlers?: HandlerSet<string>;
219219
nodeGenerator?: NodeGenerator<string>;
220+
tolerateEvalErrors?: boolean;
220221
}
221222

222223
/**

modules/code-generator/src/types/jsx.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ export interface CodePiece {
1515
type: PIECE_TYPE;
1616
}
1717

18-
export interface AttrData { attrName: string; attrValue: CompositeValue }
18+
export interface AttrData {
19+
attrName: string;
20+
attrValue: CompositeValue;
21+
}
1922
// 对 JSX 出码的理解,目前定制点包含 【包装】【标签名】【属性】
2023
export type AttrPlugin = BaseGenerator<AttrData, CodePiece[], NodeGeneratorConfig>;
2124
export type NodePlugin = BaseGenerator<NodeSchema, CodePiece[], NodeGeneratorConfig>;
@@ -26,4 +29,12 @@ export interface NodeGeneratorConfig {
2629
attrPlugins?: AttrPlugin[];
2730
nodePlugins?: NodePlugin[];
2831
self?: NodeGenerator<string>;
32+
33+
/**
34+
* 是否要容忍对 JSExpression 求值时的异常
35+
* 默认:true
36+
* 注: 如果容忍异常,则会在求值时包裹 try-catch 块 -- 通过 __$$eval / __$$evalArray
37+
* catch 到异常时默认会抛出一个 CustomEvent 事件里面包含异常信息和求值的表达式
38+
*/
39+
tolerateEvalErrors?: boolean;
2940
}

modules/code-generator/src/utils/jsExpression.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,11 @@ export function isJsCode(value: unknown): boolean {
7979

8080
export function generateExpression(value: any, scope: IScope): string {
8181
if (isJSExpression(value)) {
82-
const exprVal = (value as JSExpression).value;
82+
const exprVal = (value as JSExpression).value.trim();
8383
if (!exprVal) {
8484
return 'null';
8585
}
86+
8687
const afterProcessWithLocals = transformExpressionLocalRef(exprVal, scope);
8788
return afterProcessWithLocals;
8889
}

modules/code-generator/src/utils/nodeToJSX.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import _ from 'lodash';
2+
import { pipe } from 'fp-ts/function';
23
import { NodeSchema, isNodeSchema, NodeDataType, CompositeValue } from '@alilc/lowcode-types';
34

45
import {
@@ -17,6 +18,7 @@ import { getStaticExprValue } from './common';
1718
import { executeFunctionStack } from './aopHelper';
1819
import { encodeJsxStringNode } from './encodeJsxAttrString';
1920
import { unwrapJsExprQuoteInJsx } from './jsxHelpers';
21+
import { transformThis2Context } from '../core/jsx/handlers/transformThis2Context';
2022

2123
function mergeNodeGeneratorConfig(
2224
cfg1: NodeGeneratorConfig,
@@ -255,16 +257,29 @@ export function generateReactLoopCtrl(
255257
next?: NodePlugin,
256258
): CodePiece[] {
257259
if (nodeItem.loop) {
260+
const tolerateEvalErrors = config?.tolerateEvalErrors ?? true;
261+
258262
const loopItemName = nodeItem.loopArgs?.[0] || 'item';
259263
const loopIndexName = nodeItem.loopArgs?.[1] || 'index';
260264

261265
// 新建作用域
262266
const subScope = scope.createSubScope([loopItemName, loopIndexName]);
263267
const pieces: CodePiece[] = next ? next(nodeItem, subScope, config) : [];
264268

265-
const loopDataExpr = generateCompositeType(nodeItem.loop, scope, {
266-
handlers: config?.handlers,
267-
});
269+
// 生成循环变量表达式
270+
const loopDataExpr = pipe(
271+
nodeItem.loop,
272+
// 将 JSExpression 转换为 JS 表达式代码:
273+
(expr) =>
274+
generateCompositeType(expr, scope, {
275+
handlers: config?.handlers,
276+
tolerateEvalErrors: false, // 这个内部不需要包 try catch, 下面会统一加的
277+
}),
278+
// 将 this.xxx 转换为 __$$context.xxx:
279+
(expr) => transformThis2Context(expr, scope, { ignoreRootScope: true }),
280+
// 如果要容忍错误,则包一层 try catch (基于助手函数 __$$evalArray)
281+
(expr) => (tolerateEvalErrors ? `__$$evalArray(() => (${expr}))` : expr),
282+
);
268283

269284
pieces.unshift({
270285
value: `(${loopDataExpr}).map((${loopItemName}, ${loopIndexName}) => ((__$$context) => (`,

modules/code-generator/test-cases/react-app/demo1/expected/demo-project/src/pages/Test/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class Test$$Page extends React.Component {
152152
</Form.Item>
153153
<div style={{ textAlign: "center" }}>
154154
<Button.Group>
155-
{["a", "b", "c"].map((item, index) =>
155+
{__$$evalArray(() => ["a", "b", "c"]).map((item, index) =>
156156
((__$$context) =>
157157
!!__$$eval(() => index >= 1) && (
158158
<Button type="primary" style={{ margin: "0 5px 0 5px" }}>

modules/code-generator/test-cases/react-app/demo2-utils-name-alias/expected/demo-project/src/pages/Aaaa/index.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ class Aaaa$$Page extends React.Component {
4545
this.state = {};
4646
}
4747

48+
$ = () => null;
49+
50+
$$ = () => [];
51+
4852
_defineDataSourceConfig() {
4953
const _this = this;
5054
return {

modules/code-generator/test-cases/react-app/demo3/expected/demo-project/src/pages/Test/index.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ class Test$$Page extends React.Component {
3838
this.state = {};
3939
}
4040

41+
$ = () => null;
42+
43+
$$ = () => [];
44+
4145
componentDidMount() {}
4246

4347
render() {

0 commit comments

Comments
 (0)