import * as fs from 'fs'; import * as t from 'io-ts'; import { isRight } from 'fp-ts/lib/Either'; import { PathReporter } from 'io-ts/lib/PathReporter'; const EmbellishOverridesProps = t.partial({ licenseHolder: t.string, }); const RepositoryProps = t.union([ t.string, t.type({ type: t.string, url: t.string, }), ]); const PackageStabilityProps = t.keyof({ deprecated: null, experimental: null, unstable: null, stable: null, frozen: null, locked: null, }); const PackageDataOptionalProps = t.partial({ license: t.string, embellish: EmbellishOverridesProps, private: t.boolean, repository: RepositoryProps, stability: PackageStabilityProps, }); const PackageDataProps = t.intersection([ t.type({ author: t.string, name: t.string, }), PackageDataOptionalProps, ]); const REPO_REGEXES = [ /^.*(github\.com)\/(.*)\.git.*$/, // github /^.*(bitbucket\.org)\/(.*)\.git.*$/, // bitbucket ]; export type PackageData = t.TypeOf; export class Package { constructor(public readonly data: PackageData) {} static deserialize(json: unknown) { const result = PackageDataProps.decode(json); if (isRight(result)) { return result.right; } throw new Error(`Unable to decode package data: ${PathReporter.report(result)}`); } static readFromFile(filename: string) { return new Promise((resolve, reject) => { fs.readFile(filename, 'utf-8', (err, content) => { if (err) { return reject(err); } try { resolve(Package.deserialize(JSON.parse(content))); } catch (e) { reject(e); } }); }); } } export const getRepository = (data: PackageData) => { const { repository } = data; if (!repository) { return undefined; } const url = typeof repository === 'string' ? repository : repository.url; const match = REPO_REGEXES.reduce((memo, regex) => { return memo || regex.exec(url); }, null); return match ? { host: match[1] as string, path: match[2].split('/').splice(0, 2).join('/') as string, } : undefined; };