Skip to content

Commit 79466ba

Browse files
alxhubjasonaden
authored andcommitted
fix(ivy): remove metadata from *Def and introduce *DefWithMeta types (angular#26203)
Previously in Ivy, metadata for directives/components/modules/etc was carried in .d.ts files inside type information encoded on the DirectiveDef, ComponentDef, NgModuleDef, etc types of Ivy definition fields. This works well, but has the side effect of complicating Ivy's runtime code as these extra generic type parameters had to be specified as <any> throughout the codebase. *DefInternal types were introduced previously to mitigate this issue, but that's the wrong way to solve the problem. This commit returns *Def types to their original form, with no metadata attached. Instead, new *DefWithMeta types are introduced that alias the plain definition types and add extra generic parameters. This way the only code that needs to deal with the extra metadata parameters is the compiler code that reads and writes them - the existence of this metadata is transparent to the runtime, as it should be. PR Close angular#26203
1 parent b0070df commit 79466ba

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+279
-252
lines changed

modules/benchmarks/src/largetable/render3/table.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {ɵRenderFlags, ɵbind, ɵcontainer, ɵcontainerRefreshEnd, ɵcontainerRefreshStart, ɵdefineComponent, ɵdetectChanges, ɵelementEnd, ɵelementStart, ɵelementStyleProp, ɵelementStyling, ɵembeddedViewEnd, ɵembeddedViewStart, ɵtext, ɵtextBinding as ɵtextBinding} from '@angular/core';
10-
import {ComponentDefInternal} from '@angular/core/src/render3/interfaces/definition';
10+
import {ComponentDef} from '@angular/core/src/render3/interfaces/definition';
1111

1212
import {TableCell, buildTable, emptyTable} from '../util';
1313

@@ -16,7 +16,7 @@ export class LargeTableComponent {
1616
data: TableCell[][] = emptyTable;
1717

1818
/** @nocollapse */
19-
static ngComponentDef: ComponentDefInternal<LargeTableComponent> = ɵdefineComponent({
19+
static ngComponentDef: ComponentDef<LargeTableComponent> = ɵdefineComponent({
2020
type: LargeTableComponent,
2121
selectors: [['largetable']],
2222
consts: 3,

packages/compiler-cli/src/ngtsc/annotations/src/selector_scope.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ export class SelectorScopeRegistry {
313313
return null;
314314
} else if (
315315
def.type === null || !ts.isTypeReferenceNode(def.type) ||
316-
def.type.typeArguments === undefined || def.type.typeArguments.length !== 2) {
316+
def.type.typeArguments === undefined || def.type.typeArguments.length < 2) {
317317
// The type metadata was the wrong shape.
318318
return null;
319319
}
@@ -337,7 +337,7 @@ export class SelectorScopeRegistry {
337337
return null;
338338
} else if (
339339
def.type === null || !ts.isTypeReferenceNode(def.type) ||
340-
def.type.typeArguments === undefined || def.type.typeArguments.length !== 2) {
340+
def.type.typeArguments === undefined || def.type.typeArguments.length < 2) {
341341
// The type metadata was the wrong shape.
342342
return null;
343343
}

packages/compiler-cli/src/ngtsc/annotations/test/selector_scope_spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe('SelectorScopeRegistry', () => {
2020
{
2121
name: 'node_modules/@angular/core/index.d.ts',
2222
contents: `
23-
export interface NgComponentDef<A, B> {}
23+
export interface NgComponentDefWithMeta<A, B, C, D, E, F> {}
2424
export interface NgModuleDef<A, B, C, D> {}
2525
`
2626
},
@@ -38,10 +38,10 @@ describe('SelectorScopeRegistry', () => {
3838
{
3939
name: 'node_modules/some_library/component.d.ts',
4040
contents: `
41-
import {NgComponentDef} from '@angular/core';
41+
import {NgComponentDefWithMeta} from '@angular/core';
4242
4343
export declare class SomeCmp {
44-
static ngComponentDef: NgComponentDef<SomeCmp, 'some-cmp'>;
44+
static ngComponentDef: NgComponentDefWithMeta<SomeCmp, 'some-cmp', never, {}, {}, never>;
4545
}
4646
`
4747
},
@@ -84,21 +84,21 @@ describe('SelectorScopeRegistry', () => {
8484
{
8585
name: 'node_modules/@angular/core/index.d.ts',
8686
contents: `
87-
export interface NgComponentDef<A, B> {}
87+
export interface NgComponentDefWithMeta<A, B, C, D, E, F> {}
8888
export interface NgModuleDef<A, B, C, D> {}
8989
`
9090
},
9191
{
9292
name: 'node_modules/some_library/index.d.ts',
9393
contents: `
94-
import {NgComponentDef, NgModuleDef} from '@angular/core';
94+
import {NgComponentDefWithMeta, NgModuleDef} from '@angular/core';
9595
9696
export declare class SomeModule {
9797
static ngModuleDef: NgModuleDef<SomeModule, [typeof SomeCmp], never, [typeof SomeCmp]>;
9898
}
9999
100100
export declare class SomeCmp {
101-
static ngComponentDef: NgComponentDef<SomeCmp, 'some-cmp'>;
101+
static ngComponentDef: NgComponentDefWithMeta<SomeCmp, 'some-cmp', never, {}, {}, never>;
102102
}
103103
`
104104
},

packages/compiler-cli/src/ngtsc/transform/src/translator.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const CORE_SUPPORTED_SYMBOLS = new Set<string>([
4545
'inject',
4646
'ɵInjectableDef',
4747
'ɵInjectorDef',
48-
'ɵNgModuleDef',
48+
'ɵNgModuleDefWithMeta',
4949
'ɵNgModuleFactory',
5050
]);
5151

@@ -416,7 +416,16 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor {
416416
}
417417

418418
visitLiteralMapExpr(ast: LiteralMapExpr, context: Context) {
419-
throw new Error('Method not implemented.');
419+
const entries = ast.entries.map(entry => {
420+
const {key, quoted} = entry;
421+
const value = entry.value.visitExpression(this, context);
422+
if (quoted) {
423+
return `'${key}': ${value}`;
424+
} else {
425+
return `${key}: ${value}`;
426+
}
427+
});
428+
return `{${entries.join(', ')}}`;
420429
}
421430

422431
visitCommaExpr(ast: CommaExpr, context: Context) { throw new Error('Method not implemented.'); }

packages/compiler-cli/test/ngtsc/ngtsc_spec.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ describe('ngtsc behavioral tests', () => {
148148
expect(jsContents).not.toContain('__decorate');
149149

150150
const dtsContents = getContents('test.d.ts');
151-
expect(dtsContents).toContain('static ngComponentDef: i0.ɵComponentDef<TestCmp, \'test-cmp\'>');
151+
expect(dtsContents)
152+
.toContain(
153+
'static ngComponentDef: i0.ɵComponentDefWithMeta<TestCmp, \'test-cmp\', never, {}, {}, never>');
152154
});
153155

154156
it('should compile Components without errors', () => {
@@ -201,10 +203,12 @@ describe('ngtsc behavioral tests', () => {
201203
'declarations: [TestCmp], imports: [], exports: [] })');
202204

203205
const dtsContents = getContents('test.d.ts');
204-
expect(dtsContents).toContain('static ngComponentDef: i0.ɵComponentDef<TestCmp, \'test-cmp\'>');
205206
expect(dtsContents)
206207
.toContain(
207-
'static ngModuleDef: i0.ɵNgModuleDef<TestModule, [typeof TestCmp], never, never>');
208+
'static ngComponentDef: i0.ɵComponentDefWithMeta<TestCmp, \'test-cmp\', never, {}, {}, never>');
209+
expect(dtsContents)
210+
.toContain(
211+
'static ngModuleDef: i0.ɵNgModuleDefWithMeta<TestModule, [typeof TestCmp], never, never>');
208212
expect(dtsContents).not.toContain('__decorate');
209213
});
210214

@@ -247,7 +251,7 @@ describe('ngtsc behavioral tests', () => {
247251
const dtsContents = getContents('test.d.ts');
248252
expect(dtsContents)
249253
.toContain(
250-
'static ngModuleDef: i0.ɵNgModuleDef<TestModule, [typeof TestCmp], [typeof OtherModule], never>');
254+
'static ngModuleDef: i0.ɵNgModuleDefWithMeta<TestModule, [typeof TestCmp], [typeof OtherModule], never>');
251255
expect(dtsContents).toContain('static ngInjectorDef: i0.ɵInjectorDef');
252256
});
253257

@@ -333,7 +337,8 @@ describe('ngtsc behavioral tests', () => {
333337
.toContain(
334338
'TestPipe.ngPipeDef = i0.ɵdefinePipe({ name: "test-pipe", type: TestPipe, ' +
335339
'factory: function TestPipe_Factory(t) { return new (t || TestPipe)(); }, pure: false })');
336-
expect(dtsContents).toContain('static ngPipeDef: i0.ɵPipeDef<TestPipe, \'test-pipe\'>;');
340+
expect(dtsContents)
341+
.toContain('static ngPipeDef: i0.ɵPipeDefWithMeta<TestPipe, \'test-pipe\'>;');
337342
});
338343

339344
it('should compile pure Pipes without errors', () => {
@@ -358,7 +363,8 @@ describe('ngtsc behavioral tests', () => {
358363
.toContain(
359364
'TestPipe.ngPipeDef = i0.ɵdefinePipe({ name: "test-pipe", type: TestPipe, ' +
360365
'factory: function TestPipe_Factory(t) { return new (t || TestPipe)(); }, pure: true })');
361-
expect(dtsContents).toContain('static ngPipeDef: i0.ɵPipeDef<TestPipe, \'test-pipe\'>;');
366+
expect(dtsContents)
367+
.toContain('static ngPipeDef: i0.ɵPipeDefWithMeta<TestPipe, \'test-pipe\'>;');
362368
});
363369

364370
it('should compile Pipes with dependencies', () => {
@@ -409,7 +415,8 @@ describe('ngtsc behavioral tests', () => {
409415

410416
const dtsContents = getContents('test.d.ts');
411417
expect(dtsContents)
412-
.toContain('i0.ɵNgModuleDef<TestModule, [typeof TestPipe, typeof TestCmp], never, never>');
418+
.toContain(
419+
'i0.ɵNgModuleDefWithMeta<TestModule, [typeof TestPipe, typeof TestCmp], never, never>');
413420
});
414421

415422
it('should unwrap a ModuleWithProviders function if a generic type is provided for it', () => {
@@ -440,7 +447,7 @@ describe('ngtsc behavioral tests', () => {
440447
const dtsContents = getContents('test.d.ts');
441448
expect(dtsContents).toContain(`import * as i1 from 'router';`);
442449
expect(dtsContents)
443-
.toContain('i0.ɵNgModuleDef<TestModule, never, [typeof i1.RouterModule], never>');
450+
.toContain('i0.ɵNgModuleDefWithMeta<TestModule, never, [typeof i1.RouterModule], never>');
444451
});
445452

446453
it('should inject special types according to the metadata', () => {

packages/compiler/src/render3/r3_identifiers.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ export class Identifiers {
126126

127127
static defineComponent: o.ExternalReference = {name: 'ɵdefineComponent', moduleName: CORE};
128128

129-
static ComponentDef: o.ExternalReference = {
130-
name: 'ɵComponentDef',
129+
static ComponentDefWithMeta: o.ExternalReference = {
130+
name: 'ɵComponentDefWithMeta',
131131
moduleName: CORE,
132132
};
133133

@@ -136,8 +136,8 @@ export class Identifiers {
136136
moduleName: CORE,
137137
};
138138

139-
static DirectiveDef: o.ExternalReference = {
140-
name: 'ɵDirectiveDef',
139+
static DirectiveDefWithMeta: o.ExternalReference = {
140+
name: 'ɵDirectiveDefWithMeta',
141141
moduleName: CORE,
142142
};
143143

@@ -151,14 +151,14 @@ export class Identifiers {
151151
moduleName: CORE,
152152
};
153153

154-
static NgModuleDef: o.ExternalReference = {
155-
name: 'ɵNgModuleDef',
154+
static NgModuleDefWithMeta: o.ExternalReference = {
155+
name: 'ɵNgModuleDefWithMeta',
156156
moduleName: CORE,
157157
};
158158

159159
static defineNgModule: o.ExternalReference = {name: 'ɵdefineNgModule', moduleName: CORE};
160160

161-
static PipeDef: o.ExternalReference = {name: 'ɵPipeDef', moduleName: CORE};
161+
static PipeDefWithMeta: o.ExternalReference = {name: 'ɵPipeDefWithMeta', moduleName: CORE};
162162

163163
static definePipe: o.ExternalReference = {name: 'ɵdefinePipe', moduleName: CORE};
164164

packages/compiler/src/render3/r3_module_compiler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef {
7373
exports: o.literalArr(exports.map(ref => ref.value)),
7474
})]);
7575

76-
const type = new o.ExpressionType(o.importExpr(R3.NgModuleDef, [
76+
const type = new o.ExpressionType(o.importExpr(R3.NgModuleDefWithMeta, [
7777
new o.ExpressionType(moduleType), tupleTypeOf(declarations), tupleTypeOf(imports),
7878
tupleTypeOf(exports)
7979
]));

packages/compiler/src/render3/r3_pipe_compiler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export function compilePipeFromMetadata(metadata: R3PipeMetadata) {
5050
definitionMapValues.push({key: 'pure', value: o.literal(metadata.pure), quoted: false});
5151

5252
const expression = o.importExpr(R3.definePipe).callFn([o.literalMap(definitionMapValues)]);
53-
const type = new o.ExpressionType(o.importExpr(R3.PipeDef, [
53+
const type = new o.ExpressionType(o.importExpr(R3.PipeDefWithMeta, [
5454
new o.ExpressionType(metadata.type),
5555
new o.ExpressionType(new o.LiteralExpr(metadata.pipeName)),
5656
]));

packages/compiler/src/render3/view/compiler.ts

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,7 @@ export function compileDirectiveFromMetadata(
117117
// string literal, which must be on one line.
118118
const selectorForType = (meta.selector || '').replace(/\n/g, '');
119119

120-
const type = new o.ExpressionType(o.importExpr(R3.DirectiveDef, [
121-
typeWithParameters(meta.type, meta.typeArgumentCount),
122-
new o.ExpressionType(o.literal(selectorForType))
123-
]));
120+
const type = createTypeForDef(meta, R3.DirectiveDefWithMeta);
124121
return {expression, type, statements};
125122
}
126123

@@ -257,10 +254,7 @@ export function compileComponentFromMetadata(
257254
const selectorForType = (meta.selector || '').replace(/\n/g, '');
258255

259256
const expression = o.importExpr(R3.defineComponent).callFn([definitionMap.toLiteralMap()]);
260-
const type = new o.ExpressionType(o.importExpr(R3.ComponentDef, [
261-
typeWithParameters(meta.type, meta.typeArgumentCount),
262-
new o.ExpressionType(o.literal(selectorForType))
263-
]));
257+
const type = createTypeForDef(meta, R3.ComponentDefWithMeta);
264258

265259
return {expression, type, statements};
266260
}
@@ -509,6 +503,39 @@ function createContentQueriesRefreshFunction(meta: R3DirectiveMetadata): o.Expre
509503
return null;
510504
}
511505

506+
function stringAsType(str: string): o.Type {
507+
return o.expressionType(o.literal(str));
508+
}
509+
510+
function stringMapAsType(map: {[key: string]: string}): o.Type {
511+
const mapValues = Object.keys(map).map(key => ({
512+
key,
513+
value: o.literal(map[key]),
514+
quoted: true,
515+
}));
516+
return o.expressionType(o.literalMap(mapValues));
517+
}
518+
519+
function stringArrayAsType(arr: string[]): o.Type {
520+
return arr.length > 0 ? o.expressionType(o.literalArr(arr.map(value => o.literal(value)))) :
521+
o.NONE_TYPE;
522+
}
523+
524+
function createTypeForDef(meta: R3DirectiveMetadata, typeBase: o.ExternalReference): o.Type {
525+
// On the type side, remove newlines from the selector as it will need to fit into a TypeScript
526+
// string literal, which must be on one line.
527+
const selectorForType = (meta.selector || '').replace(/\n/g, '');
528+
529+
return o.expressionType(o.importExpr(typeBase, [
530+
typeWithParameters(meta.type, meta.typeArgumentCount),
531+
stringAsType(selectorForType),
532+
meta.exportAs !== null ? stringAsType(meta.exportAs) : o.NONE_TYPE,
533+
stringMapAsType(meta.inputs),
534+
stringMapAsType(meta.outputs),
535+
stringArrayAsType(meta.queries.map(q => q.propertyName)),
536+
]));
537+
}
538+
512539
// Define and update any view queries
513540
function createViewQueriesFunction(
514541
meta: R3ComponentMetadata, constantPool: ConstantPool): o.Expression {

packages/core/src/core_render3_private_export.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,11 @@ export {
100100
pipe as ɵpipe,
101101
BaseDef as ɵBaseDef,
102102
ComponentDef as ɵComponentDef,
103-
ComponentDefInternal as ɵComponentDefInternal,
103+
ComponentDefWithMeta as ɵComponentDefWithMeta,
104104
DirectiveDef as ɵDirectiveDef,
105+
DirectiveDefWithMeta as ɵDirectiveDefWithMeta,
105106
PipeDef as ɵPipeDef,
107+
PipeDefWithMeta as ɵPipeDefWithMeta,
106108
whenRendered as ɵwhenRendered,
107109
i18nApply as ɵi18nApply,
108110
i18nExpMapping as ɵi18nExpMapping,
@@ -134,7 +136,7 @@ export {
134136

135137
export {
136138
NgModuleDef as ɵNgModuleDef,
137-
NgModuleDefInternal as ɵNgModuleDefInternal,
139+
NgModuleDefWithMeta as ɵNgModuleDefWithMeta,
138140
NgModuleTransitiveScopes as ɵNgModuleTransitiveScopes,
139141
} from './metadata/ng_module';
140142

packages/core/src/metadata/ng_module.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,7 @@ export interface NgModuleTransitiveScopes {
2626
exported: {directives: Set<any>; pipes: Set<any>;};
2727
}
2828

29-
/**
30-
* A version of {@link NgModuleDef} that represents the runtime type shape only, and excludes
31-
* metadata parameters.
32-
*/
33-
export type NgModuleDefInternal<T> = NgModuleDef<T, any, any, any>;
29+
export type NgModuleDefWithMeta<T, Declarations, Imports, Exports> = NgModuleDef<T>;
3430

3531
/**
3632
* Runtime link information for NgModules.
@@ -42,7 +38,7 @@ export type NgModuleDefInternal<T> = NgModuleDef<T, any, any, any>;
4238
* never create the object directly since the shape of this object
4339
* can change between versions.
4440
*/
45-
export interface NgModuleDef<T, Declarations, Imports, Exports> {
41+
export interface NgModuleDef<T> {
4642
/** Token representing the module. Used by DI. */
4743
type: T;
4844

packages/core/src/render3/component.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {queueInitHooks, queueLifecycleHooks} from './hooks';
1919
import {PlayerHandler} from './interfaces/player';
2020

2121
import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, hostElement, leaveView, locateHostElement, setHostBindings, queueHostBindingForCheck,} from './instructions';
22-
import {ComponentDef, ComponentDefInternal, ComponentType} from './interfaces/definition';
22+
import {ComponentDef, ComponentType} from './interfaces/definition';
2323
import {LElementNode} from './interfaces/node';
2424
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
2525
import {CONTEXT, INJECTOR, LViewData, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
@@ -77,7 +77,7 @@ export interface CreateComponentOptions {
7777
}
7878

7979
/** See CreateComponentOptions.hostFeatures */
80-
type HostFeature = (<T>(component: T, componentDef: ComponentDef<T, string>) => void);
80+
type HostFeature = (<T>(component: T, componentDef: ComponentDef<T>) => void);
8181

8282
// TODO: A hack to not pull in the NullInjector from @angular/core.
8383
export const NULL_INJECTOR: Injector = {
@@ -149,7 +149,7 @@ export function renderComponent<T>(
149149
* renderComponent() and ViewContainerRef.createComponent().
150150
*/
151151
export function createRootComponent<T>(
152-
elementNode: LElementNode, componentDef: ComponentDef<T, string>, rootView: LViewData,
152+
elementNode: LElementNode, componentDef: ComponentDef<T>, rootView: LViewData,
153153
rootContext: RootContext, hostFeatures: HostFeature[] | null): any {
154154
// Create directive instance with factory() and store at index 0 in directives array
155155
const component = baseDirectiveCreate(0, componentDef.factory() as T, componentDef, elementNode);
@@ -188,7 +188,7 @@ export function createRootContext(
188188
* renderComponent(AppComponent, {features: [RootLifecycleHooks]});
189189
* ```
190190
*/
191-
export function LifecycleHooksFeature(component: any, def: ComponentDefInternal<any>): void {
191+
export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): void {
192192
const rootTView = readPatchedLViewData(component) ![TVIEW];
193193

194194
// Root component is always created at dir index 0

0 commit comments

Comments
 (0)