|
#!/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('/'); |