Skip to content

Commit de3a076

Browse files
clydinhansl
authored andcommitted
feat(@angular-devkit/build-webpack): support multi-tsconfig tslint
1 parent 438266a commit de3a076

File tree

3 files changed

+100
-31
lines changed

3 files changed

+100
-31
lines changed

packages/angular_devkit/build_webpack/src/tslint/index.ts

Lines changed: 60 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { stripBom } from '../angular-cli-files/utilities/strip-bom';
2626

2727
export interface TslintBuilderOptions {
2828
tslintConfig?: string;
29-
tsConfig?: string;
29+
tsConfig?: string | string[];
3030
fix: boolean;
3131
typeCheck: boolean;
3232
force: boolean;
@@ -58,39 +58,32 @@ export class TslintBuilder implements Builder<TslintBuilderOptions> {
5858
? path.resolve(systemRoot, options.tslintConfig)
5959
: null;
6060
const Linter = projectTslint.Linter;
61-
const Configuration = projectTslint.Configuration;
6261

63-
let program: ts.Program | undefined = undefined;
62+
let result;
6463
if (options.tsConfig) {
65-
program = Linter.createProgram(path.resolve(systemRoot, options.tsConfig));
66-
}
67-
68-
const files = getFilesToLint(systemRoot, options, Linter, program);
69-
const lintOptions = {
70-
fix: options.fix,
71-
formatter: options.format,
72-
};
73-
74-
const linter = new Linter(lintOptions, program);
75-
76-
let lastDirectory;
77-
let configLoad;
78-
for (const file of files) {
79-
const contents = getFileContents(file, options, program);
80-
81-
// Only check for a new tslint config if the path changes.
82-
const currentDirectory = path.dirname(file);
83-
if (currentDirectory !== lastDirectory) {
84-
configLoad = Configuration.findConfiguration(tslintConfigPath, file);
85-
lastDirectory = currentDirectory;
86-
}
87-
88-
if (contents && configLoad) {
89-
linter.lint(file, contents, configLoad.results);
64+
const tsConfigs = Array.isArray(options.tsConfig) ? options.tsConfig : [options.tsConfig];
65+
66+
for (const tsConfig of tsConfigs) {
67+
const program = Linter.createProgram(path.resolve(systemRoot, tsConfig));
68+
const partial = lint(projectTslint, systemRoot, tslintConfigPath, options, program);
69+
if (result == undefined) {
70+
result = partial;
71+
} else {
72+
result.errorCount += partial.errorCount;
73+
result.warningCount += partial.warningCount;
74+
result.failures = result.failures.concat(partial.failures);
75+
if (partial.fixes) {
76+
result.fixes = result.fixes ? result.fixes.concat(partial.fixes) : partial.fixes;
77+
}
78+
}
9079
}
80+
} else {
81+
result = lint(projectTslint, systemRoot, tslintConfigPath, options);
9182
}
9283

93-
const result = linter.getResult();
84+
if (result == undefined) {
85+
throw new Error('Invalid lint configuration. Nothing to lint.');
86+
}
9487

9588
if (!options.silent) {
9689
const Formatter = projectTslint.findFormatter(options.format);
@@ -130,6 +123,44 @@ export class TslintBuilder implements Builder<TslintBuilderOptions> {
130123
}
131124
}
132125

126+
function lint(
127+
projectTslint: typeof tslint,
128+
systemRoot: string,
129+
tslintConfigPath: string | null,
130+
options: TslintBuilderOptions,
131+
program?: ts.Program,
132+
) {
133+
const Linter = projectTslint.Linter;
134+
const Configuration = projectTslint.Configuration;
135+
136+
const files = getFilesToLint(systemRoot, options, Linter, program);
137+
const lintOptions = {
138+
fix: options.fix,
139+
formatter: options.format,
140+
};
141+
142+
const linter = new Linter(lintOptions, program);
143+
144+
let lastDirectory;
145+
let configLoad;
146+
for (const file of files) {
147+
const contents = getFileContents(file, options, program);
148+
149+
// Only check for a new tslint config if the path changes.
150+
const currentDirectory = path.dirname(file);
151+
if (currentDirectory !== lastDirectory) {
152+
configLoad = Configuration.findConfiguration(tslintConfigPath, file);
153+
lastDirectory = currentDirectory;
154+
}
155+
156+
if (contents && configLoad) {
157+
linter.lint(file, contents, configLoad.results);
158+
}
159+
}
160+
161+
return linter.getResult();
162+
}
163+
133164
function getFilesToLint(
134165
root: string,
135166
options: TslintBuilderOptions,

packages/angular_devkit/build_webpack/src/tslint/schema.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,16 @@
88
"description": "The name of the TSLint configuration file."
99
},
1010
"tsConfig": {
11-
"type": "string",
12-
"description": "The name of the TypeScript configuration file."
11+
"description": "The name of the TypeScript configuration file.",
12+
"oneOf": [
13+
{ "type": "string" },
14+
{
15+
"type": "array",
16+
"items": {
17+
"type": "string"
18+
}
19+
}
20+
]
1321
},
1422
"fix": {
1523
"type": "boolean",

packages/angular_devkit/build_webpack/test/tslint/works_spec_large.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,36 @@ describe('Tslint Target', () => {
127127
).subscribe(undefined, done.fail, done);
128128
}, 30000);
129129

130+
it('supports using one project as a string', (done) => {
131+
const overrides: Partial<TslintBuilderOptions> = {
132+
tsConfig: 'src/tsconfig.app.json',
133+
};
134+
135+
runTargetSpec(host, tslintTargetSpec, overrides).pipe(
136+
tap((buildEvent) => expect(buildEvent.success).toBe(true)),
137+
).subscribe(undefined, done.fail, done);
138+
}, 30000);
139+
140+
it('supports using one project as an array', (done) => {
141+
const overrides: Partial<TslintBuilderOptions> = {
142+
tsConfig: ['src/tsconfig.app.json'],
143+
};
144+
145+
runTargetSpec(host, tslintTargetSpec, overrides).pipe(
146+
tap((buildEvent) => expect(buildEvent.success).toBe(true)),
147+
).subscribe(undefined, done.fail, done);
148+
}, 30000);
149+
150+
it('supports using two projects', (done) => {
151+
const overrides: Partial<TslintBuilderOptions> = {
152+
tsConfig: ['src/tsconfig.app.json', 'src/tsconfig.spec.json'],
153+
};
154+
155+
runTargetSpec(host, tslintTargetSpec, overrides).pipe(
156+
tap((buildEvent) => expect(buildEvent.success).toBe(true)),
157+
).subscribe(undefined, done.fail, done);
158+
}, 30000);
159+
130160
it('errors when type checking is used without a project', (done) => {
131161
const overrides: Partial<TslintBuilderOptions> = {
132162
tsConfig: undefined,

0 commit comments

Comments
 (0)