diff --git a/packages/core/schematics/migrations/static-queries/index.ts b/packages/core/schematics/migrations/static-queries/index.ts index efc8a5f525..19bdba3934 100644 --- a/packages/core/schematics/migrations/static-queries/index.ts +++ b/packages/core/schematics/migrations/static-queries/index.ts @@ -61,11 +61,12 @@ async function runMigration(tree: Tree, context: SchematicContext) { 'to explicit timing.'); } + const analyzedFiles = new Set(); const buildProjects = new Set(); const failures = []; for (const tsconfigPath of buildPaths) { - const project = analyzeProject(tree, tsconfigPath, basePath); + const project = analyzeProject(tree, tsconfigPath, basePath, analyzedFiles); if (project) { buildProjects.add(project); } @@ -83,7 +84,7 @@ async function runMigration(tree: Tree, context: SchematicContext) { // For the "test" tsconfig projects we always want to use the test strategy as // we can't detect the proper timing within spec files. for (const tsconfigPath of testPaths) { - const project = await analyzeProject(tree, tsconfigPath, basePath); + const project = await analyzeProject(tree, tsconfigPath, basePath, analyzedFiles); if (project) { failures.push( ...await runStaticQueryMigration(tree, project, SELECTED_STRATEGY.TESTS, logger)); @@ -103,7 +104,8 @@ async function runMigration(tree: Tree, context: SchematicContext) { * Analyzes the given TypeScript project by looking for queries that need to be * migrated. In case there are no queries that can be migrated, null is returned. */ -function analyzeProject(tree: Tree, tsconfigPath: string, basePath: string): +function analyzeProject( + tree: Tree, tsconfigPath: string, basePath: string, analyzedFiles: Set): AnalyzedProject|null { const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); const host = ts.createCompilerHost(parsed.options, true); @@ -125,7 +127,16 @@ function analyzeProject(tree: Tree, tsconfigPath: string, basePath: string): // Analyze all project source-files and collect all queries that // need to be migrated. - sourceFiles.forEach(sourceFile => queryVisitor.visitNode(sourceFile)); + sourceFiles.forEach(sourceFile => { + const relativePath = relative(basePath, sourceFile.fileName); + + // Only look for queries within the current source files if the + // file has not been analyzed before. + if (!analyzedFiles.has(relativePath)) { + analyzedFiles.add(relativePath); + queryVisitor.visitNode(sourceFile); + } + }); if (queryVisitor.resolvedQueries.size === 0) { return null; diff --git a/packages/core/schematics/test/static_queries_migration_template_spec.ts b/packages/core/schematics/test/static_queries_migration_template_spec.ts index e8f6b96815..31483df180 100644 --- a/packages/core/schematics/test/static_queries_migration_template_spec.ts +++ b/packages/core/schematics/test/static_queries_migration_template_spec.ts @@ -515,8 +515,6 @@ describe('static-queries migration with template strategy', () => { await runMigration(); expect(errorOutput.length).toBe(1); - expect(errorOutput[0]) - .toMatch(/^Error: Could not create Angular AOT compiler to determine query timing./); expect(errorOutput[0]).toMatch(/Cannot determine the module for class MyComp/); }); @@ -673,15 +671,49 @@ describe('static-queries migration with template strategy', () => { export class MyModule {} `); - spyOn(console, 'error').and.callThrough(); - await runMigration(); - expect(console.error).toHaveBeenCalledTimes(0); + expect(errorOutput.length).toBe(0); expect(tree.readContent('/src/test.ts')) .toContain(`@ViewChild('test', /* TODO: add static flag */ {}) query: any;`); expect(tree.readContent('/src/app.component.ts')) .toContain(`@ViewChild('test', { static: true }) query: any;`); }); + + it('should not fall back to test strategy if selected strategy fails', async() => { + writeFile('/src/tsconfig.spec.json', JSON.stringify({ + compilerOptions: { + experimentalDecorators: true, + lib: ['es2015'], + }, + files: [ + 'test.ts', + ], + })); + + writeFile('/src/test.ts', `import * as mod from './app.module';`); + writeFile('/src/app.component.ts', ` + import {Component, ViewChild} from '@angular/core'; + + @Component({template: 'Test'}) + export class AppComponent { + @ViewChild('test') query: any; + } + `); + + writeFile('/src/app.module.ts', ` + import {NgModule} from '@angular/core'; + import {AppComponent} from './app.component'; + + @NgModule({declarations: [AppComponent, ThisCausesAnError]}) + export class MyModule {} + `); + + await runMigration(); + + expect(errorOutput.length).toBe(1); + expect(errorOutput[0]).toMatch(/Unexpected value 'undefined'/); + expect(tree.readContent('/src/app.component.ts')).toContain(`@ViewChild('test') query: any;`); + }); }); });