From 6f5f481fdae03f1d8db36284b64c7b82d9519d85 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 2 Oct 2019 11:28:23 +0200 Subject: [PATCH] refactor(core): undecorated-classes-with-di migration should ignore referenced resources (#32953) Currently the undecorated-classes-with-di migration leverages NGC in order to work with metadata resolution. Since NGC by default tries to resolve referenced resources on initialization of the underlying TS program, it can result in unexpected migration failures due to missing resource files. This is especially an issue since the CLI wraps the `AngularCompilerProgram` with special logic (i.e. to support SCSS preprocessing etc.). We don't have all of this since we instantiate a vanilla NGC program. The solution to the problem is to simply treat resource requests as valid, and returning a fake content. The migration is not dependent on templates or stylesheets.. so it's the simplest and most robust solution. Fixes #32826 PR Close #32953 --- .../create_ngc_program.ts | 27 +++++++++---------- ...ecorated_classes_with_di_migration_spec.ts | 22 +++++++++++++++ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/packages/core/schematics/migrations/undecorated-classes-with-di/create_ngc_program.ts b/packages/core/schematics/migrations/undecorated-classes-with-di/create_ngc_program.ts index 35b6c764ae..4c0f09cb51 100644 --- a/packages/core/schematics/migrations/undecorated-classes-with-di/create_ngc_program.ts +++ b/packages/core/schematics/migrations/undecorated-classes-with-di/create_ngc_program.ts @@ -6,13 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {AotCompiler, CompileStylesheetMetadata} from '@angular/compiler'; -import {createProgram, readConfiguration} from '@angular/compiler-cli'; +import {AotCompiler} from '@angular/compiler'; +import {CompilerHost, createProgram, readConfiguration} from '@angular/compiler-cli'; import * as ts from 'typescript'; /** Creates an NGC program that can be used to read and parse metadata for files. */ export function createNgcProgram( - createHost: (options: ts.CompilerOptions) => ts.CompilerHost, tsconfigPath: string) { + createHost: (options: ts.CompilerOptions) => CompilerHost, tsconfigPath: string) { const {rootNames, options} = readConfiguration(tsconfigPath); // https://github.com/angular/angular/commit/ec4381dd401f03bded652665b047b6b90f2b425f made Ivy @@ -21,24 +21,21 @@ export function createNgcProgram( options.enableIvy = false; const host = createHost(options); + + // For this migration, we never need to read resources and can just return + // an empty string for requested resources. We need to handle requested resources + // because our created NGC compiler program does not know about special resolutions + // which are set up by the Angular CLI. i.e. resolving stylesheets through "tilde". + host.readResource = () => ''; + host.resourceNameToFileName = () => '$fake-file$'; + const ngcProgram = createProgram({rootNames, options, host}); - const program = ngcProgram.getTsProgram(); // The "AngularCompilerProgram" does not expose the "AotCompiler" instance, nor does it // expose the logic that is necessary to analyze the determined modules. We work around // this by just accessing the necessary private properties using the bracket notation. const compiler: AotCompiler = (ngcProgram as any)['compiler']; - const metadataResolver = compiler['_metadataResolver']; - // Modify the "DirectiveNormalizer" to not normalize any referenced external stylesheets. - // This is necessary because in CLI projects preprocessor files are commonly referenced - // and we don't want to parse them in order to extract relative style references. This - // breaks the analysis of the project because we instantiate a standalone AOT compiler - // program which does not contain the custom logic by the Angular CLI Webpack compiler plugin. - const directiveNormalizer = metadataResolver !['_directiveNormalizer']; - directiveNormalizer['_normalizeStylesheet'] = function(metadata: CompileStylesheetMetadata) { - return new CompileStylesheetMetadata( - {styles: metadata.styles, styleUrls: [], moduleUrl: metadata.moduleUrl !}); - }; + const program = ngcProgram.getTsProgram(); return {host, ngcProgram, program, compiler}; } diff --git a/packages/core/schematics/test/undecorated_classes_with_di_migration_spec.ts b/packages/core/schematics/test/undecorated_classes_with_di_migration_spec.ts index 5ec019d950..06dfbb231d 100644 --- a/packages/core/schematics/test/undecorated_classes_with_di_migration_spec.ts +++ b/packages/core/schematics/test/undecorated_classes_with_di_migration_spec.ts @@ -1457,5 +1457,27 @@ describe('Undecorated classes with DI migration', () => { expect(errorOutput.length).toBe(1); expect(errorOutput[0]).toMatch(/error TS1005: 'from' expected/); }); + + it('should not throw if resources could not be read', async() => { + writeFile('/index.ts', ` + import {Component, NgModule} from '@angular/core'; + + @Component({ + templateUrl: './my-template.pug', + styleUrls: ["./test.scss", "./some-special-file.custom"], + }) + export class TestComp {} + + @NgModule({declarations: [TestComp]}) + export class MyModule {} + `); + + writeFile('/test.scss', `@import '~theme.scss';`); + + await runMigration(); + + expect(warnOutput.length).toBe(0); + expect(errorOutput.length).toBe(0); + }); }); });