fix(ivy): ngcc should process undecorated base classes (#30821)

Currently undecorated classes are intentionally not processed
with ngcc. This is causing unexpected behavior because decorator
handlers such as `base_def.ts` are specifically interested in class
definitions without top-level decorators, so that the base definition
can be generated if there are Angular-specific class members.

In order to ensure that undecorated base-classes work as expected
with Ivy, we need to run the decorator handlers for all top-level
class declarations (not only for those with decorators). This is similar
to when `ngtsc` runs decorator handlers when analyzing source-files.

Resolves FW-1355. Fixes https://github.com/angular/components/issues/16178

PR Close #30821
This commit is contained in:
Paul Gschwendtner
2019-06-03 18:41:47 +02:00
committed by Igor Minar
parent 271d2b51a9
commit 2b4d5c7548
20 changed files with 520 additions and 367 deletions

View File

@ -95,8 +95,13 @@ describe('DecorationAnalyzer', () => {
]);
// Only detect the Component and Directive decorators
handler.detect.and.callFake(
(node: ts.Declaration, decorators: Decorator[]): DetectResult<any>| undefined => {
logs.push(`detect: ${(node as any).name.text}@${decorators.map(d => d.name)}`);
(node: ts.Declaration, decorators: Decorator[] | null): DetectResult<any>| undefined => {
const className = (node as any).name.text;
if (decorators === null) {
logs.push(`detect: ${className} (no decorators)`);
} else {
logs.push(`detect: ${className}@${decorators.map(d => d.name)}`);
}
if (!decorators) {
return undefined;
}
@ -160,12 +165,13 @@ describe('DecorationAnalyzer', () => {
it('should call detect on the decorator handlers with each class from the parsed file',
() => {
expect(testHandler.detect).toHaveBeenCalledTimes(4);
expect(testHandler.detect.calls.allArgs().map(args => args[1][0])).toEqual([
jasmine.objectContaining({name: 'Component'}),
jasmine.objectContaining({name: 'Directive'}),
jasmine.objectContaining({name: 'Injectable'}),
jasmine.objectContaining({name: 'Component'}),
expect(testHandler.detect).toHaveBeenCalledTimes(5);
expect(testHandler.detect.calls.allArgs().map(args => args[1])).toEqual([
null,
jasmine.arrayContaining([jasmine.objectContaining({name: 'Component'})]),
jasmine.arrayContaining([jasmine.objectContaining({name: 'Directive'})]),
jasmine.arrayContaining([jasmine.objectContaining({name: 'Injectable'})]),
jasmine.arrayContaining([jasmine.objectContaining({name: 'Component'})]),
]);
});
@ -190,6 +196,8 @@ describe('DecorationAnalyzer', () => {
it('should analyze, resolve and compile the classes that are detected', () => {
expect(logs).toEqual([
// Classes without decorators should also be detected.
'detect: InjectionToken (no decorators)',
// First detect and (potentially) analyze.
'detect: MyComponent@Component',
'analyze: MyComponent@Component',