diff --git a/packages/code-generator/src/demo/loopDemo.ts b/packages/code-generator/src/demo/loopDemo.ts
deleted file mode 100644
index cf5b83bc5..000000000
--- a/packages/code-generator/src/demo/loopDemo.ts
+++ /dev/null
@@ -1,276 +0,0 @@
-import { IBasicSchema } from '@/types';
-
-const demoData: IBasicSchema = {
- version: '1.0.0',
- componentsMap: [
- {
- componentName: 'Button',
- package: '@alifd/next',
- version: '1.19.4',
- destructuring: true,
- exportName: 'Select',
- subName: 'Button',
- },
- ],
- utils: [
- {
- name: 'clone',
- type: 'npm',
- content: {
- package: 'lodash',
- version: '0.0.1',
- exportName: 'clone',
- subName: '',
- destructuring: false,
- main: '/lib/clone',
- },
- },
- {
- name: 'moment',
- type: 'npm',
- content: {
- package: '@alife/next',
- version: '0.0.1',
- exportName: 'Moment',
- subName: '',
- destructuring: true,
- main: '',
- },
- },
- ],
- componentsTree: [
- {
- componentName: 'Page',
- fileName: 'loopDemo',
- props: {},
- children: [
- {
- componentName: 'Html',
- props: {
- html:
- '1.选中Col组件,在右侧“数据”面板,设置循环数据;
\n2.给Col组件内的子组件文本内容,绑定对应的数据变量;this.item获取当前循环数据,this.index获取当前循环序号',
- },
- },
- {
- componentName: 'Row',
- props: {
- style: {
- paddingTop: 30,
- paddingRight: 30,
- paddingBottom: 30,
- paddingLeft: 30,
- },
- },
- children: [
- {
- componentName: 'Col',
- props: {},
- children: [
- {
- componentName: 'Text',
- props: {
- style: {
- display: 'block',
- marginBottom: 8,
- fontWeight: 'bold',
- fontSize: 14,
- lineHeight: '32px',
- },
- text: {
- type: 'JSExpression',
- value: 'this.item.title',
- },
- },
- },
- {
- componentName: 'Text',
- props: {
- style: {
- display: 'block',
- marginBottom: 12,
- fontWeight: 'bold',
- fontSize: 16,
- color: '#65aa14',
- lineHeight: '12px',
- },
- text: {
- type: 'JSExpression',
- value: 'this.item.num',
- },
- },
- },
- {
- componentName: 'Text',
- props: {
- style: {
- display: 'block',
- color: '#9b9b9b',
- },
- text: {
- type: 'JSExpression',
- value: 'this.item.description',
- },
- },
- },
- ],
- loop: [
- {
- title: '活跃UV',
- num: 2783,
- description: '小二:外包商家:12',
- },
- {
- title: '活跃PV',
- num: 17382,
- description: '小二外包商家123',
- },
- {
- title: '不活跃页面数',
- num: 36,
- description: '占总页面数比例 30%',
- },
- {
- title: '人均使用时长',
- num: 788,
- description: '人均使用频次',
- },
- {
- title: '新增用户数',
- num: 14,
- description: '小二:外包:商家 1:1:1',
- },
- ],
- },
- {
- componentName: 'Col',
- props: {},
- children: [
- {
- componentName: 'Text',
- props: {
- style: {
- display: 'block',
- marginBottom: 8,
- fontWeight: 'bold',
- fontSize: '14px',
- lineHeight: '32px',
- },
- text: '更多用户数据分析',
- },
- },
- {
- componentName: 'Button',
- props: {
- type: 'primary',
- style: {
- margin: '0 5px 0 5px',
- },
- },
- children: '查看详情',
- },
- ],
- },
- ],
- },
- {
- componentName: 'Table',
- props: {
- hasBorder: true,
- hasHeader: true,
- dataSource: [
- {
- id: 1,
- name: 'a1',
- age: 1,
- },
- {
- id: 2,
- name: 'a2',
- age: 2,
- },
- {
- id: 3,
- name: 'a3',
- age: 3,
- },
- {
- id: 4,
- name: 'a4',
- age: 4,
- },
- ],
- },
- children: [
- {
- componentName: 'TableColumn',
- props: {
- title: {
- type: 'JSExpression',
- value: 'this.item.title',
- },
- dataIndex: {
- type: 'JSExpression',
- value: 'this.item.dataIndex',
- },
- },
- loop: {
- type: 'JSExpression',
- value: 'this.state.columns',
- },
- },
- ],
- },
- ],
- state: {
- dataSource: [
- {
- id: 1,
- name: 'a1',
- age: 21,
- },
- {
- id: 2,
- name: 'a2',
- age: 22,
- },
- {
- id: 3,
- name: 'a3',
- age: 23,
- },
- {
- id: 4,
- name: 'a4',
- age: 24,
- },
- ],
- columns: [
- {
- title: 'ID',
- dataIndex: 'id',
- },
- {
- title: '姓名',
- dataIndex: 'name',
- },
- {
- title: '年龄',
- dataIndex: 'age',
- },
- ],
- },
- },
- ],
- i18n: {
- 'zh-CN': {
- 'i18n-jwg27yo4': '你好',
- 'i18n-jwg27yo3': '中国',
- },
- 'en-US': {
- 'i18n-jwg27yo4': 'Hello',
- 'i18n-jwg27yo3': 'China',
- },
- },
-};
-
-export default demoData;
diff --git a/packages/code-generator/src/demo/main.ts b/packages/code-generator/src/demo/main.ts
index 722cff307..76fa8a837 100644
--- a/packages/code-generator/src/demo/main.ts
+++ b/packages/code-generator/src/demo/main.ts
@@ -1,136 +1,7 @@
-import { IProjectSchema, IResultDir, IResultFile } from '@/types';
+import { IResultDir, IResultFile } from '@/types';
import CodeGenerator from '@/index';
-
-const schema: IProjectSchema = {
- version: '1.0.0',
- componentsMap: [
- {
- componentName: 'Button',
- package: 'alife/next',
- version: '1.0.0',
- destructuring: true,
- exportName: 'Select',
- subName: 'Button',
- },
- ],
- componentsTree: [
- {
- componentName: 'Page',
- fileName: 'Page1',
- props: {},
- css: 'body {font-size: 12px;} .table { width: 100px;}',
- children: [
- {
- componentName: 'Div',
- props: {
- className: '',
- },
- children: [
- {
- componentName: 'Button',
- props: {
- prop1: 1234,
- prop2: [
- {
- label: '选项1',
- value: 1,
- },
- {
- label: '选项2',
- value: 2,
- },
- ],
- prop3: [
- {
- name: 'myName',
- rule: {
- type: 'JSExpression',
- value: '/w+/i',
- },
- },
- ],
- valueBind: {
- type: 'JSExpression',
- value: 'this.state.user.name',
- },
- onClick: {
- type: 'JSExpression',
- value: 'function(e) { console.log(e.target.innerText) }',
- },
- onClick2: {
- type: 'JSExpression',
- value: 'this.submit',
- },
- },
- },
- ],
- },
- ],
- },
- ],
- utils: [
- {
- name: 'clone',
- type: 'npm',
- content: {
- package: 'lodash',
- version: '0.0.1',
- exportName: 'clone',
- subName: '',
- destructuring: false,
- main: '/lib/clone',
- },
- },
- {
- name: 'beforeRequestHandler',
- type: 'function',
- content: {
- type: 'JSExpression',
- value: 'function(){\n ... \n}',
- },
- },
- ],
- constants: {
- ENV: 'prod',
- DOMAIN: 'xxx.alibaba-inc.com',
- },
- css: 'body {font-size: 12px;} .table { width: 100px;}',
- config: {
- sdkVersion: '1.0.3',
- historyMode: 'hash',
- targetRootID: 'J_Container',
- layout: {
- componentName: 'BasicLayout',
- props: {
- logo: '...',
- name: '测试网站',
- },
- },
- theme: {
- package: '@alife/theme-fusion',
- version: '^0.1.0',
- },
- },
- meta: {
- name: 'demo应用',
- git_group: 'appGroup',
- project_name: 'app_demo',
- description: '这是一个测试应用',
- spma: 'spa23d',
- creator: '月飞',
- },
- i18n: {
- 'zh-CN': {
- i18nJwg27yo4: '你好',
- i18nJwg27yo3: '中国',
- },
- 'en-US': {
- i18nJwg27yo4: 'Hello',
- i18nJwg27yo3: 'China',
- },
- },
-};
+import demoSchema from './simpleDemo';
function flatFiles(rootName: string | null, dir: IResultDir): IResultFile[] {
const dirRoot: string = rootName ? `${rootName}/${dir.name}` : dir.name;
@@ -148,7 +19,7 @@ function flatFiles(rootName: string | null, dir: IResultDir): IResultFile[] {
function main() {
const createIceJsProjectBuilder = CodeGenerator.solutions.icejs;
const builder = createIceJsProjectBuilder();
- builder.generateProject(schema).then(result => {
+ builder.generateProject(demoSchema).then(result => {
const files = flatFiles('.', result);
files.forEach(file => {
console.log(`========== ${file.name} Start ==========`);
diff --git a/packages/code-generator/src/demo/simpleDemo.ts b/packages/code-generator/src/demo/simpleDemo.ts
new file mode 100644
index 000000000..6e45cba6a
--- /dev/null
+++ b/packages/code-generator/src/demo/simpleDemo.ts
@@ -0,0 +1,230 @@
+import { IProjectSchema } from '@/types';
+
+const demoData: IProjectSchema = {
+ version: '1.0.0',
+ componentsMap: [
+ {
+ componentName: 'Button',
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Button',
+ },
+ {
+ componentName: 'Button.Group',
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Button',
+ subName: 'Group',
+ },
+ {
+ componentName: 'Input',
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Input',
+ },
+ {
+ componentName: 'Form',
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Form',
+ },
+ {
+ componentName: 'Form.Item',
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Form',
+ subName: 'Item',
+ },
+ {
+ componentName: 'NumberPicker',
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'NumberPicker',
+ },
+ {
+ componentName: 'Select',
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Select',
+ },
+ ],
+ componentsTree: [
+ {
+ componentName: 'Page',
+ id: 'node$1',
+ props: {
+ ref: 'outterView',
+ autoLoading: true,
+ },
+ fileName: 'test',
+ state: {
+ text: 'outter',
+ },
+ children: [
+ {
+ componentName: 'Form',
+ id: 'node$2',
+ props: {
+ labelCol: 4,
+ style: {},
+ ref: 'testForm',
+ },
+ children: [
+ {
+ componentName: 'Form.Item',
+ id: 'node$3',
+ props: {
+ label: '姓名:',
+ name: 'name',
+ initValue: '李雷',
+ },
+ children: [
+ {
+ componentName: 'Input',
+ id: 'node$4',
+ props: {
+ placeholder: '请输入',
+ size: 'medium',
+ style: {
+ width: 320,
+ },
+ },
+ },
+ ],
+ },
+ {
+ componentName: 'Form.Item',
+ id: 'node$5',
+ props: {
+ label: '年龄:',
+ name: 'age',
+ initValue: '22',
+ },
+ children: [
+ {
+ componentName: 'NumberPicker',
+ id: 'node$6',
+ props: {
+ size: 'medium',
+ type: 'normal',
+ },
+ },
+ ],
+ },
+ {
+ componentName: 'Form.Item',
+ id: 'node$7',
+ props: {
+ label: '职业:',
+ name: 'profession',
+ },
+ children: [
+ {
+ componentName: 'Select',
+ id: 'node$8',
+ props: {
+ dataSource: [
+ {
+ label: '教师',
+ value: 't',
+ },
+ {
+ label: '医生',
+ value: 'd',
+ },
+ {
+ label: '歌手',
+ value: 's',
+ },
+ ],
+ },
+ },
+ ],
+ },
+ {
+ componentName: 'Div',
+ id: 'node$9',
+ props: {
+ style: {
+ textAlign: 'center',
+ },
+ },
+ children: [
+ {
+ componentName: 'Button.Group',
+ id: 'node$a',
+ props: {},
+ children: [
+ {
+ componentName: 'Button',
+ id: 'node$b',
+ props: {
+ type: 'primary',
+ style: {
+ margin: '0 5px 0 5px',
+ },
+ htmlType: 'submit',
+ },
+ children: ['提交'],
+ },
+ {
+ componentName: 'Button',
+ id: 'node$d',
+ props: {
+ type: 'normal',
+ style: {
+ margin: '0 5px 0 5px',
+ },
+ htmlType: 'reset',
+ },
+ children: ['重置'],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ constants: {
+ ENV: 'prod',
+ DOMAIN: 'xxx.alibaba-inc.com',
+ },
+ css: 'body {font-size: 12px;} .table { width: 100px;}',
+ config: {
+ sdkVersion: '1.0.3',
+ historyMode: 'hash',
+ targetRootID: 'J_Container',
+ layout: {
+ componentName: 'BasicLayout',
+ props: {
+ logo: '...',
+ name: '测试网站',
+ },
+ },
+ theme: {
+ package: '@alife/theme-fusion',
+ version: '^0.1.0',
+ primary: '#ff9966',
+ },
+ },
+ meta: {
+ name: 'demo应用',
+ git_group: 'appGroup',
+ project_name: 'app_demo',
+ description: '这是一个测试应用',
+ spma: 'spa23d',
+ creator: '月飞',
+ },
+};
+
+export default demoData;
diff --git a/packages/code-generator/src/parse/SchemaParser.ts b/packages/code-generator/src/parse/SchemaParser.ts
index 06c2e0548..b1880e49e 100644
--- a/packages/code-generator/src/parse/SchemaParser.ts
+++ b/packages/code-generator/src/parse/SchemaParser.ts
@@ -5,9 +5,12 @@
import { SUPPORT_SCHEMA_VERSION_LIST } from '../const';
+import { handleChildren } from '../utils/children';
import { uniqueArray } from '../utils/common';
import {
+ ChildNodeItem,
+ ChildNodeType,
CodeGeneratorError,
CompatibilityError,
DependencyType,
@@ -169,12 +172,10 @@ class SchemaParser implements ISchemaParser {
};
}
- public getComponentNames(list: IComponentNodeItem[]): string[] {
- const names = list.map(i => i.componentName);
- const namesForward = list
- .map(i => this.getComponentNames(i.children || []))
- .reduce((p, c) => p.concat(c), []);
- return names.concat(namesForward);
+ public getComponentNames(children: ChildNodeType): string[] {
+ return handleChildren(children, {
+ node: (i: IComponentNodeItem) => [i.componentName],
+ });
}
}
diff --git a/packages/code-generator/src/plugins/component/react/jsx.ts b/packages/code-generator/src/plugins/component/react/jsx.ts
index ff2a3f8cf..7dad9c0b3 100644
--- a/packages/code-generator/src/plugins/component/react/jsx.ts
+++ b/packages/code-generator/src/plugins/component/react/jsx.ts
@@ -1,9 +1,7 @@
-import { REACT_CHUNK_NAME } from './const';
-
-import { generateCompositeType } from '../../utils/compositeType';
-
import {
BuilderComponentPlugin,
+ ChildNodeItem,
+ ChildNodeType,
ChunkType,
FileType,
ICodeStruct,
@@ -13,6 +11,10 @@ import {
IJSExpression,
} from '../../../types';
+import { handleChildren } from '@/utils/children';
+import { generateCompositeType } from '../../utils/compositeType';
+import { REACT_CHUNK_NAME } from './const';
+
function generateInlineStyle(style: IInlineStyle): string | null {
const attrLines = Object.keys(style).map((cssAttribute: string) => {
const [isString, valueStr] = generateCompositeType(style[cssAttribute]);
@@ -52,9 +54,9 @@ function generateNode(nodeItem: IComponentNodeItem): string {
);
codePieces.push.apply(codePieces, propLines);
- if (nodeItem.children && nodeItem.children.length > 0) {
+ if (nodeItem.children && (nodeItem.children as unknown[]).length > 0) {
codePieces.push('>');
- const childrenLines = nodeItem.children.map(child => generateNode(child));
+ const childrenLines = generateChildren(nodeItem.children);
codePieces.push.apply(codePieces, childrenLines);
codePieces.push(`${nodeItem.componentName}>`);
} else {
@@ -87,6 +89,15 @@ function generateNode(nodeItem: IComponentNodeItem): string {
return codePieces.join(' ');
}
+function generateChildren(children: ChildNodeType): string[] {
+ return handleChildren(children, {
+ // TODO: 如果容器直接只有一个 字符串 children 呢?
+ string: (input: string) => [input],
+ expression: (input: IJSExpression) => [`{${input.value}}`],
+ node: (input: IComponentNodeItem) => [generateNode(input)],
+ });
+}
+
const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const next: ICodeStruct = {
...pre,
@@ -95,14 +106,17 @@ const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => {
const ir = next.ir as IContainerInfo;
let jsxContent: string;
- if (!ir.children || ir.children.length === 0) {
+ if (!ir.children || (ir.children as unknown[]).length === 0) {
jsxContent = 'null';
- } else if (ir.children.length === 1) {
- jsxContent = `(${generateNode(ir.children[0])})`;
} else {
- jsxContent = `(${ir.children
- .map(child => generateNode(child))
- .join('')})`;
+ const childrenCode = generateChildren(ir.children);
+ if (childrenCode.length === 1) {
+ jsxContent = `(${childrenCode[0]})`;
+ } else {
+ jsxContent = `(${childrenCode.join(
+ '',
+ )})`;
+ }
}
next.chunks.push({
diff --git a/packages/code-generator/src/types/intermediate.ts b/packages/code-generator/src/types/intermediate.ts
index 57d696b82..7ac66079a 100644
--- a/packages/code-generator/src/types/intermediate.ts
+++ b/packages/code-generator/src/types/intermediate.ts
@@ -8,18 +8,6 @@ import {
IUtilItem,
} from './index';
-export interface IPackageJSON {
- name: string;
- description: string;
- version: string;
- main?: string;
- author?: string;
- license?: string;
- scripts?: Record;
- dependencies?: Record;
- [key: string]: unknown;
-}
-
export interface IParseResult {
containers: IContainerInfo[];
globalUtils?: IUtilInfo;
diff --git a/packages/code-generator/src/types/schema.ts b/packages/code-generator/src/types/schema.ts
index 071e94c81..22b7ab870 100644
--- a/packages/code-generator/src/types/schema.ts
+++ b/packages/code-generator/src/types/schema.ts
@@ -91,8 +91,8 @@ export interface IInlineStyle {
[cssAttribute: string]: string | number | IJSExpression;
}
-type ChildNodeItem = string | IJSExpression | IComponentNodeItem;
-type ChildNodeType = ChildNodeItem | ChildNodeItem[];
+export type ChildNodeItem = string | IJSExpression | IComponentNodeItem;
+export type ChildNodeType = ChildNodeItem | ChildNodeItem[];
/**
* 搭建基础协议 - 单个组件树节点描述
@@ -102,6 +102,8 @@ type ChildNodeType = ChildNodeItem | ChildNodeItem[];
* @interface IComponentNodeItem
*/
export interface IComponentNodeItem {
+ // TODO: 不需要 id 字段,暂时简单兼容
+ id?: string;
componentName: string; // 组件名称 必填、首字母大写
props: {
className?: string; // 组件样式类名
@@ -170,7 +172,7 @@ export interface IDataSource {
* 返回值:数据对象data,将会在渲染引擎和schemaToCode中通过调用this.setState(...)将返回的数据对象生效到state中;
* 支持返回一个Promise,通过resolve(返回数据),常用于串型发送请求场景,配合this.dataSourceMap[oneRequest.id].load()使用;
*/
- dataHandler: IJSExpression;
+ dataHandler?: IJSExpression;
}
/**
@@ -182,7 +184,7 @@ export interface IDataSource {
export interface IDataSourceConfig {
id: string; // 数据请求ID标识
isInit: boolean; // 是否为初始数据 支持表达式 值为true时,将在组件初始化渲染时自动发送当前数据请求
- type: 'fetch' | 'mtop' | 'jsonp' | 'custom'; // 数据请求类型
+ type: 'fetch' | 'mtop' | 'jsonp' | 'custom' | 'doServer'; // 数据请求类型
requestHandler?: IJSExpression; // 自定义扩展的外部请求处理器 仅type='custom'时生效
options?: IFetchOptions; // 请求参数配置 每种请求类型对应不同参数
dataHandler?: IJSExpression; // 数据结果处理函数,形如:(data, err) => Object
@@ -201,8 +203,8 @@ export interface IFetchOptions {
[key: string]: any;
};
method: 'GET' | 'POST';
- isCors: boolean; // 是否支持跨域,对应credentials = 'include'
- timeout: number; // 超时时长
+ isCors?: boolean; // 是否支持跨域,对应credentials = 'include'
+ timeout?: number; // 超时时长
headers?: {
// 自定义请求头
[key: string]: string;
diff --git a/packages/code-generator/src/utils/children.ts b/packages/code-generator/src/utils/children.ts
new file mode 100644
index 000000000..3d848f988
--- /dev/null
+++ b/packages/code-generator/src/utils/children.ts
@@ -0,0 +1,35 @@
+import {
+ ChildNodeItem,
+ ChildNodeType,
+ IComponentNodeItem,
+ IJSExpression,
+} from '@/types';
+
+// tslint:disable-next-line: no-empty
+const noop = () => [];
+
+export function handleChildren(
+ children: ChildNodeType,
+ handlers: {
+ string?: (input: string) => T[];
+ expression?: (input: IJSExpression) => T[];
+ node?: (input: IComponentNodeItem) => T[];
+ common?: (input: unknown) => T[];
+ },
+): T[] {
+ if (Array.isArray(children)) {
+ const list: ChildNodeItem[] = children as ChildNodeItem[];
+ return list
+ .map(child => handleChildren(child, handlers))
+ .reduce((p, c) => p.concat(c), []);
+ } else if (typeof children === 'string') {
+ const handler = handlers.string || handlers.common || noop;
+ return handler(children as string);
+ } else if ((children as IJSExpression).type === 'JSExpression') {
+ const handler = handlers.expression || handlers.common || noop;
+ return handler(children as IJSExpression);
+ } else {
+ const handler = handlers.node || handlers.common || noop;
+ return handler(children as IComponentNodeItem);
+ }
+}