diff --git a/modules/code-generator/CHANGELOG.md b/modules/code-generator/CHANGELOG.md index b79585ae0..e80d64204 100644 --- a/modules/code-generator/CHANGELOG.md +++ b/modules/code-generator/CHANGELOG.md @@ -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. +### [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-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) diff --git a/modules/code-generator/package.json b/modules/code-generator/package.json index f6d26e3cb..27253b3ee 100644 --- a/modules/code-generator/package.json +++ b/modules/code-generator/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-code-generator", - "version": "1.0.2", + "version": "1.0.3", "description": "出码引擎 for LowCode Engine", "license": "MIT", "main": "lib/index.js", diff --git a/modules/code-generator/src/plugins/common/esmodule.ts b/modules/code-generator/src/plugins/common/esmodule.ts index 3401d5eac..ee28083d1 100644 --- a/modules/code-generator/src/plugins/common/esmodule.ts +++ b/modules/code-generator/src/plugins/common/esmodule.ts @@ -1,4 +1,4 @@ -import { flatMap } from 'lodash'; +import { flatMap, camelCase, get } from 'lodash'; import { COMMON_CHUNK_NAME } from '../../const/generator'; import { @@ -71,6 +71,37 @@ function getDependencyIdentifier(info: IDependencyItem): string { 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( pkg: string, deps: IDependency[], @@ -90,7 +121,7 @@ function buildPackageImport( const depsInfo: IDependencyItem[] = deps.map((dep) => { const info: IDependencyItem = { - exportName: dep.exportName, + exportName: getExportNameOfDep(dep), isDefault: !dep.destructuring, subName: dep.subName || undefined, nodeIdentifier: dep.componentName || undefined, @@ -171,24 +202,21 @@ function buildPackageImport( // 发现 nodeIdentifier 与 exportName 或者 aliasName 冲突的场景 const nodeIdentifiers = depsInfo.map((info) => info.nodeIdentifier).filter(Boolean); - const conflictInfos = flatMap( - Object.keys(exportItems), - (exportName) => { - const exportItem = exportItems[exportName]; - const usedNames = [ - ...exportItem.aliasNames, - ...(exportItem.needOriginExport || exportItem.aliasNames.length <= 0 ? [exportName] : []), + const conflictInfos = flatMap(Object.keys(exportItems), (exportName) => { + const exportItem = exportItems[exportName]; + const usedNames = [ + ...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 [ - ...(conflictNames.indexOf(exportName) >= 0 ? [[exportName, true, exportItem]] : []), - ...conflictNames.filter((n) => n !== exportName).map((n) => [n, false, exportItem]), - ]; - } - return []; - }, - ); + } + return []; + }); 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); @@ -282,6 +310,12 @@ function buildPackageImport( }, }); } else if (info.aliasName) { + // default 方式的导入会生成单独de import 语句,无需生成赋值语句 + if (info.isDefault && defaultExportNames.find((n) => n === info.aliasName)) { + delete aliasDefineStatements[info.aliasName]; + return; + } + let contentStatement = ''; if (aliasDefineStatements[info.aliasName]) { contentStatement = aliasDefineStatements[info.aliasName]; diff --git a/modules/code-generator/test-cases/react-app/demo3/expected/demo-project/src/pages/Test/index.jsx b/modules/code-generator/test-cases/react-app/demo3/expected/demo-project/src/pages/Test/index.jsx index 70b913417..adf65c1b7 100644 --- a/modules/code-generator/test-cases/react-app/demo3/expected/demo-project/src/pages/Test/index.jsx +++ b/modules/code-generator/test-cases/react-app/demo3/expected/demo-project/src/pages/Test/index.jsx @@ -9,6 +9,8 @@ import Super, { SearchTable as SearchTableExport, } from "@alifd/next"; +import SuperOther from "@alifd/next"; + import utils from "../../utils"; import { i18n as _$$i18n } from "../../i18n"; @@ -17,8 +19,6 @@ import "./index.css"; const SuperSub = Super.Sub; -const SuperOther = Super; - const SelectOption = Select.Option; const SearchTable = SearchTableExport.default; diff --git a/modules/code-generator/tests/bugfix/icejs-import-wrong-naming.schema.json b/modules/code-generator/tests/bugfix/icejs-import-wrong-naming.schema.json new file mode 100644 index 000000000..459b1aac9 --- /dev/null +++ b/modules/code-generator/tests/bugfix/icejs-import-wrong-naming.schema.json @@ -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": {} +} diff --git a/modules/code-generator/tests/bugfix/icejs-import-wrong-naming.test.ts b/modules/code-generator/tests/bugfix/icejs-import-wrong-naming.test.ts new file mode 100644 index 000000000..92003788f --- /dev/null +++ b/modules/code-generator/tests/bugfix/icejs-import-wrong-naming.test.ts @@ -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, +) { + 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'); +} diff --git a/modules/material-parser/README.md b/modules/material-parser/README.md index a217dc16f..4507b7e6e 100644 --- a/modules/material-parser/README.md +++ b/modules/material-parser/README.md @@ -1,16 +1,22 @@ -# @ali/lowcode-material-parser +# @alilc/lowcode-material-parser > 入料模块 本模块负责物料接入,能自动扫描、解析源码组件,并最终产出一份符合《中后台搭建组件描述协议》的 **JSON Schema**。 -详见[文档](https://yuque.antfin-inc.com/ali-lowcode/docs/tyktrt)。 +详见[文档](https://lowcode-engine.cn/docV2/yhgcqb)。 ## demo ```shell cd demo -node index.js + +// parse jsx +node parse-jsx.js + +// parse tsx +node parse-tsx.js + ``` ## API diff --git a/modules/material-parser/demo/component.tsx b/modules/material-parser/demo/component.tsx new file mode 100644 index 000000000..29eb7122d --- /dev/null +++ b/modules/material-parser/demo/component.tsx @@ -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. ). + 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, + + // 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
Test
; +} + +Demo.defaultProps = { + optionalString: 'optionalString' +}; + +export default Demo; diff --git a/modules/material-parser/demo/index.js b/modules/material-parser/demo/parse-jsx.js similarity index 100% rename from modules/material-parser/demo/index.js rename to modules/material-parser/demo/parse-jsx.js diff --git a/modules/material-parser/demo/parse-tsx.js b/modules/material-parser/demo/parse-tsx.js new file mode 100644 index 000000000..05d4b1e45 --- /dev/null +++ b/modules/material-parser/demo/parse-tsx.js @@ -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)); +})(); diff --git a/modules/material-parser/package.json b/modules/material-parser/package.json index 4b3c4f160..7d7ea57c2 100644 --- a/modules/material-parser/package.json +++ b/modules/material-parser/package.json @@ -1,6 +1,6 @@ { "name": "@alilc/lowcode-material-parser", - "version": "1.0.1", + "version": "1.0.3", "description": "material parser for Ali lowCode engine", "main": "lib/index.js", "files": [ @@ -18,6 +18,8 @@ "@types/prop-types": "^15.7.3", "copy-webpack-plugin": "^9.1.0", "copyfiles": "^2.4.1", + "eslint": "^8.12.0", + "eslint-config-ali": "^14.0.0", "jest": "^26.6.3", "js-yaml": "^3.13.1", "json-schema-to-typescript": "^8.2.0", diff --git a/modules/material-parser/src/parse/transform.ts b/modules/material-parser/src/parse/transform.ts index 6325be8b4..9a92f3aba 100644 --- a/modules/material-parser/src/parse/transform.ts +++ b/modules/material-parser/src/parse/transform.ts @@ -282,21 +282,21 @@ export function transformItem(name: string, item: any) { if (!isNil(defaultValue) && typeof defaultValue === 'object' && isEvaluable(defaultValue)) { if (defaultValue === null) { result.defaultValue = defaultValue; - } else { - // if ('computed' in defaultValue) { - // val = val.value; + } else if ('computed' in defaultValue) { + // parsed data from react-docgen try { - const value = safeEval(defaultValue.value); - if (isEvaluable(value)) { - result.defaultValue = value; + if (isEvaluable(defaultValue.value)) { + result.defaultValue = safeEval(defaultValue.value); + } else { + result.defaultValue = defaultValue.value; } } catch (e) { log(e); } + } else { + // parsed data from react-docgen-typescript + result.defaultValue = defaultValue.value; } - // else { - // result.defaultValue = defaultValue.value; - // } } if (result.propType === undefined) { delete result.propType; diff --git a/modules/material-parser/src/utils.ts b/modules/material-parser/src/utils.ts index dfd36d880..573a98045 100644 --- a/modules/material-parser/src/utils.ts +++ b/modules/material-parser/src/utils.ts @@ -13,6 +13,14 @@ export async function isNPMInstalled(args: { 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 }) { if (await isNPMInstalled(args)) return; const { workDir, npmClient = 'tnpm' } = args; @@ -27,6 +35,7 @@ export async function installModule( args: { workDir: string; moduleDir: string; npmClient?: string }, name: string, ) { + if (await isNPMModuleInstalled(args, name)) return; const { workDir, npmClient = 'tnpm' } = args; try { await spawn(npmClient, ['i', name], { stdio: 'inherit', cwd: workDir } as any); diff --git a/packages/designer/src/designer/drag-ghost/index.tsx b/packages/designer/src/designer/drag-ghost/index.tsx index a9ecd08b6..28c9174b5 100644 --- a/packages/designer/src/designer/drag-ghost/index.tsx +++ b/packages/designer/src/designer/drag-ghost/index.tsx @@ -26,7 +26,7 @@ export default class DragGhost extends Component<{ designer: Designer }> { makeObservable(this); this.dispose = [ this.dragon.onDragstart(e => { - if (e.originalEvent.type.substr(0, 4) === 'drag') { + if (e.originalEvent.type.slice(0, 4) === 'drag') { return; } this.dragObject = e.dragObject; diff --git a/packages/designer/src/designer/setting/setting-prop-entry.ts b/packages/designer/src/designer/setting/setting-prop-entry.ts index d588f9d97..cfd603ea4 100644 --- a/packages/designer/src/designer/setting/setting-prop-entry.ts +++ b/packages/designer/src/designer/setting/setting-prop-entry.ts @@ -56,7 +56,7 @@ export class SettingPropEntry implements SettingEntry { constructor(readonly parent: SettingEntry, name: string | number, type?: 'field' | 'group') { makeObservable(this); if (type == null) { - const c = typeof name === 'string' ? name.substr(0, 1) : ''; + const c = typeof name === 'string' ? name.slice(0, 1) : ''; if (c === '#') { this.type = 'group'; } else { diff --git a/packages/designer/src/document/node/props/props.ts b/packages/designer/src/document/node/props/props.ts index e5aad1a83..01dbbb8b4 100644 --- a/packages/designer/src/document/node/props/props.ts +++ b/packages/designer/src/document/node/props/props.ts @@ -14,7 +14,7 @@ export function getConvertedExtraKey(key: string): string { if (key.indexOf('.') > 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 { return key.replace(new RegExp(`${EXTRA_KEY_PREFIX}`, 'g'), ''); diff --git a/packages/designer/src/document/node/props/value-to-source.ts b/packages/designer/src/document/node/props/value-to-source.ts index 77f5db856..a3ad81b75 100644 --- a/packages/designer/src/document/node/props/value-to-source.ts +++ b/packages/designer/src/document/node/props/value-to-source.ts @@ -66,7 +66,7 @@ export function valueToSource( indentString, lineEnding, visitedObjects: new Set([value, ...visitedObjects]), - }).substr(indentLevel * indentString.length)})` + }).slice(indentLevel * indentString.length)})` : `${indentString.repeat(indentLevel)}new Map()`; } @@ -85,7 +85,7 @@ export function valueToSource( indentString, lineEnding, visitedObjects: new Set([value, ...visitedObjects]), - }).substr(indentLevel * indentString.length)})` + }).slice(indentLevel * indentString.length)})` : `${indentString.repeat(indentLevel)}new Set()`; } @@ -129,7 +129,7 @@ export function valueToSource( if (item === null) { items.push(indentString.repeat(indentLevel + 1)); } else if (itemsStayOnTheSameLine) { - items.push(item.substr(indentLevel * indentString.length)); + items.push(item.slice(indentLevel * indentString.length)); } else { items.push(item); } @@ -166,11 +166,11 @@ export function valueToSource( doubleQuote, }) : propertyName; - const trimmedPropertyValueString = propertyValueString.substr((indentLevel + 1) * indentString.length); + const trimmedPropertyValueString = propertyValueString.slice((indentLevel + 1) * indentString.length); if (typeof propertyValue === 'function' && trimmedPropertyValueString.startsWith(`${propertyName}()`)) { entries.push( - `${indentString.repeat(indentLevel + 1)}${quotedPropertyName} ${trimmedPropertyValueString.substr( + `${indentString.repeat(indentLevel + 1)}${quotedPropertyName} ${trimmedPropertyValueString.slice( propertyName.length, )}`, ); diff --git a/packages/editor-core/src/widgets/tip/utils.ts b/packages/editor-core/src/widgets/tip/utils.ts index 20fff3a4b..6dc3f6184 100644 --- a/packages/editor-core/src/widgets/tip/utils.ts +++ b/packages/editor-core/src/widgets/tip/utils.ts @@ -79,7 +79,7 @@ function resolvePrefer(prefer: any, targetRect: any, bounds: any) { } const force = prefer[0] === '!'; if (force) { - prefer = prefer.substr(1); + prefer = prefer.slice(1); } let [dir, offset] = prefer.split(/\s+/); let forceDirection = false; diff --git a/packages/engine/README-zh_CN.md b/packages/engine/README-zh_CN.md index e96b85b89..9cd5f85d3 100644 --- a/packages/engine/README-zh_CN.md +++ b/packages/engine/README-zh_CN.md @@ -29,7 +29,7 @@ -[![](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) @@ -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) 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)) \ No newline at end of file +> 强烈推荐阅读 [《提问的智慧》](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)) diff --git a/packages/engine/README.md b/packages/engine/README.md index 9c2e925ec..b442ff582 100644 --- a/packages/engine/README.md +++ b/packages/engine/README.md @@ -29,7 +29,7 @@ An enterprise-class low-code technology stack for scale-out design -[![](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) @@ -47,9 +47,9 @@ English | [简体中文](./packages/engine/README-zh_CN.md) ## 📚 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 @@ -152,4 +152,4 @@ Please read first: 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) -> 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)) \ No newline at end of file +> 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)) diff --git a/packages/rax-simulator-renderer/src/rax-use-router.js b/packages/rax-simulator-renderer/src/rax-use-router.js index a759d127c..9a399a947 100644 --- a/packages/rax-simulator-renderer/src/rax-use-router.js +++ b/packages/rax-simulator-renderer/src/rax-use-router.js @@ -59,7 +59,7 @@ function matchPath(route, pathname, parentParams) { } return { - path: !end && url.charAt(url.length - 1) === '/' ? url.substr(1) : url, + path: !end && url.charAt(url.length - 1) === '/' ? url.slice(1) : url, params, }; } @@ -96,7 +96,7 @@ function matchRoute(route, baseUrl, pathname, parentParams) { childMatches = matchRoute( childRoute, baseUrl + matched.path, - pathname.substr(matched.path.length), + pathname.slice(matched.path.length), matched.params, ); } diff --git a/packages/rax-simulator-renderer/src/renderer.ts b/packages/rax-simulator-renderer/src/renderer.ts index 05cf09f5d..7a8e85e5a 100644 --- a/packages/rax-simulator-renderer/src/renderer.ts +++ b/packages/rax-simulator-renderer/src/renderer.ts @@ -291,7 +291,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer { }); this.history = history; history.listen(({ location }) => { - host.project.open(location.pathname.substr(1)); + host.project.open(location.pathname.slice(1)); }); host.componentsConsumer.consume(async (componentsAsset) => { if (componentsAsset) { diff --git a/packages/react-renderer/jest.config.js b/packages/react-renderer/jest.config.js index 865d30116..00bf21f6d 100644 --- a/packages/react-renderer/jest.config.js +++ b/packages/react-renderer/jest.config.js @@ -1,5 +1,3 @@ -const esModules = ['@recore/obx-react'].join('|'); - module.exports = { // transform: { // '^.+\\.[jt]sx?$': 'babel-jest', @@ -7,9 +5,6 @@ module.exports = { // // '^.+\\.(js|jsx)$': 'babel-jest', // }, // testMatch: ['(/tests?/.*(test))\\.[jt]s$'], - transformIgnorePatterns: [ - `/node_modules/(?!${esModules})/`, - ], moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], collectCoverage: true, collectCoverageFrom: [ diff --git a/packages/react-simulator-renderer/src/renderer.ts b/packages/react-simulator-renderer/src/renderer.ts index b462a521e..45ce896f7 100644 --- a/packages/react-simulator-renderer/src/renderer.ts +++ b/packages/react-simulator-renderer/src/renderer.ts @@ -264,7 +264,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer { }); this.history = history; history.listen((location, action) => { - const docId = location.pathname.substr(1); + const docId = location.pathname.slice(1); docId && host.project.open(docId); }); host.componentsConsumer.consume(async (componentsAsset) => { diff --git a/packages/renderer-core/jest.config.js b/packages/renderer-core/jest.config.js index 00b2654f9..af6e06512 100644 --- a/packages/renderer-core/jest.config.js +++ b/packages/renderer-core/jest.config.js @@ -1,5 +1,4 @@ const esModules = [ - '@recore/obx-react', '@alilc/lowcode-datasource-engine', ].join('|'); diff --git a/packages/renderer-core/src/renderer/component.tsx b/packages/renderer-core/src/renderer/component.tsx index 1e06bcc5f..f79dab8c9 100644 --- a/packages/renderer-core/src/renderer/component.tsx +++ b/packages/renderer-core/src/renderer/component.tsx @@ -30,7 +30,7 @@ export default function componentRendererFactory(): IBaseRenderComponent { }); this.__render(); - const { noContainer } = this.__parseData(__schema.props); + const noContainer = this.__parseData(__schema.props?.noContainer); if (noContainer) { return this.__renderContextProvider({ compContext: this }); diff --git a/packages/renderer-core/src/utils/data-helper.ts b/packages/renderer-core/src/utils/data-helper.ts index 50564937a..59c6aebef 100644 --- a/packages/renderer-core/src/utils/data-helper.ts +++ b/packages/renderer-core/src/utils/data-helper.ts @@ -179,7 +179,7 @@ export class DataHelper { const _tb_token_ = (csrfInput as any)?.value; asyncDataList.forEach((req) => { const { id, type, options } = req; - if (!id || !type || type === 'legao') return; + if (!id || !type) return; if (type === 'doServer') { const { uri, params } = options || {}; if (!uri) return; @@ -314,4 +314,4 @@ export class DataHelper { } } -type DataSourceType = 'fetch' | 'jsonp'; \ No newline at end of file +type DataSourceType = 'fetch' | 'jsonp'; diff --git a/packages/utils/build.test.json b/packages/utils/build.test.json new file mode 100644 index 000000000..dcdc891e9 --- /dev/null +++ b/packages/utils/build.test.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + "build-plugin-component", + "@alilc/lowcode-test-mate/plugin/index.ts" + ] +} diff --git a/packages/utils/jest.config.js b/packages/utils/jest.config.js new file mode 100644 index 000000000..0e05687d7 --- /dev/null +++ b/packages/utils/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], + collectCoverage: true, + collectCoverageFrom: [ + 'src/**/*.{ts,tsx}', + '!**/node_modules/**', + '!**/vendor/**', + ], +}; diff --git a/packages/utils/package.json b/packages/utils/package.json index bb913041c..18c8b6015 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -9,6 +9,7 @@ "main": "lib/index.js", "module": "es/index.js", "scripts": { + "test": "build-scripts test --config build.test.json", "build": "build-scripts build --skip-demo" }, "dependencies": { diff --git a/packages/utils/src/schema.ts b/packages/utils/src/schema.ts index cc89838d7..dfb487583 100644 --- a/packages/utils/src/schema.ts +++ b/packages/utils/src/schema.ts @@ -26,7 +26,7 @@ export function compatibleLegaoSchema(props: any): any { type: 'JSSlot', title: (props.value.props as any)?.slotTitle, name: (props.value.props as any)?.slotName, - value: props.value.children, + value: compatibleLegaoSchema(props.value.children), params: (props.value.props as any)?.slotParams, }; } else { diff --git a/packages/utils/test/src/__snapshots__/schema.test.ts.snap b/packages/utils/test/src/__snapshots__/schema.test.ts.snap new file mode 100644 index 000000000..e926c89b3 --- /dev/null +++ b/packages/utils/test/src/__snapshots__/schema.test.ts.snap @@ -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", + }, + }, + ], + }, + }, +} +`; diff --git a/packages/utils/test/src/schema.test.ts b/packages/utils/test/src/schema.test.ts new file mode 100644 index 000000000..138dd7a82 --- /dev/null +++ b/packages/utils/test/src/schema.test.ts @@ -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'); + }); +}) \ No newline at end of file