stylebot-harmony/scripts/build-license.ts
2023-08-03 20:09:32 -04:00

146 lines
3.7 KiB
JavaScript
Executable File

#!/usr/bin/env -S node -r esbin
import fs from "node:fs"
import { resolve } from "node:path"
import { load } from "js-yaml"
process.chdir(resolve(__dirname, ".."))
const candidates = [
"master/LICENSE",
"master/LICENCE",
"master/LICENSE.md",
"master/license.md",
"main/LICENSE.txt",
]
async function getLicense(repo: string) {
for (const candidate of candidates) {
const path = `https://raw.githubusercontent.com/${repo}/${candidate}`
const res = await fetch(path)
if (!res.ok) continue
const text = await res.text()
return text
}
throw new Error(`Could not find license for ${repo}`)
}
async function getSDPXLicense(license: string) {
const path = `https://raw.githubusercontent.com/spdx/license-list-data/main/text/${license}.txt`
const res = await fetch(path)
if (!res.ok) throw new Error(`Could not find license ${license}`)
const text = await res.text()
return text
}
function getVendorLicenses() {
return fs
.readdirSync("src/vendor")
.map(name => ({ name, path: `src/vendor/${name}/LICENSE` }))
.filter(({ path }) => fs.existsSync(path))
.map(({ name, path }) => [name, fs.readFileSync(path, "utf8")])
}
async function getPackageLicense(pkg: string) {
const files = ["LICENSE", "LICENCE", "license.md"]
for (const file of files) {
const path = `node_modules/${pkg}/${file}`
if (fs.existsSync(path)) {
return fs.readFileSync(path, "utf8")
}
}
const pkgJson = JSON.parse(fs.readFileSync(`node_modules/${pkg}/package.json`, "utf8"))
const licenseName = pkgJson.license as string
if (!licenseName) throw new Error(`Could not find license for ${pkg}`)
return getSDPXLicense(licenseName)
}
function getPackageLicenses() {
const {
dependencies = {},
devDependencies = {},
peerDependencies = {},
} = JSON.parse(fs.readFileSync("package.json", "utf8"))
const packages = Object.keys({
...dependencies,
...devDependencies,
...peerDependencies,
})
return Promise.all(packages.map(async pkg => [pkg, await getPackageLicense(pkg)]))
}
class Licenses {
libs = new Set<string>()
constructor(readonly json: Record<string, string>) {}
set(name: string, license: string) {
this.libs.add(name)
this.json[name] = license
}
async setIfAbsent(name: string, license: () => Promise<string>) {
if (!this.json[name]) {
const text = await license()
this.set(name, text)
}
}
assign(others: Record<string, string>) {
for (const [name, license] of Object.entries(others)) {
this.set(name, license)
}
}
purge() {
for (const name of Object.keys(this.json)) {
if (!this.libs.has(name)) {
delete this.json[name]
}
}
}
toJSON() {
return this.json
}
}
async function main() {
const outPath = "src/generated/licenses.json"
if (!fs.existsSync(outPath)) {
fs.writeFileSync(outPath, "{}")
}
const res = new Licenses(
JSON.parse(fs.readFileSync(outPath, "utf8")) as Record<string, string>,
)
const config = load(fs.readFileSync("src/licenses.yml", "utf8")) as {
github: string[]
licenses: { [name: string]: string }
}
// res.set("archive", await getSPLicense("AGPL-3.0-or-later"));
for (const repo of config.github) {
await res.setIfAbsent(repo, () => getLicense(repo))
}
for (const [name, license] of Object.entries(config.licenses)) {
await res.setIfAbsent(name, () => getSDPXLicense(license))
}
res.assign(Object.fromEntries(getVendorLicenses()))
res.assign(Object.fromEntries(await getPackageLicenses()))
res.purge()
fs.writeFileSync(
outPath,
JSON.stringify(
Object.fromEntries(
Object.entries(res.toJSON()).sort(([a], [b]) => a.localeCompare(b)),
),
null,
2,
) + "\n",
)
}
void main()