Skip to content

Commit

Permalink
feat: tsd expectError
Browse files Browse the repository at this point in the history
  • Loading branch information
skarab42 committed Jul 12, 2022
1 parent ea54d4d commit b7393f8
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 9 deletions.
2 changes: 2 additions & 0 deletions src/common/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum ErrorCode {
ASSERT_TYPE_NOT_IDENTICAL,
ASSERT_TYPE_IDENTICAL,
ASSERT_TYPE_TOO_WIDE,
ASSERT_ERROR,
}

export const errorMessages: Record<ErrorCode, string> = {
Expand All @@ -28,6 +29,7 @@ export const errorMessages: Record<ErrorCode, string> = {
[ErrorCode.ASSERT_TYPE_NOT_IDENTICAL]: "Type '{expected}' is not identical to argument type '{argument}'.",
[ErrorCode.ASSERT_TYPE_IDENTICAL]: "Type '{expected}' is identical to argument type '{argument}'.",
[ErrorCode.ASSERT_TYPE_TOO_WIDE]: "Type '{expected}' is declared too wide for argument type '{argument}'.",
[ErrorCode.ASSERT_ERROR]: 'An error is expected.',
};

export function errorMessage(code: ErrorCode, data?: Record<string, unknown>): string {
Expand Down
42 changes: 42 additions & 0 deletions src/plugin/assert/tsd/expect-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { missingArgument } from './util';
import type ts from 'unleashed-typescript';
import type { Assertion } from '../../types';
import type { Compiler } from '../../../typescript/types';
import { createAssertionDiagnostic } from '../../diagnostics';
import { ErrorCode, errorMessage } from '../../../common/error';

// https://github.dev/SamVerschueren/tsd/blob/e4a398c1b47a4d2f914446b662840e2be5994997/source/lib/compiler.ts#L54-L55
export function expectError({ node }: Assertion, compiler: Compiler): ts.Diagnostic | undefined {
const argument = node.arguments[0];

if (!argument) {
return missingArgument(node, compiler.sourceFile);
}

let assertDiagnostic: ts.Diagnostic | undefined = undefined;

if (!compiler.diagnostics.length) {
return createAssertionDiagnostic(errorMessage(ErrorCode.ASSERT_ERROR), compiler.sourceFile, argument.getStart());
}

// TODO: Create a method in Compiler for cleanly removing/filter a diagnostic.
compiler.diagnostics = compiler.diagnostics.filter((diagnostic) => {
if (assertDiagnostic || !diagnostic.start) {
return true;
}

if (diagnostic.start < argument.getStart() || diagnostic.start > argument.getEnd()) {
assertDiagnostic = createAssertionDiagnostic(
errorMessage(ErrorCode.ASSERT_ERROR),
compiler.sourceFile,
diagnostic.start,
);

return true;
}

return false;
});

return assertDiagnostic;
}
5 changes: 1 addition & 4 deletions src/plugin/assert/tsd/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@ export * from './expect-type';
export * from './expect-not-type';
export * from './expect-assignable';
export * from './expect-not-assignable';
export * from './expect-error';

// All credits go to tsd! Most of the logic here comme from their code:
// https://github.com/SamVerschueren/tsd/blob/main/source/lib/assertions/index.ts
// https://github.com/SamVerschueren/tsd/tree/main/source/lib/assertions/handlers

export function expectError(assertion: Assertion, compiler: Compiler): ts.Diagnostic | undefined {
return createAssertionDiagnostic('Not yet implemented.', compiler.sourceFile, assertion.node.getStart());
}

export function expectDeprecated(assertion: Assertion, compiler: Compiler): ts.Diagnostic | undefined {
return createAssertionDiagnostic('Not yet implemented.', compiler.sourceFile, assertion.node.getStart());
}
Expand Down
8 changes: 4 additions & 4 deletions src/plugin/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ export function transform({ code, fileName, report, typescript }: TransformSetti
const compiler = createCompiler({ config: typescript.config, fileName });
const newCode = new MagicString(code);

if (report.includes('type-error')) {
reportDiagnostics(compiler.diagnostics, newCode, fileName);
}

if (report.includes('type-assertion')) {
const assertions = getAssertions(compiler.sourceFile, compiler.typeChecker);
const diagnostics = processAssertions(assertions, compiler);
Expand All @@ -26,6 +22,10 @@ export function transform({ code, fileName, report, typescript }: TransformSetti
}
}

if (report.includes('type-error')) {
reportDiagnostics(compiler.diagnostics, newCode, fileName);
}

return {
code: newCode.toString(),
map: newCode.generateMap({ hires: true }),
Expand Down
2 changes: 1 addition & 1 deletion src/typescript/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export interface Compiler {
program: ts.Program;
sourceFile: ts.SourceFile;
typeChecker: ts.TypeChecker;
diagnostics: ts.Diagnostic[];
compilerOptions: ts.CompilerOptions;
diagnostics: readonly ts.Diagnostic[];
}

export interface TypeScriptConfigOptions {
Expand Down
9 changes: 9 additions & 0 deletions test/tsd.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,12 @@ test('test-2', () => {
test('test-3', () => {
tsd.expectNotType<string>('hello');
});

test('test-4', () => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
tsd.expectError(42 === 'life');
});

test('test-5', () => {
tsd.expectError(true);
});

0 comments on commit b7393f8

Please sign in to comment.