Skip to content

Commit

Permalink
Merge pull request #235 from TobyAndToby/ts/legacy-license-fields
Browse files Browse the repository at this point in the history
Update license content resolutions to handle deprecated license fields
  • Loading branch information
tobybessant authored Nov 27, 2023
2 parents cec57b7 + 91aff07 commit c139afd
Show file tree
Hide file tree
Showing 6 changed files with 728 additions and 123 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import logger from "../../utils/console.utils";
import { readFile } from "../../utils/file.utils";
import { extname } from "path";

// This file specifically handles cases where we're able to find
// a license file on disk that is a part of the package but it's
// not referenced in the package.json file.

// A 'best guess' for file extensions that are not license files
// but that may have the same name as a license file
export const extensionDenyList = [".js", ".ts", ".sh", ".ps1"];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,49 @@
import { doesFileExist, readFile } from "../../utils/file.utils";
import logger from "../../utils/console.utils";
import { join } from "path";
import { Resolution } from "../resolveLicenseContent";
import { Resolution } from "./index";
import { PackageJson, PackageJsonLicense } from "../../utils/packageJson.utils";

// This file specifically handles cases where the package.json links
// to a license file that is on disk and is a part of the package.
//
// If it instead finds a URL to a license file, it will return that URL as-is.

export const packageJsonLicense: Resolution = async inputs => {
const { packageJson, directory } = inputs;
const { license, licenses } = packageJson;

if (typeof license === "string") {
return parseStringLicense(license, directory);
}

if (Array.isArray(license)) {
return parseArrayLicense(license, packageJson);
}

if (typeof license === "object") {
return parseObjectLicense(license);
}

const spdxExpression = packageJson.license;
if (Array.isArray(licenses)) {
return parseArrayLicense(licenses, packageJson);
}

return null;
};

const parseStringLicense = async (spdxExpression: string, directory: string) => {
if (!spdxExpression) {
return null;
}

if (!spdxExpression.toLowerCase().startsWith("see license in ")) {
const lowerCaseExpression = spdxExpression.toLowerCase();

if (lowerCaseExpression.startsWith("http") || lowerCaseExpression.startsWith("www")) {
return spdxExpression;
}

if (!lowerCaseExpression.startsWith("see license in ")) {
return null;
}

Expand Down Expand Up @@ -44,3 +75,33 @@ const readLicenseFromDisk = async (dir: string, path: string): Promise<string |
return null;
}
};

const parseArrayLicense = (license: PackageJsonLicense[], packageJson: PackageJson) => {
if (license.length === 0) {
return null;
}

if (license.length === 1) {
return parseObjectLicense(license[0]);
}

const warningLines = [
`The license key for ${packageJson.name}@${packageJson.version} contains multiple licenses"`,
"We suggest you determine which license applies to your project and replace the license content",
`for ${packageJson.name}@${packageJson.version} using a generate-license-file config file.`,
"See: https://generate-license-file.js.org/docs/cli/config-file for more information.",
"", // Empty line for spacing
];

logger.warn(warningLines.join("\n"));

return parseObjectLicense(license[0]);
};

const parseObjectLicense = (license: PackageJsonLicense) => {
if (!license.url) {
return null;
}

return license.url;
};
Original file line number Diff line number Diff line change
@@ -1,12 +1,66 @@
import { Resolution } from "../resolveLicenseContent";
import { Resolution } from "./index";
import logger from "../../utils/console.utils";
import { PackageJson, PackageJsonLicense } from "../../utils/packageJson.utils";

// This file specifically handles cases where the package.json contains an SPDX license expression.

export const spdxExpression: Resolution = async input => {
const { packageJson } = input;

const expression = packageJson.license;
const { license, licenses } = packageJson;

if (typeof license === "string") {
return handleStringLicense(license, packageJson);
}

if (Array.isArray(license)) {
return handleArrayLicense(license, packageJson);
}

if (typeof license === "object") {
return handleObjectLicense(license, packageJson);
}

if (Array.isArray(licenses)) {
return handleArrayLicense(licenses, packageJson);
}

return null;
};

const handleArrayLicense = (licenses: PackageJsonLicense[], packageJson: PackageJson) => {
if (licenses.length === 0) {
return null;
}

if (licenses.length === 1) {
return handleObjectLicense(licenses[0], packageJson);
}

const warningLines = [
`The license field for ${packageJson.name}@${packageJson.version} contains multiple licenses:`,
JSON.stringify(licenses),
"We suggest you determine which license applies to your project and replace the license content",
`for ${packageJson.name}@${packageJson.version} using a generate-license-file config file.`,
"See: https://generate-license-file.js.org/docs/cli/config-file for more information.",
"", // Empty line for spacing
];

logger.warn(warningLines.join("\n"));

return handleObjectLicense(licenses[0], packageJson);
};

const handleObjectLicense = (packageJsonLicence: PackageJsonLicense, packageJson: PackageJson) => {
if (!packageJsonLicence.type) {
return null;
}

return handleStringLicense(packageJsonLicence.type, packageJson);
};

if (!expression) {
const handleStringLicense = (expression: string, packageJson: PackageJson) => {
if (expression.length === 0) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { doesFileExist, readFile } from "./file.utils";
export interface PackageJson {
name?: string;
version?: string;
license?: string;
license?: string | PackageJsonLicense | PackageJsonLicense[];
licenses?: PackageJsonLicense[];
}

export interface PackageJsonLicense {
type?: string;
url?: string;
}

export const readPackageJson = async (pathToPackageJson: string): Promise<PackageJson> => {
Expand All @@ -14,6 +20,5 @@ export const readPackageJson = async (pathToPackageJson: string): Promise<Packag

const packageJsonAsString: string = await readFile(pathToPackageJson, { encoding: "utf-8" });

const packageJson: PackageJson = JSON.parse(packageJsonAsString);
return packageJson;
return JSON.parse(packageJsonAsString);
};
Loading

0 comments on commit c139afd

Please sign in to comment.