mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-12 11:20:11 +00:00
fix: 🐛 解决 react 中 jsx 出码的时候对于循环数据漏包 __$evalArray 的问题
This commit is contained in:
parent
ee65b8fa17
commit
3b9b177b05
@ -75,6 +75,7 @@
|
|||||||
"change-case": "^3.1.0",
|
"change-case": "^3.1.0",
|
||||||
"commander": "^6.1.0",
|
"commander": "^6.1.0",
|
||||||
"debug": "^4.3.2",
|
"debug": "^4.3.2",
|
||||||
|
"fp-ts": "^2.11.9",
|
||||||
"fs-extra": "9.x",
|
"fs-extra": "9.x",
|
||||||
"glob": "^7.2.0",
|
"glob": "^7.2.0",
|
||||||
"html-entities": "^2.3.2",
|
"html-entities": "^2.3.2",
|
||||||
|
|||||||
@ -83,6 +83,23 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
|
|||||||
`,
|
`,
|
||||||
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]],
|
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]],
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// useRef 为 false 的时候是指没有组件在 props 中配置 ref 属性,但这个时候其实也可能有代码访问 this.$/$$ 所以还是加上个空的代码
|
||||||
|
next.chunks.push({
|
||||||
|
type: ChunkType.STRING,
|
||||||
|
fileType: cfg.fileType,
|
||||||
|
name: CLASS_DEFINE_CHUNK_NAME.InsMethod,
|
||||||
|
content: ` $ = () => null; `,
|
||||||
|
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]],
|
||||||
|
});
|
||||||
|
|
||||||
|
next.chunks.push({
|
||||||
|
type: ChunkType.STRING,
|
||||||
|
fileType: cfg.fileType,
|
||||||
|
name: CLASS_DEFINE_CHUNK_NAME.InsMethod,
|
||||||
|
content: ` $$ = () => []; `,
|
||||||
|
linkAfter: [...DEFAULT_LINK_AFTER[CLASS_DEFINE_CHUNK_NAME.InsMethod]],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return next;
|
return next;
|
||||||
|
|||||||
@ -47,9 +47,9 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
|
|||||||
// 这里会将内部的一些子上下文的访问(this.xxx)转换为 __$$context.xxx 的形式
|
// 这里会将内部的一些子上下文的访问(this.xxx)转换为 __$$context.xxx 的形式
|
||||||
// 与 Rax 所不同的是,这里不会将最顶层的 this 转换掉
|
// 与 Rax 所不同的是,这里不会将最顶层的 this 转换掉
|
||||||
const customHandlers: HandlerSet<string> = {
|
const customHandlers: HandlerSet<string> = {
|
||||||
expression(input: JSExpression, scope: IScope) {
|
expression(input: JSExpression, scope: IScope, config) {
|
||||||
return transformJsExpr(generateExpression(input, scope), scope, {
|
return transformJsExpr(generateExpression(input, scope), scope, {
|
||||||
dontWrapEval: !tolerateEvalErrors,
|
dontWrapEval: !(config?.tolerateEvalErrors ?? tolerateEvalErrors),
|
||||||
dontTransformThis2ContextAtRootScope: true,
|
dontTransformThis2ContextAtRootScope: true,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -71,6 +71,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
|
|||||||
const generatorPlugins: NodeGeneratorConfig = {
|
const generatorPlugins: NodeGeneratorConfig = {
|
||||||
handlers: customHandlers,
|
handlers: customHandlers,
|
||||||
tagMapping: (v) => nodeTypeMapping[v] || v,
|
tagMapping: (v) => nodeTypeMapping[v] || v,
|
||||||
|
tolerateEvalErrors,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (next.contextData.useRefApi) {
|
if (next.contextData.useRefApi) {
|
||||||
|
|||||||
@ -217,6 +217,7 @@ export interface HandlerSet<T> {
|
|||||||
export interface CompositeValueGeneratorOptions {
|
export interface CompositeValueGeneratorOptions {
|
||||||
handlers?: HandlerSet<string>;
|
handlers?: HandlerSet<string>;
|
||||||
nodeGenerator?: NodeGenerator<string>;
|
nodeGenerator?: NodeGenerator<string>;
|
||||||
|
tolerateEvalErrors?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -15,7 +15,10 @@ export interface CodePiece {
|
|||||||
type: PIECE_TYPE;
|
type: PIECE_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AttrData { attrName: string; attrValue: CompositeValue }
|
export interface AttrData {
|
||||||
|
attrName: string;
|
||||||
|
attrValue: CompositeValue;
|
||||||
|
}
|
||||||
// 对 JSX 出码的理解,目前定制点包含 【包装】【标签名】【属性】
|
// 对 JSX 出码的理解,目前定制点包含 【包装】【标签名】【属性】
|
||||||
export type AttrPlugin = BaseGenerator<AttrData, CodePiece[], NodeGeneratorConfig>;
|
export type AttrPlugin = BaseGenerator<AttrData, CodePiece[], NodeGeneratorConfig>;
|
||||||
export type NodePlugin = BaseGenerator<NodeSchema, CodePiece[], NodeGeneratorConfig>;
|
export type NodePlugin = BaseGenerator<NodeSchema, CodePiece[], NodeGeneratorConfig>;
|
||||||
@ -26,4 +29,12 @@ export interface NodeGeneratorConfig {
|
|||||||
attrPlugins?: AttrPlugin[];
|
attrPlugins?: AttrPlugin[];
|
||||||
nodePlugins?: NodePlugin[];
|
nodePlugins?: NodePlugin[];
|
||||||
self?: NodeGenerator<string>;
|
self?: NodeGenerator<string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否要容忍对 JSExpression 求值时的异常
|
||||||
|
* 默认:true
|
||||||
|
* 注: 如果容忍异常,则会在求值时包裹 try-catch 块 -- 通过 __$$eval / __$$evalArray
|
||||||
|
* catch 到异常时默认会抛出一个 CustomEvent 事件里面包含异常信息和求值的表达式
|
||||||
|
*/
|
||||||
|
tolerateEvalErrors?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,10 +79,11 @@ export function isJsCode(value: unknown): boolean {
|
|||||||
|
|
||||||
export function generateExpression(value: any, scope: IScope): string {
|
export function generateExpression(value: any, scope: IScope): string {
|
||||||
if (isJSExpression(value)) {
|
if (isJSExpression(value)) {
|
||||||
const exprVal = (value as JSExpression).value;
|
const exprVal = (value as JSExpression).value.trim();
|
||||||
if (!exprVal) {
|
if (!exprVal) {
|
||||||
return 'null';
|
return 'null';
|
||||||
}
|
}
|
||||||
|
|
||||||
const afterProcessWithLocals = transformExpressionLocalRef(exprVal, scope);
|
const afterProcessWithLocals = transformExpressionLocalRef(exprVal, scope);
|
||||||
return afterProcessWithLocals;
|
return afterProcessWithLocals;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { pipe } from 'fp-ts/function';
|
||||||
import { NodeSchema, isNodeSchema, NodeDataType, CompositeValue } from '@alilc/lowcode-types';
|
import { NodeSchema, isNodeSchema, NodeDataType, CompositeValue } from '@alilc/lowcode-types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -17,6 +18,7 @@ import { getStaticExprValue } from './common';
|
|||||||
import { executeFunctionStack } from './aopHelper';
|
import { executeFunctionStack } from './aopHelper';
|
||||||
import { encodeJsxStringNode } from './encodeJsxAttrString';
|
import { encodeJsxStringNode } from './encodeJsxAttrString';
|
||||||
import { unwrapJsExprQuoteInJsx } from './jsxHelpers';
|
import { unwrapJsExprQuoteInJsx } from './jsxHelpers';
|
||||||
|
import { transformThis2Context } from '../core/jsx/handlers/transformThis2Context';
|
||||||
|
|
||||||
function mergeNodeGeneratorConfig(
|
function mergeNodeGeneratorConfig(
|
||||||
cfg1: NodeGeneratorConfig,
|
cfg1: NodeGeneratorConfig,
|
||||||
@ -255,6 +257,8 @@ export function generateReactLoopCtrl(
|
|||||||
next?: NodePlugin,
|
next?: NodePlugin,
|
||||||
): CodePiece[] {
|
): CodePiece[] {
|
||||||
if (nodeItem.loop) {
|
if (nodeItem.loop) {
|
||||||
|
const tolerateEvalErrors = config?.tolerateEvalErrors ?? true;
|
||||||
|
|
||||||
const loopItemName = nodeItem.loopArgs?.[0] || 'item';
|
const loopItemName = nodeItem.loopArgs?.[0] || 'item';
|
||||||
const loopIndexName = nodeItem.loopArgs?.[1] || 'index';
|
const loopIndexName = nodeItem.loopArgs?.[1] || 'index';
|
||||||
|
|
||||||
@ -262,9 +266,20 @@ export function generateReactLoopCtrl(
|
|||||||
const subScope = scope.createSubScope([loopItemName, loopIndexName]);
|
const subScope = scope.createSubScope([loopItemName, loopIndexName]);
|
||||||
const pieces: CodePiece[] = next ? next(nodeItem, subScope, config) : [];
|
const pieces: CodePiece[] = next ? next(nodeItem, subScope, config) : [];
|
||||||
|
|
||||||
const loopDataExpr = generateCompositeType(nodeItem.loop, scope, {
|
// 生成循环变量表达式
|
||||||
handlers: config?.handlers,
|
const loopDataExpr = pipe(
|
||||||
});
|
nodeItem.loop,
|
||||||
|
// 将 JSExpression 转换为 JS 表达式代码:
|
||||||
|
(expr) =>
|
||||||
|
generateCompositeType(expr, scope, {
|
||||||
|
handlers: config?.handlers,
|
||||||
|
tolerateEvalErrors: false, // 这个内部不需要包 try catch, 下面会统一加的
|
||||||
|
}),
|
||||||
|
// 将 this.xxx 转换为 __$$context.xxx:
|
||||||
|
(expr) => transformThis2Context(expr, scope, { ignoreRootScope: true }),
|
||||||
|
// 如果要容忍错误,则包一层 try catch (基于助手函数 __$$evalArray)
|
||||||
|
(expr) => (tolerateEvalErrors ? `__$$evalArray(() => (${expr}))` : expr),
|
||||||
|
);
|
||||||
|
|
||||||
pieces.unshift({
|
pieces.unshift({
|
||||||
value: `(${loopDataExpr}).map((${loopItemName}, ${loopIndexName}) => ((__$$context) => (`,
|
value: `(${loopDataExpr}).map((${loopItemName}, ${loopIndexName}) => ((__$$context) => (`,
|
||||||
|
|||||||
@ -152,7 +152,7 @@ class Test$$Page extends React.Component {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
<Button.Group>
|
<Button.Group>
|
||||||
{["a", "b", "c"].map((item, index) =>
|
{__$$evalArray(() => ["a", "b", "c"]).map((item, index) =>
|
||||||
((__$$context) =>
|
((__$$context) =>
|
||||||
!!__$$eval(() => index >= 1) && (
|
!!__$$eval(() => index >= 1) && (
|
||||||
<Button type="primary" style={{ margin: "0 5px 0 5px" }}>
|
<Button type="primary" style={{ margin: "0 5px 0 5px" }}>
|
||||||
|
|||||||
@ -45,6 +45,10 @@ class Aaaa$$Page extends React.Component {
|
|||||||
this.state = {};
|
this.state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ = () => null;
|
||||||
|
|
||||||
|
$$ = () => [];
|
||||||
|
|
||||||
_defineDataSourceConfig() {
|
_defineDataSourceConfig() {
|
||||||
const _this = this;
|
const _this = this;
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -38,6 +38,10 @@ class Test$$Page extends React.Component {
|
|||||||
this.state = {};
|
this.state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ = () => null;
|
||||||
|
|
||||||
|
$$ = () => [];
|
||||||
|
|
||||||
componentDidMount() {}
|
componentDidMount() {}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@ -152,7 +152,7 @@ class Test$$Page extends React.Component {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
<div style={{ textAlign: "center" }}>
|
<div style={{ textAlign: "center" }}>
|
||||||
<Button.Group>
|
<Button.Group>
|
||||||
{["a", "b", "c"].map((item, index) =>
|
{__$$evalArray(() => ["a", "b", "c"]).map((item, index) =>
|
||||||
((__$$context) =>
|
((__$$context) =>
|
||||||
!!false && (
|
!!false && (
|
||||||
<Button type="primary" style={{ margin: "0 5px 0 5px" }}>
|
<Button type="primary" style={{ margin: "0 5px 0 5px" }}>
|
||||||
|
|||||||
@ -41,6 +41,10 @@ class Example$$Page extends React.Component {
|
|||||||
this.state = {};
|
this.state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ = () => null;
|
||||||
|
|
||||||
|
$$ = () => [];
|
||||||
|
|
||||||
_defineDataSourceConfig() {
|
_defineDataSourceConfig() {
|
||||||
const _this = this;
|
const _this = this;
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -41,6 +41,10 @@ class Index$$Page extends React.Component {
|
|||||||
this.state = {};
|
this.state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ = () => null;
|
||||||
|
|
||||||
|
$$ = () => [];
|
||||||
|
|
||||||
_defineDataSourceConfig() {
|
_defineDataSourceConfig() {
|
||||||
const _this = this;
|
const _this = this;
|
||||||
return {
|
return {
|
||||||
@ -75,16 +79,17 @@ class Index$$Page extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
{__$$eval(() => this.dataSourceMap.todos.data).map((item, index) =>
|
{__$$evalArray(() => this.dataSourceMap.todos.data).map(
|
||||||
((__$$context) => (
|
(item, index) =>
|
||||||
<div>
|
((__$$context) => (
|
||||||
<Switch
|
<div>
|
||||||
checkedChildren="开"
|
<Switch
|
||||||
unCheckedChildren="关"
|
checkedChildren="开"
|
||||||
checked={__$$eval(() => item.done)}
|
unCheckedChildren="关"
|
||||||
/>
|
checked={__$$eval(() => item.done)}
|
||||||
</div>
|
/>
|
||||||
))(__$$createChildContext(__$$context, { item, index }))
|
</div>
|
||||||
|
))(__$$createChildContext(__$$context, { item, index }))
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -238,7 +238,7 @@ class Test$$Page extends React.Component {
|
|||||||
width="720px"
|
width="720px"
|
||||||
centered={true}
|
centered={true}
|
||||||
>
|
>
|
||||||
{__$$eval(() => this.state.results).map((item, index) =>
|
{__$$evalArray(() => this.state.results).map((item, index) =>
|
||||||
((__$$context) => (
|
((__$$context) => (
|
||||||
<AliAutoDivDefault style={{ width: "100%" }}>
|
<AliAutoDivDefault style={{ width: "100%" }}>
|
||||||
{!!__$$eval(
|
{!!__$$eval(
|
||||||
@ -441,7 +441,7 @@ class Test$$Page extends React.Component {
|
|||||||
width: 142,
|
width: 142,
|
||||||
render: (text, record, index) =>
|
render: (text, record, index) =>
|
||||||
((__$$context) =>
|
((__$$context) =>
|
||||||
__$$eval(() => text.split(",")).map(
|
__$$evalArray(() => text.split(",")).map(
|
||||||
(item, index) =>
|
(item, index) =>
|
||||||
((__$$context) => (
|
((__$$context) => (
|
||||||
<Typography.Text
|
<Typography.Text
|
||||||
@ -470,7 +470,7 @@ class Test$$Page extends React.Component {
|
|||||||
render: (text, record, index) =>
|
render: (text, record, index) =>
|
||||||
((__$$context) => (
|
((__$$context) => (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={__$$eval(() => text || []).map(
|
title={__$$evalArray(() => text || []).map(
|
||||||
(item, index) =>
|
(item, index) =>
|
||||||
((__$$context) => (
|
((__$$context) => (
|
||||||
<Typography.Text
|
<Typography.Text
|
||||||
|
|||||||
@ -303,7 +303,7 @@ class Test$$Page extends React.Component {
|
|||||||
</Button>
|
</Button>
|
||||||
</AliAutoDivDefault>
|
</AliAutoDivDefault>
|
||||||
)}
|
)}
|
||||||
{__$$eval(() => this.state.results).map((item, index) =>
|
{__$$evalArray(() => this.state.results).map((item, index) =>
|
||||||
((__$$context) => (
|
((__$$context) => (
|
||||||
<AliAutoDivDefault style={{ width: "100%", marginTop: "10px" }}>
|
<AliAutoDivDefault style={{ width: "100%", marginTop: "10px" }}>
|
||||||
<Typography.Text>
|
<Typography.Text>
|
||||||
@ -595,17 +595,18 @@ class Test$$Page extends React.Component {
|
|||||||
width: 142,
|
width: 142,
|
||||||
render: (text, record, index) =>
|
render: (text, record, index) =>
|
||||||
((__$$context) =>
|
((__$$context) =>
|
||||||
__$$eval(() => text.split(",")).map((item, index) =>
|
__$$evalArray(() => text.split(",")).map(
|
||||||
((__$$context) => (
|
(item, index) =>
|
||||||
<Typography.Text style={{ display: "block" }}>
|
((__$$context) => (
|
||||||
{__$$eval(() => item)}
|
<Typography.Text style={{ display: "block" }}>
|
||||||
</Typography.Text>
|
{__$$eval(() => item)}
|
||||||
))(
|
</Typography.Text>
|
||||||
__$$createChildContext(__$$context, {
|
))(
|
||||||
item,
|
__$$createChildContext(__$$context, {
|
||||||
index,
|
item,
|
||||||
})
|
index,
|
||||||
)
|
})
|
||||||
|
)
|
||||||
))(
|
))(
|
||||||
__$$createChildContext(__$$context, {
|
__$$createChildContext(__$$context, {
|
||||||
text,
|
text,
|
||||||
@ -621,7 +622,7 @@ class Test$$Page extends React.Component {
|
|||||||
render: (text, record, index) =>
|
render: (text, record, index) =>
|
||||||
((__$$context) => (
|
((__$$context) => (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={__$$eval(() => text || []).map(
|
title={__$$evalArray(() => text || []).map(
|
||||||
(item, index) =>
|
(item, index) =>
|
||||||
((__$$context) => (
|
((__$$context) => (
|
||||||
<Typography.Text
|
<Typography.Text
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"componentsMap": [
|
||||||
|
{
|
||||||
|
"package": "react-greetings",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"componentName": "Greetings",
|
||||||
|
"exportName": "Greetings",
|
||||||
|
"destructuring": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"componentsTree": [
|
||||||
|
{
|
||||||
|
"componentName": "Page",
|
||||||
|
"id": "node_ocl137q7oc1",
|
||||||
|
"fileName": "test",
|
||||||
|
"props": { "style": {} },
|
||||||
|
"lifeCycles": {},
|
||||||
|
"dataSource": { "list": [] },
|
||||||
|
"state": {
|
||||||
|
"name": "lowcode world",
|
||||||
|
"users": null
|
||||||
|
},
|
||||||
|
"methods": {},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"componentName": "Greetings",
|
||||||
|
"id": "node_ocl137q7oc4",
|
||||||
|
"loop": {
|
||||||
|
"type": "JSExpression",
|
||||||
|
"value": "this.state.users"
|
||||||
|
},
|
||||||
|
"loopArgs": ["item", ""],
|
||||||
|
"props": {
|
||||||
|
"content": {
|
||||||
|
"type": "i18n",
|
||||||
|
"key": "greetings.hello",
|
||||||
|
"params": {
|
||||||
|
"name": {
|
||||||
|
"type": "JSExpression",
|
||||||
|
"value": "this.item"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"i18n": {
|
||||||
|
"zh_CN": {
|
||||||
|
"greetings.hello": "${name}, 你好!"
|
||||||
|
},
|
||||||
|
"en_US": {
|
||||||
|
"greetings.hello": "Hello, ${name}!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
import CodeGenerator from '../../src';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { ProjectSchema } from '@alilc/lowcode-types';
|
||||||
|
import { createDiskPublisher } from '../helpers/solutionHelper';
|
||||||
|
import { IceJsProjectBuilderOptions } from '../../src/solutions/icejs';
|
||||||
|
|
||||||
|
const testCaseBaseName = path.basename(__filename, '.test.ts');
|
||||||
|
const inputSchemaJsonFile = path.join(__dirname, `${testCaseBaseName}.schema.json`);
|
||||||
|
const outputDir = path.join(__dirname, `${testCaseBaseName}.generated`);
|
||||||
|
|
||||||
|
jest.setTimeout(60 * 60 * 1000);
|
||||||
|
|
||||||
|
test('loop should be generated link __$$evalArray(xxx).map', async () => {
|
||||||
|
await exportProject(
|
||||||
|
inputSchemaJsonFile,
|
||||||
|
outputDir,
|
||||||
|
{},
|
||||||
|
{ inStrictMode: true, tolerateEvalErrors: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
const generatedPageFileContent = readOutputTextFile('demo-project/src/pages/Test/index.jsx');
|
||||||
|
expect(generatedPageFileContent).toContain(
|
||||||
|
'{__$$evalArray(() => this.state.users).map((item, index) =>',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function exportProject(
|
||||||
|
importPath: string,
|
||||||
|
outputPath: string,
|
||||||
|
mergeSchema?: Partial<ProjectSchema>,
|
||||||
|
options?: IceJsProjectBuilderOptions,
|
||||||
|
) {
|
||||||
|
const schemaJsonStr = fs.readFileSync(importPath, { encoding: 'utf8' });
|
||||||
|
const schema = { ...JSON.parse(schemaJsonStr), ...mergeSchema };
|
||||||
|
const builder = CodeGenerator.solutions.icejs(options);
|
||||||
|
|
||||||
|
return builder.generateProject(schema).then(async (result) => {
|
||||||
|
const publisher = createDiskPublisher();
|
||||||
|
await publisher.publish({
|
||||||
|
project: result,
|
||||||
|
outputPath,
|
||||||
|
projectSlug: 'demo-project',
|
||||||
|
createProjectFolder: true,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function readOutputTextFile(outputFilePath: string): string {
|
||||||
|
return fs.readFileSync(path.resolve(outputDir, outputFilePath), 'utf-8');
|
||||||
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"version": "1.0.0",
|
||||||
|
"componentsMap": [
|
||||||
|
{
|
||||||
|
"package": "react-greetings",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"componentName": "Greetings",
|
||||||
|
"exportName": "Greetings",
|
||||||
|
"destructuring": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"componentsTree": [
|
||||||
|
{
|
||||||
|
"componentName": "Page",
|
||||||
|
"id": "node_ocl137q7oc1",
|
||||||
|
"fileName": "test",
|
||||||
|
"props": { "style": {} },
|
||||||
|
"lifeCycles": {},
|
||||||
|
"dataSource": { "list": [] },
|
||||||
|
"state": {
|
||||||
|
"name": "lowcode world",
|
||||||
|
"users": null
|
||||||
|
},
|
||||||
|
"methods": {},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"componentName": "Greetings",
|
||||||
|
"id": "node_ocl137q7oc4",
|
||||||
|
"loop": {
|
||||||
|
"type": "JSExpression",
|
||||||
|
"value": "this.state.users"
|
||||||
|
},
|
||||||
|
"loopArgs": ["item", ""],
|
||||||
|
"props": {
|
||||||
|
"content": {
|
||||||
|
"type": "i18n",
|
||||||
|
"key": "greetings.hello",
|
||||||
|
"params": { "name": { "type": "JSExpression", "value": "this.item" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"componentName": "Greetings",
|
||||||
|
"id": "node_ocl137q7oc4",
|
||||||
|
"loop": {
|
||||||
|
"type": "JSExpression",
|
||||||
|
"value": "this.state.messages"
|
||||||
|
},
|
||||||
|
"loopArgs": ["msg", ""],
|
||||||
|
"props": {
|
||||||
|
"content": {
|
||||||
|
"type": "JSExpression",
|
||||||
|
"value": "this.msg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"i18n": {
|
||||||
|
"zh_CN": {
|
||||||
|
"greetings.hello": "${name}, 你好!"
|
||||||
|
},
|
||||||
|
"en_US": {
|
||||||
|
"greetings.hello": "Hello, ${name}!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
import CodeGenerator from '../../src';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { ProjectSchema } from '@alilc/lowcode-types';
|
||||||
|
import { createDiskPublisher } from '../helpers/solutionHelper';
|
||||||
|
import { IceJsProjectBuilderOptions } from '../../src/solutions/icejs';
|
||||||
|
|
||||||
|
const testCaseBaseName = path.basename(__filename, '.test.ts');
|
||||||
|
const inputSchemaJsonFile = path.join(__dirname, `${testCaseBaseName}.schema.json`);
|
||||||
|
const outputDir = path.join(__dirname, `${testCaseBaseName}.generated`);
|
||||||
|
|
||||||
|
jest.setTimeout(60 * 60 * 1000);
|
||||||
|
|
||||||
|
test('loop should be generated link __$$evalArray(xxx).map', async () => {
|
||||||
|
await exportProject(
|
||||||
|
inputSchemaJsonFile,
|
||||||
|
outputDir,
|
||||||
|
{},
|
||||||
|
{ inStrictMode: true, tolerateEvalErrors: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
const generatedPageFileContent = readOutputTextFile('demo-project/src/pages/Test/index.jsx');
|
||||||
|
expect(generatedPageFileContent).toContain(
|
||||||
|
'{__$$evalArray(() => this.state.users).map((item, index) =>',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(generatedPageFileContent).toContain(
|
||||||
|
'{__$$evalArray(() => __$$context.state.messages).map(',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function exportProject(
|
||||||
|
importPath: string,
|
||||||
|
outputPath: string,
|
||||||
|
mergeSchema?: Partial<ProjectSchema>,
|
||||||
|
options?: IceJsProjectBuilderOptions,
|
||||||
|
) {
|
||||||
|
const schemaJsonStr = fs.readFileSync(importPath, { encoding: 'utf8' });
|
||||||
|
const schema = { ...JSON.parse(schemaJsonStr), ...mergeSchema };
|
||||||
|
const builder = CodeGenerator.solutions.icejs(options);
|
||||||
|
|
||||||
|
return builder.generateProject(schema).then(async (result) => {
|
||||||
|
const publisher = createDiskPublisher();
|
||||||
|
await publisher.publish({
|
||||||
|
project: result,
|
||||||
|
outputPath,
|
||||||
|
projectSlug: 'demo-project',
|
||||||
|
createProjectFolder: true,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function readOutputTextFile(outputFilePath: string): string {
|
||||||
|
return fs.readFileSync(path.resolve(outputDir, outputFilePath), 'utf-8');
|
||||||
|
}
|
||||||
@ -7,7 +7,7 @@ Object {
|
|||||||
"content": "
|
"content": "
|
||||||
const __$$context = this._context || this;
|
const __$$context = this._context || this;
|
||||||
const { state } = __$$context;
|
const { state } = __$$context;
|
||||||
return (__$$eval(() => (this.state.otherThings))).map((item, index) => ((__$$context) => (!!(__$$eval(() => (__$$context.state.something))) && (<Page><Text>Hello world!</Text></Page>)))(__$$createChildContext(__$$context, { item, index })));
|
return (__$$evalArray(() => (this.state.otherThings))).map((item, index) => ((__$$context) => (!!(__$$eval(() => (__$$context.state.something))) && (<Page><Text>Hello world!</Text></Page>)))(__$$createChildContext(__$$context, { item, index })));
|
||||||
",
|
",
|
||||||
"fileType": "jsx",
|
"fileType": "jsx",
|
||||||
"linkAfter": Array [
|
"linkAfter": Array [
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user