Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): error only when file is not found…
Browse files Browse the repository at this point in the history
… in non of the programs

At the moment, if a user provides multiple tsconfig, a file needs to be part of all compilations as otherwise it will fail.

This PR changes this behavour and as long as it's in one of the compilations it will not error out.

Fixes #13399
  • Loading branch information
Alan authored and mgechev committed Jan 23, 2019
1 parent 5cdd5e1 commit b500420
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 22 deletions.
44 changes: 23 additions & 21 deletions packages/angular_devkit/build_angular/src/tslint/index.ts
Expand Up @@ -84,10 +84,12 @@ export default class TslintBuilder implements Builder<TslintBuilderOptions> {
let result: undefined | tslint.LintResult;
if (options.tsConfig) {
const tsConfigs = Array.isArray(options.tsConfig) ? options.tsConfig : [options.tsConfig];
const allPrograms =
tsConfigs.map(tsConfig => Linter.createProgram(path.resolve(systemRoot, tsConfig)));

for (const tsConfig of tsConfigs) {
const program = Linter.createProgram(path.resolve(systemRoot, tsConfig));
const partial = lint(projectTslint, systemRoot, tslintConfigPath, options, program);
for (const program of allPrograms) {
const partial
= lint(projectTslint, systemRoot, tslintConfigPath, options, program, allPrograms);
if (result == undefined) {
result = partial;
} else {
Expand Down Expand Up @@ -152,6 +154,7 @@ function lint(
tslintConfigPath: string | null,
options: TslintBuilderOptions,
program?: ts.Program,
allPrograms?: ts.Program[],
) {
const Linter = projectTslint.Linter;
const Configuration = projectTslint.Configuration;
Expand All @@ -167,7 +170,21 @@ function lint(
let lastDirectory;
let configLoad;
for (const file of files) {
const contents = getFileContents(file, options, program);
let contents = '';
if (program && allPrograms) {
if (!program.getSourceFile(file)) {
if (!allPrograms.some(p => p.getSourceFile(file) !== undefined)) {
// File is not part of any typescript program
throw new Error(
`File '${file}' is not part of a TypeScript project '${options.tsConfig}'.`);
}

// if the Source file exists but it's not in the current program skip
continue;
}
} else {
contents = getFileContents(file);
}

// Only check for a new tslint config if the path changes.
const currentDirectory = path.dirname(file);
Expand All @@ -176,7 +193,7 @@ function lint(
lastDirectory = currentDirectory;
}

if (contents && configLoad) {
if (configLoad) {
linter.lint(file, contents, configLoad.results);
}
}
Expand Down Expand Up @@ -217,22 +234,7 @@ function getFilesToLint(
return programFiles;
}

function getFileContents(
file: string,
options: TslintBuilderOptions,
program?: ts.Program,
): string | undefined {
// The linter retrieves the SourceFile TS node directly if a program is used
if (program) {
if (program.getSourceFile(file) == undefined) {
const message = `File '${file}' is not part of the TypeScript project '${options.tsConfig}'.`;
throw new Error(message);
}

// TODO: this return had to be commented out otherwise no file would be linted, figure out why.
// return undefined;
}

function getFileContents(file: string): string {
// NOTE: The tslint CLI checks for and excludes MPEG transport streams; this does not.
try {
return stripBom(readFileSync(file, 'utf-8'));
Expand Down
Expand Up @@ -8,7 +8,8 @@

import { DefaultTimeout, TestLogger, runTargetSpec } from '@angular-devkit/architect/testing';
import { normalize, virtualFs } from '@angular-devkit/core';
import { tap } from 'rxjs/operators';
import { EMPTY } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { TslintBuilderOptions } from '../../src';
import { host, tslintTargetSpec } from '../utils';

Expand Down Expand Up @@ -215,6 +216,37 @@ describe('Tslint Target', () => {
).toPromise().then(done, done.fail);
}, 30000);

it('works when a file is only part of one project when using two program', (done) => {
const overrides: Partial<TslintBuilderOptions> = {
tsConfig: ['src/tsconfig.app.json', 'src/tsconfig.spec.json'],
files: ['src/foo/foo.component.ts'],
};

host.writeMultipleFiles({ 'src/foo/foo.component.ts': `const foo = '';\n` });

runTargetSpec(host, tslintTargetSpec, overrides).pipe(
tap((buildEvent) => expect(buildEvent.success).toBe(true)),
).toPromise().then(done, done.fail);
}, 30000);

it('errors when file is not part of any typescript program', (done) => {
const overrides: Partial<TslintBuilderOptions> = {
tsConfig: ['src/tsconfig.spec.json'],
files: ['src/foo/foo.component.ts'],
};

host.writeMultipleFiles({ 'src/foo/foo.component.ts': `const foo = '';\n` });

runTargetSpec(host, tslintTargetSpec, overrides).pipe(
tap((buildEvent) => expect(buildEvent.success).toBe(false)),
catchError((err) => {
expect(err).toMatch(`foo.component.ts' is not part of a TypeScript project`);

return EMPTY;
}),
).toPromise().then(done, done.fail);
}, 30000);

it('errors when type checking is used without a project', (done) => {
const overrides: Partial<TslintBuilderOptions> = {
tsConfig: undefined,
Expand Down

0 comments on commit b500420

Please sign in to comment.