1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | var parse = require('spdx-expression-parse')
|
17 | var spdxLicenseIds = require('spdx-license-ids')
|
18 |
|
19 | function valid (string) {
|
20 | try {
|
21 | parse(string)
|
22 | return true
|
23 | } catch (error) {
|
24 | return false
|
25 | }
|
26 | }
|
27 |
|
28 |
|
29 | var transpositions = [
|
30 | ['APGL', 'AGPL'],
|
31 | ['Gpl', 'GPL'],
|
32 | ['GLP', 'GPL'],
|
33 | ['APL', 'Apache'],
|
34 | ['ISD', 'ISC'],
|
35 | ['GLP', 'GPL'],
|
36 | ['IST', 'ISC'],
|
37 | ['Claude', 'Clause'],
|
38 | [' or later', '+'],
|
39 | [' International', ''],
|
40 | ['GNU', 'GPL'],
|
41 | ['GUN', 'GPL'],
|
42 | ['+', ''],
|
43 | ['GNU GPL', 'GPL'],
|
44 | ['GNU/GPL', 'GPL'],
|
45 | ['GNU GLP', 'GPL'],
|
46 | ['GNU General Public License', 'GPL'],
|
47 | ['Gnu public license', 'GPL'],
|
48 | ['GNU Public License', 'GPL'],
|
49 | ['GNU GENERAL PUBLIC LICENSE', 'GPL'],
|
50 | ['MTI', 'MIT'],
|
51 | ['Mozilla Public License', 'MPL'],
|
52 | ['Universal Permissive License', 'UPL'],
|
53 | ['WTH', 'WTF'],
|
54 | ['-License', '']
|
55 | ]
|
56 |
|
57 | var TRANSPOSED = 0
|
58 | var CORRECT = 1
|
59 |
|
60 |
|
61 | var transforms = [
|
62 |
|
63 | function (argument) {
|
64 | return argument.toUpperCase()
|
65 | },
|
66 |
|
67 | function (argument) {
|
68 | return argument.trim()
|
69 | },
|
70 |
|
71 | function (argument) {
|
72 | return argument.replace(/\./g, '')
|
73 | },
|
74 |
|
75 | function (argument) {
|
76 | return argument.replace(/\s+/g, '')
|
77 | },
|
78 |
|
79 | function (argument) {
|
80 | return argument.replace(/\s+/g, '-')
|
81 | },
|
82 |
|
83 | function (argument) {
|
84 | return argument.replace('v', '-')
|
85 | },
|
86 |
|
87 | function (argument) {
|
88 | return argument.replace(/,?\s*(\d)/, '-$1')
|
89 | },
|
90 |
|
91 | function (argument) {
|
92 | return argument.replace(/,?\s*(\d)/, '-$1.0')
|
93 | },
|
94 |
|
95 | function (argument) {
|
96 | return argument
|
97 | .replace(/,?\s*(V\.|v\.|V|v|Version|version)\s*(\d)/, '-$2')
|
98 | },
|
99 |
|
100 | function (argument) {
|
101 | return argument
|
102 | .replace(/,?\s*(V\.|v\.|V|v|Version|version)\s*(\d)/, '-$2.0')
|
103 | },
|
104 |
|
105 | function (argument) {
|
106 | return argument[0].toUpperCase() + argument.slice(1)
|
107 | },
|
108 |
|
109 | function (argument) {
|
110 | return argument.replace('/', '-')
|
111 | },
|
112 |
|
113 | function (argument) {
|
114 | return argument
|
115 | .replace(/\s*V\s*(\d)/, '-$1')
|
116 | .replace(/(\d)$/, '$1.0')
|
117 | },
|
118 |
|
119 | function (argument) {
|
120 | if (argument.indexOf('3.0') !== -1) {
|
121 | return argument + '-or-later'
|
122 | } else {
|
123 | return argument + '-only'
|
124 | }
|
125 | },
|
126 |
|
127 | function (argument) {
|
128 | return argument + 'only'
|
129 | },
|
130 |
|
131 | function (argument) {
|
132 | return argument.replace(/(\d)$/, '-$1.0')
|
133 | },
|
134 |
|
135 | function (argument) {
|
136 | return argument.replace(/(-| )?(\d)$/, '-$2-Clause')
|
137 | },
|
138 |
|
139 | function (argument) {
|
140 | return argument.replace(/(-| )clause(-| )(\d)/, '-$3-Clause')
|
141 | },
|
142 |
|
143 | function (argument) {
|
144 | return argument.replace(/\b(Modified|New|Revised)(-| )?BSD((-| )License)?/i, 'BSD-3-Clause')
|
145 | },
|
146 |
|
147 | function (argument) {
|
148 | return argument.replace(/\bSimplified(-| )?BSD((-| )License)?/i, 'BSD-2-Clause')
|
149 | },
|
150 |
|
151 | function (argument) {
|
152 | return argument.replace(/\b(Free|Net)(-| )?BSD((-| )License)?/i, 'BSD-2-Clause-$1BSD')
|
153 | },
|
154 |
|
155 | function (argument) {
|
156 | return argument.replace(/\bClear(-| )?BSD((-| )License)?/i, 'BSD-3-Clause-Clear')
|
157 | },
|
158 |
|
159 | function (argument) {
|
160 | return argument.replace(/\b(Old|Original)(-| )?BSD((-| )License)?/i, 'BSD-4-Clause')
|
161 | },
|
162 |
|
163 | function (argument) {
|
164 | return 'CC-' + argument
|
165 | },
|
166 |
|
167 | function (argument) {
|
168 | return 'CC-' + argument + '-4.0'
|
169 | },
|
170 |
|
171 | function (argument) {
|
172 | return argument
|
173 | .replace('Attribution', 'BY')
|
174 | .replace('NonCommercial', 'NC')
|
175 | .replace('NoDerivatives', 'ND')
|
176 | .replace(/ (\d)/, '-$1')
|
177 | .replace(/ ?International/, '')
|
178 | },
|
179 |
|
180 | function (argument) {
|
181 | return 'CC-' +
|
182 | argument
|
183 | .replace('Attribution', 'BY')
|
184 | .replace('NonCommercial', 'NC')
|
185 | .replace('NoDerivatives', 'ND')
|
186 | .replace(/ (\d)/, '-$1')
|
187 | .replace(/ ?International/, '') +
|
188 | '-4.0'
|
189 | }
|
190 | ]
|
191 |
|
192 | var licensesWithVersions = spdxLicenseIds
|
193 | .map(function (id) {
|
194 | var match = /^(.*)-\d+\.\d+$/.exec(id)
|
195 | return match
|
196 | ? [match[0], match[1]]
|
197 | : [id, null]
|
198 | })
|
199 | .reduce(function (objectMap, item) {
|
200 | var key = item[1]
|
201 | objectMap[key] = objectMap[key] || []
|
202 | objectMap[key].push(item[0])
|
203 | return objectMap
|
204 | }, {})
|
205 |
|
206 | var licensesWithOneVersion = Object.keys(licensesWithVersions)
|
207 | .map(function makeEntries (key) {
|
208 | return [key, licensesWithVersions[key]]
|
209 | })
|
210 | .filter(function identifySoleVersions (item) {
|
211 | return (
|
212 |
|
213 | item[1].length === 1 &&
|
214 | item[0] !== null &&
|
215 |
|
216 | item[0] !== 'APL'
|
217 | )
|
218 | })
|
219 | .map(function createLastResorts (item) {
|
220 | return [item[0], item[1][0]]
|
221 | })
|
222 |
|
223 | licensesWithVersions = undefined
|
224 |
|
225 |
|
226 |
|
227 | var lastResorts = [
|
228 | ['UNLI', 'Unlicense'],
|
229 | ['WTF', 'WTFPL'],
|
230 | ['2 CLAUSE', 'BSD-2-Clause'],
|
231 | ['2-CLAUSE', 'BSD-2-Clause'],
|
232 | ['3 CLAUSE', 'BSD-3-Clause'],
|
233 | ['3-CLAUSE', 'BSD-3-Clause'],
|
234 | ['AFFERO', 'AGPL-3.0-or-later'],
|
235 | ['AGPL', 'AGPL-3.0-or-later'],
|
236 | ['APACHE', 'Apache-2.0'],
|
237 | ['ARTISTIC', 'Artistic-2.0'],
|
238 | ['Affero', 'AGPL-3.0-or-later'],
|
239 | ['BEER', 'Beerware'],
|
240 | ['BOOST', 'BSL-1.0'],
|
241 | ['BSD', 'BSD-2-Clause'],
|
242 | ['CDDL', 'CDDL-1.1'],
|
243 | ['ECLIPSE', 'EPL-1.0'],
|
244 | ['FUCK', 'WTFPL'],
|
245 | ['GNU', 'GPL-3.0-or-later'],
|
246 | ['LGPL', 'LGPL-3.0-or-later'],
|
247 | ['GPLV1', 'GPL-1.0-only'],
|
248 | ['GPL-1', 'GPL-1.0-only'],
|
249 | ['GPLV2', 'GPL-2.0-only'],
|
250 | ['GPL-2', 'GPL-2.0-only'],
|
251 | ['GPL', 'GPL-3.0-or-later'],
|
252 | ['MIT +NO-FALSE-ATTRIBS', 'MITNFA'],
|
253 | ['MIT', 'MIT'],
|
254 | ['MPL', 'MPL-2.0'],
|
255 | ['X11', 'X11'],
|
256 | ['ZLIB', 'Zlib']
|
257 | ].concat(licensesWithOneVersion)
|
258 |
|
259 | var SUBSTRING = 0
|
260 | var IDENTIFIER = 1
|
261 |
|
262 | var validTransformation = function (identifier) {
|
263 | for (var i = 0; i < transforms.length; i++) {
|
264 | var transformed = transforms[i](identifier).trim()
|
265 | if (transformed !== identifier && valid(transformed)) {
|
266 | return transformed
|
267 | }
|
268 | }
|
269 | return null
|
270 | }
|
271 |
|
272 | var validLastResort = function (identifier) {
|
273 | var upperCased = identifier.toUpperCase()
|
274 | for (var i = 0; i < lastResorts.length; i++) {
|
275 | var lastResort = lastResorts[i]
|
276 | if (upperCased.indexOf(lastResort[SUBSTRING]) > -1) {
|
277 | return lastResort[IDENTIFIER]
|
278 | }
|
279 | }
|
280 | return null
|
281 | }
|
282 |
|
283 | var anyCorrection = function (identifier, check) {
|
284 | for (var i = 0; i < transpositions.length; i++) {
|
285 | var transposition = transpositions[i]
|
286 | var transposed = transposition[TRANSPOSED]
|
287 | if (identifier.indexOf(transposed) > -1) {
|
288 | var corrected = identifier.replace(
|
289 | transposed,
|
290 | transposition[CORRECT]
|
291 | )
|
292 | var checked = check(corrected)
|
293 | if (checked !== null) {
|
294 | return checked
|
295 | }
|
296 | }
|
297 | }
|
298 | return null
|
299 | }
|
300 |
|
301 | module.exports = function (identifier, options) {
|
302 | options = options || {}
|
303 | var upgrade = options.upgrade === undefined ? true : !!options.upgrade
|
304 | function postprocess (value) {
|
305 | return upgrade ? upgradeGPLs(value) : value
|
306 | }
|
307 | var validArugment = (
|
308 | typeof identifier === 'string' &&
|
309 | identifier.trim().length !== 0
|
310 | )
|
311 | if (!validArugment) {
|
312 | throw Error('Invalid argument. Expected non-empty string.')
|
313 | }
|
314 | identifier = identifier.trim()
|
315 | if (valid(identifier)) {
|
316 | return postprocess(identifier)
|
317 | }
|
318 | var noPlus = identifier.replace(/\+$/, '').trim()
|
319 | if (valid(noPlus)) {
|
320 | return postprocess(noPlus)
|
321 | }
|
322 | var transformed = validTransformation(identifier)
|
323 | if (transformed !== null) {
|
324 | return postprocess(transformed)
|
325 | }
|
326 | transformed = anyCorrection(identifier, function (argument) {
|
327 | if (valid(argument)) {
|
328 | return argument
|
329 | }
|
330 | return validTransformation(argument)
|
331 | })
|
332 | if (transformed !== null) {
|
333 | return postprocess(transformed)
|
334 | }
|
335 | transformed = validLastResort(identifier)
|
336 | if (transformed !== null) {
|
337 | return postprocess(transformed)
|
338 | }
|
339 | transformed = anyCorrection(identifier, validLastResort)
|
340 | if (transformed !== null) {
|
341 | return postprocess(transformed)
|
342 | }
|
343 | return null
|
344 | }
|
345 |
|
346 | function upgradeGPLs (value) {
|
347 | if ([
|
348 | 'GPL-1.0', 'LGPL-1.0', 'AGPL-1.0',
|
349 | 'GPL-2.0', 'LGPL-2.0', 'AGPL-2.0',
|
350 | 'LGPL-2.1'
|
351 | ].indexOf(value) !== -1) {
|
352 | return value + '-only'
|
353 | } else if ([
|
354 | 'GPL-1.0+', 'GPL-2.0+', 'GPL-3.0+',
|
355 | 'LGPL-2.0+', 'LGPL-2.1+', 'LGPL-3.0+',
|
356 | 'AGPL-1.0+', 'AGPL-3.0+'
|
357 | ].indexOf(value) !== -1) {
|
358 | return value.replace(/\+$/, '-or-later')
|
359 | } else if (['GPL-3.0', 'LGPL-3.0', 'AGPL-3.0'].indexOf(value) !== -1) {
|
360 | return value + '-or-later'
|
361 | } else {
|
362 | return value
|
363 | }
|
364 | }
|