diff --git a/index.js b/index.js index 52fab56..040e26a 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,6 @@ var scan = require('./scan') var parse = require('./parse') -module.exports = function (source) { - return parse(scan(source)) +module.exports = function (source, options) { + return parse(scan(source), options) } diff --git a/parse.js b/parse.js index a4a52ce..4b36e47 100644 --- a/parse.js +++ b/parse.js @@ -1,11 +1,19 @@ 'use strict' +var licenses = [] + .concat(require('spdx-license-ids')) + .concat(require('spdx-license-ids/deprecated')) +var exceptions = require('spdx-exceptions') + // The ABNF grammar in the spec is totally ambiguous. // // This parser follows the operator precedence defined in the // `Order of Precedence and Parentheses` section. - -module.exports = function (tokens) { +// +// options: +// - Set `relaxed` to `true` to accept invalid license or exception IDs. +module.exports = function (tokens, options) { + options = options || {} var index = 0 function hasMore () { @@ -34,7 +42,10 @@ module.exports = function (tokens) { function parseWith () { if (parseOperator('WITH')) { var t = token() - if (t && t.type === 'EXCEPTION') { + if (t && t.type === 'IDENTIFIER') { + if (!options.relaxed && exceptions.indexOf(t.string) === -1) { + throw new Error('`' + t.string + '` is not a valid exception name') + } next() return t.string } @@ -67,8 +78,14 @@ module.exports = function (tokens) { function parseLicense () { var t = token() - if (t && t.type === 'LICENSE') { + if (t && t.type === 'IDENTIFIER') { next() + if (licenses.indexOf(t.string) === -1) { + if (options.relaxed) { + return {noassertion: t.string} + } + throw new Error('`' + t.string + '` is not a valid license name') + } var node = {license: t.string} if (parseOperator('+')) { node.plus = true diff --git a/scan.js b/scan.js index d0567f4..a146d19 100644 --- a/scan.js +++ b/scan.js @@ -1,10 +1,5 @@ 'use strict' -var licenses = [] - .concat(require('spdx-license-ids')) - .concat(require('spdx-license-ids/deprecated')) -var exceptions = require('spdx-exceptions') - module.exports = function (source) { var index = 0 @@ -82,22 +77,11 @@ module.exports = function (source) { } function identifier () { - var begin = index var string = idstring() - - if (licenses.indexOf(string) !== -1) { - return { - type: 'LICENSE', - string: string - } - } else if (exceptions.indexOf(string) !== -1) { - return { - type: 'EXCEPTION', - string: string - } + return string && { + type: 'IDENTIFIER', + string: string } - - index = begin } // Tries to read the next token. Returns `undefined` if no token is diff --git a/test/index.js b/test/index.js index 46318fa..6688f97 100644 --- a/test/index.js +++ b/test/index.js @@ -83,3 +83,36 @@ it('parses `AND`, `OR` and `WITH` with the correct precedence', function () { } ) }) + +it('rejects invalid license and exception names by default', function () { + assert.throws( + function () { p('unknownLicense') }, + /`unknownLicense` is not a valid license name/ + ) + + assert.throws( + function () { p('MIT WITH unknownException') }, + /`unknownException` is not a valid exception name/ + ) +}) + +it('accepts invalid license and exception names in relaxed mode', function () { + assert.deepEqual( + p('unknownLicense', {relaxed: true}), + {noassertion: 'unknownLicense'} + ) + + assert.deepEqual( + p('MIT WITH unknownException', {relaxed: true}), + {license: 'MIT', exception: 'unknownException'} + ) + + assert.deepEqual( + p('MIT OR Commercial', {relaxed: true}), + { + left: {license: 'MIT'}, + conjunction: 'or', + right: {noassertion: 'Commercial'} + } + ) +})