From e72fea5c78ce07132dedab91c0c246e27e1ea6b6 Mon Sep 17 00:00:00 2001 From: Romain Marcadier-Muller Date: Tue, 2 Apr 2019 14:59:40 +0200 Subject: [PATCH] feat(jsii): Enforce use of peerDependencies (#421) Any direct dependency to another jsii module must be recorded as a peerDependency as well, guaranteeing a consistent behavior of dependencies across all possible jsii runtimes. BREAKING CHANGE: All direct dependencies must be duplicated in peerDependencies unless they are in bundledDependencies. Fixes #361 --- .gitignore | 1 + packages/codemaker/package.json | 4 +- packages/codemaker/tsconfig.json | 3 +- packages/jsii-dotnet-jsonmodel/package.json | 2 +- .../jsii-dotnet-runtime-test/package.json | 2 +- packages/jsii-dotnet-runtime/package.json | 2 +- packages/jsii-java-runtime/package.json | 2 +- packages/jsii-kernel/package.json | 4 +- packages/jsii-kernel/tsconfig.json | 9 +- packages/jsii-pacmak/package.json | 4 +- packages/jsii-pacmak/tsconfig.json | 13 ++- packages/jsii-reflect/package.json | 4 +- packages/jsii-runtime/package.json | 4 +- packages/jsii-spec/lib/spec.ts | 11 ++- packages/jsii-spec/package.json | 4 +- packages/jsii-spec/tsconfig.json | 3 +- packages/jsii/bin/jsii-fix-peers | 3 - packages/jsii/bin/jsii-fix-peers.ts | 95 ------------------- packages/jsii/bin/jsii.ts | 14 ++- packages/jsii/lib/project-info.ts | 42 +++++++- packages/jsii/package.json | 4 +- packages/jsii/test/test.project-info.ts | 82 +++++++++++++--- packages/jsii/tsconfig.json | 11 ++- packages/oo-ascii-tree/package.json | 4 +- 24 files changed, 177 insertions(+), 150 deletions(-) delete mode 100755 packages/jsii/bin/jsii-fix-peers delete mode 100644 packages/jsii/bin/jsii-fix-peers.ts diff --git a/.gitignore b/.gitignore index b70cc9f0e6..2080f36b07 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ node_modules/ .BUILD_COMPLETED lerna-debug.log +tsconfig.tsbuildinfo .DS_Store .idea .vs diff --git a/packages/codemaker/package.json b/packages/codemaker/package.json index 83633273f5..a78e24b99f 100644 --- a/packages/codemaker/package.json +++ b/packages/codemaker/package.json @@ -5,8 +5,8 @@ "main": "lib/index.js", "types": "lib/index.d.ts", "scripts": { - "build": "tsc", - "watch": "tsc -w", + "build": "tsc --build", + "watch": "tsc --build -w", "test": "nodeunit test/test.*.js", "package": "rm -fr dist/js && mkdir -p dist/js && mv $(npm pack) dist/js" }, diff --git a/packages/codemaker/tsconfig.json b/packages/codemaker/tsconfig.json index ad474280a6..ae88dc22ee 100644 --- a/packages/codemaker/tsconfig.json +++ b/packages/codemaker/tsconfig.json @@ -12,6 +12,7 @@ "noUnusedLocals": true, /* Report errors on unused locals. */ "noUnusedParameters": true, /* Report errors on unused parameters. */ "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */ + "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + "composite": true } } diff --git a/packages/jsii-dotnet-jsonmodel/package.json b/packages/jsii-dotnet-jsonmodel/package.json index 86055ee18e..bc8a691db4 100644 --- a/packages/jsii-dotnet-jsonmodel/package.json +++ b/packages/jsii-dotnet-jsonmodel/package.json @@ -7,7 +7,7 @@ "types": "lib/index.d.ts", "scripts": { "gen": "/bin/bash ./generate.sh", - "build": "tsc && /bin/bash ./build.sh", + "build": "tsc --build && /bin/bash ./build.sh", "test": "/bin/bash ./test.sh", "package": "package-dotnet" }, diff --git a/packages/jsii-dotnet-runtime-test/package.json b/packages/jsii-dotnet-runtime-test/package.json index 4272c4684a..94af26a29c 100644 --- a/packages/jsii-dotnet-runtime-test/package.json +++ b/packages/jsii-dotnet-runtime-test/package.json @@ -7,7 +7,7 @@ "private": true, "scripts": { "gen": "/bin/bash ./generate.sh", - "build": "npm run gen && tsc && /bin/bash ./build.sh", + "build": "npm run gen && tsc --build && /bin/bash ./build.sh", "test": "/bin/bash ./test.sh" }, "devDependencies": { diff --git a/packages/jsii-dotnet-runtime/package.json b/packages/jsii-dotnet-runtime/package.json index 0dd27c95e1..e7749d88b9 100644 --- a/packages/jsii-dotnet-runtime/package.json +++ b/packages/jsii-dotnet-runtime/package.json @@ -7,7 +7,7 @@ "types": "lib/index.d.ts", "scripts": { "gen": "/bin/bash ./generate.sh", - "build": "npm run gen && tsc && /bin/bash ./build.sh", + "build": "npm run gen && tsc --build && /bin/bash ./build.sh", "test": "/bin/bash ./test.sh", "package": "package-dotnet" }, diff --git a/packages/jsii-java-runtime/package.json b/packages/jsii-java-runtime/package.json index 1609887b2c..49f0cac40e 100644 --- a/packages/jsii-java-runtime/package.json +++ b/packages/jsii-java-runtime/package.json @@ -7,7 +7,7 @@ "private": true, "scripts": { "gen": "/bin/bash ./generate.sh", - "build": "tsc && npm run gen && cd project && mvn deploy -D altDeploymentRepository=local::default::file://${PWD}/../maven-repo", + "build": "tsc --build && npm run gen && cd project && mvn deploy -D altDeploymentRepository=local::default::file://${PWD}/../maven-repo", "test": "echo 'Tests are run as part of the build target'", "package": "package-java" }, diff --git a/packages/jsii-kernel/package.json b/packages/jsii-kernel/package.json index dbffb8a582..fb30650756 100644 --- a/packages/jsii-kernel/package.json +++ b/packages/jsii-kernel/package.json @@ -5,8 +5,8 @@ "main": "lib/index.js", "types": "lib/index.d.ts", "scripts": { - "build": "tsc && tslint -p .", - "watch": "tsc -w", + "build": "tsc --build && tslint -p .", + "watch": "tsc --build -w", "lint": "tslint -p . --force", "test": "nodeunit test/test.*.js", "package": "package-js" diff --git a/packages/jsii-kernel/tsconfig.json b/packages/jsii-kernel/tsconfig.json index 907de24775..7a4668f0c9 100644 --- a/packages/jsii-kernel/tsconfig.json +++ b/packages/jsii-kernel/tsconfig.json @@ -48,7 +48,12 @@ "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ /* Experimental Options */ - "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - } + "composite": true + }, + "include": ["**/*.ts"], + "references": [{ + "path": "../jsii-spec" + }] } diff --git a/packages/jsii-pacmak/package.json b/packages/jsii-pacmak/package.json index bf011d4964..16926dac54 100644 --- a/packages/jsii-pacmak/package.json +++ b/packages/jsii-pacmak/package.json @@ -9,8 +9,8 @@ "types": "lib/index.d.ts", "scripts": { "gen": "/bin/bash generate.sh", - "build": "npm run gen && tsc && chmod +x bin/jsii-pacmak && tslint -p .", - "watch": "tsc -w", + "build": "npm run gen && tsc --build && chmod +x bin/jsii-pacmak && tslint -p .", + "watch": "tsc --build -w", "lint": "tslint -p . --force", "test": "/bin/bash test/diff-test.sh && /bin/bash test/build-test.sh", "package": "package-js" diff --git a/packages/jsii-pacmak/tsconfig.json b/packages/jsii-pacmak/tsconfig.json index 3f968d60b3..d0a9a2d8cc 100644 --- a/packages/jsii-pacmak/tsconfig.json +++ b/packages/jsii-pacmak/tsconfig.json @@ -48,7 +48,16 @@ "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ /* Experimental Options */ - "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - } + "composite": true + }, + "include": [ + "**/*.ts" + ], + "references": [{ + "path": "../jsii-spec" + }, { + "path": "../codemaker" + }] } diff --git a/packages/jsii-reflect/package.json b/packages/jsii-reflect/package.json index 9273c398f0..365fcd38f5 100644 --- a/packages/jsii-reflect/package.json +++ b/packages/jsii-reflect/package.json @@ -8,8 +8,8 @@ "jsii-tree": "bin/jsii-tree" }, "scripts": { - "build": "tsc && chmod +x bin/jsii-tree", - "watch": "tsc -w", + "build": "tsc --build && chmod +x bin/jsii-tree", + "watch": "tsc --build -w", "test": "jest", "package": "package-js" }, diff --git a/packages/jsii-runtime/package.json b/packages/jsii-runtime/package.json index c171bb54b1..0072bf60db 100644 --- a/packages/jsii-runtime/package.json +++ b/packages/jsii-runtime/package.json @@ -8,8 +8,8 @@ "jsii-runtime": "bin/jsii-runtime" }, "scripts": { - "build": "tsc && chmod +x bin/jsii-runtime && /bin/bash ./bundle.sh", - "watch": "tsc -w", + "build": "tsc --build && chmod +x bin/jsii-runtime && /bin/bash ./bundle.sh", + "watch": "tsc --build -w", "test": "/bin/bash test/playback-test.sh", "package": "package-js" }, diff --git a/packages/jsii-spec/lib/spec.ts b/packages/jsii-spec/lib/spec.ts index cb04ecb2b7..10c5eb0678 100644 --- a/packages/jsii-spec/lib/spec.ts +++ b/packages/jsii-spec/lib/spec.ts @@ -161,17 +161,18 @@ export interface PackageVersion { dependencies?: { [assembly: string]: PackageVersion }; /** - * Indicates if this dependency is a peer dependency or a normal dependency. + * Indicates if this dependency is a direct (peer) dependency or a + * transitive 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. + * jsii enforces that any direct dependency on another jsii module is also + * defined as a peerDependency. 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://github.com/awslabs/jsii/issues/361 * @see https://nodejs.org/en/blog/npm/peer-dependencies/ */ peer?: boolean; diff --git a/packages/jsii-spec/package.json b/packages/jsii-spec/package.json index 78b37d7979..ffe17dcc2e 100644 --- a/packages/jsii-spec/package.json +++ b/packages/jsii-spec/package.json @@ -5,8 +5,8 @@ "main": "lib/index.js", "types": "lib/index.d.ts", "scripts": { - "watch": "tsc -w", - "build": "tsc && bash generate-json-schema.sh", + "build": "tsc --build && bash generate-json-schema.sh", + "watch": "tsc --build -w", "test": "nodeunit test/test.*.js", "package": "package-js" }, diff --git a/packages/jsii-spec/tsconfig.json b/packages/jsii-spec/tsconfig.json index 6f8f29cf64..20bcfd995b 100644 --- a/packages/jsii-spec/tsconfig.json +++ b/packages/jsii-spec/tsconfig.json @@ -48,7 +48,8 @@ // "inlineSources": false, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ /* Experimental Options */ - "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + "composite": true } } diff --git a/packages/jsii/bin/jsii-fix-peers b/packages/jsii/bin/jsii-fix-peers deleted file mode 100755 index 20df705cb2..0000000000 --- a/packages/jsii/bin/jsii-fix-peers +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env node -require('./jsii-fix-peers.js'); - diff --git a/packages/jsii/bin/jsii-fix-peers.ts b/packages/jsii/bin/jsii-fix-peers.ts deleted file mode 100644 index 7e2feb7ed4..0000000000 --- a/packages/jsii/bin/jsii-fix-peers.ts +++ /dev/null @@ -1,95 +0,0 @@ -// -// 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/bin/jsii.ts b/packages/jsii/bin/jsii.ts index efb6a28779..604cae4f9e 100644 --- a/packages/jsii/bin/jsii.ts +++ b/packages/jsii/bin/jsii.ts @@ -12,8 +12,16 @@ import { VERSION } from '../lib/version'; .env('JSII') .option('watch', { alias: 'w', type: 'boolean', desc: 'Watch for file changes and recompile automatically' }) .option('verbose', { alias: 'v', type: 'count', desc: 'Increase the verbosity of output', global: true }) - // tslint:disable-next-line:max-line-length - .option('project-references', { alias: 'r', type: 'boolean', desc: 'Generate TypeScript project references (also [package.json].jsii.projectReferences)' }) + .option('project-references', { + alias: 'r', + type: 'boolean', + desc: 'Generate TypeScript project references (also [package.json].jsii.projectReferences)' + }) + .option('fix-peer-dependencies', { + type: 'boolean', + default: true, + desc: 'Automatically add missing entries in the peerDependencies section of package.json' + }) .help() .version(VERSION) .argv; @@ -23,7 +31,7 @@ import { VERSION } from '../lib/version'; const projectRoot = path.normalize(path.resolve(process.cwd(), argv._[0] || '.')); const compiler = new Compiler({ - projectInfo: await loadProjectInfo(projectRoot), + projectInfo: await loadProjectInfo(projectRoot, { fixPeerDependencies: argv['fix-peer-dependencies'] }), watch: argv.watch, projectReferences: argv['project-references'] }); diff --git a/packages/jsii/lib/project-info.ts b/packages/jsii/lib/project-info.ts index 9022f4e1ca..5a3c8a423c 100644 --- a/packages/jsii/lib/project-info.ts +++ b/packages/jsii/lib/project-info.ts @@ -39,23 +39,55 @@ export interface ProjectInfo { readonly projectReferences?: boolean; } -export async function loadProjectInfo(projectRoot: string): Promise { - const pkg = require(path.join(projectRoot, 'package.json')); +export async function loadProjectInfo(projectRoot: string, { fixPeerDependencies }: { fixPeerDependencies: boolean }): Promise { + const packageJsonPath = path.join(projectRoot, 'package.json'); + const pkg = require(packageJsonPath); const bundleDependencies: { [name: string]: string } = {}; (pkg.bundleDependencies || pkg.bundledDependencies || []).forEach((name: string) => { const version = pkg.dependencies && pkg.dependencies[name]; if (!version) { - throw new Error(`The "package.json" has "${name}" in "bundleDependencies", but it is not declared in "dependencies"`); + throw new Error(`The "package.json" file has "${name}" in "bundleDependencies", but it is not declared in "dependencies"`); } if (pkg.peerDependencies && name in pkg.peerDependencies) { - throw new Error(`The "package.json" has "${name}" in "bundleDependencies", and also in "peerDependencies"`); + throw new Error(`The "package.json" file has "${name}" in "bundleDependencies", and also in "peerDependencies"`); } bundleDependencies[name] = version; }); + let addedPeerDependency = false; + Object.entries(pkg.dependencies || {}).forEach(([name, version]) => { + if (name in bundleDependencies) { + return; + } + pkg.peerDependencies = pkg.peerDependencies || {}; + const peerVersion = pkg.peerDependencies[name]; + if (peerVersion === version) { + return; + } + if (!fixPeerDependencies) { + if (peerVersion) { + // tslint:disable-next-line: max-line-length + throw new Error(`The "package.json" file has different version requirements for "${name}" in "dependencies" (${version}) versus "peerDependencies" (${peerVersion})`); + } + throw new Error(`The "package.json" file has "${name}" in "dependencies", but not in "peerDependencies"`); + } + if (peerVersion) { + LOG.warn(`Changing "peerDependency" on "${name}" from "${peerVersion}" to ${version}`); + } else { + LOG.warn(`Recording missing "peerDependency" on "${name}" at ${version}`); + } + pkg.peerDependencies[name] = version; + addedPeerDependency = true; + }); + // Re-write "package.json" if we fixed up "peerDependencies" and were told to automatically fix. + // Yes, we should never have addedPeerDependencies if not fixPeerDependency, but I still check again. + if (addedPeerDependency && fixPeerDependencies) { + await fs.writeJson(packageJsonPath, pkg, { encoding: 'utf8', spaces: 2 }); + } + const transitiveAssemblies: { [name: string]: spec.Assembly } = {}; const dependencies = await _loadDependencies(pkg.dependencies, projectRoot, transitiveAssemblies, new Set(Object.keys(bundleDependencies))); @@ -109,7 +141,7 @@ 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 | spec.PackageVersion } | undefined, searchPath: string, transitiveAssemblies: { [name: string]: spec.Assembly }, bundled = new Set()): Promise { diff --git a/packages/jsii/package.json b/packages/jsii/package.json index 2c69d2e267..e77d4061f3 100644 --- a/packages/jsii/package.json +++ b/packages/jsii/package.json @@ -13,8 +13,8 @@ "jsii-fix-peers": "bin/jsii-fix-peers" }, "scripts": { - "build": "cp ../../README.md . && bash ./generate.sh && tsc", - "watch": "bash ./generate.sh && tsc -w", + "build": "cp ../../README.md . && bash ./generate.sh && tsc --build", + "watch": "bash ./generate.sh && tsc --build -w", "test": "nyc nodeunit test/test.*.js", "package": "package-js" }, diff --git a/packages/jsii/test/test.project-info.ts b/packages/jsii/test/test.project-info.ts index 534bf7bdb1..4d4a7207a4 100644 --- a/packages/jsii/test/test.project-info.ts +++ b/packages/jsii/test/test.project-info.ts @@ -19,7 +19,8 @@ const BASE_PROJECT = { jsii: { targets: { foo: { bar: 'baz' } } }, - dependencies: { 'jsii-test-dep': '^1.2.3' } + dependencies: { 'jsii-test-dep': '^1.2.3' }, + peerDependencies: { 'jsii-test-dep': '^1.2.3' } }; export = nodeunit.testCase({ @@ -27,7 +28,7 @@ export = nodeunit.testCase({ async 'loads valid project'(test: nodeunit.Test) { await _withTestProject(async projectRoot => { try { - const info = await loadProjectInfo(projectRoot); + const info = await loadProjectInfo(projectRoot, { fixPeerDependencies: false }); test.equal(info.name, BASE_PROJECT.name); test.equal(info.version, BASE_PROJECT.version); test.equal(info.description, BASE_PROJECT.description); @@ -52,7 +53,7 @@ export = nodeunit.testCase({ async 'loads valid project (UNLICENSED)'(test: nodeunit.Test) { await _withTestProject(async projectRoot => { try { - const info = await loadProjectInfo(projectRoot); + const info = await loadProjectInfo(projectRoot, { fixPeerDependencies: false }); test.equal(info && info.license, 'UNLICENSED'); } catch (e) { test.ifError(e); @@ -67,7 +68,7 @@ export = nodeunit.testCase({ async 'loads valid project (using bundleDependencies)'(test: nodeunit.Test) { await _withTestProject(async projectRoot => { try { - const info = await loadProjectInfo(projectRoot); + const info = await loadProjectInfo(projectRoot, { fixPeerDependencies: false }); test.deepEqual(info.bundleDependencies, { bundled: '^1.2.3' }); } catch (e) { test.ifError(e); @@ -83,7 +84,7 @@ export = nodeunit.testCase({ async 'loads valid project (using bundledDependencies)'(test: nodeunit.Test) { await _withTestProject(async projectRoot => { try { - const info = await loadProjectInfo(projectRoot); + const info = await loadProjectInfo(projectRoot, { fixPeerDependencies: false }); test.deepEqual(info.bundleDependencies, { bundled: '^1.2.3' }); } catch (e) { test.ifError(e); @@ -100,7 +101,7 @@ export = nodeunit.testCase({ const contributors = [{ name: 'foo', email: 'nobody@amazon.com' }]; await _withTestProject(async projectRoot => { try { - const info = await loadProjectInfo(projectRoot); + const info = await loadProjectInfo(projectRoot, { fixPeerDependencies: false }); test.deepEqual(info && info.contributors && info.contributors.map(_stripUndefined), contributors.map(c => ({ ...c, roles: ['contributor'] }))); } catch (e) { @@ -115,7 +116,7 @@ export = nodeunit.testCase({ await _withTestProject(async projectRoot => { let error: Error | undefined; try { - await loadProjectInfo(projectRoot); + await loadProjectInfo(projectRoot, { fixPeerDependencies: false }); } catch (e) { error = e; } finally { @@ -132,7 +133,7 @@ export = nodeunit.testCase({ await _withTestProject(async projectRoot => { let error: Error | undefined; try { - await loadProjectInfo(projectRoot); + await loadProjectInfo(projectRoot, { fixPeerDependencies: false }); } catch (e) { error = e; } finally { @@ -149,7 +150,7 @@ export = nodeunit.testCase({ await _withTestProject(async projectRoot => { let error: Error | undefined; try { - await loadProjectInfo(projectRoot); + await loadProjectInfo(projectRoot, { fixPeerDependencies: false }); } catch (e) { error = e; } finally { @@ -159,8 +160,66 @@ export = nodeunit.testCase({ } }, info => { info.dependencies[TEST_DEP_ASSEMBLY.name] = '^1.2.5'; + info.peerDependencies[TEST_DEP_ASSEMBLY.name] = '^1.2.5'; }); - } + }, + + async 'fails to load with missing peerDependency (refusing to auto-fix)'(test: nodeunit.Test) { + await _withTestProject(async projectRoot => { + let error: Error | undefined; + try { + await loadProjectInfo(projectRoot, { fixPeerDependencies: false }); + } catch (e) { + error = e; + } finally { + test.throws(() => { if (error) { throw error; } }, + `The "package.json" file has "${TEST_DEP_ASSEMBLY.name}" in "dependencies", but not in "peerDependencies"`); + test.done(); + } + }, info => { + delete info.peerDependencies[TEST_DEP_ASSEMBLY.name]; + }); + }, + + async 'loads with missing peerDependency (when auto-fixing)'(test: nodeunit.Test) { + await _withTestProject(async projectRoot => { + await loadProjectInfo(projectRoot, { fixPeerDependencies: true }); + const info = require(path.join(projectRoot, 'package.json')); + test.equal(info.peerDependencies[TEST_DEP_ASSEMBLY.name], "^1.2.3"); + test.done(); + }, info => { + delete info.peerDependencies[TEST_DEP_ASSEMBLY.name]; + }); + }, + + async 'fails to load with inconsistent peerDependency (refusing to auto-fix)'(test: nodeunit.Test) { + await _withTestProject(async projectRoot => { + let error: Error | undefined; + try { + await loadProjectInfo(projectRoot, { fixPeerDependencies: false }); + } catch (e) { + error = e; + } finally { + test.throws(() => { if (error) { throw error; } }, + // tslint:disable-next-line: max-line-length + `The "package.json" file has different version requirements for "${TEST_DEP_ASSEMBLY.name}" in "dependencies" (^1.2.3) versus "peerDependencies" (^42.1337.0)`); + test.done(); + } + }, info => { + info.peerDependencies[TEST_DEP_ASSEMBLY.name] = '^42.1337.0'; + }); + }, + + async 'loads with inconsistent peerDependency (when auto-fixing)'(test: nodeunit.Test) { + await _withTestProject(async projectRoot => { + await loadProjectInfo(projectRoot, { fixPeerDependencies: true }); + const info = require(path.join(projectRoot, 'package.json')); + test.equal(info.peerDependencies[TEST_DEP_ASSEMBLY.name], "^1.2.3"); + test.done(); + }, info => { + info.peerDependencies[TEST_DEP_ASSEMBLY.name] = '^42.1337.0'; + }); + }, } }); @@ -176,7 +235,8 @@ const TEST_DEP_ASSEMBLY: spec.Assembly = { fingerprint: 'F1NG3RPR1N7', dependencies: { 'jsii-test-dep-dep': { - version: '3.2.1' + version: '3.2.1', + peer: true, } }, jsiiVersion: VERSION, diff --git a/packages/jsii/tsconfig.json b/packages/jsii/tsconfig.json index 5bde5dff77..54b8621d79 100644 --- a/packages/jsii/tsconfig.json +++ b/packages/jsii/tsconfig.json @@ -16,9 +16,16 @@ "noFallthroughCasesInSwitch": true, "inlineSourceMap": true, "inlineSources": true, - "experimentalDecorators": true + "experimentalDecorators": true, + "composite": true }, + "include": [ + "**/*.ts" + ], "exclude": [ "test/negatives/*" - ] + ], + "references": [{ + "path": "../jsii-spec" + }] } diff --git a/packages/oo-ascii-tree/package.json b/packages/oo-ascii-tree/package.json index c6748b6a7e..9217a1cf93 100644 --- a/packages/oo-ascii-tree/package.json +++ b/packages/oo-ascii-tree/package.json @@ -5,8 +5,8 @@ "main": "lib/index.js", "types": "lib/index.d.ts", "scripts": { - "build": "tsc", - "watch": "tsc -w", + "build": "tsc --build", + "watch": "tsc --build -w", "test": "jest", "package": "package-js" },