dts-critic
Version:
Checks a new .d.ts against the Javascript source and tells you what problems it has
357 lines • 12.1 kB
JavaScript
"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