Skip to content

Commit 03c612e

Browse files
konstantinov90johnnyreilly
authored andcommitted
Respect tslint configs hierarchical order (#214)
* respect TSLint configs hierarchical order * fixed settings + doc * pass correct options to IncrementalChecker * fix passing settings * fix tests * review fix #1 * integration test for tslint hierarchical order * fix quotes * moved test to common * actual test files... * changelog * review related fix * fix test after prettier * fix * optimize common functionality
1 parent 903d1de commit 03c612e

File tree

18 files changed

+233
-70
lines changed

18 files changed

+233
-70
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ logs
44
npm-debug.log*
55

66
lib
7+
!test/**/lib
78

89
# Coverage directory used by tools like istanbul
910
coverage
@@ -24,4 +25,4 @@ package-lock.json
2425

2526
# Editor directories and files
2627
.idea
27-
.vscode
28+
.vscode

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## v1.0.0-alpha.8
2+
3+
* [Respect tslint configs hierarchical order](https://github.com/Realytics/fork-ts-checker-webpack-plugin/pull/214)
4+
15
## v1.0.0-alpha.7
26

37
* [Add ignoreLintWarnings option](https://github.com/Realytics/fork-ts-checker-webpack-plugin/pull/213)

README.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,26 @@ Path to *tsconfig.json* file. Default: `path.resolve(compiler.options.context, '
6363
* **compilerOptions** `object`:
6464
Allows overriding TypeScript options. Should be specified in the same format as you would do for the `compilerOptions` property in tsconfig.json. Default: `{}`.
6565

66-
* **tslint** `string | true`:
67-
Path to *tslint.json* file or `true`. If `true`, uses `path.resolve(compiler.options.context, './tslint.json')`. Default: `undefined`.
66+
* **tslint** `string | true | undefined`:
67+
- If `string`, path to *tslint.json* file to check source files against.
68+
- If `true`, path to `tslint.json` file will be computed with respect to currently checked file, just like TSLint
69+
CLI would do. Suppose you have a project:
70+
```
71+
./
72+
tslint.json
73+
74+
src/
75+
file.ts
76+
anotherFile.ts
77+
78+
lib/
79+
tslint.json
80+
someHelperFile.ts
81+
```
82+
In such a case `src/file.ts` and `src/anotherFile.ts` would be checked against root `tslint.json`, and
83+
`src/lib/someHelperFile.ts` would be checked against `src/lib/tslint.json`.
84+
85+
Default: `undefined`.
6886
6987
* **tslintAutoFix** `boolean `:
7088
Passes on `--fix` flag while running `tslint` to auto fix linting errors. Default: false.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fork-ts-checker-webpack-plugin",
3-
"version": "1.0.0-alpha.7",
3+
"version": "1.0.0-alpha.8",
44
"description": "Runs typescript type checker and linter on separate process.",
55
"main": "lib/index.js",
66
"types": "lib/index.d.ts",

src/ApiIncrementalChecker.ts

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
11
// tslint:disable-next-line:no-implicit-dependencies
22
import * as ts from 'typescript'; // Imported for types alone
33
// tslint:disable-next-line:no-implicit-dependencies
4-
import { Configuration, Linter, LintResult, RuleFailure } from 'tslint';
4+
import { Linter, LintResult, RuleFailure } from 'tslint';
55
import * as minimatch from 'minimatch';
66
import * as path from 'path';
77
import { IncrementalCheckerInterface } from './IncrementalCheckerInterface';
88
import { CancellationToken } from './CancellationToken';
9+
import {
10+
ConfigurationFile,
11+
loadLinterConfig,
12+
makeGetLinterConfig
13+
} from './linterConfigHelpers';
914
import { NormalizedMessage } from './NormalizedMessage';
1015
import { CompilerHost } from './CompilerHost';
1116
import { FsHelper } from './FsHelper';
1217

13-
// Need some augmentation here - linterOptions.exclude is not (yet) part of the official
14-
// types for tslint.
15-
interface ConfigurationFile extends Configuration.IConfigurationFile {
16-
linterOptions?: {
17-
typeCheck?: boolean;
18-
exclude?: string[];
19-
};
20-
}
21-
2218
export class ApiIncrementalChecker implements IncrementalCheckerInterface {
2319
private linterConfig?: ConfigurationFile;
20+
private linterConfigs: Record<string, ConfigurationFile | undefined> = {};
2421

2522
private readonly tsIncrementalCompiler: CompilerHost;
2623
private linterExclusions: minimatch.IMinimatch[] = [];
@@ -29,6 +26,8 @@ export class ApiIncrementalChecker implements IncrementalCheckerInterface {
2926
private lastUpdatedFiles: string[] = [];
3027
private lastRemovedFiles: string[] = [];
3128

29+
private readonly hasFixedConfig: boolean;
30+
3231
constructor(
3332
typescript: typeof ts,
3433
private createNormalizedMessageFromDiagnostic: (
@@ -39,10 +38,13 @@ export class ApiIncrementalChecker implements IncrementalCheckerInterface {
3938
) => NormalizedMessage,
4039
programConfigFile: string,
4140
compilerOptions: ts.CompilerOptions,
42-
private linterConfigFile: string | false,
41+
private context: string,
42+
private linterConfigFile: string | boolean,
4343
private linterAutoFix: boolean,
4444
checkSyntacticErrors: boolean
4545
) {
46+
this.hasFixedConfig = typeof this.linterConfigFile === 'string';
47+
4648
this.initLinterConfig();
4749

4850
this.tsIncrementalCompiler = new CompilerHost(
@@ -54,10 +56,8 @@ export class ApiIncrementalChecker implements IncrementalCheckerInterface {
5456
}
5557

5658
private initLinterConfig() {
57-
if (!this.linterConfig && this.linterConfigFile) {
58-
this.linterConfig = ApiIncrementalChecker.loadLinterConfig(
59-
this.linterConfigFile
60-
);
59+
if (!this.linterConfig && this.hasFixedConfig) {
60+
this.linterConfig = loadLinterConfig(this.linterConfigFile as string);
6161

6262
if (
6363
this.linterConfig.linterOptions &&
@@ -73,14 +73,13 @@ export class ApiIncrementalChecker implements IncrementalCheckerInterface {
7373
}
7474
}
7575

76-
private static loadLinterConfig(configFile: string): ConfigurationFile {
77-
// tslint:disable-next-line:no-implicit-dependencies
78-
const tslint = require('tslint');
79-
80-
return tslint.Configuration.loadConfigurationFromPath(
81-
configFile
82-
) as ConfigurationFile;
83-
}
76+
private getLinterConfig: (
77+
file: string
78+
) => ConfigurationFile | undefined = makeGetLinterConfig(
79+
this.linterConfigs,
80+
this.linterExclusions,
81+
this.context
82+
);
8483

8584
private createLinter(program: ts.Program): Linter {
8685
// tslint:disable-next-line:no-implicit-dependencies
@@ -90,7 +89,7 @@ export class ApiIncrementalChecker implements IncrementalCheckerInterface {
9089
}
9190

9291
public hasLinter(): boolean {
93-
return !!this.linterConfig;
92+
return !!this.linterConfigFile;
9493
}
9594

9695
public isFileExcluded(filePath: string): boolean {
@@ -115,10 +114,6 @@ export class ApiIncrementalChecker implements IncrementalCheckerInterface {
115114
}
116115

117116
public getLints(_cancellationToken: CancellationToken) {
118-
if (!this.linterConfig) {
119-
return [];
120-
}
121-
122117
for (const updatedFile of this.lastUpdatedFiles) {
123118
if (this.isFileExcluded(updatedFile)) {
124119
continue;
@@ -128,8 +123,14 @@ export class ApiIncrementalChecker implements IncrementalCheckerInterface {
128123
const linter = this.createLinter(
129124
this.tsIncrementalCompiler.getProgram()
130125
);
126+
const config = this.hasFixedConfig
127+
? this.linterConfig
128+
: this.getLinterConfig(updatedFile);
129+
if (!config) {
130+
continue;
131+
}
131132
// const source = fs.readFileSync(updatedFile, 'utf-8');
132-
linter.lint(updatedFile, undefined!, this.linterConfig);
133+
linter.lint(updatedFile, undefined!, config);
133134
const lints = linter.getResult();
134135
this.currentLintErrors.set(updatedFile, lints);
135136
} catch (e) {

src/IncrementalChecker.ts

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@ import * as path from 'path';
33
// tslint:disable-next-line:no-implicit-dependencies
44
import * as ts from 'typescript'; // Imported for types alone; actual requires take place in methods below
55
// tslint:disable-next-line:no-implicit-dependencies
6-
import { Configuration, Linter, RuleFailure } from 'tslint'; // Imported for types alone; actual requires take place in methods below
6+
import { Linter, RuleFailure } from 'tslint'; // Imported for types alone; actual requires take place in methods below
77
import { FilesRegister } from './FilesRegister';
88
import { FilesWatcher } from './FilesWatcher';
9+
import {
10+
ConfigurationFile,
11+
loadLinterConfig,
12+
makeGetLinterConfig
13+
} from './linterConfigHelpers';
914
import { WorkSet } from './WorkSet';
1015
import { NormalizedMessage } from './NormalizedMessage';
1116
import { CancellationToken } from './CancellationToken';
@@ -14,17 +19,9 @@ import { VueProgram } from './VueProgram';
1419
import { FsHelper } from './FsHelper';
1520
import { IncrementalCheckerInterface } from './IncrementalCheckerInterface';
1621

17-
// Need some augmentation here - linterOptions.exclude is not (yet) part of the official
18-
// types for tslint.
19-
interface ConfigurationFile extends Configuration.IConfigurationFile {
20-
linterOptions?: {
21-
typeCheck?: boolean;
22-
exclude?: string[];
23-
};
24-
}
25-
2622
export class IncrementalChecker implements IncrementalCheckerInterface {
2723
// it's shared between compilations
24+
private linterConfigs: Record<string, ConfigurationFile | undefined> = {};
2825
private files = new FilesRegister(() => ({
2926
// data shape
3027
source: undefined,
@@ -43,6 +40,8 @@ export class IncrementalChecker implements IncrementalCheckerInterface {
4340
private programConfig?: ts.ParsedCommandLine;
4441
private watcher?: FilesWatcher;
4542

43+
private readonly hasFixedConfig: boolean;
44+
4645
constructor(
4746
private typescript: typeof ts,
4847
private createNormalizedMessageFromDiagnostic: (
@@ -53,14 +52,17 @@ export class IncrementalChecker implements IncrementalCheckerInterface {
5352
) => NormalizedMessage,
5453
private programConfigFile: string,
5554
private compilerOptions: object,
56-
private linterConfigFile: string | false,
55+
private context: string,
56+
private linterConfigFile: string | boolean,
5757
private linterAutoFix: boolean,
5858
private watchPaths: string[],
5959
private workNumber: number = 0,
6060
private workDivision: number = 1,
6161
private checkSyntacticErrors: boolean = false,
6262
private vue: boolean = false
63-
) {}
63+
) {
64+
this.hasFixedConfig = typeof this.linterConfigFile === 'string';
65+
}
6466

6567
public static loadProgramConfig(
6668
typescript: typeof ts,
@@ -87,14 +89,13 @@ export class IncrementalChecker implements IncrementalCheckerInterface {
8789
return parsed;
8890
}
8991

90-
private static loadLinterConfig(configFile: string): ConfigurationFile {
91-
// tslint:disable-next-line:no-implicit-dependencies
92-
const tslint = require('tslint');
93-
94-
return tslint.Configuration.loadConfigurationFromPath(
95-
configFile
96-
) as ConfigurationFile;
97-
}
92+
private getLinterConfig: (
93+
file: string
94+
) => ConfigurationFile | undefined = makeGetLinterConfig(
95+
this.linterConfigs,
96+
this.linterExclusions,
97+
this.context
98+
);
9899

99100
private static createProgram(
100101
typescript: typeof ts,
@@ -176,10 +177,8 @@ export class IncrementalChecker implements IncrementalCheckerInterface {
176177
this.watcher.watch();
177178
}
178179

179-
if (!this.linterConfig && this.linterConfigFile) {
180-
this.linterConfig = IncrementalChecker.loadLinterConfig(
181-
this.linterConfigFile
182-
);
180+
if (!this.linterConfig && this.hasFixedConfig) {
181+
this.linterConfig = loadLinterConfig(this.linterConfigFile as string);
183182

184183
if (
185184
this.linterConfig.linterOptions &&
@@ -196,7 +195,7 @@ export class IncrementalChecker implements IncrementalCheckerInterface {
196195

197196
this.program = this.vue ? this.loadVueProgram() : this.loadDefaultProgram();
198197

199-
if (this.linterConfig) {
198+
if (this.linterConfigFile) {
200199
this.linter = this.createLinter(this.program!);
201200
}
202201
}
@@ -305,10 +304,16 @@ export class IncrementalChecker implements IncrementalCheckerInterface {
305304
// lint given work set
306305
workSet.forEach(fileName => {
307306
cancellationToken.throwIfCancellationRequested();
307+
const config = this.hasFixedConfig
308+
? this.linterConfig
309+
: this.getLinterConfig(fileName);
310+
if (!config) {
311+
return;
312+
}
308313

309314
try {
310315
// Assertion: `.lint` second parameter can be undefined
311-
linter.lint(fileName, undefined!, this.linterConfig);
316+
linter.lint(fileName, undefined!, config);
312317
} catch (e) {
313318
if (
314319
FsHelper.existsSync(fileName) &&

src/index.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ class ForkTsCheckerWebpackPlugin {
138138
: {};
139139
this.tslint = options.tslint
140140
? options.tslint === true
141-
? './tslint.json'
141+
? true
142142
: options.tslint
143143
: undefined;
144144
this.tslintAutoFix = options.tslintAutoFix || false;
@@ -249,9 +249,10 @@ class ForkTsCheckerWebpackPlugin {
249249
this.compiler = compiler;
250250

251251
this.tsconfigPath = this.computeContextPath(this.tsconfig);
252-
this.tslintPath = this.tslint
253-
? this.computeContextPath(this.tslint as string)
254-
: undefined;
252+
this.tslintPath =
253+
typeof this.tslint === 'string'
254+
? this.computeContextPath(this.tslint as string)
255+
: undefined;
255256
this.watchPaths = this.watch.map(this.computeContextPath);
256257

257258
// validate config
@@ -561,7 +562,8 @@ class ForkTsCheckerWebpackPlugin {
561562
TYPESCRIPT_PATH: this.typescriptPath,
562563
TSCONFIG: this.tsconfigPath,
563564
COMPILER_OPTIONS: JSON.stringify(this.compilerOptions),
564-
TSLINT: this.tslintPath || '',
565+
TSLINT: this.tslintPath || (this.tslint ? 'true' : ''),
566+
CONTEXT: this.compiler.options.context,
565567
TSLINTAUTOFIX: this.tslintAutoFix,
566568
WATCH: this.isWatching ? this.watchPaths.join('|') : '',
567569
WORK_DIVISION: Math.max(1, this.workersNumber),

0 commit comments

Comments
 (0)