Skip to content

Commit

Permalink
Merge pull request #19 from danvk/pr-13-update
Browse files Browse the repository at this point in the history
Use tsconfig.json, clearer errors for unattached nodes
  • Loading branch information
danvk authored Jan 24, 2017
2 parents 712ca51 + 1a495f8 commit bf30dec
Show file tree
Hide file tree
Showing 12 changed files with 356 additions and 72 deletions.
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

0 comments on commit bf30dec

Please sign in to comment.