diff --git a/README.md b/README.md index fa63ce4..648e560 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,79 @@ - [Debugging](#debugging) - [How Licenses are Found](#how-licenses-are-found) - [Related information sources on the internet](#related-information-sources-on-the-internet) - +<<< clarifications-semver +326 +  +`version` can either be an exact version or a semver range, multiple ranges are supported for a single package, for example: +327 +  +​ +328 +  +```json5 +329 +  +{ +330 +  + "package_name@^1": { +331 +  + // Any field available in customFormat can be clarified +332 +  + "licenses": "GPL", +333 +  + // ... other fields, see above +334 +  + }, +335 +  + "package_name@^2": { +336 +  + // Any field available in customFormat can be clarified +337 +  + "licenses": "MIT", +338 +  + // ... other fields, see above +339 +  + }, +340 +  +} +341 +  +``` +342 +  +​ +343 +  +For overlapping ranges, the first matching entry is used. +344 +  +​ +345 +  +The `--clarificationsMatchAll` option, when enabled, raises an error if not all specified clarifications were used, it is off by default. +346 +  +​ +347 +  + +348 +  +​ +349 +  +# ## A message from the maintainer Folks, I love and honor open software (the latter not as much as I should), and therefore I am a little ashamed of the lack of regular care I give to this project. My family (two still young kids and a wife working full-time just as me) plus my hobbies (reading - currently I read the great book "Coders at work" and plan to work my way through "Structure and interpretation of computer programs", a book many great and experienced coders say is kind of a must-read - and homebrewing) take their toll. And then there's the time I need for procrastination as well. You get the picture. @@ -249,6 +321,7 @@ before. - `--angularCli` is just a synonym for `--plainVertical` - `--clarificationsFile [filepath]` A file that describe the license clarifications for each package, see clarificationExample.json, any field available to the customFormat option can be clarified. The clarifications file can also be used to specify a subregion of a package's license file (instead reading the entire file) +- `--clarificationsMatchAll [boolean]` This optional new feature is still lacking a description - to be done - `--csv` output in csv format - `--csvComponentPrefix` prefix column for component in csv format - `--customPath` to add a custom Format file in JSON @@ -322,7 +395,30 @@ The `--clarificationsFile` option can be used to provide custom processing instr } ``` -## Custom format +`version` can either be an exact version or a semver range, multiple ranges are supported for a single package, for example: + +```json5 +{ + "package_name@^1": { + // Any field available in customFormat can be clarified + "licenses": "GPL", + // ... other fields, see above + }, + "package_name@^2": { + // Any field available in customFormat can be clarified + "licenses": "MIT", + // ... other fields, see above + }, +} +``` + +For overlapping ranges, the first matching entry is used. + +The `--clarificationsMatchAll` option, when enabled, raises an error if not all specified clarifications were used, it is off by default. + + + +## Custom format The `--customPath` option can be used with CSV to specify the columns. Note that the first column, `module_name`, will always be used. diff --git a/lib/args.js b/lib/args.js index bc90219..e9b696e 100644 --- a/lib/args.js +++ b/lib/args.js @@ -10,7 +10,8 @@ import path from 'node:path'; const knownOptions = { angularCli: Boolean, - clarificationsFile: path, + clarificationsFile: path,clarificationsMatchAll + clarificationsMatchAll: Boolean, color: Boolean, csv: Boolean, csvComponentPrefix: String, diff --git a/lib/index.js b/lib/index.js index 33ccb50..51cf51f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -20,6 +20,7 @@ import spdxCorrect from 'spdx-correct'; import spdxSatisfies from 'spdx-satisfies'; import treeify from 'treeify'; import { createHash } from 'crypto'; +import semver from 'semver'; import * as licenseCheckerHelpers from './licenseCheckerHelpers.js'; import { getLicenseTitle } from './getLicenseTitle.js'; @@ -45,8 +46,13 @@ const recursivelyCollectAllDependencies = (options) => { let licenseData; let licenseFile; let noticeFiles = []; - let clarification = options.clarifications?.[currentPackageNameAndVersion]; + const clarification = options.clarifications[currentExtendedPackageJson.name]?.find((clarification) => + currentExtendedPackageJson.version == clarification.semverRange || semver.satisfies(currentExtendedPackageJson.version, clarification.semverRange) + ); let passedClarificationCheck = clarification?.checksum ? false : true; + if (clarification) { + clarification.used = true; + } if ( // If we have processed this currentPackageNameAndVersion already, just return the data object. @@ -133,7 +139,7 @@ const recursivelyCollectAllDependencies = (options) => { ); if (licenseData) { - // License information has been collected from either the clarifiation file or from the package.json file + // License information has been collected from either the clarification file or from the package.json file /*istanbul ignore else*/ if (Array.isArray(licenseData) && licenseData.length > 0) { moduleInfo.licenses = licenseData.map((moduleLicense) => { @@ -393,10 +399,21 @@ const init = (args, callback) => { pusher = toCheckforFailOn; } - // An object mapping from Package name -> What contents it should have + // An object mapping from Package name -> list of what contents it should have, including a semver range for each entry let clarifications = {}; if (args.clarificationsFile) { - clarifications = parseJson(args.clarificationsFile); + const clarificationsFromFile = parseJson(args.clarificationsFile); + + for (const [versionString, clarification] of Object.entries(clarificationsFromFile)) { + const versionSplit = versionString.lastIndexOf('@'); + if (versionSplit !== -1) { + const name = versionString.slice(0, versionSplit); + const semverRange = versionString.slice(versionSplit + 1); + clarifications[name] ??= []; + // keep track for each clarification if it was used, optionally error when not + clarifications[name].push({...clarification, semverRange, used: false}); + } + } } if (checker && pusher) { @@ -432,7 +449,25 @@ const init = (args, callback) => { unknown: args.unknown, currentRecursionDepth: 0, clarifications, - }); + }) + + if (args.clarificationsMatchAll) { + const unusedClarifications = [] + for (const [package, entries] of Object.entries(clarifications)) { + for (const clarification of entries) { + if (!clarification.used) { + unusedClarifications.push(`${package}@${clarification.semverRange}`); + } + } + } + if (unusedClarifications.length) { + console.error( + `Some clarifications (${unusedClarifications.join(', ')}) were unused and --clarificationsMatchAll was specified. Exiting.`, + ); + + process.exit(1); + } + } const colorize = args.color; const sorted = {}; // 'sorted' will store the same items as allWantedDepthDependenciesWithVersions, but sorted by package name and version