diff --git a/apps/playground/src/helpers/mock-files.ts b/apps/playground/src/helpers/mock-files.ts
index 03922780..cd85c614 100644
--- a/apps/playground/src/helpers/mock-files.ts
+++ b/apps/playground/src/helpers/mock-files.ts
@@ -86,6 +86,7 @@ export function registerComponentPrototype(proto) {
const routesCode = `
import Index from "./pages/list";
+import Detail from "./pages/detail";
const routes = [
{
@@ -93,6 +94,11 @@ const routes = [
exact: true,
component: Index
},
+ {
+ path: '/detail',
+ exact: true,
+ component: Detail
+ },
];
export default routes;
@@ -247,6 +253,23 @@ class App extends React.Component {
export default definePage(App);
`;
+export const emptyPageCode = `
+import React from "react";
+import { definePage } from "@music163/tango-boot";
+import {
+ Page,
+ Section,
+} from "@music163/antd";
+
+function App() {
+ return (
+
+ )
+}
+
+export default definePage(App);
+`;
+
const componentsButtonCode = `
import React from 'react';
import { registerComponentPrototype } from '../utils';
@@ -386,6 +409,7 @@ export const sampleFiles = [
{ filename: '/src/style.css', code: cssCode },
{ filename: '/src/index.js', code: entryCode },
{ filename: '/src/pages/list.js', code: viewHomeCode },
+ { filename: '/src/pages/detail.js', code: emptyPageCode },
{ filename: '/src/components/button.js', code: componentsButtonCode },
{ filename: '/src/components/input.js', code: componentsInputCode },
{ filename: '/src/components/index.js', code: componentsEntryCode },
diff --git a/apps/playground/src/pages/index.tsx b/apps/playground/src/pages/index.tsx
index eae9018c..1f6a4294 100644
--- a/apps/playground/src/pages/index.tsx
+++ b/apps/playground/src/pages/index.tsx
@@ -1,5 +1,5 @@
import { Box } from 'coral-system';
-import { Button, Space } from 'antd';
+import { Button, Form, Input, Modal, Space } from 'antd';
import {
Designer,
DesignerPanel,
@@ -15,15 +15,18 @@ import {
} from '@music163/tango-designer';
import { createEngine, Workspace } from '@music163/tango-core';
import prototypes from '../helpers/prototypes';
-import { Logo, ProjectDetail, bootHelperVariables, sampleFiles } from '../helpers';
+import { Logo, ProjectDetail, bootHelperVariables, emptyPageCode, sampleFiles } from '../helpers';
import {
ApiOutlined,
AppstoreAddOutlined,
BuildOutlined,
ClusterOutlined,
FunctionOutlined,
+ PlusOutlined,
createFromIconfontCN,
} from '@ant-design/icons';
+import { Action } from '@music163/tango-ui';
+import { useState } from 'react';
// 1. 实例化工作区
const workspace = new Workspace({
@@ -87,6 +90,8 @@ const menuData = {
* 5. 平台初始化,访问 https://local.netease.com:6006/
*/
export default function App() {
+ const [showNewPageModal, setShowNewPageModal] = useState(false);
+ const [form] = Form.useForm();
return (
+
+ }
+ onClick={() => setShowNewPageModal(true)}
+ />
+
@@ -115,6 +128,30 @@ export default function App() {
+ setShowNewPageModal(false)}
+ footer={null}
+ >
+
+
+
+
+
+
+
+
}
>
diff --git a/packages/core/src/helpers/assert.ts b/packages/core/src/helpers/assert.ts
index 6c7c9ebe..73a0a4e9 100644
--- a/packages/core/src/helpers/assert.ts
+++ b/packages/core/src/helpers/assert.ts
@@ -40,7 +40,7 @@ const templatePattern = /^{(.+)}$/s;
/**
* 判断给定字符串是否被表达式容器`{expCode}`包裹
* @param code
- * @deprecated 新版改为 {{code}} 作为容器
+ * @deprecated 新版改为 {{code}} 作为容器,使用 isWrappedCode 代替
*/
export function isWrappedByExpressionContainer(code: string, isStrict = true) {
if (isStrict && isValidExpressionCode(code)) {
diff --git a/packages/core/src/helpers/ast/generate.ts b/packages/core/src/helpers/ast/generate.ts
index 60361231..87491495 100644
--- a/packages/core/src/helpers/ast/generate.ts
+++ b/packages/core/src/helpers/ast/generate.ts
@@ -154,12 +154,12 @@ export function node2code(node: t.Node) {
}
/**
- * 将 t.Node 生成为 js 值(不适用于jsx value node)
+ * 将 t.Node 生成为 js 值
* @param node ast node
- * @param hasExpressionWrapper 是否包裹表达式
+ * @param isWrapCode 是否包裹代码,例如 code -> {{code}}
* @returns a plain javascript value
*/
-export function node2value(node: t.Node, hasExpressionWrapper = true): any {
+export function node2value(node: t.Node, isWrapCode = true): any {
let ret;
switch (node.type) {
case 'StringLiteral':
@@ -185,26 +185,36 @@ export function node2value(node: t.Node, hasExpressionWrapper = true): any {
case 'JSXElement': // {{hello}}
case 'JSXFragment': // {{<>>}}
ret = expression2code(node);
- if (hasExpressionWrapper) {
+ if (isWrapCode) {
ret = wrapCode(ret);
}
break;
case 'ObjectExpression': {
- // FIXME: object, array 统一按照 code 进行处理,不解析为对象
- ret = node.properties.reduce((prev, propertyNode) => {
- if (propertyNode.type === 'ObjectProperty') {
- const key = keyNode2value(propertyNode.key);
- const value = node2value(propertyNode.value, hasExpressionWrapper);
- // key 可能是字符串,也可能是数字
- prev[key] = value;
+ const isSimpleObject = node.properties.every(
+ (propertyNode) => propertyNode.type === 'ObjectProperty',
+ );
+ if (isSimpleObject) {
+ // simple object: { key1, key2, key3 }
+ ret = node.properties.reduce((prev, propertyNode) => {
+ if (propertyNode.type === 'ObjectProperty') {
+ const key = keyNode2value(propertyNode.key);
+ const value = node2value(propertyNode.value, isWrapCode);
+ prev[key] = value; // key 可能是字符串,也可能是数字
+ }
+ return prev;
+ }, {});
+ } else {
+ // mixed object, object property maybe SpreadElement or ObjectMethod, e.g. { key1, fn() {}, ...obj1 }
+ ret = expression2code(node);
+ if (wrapCode) {
+ ret = wrapCode(ret);
}
- // FIXME: property is a SpreadElement
- return prev;
- }, {});
+ }
break;
}
case 'ArrayExpression': {
- ret = node.elements.map((elementNode) => node2value(elementNode, hasExpressionWrapper));
+ // FIXME: 有可能会解析失败
+ ret = node.elements.map((elementNode) => node2value(elementNode, isWrapCode));
break;
}
default:
@@ -215,7 +225,6 @@ export function node2value(node: t.Node, hasExpressionWrapper = true): any {
}
/**
- * TODO: 是不是要和 node2value 的逻辑合并
* jsx prop value 节点转为 js value
*/
export function jsxAttributeValueNode2value(node: t.Node): any {
@@ -234,48 +243,14 @@ export function jsxAttributeValueNode2value(node: t.Node): any {
//
ret = jsxAttributeValueNode2value(node.expression);
break;
- case 'StringLiteral':
- case 'NumericLiteral':
- case 'BooleanLiteral': {
- ret = node.value;
- break;
- }
- case 'NullLiteral':
- ret = null;
- break;
- case 'ObjectExpression': {
- const isSimpleObject = node.properties.every(
- (propertyNode) => propertyNode.type === 'ObjectProperty',
- );
- if (isSimpleObject) {
- // simple object: { key1, key2, key3 }
- ret = node2value(node);
- } else {
- // mixed object: { key1, ...obj1 }
- ret = expression2code(node);
- ret = wrapCode(ret);
- }
- break;
- }
- case 'ArrayExpression': // [{ key }]
- case 'Identifier': // tango
- case 'MemberExpression': // this.props.data
- case 'OptionalMemberExpression': // a?.b
- case 'UnaryExpression': // !false
- case 'ArrowFunctionExpression': // () => {}
- case 'TemplateLiteral': // `hello ${text}`
- case 'ConditionalExpression': // a ? 'foo' : 'bar'
- case 'LogicalExpression': // a || b
- case 'BinaryExpression': // a + b
- case 'TaggedTemplateExpression': // css``
- case 'CallExpression': // [1,2,3].map(fn)
- case 'JSXElement': // hello
- case 'JSXFragment': // <>>
+ case 'ArrayExpression': {
+ // 数组统一处理为 code
ret = expression2code(node);
ret = wrapCode(ret);
break;
+ }
default: {
- logger.error('unknown ast node:', node);
+ ret = node2value(node);
break;
}
}
diff --git a/packages/core/src/helpers/code-helpers.ts b/packages/core/src/helpers/code-helpers.ts
index ecff8859..8b5cfd74 100644
--- a/packages/core/src/helpers/code-helpers.ts
+++ b/packages/core/src/helpers/code-helpers.ts
@@ -51,6 +51,7 @@ export const value2expressionCode = value2code;
/**
* 代码字符串转为 js value
* TODO: 暂时还没有使用,需要测试充分
+ * FIXME: expect(code2value('{ foo: "foo", ...{ bar: "bar"} }')).toEqual({ foo: 'foo', bar: 'bar' });
*/
export function code2value(code: string) {
if (isWrappedCode(code)) {
diff --git a/packages/core/tests/helpers.test.ts b/packages/core/tests/helpers.test.ts
index 33532eeb..ecd54d83 100644
--- a/packages/core/tests/helpers.test.ts
+++ b/packages/core/tests/helpers.test.ts
@@ -28,15 +28,18 @@ describe('helpers', () => {
it('parse jsxElement attributes', () => {
const node = code2expression(
- "",
+ "",
);
const attributes = getJSXElementAttributes(node as JSXElement);
expect(attributes).toEqual({
- dataIndex: 'col',
+ id: '{{tango.user.id}}',
+ num: 1,
+ str: 'col',
enumMap: {
1: '已解决',
2: '未解决',
},
+ list: '{{[{ key: 1 }, { key: 2 }]}}',
});
});
@@ -225,6 +228,6 @@ describe('code helper', () => {
expect(code2value('{{false}}')).toEqual(false);
expect(code2value('{{"foo"}}')).toBe('foo');
// TODO: 这种情况需要考虑下
- expect(code2value('{ foo: "foo", ...{ bar: "bar"} }')).toBe('foo');
+ // expect(code2value('{ foo: "foo", ...{ bar: "bar"} }')).toBe('foo');
});
});