UNPKG

dts-critic

Version:

Checks a new .d.ts against the Javascript source and tells you what problems it has

357 lines 12.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const yargs = require("yargs"); const headerParser = require("@definitelytyped/header-parser"); const path = require("path"); const cp = require("child_process"); const index_1 = require("./index"); const sourcesDir = "sources"; const downloadsPath = path.join(sourcesDir, "dts-critic-internal/downloads.json"); const isNpmPath = path.join(sourcesDir, "dts-critic-internal/npm.json"); function getPackageDownloads(dtName) { const npmName = index_1.dtToNpmName(dtName); const url = `https://api.npmjs.org/downloads/point/last-month/${npmName}`; const result = JSON.parse(cp.execFileSync("curl", ["--silent", "-L", url], { encoding: "utf8" })); return result.downloads || 0; } function getAllPackageDownloads(dtPath) { if (fs.existsSync(downloadsPath)) { return JSON.parse(fs.readFileSync(downloadsPath, { encoding: "utf8" })); } initDir(path.dirname(downloadsPath)); const downloads = {}; const dtTypesPath = getDtTypesPath(dtPath); for (const item of fs.readdirSync(dtTypesPath)) { const d = getPackageDownloads(item); downloads[item] = d; } fs.writeFileSync(downloadsPath, JSON.stringify(downloads), { encoding: "utf8" }); return downloads; } function initDir(path) { if (!fs.existsSync(path)) { fs.mkdirSync(path); } } function getDtTypesPath(dtBasePath) { return path.join(dtBasePath, "types"); } function compareDownloads(downloads, package1, package2) { const count1 = downloads[package1] || 0; const count2 = downloads[package2] || 0; return count1 - count2; } function getAllIsNpm(dtPath) { if (fs.existsSync(isNpmPath)) { return JSON.parse(fs.readFileSync(isNpmPath, { encoding: "utf8" })); } initDir(path.dirname(isNpmPath)); const isNpm = {}; const dtTypesPath = getDtTypesPath(dtPath); for (const item of fs.readdirSync(dtTypesPath)) { isNpm[item] = index_1.getNpmInfo(item).isNpm; } fs.writeFileSync(isNpmPath, JSON.stringify(isNpm), { encoding: "utf8" }); return isNpm; } function getPopularNpmPackages(count, dtPath) { const dtPackages = getDtNpmPackages(dtPath); const downloads = getAllPackageDownloads(dtPath); dtPackages.sort((a, b) => compareDownloads(downloads, a, b)); return dtPackages.slice(dtPackages.length - count); } function getUnpopularNpmPackages(count, dtPath) { const dtPackages = getDtNpmPackages(dtPath); const downloads = getAllPackageDownloads(dtPath); dtPackages.sort((a, b) => compareDownloads(downloads, a, b)); return dtPackages.slice(0, count); } function getDtNpmPackages(dtPath) { const dtPackages = fs.readdirSync(getDtTypesPath(dtPath)); const isNpmJson = getAllIsNpm(dtPath); return dtPackages.filter(pkg => isNpmPackage(pkg, /* header */ undefined, isNpmJson)); } function getNonNpm(args) { const nonNpm = []; const dtTypesPath = getDtTypesPath(args.dtPath); const isNpmJson = getAllIsNpm(args.dtPath); for (const item of fs.readdirSync(dtTypesPath)) { const entry = path.join(dtTypesPath, item); const dts = fs.readFileSync(entry + "/index.d.ts", "utf8"); let header; try { header = headerParser.parseHeaderOrFail(dts); } catch (e) { header = undefined; } if (!isNpmPackage(item, header, isNpmJson)) { nonNpm.push(item); } } console.log(`List of non-npm packages on DT:\n${nonNpm.map(name => `DT name: ${name}\n`).join("")}`); } function checkAll(args) { const dtPackages = fs.readdirSync(getDtTypesPath(args.dtPath)); checkPackages({ packages: dtPackages, ...args }); } function checkPopular(args) { checkPackages({ packages: getPopularNpmPackages(args.count, args.dtPath), ...args }); } function checkUnpopular(args) { checkPackages({ packages: getUnpopularNpmPackages(args.count, args.dtPath), ...args }); } function checkPackages(args) { const results = args.packages.map(pkg => doCheck({ package: pkg, ...args })); printResults(results, args.json); } function checkPackage(args) { printResults([doCheck(args)], args.json); } function doCheck(args) { const dtPackage = args.package; const opts = getOptions(args.mode, args.enableError || []); try { const dtsPath = path.join(getDtTypesPath(args.dtPath), dtPackage, "index.d.ts"); const errors = index_1.dtsCritic(dtsPath, /* sourcePath */ undefined, opts, args.debug); return { package: args.package, output: errors }; } catch (e) { return { package: args.package, output: e.toString() }; } } function getOptions(modeArg, enabledErrors) { const mode = index_1.parseMode(modeArg); if (!mode) { throw new Error(`Could not find mode named '${modeArg}'.`); } switch (mode) { case index_1.Mode.NameOnly: return { mode }; case index_1.Mode.Code: const errors = getEnabledErrors(enabledErrors); return { mode, errors }; } } function getEnabledErrors(errorNames) { const errors = []; for (const name of errorNames) { const error = index_1.parseExportErrorKind(name); if (error === undefined) { throw new Error(`Could not find error named '${name}'.`); } errors.push(error); } return new Map(errors.map(err => [err, true])); } function checkFile(args) { console.log(`\tChecking JS file ${args.jsFile} and declaration file ${args.dtsFile}`); try { const errors = index_1.checkSource(index_1.findDtsName(args.dtsFile), args.dtsFile, args.jsFile, new Map(), args.debug); console.log(formatErrors(errors)); } catch (e) { console.log(e); } } function printResults(results, json) { if (json) { console.log(JSON.stringify(results)); return; } for (const result of results) { console.log(`\tChecking package ${result.package} ...`); if (typeof result.output === "string") { console.log(`Exception:\n${result.output}`); } else { console.log(formatErrors(result.output)); } } } function formatErrors(errors) { const lines = []; for (const error of errors) { lines.push("Error: " + error.message); } if (errors.length === 0) { lines.push("No errors found! :)"); } return lines.join("\n"); } function isNpmPackage(name, header, isNpmJson = {}) { if (header && header.nonNpm) return false; const isNpm = isNpmJson[name]; if (isNpm !== undefined) { return isNpm; } return index_1.getNpmInfo(name).isNpm; } function main() { // eslint-disable-next-line no-unused-expressions yargs .usage("$0 <command>") .command("check-all", "Check source and declaration of all DT packages that are on NPM.", { dtPath: { type: "string", default: "../DefinitelyTyped", describe: "Path of DT repository cloned locally.", }, mode: { type: "string", required: true, choices: [index_1.Mode.NameOnly, index_1.Mode.Code], describe: "Mode that defines which group of checks will be made.", }, enableError: { type: "array", string: true, describe: "Enable checking for a specific export error." }, debug: { type: "boolean", default: false, describe: "Turn debug logging on.", }, json: { type: "boolean", default: false, describe: "Format output result as json." }, }, checkAll) .command("check-popular", "Check source and declaration of most popular DT packages that are on NPM.", { count: { alias: "c", type: "number", required: true, describe: "Number of packages to be checked.", }, dtPath: { type: "string", default: "../DefinitelyTyped", describe: "Path of DT repository cloned locally.", }, mode: { type: "string", required: true, choices: [index_1.Mode.NameOnly, index_1.Mode.Code], describe: "Mode that defines which group of checks will be made.", }, enableError: { type: "array", string: true, describe: "Enable checking for a specific export error." }, debug: { type: "boolean", default: false, describe: "Turn debug logging on.", }, json: { type: "boolean", default: false, describe: "Format output result as json." }, }, checkPopular) .command("check-unpopular", "Check source and declaration of least popular DT packages that are on NPM.", { count: { alias: "c", type: "number", required: true, describe: "Number of packages to be checked.", }, dtPath: { type: "string", default: "../DefinitelyTyped", describe: "Path of DT repository cloned locally.", }, mode: { type: "string", required: true, choices: [index_1.Mode.NameOnly, index_1.Mode.Code], describe: "Mode that defines which group of checks will be made.", }, enableError: { type: "array", string: true, describe: "Enable checking for a specific export error." }, debug: { type: "boolean", default: false, describe: "Turn debug logging on.", }, json: { type: "boolean", default: false, describe: "Format output result as json." }, }, checkUnpopular) .command("check-package", "Check source and declaration of a DT package.", { package: { alias: "p", type: "string", required: true, describe: "DT name of a package." }, dtPath: { type: "string", default: "../DefinitelyTyped", describe: "Path of DT repository cloned locally.", }, mode: { type: "string", required: true, choices: [index_1.Mode.NameOnly, index_1.Mode.Code], describe: "Mode that defines which group of checks will be made.", }, enableError: { type: "array", string: true, describe: "Enable checking for a specific export error." }, debug: { type: "boolean", default: false, describe: "Turn debug logging on.", }, json: { type: "boolean", default: false, describe: "Format output result as json." }, }, checkPackage) .command("check-file", "Check a JavaScript file and its matching declaration file.", { jsFile: { alias: "j", type: "string", required: true, describe: "Path of JavaScript file.", }, dtsFile: { alias: "d", type: "string", required: true, describe: "Path of declaration file.", }, debug: { type: "boolean", default: false, describe: "Turn debug logging on.", }, }, checkFile) .command("get-non-npm", "Get list of DT packages whose source package is not on NPM", { dtPath: { type: "string", default: "../DefinitelyTyped", describe: "Path of DT repository cloned locally.", }, }, getNonNpm) .demandCommand(1) .help() .argv; } main(); //# sourceMappingURL=develop.js.map