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

Commit eb4314e

Browse files
committed
fix(lint): capture results of all linted files
capture results of all linted files closes #725
1 parent e13b857 commit eb4314e

File tree

5 files changed

+205
-60
lines changed

5 files changed

+205
-60
lines changed

src/lint.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as lint from './lint';
22
import * as workerClient from './worker-client';
33
import * as Constants from './util/constants';
44

5+
56
let originalEnv = process.env;
67

78
describe('lint task', () => {

src/lint.ts

Lines changed: 16 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
import { access } from 'fs';
22
import { BuildContext, ChangedFile, TaskInfo } from './util/interfaces';
3-
import { BuildError } from './util/errors';
4-
import { createProgram, findConfiguration, getFileNames } from 'tslint';
3+
4+
import { lintFile, LintResult, processLintResults } from './lint/lint-utils';
5+
import { createProgram, getFileNames } from './lint/lint-factory';
56
import { getUserConfigFile } from './util/config';
67
import * as Constants from './util/constants';
7-
import { readFileAsync, getBooleanPropertyValue } from './util/helpers';
8+
import { getBooleanPropertyValue } from './util/helpers';
89
import { join } from 'path';
910
import { Logger } from './logger/logger';
10-
import { printDiagnostics, DiagnosticsType } from './logger/logger-diagnostics';
11-
import { runTsLintDiagnostics } from './logger/logger-tslint';
11+
1212
import { runWorker } from './worker-client';
13-
import * as Linter from 'tslint';
14-
import * as fs from 'fs';
1513
import * as ts from 'typescript';
1614

1715

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

70-
const promises = files.map(file => {
71-
return lintFile(context, program, file);
72-
});
73-
74-
return Promise.all(promises);
68+
return lintFiles(context, program, files);
7569
}
7670

77-
function lintFiles(context: BuildContext, program: ts.Program, filePaths: string[]) {
78-
const promises: Promise<any>[] = [];
79-
for (const filePath of filePaths) {
80-
promises.push(lintFile(context, program, filePath));
81-
}
82-
return Promise.all(promises);
83-
}
84-
85-
function lintFile(context: BuildContext, program: ts.Program, filePath: string): Promise<any> {
71+
export function lintFiles(context: BuildContext, program: ts.Program, filePaths: string[]) {
8672
return Promise.resolve().then(() => {
87-
if (isMpegFile(filePath)) {
88-
throw new Error(`${filePath} is not a valid TypeScript file`);
73+
const promises: Promise<any>[] = [];
74+
for (const filePath of filePaths) {
75+
promises.push(lintFile(context, program, filePath));
8976
}
90-
return readFileAsync(filePath);
91-
}).then((fileContents: string) => {
92-
const configuration = findConfiguration(null, filePath);
93-
94-
const linter = new Linter(filePath, fileContents, {
95-
configuration: configuration,
96-
formatter: null,
97-
formattersDirectory: null,
98-
rulesDirectory: null,
99-
}, program);
100-
101-
const lintResult = linter.lint();
102-
if (lintResult && lintResult.failures && lintResult.failures.length) {
103-
const diagnostics = runTsLintDiagnostics(context, <any>lintResult.failures);
104-
printDiagnostics(context, DiagnosticsType.TsLint, diagnostics, true, false);
105-
throw new BuildError(`${filePath} did not pass TSLint`);
106-
}
107-
return lintResult;
77+
return Promise.all(promises);
78+
}).then((lintResults: LintResult[]) => {
79+
return processLintResults(context, lintResults);
10880
});
10981
}
11082

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

134106

135-
function isMpegFile(file: string) {
136-
var buffer = new Buffer(256);
137-
buffer.fill(0);
138-
139-
const fd = fs.openSync(file, 'r');
140-
try {
141-
fs.readSync(fd, buffer, 0, 256, null);
142-
if (buffer.readInt8(0) === 0x47 && buffer.readInt8(188) === 0x47) {
143-
Logger.debug(`tslint: ${file}: ignoring MPEG transport stream`);
144-
return true;
145-
}
146-
} finally {
147-
fs.closeSync(fd);
148-
}
149-
return false;
150-
}
151-
152-
153107
const taskInfo: TaskInfo = {
154108
fullArg: '--tslint',
155109
shortArg: '-i',
@@ -161,4 +115,6 @@ const taskInfo: TaskInfo = {
161115
export interface LintWorkerConfig {
162116
configFile: string;
163117
filePaths: string[];
164-
}
118+
};
119+
120+

src/lint/lint-factory.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { createProgram as lintCreateProgram, findConfiguration, getFileNames as lintGetFileNames } from 'tslint';
2+
import * as Linter from 'tslint';
3+
import { Program } from 'typescript';
4+
5+
export function getLinter(filePath: string, fileContent: string, program: Program) {
6+
const configuration = findConfiguration(null, filePath);
7+
8+
const linter = new Linter(filePath, fileContent, {
9+
configuration: configuration,
10+
formatter: null,
11+
formattersDirectory: null,
12+
rulesDirectory: null,
13+
}, program);
14+
15+
return linter;
16+
}
17+
18+
export function createProgram(configFilePath: string, sourceDir: string) {
19+
return lintCreateProgram(configFilePath, sourceDir);
20+
}
21+
22+
export function getFileNames(program: Program) {
23+
return lintGetFileNames(program);
24+
}

src/lint/lint-utils.spec.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import * as lintUtils from './lint-utils';
2+
import * as lintFactory from './lint-factory';
3+
import * as helpers from '../util/helpers';
4+
import * as fs from 'fs';
5+
6+
import * as tsLintLogger from '../logger/logger-tslint';
7+
import * as loggerDiagnostics from '../logger/logger-diagnostics';
8+
9+
describe('lint utils', () => {
10+
describe('lintFile', () => {
11+
it('should return lint details', () => {
12+
// arrange
13+
const mockLintResults: any = {
14+
failures: []
15+
};
16+
const mockLinter = {
17+
lint: () => {
18+
return mockLintResults;
19+
}
20+
};
21+
const filePath = '/Users/dan/someFile.ts';
22+
const fileContent = 'someContent';
23+
const mockProgram: any = {};
24+
spyOn(helpers, helpers.readFileAsync.name).and.returnValue(Promise.resolve(fileContent));
25+
spyOn(lintFactory, lintFactory.getLinter.name).and.returnValue(mockLinter);
26+
spyOn(fs, 'openSync').and.returnValue(null);
27+
spyOn(fs, 'readSync').and.returnValue(null);
28+
spyOn(fs, 'closeSync').and.returnValue(null);
29+
// act
30+
31+
const result = lintUtils.lintFile(null, mockProgram, filePath);
32+
33+
// assert
34+
return result.then((result: lintUtils.LintResult) => {
35+
expect(result.filePath).toEqual(filePath);
36+
expect(result.failures).toEqual(mockLintResults.failures);
37+
expect(lintFactory.getLinter).toHaveBeenCalledWith(filePath, fileContent, mockProgram);
38+
});
39+
});
40+
});
41+
42+
describe('processLintResults', () => {
43+
it('should complete when no files have an error', () => {
44+
// arrange
45+
const lintResults: any[] = [
46+
{
47+
failures: [],
48+
filePath: '/Users/myFileOne.ts'
49+
},
50+
{
51+
failures: [],
52+
filePath: '/Users/myFileTwo.ts'
53+
}
54+
];
55+
56+
// act
57+
lintUtils.processLintResults(null, lintResults);
58+
59+
// assert
60+
61+
});
62+
63+
it('should throw an error when one or more file has failures', () => {
64+
// arrange
65+
66+
spyOn(loggerDiagnostics, loggerDiagnostics.printDiagnostics.name).and.returnValue(null);
67+
spyOn(tsLintLogger, tsLintLogger.runTsLintDiagnostics.name).and.returnValue(null);
68+
const lintResults: any[] = [
69+
{
70+
failures: [
71+
{ }
72+
],
73+
filePath: '/Users/myFileOne.ts'
74+
},
75+
{
76+
failures: [
77+
],
78+
filePath: '/Users/myFileTwo.ts'
79+
}
80+
];
81+
const knownError = new Error('Should never get here');
82+
83+
// act
84+
try {
85+
lintUtils.processLintResults(null, lintResults);
86+
throw knownError;
87+
} catch (ex) {
88+
expect(loggerDiagnostics.printDiagnostics).toHaveBeenCalledTimes(1);
89+
expect(loggerDiagnostics.printDiagnostics).toHaveBeenCalledTimes(1);
90+
expect(ex).not.toEqual(knownError);
91+
}
92+
});
93+
});
94+
});

src/lint/lint-utils.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import * as fs from 'fs';
2+
import { Program } from 'typescript';
3+
import { BuildError } from '../util/errors';
4+
import { getLinter } from './lint-factory';
5+
import { readFileAsync } from '../util/helpers';
6+
import { BuildContext } from '../util/interfaces';
7+
import { Logger } from '../logger/logger';
8+
import { printDiagnostics, DiagnosticsType } from '../logger/logger-diagnostics';
9+
import { runTsLintDiagnostics } from '../logger/logger-tslint';
10+
11+
export function isMpegFile(file: string) {
12+
var buffer = new Buffer(256);
13+
buffer.fill(0);
14+
15+
const fd = fs.openSync(file, 'r');
16+
try {
17+
fs.readSync(fd, buffer, 0, 256, null);
18+
if (buffer.readInt8(0) === 0x47 && buffer.readInt8(188) === 0x47) {
19+
Logger.debug(`tslint: ${file}: ignoring MPEG transport stream`);
20+
return true;
21+
}
22+
} finally {
23+
fs.closeSync(fd);
24+
}
25+
return false;
26+
}
27+
28+
export function lintFile(context: BuildContext, program: Program, filePath: string): Promise<LintResult> {
29+
return Promise.resolve().then(() => {
30+
if (isMpegFile(filePath)) {
31+
throw new Error(`${filePath} is not a valid TypeScript file`);
32+
}
33+
return readFileAsync(filePath);
34+
}).then((fileContents: string) => {
35+
36+
const linter = getLinter(filePath, fileContents, program);
37+
const lintResult = linter.lint();
38+
39+
return {
40+
filePath: filePath,
41+
failures: lintResult.failures
42+
};
43+
});
44+
}
45+
46+
export function processLintResults(context: BuildContext, lintResults: LintResult[]) {
47+
const filesThatDidntPass: string[] = [];
48+
for (const lintResult of lintResults) {
49+
if (lintResult && lintResult.failures && lintResult.failures.length) {
50+
const diagnostics = runTsLintDiagnostics(context, <any>lintResult.failures);
51+
printDiagnostics(context, DiagnosticsType.TsLint, diagnostics, true, false);
52+
filesThatDidntPass.push(lintResult.filePath);
53+
}
54+
}
55+
if (filesThatDidntPass.length) {
56+
const errorMsg = generateFormattedErrorMsg(filesThatDidntPass);
57+
throw new BuildError(errorMsg);
58+
}
59+
}
60+
61+
export function generateFormattedErrorMsg(failingFiles: string[]) {
62+
let listOfFilesString = '';
63+
failingFiles.forEach(file => listOfFilesString = listOfFilesString + file + '\n');
64+
return `The following files did not pass tslint: \n${listOfFilesString}`;
65+
}
66+
67+
export interface LintResult {
68+
failures: any[];
69+
filePath: string;
70+
};

0 commit comments

Comments
 (0)