diff --git a/package-lock.json b/package-lock.json index 7b8bba1be7..95e0955a7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -426,7 +426,8 @@ "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true }, "growl": { "version": "1.10.5", @@ -597,6 +598,7 @@ "dedent": "^0.7.0", "execa": "^0.8.0", "find-up": "^2.1.0", + "fs-extra": "^4.0.1", "get-port": "^3.2.0", "glob": "^7.1.2", "glob-parent": "^3.1.0", @@ -1409,9 +1411,10 @@ } }, "fs-extra": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.0.tgz", - "integrity": "sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ==", + "version": "4.0.3", + "resolved": "http://localhost:4873/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "dev": true, "requires": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -1884,8 +1887,9 @@ }, "jsonfile": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "resolved": "http://localhost:4873/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, "requires": { "graceful-fs": "^4.1.6" } @@ -2844,8 +2848,9 @@ }, "universalify": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + "resolved": "http://localhost:4873/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true }, "unzip-response": { "version": "2.0.1", diff --git a/packages/jsii-calc-base/package.json b/packages/jsii-calc-base/package.json index 0e745d1f18..d7e3de28c9 100644 --- a/packages/jsii-calc-base/package.json +++ b/packages/jsii-calc-base/package.json @@ -34,6 +34,9 @@ "dependencies": { "@scope/jsii-calc-base-of-base": "^0.7.8" }, + "peerDependencies": { + "@scope/jsii-calc-base-of-base": "^0.7.8" + }, "author": { "name": "Amazon Web Services", "url": "https://aws.amazon.com", diff --git a/packages/jsii-calc-base/test/assembly.jsii b/packages/jsii-calc-base/test/assembly.jsii index 6c2dc94153..d9b4a575a4 100644 --- a/packages/jsii-calc-base/test/assembly.jsii +++ b/packages/jsii-calc-base/test/assembly.jsii @@ -9,6 +9,7 @@ }, "dependencies": { "@scope/jsii-calc-base-of-base": { + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace", @@ -102,5 +103,5 @@ } }, "version": "0.7.8", - "fingerprint": "G+ZeFV6LQWFdp9ZuDgqN4rS10q/JBHuy0Wo8qaTK/t8=" + "fingerprint": "K1rAUs6WiQ5lF08T46B8v/5UL8T8Ot59e0Nc8rh2jiQ=" } diff --git a/packages/jsii-calc-lib/package.json b/packages/jsii-calc-lib/package.json index 882955e410..66caf3214b 100644 --- a/packages/jsii-calc-lib/package.json +++ b/packages/jsii-calc-lib/package.json @@ -34,6 +34,9 @@ "dependencies": { "@scope/jsii-calc-base": "^0.7.8" }, + "peerDependencies": { + "@scope/jsii-calc-base": "^0.7.8" + }, "author": { "name": "Amazon Web Services", "url": "https://aws.amazon.com", diff --git a/packages/jsii-calc-lib/test/assembly.jsii b/packages/jsii-calc-lib/test/assembly.jsii index dc6ac23805..5e0a9d1ce5 100644 --- a/packages/jsii-calc-lib/test/assembly.jsii +++ b/packages/jsii-calc-lib/test/assembly.jsii @@ -11,6 +11,7 @@ "@scope/jsii-calc-base": { "dependencies": { "@scope/jsii-calc-base-of-base": { + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace", @@ -30,6 +31,7 @@ "version": "0.7.8" } }, + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseNamespace", @@ -351,5 +353,5 @@ } }, "version": "0.7.8", - "fingerprint": "HzcyHys0b9gFmP4dogeIJmGE6GVtrSo/P0S54Vd/X8U=" + "fingerprint": "ENH00UDSeTnXKpvD3/vp0k8zyd+KCHzc9QWB151/gM8=" } diff --git a/packages/jsii-calc/package.json b/packages/jsii-calc/package.json index 515e45b365..675cae4033 100644 --- a/packages/jsii-calc/package.json +++ b/packages/jsii-calc/package.json @@ -35,6 +35,10 @@ "@scope/jsii-calc-lib": "^0.7.8", "jsii-calc-bundled": "^0.7.8" }, + "peerDependencies": { + "@scope/jsii-calc-base": "^0.7.8", + "@scope/jsii-calc-lib": "^0.7.8" + }, "devDependencies": { "@types/node": "^8.10.37", "jsii": "^0.7.8", diff --git a/packages/jsii-calc/test/assembly.jsii b/packages/jsii-calc/test/assembly.jsii index c6ffb76b77..97e7a8e8a5 100644 --- a/packages/jsii-calc/test/assembly.jsii +++ b/packages/jsii-calc/test/assembly.jsii @@ -37,6 +37,7 @@ "@scope/jsii-calc-base": { "dependencies": { "@scope/jsii-calc-base-of-base": { + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace", @@ -56,6 +57,7 @@ "version": "0.7.8" } }, + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseNamespace", @@ -79,6 +81,7 @@ "@scope/jsii-calc-base": { "dependencies": { "@scope/jsii-calc-base-of-base": { + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace", @@ -98,6 +101,7 @@ "version": "0.7.8" } }, + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseNamespace", @@ -117,6 +121,7 @@ "version": "0.7.8" } }, + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.LibNamespace", @@ -3407,5 +3412,5 @@ } }, "version": "0.7.8", - "fingerprint": "jHSXTzCSZbwYMvLKpeZB6SE8hNgYgt9/2JF1ihM41SI=" + "fingerprint": "Xn7Rk17rqR3AaMx3+ssxT0GR1sCWwz0OGC+C8QuLI3A=" } 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 6c2dc94153..d9b4a575a4 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 @@ -9,6 +9,7 @@ }, "dependencies": { "@scope/jsii-calc-base-of-base": { + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace", @@ -102,5 +103,5 @@ } }, "version": "0.7.8", - "fingerprint": "G+ZeFV6LQWFdp9ZuDgqN4rS10q/JBHuy0Wo8qaTK/t8=" + "fingerprint": "K1rAUs6WiQ5lF08T46B8v/5UL8T8Ot59e0Nc8rh2jiQ=" } 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 dc6ac23805..5e0a9d1ce5 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 @@ -11,6 +11,7 @@ "@scope/jsii-calc-base": { "dependencies": { "@scope/jsii-calc-base-of-base": { + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace", @@ -30,6 +31,7 @@ "version": "0.7.8" } }, + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseNamespace", @@ -351,5 +353,5 @@ } }, "version": "0.7.8", - "fingerprint": "HzcyHys0b9gFmP4dogeIJmGE6GVtrSo/P0S54Vd/X8U=" + "fingerprint": "ENH00UDSeTnXKpvD3/vp0k8zyd+KCHzc9QWB151/gM8=" } 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 c6ffb76b77..97e7a8e8a5 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 @@ -37,6 +37,7 @@ "@scope/jsii-calc-base": { "dependencies": { "@scope/jsii-calc-base-of-base": { + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace", @@ -56,6 +57,7 @@ "version": "0.7.8" } }, + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseNamespace", @@ -79,6 +81,7 @@ "@scope/jsii-calc-base": { "dependencies": { "@scope/jsii-calc-base-of-base": { + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseOfBaseNamespace", @@ -98,6 +101,7 @@ "version": "0.7.8" } }, + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.BaseNamespace", @@ -117,6 +121,7 @@ "version": "0.7.8" } }, + "peer": true, "targets": { "dotnet": { "namespace": "Amazon.JSII.Tests.CalculatorNamespace.LibNamespace", @@ -3407,5 +3412,5 @@ } }, "version": "0.7.8", - "fingerprint": "jHSXTzCSZbwYMvLKpeZB6SE8hNgYgt9/2JF1ihM41SI=" + "fingerprint": "Xn7Rk17rqR3AaMx3+ssxT0GR1sCWwz0OGC+C8QuLI3A=" } diff --git a/packages/jsii-spec/lib/spec.ts b/packages/jsii-spec/lib/spec.ts index 43d239cda7..5150455ed2 100644 --- a/packages/jsii-spec/lib/spec.ts +++ b/packages/jsii-spec/lib/spec.ts @@ -153,6 +153,22 @@ export interface PackageVersion { /** Dependencies of this dependency */ dependencies?: { [assembly: string]: PackageVersion }; + + /** + * Indicates if this dependency is a peer dependency or a normal dependency. + * + * Peer dependencies are expected to be explicitly defined by the user of + * this library instead of brought in as transitive dependencies. + * + * jsii enforces that if this module exports a type from a dependency, this + * dependency must be defined as a peer and not as a normal dependency. + * Otherwise, it would be impossible to safely use two versions of this + * dependency in a closure. + * + * @see https://github.com/awslabs/aws-cdk/issues/979 + * @see https://nodejs.org/en/blog/npm/peer-dependencies/ + */ + peer?: boolean; } /** diff --git a/packages/jsii/bin/jsii-fix-peers b/packages/jsii/bin/jsii-fix-peers new file mode 100755 index 0000000000..03978a0086 --- /dev/null +++ b/packages/jsii/bin/jsii-fix-peers @@ -0,0 +1,2 @@ +#!/usr/bin/env node +require('./jsii-fix-peers.js'); \ No newline at end of file diff --git a/packages/jsii/bin/jsii-fix-peers.ts b/packages/jsii/bin/jsii-fix-peers.ts new file mode 100644 index 0000000000..7e2feb7ed4 --- /dev/null +++ b/packages/jsii/bin/jsii-fix-peers.ts @@ -0,0 +1,95 @@ +// +// jsii-fix-peers +// +// Inspects the local .jsii file and adds "peerDependencies" to the local +// package.json for all modules that include types that are referenced as part +// of the current module's public API. +// +// This resolves all peer dependency warnings emitted by the jsii compiler. +// + +// tslint:disable:no-console + +import fs = require('fs-extra'); +import spec = require('jsii-spec'); + +fixPeers().catch(e => { + console.error(e.stack); + process.exit(1); +}); + +async function fixPeers() { + const jsiiFile = './.jsii'; + const pkgFile = './package.json'; + + if (!(await fs.pathExists(jsiiFile))) { + throw new Error(`Cannot find '${jsiiFile}. Make sure to compile first`); + } + + if (!(await fs.pathExists(pkgFile))) { + throw new Error(`Cannot find '${pkgFile}' in current working directory`); + } + + const jsii = await fs.readJson(jsiiFile) as spec.Assembly; + const pkg = await fs.readJson(pkgFile); + + // find all type references (FQNs) in the assembly. + const fqns = findAllFQNs(jsii); + + let updated = false; + + // read "dependencies" and "peerDependencies" from package.json + if (!pkg.peerDependencies) { pkg.peerDependencies = {}; } + const peers = pkg.peerDependencies; + const deps = pkg.dependencies || {}; + + // iterate over all FQNs and ensure that all referenced modules appear as peer dependencies + for (const fqn of fqns) { + const [ module, ] = fqn.split('.'); + if (module === jsii.name) { + continue; + } + + // verify that `module` exists in peerDependencies + if (module in peers) { + continue; + } + + // fetch the module's version requirement from "dependencies" (we expect it to be there) + const dep = deps[module]; + if (!dep) { + throw new Error(`Cannot find ${module} neither under "peerDependencies" nor "dependencies". Can't fix.`); + } + + // fixing time! add this module to "peerDependencies" + peers[module] = dep; + console.log(`added ${module}@${dep} to "peerDependencies"`); + updated = true; + } + + if (updated) { + await fs.writeJson(pkgFile, pkg, { spaces: 2 }); + } +} + +/** + * Looks up all { "fqn": "boom" } objects in a JSON tree. + * @param node Initial node (i.e. a JSII assembly) + * @param out Used by the recursive call to collect outputs + */ +function findAllFQNs(node: any, out = new Set()) { + if (typeof(node) === 'object' && 'fqn' in node) { + out.add(node.fqn); + } + + for (const key of Object.keys(node)) { + const value = node[key]; + if (Array.isArray(value)) { + value.forEach(v => findAllFQNs(v, out)); + } else if (typeof(value) === 'object') { + findAllFQNs(value, out); + } + } + + return Array.from(out); +} diff --git a/packages/jsii/lib/assembler.ts b/packages/jsii/lib/assembler.ts index 4d2ae4ea06..d9847e6792 100644 --- a/packages/jsii/lib/assembler.ts +++ b/packages/jsii/lib/assembler.ts @@ -103,7 +103,7 @@ export class Assembler implements Emitter { author: this.projectInfo.author, contributors: this.projectInfo.contributors && [...this.projectInfo.contributors], repository: this.projectInfo.repository, - dependencies: _toDependencies(this.projectInfo.dependencies), + dependencies: _toDependencies(this.projectInfo.dependencies, this.projectInfo.peerDependencies), bundled: this.projectInfo.bundleDependencies, types: this._types, targets: this.projectInfo.targets, @@ -199,6 +199,18 @@ export class Assembler implements Emitter { } else { const assembly = this.projectInfo.transitiveDependencies.find(dep => dep.name === assm); type = assembly && assembly.types && assembly.types[ref.fqn]; + + // 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) { + this._diagnostic(referencingNode, ts.DiagnosticCategory.Warning, + `The type '${ref.fqn}' is exposed in the public API of this module. ` + + `Therefore, the module '${assembly.name}' must also be defined under "peerDependencies". ` + + `You can use the "jsii-fix-peers" utility to fix.`); + } + } } if (!type) { @@ -1045,7 +1057,7 @@ function _sortMembers(type: spec.ClassType | spec.InterfaceType): spec.ClassTyp }; } -function _toDependencies(assemblies: ReadonlyArray): { [name: string]: spec.PackageVersion } { +function _toDependencies(assemblies: ReadonlyArray, peers: ReadonlyArray): { [name: string]: spec.PackageVersion } { const result: { [name: string]: spec.PackageVersion } = {}; for (const assembly of assemblies) { result[assembly.name] = { @@ -1054,6 +1066,25 @@ function _toDependencies(assemblies: ReadonlyArray): { [name: str dependencies: assembly.dependencies }; } + + for (const peer of peers) { + if (peer.name in result) { + // module already appears as a normal dependency. just make sure it's the same version + const depVersion = result[peer.name].version; + if (depVersion !== peer.version) { + throw new Error( + `Module '${peer.name}' appears both as a dependency (${depVersion}) ` + + `and a peer dependency (${peer.version}), with mismatching versions`); + } + } + + result[peer.name] = { + version: peer.version, + targets: peer.targets, + dependencies: peer.dependencies, + peer: true + }; + } return result; } diff --git a/packages/jsii/lib/project-info.ts b/packages/jsii/lib/project-info.ts index 380743acd3..e759cac32b 100644 --- a/packages/jsii/lib/project-info.ts +++ b/packages/jsii/lib/project-info.ts @@ -26,6 +26,7 @@ export interface ProjectInfo { readonly types: string; readonly dependencies: ReadonlyArray; + readonly peerDependencies: ReadonlyArray; readonly transitiveDependencies: ReadonlyArray; readonly bundleDependencies: { readonly [name: string]: string }; readonly targets: spec.AssemblyTargets; @@ -46,8 +47,26 @@ export async function loadProjectInfo(projectRoot: string): Promise bundleDependencies[name] = version; }); - const [dependencies, transitiveDependencies] = - await _loadDependencies(pkg.dependencies, projectRoot, new Set(Object.keys(bundleDependencies))); + // verify peer dependencies and dependencies have the same version specs + const deps = pkg.dependencies || { }; + const peerDeps = pkg.peerDependencies || { }; + for (const module of Object.keys(peerDeps)) { + const peerVersion = peerDeps[module]; + const depVersion = deps[module]; + if (depVersion && peerVersion !== depVersion) { + throw new Error( + `The module '${module}' is specified as ${peerVersion} under ` + + `"peerDependencieds" and as ${depVersion} under "dependencies"`); + } + } + + const transitiveAssemblies: { [name: string]: spec.Assembly } = {}; + const dependencies = + await _loadDependencies(pkg.dependencies, projectRoot, transitiveAssemblies, new Set(Object.keys(bundleDependencies))); + const peerDependencies = + await _loadDependencies(pkg.peerDependencies, projectRoot, transitiveAssemblies); + + const transitiveDependencies = Object.keys(transitiveAssemblies).map(name => transitiveAssemblies[name]); return { projectRoot, @@ -65,6 +84,7 @@ export async function loadProjectInfo(projectRoot: string): Promise types: _required(pkg.types, 'The "package.json" file must specify the "types" attribute'), dependencies, + peerDependencies, transitiveDependencies, bundleDependencies, targets: { @@ -90,10 +110,10 @@ function _guessRepositoryType(url: string): string { async function _loadDependencies(dependencies: { [name: string]: string | spec.PackageVersion } | undefined, searchPath: string, - bundled: Set = new Set()): Promise<[spec.Assembly[], spec.Assembly[]]> { - if (!dependencies) { return [[], []]; } + transitiveAssemblies: { [name: string]: spec.Assembly }, + bundled = new Set()): Promise { + if (!dependencies) { return []; } const assemblies = new Array(); - const transitiveAssemblies = new Array(); for (const name of Object.keys(dependencies)) { if (bundled.has(name)) { continue; } const dep = dependencies[name]; @@ -109,17 +129,13 @@ async function _loadDependencies(dependencies: { [name: string]: string | spec. throw new Error(`Declared dependency on version ${versionString} of ${name}, but version ${assm.version} was found`); } assemblies.push(assm); - transitiveAssemblies.push(assm); + transitiveAssemblies[assm.name] = assm; const pkgDir = path.dirname(pkg); if (assm.dependencies) { - const [depAssemblies, depTransitiveAssemblies, ] = await _loadDependencies(assm.dependencies, pkgDir); - for (const depAssembly of depAssemblies.concat(depTransitiveAssemblies)) { - if (transitiveAssemblies.find(a => a.name === depAssembly.name) != null) { continue; } - transitiveAssemblies.push(depAssembly); - } + await _loadDependencies(assm.dependencies, pkgDir, transitiveAssemblies); } } - return [assemblies, transitiveAssemblies]; + return assemblies; } function _required(value: T, message: string): T { diff --git a/packages/jsii/package.json b/packages/jsii/package.json index 4960ad579b..bfdcc7aa9f 100644 --- a/packages/jsii/package.json +++ b/packages/jsii/package.json @@ -9,7 +9,8 @@ "organization": true }, "bin": { - "jsii": "bin/jsii" + "jsii": "bin/jsii", + "jsii-fix-peers": "bin/jsii-fix-peers" }, "scripts": { "build": "cp ../../README.md . && bash ./generate.sh && tsc", diff --git a/packages/jsii/test/test.negatives.ts b/packages/jsii/test/test.negatives.ts index 2d4b174af2..40335652a7 100644 --- a/packages/jsii/test/test.negatives.ts +++ b/packages/jsii/test/test.negatives.ts @@ -70,6 +70,7 @@ function _makeProjectInfo(types: string): ProjectInfo { author: { name: 'John Doe', roles: ['author'] }, repository: { type: 'git', url: 'https://github.com/awslabs/jsii.git' }, dependencies: [], + peerDependencies: [], transitiveDependencies: [], bundleDependencies: {}, targets: {}