UNPKG

@dev-build-deploy/reuse-it

Version:

(ReUSE) Copyright and License management library

234 lines (233 loc) 8.62 kB
"use strict"; /* SPDX-FileCopyrightText: 2023 Kevin de Jong <monkaii@hotmail.com> SPDX-License-Identifier: MIT */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SpdxFile = exports.DEP5_FILE_PATH = void 0; const commentIt = __importStar(require("@dev-build-deploy/comment-it")); const debian = __importStar(require("@dev-build-deploy/dep5-it")); const crypto = __importStar(require("crypto")); const fs = __importStar(require("fs")); const parser = __importStar(require("../parser")); /** @internal */ exports.DEP5_FILE_PATH = ".reuse/dep5"; const fileTypes = [ "SOURCE", "BINARY", "ARCHIVE", "APPLICATION", "AUDIO", "IMAGE", "TEXT", "VIDEO", "DOCUMENTATION", "SPDX", "OTHER", ]; /** * SPDX File * @member SPDXID The SPDX ID of the file * @member annotations Annotations * @member checksum Checksums * @member comment Comments * @member contributors Contributors * @member fileName The file name * @member fileTypes The file types * @member licenseComments License comments * @member licenseConcluded License concluded * @member licenseInfoInFiles License information in files * @member noticeText Notices * @member attributionTexts Attribution texts */ class SpdxFile { constructor(fileName) { this.checksums = []; this.fileContributors = []; // 0..* this.fileTypes = []; // 0..* this.licenseConcluded = "NOASSERTION"; this.licenseInfoInFiles = ["NOASSERTION"]; this.attributionTexts = []; // 0..* this.fileName = fileName.startsWith("./") ? fileName : `./${fileName}`; this.SPDXID = `SPDXRef-${crypto.createHash("SHA1").update(fileName).digest("hex")}`; // Generate the checksum const contents = fs.readFileSync(this.fileName, "utf-8"); this.checksums = [ { algorithm: "SHA1", checksumValue: crypto.createHash("SHA1").update(contents).digest("hex") } ]; } /** * Create a SPDX file from the provided file path, and update it with information from (in order of precedence): * * 1. The Debian Configuration file * 2. The .license file * 3. File tags provided in comment blocks in the file * * @param file The file path to create the SPDX file from * @returns The SPDX file */ static async fromFile(file) { /** * Updates the SPDX file with information the Debian Configuration file * @param file The SPDX file to update * @returns The updated SPDX file */ function parseDebianFile(file) { const dep5 = debian.DebianCopyright.fromFile(exports.DEP5_FILE_PATH); if (dep5.header.copyright) { file.copyrightText = dep5.header.copyright; } if (dep5.header.license) { file.licenseInfoInFiles = [dep5.header.license]; } const stanza = dep5.getFileStanza(file.fileName.replace("./", "")); if (stanza) { if (stanza.copyright) { file.copyrightText = stanza.copyright; } if (stanza.license) { file.licenseInfoInFiles = [stanza.license]; } } return file; } /** * Update the SPDX file with information provided in the plain text file * @param file The SPDX file to update * @returns The updated SPDX file */ async function parsePlainFile(source, file) { const content = fs.readFileSync(source, "utf-8"); const comment = { type: "multiline", format: { start: "", end: "" }, contents: [] }; let index = 0; for (const line of content.split("\n")) { comment.contents.push({ line: index, column: { start: 0, end: line.length }, value: line }); index += 1; } let ignore = false; for await (const token of parser.extractData(comment)) { if (token.type === "ignore") { ignore = token.data.startsWith("Start"); } if (ignore) continue; file = await updateFromToken(token, file); } return file; } /** * Updates the SPDX file with information provided as File tags * @param file The SPDX file to update * @returns The updated SPDX file */ async function parseFile(file) { let ignore = false; for await (const comment of commentIt.extractComments(file.fileName)) { for await (const token of parser.extractData(comment)) { if (token.type === "ignore") { ignore = token.data.startsWith("Start"); } if (ignore) continue; file = await updateFromToken(token, file); } } return file; } /** * Parses the SPDX File and ReUSE tokens from the provided comment * @param comment Comment to parse * @param file SPDX File to update * @returns The updated SPDX file */ async function updateFromToken(token, file) { switch (token.type) { case "attributionText": file.attributionTexts.push(token.data); break; case "comment": file.comment = token.data; break; case "contributor": file.fileContributors.push(token.data); break; case "licenseComments": file.licenseComments = token.data; break; case "licenseConcluded": file.licenseConcluded = token.data; break; case "copyright": file.copyrightText = token.data; break; case "notice": file.noticeText = token.data; break; case "type": file.fileTypes.push(token.data); break; case "licenseInfoInFile": case "license": { if (file.licenseInfoInFiles.includes("NOASSERTION")) { file.licenseInfoInFiles = [token.data]; } else { file.licenseInfoInFiles.push(token.data); } break; } } return file; } let spdxFile = new SpdxFile(file); if (fs.existsSync(exports.DEP5_FILE_PATH)) { spdxFile = parseDebianFile(spdxFile); } if (fs.existsSync(`${file}.license`)) { spdxFile = await parsePlainFile(`${file}.license`, spdxFile); } if (commentIt.isSupported(file)) { spdxFile = await parseFile(spdxFile); } else { // Fallback to plain text file parsing in case we cannot parse the file comment blocks. spdxFile = await parsePlainFile(file, spdxFile); } return spdxFile; } } exports.SpdxFile = SpdxFile;