Skip to content

Commit

Permalink
fix(core): undecorated-classes-with-di migration should report config…
Browse files Browse the repository at this point in the history
… errors (#33567)

Currently TypeScript projects with an invalid tsconfig configuration,
cause the undecorated-classes-with-di migration to throw. Instead we
should gracefully exit the migration (like we do for syntactical
diagnostics), but report that there are configuration issues.

This issue surfaced when testing this migration in combination
with the Angular CLI migrations. One of the CLI migrations currently
causes invalid tsconfig files which then cause this issue in the
undecorated-classes-with-di migration.

PR Close #33567
  • Loading branch information
devversion authored and atscott committed Nov 5, 2019
1 parent 4b62ba9 commit c0ad47a
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 4 deletions.
Expand Up @@ -9,6 +9,7 @@
import {logging} from '@angular-devkit/core';
import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics';
import {AotCompiler} from '@angular/compiler';
import {Diagnostic as NgDiagnostic} from '@angular/compiler-cli';
import {PartialEvaluator} from '@angular/compiler-cli/src/ngtsc/partial_evaluator';
import {TypeScriptReflectionHost} from '@angular/compiler-cli/src/ngtsc/reflection';
import {relative} from 'path';
Expand Down Expand Up @@ -84,10 +85,11 @@ function runUndecoratedClassesMigration(
const partialEvaluator =
new PartialEvaluator(new TypeScriptReflectionHost(typeChecker), typeChecker);
const declarationCollector = new NgDeclarationCollector(typeChecker, partialEvaluator);
const rootSourceFiles = program.getRootFileNames().map(f => program.getSourceFile(f) !);
const sourceFiles = program.getSourceFiles().filter(
s => !s.isDeclarationFile && !program.isSourceFileFromExternalLibrary(s));

// Analyze source files by detecting all directives, components and providers.
rootSourceFiles.forEach(sourceFile => declarationCollector.visitNode(sourceFile));
sourceFiles.forEach(sourceFile => declarationCollector.visitNode(sourceFile));

const {decoratedDirectives, decoratedProviders, undecoratedDeclarations} = declarationCollector;
const transform =
Expand Down Expand Up @@ -151,14 +153,28 @@ function runUndecoratedClassesMigration(
}
}

function getErrorDiagnostics(diagnostics: ReadonlyArray<ts.Diagnostic|NgDiagnostic>) {
return <ts.Diagnostic[]>diagnostics.filter(d => d.category === ts.DiagnosticCategory.Error);
}

function gracefullyCreateProgram(
tree: Tree, basePath: string, tsconfigPath: string,
logger: logging.LoggerApi): {compiler: AotCompiler, program: ts.Program}|null {
try {
const {ngcProgram, host, program, compiler} = createNgcProgram(
(options) => createMigrationCompilerHost(tree, options, basePath), tsconfigPath);
const syntacticDiagnostics = ngcProgram.getTsSyntacticDiagnostics();
const structuralDiagnostics = ngcProgram.getNgStructuralDiagnostics();
const syntacticDiagnostics = getErrorDiagnostics(ngcProgram.getTsSyntacticDiagnostics());
const structuralDiagnostics = getErrorDiagnostics(ngcProgram.getNgStructuralDiagnostics());
const configDiagnostics = getErrorDiagnostics(
[...program.getOptionsDiagnostics(), ...ngcProgram.getNgOptionDiagnostics()]);

if (configDiagnostics.length) {
logger.warn(
`\nTypeScript project "${tsconfigPath}" has configuration errors. This could cause ` +
`an incomplete migration. Please fix the following failures and rerun the migration:`);
logger.error(ts.formatDiagnostics(configDiagnostics, host));
return null;
}

// Syntactic TypeScript errors can throw off the query analysis and therefore we want
// to notify the developer that we couldn't analyze parts of the project. Developers
Expand Down
Expand Up @@ -1495,5 +1495,32 @@ describe('Undecorated classes with DI migration', () => {
expect(warnOutput.length).toBe(0);
expect(errorOutput.length).toBe(0);
});

it('should not throw if tsconfig references non-existent source file', async() => {
writeFile('/tsconfig.json', JSON.stringify({
compilerOptions: {
lib: ['es2015'],
},
files: [
'./non-existent.ts',
]
}));

let failed = false;
try {
await runMigration();
} catch (e) {
failed = true;
}

expect(failed).toBe(false, 'Expected the migration not to fail.');
expect(warnOutput.length).toBe(1);
expect(errorOutput.length).toBe(1);
expect(warnOutput[0])
.toContain(
'TypeScript project "tsconfig.json" has configuration errors. This could cause an ' +
'incomplete migration. Please fix the following failures and rerun the migration:');
expect(errorOutput[0]).toMatch(/non-existent\.ts' not found/);
});
});
});

0 comments on commit c0ad47a

Please sign in to comment.