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

Use tsconfig.json, clearer errors for unattached nodes #19

Merged
merged 2 commits into from
Jan 24, 2017
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
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,5 @@ jspm_packages
.vscode/

# Generated JS files
src/*.js
src/*.js.map
dist/

14 changes: 10 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
"name": "typings-checker",
"version": "1.1.1",
"description": "Positive and negative assertions about TypeScript types and errors",
"main": "src/index.js",
"main": "dist/index.js",
"scripts": {
"prepublish": "tsc",
"test": "./update-tests.sh"
},
"bin": {
"typings-checker": "src/index.js"
"typings-checker": "dist/index.js"
},
"author": "Dan Vanderkam (danvdk@gmail.com)",
"license": "ISC",
Expand All @@ -21,12 +22,17 @@
"@types/lodash": "^4.14.47",
"@types/mocha": "^2.2.36",
"@types/node": "^7.0.0",
"@types/yargs": "^6.5.0",
"chai": "^3.5.0",
"mocha": "^3.2.0",
"ts-node": "^2.0.0"
"ts-node": "^2.0.0",
"typescript": "^2.1.4"
},
"dependencies": {
"lodash": "^4.17.4",
"typescript": "^2.1.4"
"yargs": "^6.6.0"
},
"peerDependencies": {
"typescript": "2.x"
}
}
1 change: 1 addition & 0 deletions src/checker.js.map

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

79 changes: 23 additions & 56 deletions src/checker.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,12 @@
import * as ts from 'typescript';
import * as _ from 'lodash';

interface TypeAssertion {
kind: 'type';
type: string;
line: number; // line that the assertion applies to; 0-based.
}

interface ErrorAssertion {
kind: 'error';
pattern: string;
line: number; // line that the assertion applies to; 0-based.
}

type Assertion = TypeAssertion | ErrorAssertion;

export interface NodedAssertion {
assertion: Assertion;
node: ts.Node;
type: ts.Type;
error?: ts.Diagnostic;
}

interface LineNumber {
line: number; // 0-based
}

export interface WrongTypeFailure extends LineNumber {
type: 'WRONG_TYPE';
expectedType: string;
actualType: string;
}

export interface UnexpectedErrorFailure extends LineNumber {
type: 'UNEXPECTED_ERROR';
message: string;
}

export interface WrongErrorFailure extends LineNumber {
type: 'WRONG_ERROR';
expectedMessage: string;
actualMessage: string;
}

export interface MissingErrorFailure extends LineNumber {
type: 'MISSING_ERROR';
message: string;
}

type Failure = WrongTypeFailure | UnexpectedErrorFailure | WrongErrorFailure | MissingErrorFailure;

export interface Report {
numSuccesses: number;
failures: Failure[];
}
import {
Assertion,
Failure,
NodedAssertion,
Report,
} from './types';

// Extract information about the type/error assertions in a source file.
// The scanner should be positioned at the start of the file.
Expand Down Expand Up @@ -120,7 +73,16 @@ export function attachNodesToAssertions(

collectNodes(source);
if (assertions.length) {
console.error(assertions);
const prettyAssertions = assertions.map(assertion => {
let msg: string;
if (assertion.kind === 'type') {
msg = `$ExpectType ${assertion.type}`;
} else {
msg = `$ExpectError ${assertion.pattern}`
}
return `{assertion.line + 1}: ${msg}`;
});
console.error(JSON.stringify(prettyAssertions, null, '\t'));
throw new Error('Unable to attach nodes to all assertions.');
}

Expand All @@ -137,7 +99,7 @@ export function generateReport(

// Attach errors to nodes; if this isn't possible, then the error was unexpected.
for (const diagnostic of diagnostics) {
let { line } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
let line = diagnostic.file && diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start).line;

const nodedAssertion = _.find(nodedAssertions, {assertion: {line}});
if (nodedAssertion) {
Expand All @@ -153,13 +115,16 @@ export function generateReport(
}

// Go through and check all the assertions.
for (const {assertion, type, error} of nodedAssertions) {
for (const noded of nodedAssertions) {
let { assertion, type, error, node } = noded;
const line = assertion.line;
let code = node.getText();
if (assertion.kind === 'error') {
if (!error) {
failures.push({
type: 'MISSING_ERROR',
line,
code,
message: assertion.pattern
});
} else {
Expand All @@ -168,6 +133,7 @@ export function generateReport(
failures.push({
type: 'WRONG_ERROR',
line,
code,
expectedMessage: assertion.pattern,
actualMessage: message,
});
Expand All @@ -181,6 +147,7 @@ export function generateReport(
failures.push({
type: 'WRONG_TYPE',
line,
code,
expectedType: assertion.type,
actualType,
})
Expand Down
1 change: 1 addition & 0 deletions src/index.js.map

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

23 changes: 17 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,29 @@
*/

import * as ts from 'typescript';

import checkFile from './checker';

const [,, tsFile] = process.argv;
const argv = require('yargs')
.usage('Usage: <file> [options]')
.alias('p', 'project')
.describe('p', 'Path to the tsconfig.json file for your project')
.argv;

const [tsFile] = argv._;
const { project } = argv;

// TODO: read options from a tsconfig.json file.
const options: ts.CompilerOptions = {};
const host = ts.createCompilerHost(options, true);
// read options from a tsconfig.json file.
// TOOD: make this optional, just like tsc.
const options: ts.CompilerOptions = ts.readConfigFile(project || 'tsconfig.json', ts.sys.readFile).config['compilerOptions'] || {};
let host = ts.createCompilerHost(options, true);

const program = ts.createProgram([tsFile], options, host);

const source = program.getSourceFile(tsFile);
if (!source) {
console.error(`could not load content of ${tsFile}`);
process.exit(1);
}
const scanner = ts.createScanner(
ts.ScriptTarget.ES5, false, ts.LanguageVariant.Standard, source.getFullText());

Expand All @@ -47,7 +58,7 @@ for (const failure of report.failures) {
message = `Expected type\n ${failure.expectedType}\nbut got:\n ${failure.actualType}`;
break;
}
console.error(`${tsFile}:${line + 1}: ${message}\n`);
console.error(`${tsFile}:${line+1}: ${message}\n`);
}

const numFailures = report.failures.length;
Expand Down
62 changes: 62 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as ts from 'typescript';

export interface LineNumber {
line: number; // 0-based
}

export interface IAssertion extends LineNumber {
kind: string;
}

export interface TypeAssertion extends IAssertion {
kind: 'type';
type: string;
}

export interface ErrorAssertion extends IAssertion {
kind: 'error';
pattern: string;
}

export type Assertion = TypeAssertion | ErrorAssertion;

export interface NodedAssertion {
assertion: Assertion;
node: ts.Node;
type: ts.Type;
error?: ts.Diagnostic;
}

export interface IFailure extends LineNumber {
code?: string;
type: string;
}

export interface WrongTypeFailure extends IFailure {
type: 'WRONG_TYPE';
expectedType: string;
actualType: string;
}

export interface UnexpectedErrorFailure extends IFailure {
type: 'UNEXPECTED_ERROR';
message: string;
}

export interface WrongErrorFailure extends IFailure {
type: 'WRONG_ERROR';
expectedMessage: string;
actualMessage: string;
}

export interface MissingErrorFailure extends IFailure {
type: 'MISSING_ERROR';
message: string;
}

export type Failure = WrongTypeFailure | UnexpectedErrorFailure | WrongErrorFailure | MissingErrorFailure;

export interface Report {
numSuccesses: number;
failures: Failure[];
}
1 change: 1 addition & 0 deletions tests/doesnt-exist.ts.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
could not load content of doesnt_exist.ts
8 changes: 8 additions & 0 deletions tests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"noImplicitAny": false,
"sourceMap": false
}
}
2 changes: 2 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"module": "commonjs",
"target": "es5",
"noImplicitAny": true,
"outDir": "dist",
"rootDir": "src",
"sourceMap": true,
"noUnusedLocals": true,
"noEmit": false
Expand Down
8 changes: 6 additions & 2 deletions update-tests.sh
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
#!/bin/bash
set -o errexit
tsc
node_modules/.bin/tsc
set +o errexit

for test in $(find tests -name '*.ts'); do
node src/index.js $test > $test.out 2>&1
echo $test
node dist/index.js --project tests/tsconfig.json $test > $test.out 2>&1
done

# test wrong file path
node dist/index.js doesnt_exist.ts > tests/doesnt-exist.ts.out 2>&1

# This shows changes and sets the exit code.
set -o errexit
git --no-pager diff -- tests
Expand Down
Loading