From eba172ce518d208d4bdb3315a88ce3f5dfba6450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A7=E6=AF=85?= Date: Wed, 12 Aug 2020 15:55:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20=E6=90=9E=E5=AE=9A=20Rax?= =?UTF-8?q?=20=E5=87=BA=E7=A0=81=E7=9A=84=E6=97=B6=E5=80=99=E7=9A=84=20pac?= =?UTF-8?q?kage.json=20=E4=B8=AD=E7=9A=84=20dependencies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/code-generator/package.json | 3 +- .../code-generator/src/parser/SchemaParser.ts | 5 ++ .../framework/rax/plugins/packageJSON.ts | 47 ++++++++++++++++- .../code-generator/src/types/intermediate.ts | 2 + packages/code-generator/src/utils/schema.ts | 6 ++- packages/code-generator/src/utils/version.ts | 29 +++++++++++ .../demo1/expected/demo-project/package.json | 4 +- packages/code-generator/test/rax-app.test.ts | 6 ++- .../code-generator/test/utils/version.test.ts | 50 +++++++++++++++++++ 9 files changed, 145 insertions(+), 7 deletions(-) create mode 100644 packages/code-generator/src/utils/version.ts create mode 100644 packages/code-generator/test/utils/version.test.ts diff --git a/packages/code-generator/package.json b/packages/code-generator/package.json index d2c8e62b6..3f4af4a45 100644 --- a/packages/code-generator/package.json +++ b/packages/code-generator/package.json @@ -26,6 +26,7 @@ "change-case": "^3.1.0", "jszip": "^3.5.0", "prettier": "^2.0.2", + "semver": "^7.3.2", "short-uuid": "^3.1.1" }, "devDependencies": { @@ -40,7 +41,7 @@ "compileEnhancements": false, "snapshotDir": "test/fixtures/__snapshots__", "files": [ - "test/*.test.ts" + "test/**/*.test.ts" ], "extensions": [ "ts" diff --git a/packages/code-generator/src/parser/SchemaParser.ts b/packages/code-generator/src/parser/SchemaParser.ts index fa1ece2a0..5424bbc5a 100644 --- a/packages/code-generator/src/parser/SchemaParser.ts +++ b/packages/code-generator/src/parser/SchemaParser.ts @@ -16,6 +16,7 @@ import { IComponentNodeItem, IContainerInfo, IContainerNodeItem, + IDependency, IExternalDependency, IInternalDependency, InternalDependencyType, @@ -138,6 +139,8 @@ class SchemaParser implements ISchemaParser { } }); + const containersDeps = ([] as IDependency[]).concat(...containers.map(c => c.deps || [])); + // 分析路由配置 const routes = containers .filter(container => container.containerType === 'Page') @@ -187,6 +190,8 @@ class SchemaParser implements ISchemaParser { css: schema.css, constants: schema.constants, i18n: schema.i18n, + containersDeps, + utilsDeps, }, }; } diff --git a/packages/code-generator/src/plugins/project/framework/rax/plugins/packageJSON.ts b/packages/code-generator/src/plugins/project/framework/rax/plugins/packageJSON.ts index 3b5b1f08e..86cdace2d 100644 --- a/packages/code-generator/src/plugins/project/framework/rax/plugins/packageJSON.ts +++ b/packages/code-generator/src/plugins/project/framework/rax/plugins/packageJSON.ts @@ -1,3 +1,4 @@ +import { NpmInfo } from '@ali/lowcode-types'; import { COMMON_CHUNK_NAME } from '../../../../../const/generator'; import { @@ -9,6 +10,8 @@ import { IPackageJSON, IProjectInfo, } from '../../../../../types'; +import { isNpmInfo } from '../../../../../utils/schema'; +import { calcCompatibleVersion } from '../../../../../utils/version'; const pluginFactory: BuilderComponentPluginFactory = () => { const plugin: BuilderComponentPlugin = async (pre: ICodeStruct) => { @@ -18,6 +21,8 @@ const pluginFactory: BuilderComponentPluginFactory = () => { const ir = next.ir as IProjectInfo; + const npmDeps = getNpmDependencies(ir); + const packageJson: IPackageJSON = { name: '@ali/rax-app-demo', private: true, @@ -34,8 +39,13 @@ const pluginFactory: BuilderComponentPluginFactory = () => { rax: '^1.1.0', 'rax-app': '^2.0.0', 'rax-document': '^0.1.0', - 'rax-text': '^1.0.0', - 'rax-view': '^1.0.0', + ...npmDeps.reduce( + (acc, npm) => ({ + ...acc, + [npm.package]: npm.version || '*', + }), + {} as Record, + ), }, devDependencies: { 'build-plugin-rax-app': '^5.0.0', @@ -66,3 +76,36 @@ const pluginFactory: BuilderComponentPluginFactory = () => { }; export default pluginFactory; + +function getNpmDependencies(project: IProjectInfo): NpmInfo[] { + const npmDeps: NpmInfo[] = []; + const npmNameToPkgMap = new Map(); + + const allDeps = [...(project.containersDeps || []), ...(project.utilsDeps || [])]; + + allDeps.forEach((dep) => { + if (!isNpmInfo(dep)) { + return; + } + + const existing = npmNameToPkgMap.get(dep.package); + if (!existing) { + npmNameToPkgMap.set(dep.package, dep); + npmDeps.push(dep); + return; + } + + if (existing.version !== dep.version) { + try { + npmNameToPkgMap.set(dep.package, { + ...existing, + version: calcCompatibleVersion(existing.version, dep.version), + }); + } catch (e) { + throw new Error(`Cannot find compatible version for ${dep.package}. Detail: ${e.message}`); + } + } + }); + + return npmDeps; +} diff --git a/packages/code-generator/src/types/intermediate.ts b/packages/code-generator/src/types/intermediate.ts index 54142dfa0..c41f4d7c9 100644 --- a/packages/code-generator/src/types/intermediate.ts +++ b/packages/code-generator/src/types/intermediate.ts @@ -42,6 +42,8 @@ export interface IProjectInfo { css?: string; constants?: Record; i18n?: II18nMap; + containersDeps?: IDependency[]; + utilsDeps?: IDependency[]; } /** diff --git a/packages/code-generator/src/utils/schema.ts b/packages/code-generator/src/utils/schema.ts index 9a19ed069..f741bb111 100644 --- a/packages/code-generator/src/utils/schema.ts +++ b/packages/code-generator/src/utils/schema.ts @@ -1,5 +1,9 @@ -import { ContainerSchema } from '@ali/lowcode-types'; +import { ContainerSchema, NpmInfo } from '@ali/lowcode-types'; export function isContainerSchema(x: any): x is ContainerSchema { return typeof x === 'object' && x && typeof x.componentName === 'string' && typeof x.fileName === 'string'; } + +export function isNpmInfo(x: any): x is NpmInfo { + return typeof x === 'object' && x && typeof x.package === 'string'; +} diff --git a/packages/code-generator/src/utils/version.ts b/packages/code-generator/src/utils/version.ts new file mode 100644 index 000000000..4f5e607e8 --- /dev/null +++ b/packages/code-generator/src/utils/version.ts @@ -0,0 +1,29 @@ +import semver from 'semver'; + +export function calcCompatibleVersion(v1: string | undefined | null, v2: string | undefined | null): string { + if (!v1 && !v2) { + return '*'; + } + + if (!v1 || v1 === '*') { + return v2 || '*'; + } + + if (!v2 || v2 === '*') { + return v1; + } + + if (v1 === v2) { + return v1; + } + + if (!semver.intersects(v1, v2, { loose: true })) { + throw new Error(`no compatible versions for "${v1}" and "${v2}"`); + } + + if (semver.subset(v1, v2, { loose: true })) { + return v1; + } + + return v2; +} diff --git a/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/package.json b/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/package.json index d5595f87b..f2ad5aa15 100644 --- a/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/package.json +++ b/packages/code-generator/test-cases/rax-app/demo1/expected/demo-project/package.json @@ -13,8 +13,8 @@ "rax": "^1.1.0", "rax-app": "^2.0.0", "rax-document": "^0.1.0", - "rax-text": "^1.0.0", - "rax-view": "^1.0.0" + "rax-view": "^1.0.0", + "rax-text": "^1.0.0" }, "devDependencies": { "build-plugin-rax-app": "^5.0.0", diff --git a/packages/code-generator/test/rax-app.test.ts b/packages/code-generator/test/rax-app.test.ts index 115f73fbc..b321e6629 100644 --- a/packages/code-generator/test/rax-app.test.ts +++ b/packages/code-generator/test/rax-app.test.ts @@ -85,7 +85,11 @@ function ensureShellExec( }); if (res.status !== 0) { - throw new Error(`Shell command "${shellCmd} ${args.slice(0,2).join(' ')}..." failed with status: ${res.status} (Full command: "${shellCmd} ${args.join(' ')}" )`); + throw new Error( + `Shell command "${shellCmd} ${args.slice(0, 2).join(' ')}..." failed with status: ${ + res.status + } (Full command: "${shellCmd} ${args.join(' ')}" )`, + ); } return res; diff --git a/packages/code-generator/test/utils/version.test.ts b/packages/code-generator/test/utils/version.test.ts new file mode 100644 index 000000000..16c7b6bb9 --- /dev/null +++ b/packages/code-generator/test/utils/version.test.ts @@ -0,0 +1,50 @@ +import test from 'ava'; +import type { ExecutionContext, Macro } from 'ava'; +import { calcCompatibleVersion } from '../../src/utils/version'; + +const NO_COMPATIBLE_VERSIONS = /no compatible versions/; + +const testCalcCompatibleVersion: Macro = ( + t: ExecutionContext<{}>, + input: [string | null | undefined, string | null | undefined], + expected: string, + error?: { message: RegExp }, +) => { + if (!error) { + t.is(calcCompatibleVersion(input[0], input[1]), expected); + t.is(calcCompatibleVersion(input[1], input[0]), expected); // 应该满足交换律 + } else { + t.throws(() => { + calcCompatibleVersion(input[0], input[1]); + }, error.message); + t.throws(() => { + calcCompatibleVersion(input[1], input[0]); // 应该满足交换律 + }, error.message); + } +}; + +testCalcCompatibleVersion.title = (providedTitle: string | undefined, ...args: any[]): string => { + const [input, expected] = args; + return `calc compatible versions "${input[0]}" & "${input[1]}" should be "${expected}"`; +}; + +test(testCalcCompatibleVersion, ['*', '*'], '*'); +test(testCalcCompatibleVersion, ['1.0.0', '1.0.0'], '1.0.0'); +test(testCalcCompatibleVersion, ['^1.0.0', '^1.0.0'], '^1.0.0'); + +test(testCalcCompatibleVersion, ['*', undefined], '*'); + +test(testCalcCompatibleVersion, [undefined, undefined], '*'); + +test(testCalcCompatibleVersion, ['^1.0.0', undefined], '^1.0.0'); + +test(testCalcCompatibleVersion, ['*', '^1.0.0'], '^1.0.0'); +test(testCalcCompatibleVersion, ['^1.0.0', '^1.0.2'], '^1.0.2'); +test(testCalcCompatibleVersion, ['^1.2.0', '^1.1.2'], '^1.2.0'); +test(testCalcCompatibleVersion, ['^1.0.0', '1.0.2'], '1.0.2'); + +test(testCalcCompatibleVersion, ['^0.2.0', '^0.1.2'], 'error', { message: NO_COMPATIBLE_VERSIONS }); + +test(testCalcCompatibleVersion, ['>0.2.0', '^0.1.2'], 'error', { message: NO_COMPATIBLE_VERSIONS }); + +test(testCalcCompatibleVersion, ['1.0.1', '1.0.2'], 'error', { message: NO_COMPATIBLE_VERSIONS });