Skip to content

Commit ce531ae

Browse files
committed
fix: 🐛 解决小程序环境没有 window, 而 rax 出码中却默认在 __$eval 中用到 window 的问题
-- 解决方法: 将这个 __$$eval 的错误处理的默认行为搞成配置化的, 支持从外面传入...
1 parent 2bb8efb commit ce531ae

File tree

35 files changed

+576
-361
lines changed
  • modules/code-generator
    • src
    • test-cases
      • rax-app
        • demo01/expected/demo-project/src/pages/Home
        • demo02/expected/demo-project/src/pages/Home
        • demo03/expected/demo-project/src/pages
        • demo04/expected/demo-project/src/pages/Home
        • demo05/expected/demo-project/src/pages/Home
        • demo06-jsslot/expected/demo-project/src/pages/Home
        • demo07-newline-in-props/expected/demo-project/src/pages/Home
        • demo08-jsslot-with-multiple-children/expected/demo-project/src/pages/Home
        • demo09-jsslot-with-conditional-children/expected/demo-project/src/pages/Home
        • demo10-jsslot-with-loop-children/expected/demo-project/src/pages/Home
        • demo11-utils-name-alias/expected/demo-project/src/pages/Aaaa
        • demo12-refs/expected/demo-project/src/pages/Home
        • demo13-datasource-prop/expected/demo-project/src/pages/Example
      • react-app
        • demo1/expected/demo-project/src/pages/Test
        • demo2-utils-name-alias/expected/demo-project/src/pages/Aaaa
        • demo2/expected/demo-project/src/pages/Test
        • demo3/expected/demo-project/src/pages/Test
        • demo4/expected/demo-project/src/pages/Test
        • demo5/expected/demo-project/src/pages/Test
        • demo6-literal-condition/expected/demo-project/src/pages/Test
        • demo7-literal-condition2/expected/demo-project/src/pages/Test
        • demo8-datasource-prop/expected/demo-project/src/pages/Example
        • demo9-datasource-engine/expected/demo-project/src/components/Index
        • demo_10-jsslot/expected/demo-project/src/pages/Test
        • demo_11-jsslot-2/expected/demo-project/src/pages/Test
    • tests

35 files changed

+576
-361
lines changed

modules/code-generator/src/generator/ProjectBuilder.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,8 @@ export class ProjectBuilder implements IProjectBuilder {
280280
postProcessors: this.postProcessors,
281281
contextData: {
282282
inStrictMode: this.inStrictMode,
283+
tolerateEvalErrors: true,
284+
evalErrorsHandler: '',
283285
...this.extraContextData,
284286
},
285287
...options,

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

Lines changed: 52 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
6262

6363
const ir = next.ir as IContainerInfo;
6464
const rootScope = Scope.createRootScope();
65+
const { tolerateEvalErrors = true, evalErrorsHandler = '' } = next.contextData;
6566

6667
// Rax 构建到小程序的时候,不能给组件起起别名,得直接引用,故这里将所有的别名替换掉
6768
// 先收集下所有的 alias 的映射
@@ -86,7 +87,9 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
8687
// 3. 通过 this.xxx 能拿到的东西太多了,而且自定义的 methods 可能会无意间破坏 Rax 框架或小程序框架在页面 this 上的东东
8788
const customHandlers: HandlerSet<string> = {
8889
expression(input: JSExpression, scope: IScope) {
89-
return transformJsExpr(generateExpression(input, scope), scope);
90+
return transformJsExpr(generateExpression(input, scope), scope, {
91+
dontWrapEval: !tolerateEvalErrors,
92+
});
9093
},
9194
function(input, scope: IScope) {
9295
return transformThis2Context(input.value || 'null', scope);
@@ -138,34 +141,72 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
138141
type: ChunkType.STRING,
139142
fileType: cfg.fileType,
140143
name: COMMON_CHUNK_NAME.CustomContent,
141-
content: `
142-
144+
content: [
145+
tolerateEvalErrors &&
146+
`
143147
function __$$eval(expr) {
144148
try {
145149
return expr();
146-
} catch (err) {
147-
try {
148-
if (window.handleEvalError) {
149-
window.handleEvalError('Failed to evaluate: ', expr, err);
150-
}
151-
} catch (e) {}
150+
} catch (error) {
151+
${evalErrorsHandler}
152152
}
153153
}
154154
155155
function __$$evalArray(expr) {
156156
const res = __$$eval(expr);
157157
return Array.isArray(res) ? res : [];
158158
}
159-
159+
`,
160+
`
160161
function __$$createChildContext(oldContext, ext) {
161162
return Object.assign({}, oldContext, ext);
162163
}
163-
164164
`,
165+
]
166+
.filter(Boolean)
167+
.join('\n'),
165168
linkAfter: [COMMON_CHUNK_NAME.FileExport],
166169
});
167170

168171
return next;
172+
173+
function generateRaxLoopCtrl(
174+
nodeItem: NodeSchema,
175+
scope: IScope,
176+
config?: NodeGeneratorConfig,
177+
next?: NodePlugin,
178+
): CodePiece[] {
179+
if (nodeItem.loop) {
180+
const loopItemName = nodeItem.loopArgs?.[0] || 'item';
181+
const loopIndexName = nodeItem.loopArgs?.[1] || 'index';
182+
const subScope = scope.createSubScope([loopItemName, loopIndexName]);
183+
const pieces: CodePiece[] = next ? next(nodeItem, subScope, config) : [];
184+
185+
const loopDataExpr = tolerateEvalErrors
186+
? `__$$evalArray(() => (${transformThis2Context(
187+
generateCompositeType(nodeItem.loop, scope, { handlers: config?.handlers }),
188+
scope,
189+
)}))`
190+
: `(${transformThis2Context(
191+
generateCompositeType(nodeItem.loop, scope, { handlers: config?.handlers }),
192+
scope,
193+
)})`;
194+
195+
pieces.unshift({
196+
value: `${loopDataExpr}.map((${loopItemName}, ${loopIndexName}) => ((__$$context) => (`,
197+
type: PIECE_TYPE.BEFORE,
198+
});
199+
200+
pieces.push({
201+
value: `))(__$$createChildContext(__$$context, { ${loopItemName}, ${loopIndexName} })))`,
202+
type: PIECE_TYPE.AFTER,
203+
});
204+
205+
return pieces;
206+
}
207+
208+
return next ? next(nodeItem, scope, config) : [];
209+
}
169210
};
170211

171212
return plugin;
@@ -189,39 +230,6 @@ function isImportAliasDefineChunk(chunk: ICodeChunk): chunk is ICodeChunk & {
189230
);
190231
}
191232

192-
function generateRaxLoopCtrl(
193-
nodeItem: NodeSchema,
194-
scope: IScope,
195-
config?: NodeGeneratorConfig,
196-
next?: NodePlugin,
197-
): CodePiece[] {
198-
if (nodeItem.loop) {
199-
const loopItemName = nodeItem.loopArgs?.[0] || 'item';
200-
const loopIndexName = nodeItem.loopArgs?.[1] || 'index';
201-
const subScope = scope.createSubScope([loopItemName, loopIndexName]);
202-
const pieces: CodePiece[] = next ? next(nodeItem, subScope, config) : [];
203-
204-
const loopDataExpr = `__$$evalArray(() => (${transformThis2Context(
205-
generateCompositeType(nodeItem.loop, scope, { handlers: config?.handlers }),
206-
scope,
207-
)}))`;
208-
209-
pieces.unshift({
210-
value: `${loopDataExpr}.map((${loopItemName}, ${loopIndexName}) => ((__$$context) => (`,
211-
type: PIECE_TYPE.BEFORE,
212-
});
213-
214-
pieces.push({
215-
value: `))(__$$createChildContext(__$$context, { ${loopItemName}, ${loopIndexName} })))`,
216-
type: PIECE_TYPE.AFTER,
217-
});
218-
219-
return pieces;
220-
}
221-
222-
return next ? next(nodeItem, scope, config) : [];
223-
}
224-
225233
function generateNodeAttrForRax(
226234
this: { cfg: PluginConfig },
227235
attrData: { attrName: string; attrValue: CompositeValue },

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

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,14 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
4242
...pre,
4343
};
4444

45+
const { tolerateEvalErrors = true, evalErrorsHandler = '' } = next.contextData;
46+
4547
// 这里会将内部的一些子上下文的访问(this.xxx)转换为 __$$context.xxx 的形式
4648
// 与 Rax 所不同的是,这里不会将最顶层的 this 转换掉
4749
const customHandlers: HandlerSet<string> = {
4850
expression(input: JSExpression, scope: IScope) {
4951
return transformJsExpr(generateExpression(input, scope), scope, {
50-
dontWrapEval: true,
52+
dontWrapEval: !tolerateEvalErrors,
5153
dontTransformThis2ContextAtRootScope: true,
5254
});
5355
},
@@ -111,7 +113,23 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
111113
type: ChunkType.STRING,
112114
fileType: cfg.fileType,
113115
name: COMMON_CHUNK_NAME.CustomContent,
114-
content: `
116+
content: [
117+
tolerateEvalErrors &&
118+
`
119+
function __$$eval(expr) {
120+
try {
121+
return expr();
122+
} catch (error) {
123+
${evalErrorsHandler}
124+
}
125+
}
126+
127+
function __$$evalArray(expr) {
128+
const res = __$$eval(expr);
129+
return Array.isArray(res) ? res : [];
130+
}
131+
`,
132+
`
115133
function __$$createChildContext(oldContext, ext) {
116134
const childContext = {
117135
...oldContext,
@@ -121,6 +139,9 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
121139
return childContext;
122140
}
123141
`,
142+
]
143+
.filter(Boolean)
144+
.join('\n'),
124145
linkAfter: [COMMON_CHUNK_NAME.FileExport],
125146
});
126147
return next;

modules/code-generator/src/solutions/icejs.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { IProjectBuilder } from '../types';
1+
import { IProjectBuilder, IProjectBuilderOptions } from '../types';
22

33
import { createProjectBuilder } from '../generator/ProjectBuilder';
44

@@ -22,15 +22,14 @@ import icejs from '../plugins/project/framework/icejs';
2222

2323
import { prettier } from '../postprocessor';
2424

25-
export type IceJsProjectBuilderOptions = {
26-
inStrictMode?: boolean;
27-
};
25+
export interface IceJsProjectBuilderOptions extends IProjectBuilderOptions {}
2826

2927
export default function createIceJsProjectBuilder(
3028
options?: IceJsProjectBuilderOptions,
3129
): IProjectBuilder {
3230
return createProjectBuilder({
3331
inStrictMode: options?.inStrictMode,
32+
extraContextData: { ...options },
3433
template: icejs.template,
3534
plugins: {
3635
components: [

modules/code-generator/src/solutions/rax-app.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { IProjectBuilder } from '../types';
1+
import { IProjectBuilder, IProjectBuilderOptions } from '../types';
22

33
import { createProjectBuilder } from '../generator/ProjectBuilder';
44

@@ -22,8 +22,14 @@ import raxApp from '../plugins/project/framework/rax';
2222
import { prettier } from '../postprocessor';
2323
import { RaxFrameworkOptions } from '../plugins/project/framework/rax/types/RaxFrameworkOptions';
2424

25-
export default function createRaxProjectBuilder(options?: RaxFrameworkOptions): IProjectBuilder {
25+
export interface RaxProjectBuilderOptions extends IProjectBuilderOptions, RaxFrameworkOptions {}
26+
27+
export default function createRaxProjectBuilder(
28+
options?: RaxProjectBuilderOptions,
29+
): IProjectBuilder {
2630
return createProjectBuilder({
31+
inStrictMode: options?.inStrictMode,
32+
extraContextData: { ...options },
2733
template: raxApp.template,
2834
plugins: {
2935
components: [

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

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,7 @@ export interface ICodeStruct extends IBaseCodeStruct {
6363
}
6464

6565
/** 上下文数据,用来在插件之间共享一些数据 */
66-
export interface IContextData {
67-
/** 是否处于严格模式 */
68-
inStrictMode?: boolean;
69-
66+
export interface IContextData extends IProjectBuilderOptions {
7067
/** 是否使用了 Ref 的 API (this.$/this.$$) */
7168
useRefApi?: boolean;
7269

@@ -139,6 +136,33 @@ export interface IProjectPlugins {
139136
[slotName: string]: BuilderComponentPlugin[];
140137
}
141138

139+
export interface IProjectBuilderOptions {
140+
/** 是否处于严格模式(默认: 否) */
141+
inStrictMode?: boolean;
142+
143+
/**
144+
* 是否要容忍对 JSExpression 求值时的异常
145+
* 默认:true
146+
* 注: 如果容忍异常,则会在求值时包裹 try-catch 块,
147+
* catch 到异常时默认会抛出一个 CustomEvent 事件里面包含异常信息和求值的表达式
148+
*/
149+
tolerateEvalErrors?: boolean;
150+
151+
/**
152+
* 容忍异常的时候的的错误处理语句块
153+
* 默认: 无
154+
* 您可以设置为一个语句块,比如:
155+
* window.dispatchEvent(new CustomEvent('lowcode-eval-error', { error, expr }))
156+
*
157+
* 一般可以结合埋点监控模块用来监控求值异常
158+
*
159+
* 其中:
160+
* - error: 异常信息
161+
* - expr: 求值的表达式
162+
*/
163+
evalErrorsHandler?: string;
164+
}
165+
142166
export interface IProjectBuilder {
143167
generateProject: (schema: ProjectSchema | string) => Promise<ResultDir>;
144168
}

modules/code-generator/test-cases/rax-app/demo01/expected/demo-project/src/pages/Home/index.jsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,7 @@ export default Home$$Page;
129129
function __$$eval(expr) {
130130
try {
131131
return expr();
132-
} catch (err) {
133-
try {
134-
if (window.handleEvalError) {
135-
window.handleEvalError('Failed to evaluate: ', expr, err);
136-
}
137-
} catch (e) {}
138-
}
132+
} catch (error) {}
139133
}
140134

141135
function __$$evalArray(expr) {

modules/code-generator/test-cases/rax-app/demo02/expected/demo-project/src/pages/Home/index.jsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -336,13 +336,7 @@ export default Home$$Page;
336336
function __$$eval(expr) {
337337
try {
338338
return expr();
339-
} catch (err) {
340-
try {
341-
if (window.handleEvalError) {
342-
window.handleEvalError('Failed to evaluate: ', expr, err);
343-
}
344-
} catch (e) {}
345-
}
339+
} catch (error) {}
346340
}
347341

348342
function __$$evalArray(expr) {

modules/code-generator/test-cases/rax-app/demo03/expected/demo-project/src/pages/Detail/index.jsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,7 @@ export default Detail$$Page;
138138
function __$$eval(expr) {
139139
try {
140140
return expr();
141-
} catch (err) {
142-
try {
143-
if (window.handleEvalError) {
144-
window.handleEvalError('Failed to evaluate: ', expr, err);
145-
}
146-
} catch (e) {}
147-
}
141+
} catch (error) {}
148142
}
149143

150144
function __$$evalArray(expr) {

modules/code-generator/test-cases/rax-app/demo03/expected/demo-project/src/pages/Home/index.jsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,7 @@ export default Home$$Page;
138138
function __$$eval(expr) {
139139
try {
140140
return expr();
141-
} catch (err) {
142-
try {
143-
if (window.handleEvalError) {
144-
window.handleEvalError('Failed to evaluate: ', expr, err);
145-
}
146-
} catch (e) {}
147-
}
141+
} catch (error) {}
148142
}
149143

150144
function __$$evalArray(expr) {

0 commit comments

Comments
 (0)