From 03221fe6c2b26414ac45fb693524701ec05509dc Mon Sep 17 00:00:00 2001 From: Romain Marcadier-Muller Date: Fri, 27 Dec 2019 11:58:30 +0100 Subject: [PATCH] fix(pacmak): generated dependencies are not consistent with source npm module (#1141) * fix(pacmak): generated dependencies are arbitrary The dependency statements generated in the Java, .NET and Python packages generated by `jsii-pacmak` were not reflecting the dependency statement modeled on the source package. In certain pathological cases, such as Python, the dependency declaration was often more permissive than what the source package allowed, resulting in surprising behavior as well as difficult to troubleshoot problems. This updates several elements involved in this problem: 1. The `jsii` compiler no longer carries version information for transitive dependencies of the assembly in the `dependencyClosure` property, and instead only carries the `targets` configuration for those. 2. The version statements in the `dependencies` section of the `.jsii` file no longer contains the "compile-time resolved" version number, but the actual SemVer expression declared in the package's source. Also, the `targets` object was dropped (only the SemVer range statement remains), as this incurred needless duplication of the target configuration (in `dependencies` and `dependencyClosure`). 3. `jsii-pacmak` renders the declared SemVer range in the relevant form for the target being generated. Fixes #676 Related to #1137 * pr review feedback actions * add missing file Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- packages/@jsii/spec/lib/assembly.ts | 52 ++++----- packages/@jsii/spec/lib/index.ts | 2 +- packages/@scope/jsii-calc-base/package.json | 4 +- .../@scope/jsii-calc-base/test/assembly.jsii | 29 +---- .../@scope/jsii-calc-lib/test/assembly.jsii | 32 +----- packages/jsii-calc/test/assembly.jsii | 85 ++------------ packages/jsii-pacmak/lib/generator.ts | 4 +- .../lib/targets/dotnet/dotnetgenerator.ts | 17 +++ .../lib/targets/dotnet/dotnettyperesolver.ts | 22 ++-- .../lib/targets/dotnet/filegenerator.ts | 9 +- packages/jsii-pacmak/lib/targets/java.ts | 39 ++++--- packages/jsii-pacmak/lib/targets/python.ts | 28 ++--- .../jsii-pacmak/lib/targets/version-utils.ts | 93 +++++++++++++++ .../Internal/DependencyResolution/Anchor.cs | 9 ++ .../java/pom.xml | 2 +- .../.jsii | 29 +---- ...s.CalculatorPackageId.BasePackageId.csproj | 2 +- .../Internal/DependencyResolution/Anchor.cs | 10 ++ .../test/expected.jsii-calc-base/java/pom.xml | 4 +- .../expected.jsii-calc-base/python/setup.py | 2 +- .../.jsii | 32 +----- ...ts.CalculatorPackageId.LibPackageId.csproj | 2 +- .../Internal/DependencyResolution/Anchor.cs | 10 ++ .../test/expected.jsii-calc-lib/java/pom.xml | 4 +- .../expected.jsii-calc-lib/python/setup.py | 2 +- .../.jsii | 85 ++------------ ...azon.JSII.Tests.CalculatorPackageId.csproj | 6 +- .../Internal/DependencyResolution/Anchor.cs | 12 ++ .../test/expected.jsii-calc/java/pom.xml | 8 +- .../test/expected.jsii-calc/python/setup.py | 6 +- .../test/targets/version-utils.test.ts | 77 +++++++++++++ packages/jsii-reflect/lib/dependency.ts | 10 +- packages/jsii/lib/assembler.ts | 108 +++--------------- packages/jsii/lib/project-info.ts | 33 +++--- packages/jsii/lib/validator.ts | 4 +- packages/jsii/package.json | 1 + packages/jsii/test/negatives.test.ts | 6 +- packages/jsii/test/project-info.test.ts | 12 +- packages/jsii/vendor/.gitignore | 1 + packages/jsii/vendor/semver-intersect.d.ts | 13 +++ yarn.lock | 9 +- 41 files changed, 409 insertions(+), 506 deletions(-) create mode 100644 packages/jsii-pacmak/lib/targets/version-utils.ts create mode 100644 packages/jsii-pacmak/test/expected.jsii-calc-base-of-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BaseOfBasePackageId/Amazon/JSII/Tests/CalculatorNamespace/BaseOfBaseNamespace/Internal/DependencyResolution/Anchor.cs create mode 100644 packages/jsii-pacmak/test/expected.jsii-calc-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId/Amazon/JSII/Tests/CalculatorNamespace/BaseNamespace/Internal/DependencyResolution/Anchor.cs create mode 100644 packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/Internal/DependencyResolution/Anchor.cs create mode 100644 packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/Internal/DependencyResolution/Anchor.cs create mode 100644 packages/jsii-pacmak/test/targets/version-utils.test.ts create mode 100644 packages/jsii/vendor/.gitignore create mode 100644 packages/jsii/vendor/semver-intersect.d.ts diff --git a/packages/@jsii/spec/lib/assembly.ts b/packages/@jsii/spec/lib/assembly.ts index d3c21db5fc..779fff7d16 100644 --- a/packages/@jsii/spec/lib/assembly.ts +++ b/packages/@jsii/spec/lib/assembly.ts @@ -3,7 +3,7 @@ export const SPEC_FILE_NAME = '.jsii'; /** * A JSII assembly specification. */ -export interface Assembly extends Documentable { +export interface Assembly extends AssemblyConfiguration, Documentable { /** * The version of the spec schema */ @@ -88,14 +88,6 @@ export interface Assembly extends Documentable { */ license: string; - /** - * A map of target name to configuration, which is used when generating - * packages for various languages. - * - * @default none - */ - targets?: AssemblyTargets; - /** * Arbitrary key-value pairs of metadata, which the maintainer chose to * document with the assembly. These entries do not carry normative @@ -115,18 +107,20 @@ export interface Assembly extends Documentable { /** * Direct dependencies on other assemblies (with semver), the key is the JSII - * assembly name. + * assembly name, and the value is a SemVer expression.. * * @default none */ - dependencies?: { [assembly: string]: PackageVersion }; + dependencies?: { [assembly: string]: string }; /** - * Closure of all dependency assemblies, direct and transitive. + * Target configuration for all the assemblies that are direct or transitive + * dependencies of this assembly. This is needed to generate correct native + * type names for any transitively inherited member, in certain languages. * * @default none */ - dependencyClosure?: { [assembly: string]: PackageVersion }; + dependencyClosure?: { [assembly: string]: AssemblyConfiguration }; /** * List if bundled dependencies (these are not expected to be jsii @@ -151,6 +145,19 @@ export interface Assembly extends Documentable { readme?: { markdown: string }; } +/** + * Shareable configuration of a jsii Assembly. + */ +export interface AssemblyConfiguration { + /** + * A map of target name to configuration, which is used when generating + * packages for various languages. + * + * @default none + */ + targets?: AssemblyTargets; +} + /** * Versions of the JSII Assembly Specification. */ @@ -214,25 +221,6 @@ export interface AssemblyTargets { [language: string]: { [key: string]: any } | undefined; } -/** - * The version of a package. - */ -export interface PackageVersion { - /** - * Version of the package. - * - * @minLength 1 - */ - version: string; - - /** - * Targets for a given assembly. - * - * @default none - */ - targets?: AssemblyTargets; -} - /** * Where in the module source the definition for this API item was found */ diff --git a/packages/@jsii/spec/lib/index.ts b/packages/@jsii/spec/lib/index.ts index 27644b56f8..4b318ab612 100644 --- a/packages/@jsii/spec/lib/index.ts +++ b/packages/@jsii/spec/lib/index.ts @@ -1,4 +1,4 @@ export * from './assembly'; +export * from './configuration'; export * from './name-tree'; export * from './validate-assembly'; -export * from './configuration'; diff --git a/packages/@scope/jsii-calc-base/package.json b/packages/@scope/jsii-calc-base/package.json index 9f2c1dc38b..5ed761195a 100644 --- a/packages/@scope/jsii-calc-base/package.json +++ b/packages/@scope/jsii-calc-base/package.json @@ -29,10 +29,10 @@ "test:update": "npm run build && UPDATE_DIFF=1 npm run test" }, "dependencies": { - "@scope/jsii-calc-base-of-base": "^0.20.11" + "@scope/jsii-calc-base-of-base": "0.20.11" }, "peerDependencies": { - "@scope/jsii-calc-base-of-base": "^0.20.11" + "@scope/jsii-calc-base-of-base": "0.20.11" }, "devDependencies": { "@types/node": "^10.17.13", diff --git a/packages/@scope/jsii-calc-base/test/assembly.jsii b/packages/@scope/jsii-calc-base/test/assembly.jsii index f947cca587..201ae10995 100644 --- a/packages/@scope/jsii-calc-base/test/assembly.jsii +++ b/packages/@scope/jsii-calc-base/test/assembly.jsii @@ -8,29 +8,7 @@ "url": "https://aws.amazon.com" }, "dependencies": { - "@scope/jsii-calc-base-of-base": { - "targets": { - "dotnet": { - "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace", - "packageId": "Amazon.JSII.Tests.CalculatorPackageId.BaseOfBasePackageId" - }, - "java": { - "maven": { - "artifactId": "calculator-base-of-base", - "groupId": "software.amazon.jsii.tests" - }, - "package": "software.amazon.jsii.tests.calculator.baseofbase" - }, - "js": { - "npm": "@scope/jsii-calc-base-of-base" - }, - "python": { - "distName": "scope.jsii-calc-base-of-base", - "module": "scope.jsii_calc_base_of_base" - } - }, - "version": "0.20.11" - } + "@scope/jsii-calc-base-of-base": "0.20.11" }, "dependencyClosure": { "@scope/jsii-calc-base-of-base": { @@ -53,8 +31,7 @@ "distName": "scope.jsii-calc-base-of-base", "module": "scope.jsii_calc_base_of_base" } - }, - "version": "0.20.11" + } } }, "description": "An example direct dependency for jsii-calc.", @@ -174,5 +151,5 @@ } }, "version": "0.20.11", - "fingerprint": "lSQlk5mMPd7fxaZoCdCekSFY4rDOAu3VnLuIcFUXA6o=" + "fingerprint": "vaUHiWCfpSsCfXz18WPVQ3HhCBTPj23pDky4IqmEWxw=" } diff --git a/packages/@scope/jsii-calc-lib/test/assembly.jsii b/packages/@scope/jsii-calc-lib/test/assembly.jsii index aee0bcedeb..2aa69c9263 100644 --- a/packages/@scope/jsii-calc-lib/test/assembly.jsii +++ b/packages/@scope/jsii-calc-lib/test/assembly.jsii @@ -8,29 +8,7 @@ "url": "https://aws.amazon.com" }, "dependencies": { - "@scope/jsii-calc-base": { - "targets": { - "dotnet": { - "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseNamespace", - "packageId": "Amazon.JSII.Tests.CalculatorPackageId.BasePackageId" - }, - "java": { - "maven": { - "artifactId": "calculator-base", - "groupId": "software.amazon.jsii.tests" - }, - "package": "software.amazon.jsii.tests.calculator.base" - }, - "js": { - "npm": "@scope/jsii-calc-base" - }, - "python": { - "distName": "scope.jsii-calc-base", - "module": "scope.jsii_calc_base" - } - }, - "version": "0.20.11" - } + "@scope/jsii-calc-base": "^0.20.11" }, "dependencyClosure": { "@scope/jsii-calc-base": { @@ -53,8 +31,7 @@ "distName": "scope.jsii-calc-base", "module": "scope.jsii_calc_base" } - }, - "version": "0.20.11" + } }, "@scope/jsii-calc-base-of-base": { "targets": { @@ -76,8 +53,7 @@ "distName": "scope.jsii-calc-base-of-base", "module": "scope.jsii_calc_base_of_base" } - }, - "version": "0.20.11" + } } }, "description": "A simple calcuator library built on JSII.", @@ -540,5 +516,5 @@ } }, "version": "0.20.11", - "fingerprint": "WSHD7tywHgFC9jmImvDuy7NGYCDWAAb49jk0ZPzCoFM=" + "fingerprint": "tdRWsMWbPQxej79pQ39gbrrViv/Wl6vfTLY59IJPi4w=" } diff --git a/packages/jsii-calc/test/assembly.jsii b/packages/jsii-calc/test/assembly.jsii index 841f093220..14afe903a7 100644 --- a/packages/jsii-calc/test/assembly.jsii +++ b/packages/jsii-calc/test/assembly.jsii @@ -34,77 +34,9 @@ } ], "dependencies": { - "@scope/jsii-calc-base": { - "targets": { - "dotnet": { - "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseNamespace", - "packageId": "Amazon.JSII.Tests.CalculatorPackageId.BasePackageId" - }, - "java": { - "maven": { - "artifactId": "calculator-base", - "groupId": "software.amazon.jsii.tests" - }, - "package": "software.amazon.jsii.tests.calculator.base" - }, - "js": { - "npm": "@scope/jsii-calc-base" - }, - "python": { - "distName": "scope.jsii-calc-base", - "module": "scope.jsii_calc_base" - } - }, - "version": "0.20.11" - }, - "@scope/jsii-calc-base-of-base": { - "targets": { - "dotnet": { - "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace", - "packageId": "Amazon.JSII.Tests.CalculatorPackageId.BaseOfBasePackageId" - }, - "java": { - "maven": { - "artifactId": "calculator-base-of-base", - "groupId": "software.amazon.jsii.tests" - }, - "package": "software.amazon.jsii.tests.calculator.baseofbase" - }, - "js": { - "npm": "@scope/jsii-calc-base-of-base" - }, - "python": { - "distName": "scope.jsii-calc-base-of-base", - "module": "scope.jsii_calc_base_of_base" - } - }, - "version": "0.20.11" - }, - "@scope/jsii-calc-lib": { - "targets": { - "dotnet": { - "namespace": "Amazon.JSII.Tests.CalculatorNamespace.LibNamespace", - "packageId": "Amazon.JSII.Tests.CalculatorPackageId.LibPackageId", - "versionSuffix": "-devpreview" - }, - "java": { - "maven": { - "artifactId": "calculator-lib", - "groupId": "software.amazon.jsii.tests", - "versionSuffix": ".DEVPREVIEW" - }, - "package": "software.amazon.jsii.tests.calculator.lib" - }, - "js": { - "npm": "@scope/jsii-calc-lib" - }, - "python": { - "distName": "scope.jsii-calc-lib", - "module": "scope.jsii_calc_lib" - } - }, - "version": "0.20.11" - } + "@scope/jsii-calc-base": "^0.20.11", + "@scope/jsii-calc-base-of-base": "^0.20.11", + "@scope/jsii-calc-lib": "^0.20.11" }, "dependencyClosure": { "@scope/jsii-calc-base": { @@ -127,8 +59,7 @@ "distName": "scope.jsii-calc-base", "module": "scope.jsii_calc_base" } - }, - "version": "0.20.11" + } }, "@scope/jsii-calc-base-of-base": { "targets": { @@ -150,8 +81,7 @@ "distName": "scope.jsii-calc-base-of-base", "module": "scope.jsii_calc_base_of_base" } - }, - "version": "0.20.11" + } }, "@scope/jsii-calc-lib": { "targets": { @@ -175,8 +105,7 @@ "distName": "scope.jsii-calc-lib", "module": "scope.jsii_calc_lib" } - }, - "version": "0.20.11" + } } }, "description": "A simple calcuator built on JSII.", @@ -12093,5 +12022,5 @@ } }, "version": "0.20.11", - "fingerprint": "5M7u8+bWxu4amY/gs4Z/K4IpYqJCMGtnQiVoerS4uac=" + "fingerprint": "j7Hsf1Hd/mih3Gr3UR7vYcXeLrbixxffBTzvSokikjM=" } diff --git a/packages/jsii-pacmak/lib/generator.ts b/packages/jsii-pacmak/lib/generator.ts index 14166318e5..bc9aa592b8 100644 --- a/packages/jsii-pacmak/lib/generator.ts +++ b/packages/jsii-pacmak/lib/generator.ts @@ -502,7 +502,7 @@ export abstract class Generator implements IGenerator { * Looks up a jsii module in the dependency tree. * @param name The name of the jsii module to look up */ - protected findModule(name: string): spec.PackageVersion { + protected findModule(name: string): spec.AssemblyConfiguration { // if this is the current module, return it if (this.assembly.name === name) { @@ -515,7 +515,7 @@ export abstract class Generator implements IGenerator { return found; } - throw new Error(`Unable to find module ${name} as a direct or indirect dependency of ${this.assembly.name}`); + throw new Error(`Unable to find module ${name} as a dependency of ${this.assembly.name}`); } protected findType(fqn: string): spec.Type { diff --git a/packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts b/packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts index 9c1a1d111a..346e1157bc 100644 --- a/packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts +++ b/packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts @@ -79,6 +79,9 @@ export class DotNetGenerator extends Generator { await fs.mkdirp(path.join(outdir, packageId)); await fs.copyFile(tarball, path.join(outdir, packageId, tarballFileName)); + // Create an anchor file for the current model + this.generateDependencyAnchorFile(); + // Copying the .jsii file await fs.copyFile(this.jsiiFilePath, path.join(outdir, packageId, spec.SPEC_FILE_NAME)); @@ -86,6 +89,20 @@ export class DotNetGenerator extends Generator { return this.code.save(outdir); } + /** + * Generates the anchor file + */ + protected generateDependencyAnchorFile() { + const namespace = `${this.assembly.targets!.dotnet!.namespace}.Internal.DependencyResolution`; + this.openFileIfNeeded('Anchor', namespace, false, false); + this.code.openBlock('public sealed class Anchor'); + this.code.openBlock('public Anchor()'); + this.typeresolver.namespaceDependencies.forEach(value => this.code.line(`new ${value.namespace}.Internal.DependencyResolution.Anchor();`)); + this.code.closeBlock(); + this.code.closeBlock(); + this.closeFileIfNeeded('Anchor', namespace, false); + } + /** * Not used as we override the save() method */ diff --git a/packages/jsii-pacmak/lib/targets/dotnet/dotnettyperesolver.ts b/packages/jsii-pacmak/lib/targets/dotnet/dotnettyperesolver.ts index 66f3efb26b..f809520466 100644 --- a/packages/jsii-pacmak/lib/targets/dotnet/dotnettyperesolver.ts +++ b/packages/jsii-pacmak/lib/targets/dotnet/dotnettyperesolver.ts @@ -2,7 +2,7 @@ import * as spec from '@jsii/spec'; import { DotNetDependency } from './filegenerator'; import { DotNetNameUtils } from './nameutils'; -type FindModuleCallback = (fqn: string) => spec.Assembly | spec.PackageVersion; +type FindModuleCallback = (fqn: string) => spec.AssemblyConfiguration; type FindTypeCallback = (fqn: string) => spec.Type; export class DotNetTypeResolver { @@ -72,23 +72,21 @@ export class DotNetTypeResolver { */ public resolveNamespacesDependencies(): void { const assmDependencies = this.assembly.dependencies ?? {}; - for (const depName of Object.keys(assmDependencies)) { - const depInfo = assmDependencies[depName]; + const assmConfigurations = this.assembly.dependencyClosure ?? {}; + for (const [depName, version] of Object.entries(assmDependencies)) { + const depInfo = assmConfigurations[depName]; if (!this.namespaceDependencies.has(depName)) { - const dotnetInfo = depInfo.targets!.dotnet; - const namespace = dotnetInfo!.namespace; - const packageId = dotnetInfo!.packageId; - let version = depInfo.version; + const dotnetInfo = depInfo.targets!.dotnet!; + const namespace = dotnetInfo.namespace; + const packageId = dotnetInfo.packageId; const suffix = depInfo.targets!.dotnet!.versionSuffix; - if (suffix) { - // suffix is guaranteed to start with a leading `-` - version = `${depInfo.version}${suffix}`; - } + this.namespaceDependencies.set(depName, new DotNetDependency( namespace, packageId, depName, - version, + // suffix, when present, is guaranteed to start with a leading `-` + suffix ? `${version}${suffix}` : version, this.assembliesCurrentlyBeingCompiled.includes(depName))); } } diff --git a/packages/jsii-pacmak/lib/targets/dotnet/filegenerator.ts b/packages/jsii-pacmak/lib/targets/dotnet/filegenerator.ts index ace5d6cb3b..e372267753 100644 --- a/packages/jsii-pacmak/lib/targets/dotnet/filegenerator.ts +++ b/packages/jsii-pacmak/lib/targets/dotnet/filegenerator.ts @@ -6,15 +6,20 @@ import { DotNetNameUtils } from './nameutils'; import * as logging from '../../logging'; import { nextMajorVersion } from '../../util'; import { TARGET_FRAMEWORK } from '../dotnet'; +import { toNuGetVersionRange } from '../version-utils'; // Represents a dependency in the dependency tree. export class DotNetDependency { + public readonly version: string; + public constructor( public readonly namespace: string, public readonly packageId: string, public readonly fqn: string, - public readonly version: string, - public readonly partOfCompilation: boolean) { + version: string, + public readonly partOfCompilation: boolean + ) { + this.version = toNuGetVersionRange(version); } } diff --git a/packages/jsii-pacmak/lib/targets/java.ts b/packages/jsii-pacmak/lib/targets/java.ts index 64d76d0a5c..3f24d39b5e 100644 --- a/packages/jsii-pacmak/lib/targets/java.ts +++ b/packages/jsii-pacmak/lib/targets/java.ts @@ -2,6 +2,7 @@ import * as clone from 'clone'; import { toPascalCase } from 'codemaker/lib/case-utils'; import * as fs from 'fs-extra'; import * as reflect from 'jsii-reflect'; +import { Rosetta, typeScriptSnippetFromSource, Translation, markDownToJavaDoc } from 'jsii-rosetta'; import * as spec from '@jsii/spec'; import * as path from 'path'; import * as xmlbuilder from 'xmlbuilder'; @@ -9,10 +10,10 @@ import { Generator } from '../generator'; import { PackageInfo, Target, findLocalBuildDirs, TargetOptions } from '../target'; import * as logging from '../logging'; import { shell, Scratch, slugify, setExtend } from '../util'; -import { VERSION, VERSION_DESC } from '../version'; import { TargetBuilder, BuildOptions } from '../builder'; import { JsiiModule } from '../packaging'; -import { Rosetta, typeScriptSnippetFromSource, Translation, markDownToJavaDoc } from 'jsii-rosetta'; +import { VERSION, VERSION_DESC } from '../version'; +import { toMavenVersionRange } from './version-utils'; import { INCOMPLETE_DISCLAIMER_COMPILING, INCOMPLETE_DISCLAIMER_NONCOMPILING } from '.'; // eslint-disable-next-line @typescript-eslint/no-var-requires,@typescript-eslint/no-require-imports @@ -429,7 +430,7 @@ class JavaGenerator extends Generator { * for example, we need to refer to their types when flatting the class hierarchy for * interface proxies. */ - private readonly referencedModules: { [name: string]: spec.PackageVersion } = { }; + private readonly referencedModules: { [name: string]: spec.AssemblyConfiguration } = { }; public constructor(private readonly rosetta: Rosetta) { super({ generateOverloadsForMethodWithOptionals: true }); @@ -777,13 +778,12 @@ class JavaGenerator extends Generator { this.code.closeFile('pom.xml'); /** - * Combines a version number with an optional suffix. If the suffix starts with '-' or '.', it will be - * concatenated as-is to the semantic version number. Otherwise, it'll be appended to the version number with an - * intercalar '-'. - * - * @param version the semantic version number - * @param suffix the suffix, if any. - */ + * Combines a version number with an optional suffix. The suffix, when present, must begin with + * '-' or '.', and will be concatenated as-is to the version number.. + * + * @param version the semantic version number + * @param suffix the suffix, if any. + */ function makeVersion(version: string, suffix?: string): string { if (!suffix) { return version; } if (!suffix.startsWith('-') && !suffix.startsWith('.')) { @@ -794,23 +794,22 @@ class JavaGenerator extends Generator { function mavenDependencies(this: JavaGenerator) { const dependencies = new Array(); - const allDeps = { ...assm.dependencies ?? {}, ...this.referencedModules }; - for (const depName of Object.keys(allDeps)) { - const dep = allDeps[depName]; - if (!dep.targets?.java) { + for (const [depName, version] of Object.entries(this.assembly.dependencies ?? {})) { + const dep = this.assembly.dependencyClosure?.[depName]; + if (!dep?.targets?.java) { throw new Error(`Assembly ${assm.name} depends on ${depName}, which does not declare a java target`); } dependencies.push({ groupId: dep.targets.java.maven.groupId, artifactId: dep.targets.java.maven.artifactId, - version: makeVersion(dep.version, dep.targets.java.maven.versionSuffix), + version: toMavenVersionRange(version, dep.targets.java.maven.versionSuffix), }); } // The JSII java runtime base classes dependencies.push({ groupId: 'software.amazon.jsii', artifactId: 'jsii-runtime', - version: VERSION + version: toMavenVersionRange(`^${VERSION}`) }); // Provides @javax.annotation.* @@ -1833,10 +1832,12 @@ class JavaGenerator extends Generator { } private getNativeName(assm: spec.Assembly, name: string | undefined): string; - private getNativeName(assm: spec.PackageVersion, name: string | undefined, assmName: string): string; - private getNativeName(assm: spec.Assembly | spec.PackageVersion, + private getNativeName(assm: spec.AssemblyConfiguration, name: string | undefined, assmName: string): string; + private getNativeName( + assm: spec.AssemblyConfiguration, name: string | undefined, - assmName: string = (assm as spec.Assembly).name): string { + assmName: string = (assm as spec.Assembly).name + ): string { const javaPackage = assm.targets?.java?.package; if (!javaPackage) { throw new Error(`The module ${assmName} does not have a java.package setting`); } return `${javaPackage}${name ? `.${name}` : ''}`; diff --git a/packages/jsii-pacmak/lib/targets/python.ts b/packages/jsii-pacmak/lib/targets/python.ts index a9b4047c1e..198c4cdacf 100644 --- a/packages/jsii-pacmak/lib/targets/python.ts +++ b/packages/jsii-pacmak/lib/targets/python.ts @@ -1,8 +1,7 @@ -import * as path from 'path'; - import { CodeMaker, toSnakeCase } from 'codemaker'; import * as escapeStringRegexp from 'escape-string-regexp'; import * as reflect from 'jsii-reflect'; +import * as path from 'path'; import * as spec from '@jsii/spec'; import { Stability } from '@jsii/spec'; import { Generator, GeneratorOptions } from '../generator'; @@ -11,6 +10,7 @@ import { md2rst } from '../markdown'; import { Target, TargetOptions } from '../target'; import { shell } from '../util'; import { Translation, Rosetta, typeScriptSnippetFromSource } from 'jsii-rosetta'; +import { toPythonVersionRange } from './version-utils'; import { INCOMPLETE_DISCLAIMER_COMPILING, INCOMPLETE_DISCLAIMER_NONCOMPILING } from '.'; @@ -1093,8 +1093,8 @@ class Module implements PythonType { private emitDependencyImports(code: CodeMaker, _resolver: TypeResolver) { const deps = Array.from( new Set([ - ...Object.values(this.assembly.dependencies ?? {}).map(d => { - return d.targets!.python!.module; + ...Object.keys(this.assembly.dependencies ?? {}).map(d => { + return this.assembly.dependencyClosure![d]!.targets!.python!.module; }), ]) ); @@ -1182,21 +1182,9 @@ class Package { // Compute our list of dependencies const dependencies: string[] = []; - const expectedDeps = this.metadata.dependencies ?? {}; - for (const depName of Object.keys(expectedDeps)) { - const depInfo = expectedDeps[depName]; - // We need to figure out what our version range is. - // Basically, if it starts with Zero we want to restrict things to - // ~=X.Y.Z. If it does not start with zero, then we want to do ~=X.Y,>=X.Y.Z. - const versionParts = depInfo.version.split('.'); - let versionSpecifier: string; - if (versionParts[0] === '0') { - versionSpecifier = `~=${versionParts.slice(0, 3).join('.')}`; - } else { - versionSpecifier = `~=${versionParts.slice(0, 2).join('.')},>=${versionParts.slice(0, 3).join('.')}`; - } - - dependencies.push(`${depInfo.targets!.python!.distName}${versionSpecifier}`); + for (const [depName, version] of Object.entries(this.metadata.dependencies ?? {})) { + const depInfo = this.metadata.dependencyClosure![depName]; + dependencies.push(`${depInfo.targets!.python!.distName}${toPythonVersionRange(version)}`); } code.openFile('README.md'); @@ -1292,7 +1280,7 @@ class Package { } } -type FindModuleCallback = (fqn: string) => spec.Assembly | spec.PackageVersion; +type FindModuleCallback = (fqn: string) => spec.AssemblyConfiguration; type FindTypeCallback = (fqn: string) => spec.Type; interface TypeResolverOpts { diff --git a/packages/jsii-pacmak/lib/targets/version-utils.ts b/packages/jsii-pacmak/lib/targets/version-utils.ts new file mode 100644 index 0000000000..41afa1db02 --- /dev/null +++ b/packages/jsii-pacmak/lib/targets/version-utils.ts @@ -0,0 +1,93 @@ +import { Comparator, Range } from 'semver'; + +/** + * Converts a SemVer range expression to a Maven version range expression. + * + * @param semverRange the SemVer range expression to convert. + * @param suffix the suffix to add to versions in the range. + * + * @see https://cwiki.apache.org/confluence/display/MAVENOLD/Dependency+Mediation+and+Conflict+Resolution + */ +export function toMavenVersionRange(semverRange: string, suffix?: string): string { + return toBracketNotation(semverRange, suffix); +} + +/** + * Converts a SemVer range expression to a NuGet version range expression. + * + * @param semverRange the SemVer range expression to convert. + * + * @see https://docs.microsoft.com/en-us/nuget/concepts/package-versioning#version-ranges-and-wildcards + */ +export function toNuGetVersionRange(semverRange: string): string { + return toBracketNotation(semverRange); +} + +/** + * Converts a SemVer range expression to a Python setuptools compatible version + * constraint expression. + * + * @param semverRange the SemVer range expression to convert. + */ +export function toPythonVersionRange(semverRange: string): string { + const range = new Range(semverRange); + return range.set.map( + set => set.map( + comp => { + switch (comp.operator) { + case '': + case '=': + return `==${comp.semver.raw}`; + default: // >, >=, <, <= are all valid expressions + return `${comp.operator}${comp.semver.raw}`; + } + } + ).join(', ') + ).join(', '); +} + +function toBracketNotation(semverRange: string, suffix?: string): string { + const range = new Range(semverRange); + return range.set.map(set => { + if (set.length === 1) { + switch (set[0].operator || '=') { + // "[version]" => means exactly version + case '=': return `[${addSuffix(set[0].semver.raw)}]`; + // "(version,]" => means greater than version + case '>': return `(${addSuffix(set[0].semver.raw)},]`; + // "[version,]" => means greater than or equal to that version + case '>=': return `[${addSuffix(set[0].semver.raw)},]`; + // "[,version)" => means less than version + case '<': return `[,${addSuffix(set[0].semver.raw)})`; + // "[,version]" => means less than or equal to version + case '<=': return `[,${addSuffix(set[0].semver.raw)}]`; + } + } else if (set.length === 2) { + const nugetRange = toBracketRange(set[0], set[1]); + if (nugetRange) { + return nugetRange; + } + } + throw new Error(`Unsupported SemVer range set: ${set.map(comp => comp.value).join(', ')}`); + }).join(', '); + + function toBracketRange(left: Comparator, right: Comparator): string | undefined { + if (left.operator.startsWith('<') && right.operator.startsWith('>')) { + // Order isn't ideal, swap around.. + [left, right] = [right, left]; + } + + if (!left.operator.startsWith('>') || !right.operator.startsWith('<')) { + // We only support ranges defined like "> (or >=) left, < (or <=) right" + return undefined; + } + + const leftBrace = left.operator.endsWith('=') ? '[' : '('; + const rightBrace = right.operator.endsWith('=') ? ']' : ')'; + return `${leftBrace}${addSuffix(left.semver.raw)},${addSuffix(right.semver.raw)}${rightBrace}`; + } + + function addSuffix(str: string) { + return suffix ? `${str}${suffix}` : str; + } +} diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-base-of-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BaseOfBasePackageId/Amazon/JSII/Tests/CalculatorNamespace/BaseOfBaseNamespace/Internal/DependencyResolution/Anchor.cs b/packages/jsii-pacmak/test/expected.jsii-calc-base-of-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BaseOfBasePackageId/Amazon/JSII/Tests/CalculatorNamespace/BaseOfBaseNamespace/Internal/DependencyResolution/Anchor.cs new file mode 100644 index 0000000000..5cd3ec3cb0 --- /dev/null +++ b/packages/jsii-pacmak/test/expected.jsii-calc-base-of-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BaseOfBasePackageId/Amazon/JSII/Tests/CalculatorNamespace/BaseOfBaseNamespace/Internal/DependencyResolution/Anchor.cs @@ -0,0 +1,9 @@ +namespace Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace.Internal.DependencyResolution +{ + public sealed class Anchor + { + public Anchor() + { + } + } +} diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-base-of-base/java/pom.xml b/packages/jsii-pacmak/test/expected.jsii-calc-base-of-base/java/pom.xml index 8ec3c5795a..ae1ff0a024 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc-base-of-base/java/pom.xml +++ b/packages/jsii-pacmak/test/expected.jsii-calc-base-of-base/java/pom.xml @@ -36,7 +36,7 @@ software.amazon.jsii jsii-runtime - 0.20.11 + [0.20.11,0.21.0) javax.annotation diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId/.jsii b/packages/jsii-pacmak/test/expected.jsii-calc-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId/.jsii index f947cca587..201ae10995 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId/.jsii +++ b/packages/jsii-pacmak/test/expected.jsii-calc-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId/.jsii @@ -8,29 +8,7 @@ "url": "https://aws.amazon.com" }, "dependencies": { - "@scope/jsii-calc-base-of-base": { - "targets": { - "dotnet": { - "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace", - "packageId": "Amazon.JSII.Tests.CalculatorPackageId.BaseOfBasePackageId" - }, - "java": { - "maven": { - "artifactId": "calculator-base-of-base", - "groupId": "software.amazon.jsii.tests" - }, - "package": "software.amazon.jsii.tests.calculator.baseofbase" - }, - "js": { - "npm": "@scope/jsii-calc-base-of-base" - }, - "python": { - "distName": "scope.jsii-calc-base-of-base", - "module": "scope.jsii_calc_base_of_base" - } - }, - "version": "0.20.11" - } + "@scope/jsii-calc-base-of-base": "0.20.11" }, "dependencyClosure": { "@scope/jsii-calc-base-of-base": { @@ -53,8 +31,7 @@ "distName": "scope.jsii-calc-base-of-base", "module": "scope.jsii_calc_base_of_base" } - }, - "version": "0.20.11" + } } }, "description": "An example direct dependency for jsii-calc.", @@ -174,5 +151,5 @@ } }, "version": "0.20.11", - "fingerprint": "lSQlk5mMPd7fxaZoCdCekSFY4rDOAu3VnLuIcFUXA6o=" + "fingerprint": "vaUHiWCfpSsCfXz18WPVQ3HhCBTPj23pDky4IqmEWxw=" } diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId.csproj b/packages/jsii-pacmak/test/expected.jsii-calc-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId.csproj index eca51885aa..4e409860ad 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId.csproj +++ b/packages/jsii-pacmak/test/expected.jsii-calc-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId.csproj @@ -25,7 +25,7 @@ - + 0612,0618 diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId/Amazon/JSII/Tests/CalculatorNamespace/BaseNamespace/Internal/DependencyResolution/Anchor.cs b/packages/jsii-pacmak/test/expected.jsii-calc-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId/Amazon/JSII/Tests/CalculatorNamespace/BaseNamespace/Internal/DependencyResolution/Anchor.cs new file mode 100644 index 0000000000..4a5728b15d --- /dev/null +++ b/packages/jsii-pacmak/test/expected.jsii-calc-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId/Amazon/JSII/Tests/CalculatorNamespace/BaseNamespace/Internal/DependencyResolution/Anchor.cs @@ -0,0 +1,10 @@ +namespace Amazon.JSII.Tests.CalculatorNamespace.BaseNamespace.Internal.DependencyResolution +{ + public sealed class Anchor + { + public Anchor() + { + new Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace.Internal.DependencyResolution.Anchor(); + } + } +} diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-base/java/pom.xml b/packages/jsii-pacmak/test/expected.jsii-calc-base/java/pom.xml index 53f9bfcda7..144d30b73d 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc-base/java/pom.xml +++ b/packages/jsii-pacmak/test/expected.jsii-calc-base/java/pom.xml @@ -36,12 +36,12 @@ software.amazon.jsii.tests calculator-base-of-base - 0.20.11 + [0.20.11] software.amazon.jsii jsii-runtime - 0.20.11 + [0.20.11,0.21.0) javax.annotation diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-base/python/setup.py b/packages/jsii-pacmak/test/expected.jsii-calc-base/python/setup.py index 960636132a..d76b93338a 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc-base/python/setup.py +++ b/packages/jsii-pacmak/test/expected.jsii-calc-base/python/setup.py @@ -32,7 +32,7 @@ "install_requires": [ "jsii~=0.20.11", "publication>=0.0.3", - "scope.jsii-calc-base-of-base~=0.20.11" + "scope.jsii-calc-base-of-base==0.20.11" ], "classifiers": [ "Intended Audience :: Developers", diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/.jsii b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/.jsii index aee0bcedeb..2aa69c9263 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/.jsii +++ b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/.jsii @@ -8,29 +8,7 @@ "url": "https://aws.amazon.com" }, "dependencies": { - "@scope/jsii-calc-base": { - "targets": { - "dotnet": { - "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseNamespace", - "packageId": "Amazon.JSII.Tests.CalculatorPackageId.BasePackageId" - }, - "java": { - "maven": { - "artifactId": "calculator-base", - "groupId": "software.amazon.jsii.tests" - }, - "package": "software.amazon.jsii.tests.calculator.base" - }, - "js": { - "npm": "@scope/jsii-calc-base" - }, - "python": { - "distName": "scope.jsii-calc-base", - "module": "scope.jsii_calc_base" - } - }, - "version": "0.20.11" - } + "@scope/jsii-calc-base": "^0.20.11" }, "dependencyClosure": { "@scope/jsii-calc-base": { @@ -53,8 +31,7 @@ "distName": "scope.jsii-calc-base", "module": "scope.jsii_calc_base" } - }, - "version": "0.20.11" + } }, "@scope/jsii-calc-base-of-base": { "targets": { @@ -76,8 +53,7 @@ "distName": "scope.jsii-calc-base-of-base", "module": "scope.jsii_calc_base_of_base" } - }, - "version": "0.20.11" + } } }, "description": "A simple calcuator library built on JSII.", @@ -540,5 +516,5 @@ } }, "version": "0.20.11", - "fingerprint": "WSHD7tywHgFC9jmImvDuy7NGYCDWAAb49jk0ZPzCoFM=" + "fingerprint": "tdRWsMWbPQxej79pQ39gbrrViv/Wl6vfTLY59IJPi4w=" } diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId.csproj b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId.csproj index 39ad0bcf10..2275a0ab28 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId.csproj +++ b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId.csproj @@ -25,7 +25,7 @@ - + 0612,0618 diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/Internal/DependencyResolution/Anchor.cs b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/Internal/DependencyResolution/Anchor.cs new file mode 100644 index 0000000000..19be6786f5 --- /dev/null +++ b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/Internal/DependencyResolution/Anchor.cs @@ -0,0 +1,10 @@ +namespace Amazon.JSII.Tests.CalculatorNamespace.LibNamespace.Internal.DependencyResolution +{ + public sealed class Anchor + { + public Anchor() + { + new Amazon.JSII.Tests.CalculatorNamespace.BaseNamespace.Internal.DependencyResolution.Anchor(); + } + } +} diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-lib/java/pom.xml b/packages/jsii-pacmak/test/expected.jsii-calc-lib/java/pom.xml index 2a7c3608d6..dd403ce6a8 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc-lib/java/pom.xml +++ b/packages/jsii-pacmak/test/expected.jsii-calc-lib/java/pom.xml @@ -36,12 +36,12 @@ software.amazon.jsii.tests calculator-base - 0.20.11 + [0.20.11,0.21.0) software.amazon.jsii jsii-runtime - 0.20.11 + [0.20.11,0.21.0) javax.annotation diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-lib/python/setup.py b/packages/jsii-pacmak/test/expected.jsii-calc-lib/python/setup.py index fad4ca0394..3588e20486 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc-lib/python/setup.py +++ b/packages/jsii-pacmak/test/expected.jsii-calc-lib/python/setup.py @@ -32,7 +32,7 @@ "install_requires": [ "jsii~=0.20.11", "publication>=0.0.3", - "scope.jsii-calc-base~=0.20.11" + "scope.jsii-calc-base>=0.20.11, <0.21.0" ], "classifiers": [ "Intended Audience :: Developers", diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii index 841f093220..14afe903a7 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii +++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii @@ -34,77 +34,9 @@ } ], "dependencies": { - "@scope/jsii-calc-base": { - "targets": { - "dotnet": { - "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseNamespace", - "packageId": "Amazon.JSII.Tests.CalculatorPackageId.BasePackageId" - }, - "java": { - "maven": { - "artifactId": "calculator-base", - "groupId": "software.amazon.jsii.tests" - }, - "package": "software.amazon.jsii.tests.calculator.base" - }, - "js": { - "npm": "@scope/jsii-calc-base" - }, - "python": { - "distName": "scope.jsii-calc-base", - "module": "scope.jsii_calc_base" - } - }, - "version": "0.20.11" - }, - "@scope/jsii-calc-base-of-base": { - "targets": { - "dotnet": { - "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace", - "packageId": "Amazon.JSII.Tests.CalculatorPackageId.BaseOfBasePackageId" - }, - "java": { - "maven": { - "artifactId": "calculator-base-of-base", - "groupId": "software.amazon.jsii.tests" - }, - "package": "software.amazon.jsii.tests.calculator.baseofbase" - }, - "js": { - "npm": "@scope/jsii-calc-base-of-base" - }, - "python": { - "distName": "scope.jsii-calc-base-of-base", - "module": "scope.jsii_calc_base_of_base" - } - }, - "version": "0.20.11" - }, - "@scope/jsii-calc-lib": { - "targets": { - "dotnet": { - "namespace": "Amazon.JSII.Tests.CalculatorNamespace.LibNamespace", - "packageId": "Amazon.JSII.Tests.CalculatorPackageId.LibPackageId", - "versionSuffix": "-devpreview" - }, - "java": { - "maven": { - "artifactId": "calculator-lib", - "groupId": "software.amazon.jsii.tests", - "versionSuffix": ".DEVPREVIEW" - }, - "package": "software.amazon.jsii.tests.calculator.lib" - }, - "js": { - "npm": "@scope/jsii-calc-lib" - }, - "python": { - "distName": "scope.jsii-calc-lib", - "module": "scope.jsii_calc_lib" - } - }, - "version": "0.20.11" - } + "@scope/jsii-calc-base": "^0.20.11", + "@scope/jsii-calc-base-of-base": "^0.20.11", + "@scope/jsii-calc-lib": "^0.20.11" }, "dependencyClosure": { "@scope/jsii-calc-base": { @@ -127,8 +59,7 @@ "distName": "scope.jsii-calc-base", "module": "scope.jsii_calc_base" } - }, - "version": "0.20.11" + } }, "@scope/jsii-calc-base-of-base": { "targets": { @@ -150,8 +81,7 @@ "distName": "scope.jsii-calc-base-of-base", "module": "scope.jsii_calc_base_of_base" } - }, - "version": "0.20.11" + } }, "@scope/jsii-calc-lib": { "targets": { @@ -175,8 +105,7 @@ "distName": "scope.jsii-calc-lib", "module": "scope.jsii_calc_lib" } - }, - "version": "0.20.11" + } } }, "description": "A simple calcuator built on JSII.", @@ -12093,5 +12022,5 @@ } }, "version": "0.20.11", - "fingerprint": "5M7u8+bWxu4amY/gs4Z/K4IpYqJCMGtnQiVoerS4uac=" + "fingerprint": "j7Hsf1Hd/mih3Gr3UR7vYcXeLrbixxffBTzvSokikjM=" } diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon.JSII.Tests.CalculatorPackageId.csproj b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon.JSII.Tests.CalculatorPackageId.csproj index 1a901072ec..50cc042d87 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon.JSII.Tests.CalculatorPackageId.csproj +++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon.JSII.Tests.CalculatorPackageId.csproj @@ -27,9 +27,9 @@ - - - + + + 0612,0618 diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/Internal/DependencyResolution/Anchor.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/Internal/DependencyResolution/Anchor.cs new file mode 100644 index 0000000000..b446d0e4ed --- /dev/null +++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/Internal/DependencyResolution/Anchor.cs @@ -0,0 +1,12 @@ +namespace Amazon.JSII.Tests.CalculatorNamespace.Internal.DependencyResolution +{ + public sealed class Anchor + { + public Anchor() + { + new Amazon.JSII.Tests.CalculatorNamespace.BaseNamespace.Internal.DependencyResolution.Anchor(); + new Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace.Internal.DependencyResolution.Anchor(); + new Amazon.JSII.Tests.CalculatorNamespace.LibNamespace.Internal.DependencyResolution.Anchor(); + } + } +} diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/java/pom.xml b/packages/jsii-pacmak/test/expected.jsii-calc/java/pom.xml index d91eb3248b..e7c101d5c8 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc/java/pom.xml +++ b/packages/jsii-pacmak/test/expected.jsii-calc/java/pom.xml @@ -57,22 +57,22 @@ software.amazon.jsii.tests calculator-base - 0.20.11 + [0.20.11,0.21.0) software.amazon.jsii.tests calculator-base-of-base - 0.20.11 + [0.20.11,0.21.0) software.amazon.jsii.tests calculator-lib - 0.20.11.DEVPREVIEW + [0.20.11.DEVPREVIEW,0.21.0.DEVPREVIEW) software.amazon.jsii jsii-runtime - 0.20.11 + [0.20.11,0.21.0) javax.annotation diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/python/setup.py b/packages/jsii-pacmak/test/expected.jsii-calc/python/setup.py index 4b0431ce97..eb7056b80a 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc/python/setup.py +++ b/packages/jsii-pacmak/test/expected.jsii-calc/python/setup.py @@ -32,9 +32,9 @@ "install_requires": [ "jsii~=0.20.11", "publication>=0.0.3", - "scope.jsii-calc-base~=0.20.11", - "scope.jsii-calc-base-of-base~=0.20.11", - "scope.jsii-calc-lib~=0.20.11" + "scope.jsii-calc-base>=0.20.11, <0.21.0", + "scope.jsii-calc-base-of-base>=0.20.11, <0.21.0", + "scope.jsii-calc-lib>=0.20.11, <0.21.0" ], "classifiers": [ "Intended Audience :: Developers", diff --git a/packages/jsii-pacmak/test/targets/version-utils.test.ts b/packages/jsii-pacmak/test/targets/version-utils.test.ts new file mode 100644 index 0000000000..9b227c7303 --- /dev/null +++ b/packages/jsii-pacmak/test/targets/version-utils.test.ts @@ -0,0 +1,77 @@ +import { toMavenVersionRange, toNuGetVersionRange, toPythonVersionRange } from '../../lib/targets/version-utils'; + +const examples: Record = { + // Regular versions, "classic" ranges + '1.2.3': { + maven: '[1.2.3]', + nuget: '[1.2.3]', + python: '==1.2.3', + }, + '~1.2.3': { + maven: '[1.2.3,1.3.0)', + nuget: '[1.2.3,1.3.0)', + python: '>=1.2.3, <1.3.0', + }, + '^1.2.3': { + maven: '[1.2.3,2.0.0)', + nuget: '[1.2.3,2.0.0)', + python: '>=1.2.3, <2.0.0', + }, + + // Pre-1.0 versions, "classic" ranges + '0.1.2': { + maven: '[0.1.2]', + nuget: '[0.1.2]', + python: '==0.1.2', + }, + '~0.1.2': { + maven: '[0.1.2,0.2.0)', + nuget: '[0.1.2,0.2.0)', + python: '>=0.1.2, <0.2.0', + }, + '^0.1.2': { + maven: '[0.1.2,0.2.0)', + nuget: '[0.1.2,0.2.0)', + python: '>=0.1.2, <0.2.0', + }, + + // Less usual ranges + '>0.1.2': { + maven: '(0.1.2,]', + nuget: '(0.1.2,]', + python: '>0.1.2', + }, + '>=0.1.2': { + maven: '[0.1.2,]', + nuget: '[0.1.2,]', + python: '>=0.1.2', + }, + '<0.1.2': { + maven: '[,0.1.2)', + nuget: '[,0.1.2)', + python: '<0.1.2', + }, + '<=0.1.2': { + maven: '[,0.1.2]', + nuget: '[,0.1.2]', + python: '<=0.1.2', + }, +}; + +describe(toMavenVersionRange, () => { + for (const [semver, { maven }] of Object.entries(examples)) { + test(`${semver} translates to ${maven}`, () => expect(toMavenVersionRange(semver)).toEqual(maven)); + } +}); + +describe(toNuGetVersionRange, () => { + for (const [semver, { nuget }] of Object.entries(examples)) { + test(`${semver} translates to ${nuget}`, () => expect(toNuGetVersionRange(semver)).toEqual(nuget)); + } +}); + +describe(toPythonVersionRange, () => { + for (const [semver, { python }] of Object.entries(examples)) { + test(`${semver} translates to ${python}`, () => expect(toPythonVersionRange(semver)).toEqual(python)); + } +}); diff --git a/packages/jsii-reflect/lib/dependency.ts b/packages/jsii-reflect/lib/dependency.ts index 6a6a5c66a1..769e36fc43 100644 --- a/packages/jsii-reflect/lib/dependency.ts +++ b/packages/jsii-reflect/lib/dependency.ts @@ -1,21 +1,13 @@ -import * as jsii from '@jsii/spec'; import { TypeSystem } from './type-system'; export class Dependency { public constructor( public readonly system: TypeSystem, private readonly name: string, - public readonly spec: jsii.PackageVersion + public readonly version: string ) { } public get assembly() { return this.system.findAssembly(this.name); } - - /** - * Version of the package. - */ - public get version() { - return this.spec.version; - } } diff --git a/packages/jsii/lib/assembler.ts b/packages/jsii/lib/assembler.ts index 67facf3344..eeec9162bd 100644 --- a/packages/jsii/lib/assembler.ts +++ b/packages/jsii/lib/assembler.ts @@ -6,7 +6,6 @@ import * as fs from 'fs-extra'; import * as spec from '@jsii/spec'; import * as log4js from 'log4js'; import * as path from 'path'; -import * as semver from 'semver'; import * as ts from 'typescript'; import { JSII_DIAGNOSTICS_CODE } from './compiler'; import { getReferencedDocParams, parseSymbolDocumentation } from './docs'; @@ -115,8 +114,8 @@ export class Assembler implements Emitter { author: this.projectInfo.author, contributors: this.projectInfo.contributors && [...this.projectInfo.contributors], repository: this.projectInfo.repository, - dependencies: this._toDependencies(this.projectInfo.dependencies), - dependencyClosure: this._buildDependencyClosure(this.projectInfo.dependencies), + dependencies: noEmptyDict({ ...this.projectInfo.dependencies, ...this.projectInfo.peerDependencies }), + dependencyClosure: noEmptyDict(toDependencyClosure(this.projectInfo.dependencyClosure)), bundled: this.projectInfo.bundleDependencies, types: this._types, targets: this.projectInfo.targets, @@ -234,14 +233,13 @@ export class Assembler implements Emitter { if (assm === this.projectInfo.name) { type = this._types[ref]; } else { - const assembly = this.projectInfo.transitiveDependencies.find(dep => dep.name === assm); + const assembly = this.projectInfo.dependencyClosure.find(dep => dep.name === assm); type = assembly?.types?.[ref]; // since we are exposing a type of this assembly in this module's public API, // we expect it to appear as a peer dependency instead of a normal dependency. if (assembly) { - const asPeerDependency = this.projectInfo.peerDependencies.find(d => d.name === assembly.name); - if (!asPeerDependency) { + if (!(assembly.name in this.projectInfo.peerDependencies)) { this._diagnostic(referencingNode, ts.DiagnosticCategory.Warning, `The type '${ref}' is exposed in the public API of this module. ` + `Therefore, the module '${assembly.name}' must also be defined under "peerDependencies". ` + @@ -1341,71 +1339,6 @@ export class Assembler implements Emitter { } } } - - private _toDependencies(assemblies: readonly spec.Assembly[]): { [name: string]: spec.PackageVersion } | undefined { - const ret: { [name: string]: spec.PackageVersion } = {}; - - for (const a of assemblies) { - Object.assign(ret, assemblyToPackageVersionMap(a)); - } - - return noEmptyDict(ret); - } - - private _buildDependencyClosure(assemblies: readonly spec.Assembly[]): { [name: string]: spec.PackageVersion } | undefined { - // Merge the dependency closures of all dependencies and add the direct dependencies. - // There should not be version conflicts between them but we guard against it anyway. - - // Get an array of dependency maps - const dependencyBags = flatten(assemblies.map(a => [assemblyToPackageVersionMap(a), a.dependencies ?? {}])); - - const warned = new Set(); - const result: { [name: string]: spec.PackageVersion } = {}; - for (const bag of dependencyBags) { - for (const [name, packV] of Object.entries(bag)) { - maybeRecord.call(this, name, packV); - } - } - - return noEmptyDict(result); - - function maybeRecord(this: Assembler, name: string, pack: spec.PackageVersion) { - let recordThisDependency = true; - - if (name in result) { - // Two dependencies on the same package, find the right version to use - const highestVersion = mostConstrainedVersion(result[name].version, pack.version); - - if (highestVersion === undefined) { - warnAboutVersionConflict.call(this, name, result[name].version, pack.version); - } - - recordThisDependency = pack.version === highestVersion; - } - - if (recordThisDependency) { - result[name] = { - version: pack.version, - targets: pack.targets, - }; - } - } - - function warnAboutVersionConflict(this: Assembler, name: string, v1: string, v2: string) { - if (warned.has(name)) { return; } - this._diagnostic(null, ts.DiagnosticCategory.Error, `Conflicting dependencies on incompatible versions for package '${name}': ${v1} and ${v2}`); - warned.add(name); - } - } -} - -function assemblyToPackageVersionMap(a: spec.Assembly): {[key: string]: spec.PackageVersion} { - return { - [a.name]: { - version: a.version, - targets: a.targets - } - }; } function _fingerprint(assembly: spec.Assembly): spec.Assembly { @@ -1624,35 +1557,20 @@ function* intersect(xs: Set, ys: Set) { } } -/** - * Return the most constrained version given two versions - * - * Returns the highest version. Return undefined if the values are not pairwise - * comparable. - */ -function mostConstrainedVersion(version1: string, version2: string): string | undefined { - if (semver.satisfies(version1, `^${version2}`)) { - // If v1 satisifies v2, then either: - // - v2 also satisfies v1, in which case it doesn't matter which we return - // - v2 does not satisfy v1, in which it must be the case that v1 is higher - return version1; - } - - // Reverse logic - if (semver.satisfies(version2, `^${version1}`)) { return version2; } - - return undefined; -} - -function flatten(xs: T[][]): T[] { - return Array.prototype.concat.call([], ...xs); -} - function noEmptyDict(xs: {[key: string]: T}): {[key: string]: T} | undefined { if (Object.keys(xs).length === 0) { return undefined; } return xs; } +function toDependencyClosure(assemblies: readonly spec.Assembly[]): { [name: string]: spec.AssemblyConfiguration } { + const result: { [name: string]: spec.AssemblyTargets } = {}; + for (const assembly of assemblies) { + if (!assembly.targets) { continue; } + result[assembly.name] = { targets: assembly.targets }; + } + return result; +} + /** * Check whether this type is the intrinsic TypeScript "error type" * diff --git a/packages/jsii/lib/project-info.ts b/packages/jsii/lib/project-info.ts index 864457aab6..1fb1b894d6 100644 --- a/packages/jsii/lib/project-info.ts +++ b/packages/jsii/lib/project-info.ts @@ -3,6 +3,7 @@ import * as spec from '@jsii/spec'; import * as log4js from 'log4js'; import * as path from 'path'; import * as semver from 'semver'; +import { intersect } from 'semver-intersect'; import { parsePerson, parseRepository } from './utils'; // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports @@ -35,9 +36,9 @@ export interface ProjectInfo { readonly main: string; readonly types: string; - readonly dependencies: readonly spec.Assembly[]; - readonly peerDependencies: readonly spec.Assembly[]; - readonly transitiveDependencies: readonly spec.Assembly[]; + readonly dependencies: { readonly [name: string]: string }; + readonly peerDependencies: { readonly [name: string]: string }; + readonly dependencyClosure: readonly spec.Assembly[]; readonly bundleDependencies?: { readonly [name: string]: string }; readonly targets: spec.AssemblyTargets; readonly metadata?: { [key: string]: any }; @@ -107,7 +108,7 @@ export async function loadProjectInfo(projectRoot: string, { fixPeerDependencies const peerDependencies = await _loadDependencies(pkg.peerDependencies, projectRoot, transitiveAssemblies); - const transitiveDependencies = Object.keys(transitiveAssemblies).map(name => transitiveAssemblies[name]); + const transitiveDependencies = Object.values(transitiveAssemblies); return { projectRoot, @@ -127,7 +128,7 @@ export async function loadProjectInfo(projectRoot: string, { fixPeerDependencies dependencies, peerDependencies, - transitiveDependencies, + dependencyClosure: transitiveDependencies, bundleDependencies, targets: { ..._required(pkg.jsii, 'The "package.json" file must specify the "jsii" attribute').targets, @@ -158,12 +159,14 @@ function _guessRepositoryType(url: string): string { throw new Error(`The "package.json" file must specify the "repository.type" attribute (could not guess from ${url})`); } -async function _loadDependencies(dependencies: { [name: string]: string | spec.PackageVersion } | undefined, +async function _loadDependencies( + dependencies: { [name: string]: string } | undefined, searchPath: string, transitiveAssemblies: { [name: string]: spec.Assembly }, - bundled = new Set()): Promise { - if (!dependencies) { return []; } - const assemblies = new Array(); + bundled = new Set() +): Promise<{ [name: string]: string }> { + if (!dependencies) { return {}; } + const packageVersions: { [name: string]: string } = {}; for (const name of Object.keys(dependencies)) { if (bundled.has(name)) { continue; } const { version: versionString, localPackage } = _resolveVersion(dependencies[name], searchPath); @@ -178,7 +181,9 @@ async function _loadDependencies(dependencies: { [name: string]: string | spec.P if (!version.intersects(new semver.Range(assm.version))) { throw new Error(`Declared dependency on version ${versionString} of ${name}, but version ${assm.version} was found`); } - assemblies.push(assm); + packageVersions[assm.name] = packageVersions[assm.name] != null + ? intersect(versionString!, packageVersions[assm.name]) + : versionString!; transitiveAssemblies[assm.name] = assm; const pkgDir = path.dirname(pkg); if (assm.dependencies) { @@ -186,7 +191,7 @@ async function _loadDependencies(dependencies: { [name: string]: string | spec.P await _loadDependencies(assm.dependencies, pkgDir, transitiveAssemblies); } } - return assemblies; + return packageVersions; } const ASSEMBLY_CACHE = new Map(); @@ -278,11 +283,7 @@ function _validateStability(stability: string | undefined, deprecated: string | return stability as spec.Stability; } -function _resolveVersion(dep: spec.PackageVersion | string | undefined, searchPath: string): { version: string | undefined, localPackage?: string } { - if (typeof dep !== 'string') { - return { version: dep?.version }; - } - +function _resolveVersion(dep: string, searchPath: string): { version: string | undefined, localPackage?: string } { const matches = /^file:(.+)$/.exec(dep); if (!matches) { return { version: dep }; diff --git a/packages/jsii/lib/validator.ts b/packages/jsii/lib/validator.ts index 7eb2dc29d8..f29c25b386 100644 --- a/packages/jsii/lib/validator.ts +++ b/packages/jsii/lib/validator.ts @@ -130,7 +130,7 @@ function _defaultValidations(): ValidationFunction[] { } continue; } - const foreignAssm = validator.projectInfo.transitiveDependencies.find(dep => dep.name === assm); + const foreignAssm = validator.projectInfo.dependencyClosure.find(dep => dep.name === assm); if (!foreignAssm) { diagnostic(ts.DiagnosticCategory.Error, `Type reference is rooted in unknown module: ${assm}`); @@ -362,7 +362,7 @@ function _dereference(typeRef: string | spec.NamedTypeReference, assembly: spec. if (assembly.name === assm) { return assembly.types?.[typeRef]; } - const foreignAssm = validator.projectInfo.transitiveDependencies.find(dep => dep.name === assm); + const foreignAssm = validator.projectInfo.dependencyClosure.find(dep => dep.name === assm); return foreignAssm?.types?.[typeRef]; } diff --git a/packages/jsii/package.json b/packages/jsii/package.json index eac0430c64..5cd9aaa34d 100644 --- a/packages/jsii/package.json +++ b/packages/jsii/package.json @@ -40,6 +40,7 @@ "fs-extra": "^8.1.0", "log4js": "^6.1.0", "semver": "^7.1.1", + "semver-intersect": "^1.4.0", "sort-json": "^2.0.0", "spdx-license-list": "^6.1.0", "typescript": "~3.7.4", diff --git a/packages/jsii/test/negatives.test.ts b/packages/jsii/test/negatives.test.ts index 45b70c46f1..9a33f8bbfe 100644 --- a/packages/jsii/test/negatives.test.ts +++ b/packages/jsii/test/negatives.test.ts @@ -74,9 +74,9 @@ function _makeProjectInfo(types: string): ProjectInfo { license: 'Apache-2.0', author: { name: 'John Doe', roles: ['author'] }, repository: { type: 'git', url: 'https://github.com/aws/jsii.git' }, - dependencies: [], - peerDependencies: [], - transitiveDependencies: [], + dependencies: {}, + peerDependencies: {}, + dependencyClosure: [], bundleDependencies: {}, targets: {}, excludeTypescript: [], diff --git a/packages/jsii/test/project-info.test.ts b/packages/jsii/test/project-info.test.ts index 45a0787684..44bbe2d8dd 100644 --- a/packages/jsii/test/project-info.test.ts +++ b/packages/jsii/test/project-info.test.ts @@ -18,8 +18,8 @@ const BASE_PROJECT = { jsii: { targets: { foo: { bar: 'baz' } } }, - dependencies: { 'jsii-test-dep': '^1.2.3' }, - peerDependencies: { 'jsii-test-dep': '^1.2.3' } + dependencies: { 'jsii-test-dep': '^1.2.3' } as { [name: string]: string }, + peerDependencies: { 'jsii-test-dep': '^1.2.3' } as { [name: string]: string } }; describe('loadProjectInfo', () => { @@ -36,8 +36,8 @@ describe('loadProjectInfo', () => { expect(info.repository?.type).toBe('git'); expect(info.repository?.url).toBe(BASE_PROJECT.repository.url); expect(info.targets).toEqual({ ...BASE_PROJECT.jsii.targets, js: { npm: BASE_PROJECT.name } }); - expect(info.dependencies).toEqual([TEST_DEP_ASSEMBLY]); - expect(info.transitiveDependencies).toEqual([TEST_DEP_ASSEMBLY, TEST_DEP_DEP_ASSEMBLY]); + expect(info.dependencies).toEqual({ [TEST_DEP_ASSEMBLY.name]: BASE_PROJECT.dependencies[TEST_DEP_ASSEMBLY.name] }); + expect(info.dependencyClosure).toEqual([TEST_DEP_ASSEMBLY, TEST_DEP_DEP_ASSEMBLY]); })); test('loads valid project (UNLICENSED)', () => _withTestProject(async projectRoot => { @@ -148,9 +148,7 @@ const TEST_DEP_ASSEMBLY: spec.Assembly = { author: { name: 'Amazon Web Services', url: 'https://aws.amazon.com', organization: true, roles: ['author'] }, fingerprint: 'F1NG3RPR1N7', dependencies: { - 'jsii-test-dep-dep': { - version: '3.2.1', - } + 'jsii-test-dep-dep': '3.2.1', }, jsiiVersion: VERSION, }; diff --git a/packages/jsii/vendor/.gitignore b/packages/jsii/vendor/.gitignore new file mode 100644 index 0000000000..f955bf36ab --- /dev/null +++ b/packages/jsii/vendor/.gitignore @@ -0,0 +1 @@ +!*.d.ts diff --git a/packages/jsii/vendor/semver-intersect.d.ts b/packages/jsii/vendor/semver-intersect.d.ts new file mode 100644 index 0000000000..c7f919bcf7 --- /dev/null +++ b/packages/jsii/vendor/semver-intersect.d.ts @@ -0,0 +1,13 @@ +/// Hand-written declaration for the semver-intersect module +declare module 'semver-intersect' { + /** + * Computes the intersection between multiple semver ranges. + * + * @param ranges the ranges for which the intersection is requested. + * + * @returns the intersection of `ranges`. + * + * @throws Error if the intersection is empty. + */ + function intersect(...ranges: string[]): string; +} diff --git a/yarn.lock b/yarn.lock index f7157bdedb..6f4ed898d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7140,7 +7140,14 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: +semver-intersect@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/semver-intersect/-/semver-intersect-1.4.0.tgz#bdd9c06bedcdd2fedb8cd352c3c43ee8c61321f3" + integrity sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ== + dependencies: + semver "^5.0.0" + +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.0.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==