Spaces:
Running
Running
Download scripts/license-check.cjs from Felladrin/MiniSearch: direct link, hf CLI and curl.
- Browser
- Download file 4.74 kB
-
https://huggingface.co/spaces/Felladrin/MiniSearch/resolve/main/scripts/license-check.cjs
- Command line
-
hf download hf://spaces/Felladrin/MiniSearch/scripts/license-check.cjs
-
curl -L -o license-check.cjs https://huggingface.co/spaces/Felladrin/MiniSearch/resolve/main/scripts/license-check.cjs
4.74 kB
| const fs = require("node:fs"); | |
| const path = require("node:path"); | |
| const ALLOWED = new Set([ | |
| "MIT", | |
| "Apache-2.0", | |
| "BSD-2-Clause", | |
| "BSD-3-Clause", | |
| "ISC", | |
| "Unlicense", | |
| "CC0-1.0", | |
| "0BSD", | |
| "LGPL-2.1", | |
| "LGPL-3.0", | |
| // MPL-2.0 is file-level copyleft; combining it with Apache-2.0 in one | |
| // program is explicitly permitted by Mozilla's own FAQ (Q13, | |
| // https://www.mozilla.org/en-US/MPL/2.0/FAQ/). Ships via vite's | |
| // lightningcss binaries, unmodified. | |
| "MPL-2.0", | |
| ]); | |
| // Each entry below was verified by hand against the LICENSE file shipped in | |
| // that package's own node_modules directory. A new opaque-license package | |
| // must be added here ONLY after a human has read its license file. | |
| const OVERRIDE = { | |
| flatbuffers: "Apache-2.0", | |
| "keyword-extractor": "MIT", | |
| }; | |
| const OPAQUE = "SEE LICENSE IN LICENSE.txt"; | |
| // OR = any side may be allowed; AND = every side must be allowed. | |
| function isAllowed(license) { | |
| if (typeof license !== "string") return false; | |
| if (ALLOWED.has(license)) return true; | |
| if (license.includes(" OR ")) { | |
| return license | |
| .split(" OR ") | |
| .some((side) => ALLOWED.has(side.replace(/^\(|\)$/g, "").trim())); | |
| } | |
| if (license.includes(" AND ")) { | |
| return license | |
| .split(" AND ") | |
| .every((side) => ALLOWED.has(side.replace(/^\(|\)$/g, "").trim())); | |
| } | |
| return false; | |
| } | |
| // A missing license field and the opaque "SEE LICENSE IN LICENSE.txt" field | |
| // are only allowed via the OVERRIDE map, which a human must populate after | |
| // reading the package's own license. Any other package with such a field | |
| // fails the gate (safety net). | |
| function isAllowedLicense(name, license) { | |
| if (license === undefined || license === OPAQUE) { | |
| return isAllowed(OVERRIDE[name]); | |
| } | |
| return isAllowed(license); | |
| } | |
| function productionPackages(lock) { | |
| const packages = []; | |
| for (const [key, entry] of Object.entries(lock.packages ?? {})) { | |
| if (key === "") continue; | |
| if (entry.dev) continue; | |
| if (entry.link) continue; | |
| const name = key.split("node_modules/").pop(); | |
| packages.push({ | |
| name, | |
| version: entry.version ?? "unknown", | |
| license: entry.license, | |
| }); | |
| } | |
| return packages; | |
| } | |
| function formatLicense(license) { | |
| return typeof license === "string" ? license : "no license field"; | |
| } | |
| function writeReport(reportPath, packages) { | |
| const rows = [...packages].sort((a, b) => a.name.localeCompare(b.name)); | |
| const lines = [ | |
| "# Dependency Licenses", | |
| "", | |
| `Project license: **Apache-2.0**`, | |
| "", | |
| "| Package | License |", | |
| "| --- | --- |", | |
| ...rows.map((p) => `| ${p.name} | ${formatLicense(p.license)} |`), | |
| "", | |
| ]; | |
| fs.mkdirSync(path.dirname(reportPath), { recursive: true }); | |
| fs.writeFileSync(reportPath, lines.join("\n"), "utf8"); | |
| } | |
| function run(lockPath, reportPath) { | |
| const lock = JSON.parse(fs.readFileSync(lockPath, "utf8")); | |
| const packages = productionPackages(lock); | |
| const violations = packages.filter( | |
| (p) => !isAllowedLicense(p.name, p.license), | |
| ); | |
| if (violations.length > 0) { | |
| for (const v of violations) { | |
| console.log( | |
| `NOT ALLOWED: ${v.name}@${v.version} (${formatLicense(v.license)})`, | |
| ); | |
| } | |
| console.log( | |
| `\n${violations.length} of ${packages.length} production packages have a license that is not compatible with Apache-2.0`, | |
| ); | |
| if (reportPath) writeReport(reportPath, packages); | |
| return 1; | |
| } | |
| console.log( | |
| `All ${packages.length} production packages have a license compatible with Apache-2.0`, | |
| ); | |
| if (reportPath) writeReport(reportPath, packages); | |
| return 0; | |
| } | |
| if (require.main === module) { | |
| const args = process.argv.slice(2); | |
| const usage = | |
| "Usage: node scripts/license-check.cjs [lockfilePath] [--report <path>]"; | |
| let lockfilePath; | |
| let reportPath; | |
| for (let i = 0; i < args.length; i++) { | |
| if (args[i] === "--report") { | |
| reportPath = args[++i]; | |
| if (!reportPath) { | |
| console.error(`β --report requires a path\n ${usage}`); | |
| process.exit(1); | |
| } | |
| } else if (args[i].startsWith("-")) { | |
| console.error(`β Unknown option: ${args[i]}\n ${usage}`); | |
| process.exit(1); | |
| } else if (lockfilePath === undefined) { | |
| lockfilePath = args[i]; | |
| } else { | |
| console.error(`β Too many arguments\n ${usage}`); | |
| process.exit(1); | |
| } | |
| } | |
| const rootDir = path.resolve(__dirname, ".."); | |
| lockfilePath = lockfilePath | |
| ? path.resolve(lockfilePath) | |
| : path.join(rootDir, "package-lock.json"); | |
| if (!fs.existsSync(lockfilePath)) { | |
| console.error(`β Lockfile not found: ${lockfilePath}`); | |
| process.exit(1); | |
| } | |
| process.exit(run(lockfilePath, reportPath)); | |
| } | |
| module.exports = { run, isAllowedLicense, ALLOWED, OVERRIDE }; | |