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

Added diagnostics to all errors #1963

Merged
merged 34 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
717bb1a
fix: errors for sourceless nodes
arthurfiorette Jan 31, 2023
68b6c0b
initial promise support
arthurfiorette Apr 16, 2024
09ff47a
Merge remote-tracking branch 'upstream/next' into next
arthurfiorette Apr 16, 2024
19e481f
revert
arthurfiorette Apr 16, 2024
2d36fbf
feat: final implementation
arthurfiorette May 15, 2024
2517d66
Merge remote-tracking branch 'upstream/next' into next
arthurfiorette May 15, 2024
a13545c
fix: imports
arthurfiorette May 15, 2024
c78f38c
code
arthurfiorette May 15, 2024
8f80837
lint
arthurfiorette May 15, 2024
624736e
code
arthurfiorette May 15, 2024
7944bc7
fix: tests
arthurfiorette May 16, 2024
697805f
expose internals
arthurfiorette May 16, 2024
a05e492
i was wrong
arthurfiorette May 16, 2024
9ca48e0
fix: entire check
arthurfiorette May 16, 2024
ec698e2
fix: lint
arthurfiorette May 16, 2024
3b74479
type
arthurfiorette May 16, 2024
a4a497f
somehow tests were getting this out of order
arthurfiorette May 16, 2024
2a3e4ce
fix: removed unused method
arthurfiorette May 16, 2024
42d2bd9
feat: added unnamed class test
arthurfiorette May 16, 2024
e5daa4a
fix: test
arthurfiorette May 16, 2024
d6ed5a7
documented promise unwrapping
arthurfiorette May 16, 2024
6b11f65
Update src/NodeParser/FunctionNodeParser.ts
arthurfiorette May 19, 2024
8100f43
fix: removed allowUnionTypes
arthurfiorette May 19, 2024
e804590
chore: style
arthurfiorette May 19, 2024
62da85f
feat: code
arthurfiorette May 23, 2024
a3c7119
fixes
arthurfiorette May 23, 2024
c8278b9
non tty errors
arthurfiorette May 23, 2024
b5f5c14
remove useless test
arthurfiorette May 23, 2024
aaa9605
fix: keep same error message
arthurfiorette May 23, 2024
be22ec4
merge
arthurfiorette May 23, 2024
964546a
Merge remote-tracking branch 'upstream/next' into arthur/better-errors
arthurfiorette May 23, 2024
7ec60d4
fix: remove TSJG prefixes
arthurfiorette May 23, 2024
e7605ac
fix yarn
arthurfiorette May 23, 2024
941493d
keep exact error message
arthurfiorette May 23, 2024
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
77 changes: 43 additions & 34 deletions factory/program.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,47 @@
import * as glob from "glob";
import * as path from "path";
import ts from "typescript";
import * as path from "node:path";
import normalize from "normalize-path";

import { CompletedConfig, Config } from "../src/Config.js";
import { DiagnosticError } from "../src/Error/DiagnosticError.js";
import { LogicError } from "../src/Error/LogicError.js";
import { NoRootNamesError } from "../src/Error/NoRootNamesError.js";
import { NoTSConfigError } from "../src/Error/NoTSConfigError.js";
import ts from "typescript";
import type { CompletedConfig, Config } from "../src/Config.js";
import { BuildError } from "../src/Error/Errors.js";

function loadTsConfigFile(configFile: string) {
const raw = ts.sys.readFile(configFile);
if (raw) {
const config = ts.parseConfigFileTextToJson(configFile, raw);

if (config.error) {
throw new DiagnosticError([config.error]);
} else if (!config.config) {
throw new LogicError(`Invalid parsed config file "${configFile}"`);
}
if (!raw) {
throw new BuildError({

Check warning on line 12 in factory/program.ts

View check run for this annotation

Codecov / codecov/patch

factory/program.ts#L12

Added line #L12 was not covered by tests
messageText: `Cannot read config file "${configFile}"`,
});
}

const parseResult = ts.parseJsonConfigFileContent(
config.config,
ts.sys,
path.resolve(path.dirname(configFile)),
{},
configFile,
);
parseResult.options.noEmit = true;
delete parseResult.options.out;
delete parseResult.options.outDir;
delete parseResult.options.outFile;
delete parseResult.options.declaration;
delete parseResult.options.declarationDir;
delete parseResult.options.declarationMap;
const config = ts.parseConfigFileTextToJson(configFile, raw);

return parseResult;
} else {
throw new NoTSConfigError();
if (config.error) {
throw new BuildError(config.error);

Check warning on line 20 in factory/program.ts

View check run for this annotation

Codecov / codecov/patch

factory/program.ts#L20

Added line #L20 was not covered by tests
}

if (!config.config) {
throw new BuildError({

Check warning on line 24 in factory/program.ts

View check run for this annotation

Codecov / codecov/patch

factory/program.ts#L24

Added line #L24 was not covered by tests
messageText: `Invalid parsed config file "${configFile}"`,
});
}

const parseResult = ts.parseJsonConfigFileContent(
config.config,
ts.sys,
path.resolve(path.dirname(configFile)),
{},
configFile,
);
parseResult.options.noEmit = true;
delete parseResult.options.out;
delete parseResult.options.outDir;
delete parseResult.options.outFile;
delete parseResult.options.declaration;
delete parseResult.options.declarationDir;
delete parseResult.options.declarationMap;

return parseResult;
}

function getTsConfig(config: Config) {
Expand Down Expand Up @@ -67,15 +70,21 @@
const rootNames = rootNamesFromPath.length ? rootNamesFromPath : tsconfig.fileNames;

if (!rootNames.length) {
throw new NoRootNamesError();
throw new BuildError({

Check warning on line 73 in factory/program.ts

View check run for this annotation

Codecov / codecov/patch

factory/program.ts#L73

Added line #L73 was not covered by tests
messageText: "No input files",
});
}

const program: ts.Program = ts.createProgram(rootNames, tsconfig.options);

if (!config.skipTypeCheck) {
const diagnostics = ts.getPreEmitDiagnostics(program);

if (diagnostics.length) {
throw new DiagnosticError(diagnostics);
throw new BuildError({

Check warning on line 84 in factory/program.ts

View check run for this annotation

Codecov / codecov/patch

factory/program.ts#L84

Added line #L84 was not covered by tests
messageText: "Type check error",
relatedInformation: [...diagnostics],
});
}
}

Expand Down
10 changes: 1 addition & 9 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
export * from "./src/Error/BaseError.js";
export * from "./src/Error/DiagnosticError.js";
export * from "./src/Error/LogicError.js";
export * from "./src/Error/NoRootNamesError.js";
export * from "./src/Error/NoRootTypeError.js";
export * from "./src/Error/NoTSConfigError.js";
export * from "./src/Error/UnknownNodeError.js";
export * from "./src/Error/UnknownTypeError.js";
export * from "./src/Error/Errors.js";

export * from "./src/Config.js";

export * from "./src/Utils/allOfDefinition.js";
export * from "./src/Utils/assert.js";
export * from "./src/Utils/deepMerge.js";
export * from "./src/Utils/derefType.js";
export * from "./src/Utils/extractLiterals.js";
export * from "./src/Utils/formatError.js";
export * from "./src/Utils/hasJsDocTag.js";
export * from "./src/Utils/intersectionOfArrays.js";
export * from "./src/Utils/isAssignableTo.js";
Expand Down
18 changes: 9 additions & 9 deletions src/ChainNodeParser.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import ts from "typescript";
import { UnknownNodeError } from "./Error/UnknownNodeError.js";
import { MutableParser } from "./MutableParser.js";
import { Context } from "./NodeParser.js";
import { SubNodeParser } from "./SubNodeParser.js";
import { BaseType } from "./Type/BaseType.js";
import type ts from "typescript";
import { UnknownNodeError } from "./Error/Errors.js";
import type { MutableParser } from "./MutableParser.js";
import type { Context } from "./NodeParser.js";
import type { SubNodeParser } from "./SubNodeParser.js";
import type { BaseType } from "./Type/BaseType.js";
import { ReferenceType } from "./Type/ReferenceType.js";

export class ChainNodeParser implements SubNodeParser, MutableParser {
Expand Down Expand Up @@ -32,21 +32,21 @@
const contextCacheKey = context.getCacheKey();
let type = typeCache.get(contextCacheKey);
if (!type) {
type = this.getNodeParser(node, context).createType(node, context, reference);
type = this.getNodeParser(node).createType(node, context, reference);
if (!(type instanceof ReferenceType)) {
typeCache.set(contextCacheKey, type);
}
}
return type;
}

protected getNodeParser(node: ts.Node, context: Context): SubNodeParser {
protected getNodeParser(node: ts.Node): SubNodeParser {
for (const nodeParser of this.nodeParsers) {
if (nodeParser.supportsNode(node)) {
return nodeParser;
}
}

throw new UnknownNodeError(node, context.getReference());
throw new UnknownNodeError(node);

Check warning on line 50 in src/ChainNodeParser.ts

View check run for this annotation

Codecov / codecov/patch

src/ChainNodeParser.ts#L50

Added line #L50 was not covered by tests
}
}
10 changes: 5 additions & 5 deletions src/ChainTypeFormatter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { UnknownTypeError } from "./Error/UnknownTypeError.js";
import { MutableTypeFormatter } from "./MutableTypeFormatter.js";
import { Definition } from "./Schema/Definition.js";
import { SubTypeFormatter } from "./SubTypeFormatter.js";
import { BaseType } from "./Type/BaseType.js";
import { UnknownTypeError } from "./Error/Errors.js";
import type { MutableTypeFormatter } from "./MutableTypeFormatter.js";
import type { Definition } from "./Schema/Definition.js";
import type { SubTypeFormatter } from "./SubTypeFormatter.js";
import type { BaseType } from "./Type/BaseType.js";

export class ChainTypeFormatter implements SubTypeFormatter, MutableTypeFormatter {
public constructor(protected typeFormatters: SubTypeFormatter[]) {}
Expand Down
62 changes: 60 additions & 2 deletions src/Error/BaseError.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,63 @@
import ts from "typescript";

export type PartialDiagnostic = Omit<ts.Diagnostic, "category" | "file" | "start" | "length"> & {
file?: ts.SourceFile;
start?: number;
length?: number;

/** If we should populate `file`, `source`, `start` and `length` with this node information */
node?: ts.Node;

/** @default Error */
category?: ts.DiagnosticCategory;
};

const isTTY = process.env.TTY || process.stdout.isTTY;

/**
* Base error for ts-json-schema-generator
*/
export abstract class BaseError extends Error {
public constructor(message?: string) {
super(message);
readonly diagnostic: ts.Diagnostic;

constructor(diagnostic: PartialDiagnostic) {
super(ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"));
this.diagnostic = BaseError.createDiagnostic(diagnostic);
}

static createDiagnostic(diagnostic: PartialDiagnostic): ts.Diagnostic {
// Swap the node for the file, source, start and length properties
// sourceless nodes cannot be referenced in the diagnostic
if (diagnostic.node && diagnostic.node.pos !== -1) {
diagnostic.file = diagnostic.node.getSourceFile();
diagnostic.start = diagnostic.node.getStart();
diagnostic.length = diagnostic.node.getWidth();

Check warning on line 34 in src/Error/BaseError.ts

View check run for this annotation

Codecov / codecov/patch

src/Error/BaseError.ts#L32-L34

Added lines #L32 - L34 were not covered by tests

diagnostic.node = undefined;

Check warning on line 36 in src/Error/BaseError.ts

View check run for this annotation

Codecov / codecov/patch

src/Error/BaseError.ts#L36

Added line #L36 was not covered by tests
}

// @ts-expect-error - Differentiates from errors from the TypeScript compiler
// error TSJ - 100: message
diagnostic.code = `J - ${diagnostic.code}`;

return Object.assign(
{
category: ts.DiagnosticCategory.Error,
file: undefined,
length: 0,
start: 0,
},
diagnostic,
);
}

format() {

Check warning on line 54 in src/Error/BaseError.ts

View check run for this annotation

Codecov / codecov/patch

src/Error/BaseError.ts#L54

Added line #L54 was not covered by tests
const formatter = isTTY ? ts.formatDiagnosticsWithColorAndContext : ts.formatDiagnostics;

return formatter([this.diagnostic], {
getCanonicalFileName: (fileName) => fileName,
getCurrentDirectory: () => "",
getNewLine: () => "\n",

Check warning on line 60 in src/Error/BaseError.ts

View check run for this annotation

Codecov / codecov/patch

src/Error/BaseError.ts#L57-L60

Added lines #L57 - L60 were not covered by tests
});
}
}
14 changes: 0 additions & 14 deletions src/Error/DiagnosticError.ts

This file was deleted.

103 changes: 103 additions & 0 deletions src/Error/Errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import ts from "typescript";
import { type PartialDiagnostic, BaseError } from "./BaseError.js";
import type { BaseType } from "../Type/BaseType.js";
import type { JSONSchema7 } from "json-schema";

export class UnknownNodeError extends BaseError {
constructor(readonly node: ts.Node) {
super({

Check warning on line 8 in src/Error/Errors.ts

View check run for this annotation

Codecov / codecov/patch

src/Error/Errors.ts#L7-L8

Added lines #L7 - L8 were not covered by tests
code: 100,
node,
messageText: `Unknown node of kind "${ts.SyntaxKind[node.kind]}"`,
});
}
}

export class UnknownTypeError extends BaseError {
constructor(readonly type: BaseType) {
super({
code: 101,
messageText: `Unknown type "${type.getId()}"`,
});
}
}

export class RootlessError extends BaseError {
constructor(readonly fullName: string) {
super({
code: 102,
messageText: `No root type "${fullName}" found`,
});
}
}

export class MultipleDefinitionsError extends BaseError {
constructor(
readonly name: string,
readonly defA: BaseType,
readonly defB?: BaseType,
) {
super({
code: 103,
messageText: `Type "${name}" has multiple definitions.`,
});
}
}

export class LogicError extends BaseError {
constructor(
readonly node: ts.Node,
messageText: string,
) {
super({

Check warning on line 52 in src/Error/Errors.ts

View check run for this annotation

Codecov / codecov/patch

src/Error/Errors.ts#L51-L52

Added lines #L51 - L52 were not covered by tests
code: 104,
messageText,
node,
});
}
}

export class ExpectationFailedError extends BaseError {
constructor(
messageText: string,
readonly node?: ts.Node,
) {
super({

Check warning on line 65 in src/Error/Errors.ts

View check run for this annotation

Codecov / codecov/patch

src/Error/Errors.ts#L64-L65

Added lines #L64 - L65 were not covered by tests
code: 105,
messageText,
node,
});
}
}

export class JsonTypeError extends BaseError {
constructor(
messageText: string,
readonly type: BaseType,
) {
super({
code: 106,
messageText,
});
}
}

export class DefinitionError extends BaseError {
constructor(
messageText: string,
readonly definition: JSONSchema7,
) {
super({

Check warning on line 90 in src/Error/Errors.ts

View check run for this annotation

Codecov / codecov/patch

src/Error/Errors.ts#L89-L90

Added lines #L89 - L90 were not covered by tests
code: 107,
messageText,
});
}
}
export class BuildError extends BaseError {
constructor(diag: Omit<PartialDiagnostic, "code">) {
super({

Check warning on line 98 in src/Error/Errors.ts

View check run for this annotation

Codecov / codecov/patch

src/Error/Errors.ts#L97-L98

Added lines #L97 - L98 were not covered by tests
code: 108,
...diag,
});
}
}
7 changes: 0 additions & 7 deletions src/Error/LogicError.ts

This file was deleted.

10 changes: 0 additions & 10 deletions src/Error/NoRootNamesError.ts

This file was deleted.

11 changes: 0 additions & 11 deletions src/Error/NoRootTypeError.ts

This file was deleted.

Loading
Loading