Skip to content
This repository has been archived by the owner on May 1, 2020. It is now read-only.

Commit

Permalink
fix(lint): capture results of all linted files
Browse files Browse the repository at this point in the history
capture results of all linted files

closes #725
  • Loading branch information
danbucholtz committed Feb 8, 2017
1 parent e13b857 commit eb4314e
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 60 deletions.
1 change: 1 addition & 0 deletions src/lint.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as lint from './lint';
import * as workerClient from './worker-client';
import * as Constants from './util/constants';


let originalEnv = process.env;

describe('lint task', () => {
Expand Down
76 changes: 16 additions & 60 deletions src/lint.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { access } from 'fs';
import { BuildContext, ChangedFile, TaskInfo } from './util/interfaces';
import { BuildError } from './util/errors';
import { createProgram, findConfiguration, getFileNames } from 'tslint';

import { lintFile, LintResult, processLintResults } from './lint/lint-utils';
import { createProgram, getFileNames } from './lint/lint-factory';
import { getUserConfigFile } from './util/config';
import * as Constants from './util/constants';
import { readFileAsync, getBooleanPropertyValue } from './util/helpers';
import { getBooleanPropertyValue } from './util/helpers';
import { join } from 'path';
import { Logger } from './logger/logger';
import { printDiagnostics, DiagnosticsType } from './logger/logger-diagnostics';
import { runTsLintDiagnostics } from './logger/logger-tslint';

import { runWorker } from './worker-client';
import * as Linter from 'tslint';
import * as fs from 'fs';
import * as ts from 'typescript';


Expand Down Expand Up @@ -67,44 +65,18 @@ function lintApp(context: BuildContext, configFile: string) {
const program = createProgram(configFile, context.srcDir);
const files = getFileNames(program);

const promises = files.map(file => {
return lintFile(context, program, file);
});

return Promise.all(promises);
return lintFiles(context, program, files);
}

function lintFiles(context: BuildContext, program: ts.Program, filePaths: string[]) {
const promises: Promise<any>[] = [];
for (const filePath of filePaths) {
promises.push(lintFile(context, program, filePath));
}
return Promise.all(promises);
}

function lintFile(context: BuildContext, program: ts.Program, filePath: string): Promise<any> {
export function lintFiles(context: BuildContext, program: ts.Program, filePaths: string[]) {
return Promise.resolve().then(() => {
if (isMpegFile(filePath)) {
throw new Error(`${filePath} is not a valid TypeScript file`);
const promises: Promise<any>[] = [];
for (const filePath of filePaths) {
promises.push(lintFile(context, program, filePath));
}
return readFileAsync(filePath);
}).then((fileContents: string) => {
const configuration = findConfiguration(null, filePath);

const linter = new Linter(filePath, fileContents, {
configuration: configuration,
formatter: null,
formattersDirectory: null,
rulesDirectory: null,
}, program);

const lintResult = linter.lint();
if (lintResult && lintResult.failures && lintResult.failures.length) {
const diagnostics = runTsLintDiagnostics(context, <any>lintResult.failures);
printDiagnostics(context, DiagnosticsType.TsLint, diagnostics, true, false);
throw new BuildError(`${filePath} did not pass TSLint`);
}
return lintResult;
return Promise.all(promises);
}).then((lintResults: LintResult[]) => {
return processLintResults(context, lintResults);
});
}

Expand Down Expand Up @@ -132,24 +104,6 @@ function getLintConfig(context: BuildContext, configFile: string): Promise<strin
}


function isMpegFile(file: string) {
var buffer = new Buffer(256);
buffer.fill(0);

const fd = fs.openSync(file, 'r');
try {
fs.readSync(fd, buffer, 0, 256, null);
if (buffer.readInt8(0) === 0x47 && buffer.readInt8(188) === 0x47) {
Logger.debug(`tslint: ${file}: ignoring MPEG transport stream`);
return true;
}
} finally {
fs.closeSync(fd);
}
return false;
}


const taskInfo: TaskInfo = {
fullArg: '--tslint',
shortArg: '-i',
Expand All @@ -161,4 +115,6 @@ const taskInfo: TaskInfo = {
export interface LintWorkerConfig {
configFile: string;
filePaths: string[];
}
};


24 changes: 24 additions & 0 deletions src/lint/lint-factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { createProgram as lintCreateProgram, findConfiguration, getFileNames as lintGetFileNames } from 'tslint';
import * as Linter from 'tslint';
import { Program } from 'typescript';

export function getLinter(filePath: string, fileContent: string, program: Program) {
const configuration = findConfiguration(null, filePath);

const linter = new Linter(filePath, fileContent, {
configuration: configuration,
formatter: null,
formattersDirectory: null,
rulesDirectory: null,
}, program);

return linter;
}

export function createProgram(configFilePath: string, sourceDir: string) {
return lintCreateProgram(configFilePath, sourceDir);
}

export function getFileNames(program: Program) {
return lintGetFileNames(program);
}
94 changes: 94 additions & 0 deletions src/lint/lint-utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import * as lintUtils from './lint-utils';
import * as lintFactory from './lint-factory';
import * as helpers from '../util/helpers';
import * as fs from 'fs';

import * as tsLintLogger from '../logger/logger-tslint';
import * as loggerDiagnostics from '../logger/logger-diagnostics';

describe('lint utils', () => {
describe('lintFile', () => {
it('should return lint details', () => {
// arrange
const mockLintResults: any = {
failures: []
};
const mockLinter = {
lint: () => {
return mockLintResults;
}
};
const filePath = '/Users/dan/someFile.ts';
const fileContent = 'someContent';
const mockProgram: any = {};
spyOn(helpers, helpers.readFileAsync.name).and.returnValue(Promise.resolve(fileContent));
spyOn(lintFactory, lintFactory.getLinter.name).and.returnValue(mockLinter);
spyOn(fs, 'openSync').and.returnValue(null);
spyOn(fs, 'readSync').and.returnValue(null);
spyOn(fs, 'closeSync').and.returnValue(null);
// act

const result = lintUtils.lintFile(null, mockProgram, filePath);

// assert
return result.then((result: lintUtils.LintResult) => {
expect(result.filePath).toEqual(filePath);
expect(result.failures).toEqual(mockLintResults.failures);
expect(lintFactory.getLinter).toHaveBeenCalledWith(filePath, fileContent, mockProgram);
});
});
});

describe('processLintResults', () => {
it('should complete when no files have an error', () => {
// arrange
const lintResults: any[] = [
{
failures: [],
filePath: '/Users/myFileOne.ts'
},
{
failures: [],
filePath: '/Users/myFileTwo.ts'
}
];

// act
lintUtils.processLintResults(null, lintResults);

// assert

});

it('should throw an error when one or more file has failures', () => {
// arrange

spyOn(loggerDiagnostics, loggerDiagnostics.printDiagnostics.name).and.returnValue(null);
spyOn(tsLintLogger, tsLintLogger.runTsLintDiagnostics.name).and.returnValue(null);
const lintResults: any[] = [
{
failures: [
{ }
],
filePath: '/Users/myFileOne.ts'
},
{
failures: [
],
filePath: '/Users/myFileTwo.ts'
}
];
const knownError = new Error('Should never get here');

// act
try {
lintUtils.processLintResults(null, lintResults);
throw knownError;
} catch (ex) {
expect(loggerDiagnostics.printDiagnostics).toHaveBeenCalledTimes(1);
expect(loggerDiagnostics.printDiagnostics).toHaveBeenCalledTimes(1);
expect(ex).not.toEqual(knownError);
}
});
});
});
70 changes: 70 additions & 0 deletions src/lint/lint-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as fs from 'fs';
import { Program } from 'typescript';
import { BuildError } from '../util/errors';
import { getLinter } from './lint-factory';
import { readFileAsync } from '../util/helpers';
import { BuildContext } from '../util/interfaces';
import { Logger } from '../logger/logger';
import { printDiagnostics, DiagnosticsType } from '../logger/logger-diagnostics';
import { runTsLintDiagnostics } from '../logger/logger-tslint';

export function isMpegFile(file: string) {
var buffer = new Buffer(256);
buffer.fill(0);

const fd = fs.openSync(file, 'r');
try {
fs.readSync(fd, buffer, 0, 256, null);
if (buffer.readInt8(0) === 0x47 && buffer.readInt8(188) === 0x47) {
Logger.debug(`tslint: ${file}: ignoring MPEG transport stream`);
return true;
}
} finally {
fs.closeSync(fd);
}
return false;
}

export function lintFile(context: BuildContext, program: Program, filePath: string): Promise<LintResult> {
return Promise.resolve().then(() => {
if (isMpegFile(filePath)) {
throw new Error(`${filePath} is not a valid TypeScript file`);
}
return readFileAsync(filePath);
}).then((fileContents: string) => {

const linter = getLinter(filePath, fileContents, program);
const lintResult = linter.lint();

return {
filePath: filePath,
failures: lintResult.failures
};
});
}

export function processLintResults(context: BuildContext, lintResults: LintResult[]) {
const filesThatDidntPass: string[] = [];
for (const lintResult of lintResults) {
if (lintResult && lintResult.failures && lintResult.failures.length) {
const diagnostics = runTsLintDiagnostics(context, <any>lintResult.failures);
printDiagnostics(context, DiagnosticsType.TsLint, diagnostics, true, false);
filesThatDidntPass.push(lintResult.filePath);
}
}
if (filesThatDidntPass.length) {
const errorMsg = generateFormattedErrorMsg(filesThatDidntPass);
throw new BuildError(errorMsg);
}
}

export function generateFormattedErrorMsg(failingFiles: string[]) {
let listOfFilesString = '';
failingFiles.forEach(file => listOfFilesString = listOfFilesString + file + '\n');
return `The following files did not pass tslint: \n${listOfFilesString}`;
}

export interface LintResult {
failures: any[];
filePath: string;
};

0 comments on commit eb4314e

Please sign in to comment.