Merge branch 'develop' of github.com:alibaba/lowcode-engine into develop

This commit is contained in:
LeoYuan 袁力皓 2022-04-11 14:00:55 +08:00
commit 244839ef09
33 changed files with 518 additions and 66 deletions

View File

@ -2,6 +2,27 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [1.0.3](https://github.com/alibaba/lowcode-engine/compare/@alilc/lowcode-code-generator@1.0.2...@alilc/lowcode-code-generator@1.0.3) (2022-03-29)
### Features
* add getConvertedExtraKey / getOriginalExtraKey to utils ([8e7bb9d](https://github.com/alibaba/lowcode-engine/commit/8e7bb9d4b86454dd77c6928eb769cd764cad8630))
### Bug Fixes
* 🐛 出码: 解决 componentName 和 exportName 不一致时生成的 import 语句的问题 ([eefc091](https://github.com/alibaba/lowcode-engine/commit/eefc091ee7e86d6214d20d486212cb5aff237946))
* component cannot be redisplayed by configuration after rendering is closed ([c54f369](https://github.com/alibaba/lowcode-engine/commit/c54f369e1860d818479dda9d6429f851c0b08fa6))
* fix loop configuration auto fill empty array issue ([d087092](https://github.com/alibaba/lowcode-engine/commit/d087092fd712eff0556adacda692d3ff6f2f9f22))
* make important true by default ([c63b6e1](https://github.com/alibaba/lowcode-engine/commit/c63b6e1bfadc3fc87ed41840952e02ffbff24fab))
* make insertAfter & insertBefore work ([70fd372](https://github.com/alibaba/lowcode-engine/commit/70fd3720d098d6e227acb9281ee22feee66b9c0b))
* npm源 ([437adcc](https://github.com/alibaba/lowcode-engine/commit/437adccf5f2dbb400de6e2bef10cfc4b65286f2b))
* prop should return undefined when all items are undefined ([5bb9ec7](https://github.com/alibaba/lowcode-engine/commit/5bb9ec7a1dfaabfdb5369226b54d5f63a7999e59))
* should not create new prop while querying fileName ([19c207d](https://github.com/alibaba/lowcode-engine/commit/19c207d29de045f473ba73baaf34e7294d40261a))
* variable binding lost after modify the mock value ([ef95b56](https://github.com/alibaba/lowcode-engine/commit/ef95b5683273d8302bde1582de8afe3d87a808d8))
* Workbench should receive the original skeleton other than shell skeleton ([d5c3ca1](https://github.com/alibaba/lowcode-engine/commit/d5c3ca1068ce2c2140980bd059d0da333574dc34))
### [1.0.2](https://github.com/alibaba/lowcode-engine/compare/@alilc/lowcode-code-generator@1.0.2-beta.1...@alilc/lowcode-code-generator@1.0.2) (2022-03-08) ### [1.0.2](https://github.com/alibaba/lowcode-engine/compare/@alilc/lowcode-code-generator@1.0.2-beta.1...@alilc/lowcode-code-generator@1.0.2) (2022-03-08)
### [1.0.2-beta.1](https://github.com/alibaba/lowcode-engine/compare/@alilc/lowcode-code-generator@1.0.2-beta.0...@alilc/lowcode-code-generator@1.0.2-beta.1) (2022-03-08) ### [1.0.2-beta.1](https://github.com/alibaba/lowcode-engine/compare/@alilc/lowcode-code-generator@1.0.2-beta.0...@alilc/lowcode-code-generator@1.0.2-beta.1) (2022-03-08)

View File

@ -1,6 +1,6 @@
{ {
"name": "@alilc/lowcode-code-generator", "name": "@alilc/lowcode-code-generator",
"version": "1.0.2", "version": "1.0.3",
"description": "出码引擎 for LowCode Engine", "description": "出码引擎 for LowCode Engine",
"license": "MIT", "license": "MIT",
"main": "lib/index.js", "main": "lib/index.js",

View File

@ -1,4 +1,4 @@
import { flatMap } from 'lodash'; import { flatMap, camelCase, get } from 'lodash';
import { COMMON_CHUNK_NAME } from '../../const/generator'; import { COMMON_CHUNK_NAME } from '../../const/generator';
import { import {
@ -71,6 +71,37 @@ function getDependencyIdentifier(info: IDependencyItem): string {
return info.aliasName || info.exportName; return info.aliasName || info.exportName;
} }
function getExportNameOfDep(dep: IDependency): string {
if (dep.destructuring) {
return (
dep.exportName ||
dep.componentName ||
throwNewError('destructuring dependency must have exportName or componentName')
);
}
if (!dep.subName) {
return (
dep.componentName ||
dep.exportName ||
throwNewError('dependency item must have componentName or exportName')
);
}
return (
dep.exportName ||
`__$${camelCase(
get(dep, 'moduleName') ||
get(dep, 'package') ||
throwNewError('dep.moduleName or dep.package is undefined'),
)}_default`
);
}
function throwNewError(msg: string): never {
throw new Error(msg);
}
function buildPackageImport( function buildPackageImport(
pkg: string, pkg: string,
deps: IDependency[], deps: IDependency[],
@ -90,7 +121,7 @@ function buildPackageImport(
const depsInfo: IDependencyItem[] = deps.map((dep) => { const depsInfo: IDependencyItem[] = deps.map((dep) => {
const info: IDependencyItem = { const info: IDependencyItem = {
exportName: dep.exportName, exportName: getExportNameOfDep(dep),
isDefault: !dep.destructuring, isDefault: !dep.destructuring,
subName: dep.subName || undefined, subName: dep.subName || undefined,
nodeIdentifier: dep.componentName || undefined, nodeIdentifier: dep.componentName || undefined,
@ -171,24 +202,21 @@ function buildPackageImport(
// 发现 nodeIdentifier 与 exportName 或者 aliasName 冲突的场景 // 发现 nodeIdentifier 与 exportName 或者 aliasName 冲突的场景
const nodeIdentifiers = depsInfo.map((info) => info.nodeIdentifier).filter(Boolean); const nodeIdentifiers = depsInfo.map((info) => info.nodeIdentifier).filter(Boolean);
const conflictInfos = flatMap( const conflictInfos = flatMap(Object.keys(exportItems), (exportName) => {
Object.keys(exportItems), const exportItem = exportItems[exportName];
(exportName) => { const usedNames = [
const exportItem = exportItems[exportName]; ...exportItem.aliasNames,
const usedNames = [ ...(exportItem.needOriginExport || exportItem.aliasNames.length <= 0 ? [exportName] : []),
...exportItem.aliasNames, ];
...(exportItem.needOriginExport || exportItem.aliasNames.length <= 0 ? [exportName] : []), const conflictNames = usedNames.filter((n) => nodeIdentifiers.indexOf(n) >= 0);
if (conflictNames.length > 0) {
return [
...(conflictNames.indexOf(exportName) >= 0 ? [[exportName, true, exportItem]] : []),
...conflictNames.filter((n) => n !== exportName).map((n) => [n, false, exportItem]),
]; ];
const conflictNames = usedNames.filter((n) => nodeIdentifiers.indexOf(n) >= 0); }
if (conflictNames.length > 0) { return [];
return [ });
...(conflictNames.indexOf(exportName) >= 0 ? [[exportName, true, exportItem]] : []),
...conflictNames.filter((n) => n !== exportName).map((n) => [n, false, exportItem]),
];
}
return [];
},
);
const conflictExports = conflictInfos.filter((c) => c[1]).map((c) => c[0] as string); const conflictExports = conflictInfos.filter((c) => c[1]).map((c) => c[0] as string);
const conflictAlias = conflictInfos.filter((c) => !c[1]).map((c) => c[0] as string); const conflictAlias = conflictInfos.filter((c) => !c[1]).map((c) => c[0] as string);
@ -282,6 +310,12 @@ function buildPackageImport(
}, },
}); });
} else if (info.aliasName) { } else if (info.aliasName) {
// default 方式的导入会生成单独de import 语句,无需生成赋值语句
if (info.isDefault && defaultExportNames.find((n) => n === info.aliasName)) {
delete aliasDefineStatements[info.aliasName];
return;
}
let contentStatement = ''; let contentStatement = '';
if (aliasDefineStatements[info.aliasName]) { if (aliasDefineStatements[info.aliasName]) {
contentStatement = aliasDefineStatements[info.aliasName]; contentStatement = aliasDefineStatements[info.aliasName];

View File

@ -9,6 +9,8 @@ import Super, {
SearchTable as SearchTableExport, SearchTable as SearchTableExport,
} from "@alifd/next"; } from "@alifd/next";
import SuperOther from "@alifd/next";
import utils from "../../utils"; import utils from "../../utils";
import { i18n as _$$i18n } from "../../i18n"; import { i18n as _$$i18n } from "../../i18n";
@ -17,8 +19,6 @@ import "./index.css";
const SuperSub = Super.Sub; const SuperSub = Super.Sub;
const SuperOther = Super;
const SelectOption = Select.Option; const SelectOption = Select.Option;
const SearchTable = SearchTableExport.default; const SearchTable = SearchTableExport.default;

View File

@ -0,0 +1,34 @@
{
"version": "1.0.0",
"componentsMap": [
{
"package": "example-package",
"version": "1.2.3",
"exportName": "Bar",
"main": "lib/index.js",
"destructuring": false,
"subName": "",
"componentName": "Foo"
}
],
"componentsTree": [
{
"componentName": "Page",
"id": "node_ocl137q7oc1",
"fileName": "test",
"props": { "style": {} },
"lifeCycles": {},
"dataSource": { "list": [] },
"state": {},
"methods": {},
"children": [
{
"componentName": "Foo",
"id": "node_ocl137q7oc4",
"props": {}
}
]
}
],
"i18n": {}
}

View File

@ -0,0 +1,221 @@
import CodeGenerator from '../../src';
import * as fs from 'fs';
import * as path from 'path';
import { ProjectSchema } from '@alilc/lowcode-types';
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);
describe(testCaseBaseName, () => {
test('default import', async () => {
await exportProject(inputSchemaJsonFile, outputDir, {
componentsMap: [
{
package: 'example-package',
version: '1.2.3',
exportName: 'Bar',
main: 'lib/index.js',
destructuring: false,
subName: '',
componentName: 'Foo',
},
],
});
const generatedPageFileContent = readOutputTextFile('demo-project/src/pages/Test/index.jsx');
expect(generatedPageFileContent).toContain(`import Foo from "example-package/lib/index.js";`);
});
test('named import with no alias', async () => {
await exportProject(inputSchemaJsonFile, outputDir, {
componentsMap: [
{
package: 'example-package',
version: '1.2.3',
exportName: 'Foo',
main: 'lib/index.js',
destructuring: true,
subName: '',
componentName: 'Foo',
},
],
});
const generatedPageFileContent = readOutputTextFile('demo-project/src/pages/Test/index.jsx');
expect(generatedPageFileContent).toContain(
`import { Foo } from "example-package/lib/index.js";`,
);
});
test('named import with alias', async () => {
await exportProject(inputSchemaJsonFile, outputDir, {
componentsMap: [
{
package: 'example-package',
version: '1.2.3',
exportName: 'Bar',
main: 'lib/index.js',
destructuring: true,
subName: '',
componentName: 'Foo',
},
],
});
const generatedPageFileContent = readOutputTextFile('demo-project/src/pages/Test/index.jsx');
expect(generatedPageFileContent).toContain(
`import { Bar as Foo } from "example-package/lib/index.js";`,
);
});
test('default import with same name', async () => {
await exportProject(inputSchemaJsonFile, outputDir, {
componentsMap: [
{
package: 'example-package',
version: '1.2.3',
exportName: 'Foo',
main: 'lib/index.js',
destructuring: false,
subName: '',
componentName: 'Foo',
},
],
});
const generatedPageFileContent = readOutputTextFile('demo-project/src/pages/Test/index.jsx');
expect(generatedPageFileContent).toContain(`import Foo from "example-package/lib/index.js";`);
});
test('default import with sub name and export name', async () => {
await exportProject(inputSchemaJsonFile, outputDir, {
componentsMap: [
{
package: 'example-package',
version: '1.2.3',
exportName: 'Bar',
main: 'lib/index.js',
destructuring: false,
subName: 'Baz',
componentName: 'Foo',
},
],
});
const generatedPageFileContent = readOutputTextFile('demo-project/src/pages/Test/index.jsx');
expect(generatedPageFileContent).toContain(`import Bar from "example-package/lib/index.js";`);
expect(generatedPageFileContent).toContain(`const Foo = Bar.Baz;`);
});
test('default import with sub name without export name', async () => {
await exportProject(inputSchemaJsonFile, outputDir, {
componentsMap: [
{
package: 'example-package',
version: '1.2.3',
main: 'lib/index.js',
destructuring: false,
exportName: '',
subName: 'Baz',
componentName: 'Foo',
},
],
});
const generatedPageFileContent = readOutputTextFile('demo-project/src/pages/Test/index.jsx');
expect(generatedPageFileContent).toContain(
`import __$examplePackage_default from "example-package/lib/index.js";`,
);
expect(generatedPageFileContent).toContain(`const Foo = __$examplePackage_default.Baz;`);
});
test('named import with sub name', async () => {
await exportProject(inputSchemaJsonFile, outputDir, {
componentsMap: [
{
package: 'example-package',
version: '1.2.3',
exportName: 'Bar',
main: 'lib/index.js',
destructuring: true,
subName: 'Baz',
componentName: 'Foo',
},
],
});
const generatedPageFileContent = readOutputTextFile('demo-project/src/pages/Test/index.jsx');
expect(generatedPageFileContent).toContain(
`import { Bar } from "example-package/lib/index.js";`,
);
expect(generatedPageFileContent).toContain(`const Foo = Bar.Baz;`);
});
test('default imports with different componentName', async () => {
await exportProject(inputSchemaJsonFile, outputDir, {
componentsMap: [
{
package: 'example-package',
version: '1.2.3',
exportName: 'Bar',
destructuring: false,
componentName: 'Foo',
},
{
package: 'example-package',
version: '1.2.3',
exportName: 'Bar',
destructuring: false,
componentName: 'Baz',
},
],
componentsTree: [
{
componentName: 'Page',
fileName: 'test',
dataSource: { list: [] },
children: [{ componentName: 'Foo' }, { componentName: 'Baz' }],
},
],
});
const generatedPageFileContent = readOutputTextFile('demo-project/src/pages/Test/index.jsx');
expect(generatedPageFileContent).toContain(`import Foo from "example-package";`);
expect(generatedPageFileContent).toContain(`import Baz from "example-package";`);
expect(generatedPageFileContent).not.toContain(`const Foo =`);
expect(generatedPageFileContent).not.toContain(`const Baz =`);
});
});
function exportProject(
importPath: string,
outputPath: string,
mergeSchema?: Partial<ProjectSchema>,
) {
const schemaJsonStr = fs.readFileSync(importPath, { encoding: 'utf8' });
const schema = { ...JSON.parse(schemaJsonStr), ...mergeSchema };
const builder = CodeGenerator.solutions.icejs();
return builder.generateProject(schema).then(async (result) => {
// displayResultInConsole(result);
const publisher = CodeGenerator.publishers.disk();
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');
}

View File

@ -1,16 +1,22 @@
# @ali/lowcode-material-parser # @alilc/lowcode-material-parser
> 入料模块 > 入料模块
本模块负责物料接入,能自动扫描、解析源码组件,并最终产出一份符合《中后台搭建组件描述协议》的 **JSON Schema** 本模块负责物料接入,能自动扫描、解析源码组件,并最终产出一份符合《中后台搭建组件描述协议》的 **JSON Schema**
详见[文档](https://yuque.antfin-inc.com/ali-lowcode/docs/tyktrt)。 详见[文档](https://lowcode-engine.cn/docV2/yhgcqb)。
## demo ## demo
```shell ```shell
cd demo cd demo
node index.js
// parse jsx
node parse-jsx.js
// parse tsx
node parse-tsx.js
``` ```
## API ## API

View File

@ -0,0 +1,54 @@
/* eslint-disable react/forbid-prop-types,react/no-unused-prop-types */
import React from 'react';
import './main.scss';
interface DemoProps {
optionalArray?: [],
optionalBool: boolean,
optionalFunc: Function,
optionalNumber: number,
optionalObject: object,
optionalString: string,
optionalSymbol: symbol,
// Anything that can be rendered: numbers, strings, elements or an array
// (or fragment) containing these types.
optionalNode: React.ReactNode,
// A React element (ie. <MyComponent />).
optionalElement: React.ReactElement,
// A React element type (ie. MyComponent).
optionalElementType: React.ElementType,
// You can also declare that a prop is an instance of a class. This uses
// JS's instanceof operator.
optionalMessage: React.ReactInstance,
// You can ensure that your prop is limited to specific values by treating
// it as an enum.
optionalEnum: 'News'|'Photos',
// An object that could be one of many types
optionalUnion: string|number|React.ReactInstance,
// An array of a certain type
optionalArrayOf: number[],
// An object with property values of a certain type
optionalObjectOf: Record<number, any>,
// You can chain any of the above with `isRequired` to make sure a warning
// is shown if the prop isn't provided.
}
const Demo = (props: DemoProps) => {
return <div> Test </div>;
}
Demo.defaultProps = {
optionalString: 'optionalString'
};
export default Demo;

View File

@ -0,0 +1,11 @@
const parse = require('../lib').default;
(async () => {
const options = {
entry: './component.tsx',
accesser: 'local',
};
const actual = await parse(options);
console.log(JSON.stringify(actual, null, 2));
})();

View File

@ -1,6 +1,6 @@
{ {
"name": "@alilc/lowcode-material-parser", "name": "@alilc/lowcode-material-parser",
"version": "1.0.1", "version": "1.0.3",
"description": "material parser for Ali lowCode engine", "description": "material parser for Ali lowCode engine",
"main": "lib/index.js", "main": "lib/index.js",
"files": [ "files": [
@ -18,6 +18,8 @@
"@types/prop-types": "^15.7.3", "@types/prop-types": "^15.7.3",
"copy-webpack-plugin": "^9.1.0", "copy-webpack-plugin": "^9.1.0",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
"eslint": "^8.12.0",
"eslint-config-ali": "^14.0.0",
"jest": "^26.6.3", "jest": "^26.6.3",
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
"json-schema-to-typescript": "^8.2.0", "json-schema-to-typescript": "^8.2.0",

View File

@ -282,21 +282,21 @@ export function transformItem(name: string, item: any) {
if (!isNil(defaultValue) && typeof defaultValue === 'object' && isEvaluable(defaultValue)) { if (!isNil(defaultValue) && typeof defaultValue === 'object' && isEvaluable(defaultValue)) {
if (defaultValue === null) { if (defaultValue === null) {
result.defaultValue = defaultValue; result.defaultValue = defaultValue;
} else { } else if ('computed' in defaultValue) {
// if ('computed' in defaultValue) { // parsed data from react-docgen
// val = val.value;
try { try {
const value = safeEval(defaultValue.value); if (isEvaluable(defaultValue.value)) {
if (isEvaluable(value)) { result.defaultValue = safeEval(defaultValue.value);
result.defaultValue = value; } else {
result.defaultValue = defaultValue.value;
} }
} catch (e) { } catch (e) {
log(e); log(e);
} }
} else {
// parsed data from react-docgen-typescript
result.defaultValue = defaultValue.value;
} }
// else {
// result.defaultValue = defaultValue.value;
// }
} }
if (result.propType === undefined) { if (result.propType === undefined) {
delete result.propType; delete result.propType;

View File

@ -13,6 +13,14 @@ export async function isNPMInstalled(args: {
return pathExists(path.join(args.workDir, 'node_modules')); return pathExists(path.join(args.workDir, 'node_modules'));
} }
export async function isNPMModuleInstalled(
args: { workDir: string; moduleDir: string; npmClient?: string },
name: string,
) {
const modulePkgJsonPath = path.resolve(args.workDir, 'node_modules', name, 'package.json');
return pathExists(modulePkgJsonPath);
}
export async function install(args: { workDir: string; moduleDir: string; npmClient?: string }) { export async function install(args: { workDir: string; moduleDir: string; npmClient?: string }) {
if (await isNPMInstalled(args)) return; if (await isNPMInstalled(args)) return;
const { workDir, npmClient = 'tnpm' } = args; const { workDir, npmClient = 'tnpm' } = args;
@ -27,6 +35,7 @@ export async function installModule(
args: { workDir: string; moduleDir: string; npmClient?: string }, args: { workDir: string; moduleDir: string; npmClient?: string },
name: string, name: string,
) { ) {
if (await isNPMModuleInstalled(args, name)) return;
const { workDir, npmClient = 'tnpm' } = args; const { workDir, npmClient = 'tnpm' } = args;
try { try {
await spawn(npmClient, ['i', name], { stdio: 'inherit', cwd: workDir } as any); await spawn(npmClient, ['i', name], { stdio: 'inherit', cwd: workDir } as any);

View File

@ -26,7 +26,7 @@ export default class DragGhost extends Component<{ designer: Designer }> {
makeObservable(this); makeObservable(this);
this.dispose = [ this.dispose = [
this.dragon.onDragstart(e => { this.dragon.onDragstart(e => {
if (e.originalEvent.type.substr(0, 4) === 'drag') { if (e.originalEvent.type.slice(0, 4) === 'drag') {
return; return;
} }
this.dragObject = e.dragObject; this.dragObject = e.dragObject;

View File

@ -56,7 +56,7 @@ export class SettingPropEntry implements SettingEntry {
constructor(readonly parent: SettingEntry, name: string | number, type?: 'field' | 'group') { constructor(readonly parent: SettingEntry, name: string | number, type?: 'field' | 'group') {
makeObservable(this); makeObservable(this);
if (type == null) { if (type == null) {
const c = typeof name === 'string' ? name.substr(0, 1) : ''; const c = typeof name === 'string' ? name.slice(0, 1) : '';
if (c === '#') { if (c === '#') {
this.type = 'group'; this.type = 'group';
} else { } else {

View File

@ -14,7 +14,7 @@ export function getConvertedExtraKey(key: string): string {
if (key.indexOf('.') > 0) { if (key.indexOf('.') > 0) {
_key = key.split('.')[0]; _key = key.split('.')[0];
} }
return EXTRA_KEY_PREFIX + _key + EXTRA_KEY_PREFIX + key.substr(_key.length); return EXTRA_KEY_PREFIX + _key + EXTRA_KEY_PREFIX + key.slice(_key.length);
} }
export function getOriginalExtraKey(key: string): string { export function getOriginalExtraKey(key: string): string {
return key.replace(new RegExp(`${EXTRA_KEY_PREFIX}`, 'g'), ''); return key.replace(new RegExp(`${EXTRA_KEY_PREFIX}`, 'g'), '');

View File

@ -66,7 +66,7 @@ export function valueToSource(
indentString, indentString,
lineEnding, lineEnding,
visitedObjects: new Set([value, ...visitedObjects]), visitedObjects: new Set([value, ...visitedObjects]),
}).substr(indentLevel * indentString.length)})` }).slice(indentLevel * indentString.length)})`
: `${indentString.repeat(indentLevel)}new Map()`; : `${indentString.repeat(indentLevel)}new Map()`;
} }
@ -85,7 +85,7 @@ export function valueToSource(
indentString, indentString,
lineEnding, lineEnding,
visitedObjects: new Set([value, ...visitedObjects]), visitedObjects: new Set([value, ...visitedObjects]),
}).substr(indentLevel * indentString.length)})` }).slice(indentLevel * indentString.length)})`
: `${indentString.repeat(indentLevel)}new Set()`; : `${indentString.repeat(indentLevel)}new Set()`;
} }
@ -129,7 +129,7 @@ export function valueToSource(
if (item === null) { if (item === null) {
items.push(indentString.repeat(indentLevel + 1)); items.push(indentString.repeat(indentLevel + 1));
} else if (itemsStayOnTheSameLine) { } else if (itemsStayOnTheSameLine) {
items.push(item.substr(indentLevel * indentString.length)); items.push(item.slice(indentLevel * indentString.length));
} else { } else {
items.push(item); items.push(item);
} }
@ -166,11 +166,11 @@ export function valueToSource(
doubleQuote, doubleQuote,
}) })
: propertyName; : propertyName;
const trimmedPropertyValueString = propertyValueString.substr((indentLevel + 1) * indentString.length); const trimmedPropertyValueString = propertyValueString.slice((indentLevel + 1) * indentString.length);
if (typeof propertyValue === 'function' && trimmedPropertyValueString.startsWith(`${propertyName}()`)) { if (typeof propertyValue === 'function' && trimmedPropertyValueString.startsWith(`${propertyName}()`)) {
entries.push( entries.push(
`${indentString.repeat(indentLevel + 1)}${quotedPropertyName} ${trimmedPropertyValueString.substr( `${indentString.repeat(indentLevel + 1)}${quotedPropertyName} ${trimmedPropertyValueString.slice(
propertyName.length, propertyName.length,
)}`, )}`,
); );

View File

@ -79,7 +79,7 @@ function resolvePrefer(prefer: any, targetRect: any, bounds: any) {
} }
const force = prefer[0] === '!'; const force = prefer[0] === '!';
if (force) { if (force) {
prefer = prefer.substr(1); prefer = prefer.slice(1);
} }
let [dir, offset] = prefer.split(/\s+/); let [dir, offset] = prefer.split(/\s+/);
let forceDirection = false; let forceDirection = false;

View File

@ -29,7 +29,7 @@
</div> </div>
[![](https://img.alicdn.com/imgextra/i4/O1CN01GhzQuE1rnenyCCQTF_!!6000000005676-0-tps-2878-1588.jpg)](https://lowcode-engine.cn) [![](https://img.alicdn.com/imgextra/i2/O1CN01UhoS7C1sNNhySvfWi_!!6000000005754-2-tps-2878-1588.png)](https://lowcode-engine.cn)
简体中文 | [English](./README.md) 简体中文 | [English](./README.md)
@ -47,9 +47,9 @@
## 📚 引擎协议 ## 📚 引擎协议
引擎完整实现了《阿里巴巴中后台前端基础搭建协议规范》和《阿里巴巴中后台前端物料协议规范》,协议栈是低代码领域的物料能否流通的关键部分。 引擎完整实现了《低代码引擎搭建协议规范》和《低代码引擎物料协议规范》,协议栈是低代码领域的物料能否流通的关键部分。
![image](https://user-images.githubusercontent.com/1195765/150266126-fef3e3a9-d6a4-4f8e-8592-745f1a344162.png) ![image](https://img.alicdn.com/imgextra/i3/O1CN01IisBcy1dNBIg16QFM_!!6000000003723-2-tps-1916-1070.png)
## 🌰 使用示例 ## 🌰 使用示例
@ -152,4 +152,4 @@ lowcode-engine 启动后,提供了几个 umd 文件,可以结合 [lowcode-de
2. [关于引擎的研发协作流程](https://www.yuque.com/lce/doc/contributing) 2. [关于引擎的研发协作流程](https://www.yuque.com/lce/doc/contributing)
3. [引擎的工程化配置](https://www.yuque.com/lce/doc/gxwqg6) 3. [引擎的工程化配置](https://www.yuque.com/lce/doc/gxwqg6)
> 强烈推荐阅读 [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)、[《如何向开源社区提问题》](https://github.com/seajs/seajs/issues/545) 和 [《如何有效地报告 Bug》](http://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html)、[《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393),更好的问题更容易获得帮助。(此段参考 [antd](https://github.com/ant-design/ant-design) > 强烈推荐阅读 [《提问的智慧》](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way)、[《如何向开源社区提问题》](https://github.com/seajs/seajs/issues/545) 和 [《如何有效地报告 Bug》](http://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html)、[《如何向开源项目提交无法解答的问题》](https://zhuanlan.zhihu.com/p/25795393),更好的问题更容易获得帮助。(此段参考 [antd](https://github.com/ant-design/ant-design)

View File

@ -29,7 +29,7 @@ An enterprise-class low-code technology stack for scale-out design
</div> </div>
[![](https://img.alicdn.com/imgextra/i4/O1CN01GhzQuE1rnenyCCQTF_!!6000000005676-0-tps-2878-1588.jpg)](http://lowcode-engine.cn) [![](https://img.alicdn.com/imgextra/i2/O1CN01UhoS7C1sNNhySvfWi_!!6000000005754-2-tps-2878-1588.png)](http://lowcode-engine.cn)
English | [简体中文](./packages/engine/README-zh_CN.md) English | [简体中文](./packages/engine/README-zh_CN.md)
@ -47,9 +47,9 @@ English | [简体中文](./packages/engine/README-zh_CN.md)
## 📚 Engine Protocol ## 📚 Engine Protocol
The engine fully implements the "Alibaba Mid-Backend Front-End Basic Construction Protocol Specification" and "Alibaba Mid-Backend Front-End Material Protocol Specification". The protocol stack is a key part of whether materials in the low-code field can be circulated. The engine fully implements the "LowCodeEngine Basic Construction Protocol Specification" and "LowCodeEngine Material Protocol Specification". The protocol stack is a key part of whether materials in the low-code field can be circulated.
![image](https://user-images.githubusercontent.com/1195765/150266126-fef3e3a9-d6a4-4f8e-8592-745f1a344162.png) ![image](https://img.alicdn.com/imgextra/i3/O1CN01IisBcy1dNBIg16QFM_!!6000000003723-2-tps-1916-1070.png)
## 🌰 Usage example ## 🌰 Usage example
@ -152,4 +152,4 @@ Please read first:
2. [About the R&D collaboration process of the engine](https://www.yuque.com/lce/doc/contributing) 2. [About the R&D collaboration process of the engine](https://www.yuque.com/lce/doc/contributing)
3. [Engineering Configuration of Engine](https://www.yuque.com/lce/doc/gxwqg6) 3. [Engineering Configuration of Engine](https://www.yuque.com/lce/doc/gxwqg6)
> Strongly recommend reading ["The Wisdom of Asking Questions"](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way), ["How to Ask Questions to the Open Source Community"](https: //github.com/seajs/seajs/issues/545) and [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html), [ "How to Submit Unanswerable Questions to Open Source Projects"](https://zhuanlan.zhihu.com/p/25795393), better questions are easier to get help. (This paragraph refers to [antd](https://github.com/ant-design/ant-design)) > Strongly recommend reading ["The Wisdom of Asking Questions"](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way), ["How to Ask Questions to the Open Source Community"](https: //github.com/seajs/seajs/issues/545) and [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/%7Esgtatham/bugs-cn.html), [ "How to Submit Unanswerable Questions to Open Source Projects"](https://zhuanlan.zhihu.com/p/25795393), better questions are easier to get help. (This paragraph refers to [antd](https://github.com/ant-design/ant-design))

View File

@ -59,7 +59,7 @@ function matchPath(route, pathname, parentParams) {
} }
return { return {
path: !end && url.charAt(url.length - 1) === '/' ? url.substr(1) : url, path: !end && url.charAt(url.length - 1) === '/' ? url.slice(1) : url,
params, params,
}; };
} }
@ -96,7 +96,7 @@ function matchRoute(route, baseUrl, pathname, parentParams) {
childMatches = matchRoute( childMatches = matchRoute(
childRoute, childRoute,
baseUrl + matched.path, baseUrl + matched.path,
pathname.substr(matched.path.length), pathname.slice(matched.path.length),
matched.params, matched.params,
); );
} }

View File

@ -291,7 +291,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
}); });
this.history = history; this.history = history;
history.listen(({ location }) => { history.listen(({ location }) => {
host.project.open(location.pathname.substr(1)); host.project.open(location.pathname.slice(1));
}); });
host.componentsConsumer.consume(async (componentsAsset) => { host.componentsConsumer.consume(async (componentsAsset) => {
if (componentsAsset) { if (componentsAsset) {

View File

@ -1,5 +1,3 @@
const esModules = ['@recore/obx-react'].join('|');
module.exports = { module.exports = {
// transform: { // transform: {
// '^.+\\.[jt]sx?$': 'babel-jest', // '^.+\\.[jt]sx?$': 'babel-jest',
@ -7,9 +5,6 @@ module.exports = {
// // '^.+\\.(js|jsx)$': 'babel-jest', // // '^.+\\.(js|jsx)$': 'babel-jest',
// }, // },
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'], // testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
transformIgnorePatterns: [
`/node_modules/(?!${esModules})/`,
],
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
collectCoverage: true, collectCoverage: true,
collectCoverageFrom: [ collectCoverageFrom: [

View File

@ -264,7 +264,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
}); });
this.history = history; this.history = history;
history.listen((location, action) => { history.listen((location, action) => {
const docId = location.pathname.substr(1); const docId = location.pathname.slice(1);
docId && host.project.open(docId); docId && host.project.open(docId);
}); });
host.componentsConsumer.consume(async (componentsAsset) => { host.componentsConsumer.consume(async (componentsAsset) => {

View File

@ -1,5 +1,4 @@
const esModules = [ const esModules = [
'@recore/obx-react',
'@alilc/lowcode-datasource-engine', '@alilc/lowcode-datasource-engine',
].join('|'); ].join('|');

View File

@ -30,7 +30,7 @@ export default function componentRendererFactory(): IBaseRenderComponent {
}); });
this.__render(); this.__render();
const { noContainer } = this.__parseData(__schema.props); const noContainer = this.__parseData(__schema.props?.noContainer);
if (noContainer) { if (noContainer) {
return this.__renderContextProvider({ compContext: this }); return this.__renderContextProvider({ compContext: this });

View File

@ -179,7 +179,7 @@ export class DataHelper {
const _tb_token_ = (csrfInput as any)?.value; const _tb_token_ = (csrfInput as any)?.value;
asyncDataList.forEach((req) => { asyncDataList.forEach((req) => {
const { id, type, options } = req; const { id, type, options } = req;
if (!id || !type || type === 'legao') return; if (!id || !type) return;
if (type === 'doServer') { if (type === 'doServer') {
const { uri, params } = options || {}; const { uri, params } = options || {};
if (!uri) return; if (!uri) return;
@ -314,4 +314,4 @@ export class DataHelper {
} }
} }
type DataSourceType = 'fetch' | 'jsonp'; type DataSourceType = 'fetch' | 'jsonp';

View File

@ -0,0 +1,6 @@
{
"plugins": [
"build-plugin-component",
"@alilc/lowcode-test-mate/plugin/index.ts"
]
}

View File

@ -0,0 +1,9 @@
module.exports = {
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
collectCoverage: true,
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!**/node_modules/**',
'!**/vendor/**',
],
};

View File

@ -9,6 +9,7 @@
"main": "lib/index.js", "main": "lib/index.js",
"module": "es/index.js", "module": "es/index.js",
"scripts": { "scripts": {
"test": "build-scripts test --config build.test.json",
"build": "build-scripts build --skip-demo" "build": "build-scripts build --skip-demo"
}, },
"dependencies": { "dependencies": {

View File

@ -26,7 +26,7 @@ export function compatibleLegaoSchema(props: any): any {
type: 'JSSlot', type: 'JSSlot',
title: (props.value.props as any)?.slotTitle, title: (props.value.props as any)?.slotTitle,
name: (props.value.props as any)?.slotName, name: (props.value.props as any)?.slotName,
value: props.value.children, value: compatibleLegaoSchema(props.value.children),
params: (props.value.props as any)?.slotParams, params: (props.value.props as any)?.slotParams,
}; };
} else { } else {

View File

@ -0,0 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Schema Ut props 1`] = `
Object {
"props": Object {
"mobileSlot": Object {
"name": undefined,
"params": undefined,
"title": undefined,
"type": "JSSlot",
"value": Array [
Object {
"loop": Object {
"mock": undefined,
"type": "JSExpression",
"value": "props.content",
},
},
],
},
},
}
`;

View File

@ -0,0 +1,27 @@
import { compatibleLegaoSchema } from '../../src/schema';
describe('Schema Ut', () => {
it('props', () => {
const schema = {
props: {
mobileSlot: {
type: "JSBlock",
value: {
componentName: "Slot",
children: [
{
loop: {
variable: "props.content",
type: "variable"
},
}
],
}
},
},
};
const result = compatibleLegaoSchema(schema);
expect(result).toMatchSnapshot();
expect(result.props.mobileSlot.value[0].loop.type).toBe('JSExpression');
});
})