From 23dbdc826f67e78200f1e6d265538173b23b0115 Mon Sep 17 00:00:00 2001 From: Ali Ijaz Sheikh Date: Wed, 5 Jun 2019 15:40:01 -0700 Subject: [PATCH 1/3] feat: support local paths For local packages, follow local package references. Fixes: https://github.com/google/js-green-licenses/issues/96 --- src/checker.ts | 60 ++++++++++++++++++++++++++++++++++++-------- test/checker-test.ts | 57 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 11 deletions(-) diff --git a/src/checker.ts b/src/checker.ts index a480a95..f9e4f84 100644 --- a/src/checker.ts +++ b/src/checker.ts @@ -227,20 +227,51 @@ export class LicenseChecker extends EventEmitter { } } + private async getPackageJson( + packageName: string, + versionSpec: string, + localDirectory: string | null + ): Promise<{}> { + // If this has a relative URL, and is a local package, find the package json from the + // indicated directory + if (versionSpec.startsWith('file:') && localDirectory) { + const relativePath = versionSpec.slice('file:'.length); + const packageJsonPath = path.join( + localDirectory, + relativePath, + 'package.json' + ); + this.emit('package.json', packageJsonPath); + const contents = await fsReadFile(packageJsonPath, 'utf8'); + return JSON.parse(contents); + } + return packageJson(packageName, { + version: versionSpec, + fullMetadata: true, + }); + } + private async checkLicenses( packageName: string, versionSpec: string, + localDirectory: string | null, ...parents: string[] ): Promise { const spec = `${packageName}@${versionSpec}`; if (this.failedPackages.has(spec)) return; try { - const json = await packageJson(packageName, { - version: versionSpec, - fullMetadata: true, - }); - await this.checkPackageJson(json, packageName, ...parents); + const json = await this.getPackageJson( + packageName, + versionSpec, + localDirectory + ); + await this.checkPackageJson( + json, + packageName, + localDirectory, + ...parents + ); } catch (err) { this.failedPackages.add(spec); this.emit('error', { @@ -254,18 +285,20 @@ export class LicenseChecker extends EventEmitter { private async checkLicensesForDeps( deps: Dependencies | undefined, + localDirectory: string | null, ...parents: string[] ): Promise { if (!deps) return; for (const pkg of Object.keys(deps)) { const depVersion = deps[pkg]; - await this.checkLicenses(pkg, depVersion, ...parents); + await this.checkLicenses(pkg, depVersion, localDirectory, ...parents); } } private async checkPackageJson( json: {}, packageName: string | null, + localDirectory: string | null, ...parents: string[] ): Promise { const pj: PackageJson = ensurePackageJson(json); @@ -298,24 +331,29 @@ export class LicenseChecker extends EventEmitter { await this.checkLicensesForDeps( pj.dependencies, + localDirectory, ...parents, packageAndVersion ); if (this.opts.dev) { await this.checkLicensesForDeps( pj.devDependencies, + localDirectory, ...parents, packageAndVersion ); } } - private async checkPackageJsonContent(content: string): Promise { + private async checkPackageJsonContent( + content: string, + localDirectory: string | null + ): Promise { // tslint:disable-next-line:no-any `JSON.parse()` returns any let json: any = null; try { json = JSON.parse(content); - await this.checkPackageJson(json, json.name); + await this.checkPackageJson(json, json.name, localDirectory); } catch (err) { const packageName = (json && json.name) || '(unknown package)'; const versionSpec = (json && json.version) || '(unknown version)'; @@ -369,7 +407,7 @@ export class LicenseChecker extends EventEmitter { for (const pj of packageJsons) { this.emit('package.json', pj); const content = await fsReadFile(pj, 'utf8'); - await this.checkPackageJsonContent(content); + await this.checkPackageJsonContent(content, path.dirname(pj)); } this.emit('end'); } @@ -385,7 +423,7 @@ export class LicenseChecker extends EventEmitter { if (!pkgArgs.name || !pkgArgs.fetchSpec) { throw new Error(`Invalid package spec: ${pkg}`); } - await this.checkLicenses(pkgArgs.name, pkgArgs.fetchSpec); + await this.checkLicenses(pkgArgs.name, pkgArgs.fetchSpec, null); this.emit('end'); } @@ -416,7 +454,7 @@ export class LicenseChecker extends EventEmitter { } for (const pj of packageJsons) { this.emit('package.json', pj.filePath); - await this.checkPackageJsonContent(pj.content); + await this.checkPackageJsonContent(pj.content, null); } this.emit('end'); } diff --git a/test/checker-test.ts b/test/checker-test.ts index aea66a9..8b5e257 100644 --- a/test/checker-test.ts +++ b/test/checker-test.ts @@ -333,3 +333,60 @@ test.serial('decline private package (local repo)', t => { } ); }); + +test.serial('support local paths', t => { + const primaryPackageJson = JSON.stringify({ + name: 'hello', + version: '1.0.0', + license: 'Apache-2.0', + dependencies: { + foo: '^1.2.3', + linked: 'file:../linked', + }, + }); + const linkedPackageJson = JSON.stringify({ + name: 'linked', + version: '1.0.0', + license: 'Apache-2.0', + dependencies: { + baz: '^7.0.0', + }, + }); + return withFixtures( + { + 'path/to/primary': { + 'package.json': primaryPackageJson, + 'another-file': 'meh, world.', + }, + 'path/to/linked': { + 'package.json': linkedPackageJson, + 'another-file': 'i depend on a package with an evil license', + }, + }, + async () => { + requestedPackages = []; + const nonGreenPackages: string[] = []; + const packageJsonPaths: string[] = []; + const checker = new LicenseChecker(); + checker + .on('non-green-license', arg => { + nonGreenPackages.push(`${arg.packageName}@${arg.version}`); + }) + .on('package.json', filePath => { + packageJsonPaths.push(filePath); + }); + await checker.checkLocalDirectory('path/to/primary'); + console.log('requested packages: ', requestedPackages); + t.deepEqual(requestedPackages, [ + 'foo@^1.2.3', + 'bar@^4.5.0', + 'baz@^7.0.0', + ]); + t.deepEqual(nonGreenPackages, ['bar@4.5.6', 'baz@7.8.9']); + t.deepEqual(packageJsonPaths, [ + 'path/to/primary/package.json', + 'path/to/linked/package.json', + ]); + } + ); +}); From db8406bbb332fb4c607454be325d45edbec423ed Mon Sep 17 00:00:00 2001 From: Ali Ijaz Sheikh Date: Wed, 5 Jun 2019 15:49:42 -0700 Subject: [PATCH 2/3] resolve is better --- src/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/checker.ts b/src/checker.ts index f9e4f84..b85d09b 100644 --- a/src/checker.ts +++ b/src/checker.ts @@ -236,7 +236,7 @@ export class LicenseChecker extends EventEmitter { // indicated directory if (versionSpec.startsWith('file:') && localDirectory) { const relativePath = versionSpec.slice('file:'.length); - const packageJsonPath = path.join( + const packageJsonPath = path.resolve( localDirectory, relativePath, 'package.json' From 6f2828976a32ef814bea72729fa856ba041f6041 Mon Sep 17 00:00:00 2001 From: Ali Ijaz Sheikh Date: Wed, 5 Jun 2019 15:53:26 -0700 Subject: [PATCH 3/3] nah, join was better --- src/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/checker.ts b/src/checker.ts index b85d09b..f9e4f84 100644 --- a/src/checker.ts +++ b/src/checker.ts @@ -236,7 +236,7 @@ export class LicenseChecker extends EventEmitter { // indicated directory if (versionSpec.startsWith('file:') && localDirectory) { const relativePath = versionSpec.slice('file:'.length); - const packageJsonPath = path.resolve( + const packageJsonPath = path.join( localDirectory, relativePath, 'package.json'