Files
wehub-resource-sync 9119799461
构建测试 / 构建 Linux (x64) (push) Failing after 2s
构建测试 / 构建 Linux (ARM64) (push) Failing after 2s
构建测试 / 构建 Windows (ARM64) (push) Has been cancelled
构建测试 / 构建 Windows (x64) (push) Has been cancelled
构建测试 / 构建 macOS (Apple Silicon) (push) Has been cancelled
构建测试 / 构建 macOS (Intel) (push) Has been cancelled
构建测试 / 构建摘要 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:24:21 +08:00

100 lines
3.6 KiB
TypeScript

import * as fs from "fs";
import * as path from "path";
import checker from "license-checker";
const excludeNames = ["toonflow-serve"];
// const strictWhiteList = ["MIT", "BSD-2-Clause", "BSD-3-Clause", "BSD", "0BSD"];
const strictWhiteList: string[] = [];
// 检查是否在白名单协议
function isStrictWhiteLicense(license: string): boolean {
const normalized = license.replace(/[\(\)]/g, "").trim();
const parts = normalized.split(/\s*(OR|AND|\/)\s*/i).map((part) => part.trim());
return parts.every((part) => strictWhiteList.some((wl) => part === wl || part.replace(/ with .*/i, "") === wl));
}
// 读取 package.json 里的直接依赖
function getDirectDependencyNames(): string[] {
const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), "package.json"), "utf-8"));
const deps = Object.keys(pkg.dependencies ?? {});
const devDeps = Object.keys(pkg.devDependencies ?? {});
return [...deps, ...devDeps];
}
// 执行主逻辑
checker.init({ start: process.cwd() }, (err: Error, packages: Record<string, any>) => {
if (err) {
console.error("license-checker 出错: ", err);
process.exit(1);
}
const directNames = getDirectDependencyNames();
interface PackageInfo {
name: string;
version: string;
licenses: string | string[];
repository: string | undefined;
}
const needDeclare: PackageInfo[] = [];
for (const fullName in packages) {
// fullName 一般形如 [@scope/]pkg@version, 但 license-checker 会带路径,如 @scope/name@1.0.0@./node_modules/@scope/name
// 所以可以正则只保留 name@version 部分
// nameMatch[1] 为包名,nameMatch[2] 为版本
const nameMatch = fullName.match(/^((?:@[^\/]+\/)?[^@]+)@([^@]+)$/);
if (!nameMatch) continue;
const name = nameMatch[1];
// 仅关注直接依赖
if (!directNames.includes(name!)) continue;
const info = packages[fullName];
const licenseArr: string[] = Array.isArray(info.licenses) ? info.licenses : [info.licenses];
if (!licenseArr.every(isStrictWhiteLicense)) {
needDeclare.push({
name: name!,
version: info.version,
licenses: licenseArr,
repository: info.repository,
});
}
}
// 排除名单过滤
const filteredDeclare = needDeclare.filter((pkg) => pkg.name && !excludeNames.some((exName) => pkg.name.startsWith(exName)));
// 去重:同一个 name@version 只保留一条,并合并 licenses
const dedupedDeclare = Array.from(
filteredDeclare
.reduce((acc, pkg) => {
const key = `${pkg.name}@${pkg.version}`;
const licenseList = Array.isArray(pkg.licenses) ? pkg.licenses : [pkg.licenses];
const existing = acc.get(key);
if (!existing) {
acc.set(key, {
...pkg,
licenses: [...new Set(licenseList.filter(Boolean))],
});
return acc;
}
const existingLicenses = Array.isArray(existing.licenses) ? existing.licenses : [existing.licenses];
existing.licenses = [...new Set([...existingLicenses, ...licenseList].filter(Boolean))];
if (!existing.repository && pkg.repository) {
existing.repository = pkg.repository;
}
return acc;
}, new Map<string, PackageInfo>())
.values()
);
const content = dedupedDeclare
.map(
(pkg) =>
`Name: ${pkg.name}\nLicense: ${Array.isArray(pkg.licenses) ? pkg.licenses.join(", ") : pkg.licenses}\nRepository: ${pkg.repository ?? "N/A"}`
)
.join("\n\n-----------------------------\n\n");
fs.writeFileSync(path.resolve(process.cwd(), "NOTICES.txt"), content, "utf-8");
console.log("已生成依赖声明 NOTICES.txt");
});