Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose CLI renderer #137

Merged
merged 4 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/nervous-carrots-yell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@arethetypeswrong/cli": patch
---

Expose internal renderer and exit code API
12 changes: 9 additions & 3 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@
},
"files": [
"LICENSE",
"dist/**/*.js"
"dist/**/*.js",
"dist/**/*.js.map",
"dist/**/*.d.ts"
],
"bin": {
"attw": "./dist/index.js"
},
"exports": {
"./internal/getExitCode": "./dist/getExitCode.js",
"./internal/render": "./dist/render/index.js"
},
"publishConfig": {
"access": "public"
},
Expand All @@ -42,8 +48,8 @@
"@types/marked-terminal": "^3.1.3",
"@types/node": "^20.2.5",
"@types/semver": "^7.5.3",
"ts-expose-internals": "5.3.2",
"typescript": "5.3.2"
"ts-expose-internals-conditionally": "1.0.0-empty.0",
"typescript": "5.3.3"
},
"dependencies": {
"@arethetypeswrong/core": "0.13.6",
Expand Down
13 changes: 13 additions & 0 deletions packages/cli/src/getExitCode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { CheckResult } from "@arethetypeswrong/core";
import { problemFlags } from "./problemUtils.js";
import type { RenderOptions } from "./render/index.js";

export function getExitCode(analysis: CheckResult, opts?: RenderOptions): number {
if (!analysis.types) {
return 0;
}
if (!opts?.ignoreRules) {
return analysis.problems.length > 0 ? 1 : 0;
}
return analysis.problems.some((problem) => !opts.ignoreRules!.includes(problemFlags[problem.kind])) ? 1 : 0;
}
40 changes: 20 additions & 20 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,25 @@ import { problemFlags } from "./problemUtils.js";
import { readConfig } from "./readConfig.js";
import * as render from "./render/index.js";
import { major, minor } from "semver";
import { getExitCode } from "./getExitCode.js";

const packageJson = createRequire(import.meta.url)("../package.json");
const version = packageJson.version;

const formats = ["auto", "table", "table-flipped", "ascii", "json"] as const;
const formats = Object.keys({
auto: true,
json: true,
ascii: true,
table: true,
"table-flipped": true,
} satisfies Record<render.Format, any>) as render.Format[];

type Format = (typeof formats)[number];

export interface Opts {
interface Opts extends render.RenderOptions {
pack?: boolean;
fromNpm?: boolean;
definitelyTyped?: boolean | string;
summary?: boolean;
emoji?: boolean;
color?: boolean;
quiet?: boolean;
configPath?: string;
ignoreRules?: string[];
format: Format;

entrypoints?: string[];
includeEntrypoints?: string[];
Expand Down Expand Up @@ -81,9 +81,6 @@ particularly ESM-related module resolution issues.`,
.action(async (fileOrDirectory = ".") => {
const opts = program.opts<Opts>();
await readConfig(program, opts.configPath);
opts.ignoreRules = opts.ignoreRules?.map(
(value) => Object.keys(problemFlags).find((key) => problemFlags[key as core.ProblemKind] === value) as string,
);

if (opts.quiet) {
console.log = () => {};
Expand Down Expand Up @@ -210,21 +207,24 @@ particularly ESM-related module resolution issues.`,

console.log(JSON.stringify(result));

if (analysis.types && analysis.problems.some((problem) => !opts.ignoreRules?.includes(problem.kind)))
process.exit(1);
if (deleteTgz) {
await unlink(deleteTgz);
}

const exitCode = getExitCode(analysis, opts);
if (exitCode) {
process.exit(exitCode);
}

return;
}

console.log();
if (analysis.types) {
await render.typed(analysis, opts);

if (analysis.types && analysis.problems.some((problem) => !opts.ignoreRules?.includes(problem.kind))) {
process.exitCode = 1;
}
console.log(await render.typed(analysis, opts));
process.exitCode = getExitCode(analysis, opts);
} else {
render.untyped(analysis as core.UntypedResult);
console.log(render.untyped(analysis as core.UntypedResult));
}

if (deleteTgz) {
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/problemUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as core from "@arethetypeswrong/core";
import type { ProblemKind } from "@arethetypeswrong/core";

export const problemFlags: Record<ProblemKind, string> = {
export const problemFlags = {
NoResolution: "no-resolution",
UntypedResolution: "untyped-resolution",
FalseCJS: "false-cjs",
Expand All @@ -13,7 +13,7 @@ export const problemFlags: Record<ProblemKind, string> = {
MissingExportEquals: "missing-export-equals",
UnexpectedModuleSyntax: "unexpected-module-syntax",
InternalResolutionError: "internal-resolution-error",
};
} as const satisfies Record<ProblemKind, string>;

export const resolutionKinds: Record<core.ResolutionKind, string> = {
node10: "node10",
Expand Down
11 changes: 11 additions & 0 deletions packages/cli/src/render/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
import type { problemFlags } from "../problemUtils.js";

export type Format = "auto" | "table" | "table-flipped" | "ascii" | "json";
export interface RenderOptions {
ignoreRules?: (typeof problemFlags)[keyof typeof problemFlags][];
format?: Format;
color?: boolean;
summary?: boolean;
emoji?: boolean;
}

export * from "./typed.js";
export * from "./untyped.js";
54 changes: 28 additions & 26 deletions packages/cli/src/render/typed.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,44 @@
import * as core from "@arethetypeswrong/core";
import { filterProblems, problemAffectsEntrypoint, problemKindInfo } from "@arethetypeswrong/core/problems";
import { allResolutionKinds, getResolutionOption, groupProblemsByKind } from "@arethetypeswrong/core/utils";
import chalk from "chalk";
import Table, { type GenericTable, type HorizontalTableRow } from "cli-table3";
import { marked } from "marked";

import { filterProblems, problemAffectsEntrypoint, problemKindInfo } from "@arethetypeswrong/core/problems";
import type { Opts } from "../index.js";
import TerminalRenderer from "marked-terminal";
import { moduleKinds, problemFlags, resolutionKinds } from "../problemUtils.js";
import { asciiTable } from "./asciiTable.js";
import TerminalRenderer from "marked-terminal";
import type { RenderOptions } from "./index.js";

export async function typed(analysis: core.Analysis, opts: Opts) {
const problems = analysis.problems.filter((problem) => !opts.ignoreRules || !opts.ignoreRules.includes(problem.kind));
export async function typed(analysis: core.Analysis, opts: RenderOptions): Promise<string> {
let output = "";
const problems = analysis.problems.filter(
(problem) => !opts.ignoreRules || !opts.ignoreRules.includes(problemFlags[problem.kind]),
);
const grouped = groupProblemsByKind(problems);
const entrypoints = Object.keys(analysis.entrypoints);
marked.setOptions({
renderer: new TerminalRenderer(),
});

console.log(`${analysis.packageName} v${analysis.packageVersion}`);
out(`${analysis.packageName} v${analysis.packageVersion}`);
if (analysis.types.kind === "@types") {
console.log(`${analysis.types.packageName} v${analysis.types.packageVersion}`);
out(`${analysis.types.packageName} v${analysis.types.packageVersion}`);
}
console.log();
out();
if (Object.keys(analysis.buildTools).length) {
console.log("Build tools:");
console.log(
out("Build tools:");
out(
Object.entries(analysis.buildTools)
.map(([tool, version]) => {
return `- ${tool}@${version}`;
})
.join("\n"),
);
console.log();
out();
}

if (opts.ignoreRules && opts.ignoreRules.length) {
console.log(
chalk.gray(
` (ignoring rules: ${opts.ignoreRules
.map((rule) => `'${problemFlags[rule as core.ProblemKind]}'`)
.join(", ")})\n`,
),
);
out(chalk.gray(` (ignoring rules: ${opts.ignoreRules.map((rule) => `'${rule}'`).join(", ")})\n`));
}

if (opts.summary) {
Expand All @@ -54,7 +50,7 @@ export async function typed(analysis: core.Analysis, opts: Opts) {
return `${emoji}${description}`;
});

console.log(summaryTexts.join("") || defaultSummary);
out(summaryTexts.join("") || defaultSummary);
}

const entrypointNames = entrypoints.map(
Expand Down Expand Up @@ -119,25 +115,31 @@ export async function typed(analysis: core.Analysis, opts: Opts) {

switch (opts.format) {
case "table":
console.log(table!.toString());
out(table!.toString());
break;
case "table-flipped":
console.log(flippedTable!.toString());
out(flippedTable!.toString());
break;
case "ascii":
console.log(asciiTable(table!));
out(asciiTable(table!));
break;
case "auto":
const terminalWidth = process.stdout.columns || 133; // This looks like GitHub Actions' width
if (table!.width <= terminalWidth) {
console.log(table!.toString());
out(table!.toString());
} else if (flippedTable!.width <= terminalWidth) {
console.log(flippedTable!.toString());
out(flippedTable!.toString());
} else {
console.log(asciiTable(table!));
out(asciiTable(table!));
}
break;
}

return output.trimEnd();

function out(s: string = "") {
output += s + "\n";
}
}

function memo<Args extends (string | number)[], Result>(fn: (...args: Args) => Result): (...args: Args) => Result {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/render/untyped.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as core from "@arethetypeswrong/core";

export function untyped(analysis: core.UntypedResult) {
console.log("This package does not contain types.\nDetails: ", analysis);
return "This package does not contain types.\nDetails: " + JSON.stringify(analysis, null, 2);
}
6 changes: 5 additions & 1 deletion packages/cli/test/snapshots/[email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ $ attw [email protected] -f table-flipped


This package does not contain types.
Details: { packageName: 'ejs', packageVersion: '3.1.9', types: false }
Details: {
"packageName": "ejs",
"packageVersion": "3.1.9",
"types": false
}


```
Expand Down
3 changes: 1 addition & 2 deletions packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
"compilerOptions": {
"module": "nodenext",
"rootDir": "src",
"types": ["ts-expose-internals", "node"],
"types": ["ts-expose-internals-conditionally", "node"],
"outDir": "./dist",
"declarationDir": "./lib",
"sourceMap": true
},
"include": ["src"],
Expand Down
21 changes: 5 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading