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:

committed by
Igor Minar

parent
271d2b51a9
commit
2b4d5c7548
@ -326,7 +326,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -345,7 +345,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -366,7 +366,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -389,7 +389,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -406,7 +406,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -424,7 +424,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
|
@ -318,7 +318,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -336,7 +336,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -355,7 +355,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -376,7 +376,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -393,7 +393,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -411,7 +411,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
|
@ -226,7 +226,7 @@ A.decorators = [
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -245,7 +245,7 @@ A.decorators = [
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -264,7 +264,7 @@ A.decorators = [
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -324,7 +324,7 @@ export { D };
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -341,7 +341,7 @@ export { D };
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -359,7 +359,7 @@ export { D };
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
|
@ -234,6 +234,38 @@ describe('Renderer', () => {
|
||||
.toEqual(`{ type: Directive, args: [{ selector: '[a]' }] }`);
|
||||
});
|
||||
|
||||
it('should render classes without decorators if handler matches', () => {
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
testFormatter} = createTestRenderer('test-package', [{
|
||||
name: '/src/file.js',
|
||||
contents: `
|
||||
import { Directive, ViewChild } from '@angular/core';
|
||||
|
||||
export class UndecoratedBase { test = null; }
|
||||
|
||||
UndecoratedBase.propDecorators = {
|
||||
test: [{
|
||||
type: ViewChild,
|
||||
args: ["test", {static: true}]
|
||||
}],
|
||||
};
|
||||
`
|
||||
}]);
|
||||
|
||||
renderer.renderProgram(
|
||||
decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses);
|
||||
|
||||
const addDefinitionsSpy = testFormatter.addDefinitions as jasmine.Spy;
|
||||
expect(addDefinitionsSpy.calls.first().args[2])
|
||||
.toEqual(
|
||||
`UndecoratedBase.ngBaseDef = ɵngcc0.ɵɵdefineBase({ viewQuery: function (rf, ctx) { if (rf & 1) {
|
||||
ɵngcc0.ɵɵstaticViewQuery(_c0, true, null);
|
||||
} if (rf & 2) {
|
||||
var _t;
|
||||
ɵngcc0.ɵɵqueryRefresh(_t = ɵngcc0.ɵɵloadViewQuery()) && (ctx.test = _t.first);
|
||||
} } });`);
|
||||
});
|
||||
|
||||
it('should call renderImports after other abstract methods', () => {
|
||||
// This allows the other methods to add additional imports if necessary
|
||||
const {renderer, decorationAnalyses, switchMarkerAnalyses, privateDeclarationsAnalyses,
|
||||
|
@ -377,7 +377,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -396,7 +396,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -417,7 +417,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators[0];
|
||||
const decorator = compiledClass.decorators ![0];
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -439,7 +439,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'A') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -456,7 +456,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'B') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
@ -474,7 +474,7 @@ SOME DEFINITION TEXT
|
||||
const output = new MagicString(PROGRAM_DECORATE_HELPER.contents);
|
||||
const compiledClass =
|
||||
decorationAnalyses.get(sourceFile) !.compiledClasses.find(c => c.name === 'C') !;
|
||||
const decorator = compiledClass.decorators.find(d => d.name === 'Directive') !;
|
||||
const decorator = compiledClass.decorators !.find(d => d.name === 'Directive') !;
|
||||
const decoratorsToRemove = new Map<ts.Node, ts.Node[]>();
|
||||
decoratorsToRemove.set(decorator.node.parent !, [decorator.node]);
|
||||
renderer.removeDecorators(output, decoratorsToRemove);
|
||||
|
Reference in New Issue
Block a user