| /* |
| Copyright spdx-correct.js contributors |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| var parse = require('spdx-expression-parse') |
| var spdxLicenseIds = require('spdx-license-ids') |
| |
| function valid (string) { |
| try { |
| parse(string) |
| return true |
| } catch (error) { |
| return false |
| } |
| } |
| |
| // Common transpositions of license identifier acronyms |
| var transpositions = [ |
| ['APGL', 'AGPL'], |
| ['Gpl', 'GPL'], |
| ['GLP', 'GPL'], |
| ['APL', 'Apache'], |
| ['ISD', 'ISC'], |
| ['GLP', 'GPL'], |
| ['IST', 'ISC'], |
| ['Claude', 'Clause'], |
| [' or later', '+'], |
| [' International', ''], |
| ['GNU', 'GPL'], |
| ['GUN', 'GPL'], |
| ['+', ''], |
| ['GNU GPL', 'GPL'], |
| ['GNU/GPL', 'GPL'], |
| ['GNU GLP', 'GPL'], |
| ['GNU General Public License', 'GPL'], |
| ['Gnu public license', 'GPL'], |
| ['GNU Public License', 'GPL'], |
| ['GNU GENERAL PUBLIC LICENSE', 'GPL'], |
| ['MTI', 'MIT'], |
| ['Mozilla Public License', 'MPL'], |
| ['WTH', 'WTF'], |
| ['-License', ''] |
| ] |
| |
| var TRANSPOSED = 0 |
| var CORRECT = 1 |
| |
| // Simple corrections to nearly valid identifiers. |
| var transforms = [ |
| // e.g. 'mit' |
| function (argument) { |
| return argument.toUpperCase() |
| }, |
| // e.g. 'MIT ' |
| function (argument) { |
| return argument.trim() |
| }, |
| // e.g. 'M.I.T.' |
| function (argument) { |
| return argument.replace(/\./g, '') |
| }, |
| // e.g. 'Apache- 2.0' |
| function (argument) { |
| return argument.replace(/\s+/g, '') |
| }, |
| // e.g. 'CC BY 4.0'' |
| function (argument) { |
| return argument.replace(/\s+/g, '-') |
| }, |
| // e.g. 'LGPLv2.1' |
| function (argument) { |
| return argument.replace('v', '-') |
| }, |
| // e.g. 'Apache 2.0' |
| function (argument) { |
| return argument.replace(/,?\s*(\d)/, '-$1') |
| }, |
| // e.g. 'GPL 2' |
| function (argument) { |
| return argument.replace(/,?\s*(\d)/, '-$1.0') |
| }, |
| // e.g. 'Apache Version 2.0' |
| function (argument) { |
| return argument |
| .replace(/,?\s*(V\.|v\.|V|v|Version|version)\s*(\d)/, '-$2') |
| }, |
| // e.g. 'Apache Version 2' |
| function (argument) { |
| return argument |
| .replace(/,?\s*(V\.|v\.|V|v|Version|version)\s*(\d)/, '-$2.0') |
| }, |
| // e.g. 'ZLIB' |
| function (argument) { |
| return argument[0].toUpperCase() + argument.slice(1) |
| }, |
| // e.g. 'MPL/2.0' |
| function (argument) { |
| return argument.replace('/', '-') |
| }, |
| // e.g. 'Apache 2' |
| function (argument) { |
| return argument |
| .replace(/\s*V\s*(\d)/, '-$1') |
| .replace(/(\d)$/, '$1.0') |
| }, |
| // e.g. 'GPL-2.0', 'GPL-3.0' |
| function (argument) { |
| if (argument.indexOf('3.0') !== -1) { |
| return argument + '-or-later' |
| } else { |
| return argument + '-only' |
| } |
| }, |
| // e.g. 'GPL-2.0-' |
| function (argument) { |
| return argument + 'only' |
| }, |
| // e.g. 'GPL2' |
| function (argument) { |
| return argument.replace(/(\d)$/, '-$1.0') |
| }, |
| // e.g. 'BSD 3' |
| function (argument) { |
| return argument.replace(/(-| )?(\d)$/, '-$2-Clause') |
| }, |
| // e.g. 'BSD clause 3' |
| function (argument) { |
| return argument.replace(/(-| )clause(-| )(\d)/, '-$3-Clause') |
| }, |
| // e.g. 'BY-NC-4.0' |
| function (argument) { |
| return 'CC-' + argument |
| }, |
| // e.g. 'BY-NC' |
| function (argument) { |
| return 'CC-' + argument + '-4.0' |
| }, |
| // e.g. 'Attribution-NonCommercial' |
| function (argument) { |
| return argument |
| .replace('Attribution', 'BY') |
| .replace('NonCommercial', 'NC') |
| .replace('NoDerivatives', 'ND') |
| .replace(/ (\d)/, '-$1') |
| .replace(/ ?International/, '') |
| }, |
| // e.g. 'Attribution-NonCommercial' |
| function (argument) { |
| return 'CC-' + |
| argument |
| .replace('Attribution', 'BY') |
| .replace('NonCommercial', 'NC') |
| .replace('NoDerivatives', 'ND') |
| .replace(/ (\d)/, '-$1') |
| .replace(/ ?International/, '') + |
| '-4.0' |
| } |
| ] |
| |
| var licensesWithVersions = spdxLicenseIds |
| .map(function (id) { |
| var match = /^(.*)-\d+\.\d+$/.exec(id) |
| return match |
| ? [match[0], match[1]] |
| : [id, null] |
| }) |
| .reduce(function (objectMap, item) { |
| var key = item[1] |
| objectMap[key] = objectMap[key] || [] |
| objectMap[key].push(item[0]) |
| return objectMap |
| }, {}) |
| |
| var licensesWithOneVersion = Object.keys(licensesWithVersions) |
| .map(function makeEntries (key) { |
| return [key, licensesWithVersions[key]] |
| }) |
| .filter(function identifySoleVersions (item) { |
| return ( |
| // Licenses has just one valid version suffix. |
| item[1].length === 1 && |
| item[0] !== null && |
| // APL will be considered Apache, rather than APL-1.0 |
| item[0] !== 'APL' |
| ) |
| }) |
| .map(function createLastResorts (item) { |
| return [item[0], item[1][0]] |
| }) |
| |
| licensesWithVersions = undefined |
| |
| // If all else fails, guess that strings containing certain substrings |
| // meant to identify certain licenses. |
| var lastResorts = [ |
| ['UNLI', 'Unlicense'], |
| ['WTF', 'WTFPL'], |
| ['2 CLAUSE', 'BSD-2-Clause'], |
| ['2-CLAUSE', 'BSD-2-Clause'], |
| ['3 CLAUSE', 'BSD-3-Clause'], |
| ['3-CLAUSE', 'BSD-3-Clause'], |
| ['AFFERO', 'AGPL-3.0-or-later'], |
| ['AGPL', 'AGPL-3.0-or-later'], |
| ['APACHE', 'Apache-2.0'], |
| ['ARTISTIC', 'Artistic-2.0'], |
| ['Affero', 'AGPL-3.0-or-later'], |
| ['BEER', 'Beerware'], |
| ['BOOST', 'BSL-1.0'], |
| ['BSD', 'BSD-2-Clause'], |
| ['CDDL', 'CDDL-1.1'], |
| ['ECLIPSE', 'EPL-1.0'], |
| ['FUCK', 'WTFPL'], |
| ['GNU', 'GPL-3.0-or-later'], |
| ['LGPL', 'LGPL-3.0-or-later'], |
| ['GPLV1', 'GPL-1.0-only'], |
| ['GPL-1', 'GPL-1.0-only'], |
| ['GPLV2', 'GPL-2.0-only'], |
| ['GPL-2', 'GPL-2.0-only'], |
| ['GPL', 'GPL-3.0-or-later'], |
| ['MIT +NO-FALSE-ATTRIBS', 'MITNFA'], |
| ['MIT', 'MIT'], |
| ['MPL', 'MPL-2.0'], |
| ['X11', 'X11'], |
| ['ZLIB', 'Zlib'] |
| ].concat(licensesWithOneVersion) |
| |
| var SUBSTRING = 0 |
| var IDENTIFIER = 1 |
| |
| var validTransformation = function (identifier) { |
| for (var i = 0; i < transforms.length; i++) { |
| var transformed = transforms[i](identifier).trim() |
| if (transformed !== identifier && valid(transformed)) { |
| return transformed |
| } |
| } |
| return null |
| } |
| |
| var validLastResort = function (identifier) { |
| var upperCased = identifier.toUpperCase() |
| for (var i = 0; i < lastResorts.length; i++) { |
| var lastResort = lastResorts[i] |
| if (upperCased.indexOf(lastResort[SUBSTRING]) > -1) { |
| return lastResort[IDENTIFIER] |
| } |
| } |
| return null |
| } |
| |
| var anyCorrection = function (identifier, check) { |
| for (var i = 0; i < transpositions.length; i++) { |
| var transposition = transpositions[i] |
| var transposed = transposition[TRANSPOSED] |
| if (identifier.indexOf(transposed) > -1) { |
| var corrected = identifier.replace( |
| transposed, |
| transposition[CORRECT] |
| ) |
| var checked = check(corrected) |
| if (checked !== null) { |
| return checked |
| } |
| } |
| } |
| return null |
| } |
| |
| module.exports = function (identifier, options) { |
| options = options || {} |
| var upgrade = options.upgrade === undefined ? true : !!options.upgrade |
| function postprocess (value) { |
| return upgrade ? upgradeGPLs(value) : value |
| } |
| var validArugment = ( |
| typeof identifier === 'string' && |
| identifier.trim().length !== 0 |
| ) |
| if (!validArugment) { |
| throw Error('Invalid argument. Expected non-empty string.') |
| } |
| identifier = identifier.trim() |
| if (valid(identifier)) { |
| return postprocess(identifier) |
| } |
| var noPlus = identifier.replace(/\+$/, '').trim() |
| if (valid(noPlus)) { |
| return postprocess(noPlus) |
| } |
| var transformed = validTransformation(identifier) |
| if (transformed !== null) { |
| return postprocess(transformed) |
| } |
| transformed = anyCorrection(identifier, function (argument) { |
| if (valid(argument)) { |
| return argument |
| } |
| return validTransformation(argument) |
| }) |
| if (transformed !== null) { |
| return postprocess(transformed) |
| } |
| transformed = validLastResort(identifier) |
| if (transformed !== null) { |
| return postprocess(transformed) |
| } |
| transformed = anyCorrection(identifier, validLastResort) |
| if (transformed !== null) { |
| return postprocess(transformed) |
| } |
| return null |
| } |
| |
| function upgradeGPLs (value) { |
| if ([ |
| 'GPL-1.0', 'LGPL-1.0', 'AGPL-1.0', |
| 'GPL-2.0', 'LGPL-2.0', 'AGPL-2.0', |
| 'LGPL-2.1' |
| ].indexOf(value) !== -1) { |
| return value + '-only' |
| } else if ([ |
| 'GPL-1.0+', 'GPL-2.0+', 'GPL-3.0+', |
| 'LGPL-2.0+', 'LGPL-2.1+', 'LGPL-3.0+', |
| 'AGPL-1.0+', 'AGPL-3.0+' |
| ].indexOf(value) !== -1) { |
| return value.replace(/\+$/, '-or-later') |
| } else if (['GPL-3.0', 'LGPL-3.0', 'AGPL-3.0'].indexOf(value) !== -1) { |
| return value + '-or-later' |
| } else { |
| return value |
| } |
| } |