diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index ba723a1cd5..553e4ca273 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -302,6 +302,8 @@ export class ComponentDecoratorHandler implements this.preanalyzeStylesCache.set(node, styles); }); } + } else { + this.preanalyzeStylesCache.set(node, null); } // Wait for both the template and all styleUrl resources to resolve. diff --git a/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts index 8fcfca7eb6..d94519770e 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/test/component_spec.ts @@ -302,6 +302,74 @@ runInEachFileSystem(() => { const {analysis} = handler.analyze(TestCmp, detected.metadata); expect(analysis?.inlineStyles).toEqual(jasmine.arrayWithExactContents(['.xyz {}'])); }); + + it('should error if canPreprocess is true and async analyze is not used', async () => { + const {program, options, host} = makeProgram([ + { + name: _('/node_modules/@angular/core/index.d.ts'), + contents: 'export const Component: any;', + }, + { + name: _('/entry.ts'), + contents: ` + import {Component} from '@angular/core'; + + @Component({ + template: '', + styles: ['.abc {}'] + }) class TestCmp {} + ` + }, + ]); + const {reflectionHost, handler, resourceLoader} = setup(program, options, host); + resourceLoader.canPreload = true; + resourceLoader.canPreprocess = true; + + const TestCmp = getDeclaration(program, _('/entry.ts'), 'TestCmp', isNamedClassDeclaration); + const detected = handler.detect(TestCmp, reflectionHost.getDecoratorsOfDeclaration(TestCmp)); + if (detected === undefined) { + return fail('Failed to recognize @Component'); + } + + expect(() => handler.analyze(TestCmp, detected.metadata)) + .toThrowError('Inline resource processing requires asynchronous preanalyze.'); + }); + + it('should not error if component has no inline styles and canPreprocess is true', async () => { + const {program, options, host} = makeProgram([ + { + name: _('/node_modules/@angular/core/index.d.ts'), + contents: 'export const Component: any;', + }, + { + name: _('/entry.ts'), + contents: ` + import {Component} from '@angular/core'; + + @Component({ + template: '', + }) class TestCmp {} + ` + }, + ]); + const {reflectionHost, handler, resourceLoader} = setup(program, options, host); + resourceLoader.canPreload = true; + resourceLoader.canPreprocess = true; + resourceLoader.preprocessInline = async function(data, context) { + fail('preprocessInline should not have been called.'); + return data; + }; + + const TestCmp = getDeclaration(program, _('/entry.ts'), 'TestCmp', isNamedClassDeclaration); + const detected = handler.detect(TestCmp, reflectionHost.getDecoratorsOfDeclaration(TestCmp)); + if (detected === undefined) { + return fail('Failed to recognize @Component'); + } + + await handler.preanalyze(TestCmp, detected.metadata); + + expect(() => handler.analyze(TestCmp, detected.metadata)).not.toThrow(); + }); }); function ivyCode(code: ErrorCode): number {