diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts index 33ae5ee7f3..b8cb380501 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/ng_module.ts @@ -134,6 +134,8 @@ export class NgModuleDecoratorHandler implements DecoratorHandler this._toR3Reference(exp, valueContext, typeContext)), imports: imports.map(imp => this._toR3Reference(imp, valueContext, typeContext)), emitInline: false, + // TODO: to be implemented as a part of FW-1004. + schemas: [], }; const providers: Expression = ngModule.has('providers') ? diff --git a/packages/compiler/src/compiler_facade_interface.ts b/packages/compiler/src/compiler_facade_interface.ts index b375c16592..ad281c618e 100644 --- a/packages/compiler/src/compiler_facade_interface.ts +++ b/packages/compiler/src/compiler_facade_interface.ts @@ -97,6 +97,7 @@ export interface R3NgModuleMetadataFacade { imports: Function[]; exports: Function[]; emitInline: boolean; + schemas: {name: string}[]|null; } export interface R3InjectorMetadataFacade { @@ -155,4 +156,4 @@ export interface ParseSourceSpan { start: any; end: any; details: any; -} \ No newline at end of file +} diff --git a/packages/compiler/src/jit_compiler_facade.ts b/packages/compiler/src/jit_compiler_facade.ts index 9a5b3ba510..9d36aa3098 100644 --- a/packages/compiler/src/jit_compiler_facade.ts +++ b/packages/compiler/src/jit_compiler_facade.ts @@ -86,6 +86,7 @@ export class CompilerFacadeImpl implements CompilerFacade { imports: facade.imports.map(wrapReference), exports: facade.exports.map(wrapReference), emitInline: true, + schemas: facade.schemas ? facade.schemas.map(wrapReference) : null, }; const res = compileNgModule(meta); return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []); diff --git a/packages/compiler/src/render3/r3_module_compiler.ts b/packages/compiler/src/render3/r3_module_compiler.ts index aab985b25b..4a4a3f51d9 100644 --- a/packages/compiler/src/render3/r3_module_compiler.ts +++ b/packages/compiler/src/render3/r3_module_compiler.ts @@ -57,13 +57,18 @@ export interface R3NgModuleMetadata { * does not allow components to be tree-shaken, but is useful for JIT mode. */ emitInline: boolean; + + /** + * The set of schemas that declare elements to be allowed in the NgModule. + */ + schemas: R3Reference[]|null; } /** * Construct an `R3NgModuleDef` for the given `R3NgModuleMetadata`. */ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef { - const {type: moduleType, bootstrap, declarations, imports, exports} = meta; + const {type: moduleType, bootstrap, declarations, imports, exports, schemas} = meta; const definitionMap = { type: moduleType } as{ @@ -71,7 +76,8 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef { bootstrap: o.LiteralArrayExpr, declarations: o.LiteralArrayExpr, imports: o.LiteralArrayExpr, - exports: o.LiteralArrayExpr + exports: o.LiteralArrayExpr, + schemas: o.LiteralArrayExpr }; // Only generate the keys in the metadata if the arrays have values. @@ -91,6 +97,10 @@ export function compileNgModule(meta: R3NgModuleMetadata): R3NgModuleDef { definitionMap.exports = o.literalArr(exports.map(ref => ref.value)); } + if (schemas && schemas.length) { + definitionMap.schemas = o.literalArr(schemas.map(ref => ref.value)); + } + const expression = o.importExpr(R3.defineNgModule).callFn([mapToMapExpression(definitionMap)]); const type = new o.ExpressionType(o.importExpr(R3.NgModuleDefWithMeta, [ new o.ExpressionType(moduleType), tupleTypeOf(declarations), tupleTypeOf(imports), diff --git a/packages/core/src/compiler/compiler_facade_interface.ts b/packages/core/src/compiler/compiler_facade_interface.ts index d921bc85ae..a5f1802c2e 100644 --- a/packages/core/src/compiler/compiler_facade_interface.ts +++ b/packages/core/src/compiler/compiler_facade_interface.ts @@ -97,6 +97,7 @@ export interface R3NgModuleMetadataFacade { imports: Function[]; exports: Function[]; emitInline: boolean; + schemas: {name: string}[]|null; } export interface R3InjectorMetadataFacade { @@ -155,4 +156,4 @@ export interface ParseSourceSpan { start: any; end: any; details: any; -} \ No newline at end of file +} diff --git a/packages/core/src/metadata.ts b/packages/core/src/metadata.ts index c1e9281839..ca9b5feea3 100644 --- a/packages/core/src/metadata.ts +++ b/packages/core/src/metadata.ts @@ -14,12 +14,14 @@ import {Attribute} from './di'; import {ContentChild, ContentChildren, Query, ViewChild, ViewChildren} from './metadata/di'; import {Component, Directive, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives'; -import {DoBootstrap, ModuleWithProviders, NgModule, SchemaMetadata} from './metadata/ng_module'; +import {DoBootstrap, ModuleWithProviders, NgModule} from './metadata/ng_module'; +import {SchemaMetadata} from './metadata/schema'; import {ViewEncapsulation} from './metadata/view'; export {Attribute} from './di'; export {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnDestroy, OnInit} from './interface/lifecycle_hooks'; export {ANALYZE_FOR_ENTRY_COMPONENTS, ContentChild, ContentChildDecorator, ContentChildren, ContentChildrenDecorator, Query, ViewChild, ViewChildDecorator, ViewChildren, ViewChildrenDecorator} from './metadata/di'; export {Component, ComponentDecorator, Directive, DirectiveDecorator, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives'; -export {CUSTOM_ELEMENTS_SCHEMA, DoBootstrap, ModuleWithProviders, NO_ERRORS_SCHEMA, NgModule, SchemaMetadata} from './metadata/ng_module'; +export {DoBootstrap, ModuleWithProviders, NgModule} from './metadata/ng_module'; +export {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from './metadata/schema'; export {ViewEncapsulation} from './metadata/view'; diff --git a/packages/core/src/metadata/ng_module.ts b/packages/core/src/metadata/ng_module.ts index a171c2a720..b2c3a3ee82 100644 --- a/packages/core/src/metadata/ng_module.ts +++ b/packages/core/src/metadata/ng_module.ts @@ -11,6 +11,7 @@ import {InjectorType, defineInjector} from '../di/interface/defs'; import {Provider} from '../di/interface/provider'; import {convertInjectableProviderToFactory} from '../di/util'; import {Type} from '../interface/type'; +import {SchemaMetadata} from '../metadata/schema'; import {NgModuleType} from '../render3'; import {compileNgModule as render3CompileNgModule} from '../render3/jit/module'; import {TypeDecorator, makeDecorator} from '../util/decorators'; @@ -28,6 +29,7 @@ import {TypeDecorator, makeDecorator} from '../util/decorators'; export interface NgModuleTransitiveScopes { compilation: {directives: Set; pipes: Set;}; exported: {directives: Set; pipes: Set;}; + schemas: SchemaMetadata[]|null; } export type NgModuleDefWithMeta = NgModuleDef; @@ -67,6 +69,9 @@ export interface NgModuleDef { * This should never be read directly, but accessed via `transitiveScopesFor`. */ transitiveCompileScopes: NgModuleTransitiveScopes|null; + + /** The set of schemas that declare elements to be allowed in the NgModule. */ + schemas: SchemaMetadata[]|null; } /** @@ -83,38 +88,6 @@ export interface ModuleWithProviders< providers?: Provider[]; } -/** - * A schema definition associated with an NgModule. - * - * @see `@NgModule`, `CUSTOM_ELEMENTS_SCHEMA`, `NO_ERRORS_SCHEMA` - * - * @param name The name of a defined schema. - * - * @publicApi - */ -export interface SchemaMetadata { name: string; } - -/** - * Defines a schema that allows an NgModule to contain the following: - * - Non-Angular elements named with dash case (`-`). - * - Element properties named with dash case (`-`). - * Dash case is the naming convention for custom elements. - * - * @publicApi - */ -export const CUSTOM_ELEMENTS_SCHEMA: SchemaMetadata = { - name: 'custom-elements' -}; - -/** - * Defines a schema that allows any property on any element. - * - * @publicApi - */ -export const NO_ERRORS_SCHEMA: SchemaMetadata = { - name: 'no-errors-schema' -}; - /** * Type of the NgModule decorator / constructor function. diff --git a/packages/core/src/metadata/schema.ts b/packages/core/src/metadata/schema.ts new file mode 100644 index 0000000000..54c3d56dbe --- /dev/null +++ b/packages/core/src/metadata/schema.ts @@ -0,0 +1,40 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + +/** + * A schema definition associated with an NgModule. + * + * @see `@NgModule`, `CUSTOM_ELEMENTS_SCHEMA`, `NO_ERRORS_SCHEMA` + * + * @param name The name of a defined schema. + * + * @publicApi + */ +export interface SchemaMetadata { name: string; } + +/** + * Defines a schema that allows an NgModule to contain the following: + * - Non-Angular elements named with dash case (`-`). + * - Element properties named with dash case (`-`). + * Dash case is the naming convention for custom elements. + * + * @publicApi + */ +export const CUSTOM_ELEMENTS_SCHEMA: SchemaMetadata = { + name: 'custom-elements' +}; + +/** + * Defines a schema that allows any property on any element. + * + * @publicApi + */ +export const NO_ERRORS_SCHEMA: SchemaMetadata = { + name: 'no-errors-schema' +}; diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index 0078a9a772..12f99a30c0 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -122,7 +122,7 @@ export function renderComponent( const renderer = rendererFactory.createRenderer(hostRNode, componentDef); const rootView: LView = createLView( - null, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, null, null, + null, createTView(-1, null, 1, 0, null, null, null, null), rootContext, rootFlags, null, null, rendererFactory, renderer, undefined, opts.injector || null); const oldView = enterView(rootView, null); @@ -165,9 +165,9 @@ export function createRootComponentView( const tView = rootView[TVIEW]; const tNode: TElementNode = createNodeAtIndex(0, TNodeType.Element, rNode, null, null); const componentView = createLView( - rootView, - getOrCreateTView( - def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery), + rootView, getOrCreateTView( + def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, + def.viewQuery, def.schemas), null, def.onPush ? LViewFlags.Dirty : LViewFlags.CheckAlways, rootView[HEADER_OFFSET], tNode, rendererFactory, renderer, sanitizer); diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index 794508126a..15d856938b 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -162,8 +162,8 @@ export class ComponentFactory extends viewEngine_ComponentFactory { // Create the root view. Uses empty TView and ContentTemplate. const rootLView = createLView( - null, createTView(-1, null, 1, 0, null, null, null), rootContext, rootFlags, null, null, - rendererFactory, renderer, sanitizer, rootViewInjector); + null, createTView(-1, null, 1, 0, null, null, null, null), rootContext, rootFlags, null, + null, rendererFactory, renderer, sanitizer, rootViewInjector); // rootView is the parent when bootstrapping const oldLView = enterView(rootLView, null); diff --git a/packages/core/src/render3/definition.ts b/packages/core/src/render3/definition.ts index 8a035b0c97..87d644ab6c 100644 --- a/packages/core/src/render3/definition.ts +++ b/packages/core/src/render3/definition.ts @@ -11,6 +11,7 @@ import '../util/ng_dev_mode'; import {ChangeDetectionStrategy} from '../change_detection/constants'; import {Mutable, Type} from '../interface/type'; import {NgModuleDef} from '../metadata/ng_module'; +import {SchemaMetadata} from '../metadata/schema'; import {ViewEncapsulation} from '../metadata/view'; import {noSideEffects} from '../util/closure'; import {stringify} from '../util/stringify'; @@ -234,6 +235,11 @@ export function defineComponent(componentDefinition: { * `PipeDefs`s. The function is necessary to be able to support forward declarations. */ pipes?: PipeTypesOrFactory | null; + + /** + * The set of schemas that declare elements to be allowed in the component's template. + */ + schemas?: SchemaMetadata[] | null; }): never { const type = componentDefinition.type; const typePrototype = type.prototype; @@ -274,6 +280,7 @@ export function defineComponent(componentDefinition: { styles: componentDefinition.styles || EMPTY_ARRAY, _: null as never, setInput: null, + schemas: componentDefinition.schemas || null, }; def._ = noSideEffects(() => { const directiveTypes = componentDefinition.directives !; @@ -326,6 +333,7 @@ export function defineNgModule(def: {type: T} & Partial>): nev imports: def.imports || EMPTY_ARRAY, exports: def.exports || EMPTY_ARRAY, transitiveCompileScopes: null, + schemas: def.schemas || null, }; return res as never; } diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index 268aed3dc8..2cede59d00 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -10,6 +10,7 @@ import {InjectFlags, InjectionToken, Injector} from '../di'; import {resolveForwardRef} from '../di/forward_ref'; import {ErrorHandler} from '../error_handler'; import {Type} from '../interface/type'; +import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA, SchemaMetadata} from '../metadata/schema'; import {validateAgainstEventAttributes, validateAgainstEventProperties} from '../sanitization/sanitization'; import {Sanitizer} from '../sanitization/security'; import {StyleSanitizeFn} from '../sanitization/style_sanitizer'; @@ -320,12 +321,12 @@ export function renderTemplate( // We need to create a root view so it's possible to look up the host element through its index const hostLView = createLView( - null, createTView(-1, null, 1, 0, null, null, null), {}, + null, createTView(-1, null, 1, 0, null, null, null, null), {}, LViewFlags.CheckAlways | LViewFlags.IsRoot, null, null, providedRendererFactory, renderer); enterView(hostLView, null); // SUSPECT! why do we need to enter the View? const componentTView = - getOrCreateTView(templateFn, consts, vars, directives || null, pipes || null, null); + getOrCreateTView(templateFn, consts, vars, directives || null, pipes || null, null, null); const hostTNode = createNodeAtIndex(0, TNodeType.Element, hostNode, null, null); componentView = createLView( hostLView, componentTView, context, LViewFlags.CheckAlways, hostNode, hostTNode, @@ -728,12 +729,14 @@ function saveResolvedLocalsInData( * @param vars The number of bindings and pure function bindings in this view * @param directives Directive defs that should be saved on TView * @param pipes Pipe defs that should be saved on TView + * @param viewQuery View query that should be saved on TView + * @param schemas Schemas that should be saved on TView * @returns TView */ export function getOrCreateTView( templateFn: ComponentTemplate, consts: number, vars: number, directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null, - viewQuery: ViewQueriesFunction| null): TView { + viewQuery: ViewQueriesFunction| null, schemas: SchemaMetadata[] | null): TView { // TODO(misko): reading `ngPrivateData` here is problematic for two reasons // 1. It is a megamorphic call on each invocation. // 2. For nested embedded views (ngFor inside ngFor) the template instance is per @@ -742,8 +745,8 @@ export function getOrCreateTView( // and not on embedded templates. return templateFn.ngPrivateData || - (templateFn.ngPrivateData = - createTView(-1, templateFn, consts, vars, directives, pipes, viewQuery) as never); + (templateFn.ngPrivateData = createTView( + -1, templateFn, consts, vars, directives, pipes, viewQuery, schemas) as never); } /** @@ -754,11 +757,13 @@ export function getOrCreateTView( * @param consts The number of nodes, local refs, and pipes in this template * @param directives Registry of directives for this view * @param pipes Registry of pipes for this view + * @param viewQuery View queries for this view + * @param schemas Schemas for this view */ export function createTView( viewIndex: number, templateFn: ComponentTemplate| null, consts: number, vars: number, directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null, - viewQuery: ViewQueriesFunction| null): TView { + viewQuery: ViewQueriesFunction| null, schemas: SchemaMetadata[] | null): TView { ngDevMode && ngDevMode.tView++; const bindingStartIndex = HEADER_OFFSET + consts; // This length does not yet contain host bindings from child directives because at this point, @@ -792,6 +797,7 @@ export function createTView( directiveRegistry: typeof directives === 'function' ? directives() : directives, pipeRegistry: typeof pipes === 'function' ? pipes() : pipes, firstChild: null, + schemas: schemas, }; } @@ -1218,7 +1224,7 @@ function elementPropertyInternal( } else if (tNode.type === TNodeType.Element) { if (ngDevMode) { validateAgainstEventProperties(propName); - validateAgainstUnknownProperties(element, propName, tNode); + validateAgainstUnknownProperties(lView, element, propName, tNode); ngDevMode.rendererSetProperty++; } @@ -1238,7 +1244,12 @@ function elementPropertyInternal( } function validateAgainstUnknownProperties( - element: RElement | RComment, propName: string, tNode: TNode) { + hostView: LView, element: RElement | RComment, propName: string, tNode: TNode) { + // If the tag matches any of the schemas we shouldn't throw. + if (matchingSchemas(hostView, tNode.tagName)) { + return; + } + // If prop is not a known property of the HTML element... if (!(propName in element) && // and we are in a browser context... (web worker nodes should be skipped) @@ -1251,6 +1262,22 @@ function validateAgainstUnknownProperties( } } +function matchingSchemas(hostView: LView, tagName: string | null): boolean { + const schemas = hostView[TVIEW].schemas; + + if (schemas !== null) { + for (let i = 0; i < schemas.length; i++) { + const schema = schemas[i]; + if (schema === NO_ERRORS_SCHEMA || + schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) { + return true; + } + } + } + + return false; +} + /** * Stores debugging data for this property binding on first template pass. * This enables features like DebugElement.properties. @@ -2040,7 +2067,8 @@ function addComponentLogic( const native = getNativeByTNode(previousOrParentTNode, lView); const tView = getOrCreateTView( - def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery); + def.template, def.consts, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery, + def.schemas); // Only component views should be added to the view tree directly. Embedded views are // accessed through their containers because they may be removed / re-added later. @@ -2197,7 +2225,7 @@ export function template( const tContainerNode = containerInternal(index, tagName || null, attrs || null); if (tView.firstTemplatePass) { tContainerNode.tViews = createTView( - -1, templateFn, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null); + -1, templateFn, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null, null); } createDirectivesAndLocals(tView, lView, localRefs, localRefExtractor); @@ -2435,7 +2463,7 @@ function getOrCreateEmbeddedTView( ngDevMode && assertEqual(Array.isArray(containerTViews), true, 'TViews should be in an array'); if (viewIndex >= containerTViews.length || containerTViews[viewIndex] == null) { containerTViews[viewIndex] = createTView( - viewIndex, null, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null); + viewIndex, null, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null, null); } return containerTViews[viewIndex]; } diff --git a/packages/core/src/render3/interfaces/definition.ts b/packages/core/src/render3/interfaces/definition.ts index 1228249cef..f60da2940b 100644 --- a/packages/core/src/render3/interfaces/definition.ts +++ b/packages/core/src/render3/interfaces/definition.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ViewEncapsulation} from '../../core'; +import {SchemaMetadata, ViewEncapsulation} from '../../core'; import {Type} from '../../interface/type'; import {CssSelectorList} from './projection'; @@ -264,7 +264,6 @@ export interface ComponentDef extends DirectiveDef { readonly onPush: boolean; /** - * Registry of directives and components that may be found in this view. * * The property is either an array of `DirectiveDef`s or a function which returns the array of @@ -280,6 +279,11 @@ export interface ComponentDef extends DirectiveDef { */ pipeDefs: PipeDefListOrFactory|null; + /** + * The set of schemas that declare elements to be allowed in the component's template. + */ + schemas: SchemaMetadata[]|null; + /** * Used to store the result of `noSideEffects` function so that it is not removed by closure * compiler. The property should never be read. diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index 7516848d1a..500984ab4d 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -10,6 +10,7 @@ import {InjectionToken} from '../../di/injection_token'; import {Injector} from '../../di/injector'; import {Type} from '../../interface/type'; import {QueryList} from '../../linker'; +import {SchemaMetadata} from '../../metadata'; import {Sanitizer} from '../../sanitization/security'; import {LContainer} from './container'; @@ -535,6 +536,11 @@ export interface TView { * A list of indices for child directives that have content queries. */ contentQueries: number[]|null; + + /** + * Set of schemas that declare elements to be allowed inside the view. + */ + schemas: SchemaMetadata[]|null; } export const enum RootContextFlags {Empty = 0b00, DetectChanges = 0b01, FlushPlayers = 0b10} diff --git a/packages/core/src/render3/jit/module.ts b/packages/core/src/render3/jit/module.ts index f706d178c8..0ef0cc7298 100644 --- a/packages/core/src/render3/jit/module.ts +++ b/packages/core/src/render3/jit/module.ts @@ -115,6 +115,7 @@ export function compileNgModuleDefs(moduleType: NgModuleType, ngModule: NgModule exports: flatten(ngModule.exports || EMPTY_ARRAY, resolveForwardRef) .map(expandModuleWithProviders), emitInline: true, + schemas: ngModule.schemas ? flatten(ngModule.schemas) : null, }); } return ngModuleDef; @@ -353,6 +354,7 @@ export function patchComponentDefWithScope( .filter(def => !!def); componentDef.pipeDefs = () => Array.from(transitiveScopes.compilation.pipes).map(pipe => getPipeDef(pipe) !); + componentDef.schemas = transitiveScopes.schemas; } /** @@ -375,6 +377,7 @@ export function transitiveScopesFor( } const scopes: NgModuleTransitiveScopes = { + schemas: def.schemas || null, compilation: { directives: new Set(), pipes: new Set(), diff --git a/packages/core/test/linker/ng_module_integration_spec.ts b/packages/core/test/linker/ng_module_integration_spec.ts index ad9a3e7e2a..669d48da96 100644 --- a/packages/core/test/linker/ng_module_integration_spec.ts +++ b/packages/core/test/linker/ng_module_integration_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, Compiler, Component, ComponentFactoryResolver, Directive, HostBinding, Inject, Injectable, InjectionToken, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, Self, Type, forwardRef, getModuleFactory, ɵivyEnabled as ivyEnabled} from '@angular/core'; +import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectorRef, Compiler, Component, ComponentFactoryResolver, Directive, HostBinding, Inject, Injectable, InjectionToken, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, Self, Type, forwardRef, getModuleFactory, ɵivyEnabled as ivyEnabled} from '@angular/core'; import {Console} from '@angular/core/src/console'; import {InjectableDef, defineInjectable} from '@angular/core/src/di/interface/defs'; import {getNgModuleDef} from '@angular/core/src/render3/definition'; @@ -266,22 +266,29 @@ function declareTests(config?: {useJit: boolean}) { expect(() => fixture.detectChanges()).toThrowError(/Can't bind to 'someUnknownProp'/); }); - fixmeIvy('FW-819: ngtsc compiler should support schemas') - .it('should not error on unknown bound properties on custom elements when using the CUSTOM_ELEMENTS_SCHEMA', - () => { - @Component({template: ''}) - class ComponentUsingInvalidProperty { - } + it('should not error on unknown bound properties on custom elements when using the CUSTOM_ELEMENTS_SCHEMA', + () => { + @Component({template: ''}) + class ComponentUsingInvalidProperty { + } - @NgModule({ - schemas: [CUSTOM_ELEMENTS_SCHEMA], - declarations: [ComponentUsingInvalidProperty] - }) - class SomeModule { - } + @NgModule({ + schemas: [CUSTOM_ELEMENTS_SCHEMA], + declarations: [ComponentUsingInvalidProperty], - expect(() => createModule(SomeModule)).not.toThrow(); - }); + // Note that we need to add the component to `entryComponents`, because of the + // `createComp` call below. In Ivy the property validation happens during the + // update phase so we need to create the component, in order for it to run. + entryComponents: [ComponentUsingInvalidProperty] + }) + class SomeModule { + } + + expect(() => { + const fixture = createComp(ComponentUsingInvalidProperty, SomeModule); + fixture.detectChanges(); + }).not.toThrow(); + }); }); describe('id', () => { diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index 2ff5a1600c..0aa0c0eded 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -2320,8 +2320,8 @@ describe('di', () => { describe('getOrCreateNodeInjector', () => { it('should handle initial undefined state', () => { const contentView = createLView( - null, createTView(-1, null, 1, 0, null, null, null), null, LViewFlags.CheckAlways, null, - null, {} as any, {} as any); + null, createTView(-1, null, 1, 0, null, null, null, null), null, LViewFlags.CheckAlways, + null, null, {} as any, {} as any); const oldView = enterView(contentView, null); try { const parentTNode = createNodeAtIndex(0, TNodeType.Element, null, null, null); diff --git a/packages/core/test/render3/styling/class_and_style_bindings_spec.ts b/packages/core/test/render3/styling/class_and_style_bindings_spec.ts index f4c4a9e892..d109d8ec73 100644 --- a/packages/core/test/render3/styling/class_and_style_bindings_spec.ts +++ b/packages/core/test/render3/styling/class_and_style_bindings_spec.ts @@ -34,8 +34,8 @@ describe('style and class based bindings', () => { const rootContext = createRootContext(requestAnimationFrame.bind(window), playerHandler || null); const lView = createLView( - null, createTView(-1, null, 1, 0, null, null, null), rootContext, LViewFlags.IsRoot, null, - null, domRendererFactory3, domRendererFactory3.createRenderer(element, null)); + null, createTView(-1, null, 1, 0, null, null, null, null), rootContext, LViewFlags.IsRoot, + null, null, domRendererFactory3, domRendererFactory3.createRenderer(element, null)); return lView; }