Skip to content

Instantly share code, notes, and snippets.

@princed
Last active March 7, 2019 05:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save princed/83a5a68afd284fbb5940a2b2464b7a08 to your computer and use it in GitHub Desktop.
Save princed/83a5a68afd284fbb5940a2b2464b7a08 to your computer and use it in GitHub Desktop.

List direct dependencies' licenses

Setup

git clone https://gist.github.com/83a5a68afd284fbb5940a2b2464b7a08.git list-licences
cd list-licences
npm install
npm link

Usage

In folder with target package.json:

  • license-list for production dependencies
  • license-list --dev for development dependencies
#!/usr/bin/env node
const fs = require('fs');
const https = require('https');
const path = require('path');
const checker = require('license-checker');
const semver = require('semver');
const cwd = process.cwd();
const pkgFile = fs.readFileSync(path.join(cwd, 'package.json'));
const pkg = JSON.parse(pkgFile);
const useDevDeps = process.argv.includes('--dev');
const packageLookup = useDevDeps ? pkg.devDependencies : pkg.dependencies;
checker.init({ start: cwd }, async (err, packages) => {
if (err) {
throw err;
}
// Packages format:
// { licenses: 'MIT',
// repository: 'https://github.com/archiverjs/node-zip-stream',
// publisher: 'Chris Talkington',
// url: 'http://christalkington.com/',
// path:
// '/<project-root>/node_modules/zip-stream',
// licenseFile:
// '/<project-root>/node_modules/zip-stream/LICENSE' }
const lines = await Promise.all(Object.entries(packages).map(([versionedName, info]) => {
const [name, version] = versionedName.split(/(?<=\w)@/);
if (semver.satisfies(version, packageLookup[name])) {
return getInfo(name, version, info).catch(e => {
console.error(versionedName, info, e);
process.exit(1);
});
}
}));
console.log(lines.filter(Boolean).sort().join('\n'));
});
const getInfo = async (name, version, { repository, licenses, licenseFile }) => {
const getString = (licenseUrl, status = '') => [
`${name} ${version}`,
licenseUrl,
licenses,
status,
].filter(Boolean).join('\t');
const defaultLicensePath = 'LICENSE';
const relativeLicensePath = licenseFile && licenseFile.replace(new RegExp(`^.*/node_modules/${name}/`), '');
if (!repository) {
return getString(' ', 'no repo');
}
const licenseUrls = new Set();
const addLicenseUrl = path => {
licenseUrls.add(urlJoin(repository, `blob/${version}`, path));
licenseUrls.add(urlJoin(repository, 'blob/master', path));
};
if (relativeLicensePath) {
addLicenseUrl(relativeLicensePath);
}
if (!relativeLicensePath || relativeLicensePath.match(/readme/i)) {
addLicenseUrl(defaultLicensePath);
}
for (const url of licenseUrls) {
if (await checkUrl(url)) {
return getString(url);
}
}
return getString(repository, 'no license');
};
const checkUrl = url => new Promise(resolve => {
const req = https.request(url, { method: 'HEAD' }, ({ statusCode }) => {
resolve(statusCode === 200);
});
req.on('error', (e) => {
console.error(url, e);
});
req.end();
});
const urlJoin = (...parts) => parts.map(p => p.replace(/^\/*|\/*$/g, '')).join('/');
{
"name": "license-list",
"version": "1.0.0",
"bin": "index.js",
"author": "Eugene Datsky",
"license": "ISC",
"dependencies": {
"license-checker": "^25.0.1",
"semver": "^5.6.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment