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'); +}