Skip to content

Commit

Permalink
Enhancing how the SPDX ID is extracted from package.json
Browse files Browse the repository at this point in the history
* hardened the extraction process so it can deal with old packages that do not follow the new “license” field format
* expanding the output to include individual parts of the computed “license” output (licenseId, licenseFullName and licenseFilePath)
* including module path output when used as a module
  • Loading branch information
morficus committed Jun 25, 2019
1 parent d4e7027 commit 20842ec
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 3 deletions.
2 changes: 1 addition & 1 deletion cli-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = {
include: {
description: 'List of properties to include',
type: 'array',
choices: ['id', 'name', 'version', 'license', 'repository', 'author', 'homepage', 'path', 'dependencyLevel'],
choices: ['id', 'name', 'version', 'license', 'licenseId', 'licenseFullName', 'licenseFilePath', 'path', 'repository', 'author', 'homepage', 'path', 'dependencyLevel'],
default: ['id', 'name', 'version', 'license', 'repository', 'author', 'homepage', 'dependencyLevel']
},
production: {
Expand Down
Empty file modified cli.js
100644 → 100755
Empty file.
3 changes: 3 additions & 0 deletions formatters/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ module.exports = function ({data, header}) {
id: 'Row #',
name: 'Package Name',
version: 'Version',
licenseId: 'SPDX ID',
licenseFullName: 'SPDX Full Name',
licenseFilePath: 'Path to license file',
license: 'License',
homepage: 'Homepage',
repository: 'Repository',
Expand Down
26 changes: 26 additions & 0 deletions helpers/extract-license-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const { isString, isObject, isArray, compact } = require('lodash')

/**
* Deal with all the crazy stuff the "license" field in package.json can have and return only the SPDX ID (if any)
*
* @param {*} license
* @returns {string}
*/
module.exports = function extractLicenseText(license) {
let licenseText
if (isString(license)) {
licenseText = license

} else if (isArray(license)) {
const text = license.map(extractLicenseText)
licenseText = compact(text).join(' AND ')

} else if (isObject(license)) {
licenseText = license.type

} else {
licenseText = ''
}

return licenseText
}
2 changes: 1 addition & 1 deletion helpers/get-spdx-full-name.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ function traverse(obj) {
*/
module.exports = function (identifier = '') {

const normalised = identifier.toUpperCase()
try {
const normalised = identifier.toUpperCase()
let expandedText = ''
if (normalised === 'UNLICENSED') {
expandedText = 'Proprietary License'
Expand Down
13 changes: 12 additions & 1 deletion lib.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const promisify = require('util').promisify
const npmLs = require('./helpers/npm-list')
const getPackageDetails = require('./helpers/get-package-details')
const getExpandedLicName = require('./helpers/get-spdx-full-name')
const extractLicenseText = require('./helpers/extract-license-id')
const glob = promisify(require('glob'))

/**
* Get a list of licenses for any installed project dependencies
Expand All @@ -11,12 +14,20 @@ module.exports = async function (options = {}) {
const pathList = await npmLs(options)
return await Promise.all(pathList.map(async (path, index) => {
const pkg = await getPackageDetails(path)
const licShortName = pkg.license
const licShortName = extractLicenseText(pkg.license || pkg.licenses)
const licLongName = getExpandedLicName(licShortName) || 'unknown'

// find any local licences files and build a path to them
const licFilePath = await glob('+(license**|licence**)', {cwd: path, nocase: true, nodir: true})
.then(files => files.map(file => `${path}/${file}`))

return {
id: index,
name: pkg.name,
version: pkg.version,
licenseId: licShortName,
licenseFullName: licLongName,
licenseFilePath: licFilePath || [],
license: `${licLongName} (${licShortName})`,
repository: (pkg.repository || {}).url,
author: (pkg.author || {}).name,
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
"author": "Maurice Williams <https://github.com/morficus>",
"license": "MIT",
"dependencies": {
"glob": "^7.1.4",
"json2csv": "^4.5.1",
"jstoxml": "^1.5.0",
"libnpm": "^2.0.1",
"lodash": "^4.17.11",
"ora": "^3.4.0",
"read-package-tree": "^5.2.2",
"spdx-expression-parse": "^3.0.0",
Expand Down
50 changes: 50 additions & 0 deletions tests/extract-license-id.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const extractLicenseText = require('../helpers/extract-license-id')
const test = require('ava')

test('Can deal with normal things... like text', t => {
const payload = 'MIT'
const expected = 'MIT'
const actual = extractLicenseText(payload)

t.is(actual, expected)
})

test('Can deal with objects', t => {
const payload = { type: 'MIT', url: 'some-some' }
const expected = 'MIT'
const actual = extractLicenseText(payload)

t.is(actual, expected)
})

test('Can deal with an array with a single object', t => {
const payload = [{ type: 'MIT', url: 'some-some' }]
const expected = 'MIT'
const actual = extractLicenseText(payload)

t.is(actual, expected)
})

test('Can deal with an array with multiple objects', t => {
const payload = [{ type: 'MIT', url: 'some-some' }, { type: 'ISC', url: 'some-some' }]
const expected = 'MIT AND ISC'
const actual = extractLicenseText(payload)

t.is(actual, expected)
})

test('Can deal with funky arrays', t => {
const payload = [1, '', 'MIT']
const expected = 'MIT'
const actual = extractLicenseText(payload)

t.is(actual, expected)
})

test('Can deal with REALLY funky arrays', t => {
const payload = [1, '', 'MIT', { type: 'ISC', url: 'some-url' }]
const expected = 'MIT AND ISC'
const actual = extractLicenseText(payload)

t.is(actual, expected)
})
10 changes: 10 additions & 0 deletions tests/get-spdx-full-name.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,14 @@ test('Can handle expressions with "+" in complex expressions', (t) => {
const actual = getLongName(payload)

t.is(actual, expected)
})

test('Does not blow up if given a non-string value', (t) => {
[null, undefined, 42, ['test'], {test: 'hello'}].forEach(payload => {
let expected = ''
let actual = getLongName(payload)

t.is(actual, expected)
})

})

0 comments on commit 20842ec

Please sign in to comment.