diff --git a/packages/compiler-cli/ngcc/src/host/esm5_host.ts b/packages/compiler-cli/ngcc/src/host/esm5_host.ts index 18570124d5643..e8d6754588ee7 100644 --- a/packages/compiler-cli/ngcc/src/host/esm5_host.ts +++ b/packages/compiler-cli/ngcc/src/host/esm5_host.ts @@ -86,6 +86,23 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost { return iife.parent.arguments[0]; } + getInternalNameOfClass(clazz: ClassDeclaration): ts.Identifier { + const innerClass = this.getInnerFunctionDeclarationFromClassDeclaration(clazz); + if (innerClass === undefined) { + throw new Error( + `getInternalNameOfClass() called on a non-ES5 class: expected ${clazz.name.text} to have an inner class declaration`); + } + if (innerClass.name === undefined) { + throw new Error( + `getInternalNameOfClass() called on a class with an anonymous inner declaration: expected a name on:\n${innerClass.getText()}`); + } + return innerClass.name; + } + + getAdjacentNameOfClass(clazz: ClassDeclaration): ts.Identifier { + return this.getInternalNameOfClass(clazz); + } + /** * In ES5, the implementation of a class is a function expression that is hidden inside an IIFE, * whose value is assigned to a variable (which represents the class to the rest of the program). diff --git a/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts b/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts index 72f01d82c8243..ed755b25d0814 100644 --- a/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/commonjs_host_spec.ts @@ -147,6 +147,24 @@ var NoDecoratorConstructorClass = (function() { } return NoDecoratorConstructorClass; }()); +var OuterClass1 = (function() { + function InnerClass1() { + } + return InnerClass1; +}()); +var OuterClass2 = (function() { + function InnerClass2() { + } + InnerClass2_1 = InnerClass12 + var InnerClass2_1; + return InnerClass2; +}()); +var SuperClass = (function() { function SuperClass() {} return SuperClass; }()); +var ChildClass = /** @class */ (function (_super) { + __extends(ChildClass, _super); + function InnerChildClass() {} + return InnerChildClass; +}(SuperClass); exports.EmptyClass = EmptyClass; exports.NoDecoratorConstructorClass = NoDecoratorConstructorClass; `, @@ -2212,6 +2230,54 @@ exports.ExternalModule = ExternalModule; }); }); + describe('getInternalNameOfClass()', () => { + it('should return the name of the inner class declaration', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const {program, host: compilerHost} = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = new CommonJsReflectionHost(new MockLogger(), false, program, compilerHost); + + const emptyClass = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); + expect(host.getInternalNameOfClass(emptyClass).text).toEqual('EmptyClass'); + + const class1 = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isNamedVariableDeclaration); + expect(host.getInternalNameOfClass(class1).text).toEqual('InnerClass1'); + + const class2 = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isNamedVariableDeclaration); + expect(host.getInternalNameOfClass(class2).text).toEqual('InnerClass2'); + + const childClass = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'ChildClass', isNamedVariableDeclaration); + expect(host.getInternalNameOfClass(childClass).text).toEqual('InnerChildClass'); + }); + }); + + describe('getAdjacentNameOfClass()', () => { + it('should return the name of the inner class declaration', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const {program, host: compilerHost} = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = new CommonJsReflectionHost(new MockLogger(), false, program, compilerHost); + + const emptyClass = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); + expect(host.getAdjacentNameOfClass(emptyClass).text).toEqual('EmptyClass'); + + const class1 = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isNamedVariableDeclaration); + expect(host.getAdjacentNameOfClass(class1).text).toEqual('InnerClass1'); + + const class2 = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isNamedVariableDeclaration); + expect(host.getAdjacentNameOfClass(class2).text).toEqual('InnerClass2'); + + const childClass = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'ChildClass', isNamedVariableDeclaration); + expect(host.getAdjacentNameOfClass(childClass).text).toEqual('InnerChildClass'); + }); + }); + describe('getModuleWithProvidersFunctions', () => { it('should find every exported function that returns an object that looks like a ModuleWithProviders object', () => { diff --git a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts index f17eea2e9dc77..b4f7609ba2fe3 100644 --- a/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm2015_host_spec.ts @@ -2088,6 +2088,28 @@ runInEachFileSystem(() => { }); }); + describe('getInternalNameOfClass()', () => { + it('should return the name of the class (there is no separate inner class in ES2015)', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const {program} = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker()); + const node = + getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); + expect(host.getInternalNameOfClass(node).text).toEqual('EmptyClass'); + }); + }); + + describe('getAdjacentNameOfClass()', () => { + it('should return the name of the class (there is no separate inner class in ES2015)', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const {program} = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = new Esm2015ReflectionHost(new MockLogger(), false, program.getTypeChecker()); + const node = + getDeclaration(program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedClassDeclaration); + expect(host.getAdjacentNameOfClass(node).text).toEqual('EmptyClass'); + }); + }); + describe('getModuleWithProvidersFunctions()', () => { it('should find every exported function that returns an object that looks like a ModuleWithProviders object', () => { diff --git a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts index d46e6a0639295..16f1b9b5c5736 100644 --- a/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/esm5_host_spec.ts @@ -192,6 +192,24 @@ runInEachFileSystem(() => { } return NoDecoratorConstructorClass; }()); + var OuterClass1 = (function() { + function InnerClass1() { + } + return InnerClass1; + }()); + var OuterClass2 = (function() { + function InnerClass2() { + } + InnerClass2_1 = InnerClass12 + var InnerClass2_1; + return InnerClass2; + }()); + var SuperClass = (function() { function SuperClass() {} return SuperClass; }()); + var ChildClass = /** @class */ (function (_super) { + __extends(ChildClass, _super); + function InnerChildClass() {} + return InnerChildClass; + }(SuperClass); `, }; @@ -2331,6 +2349,54 @@ runInEachFileSystem(() => { }); }); + describe('getInternalNameOfClass()', () => { + it('should return the name of the inner class declaration', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const {program} = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = new Esm5ReflectionHost(new MockLogger(), false, program.getTypeChecker()); + + const emptyClass = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); + expect(host.getInternalNameOfClass(emptyClass).text).toEqual('EmptyClass'); + + const class1 = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isNamedVariableDeclaration); + expect(host.getInternalNameOfClass(class1).text).toEqual('InnerClass1'); + + const class2 = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isNamedVariableDeclaration); + expect(host.getInternalNameOfClass(class2).text).toEqual('InnerClass2'); + + const childClass = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'ChildClass', isNamedVariableDeclaration); + expect(host.getInternalNameOfClass(childClass).text).toEqual('InnerChildClass'); + }); + }); + + describe('getAdjacentNameOfClass()', () => { + it('should return the name of the inner class declaration', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const {program} = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = new Esm5ReflectionHost(new MockLogger(), false, program.getTypeChecker()); + + const emptyClass = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); + expect(host.getAdjacentNameOfClass(emptyClass).text).toEqual('EmptyClass'); + + const class1 = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isNamedVariableDeclaration); + expect(host.getAdjacentNameOfClass(class1).text).toEqual('InnerClass1'); + + const class2 = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isNamedVariableDeclaration); + expect(host.getAdjacentNameOfClass(class2).text).toEqual('InnerClass2'); + + const childClass = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'ChildClass', isNamedVariableDeclaration); + expect(host.getAdjacentNameOfClass(childClass).text).toEqual('InnerChildClass'); + }); + }); + describe('getModuleWithProvidersFunctions', () => { it('should find every exported function that returns an object that looks like a ModuleWithProviders object', () => { diff --git a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts index 52a5ec58b9cbd..51349c598f312 100644 --- a/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts +++ b/packages/compiler-cli/ngcc/test/host/umd_host_spec.ts @@ -163,6 +163,24 @@ runInEachFileSystem(() => { } return NoDecoratorConstructorClass; }()); + var OuterClass1 = (function() { + function InnerClass1() { + } + return InnerClass1; + }()); + var OuterClass2 = (function() { + function InnerClass2() { + } + InnerClass2_1 = InnerClass12 + var InnerClass2_1; + return InnerClass2; + }()); + var SuperClass = (function() { function SuperClass() {} return SuperClass; }()); + var ChildClass = /** @class */ (function (_super) { + __extends(ChildClass, _super); + function InnerChildClass() {} + return InnerChildClass; + }(SuperClass); exports.EmptyClass = EmptyClass; exports.NoDecoratorConstructorClass = NoDecoratorConstructorClass; })));`, @@ -2221,6 +2239,54 @@ runInEachFileSystem(() => { }); }); + describe('getInternalNameOfClass()', () => { + it('should return the name of the inner class declaration', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const {program, host: compilerHost} = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = new UmdReflectionHost(new MockLogger(), false, program, compilerHost); + + const emptyClass = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); + expect(host.getInternalNameOfClass(emptyClass).text).toEqual('EmptyClass'); + + const class1 = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isNamedVariableDeclaration); + expect(host.getInternalNameOfClass(class1).text).toEqual('InnerClass1'); + + const class2 = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isNamedVariableDeclaration); + expect(host.getInternalNameOfClass(class2).text).toEqual('InnerClass2'); + + const childClass = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'ChildClass', isNamedVariableDeclaration); + expect(host.getInternalNameOfClass(childClass).text).toEqual('InnerChildClass'); + }); + }); + + describe('getAdjacentNameOfClass()', () => { + it('should return the name of the inner class declaration', () => { + loadTestFiles([SIMPLE_CLASS_FILE]); + const {program, host: compilerHost} = makeTestBundleProgram(SIMPLE_CLASS_FILE.name); + const host = new UmdReflectionHost(new MockLogger(), false, program, compilerHost); + + const emptyClass = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'EmptyClass', isNamedVariableDeclaration); + expect(host.getAdjacentNameOfClass(emptyClass).text).toEqual('EmptyClass'); + + const class1 = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'OuterClass1', isNamedVariableDeclaration); + expect(host.getAdjacentNameOfClass(class1).text).toEqual('InnerClass1'); + + const class2 = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'OuterClass2', isNamedVariableDeclaration); + expect(host.getAdjacentNameOfClass(class2).text).toEqual('InnerClass2'); + + const childClass = getDeclaration( + program, SIMPLE_CLASS_FILE.name, 'ChildClass', isNamedVariableDeclaration); + expect(host.getAdjacentNameOfClass(childClass).text).toEqual('InnerChildClass'); + }); + }); + describe('getModuleWithProvidersFunctions', () => { it('should find every exported function that returns an object that looks like a ModuleWithProviders object', () => {