From 98277dacab5c43e907b80a12826351dfc3c0cb6b Mon Sep 17 00:00:00 2001 From: Dylan Hunn Date: Thu, 14 Dec 2023 09:30:02 -0800 Subject: [PATCH 01/27] refactor(compiler): Add an escape hatch to use non-unique const pool names (#53574) The template pipeline was producing slightly different names than TemplateDefinitionBuilder for defer deps functions. I have added a workaround in the name of backwards compatibility, to avoid suffixing the const pool function names. PR Close #53574 --- .../r3_view_compiler_deferred/TEST_CASES.json | 18 +++++++++++++ .../r3_view_compiler_deferred/defer_deps.ts | 25 +++++++++++++++++++ .../defer_deps_ext.ts | 5 ++++ .../defer_deps_template.js | 18 +++++++++++++ packages/compiler/src/constant_pool.ts | 8 ++++-- .../src/phases/create_defer_deps_fns.ts | 3 ++- 6 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/defer_deps.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/defer_deps_ext.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/defer_deps_template.js diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/TEST_CASES.json index 0088a6dcaccf3..e494886f2e379 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/TEST_CASES.json @@ -225,6 +225,24 @@ "failureMessage": "Incorrect template" } ] + }, + { + "description": "should generate a deferred block with implicit trigger references", + "inputFiles": [ + "defer_deps.ts", + "defer_deps_ext.ts" + ], + "expectations": [ + { + "files": [ + { + "expected": "defer_deps_template.js", + "generated": "defer_deps.js" + } + ], + "failureMessage": "Incorrect template" + } + ] } ] } \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/defer_deps.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/defer_deps.ts new file mode 100644 index 0000000000000..d72cec98c9652 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/defer_deps.ts @@ -0,0 +1,25 @@ +import {Component} from '@angular/core'; + +import {CmpA} from './defer_deps_ext'; + +@Component({ + selector: 'local-dep', + standalone: true, + template: 'Local dependency', +}) +export class LocalDep { +} + +@Component({ + selector: 'test-cmp', + standalone: true, + imports: [CmpA, LocalDep], + template: ` + @defer { + + + } +`, +}) +export class TestCmp { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/defer_deps_ext.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/defer_deps_ext.ts new file mode 100644 index 0000000000000..7b1d7d6b877f5 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/defer_deps_ext.ts @@ -0,0 +1,5 @@ +import {Component} from '@angular/core'; + +@Component({standalone: true, selector: 'cmp-a', template: 'CmpA!'}) +export class CmpA { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/defer_deps_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/defer_deps_template.js new file mode 100644 index 0000000000000..6c6c59e03685e --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/defer_deps_template.js @@ -0,0 +1,18 @@ +const TestCmp_Defer_1_DepsFn = () => [import("./defer_deps_ext").then(m => m.CmpA), LocalDep]; + +function TestCmp_Defer_0_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵelement(0, "cmp-a")(1, "local-dep"); + } +} + +export class LocalDep { +} + +… + +function TestCmp_Template(rf, ctx) { if (rf & 1) { + i0.ɵɵtemplate(0, TestCmp_Defer_0_Template, 2, 0); + i0.ɵɵdefer(1, 0, TestCmp_Defer_1_DepsFn); + i0.ɵɵdeferOnIdle(); +} } \ No newline at end of file diff --git a/packages/compiler/src/constant_pool.ts b/packages/compiler/src/constant_pool.ts index 0380535b1d336..03d66904dc41d 100644 --- a/packages/compiler/src/constant_pool.ts +++ b/packages/compiler/src/constant_pool.ts @@ -188,7 +188,11 @@ export class ConstantPool { } } - getSharedFunctionReference(fn: o.FunctionExpr|o.ArrowFunctionExpr, prefix: string): o.Expression { + // TODO: useUniqueName(false) is necessary for naming compatibility with + // TemplateDefinitionBuilder, but should be removed once Template Pipeline is the default. + getSharedFunctionReference( + fn: o.FunctionExpr|o.ArrowFunctionExpr, prefix: string, + useUniqueName: boolean = true): o.Expression { const isArrow = fn instanceof o.ArrowFunctionExpr; for (const current of this.statements) { @@ -206,7 +210,7 @@ export class ConstantPool { } // Otherwise declare the function. - const name = this.uniqueName(prefix); + const name = useUniqueName ? this.uniqueName(prefix) : prefix; this.statements.push(fn.toDeclStmt(name, o.StmtModifier.Final)); return o.variable(name); } diff --git a/packages/compiler/src/template/pipeline/src/phases/create_defer_deps_fns.ts b/packages/compiler/src/template/pipeline/src/phases/create_defer_deps_fns.ts index 56b8f4517a239..9cc8f58c8d20d 100644 --- a/packages/compiler/src/template/pipeline/src/phases/create_defer_deps_fns.ts +++ b/packages/compiler/src/template/pipeline/src/phases/create_defer_deps_fns.ts @@ -42,7 +42,8 @@ export function createDeferDepsFns(job: ComponentCompilationJob): void { 'AssertionError: slot must be assigned bfore extracting defer deps functions'); } op.resolverFn = job.pool.getSharedFunctionReference( - depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`); + depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`, + /* Don't use unique names for TDB compatibility */ false); } } } From a5565d176ad4206c554095b98bc5e6b0d7a7b782 Mon Sep 17 00:00:00 2001 From: Dylan Hunn Date: Thu, 14 Dec 2023 10:14:53 -0800 Subject: [PATCH 02/27] refactor(compiler): Update a user visible error to match TDB (#53574) This error about global event targets is user-facing; update it to match TDB's error verbatim. PR Close #53574 --- packages/compiler/src/template/pipeline/src/phases/reify.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/compiler/src/template/pipeline/src/phases/reify.ts b/packages/compiler/src/template/pipeline/src/phases/reify.ts index a68ec1202e85b..6445dc0616ea8 100644 --- a/packages/compiler/src/template/pipeline/src/phases/reify.ts +++ b/packages/compiler/src/template/pipeline/src/phases/reify.ts @@ -125,7 +125,8 @@ function reifyCreateOperations(unit: CompilationUnit, ops: ir.OpList Date: Thu, 14 Dec 2023 10:56:35 -0800 Subject: [PATCH 03/27] refactor(compiler): Fix self-closing element source spans in template pipeline (#53574) When an element is self-closing, it will cause an `element` instruction to be emitted (instead of `elementStart`/`elementEnd`). In that case, we should use map whole source span for the instruction, not just the starting span. PR Close #53574 --- .../test/ngtsc/template_mapping_spec.ts | 63 ++++++++++--------- .../template/pipeline/ir/src/ops/create.ts | 29 ++++++--- .../src/template/pipeline/src/ingest.ts | 12 ++-- .../src/template/pipeline/src/phases/reify.ts | 12 ++-- 4 files changed, 64 insertions(+), 52 deletions(-) diff --git a/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts b/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts index b8575cbca2a5b..596d30964ec10 100644 --- a/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts +++ b/packages/compiler-cli/test/ngtsc/template_mapping_spec.ts @@ -426,36 +426,39 @@ runInEachFileSystem((os) => { }); }); - it('should correctly handle collapsed whitespace in interpolation placeholder source-mappings', - async () => { - const mappings = await compileAndMap( - `
pre-body {{greeting}} post-body
`); - expectMapping(mappings, { - source: '
', - generated: 'i0.ɵɵelementStart(0, "div", 0)', - sourceUrl: '../test.ts', - }); - expectMapping(mappings, { - source: '
', - generated: 'i0.ɵɵelementEnd()', - sourceUrl: '../test.ts', - }); - expectMapping(mappings, { - source: ' pre-body ', - generated: '` pre-body ${', - sourceUrl: '../test.ts', - }); - expectMapping(mappings, { - source: '{{greeting}}', - generated: '"\\uFFFD0\\uFFFD"', - sourceUrl: '../test.ts', - }); - expectMapping(mappings, { - source: ' post-body', - generated: '}:INTERPOLATION: post-body`', - sourceUrl: '../test.ts', - }); - }); + // TODO: Temporarily disabled because Template Pipeline produces different const indices + // than TemplateDefinitionBuilder. Re-enable after Template Pipeline is the default, and + // update the test with the new const index. + xit('should correctly handle collapsed whitespace in interpolation placeholder source-mappings', + async () => { + const mappings = await compileAndMap( + `
pre-body {{greeting}} post-body
`); + expectMapping(mappings, { + source: '
', + generated: 'i0.ɵɵelementStart(0, "div", 0)', + sourceUrl: '../test.ts', + }); + expectMapping(mappings, { + source: '
', + generated: 'i0.ɵɵelementEnd()', + sourceUrl: '../test.ts', + }); + expectMapping(mappings, { + source: ' pre-body ', + generated: '` pre-body ${', + sourceUrl: '../test.ts', + }); + expectMapping(mappings, { + source: '{{greeting}}', + generated: '"\\uFFFD0\\uFFFD"', + sourceUrl: '../test.ts', + }); + expectMapping(mappings, { + source: ' post-body', + generated: '}:INTERPOLATION: post-body`', + sourceUrl: '../test.ts', + }); + }); it('should correctly handle collapsed whitespace in element placeholder source-mappings', async () => { diff --git a/packages/compiler/src/template/pipeline/ir/src/ops/create.ts b/packages/compiler/src/template/pipeline/ir/src/ops/create.ts index 19ec3850be332..4424fb57cee19 100644 --- a/packages/compiler/src/template/pipeline/ir/src/ops/create.ts +++ b/packages/compiler/src/template/pipeline/ir/src/ops/create.ts @@ -101,7 +101,15 @@ export interface ElementOrContainerOpBase extends Op, ConsumesSlotOpTr */ nonBindable: boolean; - sourceSpan: ParseSourceSpan; + /** + * The span of the element's start tag. + */ + startSourceSpan: ParseSourceSpan; + + /** + * The whole source span of the element, including children. + */ + wholeSourceSpan: ParseSourceSpan; } export interface ElementOpBase extends ElementOrContainerOpBase { @@ -135,7 +143,7 @@ export interface ElementStartOp extends ElementOpBase { */ export function createElementStartOp( tag: string, xref: XrefId, namespace: Namespace, i18nPlaceholder: i18n.TagPlaceholder|undefined, - sourceSpan: ParseSourceSpan): ElementStartOp { + startSourceSpan: ParseSourceSpan, wholeSourceSpan: ParseSourceSpan): ElementStartOp { return { kind: OpKind.ElementStart, xref, @@ -146,7 +154,8 @@ export function createElementStartOp( nonBindable: false, namespace, i18nPlaceholder, - sourceSpan, + startSourceSpan, + wholeSourceSpan, ...TRAIT_CONSUMES_SLOT, ...NEW_OP, }; @@ -201,7 +210,7 @@ export interface TemplateOp extends ElementOpBase { export function createTemplateOp( xref: XrefId, templateKind: TemplateKind, tag: string|null, functionNameSuffix: string, namespace: Namespace, i18nPlaceholder: i18n.TagPlaceholder|i18n.BlockPlaceholder|undefined, - sourceSpan: ParseSourceSpan): TemplateOp { + startSourceSpan: ParseSourceSpan, wholeSourceSpan: ParseSourceSpan): TemplateOp { return { kind: OpKind.Template, xref, @@ -216,7 +225,8 @@ export function createTemplateOp( nonBindable: false, namespace, i18nPlaceholder, - sourceSpan, + startSourceSpan, + wholeSourceSpan, ...TRAIT_CONSUMES_SLOT, ...NEW_OP, }; @@ -280,8 +290,6 @@ export interface RepeaterCreateOp extends ElementOpBase { * The i18n placeholder for the empty template. */ emptyI18nPlaceholder: i18n.BlockPlaceholder|undefined; - - sourceSpan: ParseSourceSpan; } // TODO: add source spans? @@ -298,8 +306,8 @@ export interface RepeaterVarNames { export function createRepeaterCreateOp( primaryView: XrefId, emptyView: XrefId|null, tag: string|null, track: o.Expression, varNames: RepeaterVarNames, i18nPlaceholder: i18n.BlockPlaceholder|undefined, - emptyI18nPlaceholder: i18n.BlockPlaceholder|undefined, - sourceSpan: ParseSourceSpan): RepeaterCreateOp { + emptyI18nPlaceholder: i18n.BlockPlaceholder|undefined, startSourceSpan: ParseSourceSpan, + wholeSourceSpan: ParseSourceSpan): RepeaterCreateOp { return { kind: OpKind.RepeaterCreate, attributes: null, @@ -319,7 +327,8 @@ export function createRepeaterCreateOp( usesComponentInstance: false, i18nPlaceholder, emptyI18nPlaceholder, - sourceSpan, + startSourceSpan, + wholeSourceSpan, ...TRAIT_CONSUMES_SLOT, ...NEW_OP, numSlotsUsed: emptyView === null ? 2 : 3, diff --git a/packages/compiler/src/template/pipeline/src/ingest.ts b/packages/compiler/src/template/pipeline/src/ingest.ts index 751d0b47764fd..df75e9d470ad2 100644 --- a/packages/compiler/src/template/pipeline/src/ingest.ts +++ b/packages/compiler/src/template/pipeline/src/ingest.ts @@ -181,7 +181,7 @@ function ingestElement(unit: ViewCompilationUnit, element: t.Element): void { const startOp = ir.createElementStartOp( elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof i18n.TagPlaceholder ? element.i18n : undefined, - element.startSourceSpan); + element.startSourceSpan, element.sourceSpan); unit.create.push(startOp); ingestElementBindings(unit, startOp, element); @@ -236,7 +236,7 @@ function ingestTemplate(unit: ViewCompilationUnit, tmpl: t.Template): void { isPlainTemplate(tmpl) ? ir.TemplateKind.NgTemplate : ir.TemplateKind.Structural; const templateOp = ir.createTemplateOp( childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, - i18nPlaceholder, tmpl.startSourceSpan); + i18nPlaceholder, tmpl.startSourceSpan, tmpl.sourceSpan); unit.create.push(templateOp); ingestTemplateBindings(unit, templateOp, tmpl, templateKind); @@ -359,7 +359,7 @@ function ingestIfBlock(unit: ViewCompilationUnit, ifBlock: t.IfBlock): void { const templateOp = ir.createTemplateOp( cView.xref, ir.TemplateKind.Block, tagName, 'Conditional', ir.Namespace.HTML, - ifCaseI18nMeta, ifCase.sourceSpan); + ifCaseI18nMeta, ifCase.startSourceSpan, ifCase.sourceSpan); unit.create.push(templateOp); if (firstXref === null) { @@ -397,7 +397,7 @@ function ingestSwitchBlock(unit: ViewCompilationUnit, switchBlock: t.SwitchBlock } const templateOp = ir.createTemplateOp( cView.xref, ir.TemplateKind.Block, null, 'Case', ir.Namespace.HTML, switchCaseI18nMeta, - switchCase.sourceSpan); + switchCase.startSourceSpan, switchCase.sourceSpan); unit.create.push(templateOp); if (firstXref === null) { firstXref = cView.xref; @@ -430,7 +430,7 @@ function ingestDeferView( ingestNodes(secondaryView, children); const templateOp = ir.createTemplateOp( secondaryView.xref, ir.TemplateKind.Block, null, `Defer${suffix}`, ir.Namespace.HTML, - i18nMeta, sourceSpan!); + i18nMeta, sourceSpan!, sourceSpan!); unit.create.push(templateOp); return templateOp; } @@ -627,7 +627,7 @@ function ingestForBlock(unit: ViewCompilationUnit, forBlock: t.ForLoopBlock): vo const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock); const repeaterCreate = ir.createRepeaterCreateOp( repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, i18nPlaceholder, - emptyI18nPlaceholder, forBlock.sourceSpan); + emptyI18nPlaceholder, forBlock.startSourceSpan, forBlock.sourceSpan); unit.create.push(repeaterCreate); const expression = convertAst( diff --git a/packages/compiler/src/template/pipeline/src/phases/reify.ts b/packages/compiler/src/template/pipeline/src/phases/reify.ts index 6445dc0616ea8..b50d9f043078e 100644 --- a/packages/compiler/src/template/pipeline/src/phases/reify.ts +++ b/packages/compiler/src/template/pipeline/src/phases/reify.ts @@ -49,14 +49,14 @@ function reifyCreateOperations(unit: CompilationUnit, ops: ir.OpList Date: Thu, 14 Dec 2023 13:03:01 -0800 Subject: [PATCH 04/27] refactor(compiler): Defer when consumes a variable (binding) slot (#53574) The template pipeline was previously not reserving a variable slot for the result of the `deferWhen` instruction, which caused the `defer when` feature to crash at runtime. PR Close #53574 --- .../GOLDEN_PARTIAL.js | 148 ++++++++++++++++++ .../r3_view_compiler_deferred/TEST_CASES.json | 17 ++ ...ferred_when_with_pipe_template.pipeline.js | 2 +- .../lazy_with_blocks.ts | 40 +++++ .../lazy_with_blocks_template.js | 51 ++++++ .../template/pipeline/ir/src/expression.ts | 3 + .../template/pipeline/ir/src/ops/update.ts | 3 +- .../src/template/pipeline/src/ingest.ts | 13 +- .../pipeline/src/phases/var_counting.ts | 1 + 9 files changed, 272 insertions(+), 6 deletions(-) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/lazy_with_blocks.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/lazy_with_blocks_template.js diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/GOLDEN_PARTIAL.js index 73d180310c57c..387a2911e286c 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/GOLDEN_PARTIAL.js @@ -743,3 +743,151 @@ export declare class MyApp { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: defer_deps_ext.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class CmpA { +} +CmpA.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: CmpA, deps: [], target: i0.ɵɵFactoryTarget.Component }); +CmpA.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: CmpA, isStandalone: true, selector: "cmp-a", ngImport: i0, template: 'CmpA!', isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: CmpA, decorators: [{ + type: Component, + args: [{ standalone: true, selector: 'cmp-a', template: 'CmpA!' }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: defer_deps_ext.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class CmpA { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: defer_deps.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import { CmpA } from './defer_deps_ext'; +import * as i0 from "@angular/core"; +export class LocalDep { +} +LocalDep.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: LocalDep, deps: [], target: i0.ɵɵFactoryTarget.Component }); +LocalDep.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: LocalDep, isStandalone: true, selector: "local-dep", ngImport: i0, template: 'Local dependency', isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: LocalDep, decorators: [{ + type: Component, + args: [{ + selector: 'local-dep', + standalone: true, + template: 'Local dependency', + }] + }] }); +export class TestCmp { +} +TestCmp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestCmp, deps: [], target: i0.ɵɵFactoryTarget.Component }); +TestCmp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: TestCmp, isStandalone: true, selector: "test-cmp", ngImport: i0, template: ` + @defer { + + + } +`, isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestCmp, decorators: [{ + type: Component, + args: [{ + selector: 'test-cmp', + standalone: true, + imports: [CmpA, LocalDep], + template: ` + @defer { + + + } +`, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: defer_deps.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class LocalDep { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} +export declare class TestCmp { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: lazy_with_blocks.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +class MyLazyCmp { +} +MyLazyCmp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyLazyCmp, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyLazyCmp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyLazyCmp, isStandalone: true, selector: "my-lazy-cmp", ngImport: i0, template: 'Hi!', isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyLazyCmp, decorators: [{ + type: Component, + args: [{ + selector: 'my-lazy-cmp', + standalone: true, + template: 'Hi!', + }] + }] }); +class SimpleComponent { + constructor() { + this.isVisible = false; + } + ngOnInit() { + setTimeout(() => { + // This changes the triggering condition of the defer block, + // but it should be ignored and the placeholder content should be visible. + this.isVisible = true; + }); + } +} +SimpleComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: SimpleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +SimpleComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: SimpleComponent, isStandalone: true, selector: "app", ngImport: i0, template: ` + Visible: {{ isVisible }}. + + @defer (when isVisible) { + + } @loading { + Loading... + } @placeholder { + Placeholder! + } @error { + Failed to load dependencies :( + } + `, isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: SimpleComponent, decorators: [{ + type: Component, + args: [{ + standalone: true, + selector: 'app', + imports: [MyLazyCmp], + template: ` + Visible: {{ isVisible }}. + + @defer (when isVisible) { + + } @loading { + Loading... + } @placeholder { + Placeholder! + } @error { + Failed to load dependencies :( + } + ` + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: lazy_with_blocks.d.ts + ****************************************************************************************************/ +export {}; + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/TEST_CASES.json index e494886f2e379..8e9b79291ee81 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/TEST_CASES.json @@ -243,6 +243,23 @@ "failureMessage": "Incorrect template" } ] + }, + { + "description": "should generate a lazy deferred block with empty and placeholder", + "inputFiles": [ + "lazy_with_blocks.ts" + ], + "expectations": [ + { + "files": [ + { + "expected": "lazy_with_blocks_template.js", + "generated": "lazy_with_blocks.js" + } + ], + "failureMessage": "Incorrect template" + } + ] } ] } \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_when_with_pipe_template.pipeline.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_when_with_pipe_template.pipeline.js index eb57f1661b54b..15e3e86ca4d9b 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_when_with_pipe_template.pipeline.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_when_with_pipe_template.pipeline.js @@ -8,7 +8,7 @@ function MyApp_Template(rf, ctx) { if (rf & 2) { $r3$.ɵɵtextInterpolate1(" ", ctx.message, " "); $r3$.ɵɵadvance(2); - $r3$.ɵɵdeferWhen(ctx.isVisible() && $r3$.ɵɵpipeBind1(4, 1, ctx.isReady)); + $r3$.ɵɵdeferWhen(ctx.isVisible() && $r3$.ɵɵpipeBind1(4, 2, ctx.isReady)); } } \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/lazy_with_blocks.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/lazy_with_blocks.ts new file mode 100644 index 0000000000000..131eb4bc349dd --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/lazy_with_blocks.ts @@ -0,0 +1,40 @@ +import {Component} from '@angular/core'; + + +@Component({ + selector: 'my-lazy-cmp', + standalone: true, + template: 'Hi!', +}) +class MyLazyCmp { +} + +@Component({ + standalone: true, + selector: 'app', + imports: [MyLazyCmp], + template: ` + Visible: {{ isVisible }}. + + @defer (when isVisible) { + + } @loading { + Loading... + } @placeholder { + Placeholder! + } @error { + Failed to load dependencies :( + } + ` +}) +class SimpleComponent { + isVisible = false; + + ngOnInit() { + setTimeout(() => { + // This changes the triggering condition of the defer block, + // but it should be ignored and the placeholder content should be visible. + this.isVisible = true; + }); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/lazy_with_blocks_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/lazy_with_blocks_template.js new file mode 100644 index 0000000000000..71e8c77157df5 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/lazy_with_blocks_template.js @@ -0,0 +1,51 @@ +const SimpleComponent_Defer_5_DepsFn = () => [MyLazyCmp]; + +function SimpleComponent_Defer_1_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵelement(0, "my-lazy-cmp"); + } +} + +function SimpleComponent_DeferLoading_2_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵtext(0, " Loading... "); + } +} + +function SimpleComponent_DeferPlaceholder_3_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵtext(0, " Placeholder! "); + } +} + +function SimpleComponent_DeferError_4_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵtext(0, " Failed to load dependencies :( "); + } +} + +… + +decls: 1, +vars: 0, +template: function MyLazyCmp_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵtext(0, "Hi!"); + } +} + +… + +decls: 7, +vars: 2, +template: function SimpleComponent_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵtext(0); + i0.ɵɵtemplate(1, SimpleComponent_Defer_1_Template, 1, 0)(2, SimpleComponent_DeferLoading_2_Template, 1, 0)(3, SimpleComponent_DeferPlaceholder_3_Template, 1, 0)(4, SimpleComponent_DeferError_4_Template, 1, 0); + i0.ɵɵdefer(5, 1, SimpleComponent_Defer_5_DepsFn, 2, 3, 4); + } if (rf & 2) { + i0.ɵɵtextInterpolate1(" Visible: ", ctx.isVisible, ". "); + i0.ɵɵadvance(5); + i0.ɵɵdeferWhen(ctx.isVisible); + } +} \ No newline at end of file diff --git a/packages/compiler/src/template/pipeline/ir/src/expression.ts b/packages/compiler/src/template/pipeline/ir/src/expression.ts index 82965731f2678..2036cabb1cfad 100644 --- a/packages/compiler/src/template/pipeline/ir/src/expression.ts +++ b/packages/compiler/src/template/pipeline/ir/src/expression.ts @@ -964,6 +964,9 @@ export function transformExpressionsInOp( op.placeholderConfig = transformExpressionsInExpression(op.placeholderConfig, transform, flags); } + if (op.resolverFn !== null) { + op.resolverFn = transformExpressionsInExpression(op.resolverFn, transform, flags); + } break; case OpKind.I18nMessage: for (const [placeholder, expr] of op.params) { diff --git a/packages/compiler/src/template/pipeline/ir/src/ops/update.ts b/packages/compiler/src/template/pipeline/ir/src/ops/update.ts index 3f363311f749b..39b5e09f561d7 100644 --- a/packages/compiler/src/template/pipeline/ir/src/ops/update.ts +++ b/packages/compiler/src/template/pipeline/ir/src/ops/update.ts @@ -604,7 +604,7 @@ export function createRepeaterOp( }; } -export interface DeferWhenOp extends Op, DependsOnSlotContextOpTrait { +export interface DeferWhenOp extends Op, DependsOnSlotContextOpTrait, ConsumesVarsTrait { kind: OpKind.DeferWhen; /** @@ -636,6 +636,7 @@ export function createDeferWhenOp( sourceSpan, ...NEW_OP, ...TRAIT_DEPENDS_ON_SLOT_CONTEXT, + ...TRAIT_CONSUMES_VARS, }; } diff --git a/packages/compiler/src/template/pipeline/src/ingest.ts b/packages/compiler/src/template/pipeline/src/ingest.ts index df75e9d470ad2..9ac8e6e5c86af 100644 --- a/packages/compiler/src/template/pipeline/src/ingest.ts +++ b/packages/compiler/src/template/pipeline/src/ingest.ts @@ -531,6 +531,11 @@ function ingestDeferBlock(unit: ViewCompilationUnit, deferBlock: t.DeferredBlock deferOnOps.push(deferOnOp); } if (triggers.when !== undefined) { + if (triggers.when.value instanceof e.Interpolation) { + // TemplateDefinitionBuilder supports this case, but it's very strange to me. What would it + // even mean? + throw new Error(`Unexpected interpolation in defer block when trigger`); + } const deferOnOp = ir.createDeferWhenOp( deferXref, convertAst(triggers.when.value, unit.job, triggers.when.sourceSpan), prefetch, triggers.when.sourceSpan); @@ -767,15 +772,15 @@ function convertAst( } function convertAstWithInterpolation( - job: CompilationJob, value: e.AST|string, - i18nMeta: i18n.I18nMeta|null|undefined): o.Expression|ir.Interpolation { + job: CompilationJob, value: e.AST|string, i18nMeta: i18n.I18nMeta|null|undefined, + sourceSpan?: ParseSourceSpan): o.Expression|ir.Interpolation { let expression: o.Expression|ir.Interpolation; if (value instanceof e.Interpolation) { expression = new ir.Interpolation( - value.strings, value.expressions.map(e => convertAst(e, job, null)), + value.strings, value.expressions.map(e => convertAst(e, job, sourceSpan ?? null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {})); } else if (value instanceof e.AST) { - expression = convertAst(value, job, null); + expression = convertAst(value, job, sourceSpan ?? null); } else { expression = o.literal(value); } diff --git a/packages/compiler/src/template/pipeline/src/phases/var_counting.ts b/packages/compiler/src/template/pipeline/src/phases/var_counting.ts index f0e8ce4c8793e..46c7a80b65f53 100644 --- a/packages/compiler/src/template/pipeline/src/phases/var_counting.ts +++ b/packages/compiler/src/template/pipeline/src/phases/var_counting.ts @@ -128,6 +128,7 @@ function varsUsedByOp(op: (ir.CreateOp|ir.UpdateOp)&ir.ConsumesVarsTrait): numbe return op.interpolation.expressions.length; case ir.OpKind.I18nExpression: case ir.OpKind.Conditional: + case ir.OpKind.DeferWhen: return 1; default: throw new Error(`Unhandled op: ${ir.OpKind[op.kind]}`); From d1e7ce1b2dd3a6eac9166ec66ca0c89d4eb28787 Mon Sep 17 00:00:00 2001 From: Dylan Hunn Date: Thu, 14 Dec 2023 15:43:18 -0800 Subject: [PATCH 05/27] refactor(compiler): Host attribute bindings should always be extracted into hostAttrs (#53574) Host attribute literal bindings should not result in an `attribute` update instruction. PR Close #53574 --- .../host_bindings/TEST_CASES.json | 1 - ...with_ts_expression_node_template.pipeline.js | 5 ----- .../src/template/pipeline/src/ingest.ts | 17 +++++++++-------- .../pipeline/src/phases/attribute_extraction.ts | 4 ++-- 4 files changed, 11 insertions(+), 16 deletions(-) delete mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node_template.pipeline.js diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/TEST_CASES.json index 5835b76e33751..68f7914f131d3 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/TEST_CASES.json @@ -362,7 +362,6 @@ "files": [ { "expected": "host_with_ts_expression_node_template.js", - "templatePipelineExpected": "host_with_ts_expression_node_template.pipeline.js", "generated": "host_with_ts_expression_node.js" } ] diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node_template.pipeline.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node_template.pipeline.js deleted file mode 100644 index 39c443d3d866e..0000000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_with_ts_expression_node_template.pipeline.js +++ /dev/null @@ -1,5 +0,0 @@ -function MyComponent_HostBindings(rf, ctx) { - if (rf & 2) { - i0.ɵɵattribute("foo", BAR_CONST); - } -} \ No newline at end of file diff --git a/packages/compiler/src/template/pipeline/src/ingest.ts b/packages/compiler/src/template/pipeline/src/ingest.ts index 9ac8e6e5c86af..7dbc486ae0222 100644 --- a/packages/compiler/src/template/pipeline/src/ingest.ts +++ b/packages/compiler/src/template/pipeline/src/ingest.ts @@ -78,7 +78,7 @@ export function ingestHostBinding( .calcPossibleSecurityContexts( input.componentSelector, property.name, bindingKind === ir.BindingKind.Attribute) .filter(context => context !== SecurityContext.NONE); - ingestHostProperty(job, property, bindingKind, false, securityContexts); + ingestHostProperty(job, property, bindingKind, securityContexts); } for (const [name, expr] of Object.entries(input.attributes) ?? []) { const securityContexts = @@ -96,7 +96,7 @@ export function ingestHostBinding( // with ordinary components. This would allow us to share a lot more ingestion code. export function ingestHostProperty( job: HostBindingCompilationJob, property: e.ParsedProperty, bindingKind: ir.BindingKind, - isTextAttribute: boolean, securityContexts: SecurityContext[]): void { + securityContexts: SecurityContext[]): void { let expression: o.Expression|ir.Interpolation; const ast = property.expression.ast; if (ast instanceof e.Interpolation) { @@ -106,19 +106,20 @@ export function ingestHostProperty( expression = convertAst(ast, job, property.sourceSpan); } job.root.update.push(ir.createBindingOp( - job.root.xref, bindingKind, property.name, expression, null, securityContexts, - isTextAttribute, false, null, /* TODO: How do Host bindings handle i18n attrs? */ null, - property.sourceSpan)); + job.root.xref, bindingKind, property.name, expression, null, securityContexts, false, false, + null, /* TODO: How do Host bindings handle i18n attrs? */ null, property.sourceSpan)); } export function ingestHostAttribute( job: HostBindingCompilationJob, name: string, value: o.Expression, securityContexts: SecurityContext[]): void { const attrBinding = ir.createBindingOp( - job.root.xref, ir.BindingKind.Attribute, name, value, null, securityContexts, true, false, - null, + job.root.xref, ir.BindingKind.Attribute, name, value, null, securityContexts, + /* Host attributes should always be extracted to const hostAttrs, even if they are not + *strictly* text literals */ + true, false, null, /* TODO */ null, - /* TODO: host attribute source spans */ null!); + /** TODO: May be null? */ value.sourceSpan!); job.root.update.push(attrBinding); } diff --git a/packages/compiler/src/template/pipeline/src/phases/attribute_extraction.ts b/packages/compiler/src/template/pipeline/src/phases/attribute_extraction.ts index 0bf2d914110b0..2bc529c408cdc 100644 --- a/packages/compiler/src/template/pipeline/src/phases/attribute_extraction.ts +++ b/packages/compiler/src/template/pipeline/src/phases/attribute_extraction.ts @@ -106,10 +106,10 @@ function extractAttributeOp( return; } - let extractable = op.expression.isConstant(); + let extractable = op.isTextAttribute || op.expression.isConstant(); if (unit.job.compatibility === ir.CompatibilityMode.TemplateDefinitionBuilder) { // TemplateDefinitionBuilder only extracted attributes that were string literals. - extractable = ir.isStringLiteral(op.expression); + extractable = op.isTextAttribute || ir.isStringLiteral(op.expression); if (op.name === 'style' || op.name === 'class') { // For style and class attributes, TemplateDefinitionBuilder only extracted them if they were // text attributes. For example, `[attr.class]="'my-class'"` was not extracted despite being a From 39767e764f910f4dfa1211b3cce244055b372204 Mon Sep 17 00:00:00 2001 From: Dylan Hunn Date: Thu, 14 Dec 2023 15:54:08 -0800 Subject: [PATCH 06/27] refactor(compiler): Repeaters may consume a variable for their empty block (#53574) Repeaters consume a variable slot only when they have an empty block. PR Close #53574 --- .../compiler/src/template/pipeline/ir/src/ops/create.ts | 5 +++-- .../src/template/pipeline/src/phases/var_counting.ts | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/compiler/src/template/pipeline/ir/src/ops/create.ts b/packages/compiler/src/template/pipeline/ir/src/ops/create.ts index 4424fb57cee19..7fe6560510fca 100644 --- a/packages/compiler/src/template/pipeline/ir/src/ops/create.ts +++ b/packages/compiler/src/template/pipeline/ir/src/ops/create.ts @@ -14,7 +14,7 @@ import {R3DeferBlockMetadata} from '../../../../../render3/view/api'; import {BindingKind, DeferTriggerKind, I18nContextKind, I18nParamValueFlags, Namespace, OpKind, TemplateKind} from '../enums'; import {SlotHandle} from '../handle'; import {Op, OpList, XrefId} from '../operations'; -import {ConsumesSlotOpTrait, TRAIT_CONSUMES_SLOT} from '../traits'; +import {ConsumesSlotOpTrait, ConsumesVarsTrait, TRAIT_CONSUMES_SLOT, TRAIT_CONSUMES_VARS} from '../traits'; import {ListEndOp, NEW_OP, StatementOp, VariableOp} from './shared'; @@ -235,7 +235,7 @@ export function createTemplateOp( /** * An op that creates a repeater (e.g. a for loop). */ -export interface RepeaterCreateOp extends ElementOpBase { +export interface RepeaterCreateOp extends ElementOpBase, ConsumesVarsTrait { kind: OpKind.RepeaterCreate; /** @@ -331,6 +331,7 @@ export function createRepeaterCreateOp( wholeSourceSpan, ...TRAIT_CONSUMES_SLOT, ...NEW_OP, + ...TRAIT_CONSUMES_VARS, numSlotsUsed: emptyView === null ? 2 : 3, }; } diff --git a/packages/compiler/src/template/pipeline/src/phases/var_counting.ts b/packages/compiler/src/template/pipeline/src/phases/var_counting.ts index 46c7a80b65f53..0487580216b38 100644 --- a/packages/compiler/src/template/pipeline/src/phases/var_counting.ts +++ b/packages/compiler/src/template/pipeline/src/phases/var_counting.ts @@ -130,6 +130,12 @@ function varsUsedByOp(op: (ir.CreateOp|ir.UpdateOp)&ir.ConsumesVarsTrait): numbe case ir.OpKind.Conditional: case ir.OpKind.DeferWhen: return 1; + case ir.OpKind.RepeaterCreate: + // Repeaters may require an extra variable binding slot, if they have an empty view, for the + // empty block tracking. + // TODO: It's a bit odd to have a create mode instruction consume variable slots. Maybe we can + // find a way to use the Repeater update op instead. + return op.emptyView ? 1 : 0; default: throw new Error(`Unhandled op: ${ir.OpKind[op.kind]}`); } From 7ef5d9deefce2ed22fb8a970d02346a33bb96cf0 Mon Sep 17 00:00:00 2001 From: Dylan Hunn Date: Thu, 14 Dec 2023 18:04:35 -0800 Subject: [PATCH 07/27] refactor(compiler): Support class and non-class attributes with the same name (#53574) Some elements may have multiple bindings with the same name. We should accept and emit them all, as long as they have different kinds. Co-authored-by: Miles Malerba PR Close #53574 --- .../class_bindings/GOLDEN_PARTIAL.js | 47 +++++++++++++++++++ .../class_bindings/TEST_CASES.json | 18 +++++++ .../class_bindings/shared_name_with_consts.ts | 18 +++++++ .../shared_name_with_consts_template.js | 30 ++++++++++++ ...ared_name_with_consts_template.pipeline.js | 32 +++++++++++++ .../pipeline/src/phases/const_collection.ts | 16 +++++-- .../src/phases/parse_extracted_styles.ts | 22 +++++++++ 7 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/shared_name_with_consts.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/shared_name_with_consts_template.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/shared_name_with_consts_template.pipeline.js diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/GOLDEN_PARTIAL.js index 52f0edaa6446d..650c62c9e0052 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/GOLDEN_PARTIAL.js @@ -219,3 +219,50 @@ export declare class MyComponent { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: shared_name_with_consts.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { + constructor() { + this.tabIndex = 0; + } +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, isStandalone: true, selector: "my-component", ngImport: i0, template: ` +
+
+
+
+
+
+
+`, isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, decorators: [{ + type: Component, + args: [{ + selector: 'my-component', + standalone: true, + template: ` +
+
+
+
+
+
+
+` + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: shared_name_with_consts.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + tabIndex: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/TEST_CASES.json index adb4ec394c3c3..ca8b443f2293b 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/TEST_CASES.json @@ -70,6 +70,24 @@ ] } ] + }, + { + "description": "should handle class that shares its name with other const array items", + "inputFiles": [ + "shared_name_with_consts.ts" + ], + "expectations": [ + { + "files": [ + { + "templatePipelineExpected": "shared_name_with_consts_template.pipeline.js", + "expected": "shared_name_with_consts_template.js", + "generated": "shared_name_with_consts.js" + } + ], + "failureMessage": "Incorrect template" + } + ] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/shared_name_with_consts.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/shared_name_with_consts.ts new file mode 100644 index 0000000000000..49a467e61389b --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/shared_name_with_consts.ts @@ -0,0 +1,18 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'my-component', + standalone: true, + template: ` +
+
+
+
+
+
+
+` +}) +export class MyComponent { + tabIndex = 0; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/shared_name_with_consts_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/shared_name_with_consts_template.js new file mode 100644 index 0000000000000..090b286ed2e53 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/shared_name_with_consts_template.js @@ -0,0 +1,30 @@ +consts: () => { + __i18nMsg__('label', [], {}, {}) + return [ + ["attr", "", __AttributeMarker.Classes__, "attr"], + ["ngProjectAs", "selector", __AttributeMarker.ProjectAs__, ["selector"], __AttributeMarker.Classes__, "selector"], + [__AttributeMarker.Classes__, "width", __AttributeMarker.Styles__, "width", "0px"], + [__AttributeMarker.Classes__, "tabindex", __AttributeMarker.Bindings__, "tabindex"], + ["class", "ngIf", __AttributeMarker.Template__, "ngIf"], + ["aria-label", i18n_0, __AttributeMarker.Classes__, "aria-label"], + ["all", "", "ngProjectAs", "all", "style", "all:all", "class", "all", __AttributeMarker.ProjectAs__, ["all"], __AttributeMarker.Bindings__, "all", __AttributeMarker.Template__], + [__AttributeMarker.Classes__, "ngIf"], + ["all", "", "ngProjectAs", "all", __AttributeMarker.ProjectAs__, ["all"], __AttributeMarker.Classes__, "all", __AttributeMarker.Styles__, "all", "all", __AttributeMarker.Bindings__, "all"] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵelement(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "div", 3); + i0.ɵɵtemplate(4, MyComponent_div_4_Template, 1, 0, "div", 4); + i0.ɵɵelement(5, "div", 5); + i0.ɵɵtemplate(6, MyComponent_div_6_Template, 1, 1, "div", 6); + } + if (rf & 2) { + i0.ɵɵadvance(3); + i0.ɵɵproperty("tabindex", ctx.tabIndex); + i0.ɵɵadvance(1); + i0.ɵɵproperty("ngIf", ctx.cond); + i0.ɵɵadvance(2); + i0.ɵɵproperty("all", ctx.all); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/shared_name_with_consts_template.pipeline.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/shared_name_with_consts_template.pipeline.js new file mode 100644 index 0000000000000..876fe77eac405 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/class_bindings/shared_name_with_consts_template.pipeline.js @@ -0,0 +1,32 @@ +consts: () => { + __i18nMsg__('label', [], {}, {}) + return [ + ["attr", "", __AttributeMarker.Classes__, "attr"], + ["ngProjectAs", "selector", __AttributeMarker.ProjectAs__, ["selector"], __AttributeMarker.Classes__, "selector"], + [__AttributeMarker.Classes__, "width", __AttributeMarker.Styles__, "width", "0px"], + [__AttributeMarker.Classes__, "tabindex", __AttributeMarker.Bindings__, "tabindex"], + ["class", "ngIf", __AttributeMarker.Template__, "ngIf"], + ["aria-label", i18n_0, __AttributeMarker.Classes__, "aria-label"], + // NOTE: We tolerate a slight difference -- we emit `all` as a Template binding + ["all", "", "ngProjectAs", "all", "style", "all:all", "class", "all", __AttributeMarker.ProjectAs__, ["all"], __AttributeMarker.Bindings__, "all", __AttributeMarker.Template__, "all"], + [__AttributeMarker.Classes__, "ngIf"], + ["all", "", "ngProjectAs", "all", __AttributeMarker.ProjectAs__, ["all"], __AttributeMarker.Classes__, "all", __AttributeMarker.Styles__, "all", "all", __AttributeMarker.Bindings__, "all"] + ]; + }, + template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵelement(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "div", 3); + i0.ɵɵtemplate(4, MyComponent_div_4_Template, 1, 0, "div", 4); + i0.ɵɵelement(5, "div", 5); + i0.ɵɵtemplate(6, MyComponent_div_6_Template, 1, 1, "div", 6); + } + if (rf & 2) { + i0.ɵɵadvance(3); + i0.ɵɵproperty("tabindex", ctx.tabIndex); + i0.ɵɵadvance(1); + i0.ɵɵproperty("ngIf", ctx.cond); + i0.ɵɵadvance(2); + i0.ɵɵproperty("all", ctx.all); + } + } + \ No newline at end of file diff --git a/packages/compiler/src/template/pipeline/src/phases/const_collection.ts b/packages/compiler/src/template/pipeline/src/phases/const_collection.ts index 0e82c6a2b6788..b6f700f0d5815 100644 --- a/packages/compiler/src/template/pipeline/src/phases/const_collection.ts +++ b/packages/compiler/src/template/pipeline/src/phases/const_collection.ts @@ -73,7 +73,7 @@ const FLYWEIGHT_ARRAY: ReadonlyArray = Object.freeze(); + private known = new Map>(); private byKind = new Map; projectAs: string|null = null; @@ -102,12 +102,22 @@ class ElementAttributes { return this.byKind.get(ir.BindingKind.I18n) ?? FLYWEIGHT_ARRAY; } + isKnown(kind: ir.BindingKind, name: string, value: o.Expression|null) { + const nameToValue = this.known.get(kind) ?? new Set(); + this.known.set(kind, nameToValue); + if (nameToValue.has(name)) { + return true; + } + nameToValue.add(name); + return false; + } + add(kind: ir.BindingKind, name: string, value: o.Expression|null, trustedValueFn: o.Expression|null): void { - if (this.known.has(name)) { + if (this.isKnown(kind, name, value)) { return; } - this.known.add(name); + // TODO: Can this be its own phase if (name === 'ngProjectAs') { if (value === null || !(value instanceof o.LiteralExpr) || (value.value == null) || diff --git a/packages/compiler/src/template/pipeline/src/phases/parse_extracted_styles.ts b/packages/compiler/src/template/pipeline/src/phases/parse_extracted_styles.ts index 6e385faea8710..4695046e0d4cf 100644 --- a/packages/compiler/src/template/pipeline/src/phases/parse_extracted_styles.ts +++ b/packages/compiler/src/template/pipeline/src/phases/parse_extracted_styles.ts @@ -18,10 +18,32 @@ import type {CompilationJob} from '../compilation'; * class property. */ export function parseExtractedStyles(job: CompilationJob) { + const elements = new Map(); + + for (const unit of job.units) { + for (const op of unit.create) { + if (ir.isElementOrContainerOp(op)) { + elements.set(op.xref, op); + } + } + } + for (const unit of job.units) { for (const op of unit.create) { if (op.kind === ir.OpKind.ExtractedAttribute && op.bindingKind === ir.BindingKind.Attribute && ir.isStringLiteral(op.expression!)) { + const target = elements.get(op.target)!; + + if (target !== undefined && target.kind === ir.OpKind.Template && + target.templateKind === ir.TemplateKind.Structural) { + // TemplateDefinitionBuilder will not apply class and style bindings to structural + // directives; instead, it will leave them as attributes. + // (It's not clear what that would mean, anyway -- classes and styles on a structural + // element should probably be a parse error.) + // TODO: We may be able to remove this once Template Pipeline is the default. + continue; + } + if (op.name === 'style') { const parsedStyles = parseStyle(op.expression.value); for (let i = 0; i < parsedStyles.length - 1; i += 2) { From c9879457f628d593680e82d500925fb57aa114b9 Mon Sep 17 00:00:00 2001 From: Dylan Hunn Date: Thu, 14 Dec 2023 21:38:52 -0800 Subject: [PATCH 08/27] refactor(compiler): Not all attribute values beginning with a colon are namespaced (#53574) TemplateDefinitionBuilder is apparently more careful about when it attempts to split namespaces in attribute values. However, we are doing this on style attributes, which might start with a single `:`. Rather than refactor our logic to only try to split namespaces in some cases, we can just add an option to make namespace splitting fail gracefully. We only use this option for attributes, not elements. Note also: the compiled code for this, while "correct" is absolutely insane. Maybe we should consider fixing this, as a matter of principle. PR Close #53574 --- .../style_bindings/GOLDEN_PARTIAL.js | 31 +++++++++++++++++++ .../style_bindings/TEST_CASES.json | 10 ++++++ .../style_bindings/colon_style.js | 8 +++++ .../style_bindings/colon_style.ts | 11 +++++++ packages/compiler/src/ml_parser/tags.ts | 8 +++-- .../pipeline/src/phases/const_collection.ts | 2 +- 6 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/colon_style.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/colon_style.ts diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/GOLDEN_PARTIAL.js index bf3b80cac4263..176417a9be8ec 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/GOLDEN_PARTIAL.js @@ -323,3 +323,34 @@ export declare class MyModule { static ɵinj: i0.ɵɵInjectorDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: colon_style.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, isStandalone: true, selector: "my-component", ngImport: i0, template: ` +
+ `, isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, decorators: [{ + type: Component, + args: [{ + selector: 'my-component', + standalone: true, + template: ` +
+ ` + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: colon_style.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/TEST_CASES.json index b59b4c2739813..d20129bc03d4a 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/TEST_CASES.json @@ -60,6 +60,16 @@ "files": ["empty_style_bindings.js"] } ] + }, + { + "description": "should support style bindings with a colon at the start of the applied style", + "inputFiles": ["colon_style.ts"], + "expectations": [ + { + "failureMessage": "Incorrect template", + "files": ["colon_style.js"] + } + ] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/colon_style.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/colon_style.js new file mode 100644 index 0000000000000..9e495f2008633 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/colon_style.js @@ -0,0 +1,8 @@ +// NOTE: This is the way TemplateDefinitionBuilder behaves today, but it's crazy!! +// NOTE: TODO: Should we fix it? +consts: [[__AttributeMarker.Styles__, ":root {color", "red"]], +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵelement(0, "div", 0); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/colon_style.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/colon_style.ts new file mode 100644 index 0000000000000..3171558a85152 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/colon_style.ts @@ -0,0 +1,11 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'my-component', + standalone: true, + template: ` +
+ ` +}) +export class MyComponent { +} diff --git a/packages/compiler/src/ml_parser/tags.ts b/packages/compiler/src/ml_parser/tags.ts index af58e730920ae..ea6c0f443e235 100644 --- a/packages/compiler/src/ml_parser/tags.ts +++ b/packages/compiler/src/ml_parser/tags.ts @@ -24,7 +24,7 @@ export interface TagDefinition { getContentType(prefix?: string): TagContentType; } -export function splitNsName(elementName: string): [string|null, string] { +export function splitNsName(elementName: string, fatal: boolean = true): [string|null, string] { if (elementName[0] != ':') { return [null, elementName]; } @@ -32,7 +32,11 @@ export function splitNsName(elementName: string): [string|null, string] { const colonIndex = elementName.indexOf(':', 1); if (colonIndex === -1) { - throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`); + if (fatal) { + throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`); + } else { + return [null, elementName]; + } } return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)]; diff --git a/packages/compiler/src/template/pipeline/src/phases/const_collection.ts b/packages/compiler/src/template/pipeline/src/phases/const_collection.ts index b6f700f0d5815..193579e858c3e 100644 --- a/packages/compiler/src/template/pipeline/src/phases/const_collection.ts +++ b/packages/compiler/src/template/pipeline/src/phases/const_collection.ts @@ -161,7 +161,7 @@ class ElementAttributes { * Gets an array of literal expressions representing the attribute's namespaced name. */ function getAttributeNameLiterals(name: string): o.LiteralExpr[] { - const [attributeNamespace, attributeName] = splitNsName(name); + const [attributeNamespace, attributeName] = splitNsName(name, false); const nameLiteral = o.literal(attributeName); if (attributeNamespace) { From 7bb312fcf612c57d6606ae14f21707f10d8ff9ce Mon Sep 17 00:00:00 2001 From: Jessica Janiuk Date: Thu, 14 Dec 2023 15:58:59 -0500 Subject: [PATCH 09/27] fix(migrations): cf migration - stop removing empty newlines from i18n blocks (#53578) Since i18n sections are whitespace sensitive, we should avoid removing empty lines when inside an i18n block that were present before. PR Close #53578 --- .../core/schematics/ng-generate/control-flow-migration/util.ts | 2 +- packages/core/schematics/test/control_flow_migration_spec.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/schematics/ng-generate/control-flow-migration/util.ts b/packages/core/schematics/ng-generate/control-flow-migration/util.ts index 48b52d149fac6..3302c0a81b9d9 100644 --- a/packages/core/schematics/ng-generate/control-flow-migration/util.ts +++ b/packages/core/schematics/ng-generate/control-flow-migration/util.ts @@ -628,7 +628,7 @@ export function formatTemplate(tmpl: string, templateType: string): string { lineWasMigrated = true; } if ((line.trim() === '' && index !== 0 && index !== lines.length - 1) && - (inMigratedBlock || lineWasMigrated)) { + (inMigratedBlock || lineWasMigrated) && !inI18nBlock) { // skip blank lines except if it's the first line or last line // this preserves leading and trailing spaces if they are already present continue; diff --git a/packages/core/schematics/test/control_flow_migration_spec.ts b/packages/core/schematics/test/control_flow_migration_spec.ts index cb4f5319110af..51be174024439 100644 --- a/packages/core/schematics/test/control_flow_migration_spec.ts +++ b/packages/core/schematics/test/control_flow_migration_spec.ts @@ -4729,6 +4729,7 @@ describe('control flow migration', () => { `

`, `Content here`, ``, + ` `, `

Else Content

`, `
`, ``, @@ -4745,6 +4746,7 @@ describe('control flow migration', () => { ` Content here`, ` } @else {`, ` `, + ` `, `

Else Content

`, `
`, ` }`, From 18929040704828bf4caf76797ab141ca101c4b5f Mon Sep 17 00:00:00 2001 From: Jessica Janiuk Date: Thu, 14 Dec 2023 13:58:09 -0500 Subject: [PATCH 10/27] refactor(migrations): update cf migration to handle module imports better (#53575) This addresses the case where modules are being used and declared in the same file as the component. It is unclear whether its safe to remove the common module in this case, so best to leave it. PR Close #53575 --- .../identifier-lookup.ts | 22 +++++++ .../control-flow-migration/migration.ts | 17 +++-- .../control-flow-migration/types.ts | 60 +++++++++++++---- .../control-flow-migration/util.ts | 42 +++++++----- .../test/control_flow_migration_spec.ts | 66 +++++++++++++++++++ 5 files changed, 175 insertions(+), 32 deletions(-) create mode 100644 packages/core/schematics/ng-generate/control-flow-migration/identifier-lookup.ts diff --git a/packages/core/schematics/ng-generate/control-flow-migration/identifier-lookup.ts b/packages/core/schematics/ng-generate/control-flow-migration/identifier-lookup.ts new file mode 100644 index 0000000000000..b3f84bd2af93c --- /dev/null +++ b/packages/core/schematics/ng-generate/control-flow-migration/identifier-lookup.ts @@ -0,0 +1,22 @@ +/** + * @license + * Copyright Google LLC 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 + */ + +import ts from 'typescript'; + +export function lookupIdentifiersInSourceFile( + sourceFile: ts.SourceFile, name: string): Set { + const results = new Set(); + const visit = (node: ts.Node): void => { + if (ts.isIdentifier(node) && node.text === name) { + results.add(node); + } + ts.forEachChild(node, visit); + }; + visit(sourceFile); + return results; +} diff --git a/packages/core/schematics/ng-generate/control-flow-migration/migration.ts b/packages/core/schematics/ng-generate/control-flow-migration/migration.ts index a60b0192a7d7e..1d2d1790a8dc8 100644 --- a/packages/core/schematics/ng-generate/control-flow-migration/migration.ts +++ b/packages/core/schematics/ng-generate/control-flow-migration/migration.ts @@ -47,7 +47,7 @@ export function migrateTemplate( const parsingError = { type: 'parse', error: new Error( - `The migration resulted in invalid HTML for ${file.sourceFilePath}. ` + + `The migration resulted in invalid HTML for ${file.sourceFile.fileName}. ` + `Please check the template for valid HTML structures and run the migration again.`) }; return {migrated: template, errors: [parsingError]}; @@ -68,10 +68,19 @@ export function migrateTemplate( // whether it's safe to remove the CommonModule to the // original component class source file if (templateType === 'templateUrl' && analyzedFiles !== null && - analyzedFiles.has(file.sourceFilePath)) { - const componentFile = analyzedFiles.get(file.sourceFilePath)!; + analyzedFiles.has(file.sourceFile.fileName)) { + const componentFile = analyzedFiles.get(file.sourceFile.fileName)!; + componentFile.getSortedRanges(); + // we have already checked the template file to see if it is safe to remove the imports + // and common module. This check is passed off to the associated .ts file here so + // the class knows whether it's safe to remove from the template side. componentFile.removeCommonModule = file.removeCommonModule; componentFile.canRemoveImports = file.canRemoveImports; + + // At this point, we need to verify the component class file doesn't have any other imports + // that prevent safe removal of common module. It could be that there's an associated ngmodule + // and in that case we can't safely remove the common module import. + componentFile.verifyCanRemoveImports(); } errors = [ @@ -81,7 +90,7 @@ export function migrateTemplate( ...caseResult.errors, ]; } else if (file.canRemoveImports) { - migrated = removeImports(template, node, file.removeCommonModule); + migrated = removeImports(template, node, file); } return {migrated, errors}; diff --git a/packages/core/schematics/ng-generate/control-flow-migration/types.ts b/packages/core/schematics/ng-generate/control-flow-migration/types.ts index 428647c83254d..d7ab008e2348f 100644 --- a/packages/core/schematics/ng-generate/control-flow-migration/types.ts +++ b/packages/core/schematics/ng-generate/control-flow-migration/types.ts @@ -9,6 +9,8 @@ import {Attribute, Element, ParseTreeResult, RecursiveVisitor, Text} from '@angular/compiler'; import ts from 'typescript'; +import {lookupIdentifiersInSourceFile} from './identifier-lookup'; + export const ngtemplate = 'ng-template'; export const boundngifelse = '[ngIfElse]'; export const boundngifthenelse = '[ngIfThenElse]'; @@ -71,7 +73,7 @@ const commonModulePipes = [ */ type Range = { start: number, - end?: number, node: ts.Node, type: string, + end?: number, node: ts.Node, type: string, remove: boolean, }; export type Offsets = { @@ -252,18 +254,25 @@ export class AnalyzedFile { private ranges: Range[] = []; removeCommonModule = false; canRemoveImports = false; - sourceFilePath: string = ''; + sourceFile: ts.SourceFile; + importRanges: Range[] = []; + templateRanges: Range[] = []; + + constructor(sourceFile: ts.SourceFile) { + this.sourceFile = sourceFile; + } /** Returns the ranges in the order in which they should be migrated. */ getSortedRanges(): Range[] { // templates first for checking on whether certain imports can be safely removed - const templateRanges = this.ranges.slice() - .filter(x => x.type !== 'import') - .sort((aStart, bStart) => bStart.start - aStart.start); - const importRanges = this.ranges.slice() - .filter(x => x.type === 'import') - .sort((aStart, bStart) => bStart.start - aStart.start); - return [...templateRanges, ...importRanges]; + this.templateRanges = this.ranges.slice() + .filter(x => x.type === 'template' || x.type === 'templateUrl') + .sort((aStart, bStart) => bStart.start - aStart.start); + this.importRanges = + this.ranges.slice() + .filter(x => x.type === 'importDecorator' || x.type === 'importDeclaration') + .sort((aStart, bStart) => bStart.start - aStart.start); + return [...this.templateRanges, ...this.importRanges]; } /** @@ -273,13 +282,12 @@ export class AnalyzedFile { * @param range Range to be added. */ static addRange( - path: string, sourceFilePath: string, analyzedFiles: Map, + path: string, sourceFile: ts.SourceFile, analyzedFiles: Map, range: Range): void { let analysis = analyzedFiles.get(path); if (!analysis) { - analysis = new AnalyzedFile(); - analysis.sourceFilePath = sourceFilePath; + analysis = new AnalyzedFile(sourceFile); analyzedFiles.set(path, analysis); } @@ -290,6 +298,34 @@ export class AnalyzedFile { analysis.ranges.push(range); } } + + /** + * This verifies whether a component class is safe to remove module imports. + * It is only run on .ts files. + */ + verifyCanRemoveImports() { + // if we already know it's not safe to remove the common module import + // skip this check entirely + if (this.removeCommonModule) { + const importDeclaration = this.importRanges.find(r => r.type === 'importDeclaration'); + const instances = lookupIdentifiersInSourceFile(this.sourceFile, 'CommonModule'); + let foundImportDeclaration = false; + let count = 0; + for (let range of this.importRanges) { + for (let instance of instances) { + if (instance.getStart() >= range.start && instance.getEnd() <= range.end!) { + if (range === importDeclaration) { + foundImportDeclaration = true; + } + count++; + } + } + } + if (instances.size !== count && importDeclaration !== undefined && foundImportDeclaration) { + importDeclaration.remove = false; + } + } + } } /** Finds all non-control flow elements from common module. */ diff --git a/packages/core/schematics/ng-generate/control-flow-migration/util.ts b/packages/core/schematics/ng-generate/control-flow-migration/util.ts index 3302c0a81b9d9..71550111bb103 100644 --- a/packages/core/schematics/ng-generate/control-flow-migration/util.ts +++ b/packages/core/schematics/ng-generate/control-flow-migration/util.ts @@ -38,13 +38,18 @@ export function analyze(sourceFile: ts.SourceFile, analyzedFiles: Map r.type === 'importDeclaration'); + if (range === undefined || !range.remove) { + return false; + } + // should change if you can remove the common module // if it's not safe to remove the common module // and that's the only thing there, we should do nothing. const clause = decl.getChildAt(1) as ts.ImportClause; return !( - !removeCommonModule && clause.namedBindings && ts.isNamedImports(clause.namedBindings) && + !file.removeCommonModule && clause.namedBindings && ts.isNamedImports(clause.namedBindings) && clause.namedBindings.elements.length === 1 && clause.namedBindings.elements[0].getText() === 'CommonModule'); } @@ -109,9 +114,13 @@ function analyzeImportDeclarations( const elements = clause.namedBindings.elements.filter(el => importWithCommonRemovals.includes(el.getText())); if (elements.length > 0) { - AnalyzedFile.addRange( - sourceFile.fileName, sourceFile.fileName, analyzedFiles, - {start: node.getStart(), end: node.getEnd(), node, type: 'import'}); + AnalyzedFile.addRange(sourceFile.fileName, sourceFile, analyzedFiles, { + start: node.getStart(), + end: node.getEnd(), + node, + type: 'importDeclaration', + remove: true + }); } } } @@ -148,20 +157,22 @@ function analyzeDecorators( switch (prop.name.text) { case 'template': // +1/-1 to exclude the opening/closing characters from the range. - AnalyzedFile.addRange(sourceFile.fileName, sourceFile.fileName, analyzedFiles, { + AnalyzedFile.addRange(sourceFile.fileName, sourceFile, analyzedFiles, { start: prop.initializer.getStart() + 1, end: prop.initializer.getEnd() - 1, node: prop, - type: 'template' + type: 'template', + remove: true, }); break; case 'imports': - AnalyzedFile.addRange(sourceFile.fileName, sourceFile.fileName, analyzedFiles, { + AnalyzedFile.addRange(sourceFile.fileName, sourceFile, analyzedFiles, { start: prop.name.getStart(), end: prop.initializer.getEnd(), node: prop, - type: 'import' + type: 'importDecorator', + remove: true, }); break; @@ -170,8 +181,8 @@ function analyzeDecorators( if (ts.isStringLiteralLike(prop.initializer)) { const path = join(dirname(sourceFile.fileName), prop.initializer.text); AnalyzedFile.addRange( - path, sourceFile.fileName, analyzedFiles, - {start: 0, node: prop, type: 'templateUrl'}); + path, sourceFile, analyzedFiles, + {start: 0, node: prop, type: 'templateUrl', remove: true}); } break; } @@ -401,13 +412,12 @@ export function canRemoveCommonModule(template: string): boolean { /** * removes imports from template imports and import declarations */ -export function removeImports( - template: string, node: ts.Node, removeCommonModule: boolean): string { +export function removeImports(template: string, node: ts.Node, file: AnalyzedFile): string { if (template.startsWith('imports') && ts.isPropertyAssignment(node)) { - const updatedImport = updateClassImports(node, removeCommonModule); + const updatedImport = updateClassImports(node, file.removeCommonModule); return updatedImport ?? template; - } else if (ts.isImportDeclaration(node) && checkIfShouldChange(node, removeCommonModule)) { - return updateImportDeclaration(node, removeCommonModule); + } else if (ts.isImportDeclaration(node) && checkIfShouldChange(node, file)) { + return updateImportDeclaration(node, file.removeCommonModule); } return template; } diff --git a/packages/core/schematics/test/control_flow_migration_spec.ts b/packages/core/schematics/test/control_flow_migration_spec.ts index 51be174024439..069b8a224d401 100644 --- a/packages/core/schematics/test/control_flow_migration_spec.ts +++ b/packages/core/schematics/test/control_flow_migration_spec.ts @@ -5021,6 +5021,72 @@ describe('control flow migration', () => { expect(actual).toBe(expected); }); + + it('should not remove common module when more common module symbols are found', async () => { + writeFile('/comp.ts', [ + `import {Component, NgModule} from '@angular/core';`, + `import {CommonModule} from '@angular/common';\n`, + `@Component({`, + ` selector: 'example-cmp',`, + ` templateUrl: './comp.html',`, + `})`, + `export class ExampleCmp {`, + `}`, + `@Component({`, + ` standalone: true`, + ` selector: 'example2-cmp',`, + ` imports: [CommonModule],`, + ` templateUrl: './comp.html',`, + `})`, + `export class Example2Cmp {`, + `}`, + `const NG_MODULE_IMPORTS = [CommonModule, OtherModule];`, + ``, + `@NgModule({`, + ` declarations: [ExampleCmp],`, + ` exports: [ExampleCmp],`, + ` imports: [NG_MODULE_IMPORTS],`, + `})`, + `export class ExampleModule {}`, + ].join('\n')); + + writeFile('/comp.html', [ + `
`, + `Content here`, + `
`, + ].join('\n')); + + await runMigration(); + const actual = tree.readContent('/comp.ts'); + const expected = [ + `import {Component, NgModule} from '@angular/core';`, + `import {CommonModule} from '@angular/common';\n`, + `@Component({`, + ` selector: 'example-cmp',`, + ` templateUrl: './comp.html',`, + `})`, + `export class ExampleCmp {`, + `}`, + `@Component({`, + ` standalone: true`, + ` selector: 'example2-cmp',`, + ` imports: [],`, + ` templateUrl: './comp.html',`, + `})`, + `export class Example2Cmp {`, + `}`, + `const NG_MODULE_IMPORTS = [CommonModule, OtherModule];`, + ``, + `@NgModule({`, + ` declarations: [ExampleCmp],`, + ` exports: [ExampleCmp],`, + ` imports: [NG_MODULE_IMPORTS],`, + `})`, + `export class ExampleModule {}`, + ].join('\n'); + + expect(actual).toBe(expected); + }); }); describe('no migration needed', () => { From 13e89bcbd7ae9116c5ca0f402268243c777d5b69 Mon Sep 17 00:00:00 2001 From: Angular Robot Date: Sat, 16 Dec 2023 21:07:56 +0000 Subject: [PATCH 11/27] build: update scorecard action dependencies (#53607) See associated pull request for more information. PR Close #53607 --- .github/workflows/scorecard.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 17b6c37d9cba2..df88bce447782 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -30,7 +30,7 @@ jobs: persist-credentials: false - name: 'Run analysis' - uses: ossf/scorecard-action@483ef80eb98fb506c348f7d62e28055e49fe2398 # v2.3.0 + uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 with: results_file: results.sarif results_format: sarif @@ -39,7 +39,7 @@ jobs: # Upload the results as artifacts. - name: 'Upload artifact' - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0 with: name: SARIF file path: results.sarif @@ -47,6 +47,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@0116bc2df50751f9724a2e35ef1f24d22f90e4e1 # v2.22.3 + uses: github/codeql-action/upload-sarif@b374143c1149a9115d881581d29b8390bbcbb59c # v3.22.11 with: sarif_file: results.sarif From e52a6cf949b066a1877f98f2cf113894204a4ec2 Mon Sep 17 00:00:00 2001 From: Angular Robot Date: Fri, 15 Dec 2023 19:49:23 +0000 Subject: [PATCH 12/27] docs: update Angular CLI help [main] (#53590) Updated Angular CLI help contents. PR Close #53590 --- aio/content/cli/help/add.json | 3 +++ aio/content/cli/help/build-info.json | 2 +- aio/content/cli/help/build.json | 2 +- aio/content/cli/help/generate.json | 3 +++ aio/content/cli/help/new.json | 3 +++ 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/aio/content/cli/help/add.json b/aio/content/cli/help/add.json index e31e3510462aa..d707946ea671d 100644 --- a/aio/content/cli/help/add.json +++ b/aio/content/cli/help/add.json @@ -22,6 +22,9 @@ { "name": "dry-run", "type": "boolean", + "aliases": [ + "d" + ], "default": false, "description": "Run through and reports activity without writing out results." }, diff --git a/aio/content/cli/help/build-info.json b/aio/content/cli/help/build-info.json index a491df6fc8846..d3b1fa76b81d7 100644 --- a/aio/content/cli/help/build-info.json +++ b/aio/content/cli/help/build-info.json @@ -1,4 +1,4 @@ { "branchName": "refs/heads/main", - "sha": "8c844e03ebe99ac0fda6d1e2fa8d57d6031f1562" + "sha": "9ddd673f70f415f59d953fd72f05ebd32f92a60b" } \ No newline at end of file diff --git a/aio/content/cli/help/build.json b/aio/content/cli/help/build.json index 9f8effcf8af1f..5264217341f11 100644 --- a/aio/content/cli/help/build.json +++ b/aio/content/cli/help/build.json @@ -148,7 +148,7 @@ { "name": "output-path", "type": "string", - "description": "The full path for the new output directory, relative to the current workspace." + "description": "Specify the output path relative to workspace root." }, { "name": "poll", diff --git a/aio/content/cli/help/generate.json b/aio/content/cli/help/generate.json index bbff4a7279a1f..a6d62df446341 100644 --- a/aio/content/cli/help/generate.json +++ b/aio/content/cli/help/generate.json @@ -16,6 +16,9 @@ { "name": "dry-run", "type": "boolean", + "aliases": [ + "d" + ], "default": false, "description": "Run through and reports activity without writing out results." }, diff --git a/aio/content/cli/help/new.json b/aio/content/cli/help/new.json index 523a4e971af99..fc72d886550a5 100644 --- a/aio/content/cli/help/new.json +++ b/aio/content/cli/help/new.json @@ -43,6 +43,9 @@ { "name": "dry-run", "type": "boolean", + "aliases": [ + "d" + ], "default": false, "description": "Run through and reports activity without writing out results." }, From 1b659314287b4aff70dd521c9b606ccec4fd49f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johnny=20G=C3=A9rard?= Date: Fri, 15 Dec 2023 03:56:38 +0100 Subject: [PATCH 13/27] docs: clarify Node.js version (#53581) Resolve #53334 PR Close #53581 --- docs/DEVELOPER.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/DEVELOPER.md b/docs/DEVELOPER.md index d9c81bab657e9..6a455869e7f98 100644 --- a/docs/DEVELOPER.md +++ b/docs/DEVELOPER.md @@ -26,8 +26,9 @@ following products on your development machine: **Windows Users**: Git Bash or an equivalent shell is required\ *Windows Powershell and cmd shells are not supported [#46780](https://github.com/angular/angular/issues/46780) so some commands might fail* -* [Node.js](https://nodejs.org), (version specified in the engines field of [`package.json`](../package.json)) which is used to run a development web server, - run tests, and generate distributable files. +* [Node.js](https://nodejs.org), (version specified in [`.nvmrc`](../.nvmrc)) which is used to run a development web server, + run tests, and generate distributable files. + `.nvmrc` is read by [nvm](https://github.com/nvm-sh/nvm) commands like `nvm install` and `nvm use`. * [Yarn](https://yarnpkg.com) (version specified in the engines field of [`package.json`](../package.json)) which is used to install dependencies. From d4973ff9b074f4db918f71163e79b7d112c309f5 Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Sun, 26 Mar 2023 04:29:58 +0000 Subject: [PATCH 14/27] fix(zone.js): support addEventListener with signal option. (#49595) Close #49591 ``` const ac = new AbortController(); addEventListener(eventName, handler, {signal: ac.signal);` ac.abort(); ``` Currently `zone.js` doesn't support the `signal` option, this PR allows the user to use AbortContoller to remove the event listener. PR Close #49595 --- packages/zone.js/lib/common/events.ts | 37 ++++++ packages/zone.js/lib/common/fetch.ts | 20 +--- packages/zone.js/test/browser/browser.spec.ts | 107 ++++++++++++++++++ 3 files changed, 146 insertions(+), 18 deletions(-) diff --git a/packages/zone.js/lib/common/events.ts b/packages/zone.js/lib/common/events.ts index 58f60f77ed115..4633e7318b0b6 100644 --- a/packages/zone.js/lib/common/events.ts +++ b/packages/zone.js/lib/common/events.ts @@ -293,6 +293,16 @@ export function patchEventTarget( // if task is not marked as isRemoved, this call is directly // from Zone.prototype.cancelTask, we should remove the task // from tasksList of target first + const signal = task?.options?.signal; + if (typeof signal === 'object' && signal?.tasks) { + const abortTasks = signal.tasks; + for (let i = 0; i < abortTasks?.length || 0; i++) { + if (abortTasks[i] === task) { + abortTasks.splice(i, 1); + break; + } + } + } if (!task.isRemoved) { const symbolEventNames = zoneSymbolEventNames[task.eventName]; let symbolEventName; @@ -394,6 +404,11 @@ export function patchEventTarget( const passive = passiveSupported && !!passiveEvents && passiveEvents.indexOf(eventName) !== -1; const options = buildEventListenerOptions(arguments[2], passive); + const signal = typeof options === 'object' && options?.signal; + if (typeof signal === 'object' && signal?.aborted) { + // the signal is an aborted one, just return without attaching the event listener. + return; + } if (unpatchedEvents) { // check unpatched list @@ -465,9 +480,31 @@ export function patchEventTarget( (data as any).taskData = taskData; } + if (signal && typeof signal === 'object') { + // if addEventListener with signal options, we don't pass it to + // native addEventListener, instead we keep the signal setting + // and handle ourselves. + taskData.options.signal = undefined; + } const task: any = zone.scheduleEventTask(source, delegate, data, customScheduleFn, customCancelFn); + if (signal && typeof signal === 'object') { + // after task is scheduled, we need to store the signal back to task.options + taskData.options.signal = signal; + const tasks = signal.tasks || []; + tasks.push(task); + signal.tasks = tasks; + if (!signal[Zone.__symbol__('abortListener')]) { + signal[Zone.__symbol__('abortListener')] = true; + nativeListener.call(signal, 'abort', function() { + const sTasks = signal.tasks.slice(); + sTasks.forEach((task: Task) => task.zone.cancelTask(task)); + signal.tasks.length = 0; + }); + } + } + // should clear taskData.target to avoid memory leak // issue, https://github.com/angular/angular/issues/20442 taskData.target = null; diff --git a/packages/zone.js/lib/common/fetch.ts b/packages/zone.js/lib/common/fetch.ts index 9c2c064d8a13e..b7ebbc9afcc9e 100644 --- a/packages/zone.js/lib/common/fetch.ts +++ b/packages/zone.js/lib/common/fetch.ts @@ -29,23 +29,7 @@ Zone.__load_patch('fetch', (global: any, Zone: ZoneType, api: _ZonePrivate) => { const fetchTaskAborting = api.symbol('fetchTaskAborting'); const OriginalAbortController = global['AbortController']; const supportAbort = typeof OriginalAbortController === 'function'; - let abortNative: Function|null = null; - if (supportAbort) { - global['AbortController'] = function() { - const abortController = new OriginalAbortController(); - const signal = abortController.signal; - signal.abortController = abortController; - return abortController; - }; - abortNative = api.patchMethod( - OriginalAbortController.prototype, 'abort', - (delegate: Function) => (self: any, args: any) => { - if (self.task) { - return self.task.zone.cancelTask(self.task); - } - return delegate.apply(self, args); - }); - } + let abortNative: Function|null = OriginalAbortController?.prototype[api.symbol('abort')]; const placeholder = function() {}; global['fetch'] = function() { const args = Array.prototype.slice.call(arguments); @@ -105,7 +89,7 @@ Zone.__load_patch('fetch', (global: any, Zone: ZoneType, api: _ZonePrivate) => { } }); if (signal && signal.abortController) { - signal.abortController.task = task; + signal.abortController.tasks = [task]; } }); }; diff --git a/packages/zone.js/test/browser/browser.spec.ts b/packages/zone.js/test/browser/browser.spec.ts index 39c60e9229ae7..bd35c17e33ae2 100644 --- a/packages/zone.js/test/browser/browser.spec.ts +++ b/packages/zone.js/test/browser/browser.spec.ts @@ -1762,6 +1762,113 @@ describe('Zone', function() { expect(logs).toEqual(['click2']); }); + it('should support remove event listeners via AbortController', function() { + let logs: string[] = []; + const ac = new AbortController(); + + button.addEventListener('click', function() { + logs.push('click1'); + }, {signal: ac.signal}); + button.addEventListener('click', function() { + logs.push('click2'); + }); + button.addEventListener('click', function() { + logs.push('click3'); + }, {signal: ac.signal}); + let listeners = button.eventListeners!('click'); + expect(listeners.length).toBe(3); + + button.dispatchEvent(clickEvent); + expect(logs.length).toBe(3); + expect(logs).toEqual(['click1', 'click2', 'click3']); + ac.abort(); + logs = []; + + listeners = button.eventListeners!('click'); + button.dispatchEvent(clickEvent); + expect(logs.length).toBe(1); + expect(listeners.length).toBe(1); + expect(logs).toEqual(['click2']); + }); + + it('should support remove event listeners with AbortController', function() { + let logs: string[] = []; + const ac = new AbortController(); + + const listener1 = function() { + logs.push('click1'); + }; + button.addEventListener('click', listener1, {signal: ac.signal}); + button.addEventListener('click', function() { + logs.push('click2'); + }); + let listeners = button.eventListeners!('click'); + expect(listeners.length).toBe(2); + + button.dispatchEvent(clickEvent); + expect(logs.length).toBe(2); + expect(logs).toEqual(['click1', 'click2']); + + button.removeEventListener('click', listener1); + listeners = button.eventListeners!('click'); + expect(listeners.length).toBe(1); + + logs = []; + + listeners = button.eventListeners!('click'); + button.dispatchEvent(clickEvent); + expect(logs.length).toBe(1); + expect(listeners.length).toBe(1); + expect(logs).toEqual(['click2']); + + ac.abort(); + expect(logs).toEqual(['click2']); + }); + + it('should not add event listeners with aborted signal', function() { + let logs: string[] = []; + + button.addEventListener('click', function() { + logs.push('click1'); + }, {signal: AbortSignal.abort()}); + button.addEventListener('click', function() { + logs.push('click2'); + }); + let listeners = button.eventListeners!('click'); + expect(listeners.length).toBe(1); + + button.dispatchEvent(clickEvent); + expect(logs.length).toBe(1); + expect(logs).toEqual(['click2']); + }); + + it('should remove event listeners with timeout signal', + ifEnvSupportsWithDone( + () => typeof AbortSignal.timeout === 'function', function(done: DoneFn) { + let logs: string[] = []; + + button.addEventListener('click', function() { + logs.push('click1'); + }, {signal: AbortSignal.timeout(1)}); + button.addEventListener('click', function() { + logs.push('click2'); + }); + let listeners = button.eventListeners!('click'); + expect(listeners.length).toBe(2); + + button.dispatchEvent(clickEvent); + expect(logs.length).toBe(2); + expect(logs).toEqual(['click1', 'click2']); + + setTimeout(() => { + logs = []; + button.dispatchEvent(clickEvent); + expect(logs.length).toBe(1); + expect(logs).toEqual(['click2']); + done(); + }, 10); + })); + it('should support reschedule eventTask', ifEnvSupports(supportEventListenerOptions, function() { let hookSpy1 = jasmine.createSpy('spy1'); From b06b24b5049c07fbc18c76fd2a10e49fc93870be Mon Sep 17 00:00:00 2001 From: JiaLiPassion Date: Wed, 13 Dec 2023 02:45:01 +0000 Subject: [PATCH 15/27] fix(zone.js): handle fetch with AbortSignal (#49595) fetch support AbortSignal, zone.js schedules a macroTask when fetch() ``` fetch(..., {signal: abortSignal}); ``` we should also be able to cancel fetch with `zoneTask.cancel` call. So this commit create an internal AbortSignal to handle `zoneTask.cancel()` call and also delegate the `options.signal` from the user code. PR Close #49595 --- packages/zone.js/lib/common/events.ts | 35 +++----- packages/zone.js/lib/common/fetch.ts | 36 +++----- packages/zone.js/test/browser/browser.spec.ts | 85 ++++++++++++++++++- 3 files changed, 106 insertions(+), 50 deletions(-) diff --git a/packages/zone.js/lib/common/events.ts b/packages/zone.js/lib/common/events.ts index 4633e7318b0b6..0c27767e785a3 100644 --- a/packages/zone.js/lib/common/events.ts +++ b/packages/zone.js/lib/common/events.ts @@ -293,16 +293,6 @@ export function patchEventTarget( // if task is not marked as isRemoved, this call is directly // from Zone.prototype.cancelTask, we should remove the task // from tasksList of target first - const signal = task?.options?.signal; - if (typeof signal === 'object' && signal?.tasks) { - const abortTasks = signal.tasks; - for (let i = 0; i < abortTasks?.length || 0; i++) { - if (abortTasks[i] === task) { - abortTasks.splice(i, 1); - break; - } - } - } if (!task.isRemoved) { const symbolEventNames = zoneSymbolEventNames[task.eventName]; let symbolEventName; @@ -404,8 +394,11 @@ export function patchEventTarget( const passive = passiveSupported && !!passiveEvents && passiveEvents.indexOf(eventName) !== -1; const options = buildEventListenerOptions(arguments[2], passive); - const signal = typeof options === 'object' && options?.signal; - if (typeof signal === 'object' && signal?.aborted) { + const signal = options && typeof options === 'object' && options.signal && + typeof options.signal === 'object' ? + options.signal : + undefined; + if (signal?.aborted) { // the signal is an aborted one, just return without attaching the event listener. return; } @@ -480,7 +473,7 @@ export function patchEventTarget( (data as any).taskData = taskData; } - if (signal && typeof signal === 'object') { + if (signal) { // if addEventListener with signal options, we don't pass it to // native addEventListener, instead we keep the signal setting // and handle ourselves. @@ -489,20 +482,12 @@ export function patchEventTarget( const task: any = zone.scheduleEventTask(source, delegate, data, customScheduleFn, customCancelFn); - if (signal && typeof signal === 'object') { + if (signal) { // after task is scheduled, we need to store the signal back to task.options taskData.options.signal = signal; - const tasks = signal.tasks || []; - tasks.push(task); - signal.tasks = tasks; - if (!signal[Zone.__symbol__('abortListener')]) { - signal[Zone.__symbol__('abortListener')] = true; - nativeListener.call(signal, 'abort', function() { - const sTasks = signal.tasks.slice(); - sTasks.forEach((task: Task) => task.zone.cancelTask(task)); - signal.tasks.length = 0; - }); - } + nativeListener.call(signal, 'abort', () => { + task.zone.cancelTask(task); + }, {once: true}); } // should clear taskData.target to avoid memory leak diff --git a/packages/zone.js/lib/common/fetch.ts b/packages/zone.js/lib/common/fetch.ts index b7ebbc9afcc9e..2ff4a56843705 100644 --- a/packages/zone.js/lib/common/fetch.ts +++ b/packages/zone.js/lib/common/fetch.ts @@ -26,15 +26,22 @@ Zone.__load_patch('fetch', (global: any, Zone: ZoneType, api: _ZonePrivate) => { const ZoneAwarePromise = global.Promise; const symbolThenPatched = api.symbol('thenPatched'); const fetchTaskScheduling = api.symbol('fetchTaskScheduling'); - const fetchTaskAborting = api.symbol('fetchTaskAborting'); - const OriginalAbortController = global['AbortController']; - const supportAbort = typeof OriginalAbortController === 'function'; - let abortNative: Function|null = OriginalAbortController?.prototype[api.symbol('abort')]; const placeholder = function() {}; global['fetch'] = function() { const args = Array.prototype.slice.call(arguments); - const options = args.length > 1 ? args[1] : null; + const options = args.length > 1 ? args[1] : {}; const signal = options && options.signal; + const ac = new AbortController(); + const fetchSignal = ac.signal; + options.signal = fetchSignal; + args[1] = options; + if (signal) { + const nativeAddEventListener = + signal[Zone.__symbol__('addEventListener')] || signal.addEventListener; + nativeAddEventListener.call(signal, 'abort', function() { + ac!.abort(); + }, {once: true}); + } return new Promise((res, rej) => { const task = Zone.current.scheduleMacroTask( 'fetch', placeholder, {fetchArgs: args} as FetchTaskData, @@ -72,25 +79,8 @@ Zone.__load_patch('fetch', (global: any, Zone: ZoneType, api: _ZonePrivate) => { }); }, () => { - if (!supportAbort) { - rej('No AbortController supported, can not cancel fetch'); - return; - } - if (signal && signal.abortController && !signal.aborted && - typeof signal.abortController.abort === 'function' && abortNative) { - try { - (Zone.current as any)[fetchTaskAborting] = true; - abortNative.call(signal.abortController); - } finally { - (Zone.current as any)[fetchTaskAborting] = false; - } - } else { - rej('cancel fetch need a AbortController.signal'); - } + ac.abort(); }); - if (signal && signal.abortController) { - signal.abortController.tasks = [task]; - } }); }; }); diff --git a/packages/zone.js/test/browser/browser.spec.ts b/packages/zone.js/test/browser/browser.spec.ts index bd35c17e33ae2..974d2d4ce44fc 100644 --- a/packages/zone.js/test/browser/browser.spec.ts +++ b/packages/zone.js/test/browser/browser.spec.ts @@ -1809,7 +1809,7 @@ describe('Zone', function() { expect(logs.length).toBe(2); expect(logs).toEqual(['click1', 'click2']); - button.removeEventListener('click', listener1); + ac.abort(); listeners = button.eventListeners!('click'); expect(listeners.length).toBe(1); @@ -1820,11 +1820,92 @@ describe('Zone', function() { expect(logs.length).toBe(1); expect(listeners.length).toBe(1); expect(logs).toEqual(['click2']); + }); - ac.abort(); + it('should not break remove event listeners with AbortController', function() { + let logs: string[] = []; + const ac = new AbortController(); + + const listener1 = function() { + logs.push('click1'); + }; + button.addEventListener('click', listener1, {signal: ac.signal}); + button.addEventListener('click', function() { + logs.push('click2'); + }); + let listeners = button.eventListeners!('click'); + expect(listeners.length).toBe(2); + + button.dispatchEvent(clickEvent); + expect(logs.length).toBe(2); + expect(logs).toEqual(['click1', 'click2']); + + button.removeEventListener('click', listener1); + listeners = button.eventListeners!('click'); + expect(listeners.length).toBe(1); + + logs = []; + + listeners = button.eventListeners!('click'); + button.dispatchEvent(clickEvent); + expect(logs.length).toBe(1); + expect(listeners.length).toBe(1); expect(logs).toEqual(['click2']); }); + it('should support remove multiple event listeners with the same AbortController', + function() { + let logs: string[] = []; + const ac = new AbortController(); + const button1 = document.createElement('button'); + const keyEvent: any = document.createEvent('KeyboardEvent'); + keyEvent.initKeyboardEvent( + 'keypress', // typeArg, + true, // canBubbleArg, + true, // cancelableArg, + null, // viewArg, Specifies UIEvent.view. This value may be null. + '', + 0, + false, // ctrlKeyArg, + false, // altKeyArg, + false, // shiftKeyArg, + false, // metaKeyArg, + ); + document.body.appendChild(button1); + + const listener1 = function() { + logs.push('click1'); + }; + button.addEventListener('click', listener1, {signal: ac.signal}); + button.addEventListener('click', function() { + logs.push('click2'); + }); + button1.addEventListener('keypress', () => logs.push('click3'), {signal: ac.signal}); + button1.addEventListener('keypress', function() { + logs.push('click4'); + }); + let listeners = button.eventListeners!('click'); + expect(listeners.length).toBe(2); + + button.dispatchEvent(clickEvent); + expect(logs.length).toBe(2); + expect(logs).toEqual(['click1', 'click2']); + button1.dispatchEvent(keyEvent); + expect(logs.length).toBe(4); + expect(logs).toEqual(['click1', 'click2', 'click3', 'click4']); + + logs = []; + ac.abort(); + listeners = button.eventListeners!('click'); + button.dispatchEvent(clickEvent); + expect(logs.length).toBe(1); + expect(listeners.length).toBe(1); + expect(logs).toEqual(['click2']); + button1.dispatchEvent(keyEvent); + expect(logs.length).toBe(2); + expect(logs).toEqual(['click2', 'click4']); + }); + it('should not add event listeners with aborted signal', function() { let logs: string[] = []; From e149ebf228a7524fe26d6f7416930433a6938d70 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:26:46 -0500 Subject: [PATCH 16/27] build: update rxjs build version to v7 (#53500) The version of rxjs used to build the repository has been updated to v7. This required only minimal changes to the code. Most of which were type related only due to more strict types in v7. The behavior in those cases was left intact. The most common type related change was to handle the possibility of `undefined` with `toPromise` which was always possible with v6 but the types did not reflect the runtime behavior. The one change that was not type related was to provide a parameter value to the `defaultIfEmpty` operator. It no longer defaults to a value of `null` if no default is provided. To provide the same behavior the value of `null` is now passed to the operator. PR Close #53500 --- .../component-data-source/index.ts | 2 +- .../property-resolver/property-data-source.ts | 2 +- .../expanding_row_details_caption.ts | 2 +- package.json | 2 +- packages/common/http/test/client_spec.ts | 8 +- packages/common/http/test/fetch_spec.ts | 23 +- packages/core/src/event_emitter.ts | 2 - .../initial_render_pending_tasks_spec.ts | 10 +- packages/core/test/acceptance/outputs_spec.ts | 8 +- .../test/acceptance/property_binding_spec.ts | 2 +- .../bundle.golden_symbols.json | 194 +++++------ .../animations/bundle.golden_symbols.json | 194 +++++------ .../cyclic_import/bundle.golden_symbols.json | 190 ++++++----- .../bundling/defer/bundle.golden_symbols.json | 306 ++++++++++-------- .../forms_reactive/bundle.golden_symbols.json | 202 +++++++----- .../bundle.golden_symbols.json | 202 +++++++----- .../hello_world/bundle.golden_symbols.json | 186 ++++++----- .../hydration/bundle.golden_symbols.json | 229 ++++++------- .../router/bundle.golden_symbols.json | 289 +++++++---------- .../bundle.golden_symbols.json | 188 ++++++----- .../bundling/todo/bundle.golden_symbols.json | 192 +++++------ packages/router/src/navigation_transition.ts | 2 +- .../router/test/create_router_state.spec.ts | 4 +- packages/router/test/recognize.spec.ts | 2 +- packages/router/test/router_scroller.spec.ts | 3 +- packages/service-worker/src/low_level.ts | 14 +- packages/service-worker/src/push.ts | 4 +- .../service-worker/test/integration_spec.ts | 2 +- yarn.lock | 11 +- 29 files changed, 1269 insertions(+), 1206 deletions(-) diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/component-data-source/index.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/component-data-source/index.ts index a026aff1cf160..7c17c454df26f 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/component-data-source/index.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/directive-forest/component-data-source/index.ts @@ -176,7 +176,7 @@ export class ComponentDataSource extends DataSource { this._treeControl.expansionModel.changed, this._flattenedData, ]; - return merge(...changes) + return merge(...changes) .pipe( map(() => { this._expandedData.next( diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.ts index cc8e5aac4781b..64485768d872d 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/property-resolver/property-data-source.ts @@ -69,7 +69,7 @@ export class PropertyDataSource extends DataSource { const changes = [collectionViewer.viewChange, this._treeControl.expansionModel.changed, this._data]; - return merge(...changes).pipe(map(() => { + return merge(...changes).pipe(map(() => { this._expandedData.next( this._treeFlattener.expandFlattenedNodes(this.data, this._treeControl)); return this._expandedData.value; diff --git a/modules/benchmarks/src/expanding_rows/expanding_row_details_caption.ts b/modules/benchmarks/src/expanding_rows/expanding_row_details_caption.ts index 8099f8f4f7882..3badd601c6c25 100644 --- a/modules/benchmarks/src/expanding_rows/expanding_row_details_caption.ts +++ b/modules/benchmarks/src/expanding_rows/expanding_row_details_caption.ts @@ -35,7 +35,7 @@ export class ExpandingRowDetailsCaption implements OnDestroy { @Input() color: string = 'blue'; /** This is triggered when this component is destroyed. */ - private readonly onDestroy = new Subject(); + private readonly onDestroy = new Subject(); /** * We need a reference to parent cfc-expanding-row component here to hide diff --git a/package.json b/package.json index 236fe23b793a9..38216ff1427ad 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "rollup": "~2.79.0", "rollup-plugin-preserve-shebang": "^1.0.1", "rollup-plugin-sourcemaps": "^0.6.3", - "rxjs": "^6.6.7", + "rxjs": "^7.0.0", "selenium-webdriver": "3.5.0", "selenium-webdriver4": "npm:selenium-webdriver@4.14.0", "semver-dsl": "^1.0.1", diff --git a/packages/common/http/test/client_spec.ts b/packages/common/http/test/client_spec.ts index d23f4ad228f15..3fc1b9e08466c 100644 --- a/packages/common/http/test/client_spec.ts +++ b/packages/common/http/test/client_spec.ts @@ -106,11 +106,11 @@ describe('HttpClient', () => { }); it('that returns a stream of events', done => { client.get('/test', {observe: 'events'}).pipe(toArray()).toPromise().then(events => { - expect(events.length).toBe(2); + expect(events!.length).toBe(2); let x = HttpResponse; - expect(events[0].type).toBe(HttpEventType.Sent); - expect(events[1].type).toBe(HttpEventType.Response); - expect(events[1] instanceof HttpResponse).toBeTruthy(); + expect(events![0].type).toBe(HttpEventType.Sent); + expect(events![1].type).toBe(HttpEventType.Response); + expect(events![1] instanceof HttpResponse).toBeTruthy(); done(); }); backend.expectOne('/test').flush({'data': 'hello world'}); diff --git a/packages/common/http/test/fetch_spec.ts b/packages/common/http/test/fetch_spec.ts index 0c765db958037..aea78cf387286 100644 --- a/packages/common/http/test/fetch_spec.ts +++ b/packages/common/http/test/fetch_spec.ts @@ -15,18 +15,17 @@ import {HttpDownloadProgressEvent, HttpErrorResponse, HttpHeaderResponse, HttpPa import {FetchBackend, FetchFactory} from '../src/fetch'; function trackEvents(obs: Observable): Promise { - return obs - .pipe( - // We don't want the promise to fail on HttpErrorResponse - catchError((e) => of(e)), - scan( - (acc, event) => { - acc.push(event); - return acc; - }, - [] as any[]), - ) - .toPromise(); + return obs.pipe( + // We don't want the promise to fail on HttpErrorResponse + catchError((e) => of(e)), + scan( + (acc, event) => { + acc.push(event); + return acc; + }, + [] as any[]), + ) + .toPromise() as Promise; } const TEST_POST = new HttpRequest('POST', '/test', 'some body', { diff --git a/packages/core/src/event_emitter.ts b/packages/core/src/event_emitter.ts index a7eea17c14011..fca146714551c 100644 --- a/packages/core/src/event_emitter.ts +++ b/packages/core/src/event_emitter.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -/// - import {PartialObserver, Subject, Subscription} from 'rxjs'; /** diff --git a/packages/core/test/acceptance/initial_render_pending_tasks_spec.ts b/packages/core/test/acceptance/initial_render_pending_tasks_spec.ts index 32e8b2da71b63..ce4e87e94c987 100644 --- a/packages/core/test/acceptance/initial_render_pending_tasks_spec.ts +++ b/packages/core/test/acceptance/initial_render_pending_tasks_spec.ts @@ -53,9 +53,9 @@ describe('InitialRenderPendingTasks', () => { function hasPendingTasks(pendingTasks: InitialRenderPendingTasks): Promise { return of(EMPTY) - .pipe( - withLatestFrom(pendingTasks.hasPendingTasks), - map(([_, hasPendingTasks]) => hasPendingTasks), - ) - .toPromise(); + .pipe( + withLatestFrom(pendingTasks.hasPendingTasks), + map(([_, hasPendingTasks]) => hasPendingTasks), + ) + .toPromise() as Promise; } diff --git a/packages/core/test/acceptance/outputs_spec.ts b/packages/core/test/acceptance/outputs_spec.ts index c265ad92eab57..10a234722f7a0 100644 --- a/packages/core/test/acceptance/outputs_spec.ts +++ b/packages/core/test/acceptance/outputs_spec.ts @@ -13,14 +13,14 @@ import {TestBed} from '@angular/core/testing'; describe('outputs', () => { @Component({selector: 'button-toggle', template: ''}) class ButtonToggle { - @Output('change') change = new EventEmitter(); + @Output('change') change = new EventEmitter(); - @Output('reset') resetStream = new EventEmitter(); + @Output('reset') resetStream = new EventEmitter(); } @Directive({selector: '[otherDir]'}) class OtherDir { - @Output('change') changeStream = new EventEmitter(); + @Output('change') changeStream = new EventEmitter(); } @Component({selector: 'destroy-comp', template: ''}) @@ -33,7 +33,7 @@ describe('outputs', () => { @Directive({selector: '[myButton]'}) class MyButton { - @Output() click = new EventEmitter(); + @Output() click = new EventEmitter(); } it('should call component output function when event is emitted', () => { diff --git a/packages/core/test/acceptance/property_binding_spec.ts b/packages/core/test/acceptance/property_binding_spec.ts index a6cada6e8fcff..092fccf69f89b 100644 --- a/packages/core/test/acceptance/property_binding_spec.ts +++ b/packages/core/test/acceptance/property_binding_spec.ts @@ -396,7 +396,7 @@ describe('property bindings', () => { class MyDir { @Input() role: string|undefined; @Input('dir') direction: string|undefined; - @Output('change') changeStream = new EventEmitter(); + @Output('change') changeStream = new EventEmitter(); } @Directive({selector: '[myDirB]'}) diff --git a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json index e2e91b51373a3..a541f0189c1ef 100644 --- a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json @@ -101,6 +101,9 @@ { "name": "CIRCULAR" }, + { + "name": "COMPLETE_NOTIFICATION" + }, { "name": "COMPONENT_REGEX" }, @@ -135,10 +138,7 @@ "name": "ComponentRef2" }, { - "name": "ConnectableObservable" - }, - { - "name": "ConnectableSubscriber" + "name": "ConsumerObserver" }, { "name": "DASH_CASE_REGEXP" @@ -164,18 +164,15 @@ { "name": "DefaultDomRenderer2" }, - { - "name": "DistinctUntilChangedOperator" - }, - { - "name": "DistinctUntilChangedSubscriber" - }, { "name": "DomAdapter" }, { "name": "DomRendererFactory2" }, + { + "name": "EMPTY" + }, { "name": "EMPTY_ARRAY" }, @@ -188,12 +185,18 @@ { "name": "EMPTY_OBJECT" }, + { + "name": "EMPTY_OBSERVER" + }, { "name": "EMPTY_PAYLOAD" }, { "name": "EMPTY_PLAYER_ARRAY" }, + { + "name": "EMPTY_SUBSCRIPTION" + }, { "name": "ENTER_TOKEN_REGEX" }, @@ -287,18 +290,6 @@ { "name": "MOVED_VIEWS" }, - { - "name": "MapOperator" - }, - { - "name": "MapSubscriber" - }, - { - "name": "MergeMapOperator" - }, - { - "name": "MergeMapSubscriber" - }, { "name": "NAMESPACE_URIS" }, @@ -401,6 +392,9 @@ { "name": "Observable" }, + { + "name": "OperatorSubscriber" + }, { "name": "PARAM_REGEX" }, @@ -425,12 +419,6 @@ { "name": "REMOVE_STYLES_ON_COMPONENT_DESTROY" }, - { - "name": "RefCountOperator" - }, - { - "name": "RefCountSubscriber" - }, { "name": "RendererFactory2" }, @@ -461,12 +449,6 @@ { "name": "SimpleChange" }, - { - "name": "SimpleInnerSubscriber" - }, - { - "name": "SimpleOuterSubscriber" - }, { "name": "SpecialCasedStyles" }, @@ -482,24 +464,12 @@ { "name": "Subject" }, - { - "name": "SubjectSubscriber" - }, - { - "name": "SubjectSubscription" - }, { "name": "Subscriber" }, { "name": "Subscription" }, - { - "name": "SwitchMapOperator" - }, - { - "name": "SwitchMapSubscriber" - }, { "name": "TESTABILITY" }, @@ -566,6 +536,12 @@ { "name": "_NullComponentFactoryResolver" }, + { + "name": "__asyncValues" + }, + { + "name": "__await" + }, { "name": "__forward_ref__" }, @@ -573,13 +549,13 @@ "name": "_applyRootElementTransformImpl" }, { - "name": "_convertTimeValueToMS" + "name": "_bind" }, { - "name": "_currentInjector" + "name": "_convertTimeValueToMS" }, { - "name": "_enable_super_gross_mode_that_will_cause_bad_things" + "name": "_currentInjector" }, { "name": "_flattenGroupPlayersRecur" @@ -656,6 +632,9 @@ { "name": "applyView" }, + { + "name": "arrRemove" + }, { "name": "assertConsumerNode" }, @@ -668,6 +647,9 @@ { "name": "baseElement" }, + { + "name": "bind" + }, { "name": "bloomHasToken" }, @@ -725,9 +707,6 @@ { "name": "configureViewWithDirective" }, - { - "name": "connectableObservableDescriptor" - }, { "name": "consumerIsLive" }, @@ -737,6 +716,9 @@ { "name": "containsElement" }, + { + "name": "context" + }, { "name": "convertToBitFlags" }, @@ -749,9 +731,15 @@ { "name": "createElementRef" }, + { + "name": "createErrorClass" + }, { "name": "createInjector" }, + { + "name": "createInvalidObservableTypeError" + }, { "name": "createLFrame" }, @@ -761,6 +749,12 @@ { "name": "createNodeInjector" }, + { + "name": "createNotification" + }, + { + "name": "createOperatorSubscriber" + }, { "name": "createTView" }, @@ -779,6 +773,9 @@ { "name": "deepForEachProvider" }, + { + "name": "defaultCompare" + }, { "name": "detachMovedView" }, @@ -803,12 +800,6 @@ { "name": "documentElement" }, - { - "name": "empty" - }, - { - "name": "empty2" - }, { "name": "enterDI" }, @@ -821,6 +812,12 @@ { "name": "eraseStyles" }, + { + "name": "errorContext" + }, + { + "name": "execFinalizer" + }, { "name": "executeCheckHooks" }, @@ -830,6 +827,9 @@ { "name": "executeInitAndCheckHooks" }, + { + "name": "executeSchedule" + }, { "name": "executeTemplate" }, @@ -851,9 +851,6 @@ { "name": "findAttrIndexInNode" }, - { - "name": "flattenUnsubscriptionErrors" - }, { "name": "forEachSingleProvider" }, @@ -867,7 +864,7 @@ "name": "from" }, { - "name": "fromArray" + "name": "fromAsyncIterable" }, { "name": "generateInitialInputs" @@ -999,10 +996,16 @@ "name": "getTView" }, { - "name": "hasTagAndTypeMatch" + "name": "handleReset" + }, + { + "name": "handleStoppedNotification" }, { - "name": "hostReportError" + "name": "handleUnhandledError" + }, + { + "name": "hasTagAndTypeMatch" }, { "name": "icuContainerIterate" @@ -1041,7 +1044,7 @@ "name": "injectableDefOrInjectorDefFactory" }, { - "name": "innerSubscribe" + "name": "innerFrom" }, { "name": "insertBloom" @@ -1071,10 +1074,10 @@ "name": "invokeQuery" }, { - "name": "isArray" + "name": "isArrayLike" }, { - "name": "isArrayLike" + "name": "isAsyncIterable" }, { "name": "isComponentDef" @@ -1103,6 +1106,12 @@ { "name": "isInlineTemplate" }, + { + "name": "isInteropObservable" + }, + { + "name": "isIterable" + }, { "name": "isLContainer" }, @@ -1115,9 +1124,6 @@ { "name": "isNodeMatchingSelectorList" }, - { - "name": "isObject" - }, { "name": "isPlatformServer" }, @@ -1131,11 +1137,14 @@ "name": "isPromise2" }, { - "name": "isScheduler" + "name": "isReadableStreamLike" }, { "name": "isStableFactory" }, + { + "name": "isSubscription" + }, { "name": "isTemplateNode" }, @@ -1148,6 +1157,9 @@ { "name": "iterator" }, + { + "name": "last" + }, { "name": "leaveDI" }, @@ -1178,9 +1190,6 @@ { "name": "makeTimingAst" }, - { - "name": "map" - }, { "name": "markAncestorsForTraversal" }, @@ -1199,9 +1208,6 @@ { "name": "maybeWrapInNotSelector" }, - { - "name": "mergeAll" - }, { "name": "mergeHostAttribute" }, @@ -1235,6 +1241,9 @@ { "name": "noop" }, + { + "name": "noop2" + }, { "name": "normalizeAnimationEntry" }, @@ -1250,12 +1259,18 @@ { "name": "observable" }, + { + "name": "observeOn" + }, { "name": "onEnter" }, { "name": "onLeave" }, + { + "name": "operate" + }, { "name": "optimizeGroupPlayer" }, @@ -1265,6 +1280,9 @@ { "name": "parseTimelineCommand" }, + { + "name": "popScheduler" + }, { "name": "processInjectorTypesWithProviders" }, @@ -1281,7 +1299,7 @@ "name": "provideZoneChangeDetection" }, { - "name": "refCount" + "name": "readableStreamLikeToAsyncGenerator" }, { "name": "refreshContentQueries" @@ -1316,6 +1334,9 @@ { "name": "replacePostStylesAsPre" }, + { + "name": "reportUnhandledError" + }, { "name": "requiresRefreshOrTraversal" }, @@ -1337,14 +1358,11 @@ { "name": "roundOffset" }, - { - "name": "rxSubscriber" - }, { "name": "saveNameToExportMap" }, { - "name": "scheduleArray" + "name": "scheduleAsyncIterable" }, { "name": "searchTokensOnInjector" @@ -1394,9 +1412,6 @@ { "name": "setupStaticAttributes" }, - { - "name": "shareSubjectFactory" - }, { "name": "shimStylesContent" }, @@ -1413,16 +1428,13 @@ "name": "style" }, { - "name": "subscribeTo" + "name": "subscribeOn" }, { - "name": "subscribeToArray" - }, - { - "name": "switchMap" + "name": "throwProviderNotFoundError" }, { - "name": "throwProviderNotFoundError" + "name": "timeoutProvider" }, { "name": "toRefArray" diff --git a/packages/core/test/bundling/animations/bundle.golden_symbols.json b/packages/core/test/bundling/animations/bundle.golden_symbols.json index 24266a7e1a32e..2822bbf22feba 100644 --- a/packages/core/test/bundling/animations/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animations/bundle.golden_symbols.json @@ -122,6 +122,9 @@ { "name": "CIRCULAR" }, + { + "name": "COMPLETE_NOTIFICATION" + }, { "name": "COMPONENT_REGEX" }, @@ -159,10 +162,7 @@ "name": "ComponentRef2" }, { - "name": "ConnectableObservable" - }, - { - "name": "ConnectableSubscriber" + "name": "ConsumerObserver" }, { "name": "DASH_CASE_REGEXP" @@ -188,12 +188,6 @@ { "name": "DefaultDomRenderer2" }, - { - "name": "DistinctUntilChangedOperator" - }, - { - "name": "DistinctUntilChangedSubscriber" - }, { "name": "DomAdapter" }, @@ -203,6 +197,9 @@ { "name": "DomRendererFactory2" }, + { + "name": "EMPTY" + }, { "name": "EMPTY_ARRAY" }, @@ -215,12 +212,18 @@ { "name": "EMPTY_OBJECT" }, + { + "name": "EMPTY_OBSERVER" + }, { "name": "EMPTY_PAYLOAD" }, { "name": "EMPTY_PLAYER_ARRAY" }, + { + "name": "EMPTY_SUBSCRIPTION" + }, { "name": "ENTER_TOKEN_REGEX" }, @@ -311,18 +314,6 @@ { "name": "MOVED_VIEWS" }, - { - "name": "MapOperator" - }, - { - "name": "MapSubscriber" - }, - { - "name": "MergeMapOperator" - }, - { - "name": "MergeMapSubscriber" - }, { "name": "NAMESPACE_URIS" }, @@ -440,6 +431,9 @@ { "name": "Observable" }, + { + "name": "OperatorSubscriber" + }, { "name": "PARAM_REGEX" }, @@ -467,12 +461,6 @@ { "name": "REMOVE_STYLES_ON_COMPONENT_DESTROY" }, - { - "name": "RefCountOperator" - }, - { - "name": "RefCountSubscriber" - }, { "name": "RendererFactory2" }, @@ -509,12 +497,6 @@ { "name": "SimpleChange" }, - { - "name": "SimpleInnerSubscriber" - }, - { - "name": "SimpleOuterSubscriber" - }, { "name": "SpecialCasedStyles" }, @@ -527,24 +509,12 @@ { "name": "Subject" }, - { - "name": "SubjectSubscriber" - }, - { - "name": "SubjectSubscription" - }, { "name": "Subscriber" }, { "name": "Subscription" }, - { - "name": "SwitchMapOperator" - }, - { - "name": "SwitchMapSubscriber" - }, { "name": "TESTABILITY" }, @@ -623,6 +593,12 @@ { "name": "_NullComponentFactoryResolver" }, + { + "name": "__asyncValues" + }, + { + "name": "__await" + }, { "name": "__forward_ref__" }, @@ -630,13 +606,13 @@ "name": "_applyRootElementTransformImpl" }, { - "name": "_convertTimeValueToMS" + "name": "_bind" }, { - "name": "_currentInjector" + "name": "_convertTimeValueToMS" }, { - "name": "_enable_super_gross_mode_that_will_cause_bad_things" + "name": "_currentInjector" }, { "name": "_flattenGroupPlayersRecur" @@ -716,6 +692,9 @@ { "name": "applyView" }, + { + "name": "arrRemove" + }, { "name": "assertConsumerNode" }, @@ -728,6 +707,9 @@ { "name": "baseElement" }, + { + "name": "bind" + }, { "name": "bloomHasToken" }, @@ -785,9 +767,6 @@ { "name": "configureViewWithDirective" }, - { - "name": "connectableObservableDescriptor" - }, { "name": "consumerIsLive" }, @@ -797,6 +776,9 @@ { "name": "containsElement" }, + { + "name": "context" + }, { "name": "convertToBitFlags" }, @@ -809,12 +791,18 @@ { "name": "createElementRef" }, + { + "name": "createErrorClass" + }, { "name": "createInjector" }, { "name": "createInjectorWithoutInjectorInstances" }, + { + "name": "createInvalidObservableTypeError" + }, { "name": "createLFrame" }, @@ -824,6 +812,12 @@ { "name": "createNodeInjector" }, + { + "name": "createNotification" + }, + { + "name": "createOperatorSubscriber" + }, { "name": "createPlatformFactory" }, @@ -845,6 +839,9 @@ { "name": "deepForEachProvider" }, + { + "name": "defaultCompare" + }, { "name": "detachMovedView" }, @@ -869,12 +866,6 @@ { "name": "documentElement" }, - { - "name": "empty" - }, - { - "name": "empty2" - }, { "name": "enterDI" }, @@ -887,6 +878,12 @@ { "name": "eraseStyles" }, + { + "name": "errorContext" + }, + { + "name": "execFinalizer" + }, { "name": "executeCheckHooks" }, @@ -896,6 +893,9 @@ { "name": "executeInitAndCheckHooks" }, + { + "name": "executeSchedule" + }, { "name": "executeTemplate" }, @@ -917,9 +917,6 @@ { "name": "findAttrIndexInNode" }, - { - "name": "flattenUnsubscriptionErrors" - }, { "name": "forEachSingleProvider" }, @@ -933,7 +930,7 @@ "name": "from" }, { - "name": "fromArray" + "name": "fromAsyncIterable" }, { "name": "generateInitialInputs" @@ -1068,10 +1065,16 @@ "name": "getTView" }, { - "name": "hasTagAndTypeMatch" + "name": "handleReset" + }, + { + "name": "handleStoppedNotification" }, { - "name": "hostReportError" + "name": "handleUnhandledError" + }, + { + "name": "hasTagAndTypeMatch" }, { "name": "icuContainerIterate" @@ -1110,7 +1113,7 @@ "name": "injectableDefOrInjectorDefFactory" }, { - "name": "innerSubscribe" + "name": "innerFrom" }, { "name": "insertBloom" @@ -1140,10 +1143,10 @@ "name": "invokeQuery" }, { - "name": "isArray" + "name": "isArrayLike" }, { - "name": "isArrayLike" + "name": "isAsyncIterable" }, { "name": "isComponentDef" @@ -1172,6 +1175,12 @@ { "name": "isInlineTemplate" }, + { + "name": "isInteropObservable" + }, + { + "name": "isIterable" + }, { "name": "isLContainer" }, @@ -1184,9 +1193,6 @@ { "name": "isNodeMatchingSelectorList" }, - { - "name": "isObject" - }, { "name": "isPlatformServer" }, @@ -1200,11 +1206,14 @@ "name": "isPromise2" }, { - "name": "isScheduler" + "name": "isReadableStreamLike" }, { "name": "isStableFactory" }, + { + "name": "isSubscription" + }, { "name": "isTemplateNode" }, @@ -1217,6 +1226,9 @@ { "name": "iterator" }, + { + "name": "last" + }, { "name": "leaveDI" }, @@ -1247,9 +1259,6 @@ { "name": "makeTimingAst" }, - { - "name": "map" - }, { "name": "markAncestorsForTraversal" }, @@ -1265,9 +1274,6 @@ { "name": "maybeWrapInNotSelector" }, - { - "name": "mergeAll" - }, { "name": "mergeHostAttribute" }, @@ -1304,6 +1310,9 @@ { "name": "noop" }, + { + "name": "noop2" + }, { "name": "normalizeAnimationEntry" }, @@ -1319,12 +1328,18 @@ { "name": "observable" }, + { + "name": "observeOn" + }, { "name": "onEnter" }, { "name": "onLeave" }, + { + "name": "operate" + }, { "name": "optimizeGroupPlayer" }, @@ -1340,6 +1355,9 @@ { "name": "platformCore" }, + { + "name": "popScheduler" + }, { "name": "processInjectorTypesWithProviders" }, @@ -1353,7 +1371,7 @@ "name": "profiler" }, { - "name": "refCount" + "name": "readableStreamLikeToAsyncGenerator" }, { "name": "refreshContentQueries" @@ -1388,6 +1406,9 @@ { "name": "replacePostStylesAsPre" }, + { + "name": "reportUnhandledError" + }, { "name": "requiresRefreshOrTraversal" }, @@ -1409,14 +1430,11 @@ { "name": "roundOffset" }, - { - "name": "rxSubscriber" - }, { "name": "saveNameToExportMap" }, { - "name": "scheduleArray" + "name": "scheduleAsyncIterable" }, { "name": "searchTokensOnInjector" @@ -1466,9 +1484,6 @@ { "name": "setupStaticAttributes" }, - { - "name": "shareSubjectFactory" - }, { "name": "shimStylesContent" }, @@ -1485,16 +1500,13 @@ "name": "style" }, { - "name": "subscribeTo" + "name": "subscribeOn" }, { - "name": "subscribeToArray" - }, - { - "name": "switchMap" + "name": "throwProviderNotFoundError" }, { - "name": "throwProviderNotFoundError" + "name": "timeoutProvider" }, { "name": "toRefArray" diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json index 919751bbf1f74..b909e3f45e5c4 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -53,6 +53,9 @@ { "name": "CIRCULAR" }, + { + "name": "COMPLETE_NOTIFICATION" + }, { "name": "COMPONENT_REGEX" }, @@ -90,10 +93,7 @@ "name": "ComponentRef2" }, { - "name": "ConnectableObservable" - }, - { - "name": "ConnectableSubscriber" + "name": "ConsumerObserver" }, { "name": "DEFAULT_APP_ID" @@ -110,12 +110,6 @@ { "name": "DepComponent" }, - { - "name": "DistinctUntilChangedOperator" - }, - { - "name": "DistinctUntilChangedSubscriber" - }, { "name": "DomAdapter" }, @@ -125,15 +119,24 @@ { "name": "DomRendererFactory2" }, + { + "name": "EMPTY" + }, { "name": "EMPTY_ARRAY" }, { "name": "EMPTY_OBJ" }, + { + "name": "EMPTY_OBSERVER" + }, { "name": "EMPTY_PAYLOAD" }, + { + "name": "EMPTY_SUBSCRIPTION" + }, { "name": "ENVIRONMENT_INITIALIZER" }, @@ -212,18 +215,6 @@ { "name": "MOVED_VIEWS" }, - { - "name": "MapOperator" - }, - { - "name": "MapSubscriber" - }, - { - "name": "MergeMapOperator" - }, - { - "name": "MergeMapSubscriber" - }, { "name": "Module" }, @@ -329,6 +320,9 @@ { "name": "Observable" }, + { + "name": "OperatorSubscriber" + }, { "name": "PLATFORM_DESTROY_LISTENERS" }, @@ -353,12 +347,6 @@ { "name": "REMOVE_STYLES_ON_COMPONENT_DESTROY" }, - { - "name": "RefCountOperator" - }, - { - "name": "RefCountSubscriber" - }, { "name": "RendererFactory2" }, @@ -386,33 +374,15 @@ { "name": "SimpleChange" }, - { - "name": "SimpleInnerSubscriber" - }, - { - "name": "SimpleOuterSubscriber" - }, { "name": "Subject" }, - { - "name": "SubjectSubscriber" - }, - { - "name": "SubjectSubscription" - }, { "name": "Subscriber" }, { "name": "Subscription" }, - { - "name": "SwitchMapOperator" - }, - { - "name": "SwitchMapSubscriber" - }, { "name": "TESTABILITY" }, @@ -464,6 +434,12 @@ { "name": "_NullComponentFactoryResolver" }, + { + "name": "__asyncValues" + }, + { + "name": "__await" + }, { "name": "__forward_ref__" }, @@ -471,10 +447,10 @@ "name": "_applyRootElementTransformImpl" }, { - "name": "_currentInjector" + "name": "_bind" }, { - "name": "_enable_super_gross_mode_that_will_cause_bad_things" + "name": "_currentInjector" }, { "name": "_getInsertInFrontOfRNodeWithI18n" @@ -542,6 +518,9 @@ { "name": "applyView" }, + { + "name": "arrRemove" + }, { "name": "assertConsumerNode" }, @@ -551,6 +530,9 @@ { "name": "baseElement" }, + { + "name": "bind" + }, { "name": "bloomHasToken" }, @@ -590,15 +572,15 @@ { "name": "configureViewWithDirective" }, - { - "name": "connectableObservableDescriptor" - }, { "name": "consumerIsLive" }, { "name": "consumerPollProducersForChange" }, + { + "name": "context" + }, { "name": "convertToBitFlags" }, @@ -608,12 +590,18 @@ { "name": "createElementRef" }, + { + "name": "createErrorClass" + }, { "name": "createInjector" }, { "name": "createInjectorWithoutInjectorInstances" }, + { + "name": "createInvalidObservableTypeError" + }, { "name": "createLFrame" }, @@ -623,6 +611,12 @@ { "name": "createNodeInjector" }, + { + "name": "createNotification" + }, + { + "name": "createOperatorSubscriber" + }, { "name": "createPlatformFactory" }, @@ -635,6 +629,9 @@ { "name": "deepForEachProvider" }, + { + "name": "defaultCompare" + }, { "name": "detachMovedView" }, @@ -657,19 +654,19 @@ "name": "diPublicInInjector" }, { - "name": "empty" + "name": "enterDI" }, { - "name": "empty2" + "name": "enterView" }, { - "name": "enterDI" + "name": "epoch" }, { - "name": "enterView" + "name": "errorContext" }, { - "name": "epoch" + "name": "execFinalizer" }, { "name": "executeCheckHooks" @@ -680,6 +677,9 @@ { "name": "executeInitAndCheckHooks" }, + { + "name": "executeSchedule" + }, { "name": "executeTemplate" }, @@ -695,9 +695,6 @@ { "name": "findAttrIndexInNode" }, - { - "name": "flattenUnsubscriptionErrors" - }, { "name": "forEachSingleProvider" }, @@ -711,7 +708,7 @@ "name": "from" }, { - "name": "fromArray" + "name": "fromAsyncIterable" }, { "name": "generateInitialInputs" @@ -837,10 +834,16 @@ "name": "getTView" }, { - "name": "hasTagAndTypeMatch" + "name": "handleReset" + }, + { + "name": "handleStoppedNotification" + }, + { + "name": "handleUnhandledError" }, { - "name": "hostReportError" + "name": "hasTagAndTypeMatch" }, { "name": "icuContainerIterate" @@ -879,7 +882,7 @@ "name": "injectableDefOrInjectorDefFactory" }, { - "name": "innerSubscribe" + "name": "innerFrom" }, { "name": "insertBloom" @@ -900,10 +903,10 @@ "name": "invokeHostBindingsInCreationMode" }, { - "name": "isArray" + "name": "isArrayLike" }, { - "name": "isArrayLike" + "name": "isAsyncIterable" }, { "name": "isComponentDef" @@ -929,6 +932,12 @@ { "name": "isInlineTemplate" }, + { + "name": "isInteropObservable" + }, + { + "name": "isIterable" + }, { "name": "isLContainer" }, @@ -941,9 +950,6 @@ { "name": "isNodeMatchingSelectorList" }, - { - "name": "isObject" - }, { "name": "isPlatformServer" }, @@ -957,11 +963,14 @@ "name": "isPromise2" }, { - "name": "isScheduler" + "name": "isReadableStreamLike" }, { "name": "isStableFactory" }, + { + "name": "isSubscription" + }, { "name": "isTemplateNode" }, @@ -974,6 +983,9 @@ { "name": "iterator" }, + { + "name": "last" + }, { "name": "lastNodeWasCreated" }, @@ -995,9 +1007,6 @@ { "name": "makeRecord" }, - { - "name": "map" - }, { "name": "markAncestorsForTraversal" }, @@ -1013,9 +1022,6 @@ { "name": "maybeWrapInNotSelector" }, - { - "name": "mergeAll" - }, { "name": "mergeHostAttribute" }, @@ -1052,18 +1058,27 @@ { "name": "noop" }, + { + "name": "noop2" + }, { "name": "notFoundValueOrThrow" }, { "name": "observable" }, + { + "name": "observeOn" + }, { "name": "onEnter" }, { "name": "onLeave" }, + { + "name": "operate" + }, { "name": "optionsReducer" }, @@ -1073,6 +1088,9 @@ { "name": "platformCore" }, + { + "name": "popScheduler" + }, { "name": "processInjectorTypesWithProviders" }, @@ -1086,7 +1104,7 @@ "name": "profiler" }, { - "name": "refCount" + "name": "readableStreamLikeToAsyncGenerator" }, { "name": "refreshContentQueries" @@ -1112,6 +1130,9 @@ { "name": "renderView" }, + { + "name": "reportUnhandledError" + }, { "name": "requiresRefreshOrTraversal" }, @@ -1124,14 +1145,11 @@ { "name": "retrieveHydrationInfo" }, - { - "name": "rxSubscriber" - }, { "name": "saveNameToExportMap" }, { - "name": "scheduleArray" + "name": "scheduleAsyncIterable" }, { "name": "searchTokensOnInjector" @@ -1178,9 +1196,6 @@ { "name": "setupStaticAttributes" }, - { - "name": "shareSubjectFactory" - }, { "name": "shimStylesContent" }, @@ -1194,16 +1209,13 @@ "name": "stringifyCSSSelector" }, { - "name": "subscribeTo" + "name": "subscribeOn" }, { - "name": "subscribeToArray" - }, - { - "name": "switchMap" + "name": "throwProviderNotFoundError" }, { - "name": "throwProviderNotFoundError" + "name": "timeoutProvider" }, { "name": "toRefArray" diff --git a/packages/core/test/bundling/defer/bundle.golden_symbols.json b/packages/core/test/bundling/defer/bundle.golden_symbols.json index 0ea35badcc3a5..ae54960f13b09 100644 --- a/packages/core/test/bundling/defer/bundle.golden_symbols.json +++ b/packages/core/test/bundling/defer/bundle.golden_symbols.json @@ -56,6 +56,9 @@ { "name": "CIRCULAR" }, + { + "name": "COMPLETE_NOTIFICATION" + }, { "name": "COMPONENT_REGEX" }, @@ -90,10 +93,7 @@ "name": "ComponentRef2" }, { - "name": "ConnectableObservable" - }, - { - "name": "ConnectableSubscriber" + "name": "ConsumerObserver" }, { "name": "DEFAULT_APP_ID" @@ -134,12 +134,6 @@ { "name": "DeferDependenciesLoadingState" }, - { - "name": "DistinctUntilChangedOperator" - }, - { - "name": "DistinctUntilChangedSubscriber" - }, { "name": "DomAdapter" }, @@ -149,15 +143,24 @@ { "name": "DomRendererFactory2" }, + { + "name": "EMPTY" + }, { "name": "EMPTY_ARRAY" }, { "name": "EMPTY_OBJ" }, + { + "name": "EMPTY_OBSERVER" + }, { "name": "EMPTY_PAYLOAD" }, + { + "name": "EMPTY_SUBSCRIPTION" + }, { "name": "ENVIRONMENT_INITIALIZER" }, @@ -257,18 +260,6 @@ { "name": "MOVED_VIEWS" }, - { - "name": "MapOperator" - }, - { - "name": "MapSubscriber" - }, - { - "name": "MergeMapOperator" - }, - { - "name": "MergeMapSubscriber" - }, { "name": "NAMESPACE_URIS" }, @@ -371,6 +362,9 @@ { "name": "Observable" }, + { + "name": "OperatorSubscriber" + }, { "name": "PLATFORM_DESTROY_LISTENERS" }, @@ -401,12 +395,6 @@ { "name": "REMOVE_STYLES_ON_COMPONENT_DESTROY" }, - { - "name": "RefCountOperator" - }, - { - "name": "RefCountSubscriber" - }, { "name": "RendererFactory2" }, @@ -440,36 +428,18 @@ { "name": "SimpleChange" }, - { - "name": "SimpleInnerSubscriber" - }, - { - "name": "SimpleOuterSubscriber" - }, { "name": "StandaloneService" }, { "name": "Subject" }, - { - "name": "SubjectSubscriber" - }, - { - "name": "SubjectSubscription" - }, { "name": "Subscriber" }, { "name": "Subscription" }, - { - "name": "SwitchMapOperator" - }, - { - "name": "SwitchMapSubscriber" - }, { "name": "TESTABILITY" }, @@ -512,6 +482,12 @@ { "name": "_NullComponentFactoryResolver" }, + { + "name": "__asyncValues" + }, + { + "name": "__await" + }, { "name": "__defProp" }, @@ -528,10 +504,10 @@ "name": "_applyRootElementTransformImpl" }, { - "name": "_currentInjector" + "name": "_bind" }, { - "name": "_enable_super_gross_mode_that_will_cause_bad_things" + "name": "_currentInjector" }, { "name": "_ensureDirtyViewsAreAlwaysReachable" @@ -623,6 +599,9 @@ { "name": "applyView" }, + { + "name": "arrRemove" + }, { "name": "assertConsumerNode" }, @@ -632,6 +611,9 @@ { "name": "baseElement" }, + { + "name": "bind" + }, { "name": "bloomHasToken" }, @@ -671,15 +653,15 @@ { "name": "configureViewWithDirective" }, - { - "name": "connectableObservableDescriptor" - }, { "name": "consumerIsLive" }, { "name": "consumerPollProducersForChange" }, + { + "name": "context" + }, { "name": "convertToBitFlags" }, @@ -695,9 +677,15 @@ { "name": "createElementRef" }, + { + "name": "createErrorClass" + }, { "name": "createInjector" }, + { + "name": "createInvalidObservableTypeError" + }, { "name": "createLFrame" }, @@ -707,6 +695,12 @@ { "name": "createNodeInjector" }, + { + "name": "createNotification" + }, + { + "name": "createOperatorSubscriber" + }, { "name": "createTView" }, @@ -716,6 +710,12 @@ { "name": "deepForEachProvider" }, + { + "name": "defaultCompare" + }, + { + "name": "defaultErrorHandler" + }, { "name": "defer_component_exports" }, @@ -746,9 +746,6 @@ { "name": "diPublicInInjector" }, - { - "name": "empty" - }, { "name": "enterDI" }, @@ -758,6 +755,12 @@ { "name": "epoch" }, + { + "name": "errorContext" + }, + { + "name": "execFinalizer" + }, { "name": "executeCheckHooks" }, @@ -767,6 +770,9 @@ { "name": "executeInitAndCheckHooks" }, + { + "name": "executeSchedule" + }, { "name": "executeTemplate" }, @@ -782,9 +788,6 @@ { "name": "findAttrIndexInNode" }, - { - "name": "flattenUnsubscriptionErrors" - }, { "name": "forEachSingleProvider" }, @@ -798,7 +801,7 @@ "name": "from" }, { - "name": "fromArray" + "name": "fromAsyncIterable" }, { "name": "generateInitialInputs" @@ -951,13 +954,19 @@ "name": "handleError" }, { - "name": "hasInSkipHydrationBlockFlag" + "name": "handleReset" }, { - "name": "hasTagAndTypeMatch" + "name": "handleStoppedNotification" }, { - "name": "hostReportError" + "name": "handleUnhandledError" + }, + { + "name": "hasInSkipHydrationBlockFlag" + }, + { + "name": "hasTagAndTypeMatch" }, { "name": "icuContainerIterate" @@ -978,7 +987,7 @@ "name": "init_BehaviorSubject" }, { - "name": "init_ConnectableObservable" + "name": "init_NotificationFactories" }, { "name": "init_ObjectUnsubscribedError" @@ -987,14 +996,11 @@ "name": "init_Observable" }, { - "name": "init_Observer" + "name": "init_OperatorSubscriber" }, { "name": "init_Subject" }, - { - "name": "init_SubjectSubscription" - }, { "name": "init_Subscriber" }, @@ -1043,6 +1049,12 @@ { "name": "init_application_tokens" }, + { + "name": "init_args" + }, + { + "name": "init_arrRemove" + }, { "name": "init_array_utils" }, @@ -1076,9 +1088,6 @@ { "name": "init_bypass" }, - { - "name": "init_canReportError" - }, { "name": "init_change_detection" }, @@ -1196,6 +1205,9 @@ { "name": "init_core_render3_private_export" }, + { + "name": "init_createErrorClass" + }, { "name": "init_create_application" }, @@ -1295,6 +1307,9 @@ { "name": "init_empty" }, + { + "name": "init_empty2" + }, { "name": "init_environment" }, @@ -1304,6 +1319,9 @@ { "name": "init_equality" }, + { + "name": "init_errorContext" + }, { "name": "init_error_details_base_url" }, @@ -1329,11 +1347,14 @@ "name": "init_errors_di" }, { - "name": "init_esm2015" + "name": "init_esm" }, { "name": "init_event_emitter" }, + { + "name": "init_executeSchedule" + }, { "name": "init_fields" }, @@ -1349,9 +1370,6 @@ { "name": "init_from" }, - { - "name": "init_fromArray" - }, { "name": "init_get_current_view" }, @@ -1367,9 +1385,6 @@ { "name": "init_hooks" }, - { - "name": "init_hostReportError" - }, { "name": "init_host_directives_feature" }, @@ -1473,7 +1488,7 @@ "name": "init_injector_utils" }, { - "name": "init_innerSubscribe" + "name": "init_innerFrom" }, { "name": "init_input" @@ -1500,10 +1515,10 @@ "name": "init_interpolation" }, { - "name": "init_isArray" + "name": "init_isArrayLike" }, { - "name": "init_isArrayLike" + "name": "init_isAsyncIterable" }, { "name": "init_isFunction" @@ -1515,10 +1530,10 @@ "name": "init_isIterable" }, { - "name": "init_isObject" + "name": "init_isPromise" }, { - "name": "init_isPromise" + "name": "init_isReadableStreamLike" }, { "name": "init_isScheduler" @@ -1544,6 +1559,9 @@ { "name": "init_lang" }, + { + "name": "init_lift" + }, { "name": "init_linker" }, @@ -1580,6 +1598,9 @@ { "name": "init_mergeAll" }, + { + "name": "init_mergeInternals" + }, { "name": "init_mergeMap" }, @@ -1604,9 +1625,6 @@ { "name": "init_module_patch" }, - { - "name": "init_multicast" - }, { "name": "init_namespace" }, @@ -1673,12 +1691,18 @@ { "name": "init_noop" }, + { + "name": "init_noop2" + }, { "name": "init_null_injector" }, { "name": "init_observable" }, + { + "name": "init_observeOn" + }, { "name": "init_of" }, @@ -1760,9 +1784,6 @@ { "name": "init_reactive_lview_consumer" }, - { - "name": "init_refCount" - }, { "name": "init_reflection_capabilities" }, @@ -1776,10 +1797,10 @@ "name": "init_render3" }, { - "name": "init_resource_loading" + "name": "init_reportUnhandledError" }, { - "name": "init_rxSubscriber" + "name": "init_resource_loading" }, { "name": "init_sanitization" @@ -1790,6 +1811,9 @@ { "name": "init_scheduleArray" }, + { + "name": "init_scheduleAsyncIterable" + }, { "name": "init_scheduleIterable" }, @@ -1799,6 +1823,9 @@ { "name": "init_schedulePromise" }, + { + "name": "init_scheduleReadableStreamLike" + }, { "name": "init_scheduled" }, @@ -1878,19 +1905,7 @@ "name": "init_styling_parser" }, { - "name": "init_subscribeTo" - }, - { - "name": "init_subscribeToArray" - }, - { - "name": "init_subscribeToIterable" - }, - { - "name": "init_subscribeToObservable" - }, - { - "name": "init_subscribeToPromise" + "name": "init_subscribeOn" }, { "name": "init_switchMap" @@ -1911,10 +1926,13 @@ "name": "init_text_interpolation" }, { - "name": "init_timer_scheduler" + "name": "init_throwUnobservableError" }, { - "name": "init_toSubscriber" + "name": "init_timeoutProvider" + }, + { + "name": "init_timer_scheduler" }, { "name": "init_tokens" @@ -1934,12 +1952,18 @@ { "name": "init_trusted_types_bypass" }, + { + "name": "init_tslib_es6" + }, { "name": "init_type" }, { "name": "init_type_checks" }, + { + "name": "init_types" + }, { "name": "init_untracked" }, @@ -2022,7 +2046,7 @@ "name": "injectableDefOrInjectorDefFactory" }, { - "name": "innerSubscribe" + "name": "innerFrom" }, { "name": "insertBloom" @@ -2049,10 +2073,10 @@ "name": "invokeTriggerCleanupFns" }, { - "name": "isArray" + "name": "isArrayLike" }, { - "name": "isArrayLike" + "name": "isAsyncIterable" }, { "name": "isComponentDef" @@ -2081,6 +2105,12 @@ { "name": "isInlineTemplate" }, + { + "name": "isInteropObservable" + }, + { + "name": "isIterable" + }, { "name": "isLContainer" }, @@ -2093,9 +2123,6 @@ { "name": "isNodeMatchingSelectorList" }, - { - "name": "isObject" - }, { "name": "isPlatformServer" }, @@ -2109,11 +2136,14 @@ "name": "isPromise2" }, { - "name": "isScheduler" + "name": "isReadableStreamLike" }, { "name": "isStableFactory" }, + { + "name": "isSubscription" + }, { "name": "isTemplateNode" }, @@ -2129,6 +2159,9 @@ { "name": "iterator" }, + { + "name": "last" + }, { "name": "lastNodeWasCreated" }, @@ -2150,9 +2183,6 @@ { "name": "makeRecord" }, - { - "name": "map" - }, { "name": "markAncestorsForTraversal" }, @@ -2171,9 +2201,6 @@ { "name": "maybeWrapInNotSelector" }, - { - "name": "mergeAll" - }, { "name": "mergeHostAttribute" }, @@ -2210,21 +2237,33 @@ { "name": "noop" }, + { + "name": "noop2" + }, { "name": "notFoundValueOrThrow" }, { "name": "observable" }, + { + "name": "observeOn" + }, { "name": "onEnter" }, { "name": "onLeave" }, + { + "name": "operate" + }, { "name": "performanceMarkFeature" }, + { + "name": "popScheduler" + }, { "name": "populateDehydratedViewsInLContainer" }, @@ -2244,7 +2283,7 @@ "name": "provideZoneChangeDetection" }, { - "name": "refCount" + "name": "readableStreamLikeToAsyncGenerator" }, { "name": "refreshContentQueries" @@ -2276,6 +2315,9 @@ { "name": "renderView" }, + { + "name": "reportUnhandledError" + }, { "name": "requiresRefreshOrTraversal" }, @@ -2291,9 +2333,6 @@ { "name": "retrieveHydrationInfo" }, - { - "name": "rxSubscriber" - }, { "name": "saveNameToExportMap" }, @@ -2301,7 +2340,7 @@ "name": "saveResolvedLocalsInData" }, { - "name": "scheduleArray" + "name": "scheduleAsyncIterable" }, { "name": "searchTokensOnInjector" @@ -2351,9 +2390,6 @@ { "name": "setupStaticAttributes" }, - { - "name": "shareSubjectFactory" - }, { "name": "shimStylesContent" }, @@ -2370,22 +2406,7 @@ "name": "stringifyCSSSelector" }, { - "name": "subscribeTo" - }, - { - "name": "subscribeToArray" - }, - { - "name": "subscribeToIterable" - }, - { - "name": "subscribeToObservable" - }, - { - "name": "subscribeToPromise" - }, - { - "name": "switchMap" + "name": "subscribeOn" }, { "name": "throwError" @@ -2393,6 +2414,9 @@ { "name": "throwProviderNotFoundError" }, + { + "name": "timeoutProvider" + }, { "name": "toRefArray" }, diff --git a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json index 986f1c707899c..1b04c1b556b51 100644 --- a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json @@ -77,6 +77,9 @@ { "name": "CIRCULAR" }, + { + "name": "COMPLETE_NOTIFICATION" + }, { "name": "COMPONENT_REGEX" }, @@ -120,10 +123,7 @@ "name": "ComponentRef2" }, { - "name": "ConnectableObservable" - }, - { - "name": "ConnectableSubscriber" + "name": "ConsumerObserver" }, { "name": "ControlContainer" @@ -155,12 +155,6 @@ { "name": "DefaultValueAccessor" }, - { - "name": "DistinctUntilChangedOperator" - }, - { - "name": "DistinctUntilChangedSubscriber" - }, { "name": "DomAdapter" }, @@ -173,15 +167,24 @@ { "name": "EMAIL_REGEXP" }, + { + "name": "EMPTY" + }, { "name": "EMPTY_ARRAY" }, { "name": "EMPTY_OBJ" }, + { + "name": "EMPTY_OBSERVER" + }, { "name": "EMPTY_PAYLOAD" }, + { + "name": "EMPTY_SUBSCRIPTION" + }, { "name": "ENVIRONMENT_INITIALIZER" }, @@ -296,18 +299,6 @@ { "name": "MOVED_VIEWS" }, - { - "name": "MapOperator" - }, - { - "name": "MapSubscriber" - }, - { - "name": "MergeMapOperator" - }, - { - "name": "MergeMapSubscriber" - }, { "name": "NAMESPACE_URIS" }, @@ -440,6 +431,9 @@ { "name": "Observable" }, + { + "name": "OperatorSubscriber" + }, { "name": "Optional" }, @@ -485,12 +479,6 @@ { "name": "ReactiveFormsModule" }, - { - "name": "RefCountOperator" - }, - { - "name": "RefCountSubscriber" - }, { "name": "Renderer2" }, @@ -527,36 +515,18 @@ { "name": "SimpleChange" }, - { - "name": "SimpleInnerSubscriber" - }, - { - "name": "SimpleOuterSubscriber" - }, { "name": "SkipSelf" }, { "name": "Subject" }, - { - "name": "SubjectSubscriber" - }, - { - "name": "SubjectSubscription" - }, { "name": "Subscriber" }, { "name": "Subscription" }, - { - "name": "SwitchMapOperator" - }, - { - "name": "SwitchMapSubscriber" - }, { "name": "TESTABILITY" }, @@ -626,6 +596,12 @@ { "name": "_NullComponentFactoryResolver" }, + { + "name": "__asyncValues" + }, + { + "name": "__await" + }, { "name": "__forward_ref__" }, @@ -633,10 +609,10 @@ "name": "_applyRootElementTransformImpl" }, { - "name": "_currentInjector" + "name": "_bind" }, { - "name": "_enable_super_gross_mode_that_will_cause_bad_things" + "name": "_currentInjector" }, { "name": "_ensureDirtyViewsAreAlwaysReachable" @@ -731,6 +707,9 @@ { "name": "applyViewChange" }, + { + "name": "arrRemove" + }, { "name": "assertAllValuesPresent" }, @@ -749,6 +728,9 @@ { "name": "baseElement" }, + { + "name": "bind" + }, { "name": "bindingUpdated" }, @@ -815,15 +797,15 @@ { "name": "configureViewWithDirective" }, - { - "name": "connectableObservableDescriptor" - }, { "name": "consumerIsLive" }, { "name": "consumerPollProducersForChange" }, + { + "name": "context" + }, { "name": "controlNameBinding" }, @@ -842,12 +824,18 @@ { "name": "createElementRef" }, + { + "name": "createErrorClass" + }, { "name": "createInjector" }, { "name": "createInjectorWithoutInjectorInstances" }, + { + "name": "createInvalidObservableTypeError" + }, { "name": "createLContainer" }, @@ -860,6 +848,15 @@ { "name": "createNodeInjector" }, + { + "name": "createNotification" + }, + { + "name": "createObject" + }, + { + "name": "createOperatorSubscriber" + }, { "name": "createPlatformFactory" }, @@ -872,6 +869,9 @@ { "name": "deepForEachProvider" }, + { + "name": "defaultCompare" + }, { "name": "defaultIterableDiffersFactory" }, @@ -903,19 +903,19 @@ "name": "diPublicInInjector" }, { - "name": "empty" + "name": "enterDI" }, { - "name": "empty2" + "name": "enterView" }, { - "name": "enterDI" + "name": "epoch" }, { - "name": "enterView" + "name": "errorContext" }, { - "name": "epoch" + "name": "execFinalizer" }, { "name": "executeCheckHooks" @@ -929,6 +929,9 @@ { "name": "executeListenerWithErrorHandling" }, + { + "name": "executeSchedule" + }, { "name": "executeTemplate" }, @@ -953,14 +956,11 @@ { "name": "findStylingValue" }, - { - "name": "flattenUnsubscriptionErrors" - }, { "name": "forEachSingleProvider" }, { - "name": "forkJoinInternal" + "name": "forkJoin" }, { "name": "formArrayNameProvider" @@ -981,7 +981,7 @@ "name": "from" }, { - "name": "fromArray" + "name": "fromAsyncIterable" }, { "name": "generateInitialInputs" @@ -1148,6 +1148,15 @@ { "name": "handleError" }, + { + "name": "handleReset" + }, + { + "name": "handleStoppedNotification" + }, + { + "name": "handleUnhandledError" + }, { "name": "hasInSkipHydrationBlockFlag" }, @@ -1163,9 +1172,6 @@ { "name": "hasValidator" }, - { - "name": "hostReportError" - }, { "name": "icuContainerIterate" }, @@ -1224,7 +1230,7 @@ "name": "injectableDefOrInjectorDefFactory" }, { - "name": "innerSubscribe" + "name": "innerFrom" }, { "name": "insertBloom" @@ -1248,10 +1254,10 @@ "name": "isAbstractControlOptions" }, { - "name": "isArray" + "name": "isArrayLike" }, { - "name": "isArrayLike" + "name": "isAsyncIterable" }, { "name": "isComponentDef" @@ -1289,6 +1295,12 @@ { "name": "isInlineTemplate" }, + { + "name": "isInteropObservable" + }, + { + "name": "isIterable" + }, { "name": "isLContainer" }, @@ -1304,9 +1316,6 @@ { "name": "isNodeMatchingSelectorList" }, - { - "name": "isObject" - }, { "name": "isOptionsObj" }, @@ -1326,7 +1335,7 @@ "name": "isPromise2" }, { - "name": "isScheduler" + "name": "isReadableStreamLike" }, { "name": "isStableFactory" @@ -1337,6 +1346,9 @@ { "name": "isStylingValuePresent" }, + { + "name": "isSubscription" + }, { "name": "isTemplateNode" }, @@ -1358,6 +1370,9 @@ { "name": "keyValueArraySet" }, + { + "name": "last" + }, { "name": "lastNodeWasCreated" }, @@ -1410,7 +1425,7 @@ "name": "maybeWrapInNotSelector" }, { - "name": "mergeAll" + "name": "merge" }, { "name": "mergeErrors" @@ -1469,6 +1484,9 @@ { "name": "noop" }, + { + "name": "noop2" + }, { "name": "normalizeValidators" }, @@ -1481,12 +1499,18 @@ { "name": "observable" }, + { + "name": "observeOn" + }, { "name": "onEnter" }, { "name": "onLeave" }, + { + "name": "operate" + }, { "name": "optionsReducer" }, @@ -1502,6 +1526,9 @@ { "name": "platformCore" }, + { + "name": "popScheduler" + }, { "name": "processInjectorTypesWithProviders" }, @@ -1518,7 +1545,7 @@ "name": "providerToFactory" }, { - "name": "refCount" + "name": "readableStreamLikeToAsyncGenerator" }, { "name": "refreshContentQueries" @@ -1556,6 +1583,9 @@ { "name": "renderView" }, + { + "name": "reportUnhandledError" + }, { "name": "requiresRefreshOrTraversal" }, @@ -1574,9 +1604,6 @@ { "name": "retrieveHydrationInfo" }, - { - "name": "rxSubscriber" - }, { "name": "saveNameToExportMap" }, @@ -1584,7 +1611,7 @@ "name": "saveResolvedLocalsInData" }, { - "name": "scheduleArray" + "name": "scheduleAsyncIterable" }, { "name": "searchTokensOnInjector" @@ -1652,9 +1679,6 @@ { "name": "setupStaticAttributes" }, - { - "name": "shareSubjectFactory" - }, { "name": "shimStylesContent" }, @@ -1671,16 +1695,13 @@ "name": "stringifyCSSSelector" }, { - "name": "subscribeTo" + "name": "subscribeOn" }, { - "name": "subscribeToArray" + "name": "throwProviderNotFoundError" }, { - "name": "switchMap" - }, - { - "name": "throwProviderNotFoundError" + "name": "timeoutProvider" }, { "name": "toObservable" @@ -1727,6 +1748,15 @@ { "name": "writeToDirectiveInput" }, + { + "name": "{getPrototypeOf:getPrototypeOf,prototype:objectProto,keys:getKeys}" + }, + { + "name": "{isArray:isArray2}" + }, + { + "name": "{isArray:isArray}" + }, { "name": "ɵInternalFormsSharedModule" }, diff --git a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json index 294827acbc288..5d0e8e799977e 100644 --- a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json @@ -80,6 +80,9 @@ { "name": "CIRCULAR" }, + { + "name": "COMPLETE_NOTIFICATION" + }, { "name": "COMPONENT_REGEX" }, @@ -126,10 +129,7 @@ "name": "ComponentRef2" }, { - "name": "ConnectableObservable" - }, - { - "name": "ConnectableSubscriber" + "name": "ConsumerObserver" }, { "name": "ControlContainer" @@ -161,12 +161,6 @@ { "name": "DefaultValueAccessor" }, - { - "name": "DistinctUntilChangedOperator" - }, - { - "name": "DistinctUntilChangedSubscriber" - }, { "name": "DomAdapter" }, @@ -176,15 +170,24 @@ { "name": "DomRendererFactory2" }, + { + "name": "EMPTY" + }, { "name": "EMPTY_ARRAY" }, { "name": "EMPTY_OBJ" }, + { + "name": "EMPTY_OBSERVER" + }, { "name": "EMPTY_PAYLOAD" }, + { + "name": "EMPTY_SUBSCRIPTION" + }, { "name": "ENVIRONMENT_INITIALIZER" }, @@ -281,18 +284,6 @@ { "name": "MOVED_VIEWS" }, - { - "name": "MapOperator" - }, - { - "name": "MapSubscriber" - }, - { - "name": "MergeMapOperator" - }, - { - "name": "MergeMapSubscriber" - }, { "name": "NAMESPACE_URIS" }, @@ -431,6 +422,9 @@ { "name": "Observable" }, + { + "name": "OperatorSubscriber" + }, { "name": "Optional" }, @@ -470,12 +464,6 @@ { "name": "RadioControlRegistryModule" }, - { - "name": "RefCountOperator" - }, - { - "name": "RefCountSubscriber" - }, { "name": "Renderer2" }, @@ -515,36 +503,18 @@ { "name": "SimpleChange" }, - { - "name": "SimpleInnerSubscriber" - }, - { - "name": "SimpleOuterSubscriber" - }, { "name": "SkipSelf" }, { "name": "Subject" }, - { - "name": "SubjectSubscriber" - }, - { - "name": "SubjectSubscription" - }, { "name": "Subscriber" }, { "name": "Subscription" }, - { - "name": "SwitchMapOperator" - }, - { - "name": "SwitchMapSubscriber" - }, { "name": "TESTABILITY" }, @@ -617,6 +587,12 @@ { "name": "_NullComponentFactoryResolver" }, + { + "name": "__asyncValues" + }, + { + "name": "__await" + }, { "name": "__forward_ref__" }, @@ -624,10 +600,10 @@ "name": "_applyRootElementTransformImpl" }, { - "name": "_currentInjector" + "name": "_bind" }, { - "name": "_enable_super_gross_mode_that_will_cause_bad_things" + "name": "_currentInjector" }, { "name": "_ensureDirtyViewsAreAlwaysReachable" @@ -719,6 +695,9 @@ { "name": "applyViewChange" }, + { + "name": "arrRemove" + }, { "name": "assertConsumerNode" }, @@ -731,6 +710,9 @@ { "name": "baseElement" }, + { + "name": "bind" + }, { "name": "bindingUpdated" }, @@ -788,15 +770,15 @@ { "name": "configureViewWithDirective" }, - { - "name": "connectableObservableDescriptor" - }, { "name": "consumerIsLive" }, { "name": "consumerPollProducersForChange" }, + { + "name": "context" + }, { "name": "controlPath" }, @@ -812,12 +794,18 @@ { "name": "createElementRef" }, + { + "name": "createErrorClass" + }, { "name": "createInjector" }, { "name": "createInjectorWithoutInjectorInstances" }, + { + "name": "createInvalidObservableTypeError" + }, { "name": "createLContainer" }, @@ -830,6 +818,15 @@ { "name": "createNodeInjector" }, + { + "name": "createNotification" + }, + { + "name": "createObject" + }, + { + "name": "createOperatorSubscriber" + }, { "name": "createPlatformFactory" }, @@ -842,6 +839,9 @@ { "name": "deepForEachProvider" }, + { + "name": "defaultCompare" + }, { "name": "defaultIterableDiffersFactory" }, @@ -873,19 +873,19 @@ "name": "diPublicInInjector" }, { - "name": "empty" + "name": "enterDI" }, { - "name": "empty2" + "name": "enterView" }, { - "name": "enterDI" + "name": "epoch" }, { - "name": "enterView" + "name": "errorContext" }, { - "name": "epoch" + "name": "execFinalizer" }, { "name": "executeCheckHooks" @@ -899,6 +899,9 @@ { "name": "executeListenerWithErrorHandling" }, + { + "name": "executeSchedule" + }, { "name": "executeTemplate" }, @@ -923,14 +926,11 @@ { "name": "findStylingValue" }, - { - "name": "flattenUnsubscriptionErrors" - }, { "name": "forEachSingleProvider" }, { - "name": "forkJoinInternal" + "name": "forkJoin" }, { "name": "formControlBinding" @@ -948,7 +948,7 @@ "name": "from" }, { - "name": "fromArray" + "name": "fromAsyncIterable" }, { "name": "generateInitialInputs" @@ -1112,6 +1112,15 @@ { "name": "handleError" }, + { + "name": "handleReset" + }, + { + "name": "handleStoppedNotification" + }, + { + "name": "handleUnhandledError" + }, { "name": "hasInSkipHydrationBlockFlag" }, @@ -1124,9 +1133,6 @@ { "name": "hasValidator" }, - { - "name": "hostReportError" - }, { "name": "icuContainerIterate" }, @@ -1188,7 +1194,7 @@ "name": "injectableDefOrInjectorDefFactory" }, { - "name": "innerSubscribe" + "name": "innerFrom" }, { "name": "insertBloom" @@ -1209,10 +1215,10 @@ "name": "invokeHostBindingsInCreationMode" }, { - "name": "isArray" + "name": "isArrayLike" }, { - "name": "isArrayLike" + "name": "isAsyncIterable" }, { "name": "isComponentDef" @@ -1247,6 +1253,12 @@ { "name": "isInlineTemplate" }, + { + "name": "isInteropObservable" + }, + { + "name": "isIterable" + }, { "name": "isLContainer" }, @@ -1262,9 +1274,6 @@ { "name": "isNodeMatchingSelectorList" }, - { - "name": "isObject" - }, { "name": "isOptionsObj" }, @@ -1284,7 +1293,7 @@ "name": "isPromise2" }, { - "name": "isScheduler" + "name": "isReadableStreamLike" }, { "name": "isStableFactory" @@ -1295,6 +1304,9 @@ { "name": "isStylingValuePresent" }, + { + "name": "isSubscription" + }, { "name": "isTemplateNode" }, @@ -1316,6 +1328,9 @@ { "name": "keyValueArraySet" }, + { + "name": "last" + }, { "name": "lastNodeWasCreated" }, @@ -1368,7 +1383,7 @@ "name": "maybeWrapInNotSelector" }, { - "name": "mergeAll" + "name": "merge" }, { "name": "mergeErrors" @@ -1433,6 +1448,9 @@ { "name": "noop" }, + { + "name": "noop2" + }, { "name": "normalizeValidators" }, @@ -1445,12 +1463,18 @@ { "name": "observable" }, + { + "name": "observeOn" + }, { "name": "onEnter" }, { "name": "onLeave" }, + { + "name": "operate" + }, { "name": "optionsReducer" }, @@ -1466,6 +1490,9 @@ { "name": "platformCore" }, + { + "name": "popScheduler" + }, { "name": "processInjectorTypesWithProviders" }, @@ -1482,7 +1509,7 @@ "name": "providerToFactory" }, { - "name": "refCount" + "name": "readableStreamLikeToAsyncGenerator" }, { "name": "refreshContentQueries" @@ -1523,6 +1550,9 @@ { "name": "renderView" }, + { + "name": "reportUnhandledError" + }, { "name": "requiredValidator" }, @@ -1550,9 +1580,6 @@ { "name": "retrieveHydrationInfo" }, - { - "name": "rxSubscriber" - }, { "name": "saveNameToExportMap" }, @@ -1560,7 +1587,7 @@ "name": "saveResolvedLocalsInData" }, { - "name": "scheduleArray" + "name": "scheduleAsyncIterable" }, { "name": "searchTokensOnInjector" @@ -1628,9 +1655,6 @@ { "name": "setupStaticAttributes" }, - { - "name": "shareSubjectFactory" - }, { "name": "shimStylesContent" }, @@ -1647,16 +1671,13 @@ "name": "stringifyCSSSelector" }, { - "name": "subscribeTo" + "name": "subscribeOn" }, { - "name": "subscribeToArray" + "name": "throwProviderNotFoundError" }, { - "name": "switchMap" - }, - { - "name": "throwProviderNotFoundError" + "name": "timeoutProvider" }, { "name": "toObservable" @@ -1703,6 +1724,15 @@ { "name": "writeToDirectiveInput" }, + { + "name": "{getPrototypeOf:getPrototypeOf,prototype:objectProto,keys:getKeys}" + }, + { + "name": "{isArray:isArray2}" + }, + { + "name": "{isArray:isArray}" + }, { "name": "ɵInternalFormsSharedModule" }, diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index 34f3abe685304..47d0028296e46 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -35,6 +35,9 @@ { "name": "CIRCULAR" }, + { + "name": "COMPLETE_NOTIFICATION" + }, { "name": "CONTAINER_HEADER_OFFSET" }, @@ -60,22 +63,16 @@ "name": "ComponentRef2" }, { - "name": "ConnectableObservable" - }, - { - "name": "ConnectableSubscriber" + "name": "ConsumerObserver" }, { "name": "DOCUMENT2" }, { - "name": "DistinctUntilChangedOperator" - }, - { - "name": "DistinctUntilChangedSubscriber" + "name": "DomAdapter" }, { - "name": "DomAdapter" + "name": "EMPTY" }, { "name": "EMPTY_ARRAY" @@ -83,9 +80,15 @@ { "name": "EMPTY_OBJ" }, + { + "name": "EMPTY_OBSERVER" + }, { "name": "EMPTY_PAYLOAD" }, + { + "name": "EMPTY_SUBSCRIPTION" + }, { "name": "ENVIRONMENT_INITIALIZER" }, @@ -146,18 +149,6 @@ { "name": "MOVED_VIEWS" }, - { - "name": "MapOperator" - }, - { - "name": "MapSubscriber" - }, - { - "name": "MergeMapOperator" - }, - { - "name": "MergeMapSubscriber" - }, { "name": "NATIVE" }, @@ -248,6 +239,9 @@ { "name": "Observable" }, + { + "name": "OperatorSubscriber" + }, { "name": "PLATFORM_DESTROY_LISTENERS" }, @@ -269,12 +263,6 @@ { "name": "REACTIVE_LVIEW_CONSUMER_NODE" }, - { - "name": "RefCountOperator" - }, - { - "name": "RefCountSubscriber" - }, { "name": "RendererFactory2" }, @@ -293,33 +281,15 @@ { "name": "SimpleChange" }, - { - "name": "SimpleInnerSubscriber" - }, - { - "name": "SimpleOuterSubscriber" - }, { "name": "Subject" }, - { - "name": "SubjectSubscriber" - }, - { - "name": "SubjectSubscription" - }, { "name": "Subscriber" }, { "name": "Subscription" }, - { - "name": "SwitchMapOperator" - }, - { - "name": "SwitchMapSubscriber" - }, { "name": "TESTABILITY" }, @@ -356,6 +326,12 @@ { "name": "_NullComponentFactoryResolver" }, + { + "name": "__asyncValues" + }, + { + "name": "__await" + }, { "name": "__forward_ref__" }, @@ -363,10 +339,10 @@ "name": "_applyRootElementTransformImpl" }, { - "name": "_currentInjector" + "name": "_bind" }, { - "name": "_enable_super_gross_mode_that_will_cause_bad_things" + "name": "_currentInjector" }, { "name": "_global" @@ -410,6 +386,9 @@ { "name": "applyView" }, + { + "name": "arrRemove" + }, { "name": "assertConsumerNode" }, @@ -419,6 +398,9 @@ { "name": "baseElement" }, + { + "name": "bind" + }, { "name": "bloomHasToken" }, @@ -452,27 +434,33 @@ { "name": "configureViewWithDirective" }, - { - "name": "connectableObservableDescriptor" - }, { "name": "consumerIsLive" }, { "name": "consumerPollProducersForChange" }, + { + "name": "context" + }, { "name": "convertToBitFlags" }, { "name": "createElementRef" }, + { + "name": "createErrorClass" + }, { "name": "createInjector" }, { "name": "createInjectorWithoutInjectorInstances" }, + { + "name": "createInvalidObservableTypeError" + }, { "name": "createLFrame" }, @@ -482,6 +470,12 @@ { "name": "createNodeInjector" }, + { + "name": "createNotification" + }, + { + "name": "createOperatorSubscriber" + }, { "name": "createPlatformFactory" }, @@ -494,6 +488,9 @@ { "name": "deepForEachProvider" }, + { + "name": "defaultCompare" + }, { "name": "detachMovedView" }, @@ -516,19 +513,19 @@ "name": "diPublicInInjector" }, { - "name": "empty" + "name": "enterDI" }, { - "name": "empty2" + "name": "enterView" }, { - "name": "enterDI" + "name": "epoch" }, { - "name": "enterView" + "name": "errorContext" }, { - "name": "epoch" + "name": "execFinalizer" }, { "name": "executeCheckHooks" @@ -537,13 +534,13 @@ "name": "executeInitAndCheckHooks" }, { - "name": "executeTemplate" + "name": "executeSchedule" }, { - "name": "executeViewQueryFn" + "name": "executeTemplate" }, { - "name": "flattenUnsubscriptionErrors" + "name": "executeViewQueryFn" }, { "name": "forEachSingleProvider" @@ -558,7 +555,7 @@ "name": "from" }, { - "name": "fromArray" + "name": "fromAsyncIterable" }, { "name": "generateInitialInputs" @@ -663,7 +660,13 @@ "name": "getTNodeFromLView" }, { - "name": "hostReportError" + "name": "handleReset" + }, + { + "name": "handleStoppedNotification" + }, + { + "name": "handleUnhandledError" }, { "name": "icuContainerIterate" @@ -699,7 +702,7 @@ "name": "injectableDefOrInjectorDefFactory" }, { - "name": "innerSubscribe" + "name": "innerFrom" }, { "name": "insertBloom" @@ -714,10 +717,10 @@ "name": "invokeHostBindingsInCreationMode" }, { - "name": "isArray" + "name": "isArrayLike" }, { - "name": "isArrayLike" + "name": "isAsyncIterable" }, { "name": "isComponentDef" @@ -732,13 +735,16 @@ "name": "isInlineTemplate" }, { - "name": "isLContainer" + "name": "isInteropObservable" }, { - "name": "isLView" + "name": "isIterable" }, { - "name": "isObject" + "name": "isLContainer" + }, + { + "name": "isLView" }, { "name": "isPositive" @@ -750,11 +756,14 @@ "name": "isPromise2" }, { - "name": "isScheduler" + "name": "isReadableStreamLike" }, { "name": "isStableFactory" }, + { + "name": "isSubscription" + }, { "name": "isTypeProvider" }, @@ -764,6 +773,9 @@ { "name": "iterator" }, + { + "name": "last" + }, { "name": "leaveDI" }, @@ -782,9 +794,6 @@ { "name": "makeRecord" }, - { - "name": "map" - }, { "name": "markAncestorsForTraversal" }, @@ -797,9 +806,6 @@ { "name": "maybeWrapInNotSelector" }, - { - "name": "mergeAll" - }, { "name": "mergeHostAttribute" }, @@ -824,18 +830,27 @@ { "name": "noop" }, + { + "name": "noop2" + }, { "name": "notFoundValueOrThrow" }, { "name": "observable" }, + { + "name": "observeOn" + }, { "name": "onEnter" }, { "name": "onLeave" }, + { + "name": "operate" + }, { "name": "optionsReducer" }, @@ -845,6 +860,9 @@ { "name": "platformCore" }, + { + "name": "popScheduler" + }, { "name": "processInjectorTypesWithProviders" }, @@ -858,7 +876,7 @@ "name": "profiler" }, { - "name": "refCount" + "name": "readableStreamLikeToAsyncGenerator" }, { "name": "refreshContentQueries" @@ -881,6 +899,9 @@ { "name": "renderView" }, + { + "name": "reportUnhandledError" + }, { "name": "requiresRefreshOrTraversal" }, @@ -893,14 +914,11 @@ { "name": "retrieveHydrationInfo" }, - { - "name": "rxSubscriber" - }, { "name": "saveNameToExportMap" }, { - "name": "scheduleArray" + "name": "scheduleAsyncIterable" }, { "name": "searchTokensOnInjector" @@ -932,9 +950,6 @@ { "name": "setUpAttributes" }, - { - "name": "shareSubjectFactory" - }, { "name": "shouldSearchParent" }, @@ -945,16 +960,13 @@ "name": "stringifyCSSSelector" }, { - "name": "subscribeTo" - }, - { - "name": "subscribeToArray" + "name": "subscribeOn" }, { - "name": "switchMap" + "name": "throwProviderNotFoundError" }, { - "name": "throwProviderNotFoundError" + "name": "timeoutProvider" }, { "name": "toRefArray" diff --git a/packages/core/test/bundling/hydration/bundle.golden_symbols.json b/packages/core/test/bundling/hydration/bundle.golden_symbols.json index f39ba4ebdf4a9..c8969c443dd5d 100644 --- a/packages/core/test/bundling/hydration/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hydration/bundle.golden_symbols.json @@ -23,9 +23,6 @@ { "name": "ApplicationRef" }, - { - "name": "ArgumentOutOfRangeError" - }, { "name": "BLOOM_BUCKET_BITS" }, @@ -53,6 +50,9 @@ { "name": "CIRCULAR" }, + { + "name": "COMPLETE_NOTIFICATION" + }, { "name": "COMPONENT_REGEX" }, @@ -90,10 +90,7 @@ "name": "ComponentRef2" }, { - "name": "ConnectableObservable" - }, - { - "name": "ConnectableSubscriber" + "name": "ConsumerObserver" }, { "name": "DEFAULT_APP_ID" @@ -114,34 +111,28 @@ "name": "DefaultDomRenderer2" }, { - "name": "DefaultIfEmptyOperator" - }, - { - "name": "DefaultIfEmptySubscriber" - }, - { - "name": "DistinctUntilChangedOperator" + "name": "DomAdapter" }, { - "name": "DistinctUntilChangedSubscriber" + "name": "DomRendererFactory2" }, { - "name": "DoOperator" + "name": "EMPTY" }, { - "name": "DomAdapter" + "name": "EMPTY_ARRAY" }, { - "name": "DomRendererFactory2" + "name": "EMPTY_OBJ" }, { - "name": "EMPTY_ARRAY" + "name": "EMPTY_OBSERVER" }, { - "name": "EMPTY_OBJ" + "name": "EMPTY_PAYLOAD" }, { - "name": "EMPTY_PAYLOAD" + "name": "EMPTY_SUBSCRIPTION" }, { "name": "ENVIRONMENT_INITIALIZER" @@ -176,12 +167,6 @@ { "name": "EventManagerPlugin" }, - { - "name": "FilterOperator" - }, - { - "name": "FilterSubscriber" - }, { "name": "GenericBrowserDomAdapter" }, @@ -263,18 +248,6 @@ { "name": "MULTIPLIER" }, - { - "name": "MapOperator" - }, - { - "name": "MapSubscriber" - }, - { - "name": "MergeMapOperator" - }, - { - "name": "MergeMapSubscriber" - }, { "name": "NAMESPACE_URIS" }, @@ -377,6 +350,9 @@ { "name": "Observable" }, + { + "name": "OperatorSubscriber" + }, { "name": "PLATFORM_DESTROY_LISTENERS" }, @@ -410,12 +386,6 @@ { "name": "RESPONSE_TYPE" }, - { - "name": "RefCountOperator" - }, - { - "name": "RefCountSubscriber" - }, { "name": "RendererFactory2" }, @@ -455,36 +425,18 @@ { "name": "SimpleChange" }, - { - "name": "SimpleInnerSubscriber" - }, - { - "name": "SimpleOuterSubscriber" - }, { "name": "StandaloneService" }, { "name": "Subject" }, - { - "name": "SubjectSubscriber" - }, - { - "name": "SubjectSubscription" - }, { "name": "Subscriber" }, { "name": "Subscription" }, - { - "name": "SwitchMapOperator" - }, - { - "name": "SwitchMapSubscriber" - }, { "name": "TESTABILITY" }, @@ -497,21 +449,6 @@ { "name": "TYPE" }, - { - "name": "TakeOperator" - }, - { - "name": "TakeSubscriber" - }, - { - "name": "TapSubscriber" - }, - { - "name": "ThrowIfEmptyOperator" - }, - { - "name": "ThrowIfEmptySubscriber" - }, { "name": "TransferState" }, @@ -542,6 +479,12 @@ { "name": "_NullComponentFactoryResolver" }, + { + "name": "__asyncValues" + }, + { + "name": "__await" + }, { "name": "__forward_ref__" }, @@ -549,10 +492,10 @@ "name": "_applyRootElementTransformImpl" }, { - "name": "_currentInjector" + "name": "_bind" }, { - "name": "_enable_super_gross_mode_that_will_cause_bad_things" + "name": "_currentInjector" }, { "name": "_getInsertInFrontOfRNodeWithI18n" @@ -617,6 +560,9 @@ { "name": "applyView" }, + { + "name": "arrRemove" + }, { "name": "assertConsumerNode" }, @@ -626,6 +572,9 @@ { "name": "baseElement" }, + { + "name": "bind" + }, { "name": "bloomHasToken" }, @@ -671,15 +620,15 @@ { "name": "configureViewWithDirective" }, - { - "name": "connectableObservableDescriptor" - }, { "name": "consumerIsLive" }, { "name": "consumerPollProducersForChange" }, + { + "name": "context" + }, { "name": "convertToBitFlags" }, @@ -689,9 +638,15 @@ { "name": "createElementRef" }, + { + "name": "createErrorClass" + }, { "name": "createInjector" }, + { + "name": "createInvalidObservableTypeError" + }, { "name": "createLFrame" }, @@ -701,6 +656,12 @@ { "name": "createNodeInjector" }, + { + "name": "createNotification" + }, + { + "name": "createOperatorSubscriber" + }, { "name": "createTView" }, @@ -713,6 +674,9 @@ { "name": "deepForEachProvider" }, + { + "name": "defaultCompare" + }, { "name": "defaultErrorFactory" }, @@ -737,12 +701,6 @@ { "name": "diPublicInInjector" }, - { - "name": "empty" - }, - { - "name": "empty3" - }, { "name": "enableHydrationRuntimeSupport" }, @@ -755,12 +713,21 @@ { "name": "epoch" }, + { + "name": "errorContext" + }, + { + "name": "execFinalizer" + }, { "name": "executeCheckHooks" }, { "name": "executeInitAndCheckHooks" }, + { + "name": "executeSchedule" + }, { "name": "executeTemplate" }, @@ -773,12 +740,6 @@ { "name": "extractDirectiveDef" }, - { - "name": "first" - }, - { - "name": "flattenUnsubscriptionErrors" - }, { "name": "forEachSingleProvider" }, @@ -792,7 +753,7 @@ "name": "from" }, { - "name": "fromArray" + "name": "fromAsyncIterable" }, { "name": "generateInitialInputs" @@ -920,6 +881,15 @@ { "name": "getTNodeFromLView" }, + { + "name": "handleReset" + }, + { + "name": "handleStoppedNotification" + }, + { + "name": "handleUnhandledError" + }, { "name": "hasInSkipHydrationBlockFlag" }, @@ -929,9 +899,6 @@ { "name": "hasSkipHydrationAttrOnTNode" }, - { - "name": "hostReportError" - }, { "name": "icuContainerIterate" }, @@ -969,7 +936,7 @@ "name": "injectableDefOrInjectorDefFactory" }, { - "name": "innerSubscribe" + "name": "innerFrom" }, { "name": "insertBloom" @@ -987,10 +954,10 @@ "name": "invokeHostBindingsInCreationMode" }, { - "name": "isArray" + "name": "isArrayLike" }, { - "name": "isArrayLike" + "name": "isAsyncIterable" }, { "name": "isComponentDef" @@ -1017,13 +984,16 @@ "name": "isInlineTemplate" }, { - "name": "isLContainer" + "name": "isInteropObservable" }, { - "name": "isLView" + "name": "isIterable" }, { - "name": "isObject" + "name": "isLContainer" + }, + { + "name": "isLView" }, { "name": "isPlatformBrowser" @@ -1041,14 +1011,17 @@ "name": "isPromise2" }, { - "name": "isRootView" + "name": "isReadableStreamLike" }, { - "name": "isScheduler" + "name": "isRootView" }, { "name": "isStableFactory" }, + { + "name": "isSubscription" + }, { "name": "isTemplateNode" }, @@ -1061,6 +1034,9 @@ { "name": "iterator" }, + { + "name": "last" + }, { "name": "lastNodeWasCreated" }, @@ -1091,9 +1067,6 @@ { "name": "makeRecord" }, - { - "name": "map" - }, { "name": "markAncestorsForTraversal" }, @@ -1109,9 +1082,6 @@ { "name": "maybeWrapInNotSelector" }, - { - "name": "mergeAll" - }, { "name": "mergeHostAttribute" }, @@ -1160,6 +1130,9 @@ { "name": "observable" }, + { + "name": "observeOn" + }, { "name": "of" }, @@ -1169,9 +1142,15 @@ { "name": "onLeave" }, + { + "name": "operate" + }, { "name": "performanceMarkFeature" }, + { + "name": "popScheduler" + }, { "name": "populateDehydratedViewsInLContainerImpl" }, @@ -1191,7 +1170,7 @@ "name": "provideZoneChangeDetection" }, { - "name": "refCount" + "name": "readableStreamLikeToAsyncGenerator" }, { "name": "refreshContentQueries" @@ -1220,6 +1199,9 @@ { "name": "renderView" }, + { + "name": "reportUnhandledError" + }, { "name": "requiresRefreshOrTraversal" }, @@ -1235,14 +1217,11 @@ { "name": "retrieveHydrationInfoImpl" }, - { - "name": "rxSubscriber" - }, { "name": "saveNameToExportMap" }, { - "name": "scheduleArray" + "name": "scheduleAsyncIterable" }, { "name": "searchTokensOnInjector" @@ -1280,9 +1259,6 @@ { "name": "setUpAttributes" }, - { - "name": "shareSubjectFactory" - }, { "name": "shimStylesContent" }, @@ -1299,13 +1275,7 @@ "name": "stringifyCSSSelector" }, { - "name": "subscribeTo" - }, - { - "name": "subscribeToArray" - }, - { - "name": "switchMap" + "name": "subscribeOn" }, { "name": "throwIfEmpty" @@ -1313,6 +1283,9 @@ { "name": "throwProviderNotFoundError" }, + { + "name": "timeoutProvider" + }, { "name": "toRefArray" }, diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index d780911f106e2..76dfcabed1825 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -44,9 +44,6 @@ { "name": "ApplyRedirects" }, - { - "name": "ArgumentOutOfRangeError" - }, { "name": "BLOOM_BUCKET_BITS" }, @@ -80,6 +77,9 @@ { "name": "CIRCULAR" }, + { + "name": "COMPLETE_NOTIFICATION" + }, { "name": "COMPONENT_REGEX" }, @@ -98,12 +98,6 @@ { "name": "CanDeactivate" }, - { - "name": "CatchOperator" - }, - { - "name": "CatchSubscriber" - }, { "name": "ChainedInjector" }, @@ -122,12 +116,6 @@ { "name": "ChildrenOutletContexts" }, - { - "name": "CombineLatestOperator" - }, - { - "name": "CombineLatestSubscriber" - }, { "name": "Compiler" }, @@ -153,10 +141,10 @@ "name": "ConnectableObservable" }, { - "name": "ConnectableSubscriber" + "name": "Console" }, { - "name": "Console" + "name": "ConsumerObserver" }, { "name": "DEFAULT_APP_ID" @@ -173,12 +161,6 @@ { "name": "DefaultDomRenderer2" }, - { - "name": "DefaultIfEmptyOperator" - }, - { - "name": "DefaultIfEmptySubscriber" - }, { "name": "DefaultRouteReuseStrategy" }, @@ -191,15 +173,6 @@ { "name": "DefaultUrlSerializer" }, - { - "name": "DistinctUntilChangedOperator" - }, - { - "name": "DistinctUntilChangedSubscriber" - }, - { - "name": "DoOperator" - }, { "name": "DomAdapter" }, @@ -215,9 +188,15 @@ { "name": "EMPTY_OBJ" }, + { + "name": "EMPTY_OBSERVER" + }, { "name": "EMPTY_PAYLOAD" }, + { + "name": "EMPTY_SUBSCRIPTION" + }, { "name": "ENVIRONMENT_INITIALIZER" }, @@ -254,18 +233,6 @@ { "name": "EventType" }, - { - "name": "FilterOperator" - }, - { - "name": "FilterSubscriber" - }, - { - "name": "FinallyOperator" - }, - { - "name": "FinallySubscriber" - }, { "name": "GenericBrowserDomAdapter" }, @@ -314,9 +281,6 @@ { "name": "Injector" }, - { - "name": "InnerSubscriber" - }, { "name": "ItemComponent" }, @@ -359,24 +323,6 @@ { "name": "MOVED_VIEWS" }, - { - "name": "MapOperator" - }, - { - "name": "MapSubscriber" - }, - { - "name": "MapToOperator" - }, - { - "name": "MapToSubscriber" - }, - { - "name": "MergeMapOperator" - }, - { - "name": "MergeMapSubscriber" - }, { "name": "ModuleWithComponentFactories" }, @@ -428,9 +374,6 @@ { "name": "NG_TEMPLATE_SELECTOR" }, - { - "name": "NONE" - }, { "name": "NOT_FOUND" }, @@ -525,7 +468,7 @@ "name": "Observable" }, { - "name": "OuterSubscriber" + "name": "OperatorSubscriber" }, { "name": "OutletContext" @@ -602,12 +545,6 @@ { "name": "RedirectRequest" }, - { - "name": "RefCountOperator" - }, - { - "name": "RefCountSubscriber" - }, { "name": "Renderer2" }, @@ -683,12 +620,6 @@ { "name": "Sanitizer" }, - { - "name": "ScanOperator" - }, - { - "name": "ScanSubscriber" - }, { "name": "SecurityContext" }, @@ -701,12 +632,6 @@ { "name": "SimpleChange" }, - { - "name": "SimpleInnerSubscriber" - }, - { - "name": "SimpleOuterSubscriber" - }, { "name": "StandaloneService" }, @@ -716,24 +641,12 @@ { "name": "Subject" }, - { - "name": "SubjectSubscriber" - }, - { - "name": "SubjectSubscription" - }, { "name": "Subscriber" }, { "name": "Subscription" }, - { - "name": "SwitchMapOperator" - }, - { - "name": "SwitchMapSubscriber" - }, { "name": "TESTABILITY" }, @@ -755,36 +668,9 @@ { "name": "TYPE" }, - { - "name": "TakeLastOperator" - }, - { - "name": "TakeLastSubscriber" - }, - { - "name": "TakeOperator" - }, - { - "name": "TakeSubscriber" - }, - { - "name": "TakeUntilOperator" - }, - { - "name": "TakeUntilSubscriber" - }, - { - "name": "TapSubscriber" - }, { "name": "TemplateRef" }, - { - "name": "ThrowIfEmptyOperator" - }, - { - "name": "ThrowIfEmptySubscriber" - }, { "name": "Title" }, @@ -851,6 +737,12 @@ { "name": "_NullComponentFactoryResolver" }, + { + "name": "__asyncValues" + }, + { + "name": "__await" + }, { "name": "__forward_ref__" }, @@ -858,10 +750,10 @@ "name": "_applyRootElementTransformImpl" }, { - "name": "_currentInjector" + "name": "_bind" }, { - "name": "_enable_super_gross_mode_that_will_cause_bad_things" + "name": "_currentInjector" }, { "name": "_ensureDirtyViewsAreAlwaysReachable" @@ -953,6 +845,9 @@ { "name": "applyView" }, + { + "name": "arrRemove" + }, { "name": "assertConsumerNode" }, @@ -962,6 +857,9 @@ { "name": "baseElement" }, + { + "name": "bind" + }, { "name": "bindingUpdated" }, @@ -1025,9 +923,6 @@ { "name": "configureViewWithDirective" }, - { - "name": "connectableObservableDescriptor" - }, { "name": "consumerIsLive" }, @@ -1043,6 +938,9 @@ { "name": "containsTree" }, + { + "name": "context" + }, { "name": "convertToBitFlags" }, @@ -1067,12 +965,18 @@ { "name": "createEnvironmentInjector" }, + { + "name": "createErrorClass" + }, { "name": "createInjector" }, { "name": "createInjectorWithoutInjectorInstances" }, + { + "name": "createInvalidObservableTypeError" + }, { "name": "createLFrame" }, @@ -1091,6 +995,12 @@ { "name": "createNodeInjector" }, + { + "name": "createNotification" + }, + { + "name": "createOperatorSubscriber" + }, { "name": "createResultForNode" }, @@ -1124,11 +1034,14 @@ { "name": "deepForEachProvider" }, + { + "name": "defaultCompare" + }, { "name": "defaultErrorFactory" }, { - "name": "defaultErrorHandler" + "name": "defaultErrorHandler2" }, { "name": "defaultIfEmpty" @@ -1166,18 +1079,6 @@ { "name": "diPublicInInjector" }, - { - "name": "dispatch" - }, - { - "name": "empty" - }, - { - "name": "empty2" - }, - { - "name": "empty3" - }, { "name": "emptyPathMatch" }, @@ -1208,9 +1109,15 @@ { "name": "equalPath" }, + { + "name": "errorContext" + }, { "name": "exactMatchOptions" }, + { + "name": "execFinalizer" + }, { "name": "executeCheckHooks" }, @@ -1223,6 +1130,9 @@ { "name": "executeListenerWithErrorHandling" }, + { + "name": "executeSchedule" + }, { "name": "executeTemplate" }, @@ -1256,9 +1166,6 @@ { "name": "flattenRouteTree" }, - { - "name": "flattenUnsubscriptionErrors" - }, { "name": "forEachSingleProvider" }, @@ -1272,7 +1179,7 @@ "name": "from" }, { - "name": "fromArray" + "name": "fromAsyncIterable" }, { "name": "generateInitialInputs" @@ -1466,12 +1373,24 @@ { "name": "handleError" }, + { + "name": "handleReset" + }, + { + "name": "handleStoppedNotification" + }, + { + "name": "handleUnhandledError" + }, { "name": "hasEmptyPathConfig" }, { "name": "hasInSkipHydrationBlockFlag" }, + { + "name": "hasLift" + }, { "name": "hasParentInjector" }, @@ -1481,9 +1400,6 @@ { "name": "hasTagAndTypeMatch" }, - { - "name": "hostReportError" - }, { "name": "icuContainerIterate" }, @@ -1533,7 +1449,7 @@ "name": "injectableDefOrInjectorDefFactory" }, { - "name": "innerSubscribe" + "name": "innerFrom" }, { "name": "insertBloom" @@ -1554,10 +1470,10 @@ "name": "invokeHostBindingsInCreationMode" }, { - "name": "isArray" + "name": "isArrayLike" }, { - "name": "isArrayLike" + "name": "isAsyncIterable" }, { "name": "isCommandWithOutlets" @@ -1598,6 +1514,12 @@ { "name": "isInlineTemplate" }, + { + "name": "isInteropObservable" + }, + { + "name": "isIterable" + }, { "name": "isLContainer" }, @@ -1619,9 +1541,6 @@ { "name": "isNodeMatchingSelectorList" }, - { - "name": "isObject" - }, { "name": "isPlatformServer" }, @@ -1635,11 +1554,14 @@ "name": "isPromise2" }, { - "name": "isScheduler" + "name": "isReadableStreamLike" }, { "name": "isStableFactory" }, + { + "name": "isSubscription" + }, { "name": "isTemplateNode" }, @@ -1659,7 +1581,10 @@ "name": "joinWithSlash" }, { - "name": "last2" + "name": "last" + }, + { + "name": "last3" }, { "name": "lastNodeWasCreated" @@ -1721,6 +1646,9 @@ { "name": "matrixParamsMatch" }, + { + "name": "maybeSchedule" + }, { "name": "maybeUnwrapDefaultExport" }, @@ -1802,6 +1730,9 @@ { "name": "observable" }, + { + "name": "observeOn" + }, { "name": "of" }, @@ -1811,6 +1742,9 @@ { "name": "onLeave" }, + { + "name": "operate" + }, { "name": "paramCompareMap" }, @@ -1823,6 +1757,9 @@ { "name": "policy" }, + { + "name": "popScheduler" + }, { "name": "prioritizedGuardValue" }, @@ -1841,6 +1778,9 @@ { "name": "provideZoneChangeDetection" }, + { + "name": "readableStreamLikeToAsyncGenerator" + }, { "name": "redirectIfUrlTree" }, @@ -1880,6 +1820,9 @@ { "name": "replaceSegment" }, + { + "name": "reportUnhandledError" + }, { "name": "requiresRefreshOrTraversal" }, @@ -1901,14 +1844,11 @@ { "name": "runInInjectionContext" }, - { - "name": "rxSubscriber" - }, { "name": "saveNameToExportMap" }, { - "name": "scheduleArray" + "name": "scheduleAsyncIterable" }, { "name": "searchTokensOnInjector" @@ -1973,9 +1913,6 @@ { "name": "shallowEqual" }, - { - "name": "shareSubjectFactory" - }, { "name": "shimStylesContent" }, @@ -2007,13 +1944,7 @@ "name": "stripTrailingSlash" }, { - "name": "subscribeTo" - }, - { - "name": "subscribeToArray" - }, - { - "name": "subscribeToResult" + "name": "subscribeOn" }, { "name": "subsetMatchOptions" @@ -2045,6 +1976,9 @@ { "name": "throwProviderNotFoundError" }, + { + "name": "timeoutProvider" + }, { "name": "toRefArray" }, @@ -2099,6 +2033,15 @@ { "name": "writeToDirectiveInput" }, + { + "name": "{getPrototypeOf:getPrototypeOf,prototype:objectProto,keys:getKeys}" + }, + { + "name": "{isArray:isArray2}" + }, + { + "name": "{isArray:isArray}" + }, { "name": "ɵEmptyOutletComponent" }, diff --git a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json index 789274a735058..dcb30854fe202 100644 --- a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json +++ b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json @@ -41,6 +41,9 @@ { "name": "CIRCULAR" }, + { + "name": "COMPLETE_NOTIFICATION" + }, { "name": "COMPONENT_REGEX" }, @@ -75,10 +78,7 @@ "name": "ComponentRef2" }, { - "name": "ConnectableObservable" - }, - { - "name": "ConnectableSubscriber" + "name": "ConsumerObserver" }, { "name": "DEFAULT_APP_ID" @@ -92,27 +92,30 @@ { "name": "DefaultDomRenderer2" }, - { - "name": "DistinctUntilChangedOperator" - }, - { - "name": "DistinctUntilChangedSubscriber" - }, { "name": "DomAdapter" }, { "name": "DomRendererFactory2" }, + { + "name": "EMPTY" + }, { "name": "EMPTY_ARRAY" }, { "name": "EMPTY_OBJ" }, + { + "name": "EMPTY_OBSERVER" + }, { "name": "EMPTY_PAYLOAD" }, + { + "name": "EMPTY_SUBSCRIPTION" + }, { "name": "ENVIRONMENT_INITIALIZER" }, @@ -194,18 +197,6 @@ { "name": "MOVED_VIEWS" }, - { - "name": "MapOperator" - }, - { - "name": "MapSubscriber" - }, - { - "name": "MergeMapOperator" - }, - { - "name": "MergeMapSubscriber" - }, { "name": "NAMESPACE_URIS" }, @@ -293,6 +284,9 @@ { "name": "Observable" }, + { + "name": "OperatorSubscriber" + }, { "name": "PLATFORM_DESTROY_LISTENERS" }, @@ -314,12 +308,6 @@ { "name": "REMOVE_STYLES_ON_COMPONENT_DESTROY" }, - { - "name": "RefCountOperator" - }, - { - "name": "RefCountSubscriber" - }, { "name": "RendererFactory2" }, @@ -347,36 +335,18 @@ { "name": "SimpleChange" }, - { - "name": "SimpleInnerSubscriber" - }, - { - "name": "SimpleOuterSubscriber" - }, { "name": "StandaloneService" }, { "name": "Subject" }, - { - "name": "SubjectSubscriber" - }, - { - "name": "SubjectSubscription" - }, { "name": "Subscriber" }, { "name": "Subscription" }, - { - "name": "SwitchMapOperator" - }, - { - "name": "SwitchMapSubscriber" - }, { "name": "TESTABILITY" }, @@ -413,6 +383,12 @@ { "name": "_NullComponentFactoryResolver" }, + { + "name": "__asyncValues" + }, + { + "name": "__await" + }, { "name": "__forward_ref__" }, @@ -420,10 +396,10 @@ "name": "_applyRootElementTransformImpl" }, { - "name": "_currentInjector" + "name": "_bind" }, { - "name": "_enable_super_gross_mode_that_will_cause_bad_things" + "name": "_currentInjector" }, { "name": "_getInsertInFrontOfRNodeWithI18n" @@ -485,6 +461,9 @@ { "name": "applyView" }, + { + "name": "arrRemove" + }, { "name": "assertConsumerNode" }, @@ -494,6 +473,9 @@ { "name": "baseElement" }, + { + "name": "bind" + }, { "name": "bloomHasToken" }, @@ -527,24 +509,30 @@ { "name": "configureViewWithDirective" }, - { - "name": "connectableObservableDescriptor" - }, { "name": "consumerIsLive" }, { "name": "consumerPollProducersForChange" }, + { + "name": "context" + }, { "name": "convertToBitFlags" }, { "name": "createElementRef" }, + { + "name": "createErrorClass" + }, { "name": "createInjector" }, + { + "name": "createInvalidObservableTypeError" + }, { "name": "createLFrame" }, @@ -554,6 +542,12 @@ { "name": "createNodeInjector" }, + { + "name": "createNotification" + }, + { + "name": "createOperatorSubscriber" + }, { "name": "createTView" }, @@ -563,6 +557,9 @@ { "name": "deepForEachProvider" }, + { + "name": "defaultCompare" + }, { "name": "detachMovedView" }, @@ -585,19 +582,19 @@ "name": "diPublicInInjector" }, { - "name": "empty" + "name": "enterDI" }, { - "name": "empty2" + "name": "enterView" }, { - "name": "enterDI" + "name": "epoch" }, { - "name": "enterView" + "name": "errorContext" }, { - "name": "epoch" + "name": "execFinalizer" }, { "name": "executeCheckHooks" @@ -605,6 +602,9 @@ { "name": "executeInitAndCheckHooks" }, + { + "name": "executeSchedule" + }, { "name": "executeTemplate" }, @@ -617,9 +617,6 @@ { "name": "extractDirectiveDef" }, - { - "name": "flattenUnsubscriptionErrors" - }, { "name": "forEachSingleProvider" }, @@ -633,7 +630,7 @@ "name": "from" }, { - "name": "fromArray" + "name": "fromAsyncIterable" }, { "name": "generateInitialInputs" @@ -744,7 +741,13 @@ "name": "getTNodeFromLView" }, { - "name": "hostReportError" + "name": "handleReset" + }, + { + "name": "handleStoppedNotification" + }, + { + "name": "handleUnhandledError" }, { "name": "icuContainerIterate" @@ -780,7 +783,7 @@ "name": "injectableDefOrInjectorDefFactory" }, { - "name": "innerSubscribe" + "name": "innerFrom" }, { "name": "insertBloom" @@ -798,10 +801,10 @@ "name": "invokeHostBindingsInCreationMode" }, { - "name": "isArray" + "name": "isArrayLike" }, { - "name": "isArrayLike" + "name": "isAsyncIterable" }, { "name": "isComponentDef" @@ -816,13 +819,16 @@ "name": "isInlineTemplate" }, { - "name": "isLContainer" + "name": "isInteropObservable" }, { - "name": "isLView" + "name": "isIterable" + }, + { + "name": "isLContainer" }, { - "name": "isObject" + "name": "isLView" }, { "name": "isPlatformServer" @@ -837,11 +843,14 @@ "name": "isPromise2" }, { - "name": "isScheduler" + "name": "isReadableStreamLike" }, { "name": "isStableFactory" }, + { + "name": "isSubscription" + }, { "name": "isTemplateNode" }, @@ -854,6 +863,9 @@ { "name": "iterator" }, + { + "name": "last" + }, { "name": "leaveDI" }, @@ -872,9 +884,6 @@ { "name": "makeRecord" }, - { - "name": "map" - }, { "name": "markAncestorsForTraversal" }, @@ -890,9 +899,6 @@ { "name": "maybeWrapInNotSelector" }, - { - "name": "mergeAll" - }, { "name": "mergeHostAttribute" }, @@ -926,18 +932,30 @@ { "name": "noop" }, + { + "name": "noop2" + }, { "name": "notFoundValueOrThrow" }, { "name": "observable" }, + { + "name": "observeOn" + }, { "name": "onEnter" }, { "name": "onLeave" }, + { + "name": "operate" + }, + { + "name": "popScheduler" + }, { "name": "processInjectorTypesWithProviders" }, @@ -954,7 +972,7 @@ "name": "provideZoneChangeDetection" }, { - "name": "refCount" + "name": "readableStreamLikeToAsyncGenerator" }, { "name": "refreshContentQueries" @@ -977,6 +995,9 @@ { "name": "renderView" }, + { + "name": "reportUnhandledError" + }, { "name": "requiresRefreshOrTraversal" }, @@ -989,14 +1010,11 @@ { "name": "retrieveHydrationInfo" }, - { - "name": "rxSubscriber" - }, { "name": "saveNameToExportMap" }, { - "name": "scheduleArray" + "name": "scheduleAsyncIterable" }, { "name": "searchTokensOnInjector" @@ -1031,9 +1049,6 @@ { "name": "setUpAttributes" }, - { - "name": "shareSubjectFactory" - }, { "name": "shimStylesContent" }, @@ -1047,16 +1062,13 @@ "name": "stringifyCSSSelector" }, { - "name": "subscribeTo" + "name": "subscribeOn" }, { - "name": "subscribeToArray" - }, - { - "name": "switchMap" + "name": "throwProviderNotFoundError" }, { - "name": "throwProviderNotFoundError" + "name": "timeoutProvider" }, { "name": "toRefArray" diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 5bc987d4e3eeb..7b16c8a0c5e3f 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -53,6 +53,9 @@ { "name": "CIRCULAR" }, + { + "name": "COMPLETE_NOTIFICATION" + }, { "name": "COMPONENT_REGEX" }, @@ -90,10 +93,7 @@ "name": "ComponentRef2" }, { - "name": "ConnectableObservable" - }, - { - "name": "ConnectableSubscriber" + "name": "ConsumerObserver" }, { "name": "DEFAULT_APP_ID" @@ -113,12 +113,6 @@ { "name": "DefaultIterableDifferFactory" }, - { - "name": "DistinctUntilChangedOperator" - }, - { - "name": "DistinctUntilChangedSubscriber" - }, { "name": "DomAdapter" }, @@ -128,15 +122,24 @@ { "name": "DomRendererFactory2" }, + { + "name": "EMPTY" + }, { "name": "EMPTY_ARRAY" }, { "name": "EMPTY_OBJ" }, + { + "name": "EMPTY_OBSERVER" + }, { "name": "EMPTY_PAYLOAD" }, + { + "name": "EMPTY_SUBSCRIPTION" + }, { "name": "ENVIRONMENT_INITIALIZER" }, @@ -221,18 +224,6 @@ { "name": "MOVED_VIEWS" }, - { - "name": "MapOperator" - }, - { - "name": "MapSubscriber" - }, - { - "name": "MergeMapOperator" - }, - { - "name": "MergeMapSubscriber" - }, { "name": "NAMESPACE_URIS" }, @@ -347,6 +338,9 @@ { "name": "Observable" }, + { + "name": "OperatorSubscriber" + }, { "name": "Optional" }, @@ -380,12 +374,6 @@ { "name": "REMOVE_STYLES_ON_COMPONENT_DESTROY" }, - { - "name": "RefCountOperator" - }, - { - "name": "RefCountSubscriber" - }, { "name": "RendererFactory2" }, @@ -416,36 +404,18 @@ { "name": "SimpleChange" }, - { - "name": "SimpleInnerSubscriber" - }, - { - "name": "SimpleOuterSubscriber" - }, { "name": "SkipSelf" }, { "name": "Subject" }, - { - "name": "SubjectSubscriber" - }, - { - "name": "SubjectSubscription" - }, { "name": "Subscriber" }, { "name": "Subscription" }, - { - "name": "SwitchMapOperator" - }, - { - "name": "SwitchMapSubscriber" - }, { "name": "TESTABILITY" }, @@ -542,6 +512,12 @@ { "name": "_NullComponentFactoryResolver" }, + { + "name": "__asyncValues" + }, + { + "name": "__await" + }, { "name": "__forward_ref__" }, @@ -549,10 +525,10 @@ "name": "_applyRootElementTransformImpl" }, { - "name": "_currentInjector" + "name": "_bind" }, { - "name": "_enable_super_gross_mode_that_will_cause_bad_things" + "name": "_currentInjector" }, { "name": "_ensureDirtyViewsAreAlwaysReachable" @@ -644,6 +620,9 @@ { "name": "applyViewChange" }, + { + "name": "arrRemove" + }, { "name": "assertConsumerNode" }, @@ -659,6 +638,9 @@ { "name": "baseElement" }, + { + "name": "bind" + }, { "name": "bindingUpdated" }, @@ -707,15 +689,15 @@ { "name": "configureViewWithDirective" }, - { - "name": "connectableObservableDescriptor" - }, { "name": "consumerIsLive" }, { "name": "consumerPollProducersForChange" }, + { + "name": "context" + }, { "name": "convertToBitFlags" }, @@ -728,12 +710,18 @@ { "name": "createElementRef" }, + { + "name": "createErrorClass" + }, { "name": "createInjector" }, { "name": "createInjectorWithoutInjectorInstances" }, + { + "name": "createInvalidObservableTypeError" + }, { "name": "createLContainer" }, @@ -746,6 +734,12 @@ { "name": "createNodeInjector" }, + { + "name": "createNotification" + }, + { + "name": "createOperatorSubscriber" + }, { "name": "createPlatformFactory" }, @@ -758,6 +752,9 @@ { "name": "deepForEachProvider" }, + { + "name": "defaultCompare" + }, { "name": "defaultIterableDiffersFactory" }, @@ -789,19 +786,19 @@ "name": "diPublicInInjector" }, { - "name": "empty" + "name": "enterDI" }, { - "name": "empty2" + "name": "enterView" }, { - "name": "enterDI" + "name": "epoch" }, { - "name": "enterView" + "name": "errorContext" }, { - "name": "epoch" + "name": "execFinalizer" }, { "name": "executeCheckHooks" @@ -815,6 +812,9 @@ { "name": "executeListenerWithErrorHandling" }, + { + "name": "executeSchedule" + }, { "name": "executeTemplate" }, @@ -833,9 +833,6 @@ { "name": "findStylingValue" }, - { - "name": "flattenUnsubscriptionErrors" - }, { "name": "forEachSingleProvider" }, @@ -849,7 +846,7 @@ "name": "from" }, { - "name": "fromArray" + "name": "fromAsyncIterable" }, { "name": "generateInitialInputs" @@ -1010,6 +1007,15 @@ { "name": "handleError" }, + { + "name": "handleReset" + }, + { + "name": "handleStoppedNotification" + }, + { + "name": "handleUnhandledError" + }, { "name": "hasInSkipHydrationBlockFlag" }, @@ -1019,9 +1025,6 @@ { "name": "hasTagAndTypeMatch" }, - { - "name": "hostReportError" - }, { "name": "icuContainerIterate" }, @@ -1068,7 +1071,7 @@ "name": "injectableDefOrInjectorDefFactory" }, { - "name": "innerSubscribe" + "name": "innerFrom" }, { "name": "insertBloom" @@ -1089,10 +1092,10 @@ "name": "invokeHostBindingsInCreationMode" }, { - "name": "isArray" + "name": "isArrayLike" }, { - "name": "isArrayLike" + "name": "isAsyncIterable" }, { "name": "isComponentDef" @@ -1121,6 +1124,12 @@ { "name": "isInlineTemplate" }, + { + "name": "isInteropObservable" + }, + { + "name": "isIterable" + }, { "name": "isLContainer" }, @@ -1136,9 +1145,6 @@ { "name": "isNodeMatchingSelectorList" }, - { - "name": "isObject" - }, { "name": "isPlatformServer" }, @@ -1152,7 +1158,7 @@ "name": "isPromise2" }, { - "name": "isScheduler" + "name": "isReadableStreamLike" }, { "name": "isStableFactory" @@ -1163,6 +1169,9 @@ { "name": "isStylingValuePresent" }, + { + "name": "isSubscription" + }, { "name": "isTemplateNode" }, @@ -1184,6 +1193,9 @@ { "name": "keyValueArraySet" }, + { + "name": "last" + }, { "name": "lastNodeWasCreated" }, @@ -1208,9 +1220,6 @@ { "name": "makeRecord" }, - { - "name": "map" - }, { "name": "markAncestorsForTraversal" }, @@ -1229,9 +1238,6 @@ { "name": "maybeWrapInNotSelector" }, - { - "name": "mergeAll" - }, { "name": "mergeHostAttribute" }, @@ -1274,18 +1280,27 @@ { "name": "noop" }, + { + "name": "noop2" + }, { "name": "notFoundValueOrThrow" }, { "name": "observable" }, + { + "name": "observeOn" + }, { "name": "onEnter" }, { "name": "onLeave" }, + { + "name": "operate" + }, { "name": "optionsReducer" }, @@ -1295,6 +1310,9 @@ { "name": "platformCore" }, + { + "name": "popScheduler" + }, { "name": "processInjectorTypesWithProviders" }, @@ -1308,7 +1326,7 @@ "name": "profiler" }, { - "name": "refCount" + "name": "readableStreamLikeToAsyncGenerator" }, { "name": "refreshContentQueries" @@ -1337,6 +1355,9 @@ { "name": "renderView" }, + { + "name": "reportUnhandledError" + }, { "name": "requiresRefreshOrTraversal" }, @@ -1352,9 +1373,6 @@ { "name": "retrieveHydrationInfo" }, - { - "name": "rxSubscriber" - }, { "name": "saveNameToExportMap" }, @@ -1362,7 +1380,7 @@ "name": "saveResolvedLocalsInData" }, { - "name": "scheduleArray" + "name": "scheduleAsyncIterable" }, { "name": "searchTokensOnInjector" @@ -1421,9 +1439,6 @@ { "name": "setupStaticAttributes" }, - { - "name": "shareSubjectFactory" - }, { "name": "shimStylesContent" }, @@ -1440,16 +1455,13 @@ "name": "stringifyCSSSelector" }, { - "name": "subscribeTo" + "name": "subscribeOn" }, { - "name": "subscribeToArray" - }, - { - "name": "switchMap" + "name": "throwProviderNotFoundError" }, { - "name": "throwProviderNotFoundError" + "name": "timeoutProvider" }, { "name": "toRefArray" diff --git a/packages/router/src/navigation_transition.ts b/packages/router/src/navigation_transition.ts index 1d05b557ed949..cfed13acbbe58 100644 --- a/packages/router/src/navigation_transition.ts +++ b/packages/router/src/navigation_transition.ts @@ -599,7 +599,7 @@ export class NavigationTransitions { return loaders; }; return combineLatest(loadComponents(t.targetSnapshot!.root)) - .pipe(defaultIfEmpty(), take(1)); + .pipe(defaultIfEmpty(null), take(1)); }), switchTap(() => this.afterPreactivation()), diff --git a/packages/router/test/create_router_state.spec.ts b/packages/router/test/create_router_state.spec.ts index 1622dbeb18779..d515db5cecf16 100644 --- a/packages/router/test/create_router_state.spec.ts +++ b/packages/router/test/create_router_state.spec.ts @@ -154,8 +154,8 @@ async function createState(config: Routes, url: string): Promise result.state)) - .toPromise(); + .pipe(map(result => result.state)) + .toPromise() as Promise; } function checkActivatedRoute( diff --git a/packages/router/test/recognize.spec.ts b/packages/router/test/recognize.spec.ts index 1bf24593125d1..d539fd6016b1f 100644 --- a/packages/router/test/recognize.spec.ts +++ b/packages/router/test/recognize.spec.ts @@ -675,7 +675,7 @@ async function recognize( RootComponent, config, tree(url), paramsInheritanceStrategy, serializer) .recognize() .toPromise(); - return result.state; + return result!.state; } function checkActivatedRoute( diff --git a/packages/router/test/router_scroller.spec.ts b/packages/router/test/router_scroller.spec.ts index 16303d4e7d65f..356c7f1470a8b 100644 --- a/packages/router/test/router_scroller.spec.ts +++ b/packages/router/test/router_scroller.spec.ts @@ -40,7 +40,8 @@ describe('RouterScroller', () => { }); function nextScrollEvent(events: Subject): Promise { - return events.pipe(filter((e): e is Scroll => e instanceof Scroll), take(1)).toPromise(); + return events.pipe(filter((e): e is Scroll => e instanceof Scroll), take(1)).toPromise() as + Promise; } describe('scroll to top', () => { diff --git a/packages/service-worker/src/low_level.ts b/packages/service-worker/src/low_level.ts index e3ebfea73b933..9c4606ef2dfb5 100644 --- a/packages/service-worker/src/low_level.ts +++ b/packages/service-worker/src/low_level.ts @@ -193,13 +193,13 @@ export class NgswCommChannel { waitForOperationCompleted(nonce: number): Promise { return this.eventsOfType('OPERATION_COMPLETED') - .pipe(filter(event => event.nonce === nonce), take(1), map(event => { - if (event.result !== undefined) { - return event.result; - } - throw new Error(event.error!); - })) - .toPromise(); + .pipe(filter(event => event.nonce === nonce), take(1), map(event => { + if (event.result !== undefined) { + return event.result; + } + throw new Error(event.error!); + })) + .toPromise() as Promise; } get isEnabled(): boolean { diff --git a/packages/service-worker/src/push.ts b/packages/service-worker/src/push.ts index 92597e6152872..5dc1697cc68d1 100644 --- a/packages/service-worker/src/push.ts +++ b/packages/service-worker/src/push.ts @@ -176,8 +176,8 @@ export class SwPush { return this.pushManager.pipe(switchMap(pm => pm.subscribe(pushOptions)), take(1)) .toPromise() .then(sub => { - this.subscriptionChanges.next(sub); - return sub; + this.subscriptionChanges.next(sub!); + return sub!; }); } diff --git a/packages/service-worker/test/integration_spec.ts b/packages/service-worker/test/integration_spec.ts index 83c6f31aeeacd..2016fd71dfea2 100644 --- a/packages/service-worker/test/integration_spec.ts +++ b/packages/service-worker/test/integration_spec.ts @@ -31,7 +31,7 @@ const dist = new MockFileSystemBuilder().addFile('/only.txt', 'this is only').bu const distUpdate = new MockFileSystemBuilder().addFile('/only.txt', 'this is only v2').build(); function obsToSinglePromise(obs: Observable): Promise { - return obs.pipe(take(1)).toPromise(); + return obs.pipe(take(1)).toPromise() as Promise; } const manifest: Manifest = { diff --git a/yarn.lock b/yarn.lock index 6ac680e95886b..af464736b8c3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14845,20 +14845,13 @@ rx@4.1.0: resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" integrity sha512-CiaiuN6gapkdl+cZUr67W6I8jquN4lkak3vtIsIWCl4XIPP8ffsoyN6/+PuGXnQy8Cu8W2y9Xxh31Rq4M6wUug== -rxjs@7.8.1, rxjs@^7.2.0, rxjs@^7.5.5, rxjs@^7.8.1: +rxjs@7.8.1, rxjs@^7.0.0, rxjs@^7.2.0, rxjs@^7.5.5, rxjs@^7.8.1: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" -rxjs@^6.6.7: - version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -16312,7 +16305,7 @@ tslib@2.6.2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3. resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== -tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.13.0, tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== From dda656e054b1ba7d6eb462be93e9366b5b9e9bb7 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Thu, 14 Dec 2023 16:22:56 -0800 Subject: [PATCH 17/27] refactor(compiler): Make attribute const collection less aggressive (#53580) Changes template pipeline to be less aggressive in const collecting attrs, to match the behavior of template definition builder. There is nothing wrong with the more aggressive const collection, and in fact it would be good to re-enable it later, but for now this makes it easier to transition from TDB to template pipeline. Also adds a test to verify that sensitive iframe attributes are properly validated. PR Close #53580 --- .../elements/GOLDEN_PARTIAL.js | 34 +++++++++++++++++++ .../elements/TEST_CASES.json | 11 ++++++ .../elements/iframe_attrs.js | 8 +++++ .../elements/iframe_attrs.ts | 11 ++++++ .../attribute_bindings/TEST_CASES.json | 20 ++--------- ...ultiple_bindings_for_multiple_elements.js} | 0 ...for_multiple_elements_template.pipeline.js | 11 ------ ..._multiple_bindings_with_child_elements.js} | 0 ...s_with_child_elements_template.pipeline.js | 9 ----- ..._bindings_from_consts_template.pipeline.js | 2 +- .../src/phases/attribute_extraction.ts | 16 ++------- 11 files changed, 71 insertions(+), 51 deletions(-) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/iframe_attrs.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/iframe_attrs.ts rename packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/{chain_multiple_bindings_for_multiple_elements_template.js => chain_multiple_bindings_for_multiple_elements.js} (100%) delete mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_for_multiple_elements_template.pipeline.js rename packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/{chain_multiple_bindings_with_child_elements_template.js => chain_multiple_bindings_with_child_elements.js} (100%) delete mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_with_child_elements_template.pipeline.js diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/GOLDEN_PARTIAL.js index bd4e8dc8af3b3..94682744bf1ee 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/GOLDEN_PARTIAL.js @@ -684,3 +684,37 @@ export declare class MyModule { static ɵinj: i0.ɵɵInjectorDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: iframe_attrs.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { + constructor() { + this.fullscreen = 'false'; + } +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: ` + + `, isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, decorators: [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + + ` + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: iframe_attrs.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + fullscreen: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/TEST_CASES.json index 8c57978716e20..ea602d35a7c2f 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/TEST_CASES.json @@ -254,6 +254,17 @@ "failureMessage": "Incorrect generated template." } ] + }, + { + "description": "should validate iframe attributes", + "inputFiles": [ + "iframe_attrs.ts" + ], + "expectations": [ + { + "failureMessage": "Incorrect generated template." + } + ] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/iframe_attrs.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/iframe_attrs.js new file mode 100644 index 0000000000000..42d1bdb67035d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/iframe_attrs.js @@ -0,0 +1,8 @@ +consts: [["allow", "camera 'none'"]], +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵelement(0, "iframe", 0); + } if (rf & 2) { + i0.ɵɵattribute("fetchpriority", "low", i0.ɵɵvalidateIframeAttribute)("allowfullscreen", ctx.fullscreen, i0.ɵɵvalidateIframeAttribute); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/iframe_attrs.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/iframe_attrs.ts new file mode 100644 index 0000000000000..302c0805291b2 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/iframe_attrs.ts @@ -0,0 +1,11 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + + ` +}) +export class MyComponent { + fullscreen = 'false'; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/TEST_CASES.json index 2c748833ac192..b028b3300da8c 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/TEST_CASES.json @@ -64,14 +64,7 @@ ], "expectations": [ { - "failureMessage": "Incorrect template", - "files": [ - { - "expected": "chain_multiple_bindings_for_multiple_elements_template.js", - "templatePipelineExpected": "chain_multiple_bindings_for_multiple_elements_template.pipeline.js", - "generated": "chain_multiple_bindings_for_multiple_elements.js" - } - ] + "failureMessage": "Incorrect template" } ] }, @@ -82,14 +75,7 @@ ], "expectations": [ { - "failureMessage": "Incorrect template", - "files": [ - { - "expected": "chain_multiple_bindings_with_child_elements_template.js", - "templatePipelineExpected": "chain_multiple_bindings_with_child_elements_template.pipeline.js", - "generated": "chain_multiple_bindings_with_child_elements.js" - } - ] + "failureMessage": "Incorrect template" } ] }, @@ -126,4 +112,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_for_multiple_elements_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_for_multiple_elements.js similarity index 100% rename from packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_for_multiple_elements_template.js rename to packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_for_multiple_elements.js diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_for_multiple_elements_template.pipeline.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_for_multiple_elements_template.pipeline.js deleted file mode 100644 index df9eca0a1bb89..0000000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_for_multiple_elements_template.pipeline.js +++ /dev/null @@ -1,11 +0,0 @@ -template: function MyComponent_Template(rf, ctx) { - … - if (rf & 2) { - $r3$.ɵɵattribute("title", ctx.myTitle)("id", ctx.buttonId)("tabindex", 1); - $r3$.ɵɵadvance(1); - $r3$.ɵɵattribute("id", 1)("some-attr", 1 + 2); - $r3$.ɵɵadvance(1); - $r3$.ɵɵattribute("some-other-attr", 2); - } - } - \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_with_child_elements_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_with_child_elements.js similarity index 100% rename from packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_with_child_elements_template.js rename to packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_with_child_elements.js diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_with_child_elements_template.pipeline.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_with_child_elements_template.pipeline.js deleted file mode 100644 index f66da81600258..0000000000000 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/chain_multiple_bindings_with_child_elements_template.pipeline.js +++ /dev/null @@ -1,9 +0,0 @@ -template: function MyComponent_Template(rf, ctx) { - … - if (rf & 2) { - $r3$.ɵɵattribute("title", ctx.myTitle)("id", ctx.buttonId)("tabindex", 1); - $r3$.ɵɵadvance(1); - $r3$.ɵɵattribute("id", 1)("some-attr", 1 + 2); - } - } - \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/exclude_bindings_from_consts_template.pipeline.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/exclude_bindings_from_consts_template.pipeline.js index f6324d1ce4bc9..23723fbca3a8a 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/exclude_bindings_from_consts_template.pipeline.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/exclude_bindings_from_consts_template.pipeline.js @@ -1,3 +1,3 @@ -consts: [["target", "_blank", "aria-label", "link", "foo", "one", "bar", "two", __AttributeMarker.Bindings__, "customEvent", "title", "id"]], +consts: [["target", "_blank", "aria-label", "link", __AttributeMarker.Bindings__, "customEvent", "title", "id"]], … diff --git a/packages/compiler/src/template/pipeline/src/phases/attribute_extraction.ts b/packages/compiler/src/template/pipeline/src/phases/attribute_extraction.ts index 2bc529c408cdc..428b981d1b10f 100644 --- a/packages/compiler/src/template/pipeline/src/phases/attribute_extraction.ts +++ b/packages/compiler/src/template/pipeline/src/phases/attribute_extraction.ts @@ -108,19 +108,9 @@ function extractAttributeOp( let extractable = op.isTextAttribute || op.expression.isConstant(); if (unit.job.compatibility === ir.CompatibilityMode.TemplateDefinitionBuilder) { - // TemplateDefinitionBuilder only extracted attributes that were string literals. - extractable = op.isTextAttribute || ir.isStringLiteral(op.expression); - if (op.name === 'style' || op.name === 'class') { - // For style and class attributes, TemplateDefinitionBuilder only extracted them if they were - // text attributes. For example, `[attr.class]="'my-class'"` was not extracted despite being a - // string literal, because it is not a text attribute. - extractable &&= op.isTextAttribute; - } - if (unit.job.kind === CompilationJobKind.Host) { - // TemplateDefinitionBuilder also does not seem to extract string literals if they are part of - // a host attribute. - extractable &&= op.isTextAttribute; - } + // TemplateDefinitionBuilder only extracts text attributes. It does not extract attriibute + // bindings, even if they are constants. + extractable &&= op.isTextAttribute; } if (extractable) { From aecb675fa5059e510e0223e00fa114d92e799b04 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 15 Dec 2023 11:58:05 +0100 Subject: [PATCH 18/27] fix(core): avoid repeated work when parsing version (#53598) The `Version` class was splitting the same value 3 times instead of doing it once and reusing the result. PR Close #53598 --- packages/core/src/version.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/core/src/version.ts b/packages/core/src/version.ts index 71a5cb88781c8..bc87cf7ae5384 100644 --- a/packages/core/src/version.ts +++ b/packages/core/src/version.ts @@ -17,9 +17,10 @@ export class Version { public readonly patch: string; constructor(public full: string) { - this.major = full.split('.')[0]; - this.minor = full.split('.')[1]; - this.patch = full.split('.').slice(2).join('.'); + const parts = full.split('.'); + this.major = parts[0]; + this.minor = parts[1]; + this.patch = parts.slice(2).join('.'); } } From 872e7f25fea8a01f980e4e406ad382a9e187ce5c Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 15 Dec 2023 12:01:58 +0100 Subject: [PATCH 19/27] fix(core): tree shake version class (#53598) Core bundles were retaining the `Version` class and `VERSION` constant, because we stamp out the current version in the DOM. This shouldn't be necessary, because any usage of `0.0.0-PLACEHOLDER` will be replaced with the current version at build time. These changes remove the reference so it can be tree shaken away. PR Close #53598 --- packages/core/src/render3/component_ref.ts | 4 ++-- .../bundling/animations-standalone/bundle.golden_symbols.json | 3 --- .../core/test/bundling/animations/bundle.golden_symbols.json | 3 --- .../test/bundling/cyclic_import/bundle.golden_symbols.json | 3 --- packages/core/test/bundling/defer/bundle.golden_symbols.json | 3 --- .../test/bundling/forms_reactive/bundle.golden_symbols.json | 3 --- .../bundling/forms_template_driven/bundle.golden_symbols.json | 3 --- .../core/test/bundling/hello_world/bundle.golden_symbols.json | 3 --- .../core/test/bundling/hydration/bundle.golden_symbols.json | 3 --- packages/core/test/bundling/router/bundle.golden_symbols.json | 3 --- .../bundling/standalone_bootstrap/bundle.golden_symbols.json | 3 --- packages/core/test/bundling/todo/bundle.golden_symbols.json | 3 --- 12 files changed, 2 insertions(+), 35 deletions(-) diff --git a/packages/core/src/render3/component_ref.ts b/packages/core/src/render3/component_ref.ts index 5a30f134c248a..40be1e456b450 100644 --- a/packages/core/src/render3/component_ref.ts +++ b/packages/core/src/render3/component_ref.ts @@ -23,7 +23,6 @@ import {NgModuleRef} from '../linker/ng_module_factory'; import {Renderer2, RendererFactory2} from '../render/api'; import {Sanitizer} from '../sanitization/sanitizer'; import {assertDefined, assertGreaterThan, assertIndexInRange} from '../util/assert'; -import {VERSION} from '../version'; import {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from '../view/provider_flags'; import {AfterRenderEventManager} from './after_render_hooks'; @@ -501,7 +500,8 @@ function setRootNodeAttributes( hostRenderer: Renderer2, componentDef: ComponentDef, hostRNode: RElement, rootSelectorOrNode: any) { if (rootSelectorOrNode) { - setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION.full]); + // The placeholder will be replaced with the actual version at build time. + setUpAttributes(hostRenderer, hostRNode, ['ng-version', '0.0.0-PLACEHOLDER']); } else { // If host element is created as a part of this function call (i.e. `rootSelectorOrNode` // is not defined), also apply attributes and classes extracted from component selector. diff --git a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json index a541f0189c1ef..2aae6dac46439 100644 --- a/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json @@ -503,9 +503,6 @@ { "name": "UnsubscriptionError" }, - { - "name": "VERSION" - }, { "name": "ViewEncapsulation" }, diff --git a/packages/core/test/bundling/animations/bundle.golden_symbols.json b/packages/core/test/bundling/animations/bundle.golden_symbols.json index 2822bbf22feba..3c13424e0bfbf 100644 --- a/packages/core/test/bundling/animations/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animations/bundle.golden_symbols.json @@ -560,9 +560,6 @@ { "name": "UnsubscriptionError" }, - { - "name": "VERSION" - }, { "name": "ViewEncapsulation" }, diff --git a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json index b909e3f45e5c4..7c60aee2b6e71 100644 --- a/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json +++ b/packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json @@ -416,9 +416,6 @@ { "name": "UnsubscriptionError" }, - { - "name": "VERSION" - }, { "name": "ViewEncapsulation" }, diff --git a/packages/core/test/bundling/defer/bundle.golden_symbols.json b/packages/core/test/bundling/defer/bundle.golden_symbols.json index ae54960f13b09..b2ab854a6b8b6 100644 --- a/packages/core/test/bundling/defer/bundle.golden_symbols.json +++ b/packages/core/test/bundling/defer/bundle.golden_symbols.json @@ -461,9 +461,6 @@ { "name": "UnsubscriptionError" }, - { - "name": "VERSION" - }, { "name": "VIEW_REFS" }, diff --git a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json index 1b04c1b556b51..036c5657bc171 100644 --- a/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json @@ -560,9 +560,6 @@ { "name": "UnsubscriptionError" }, - { - "name": "VERSION" - }, { "name": "VE_ViewContainerRef" }, diff --git a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json index 5d0e8e799977e..e59db0e0b399b 100644 --- a/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json +++ b/packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json @@ -554,9 +554,6 @@ { "name": "UnsubscriptionError" }, - { - "name": "VERSION" - }, { "name": "VE_ViewContainerRef" }, diff --git a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json index 47d0028296e46..849d47edd29e3 100644 --- a/packages/core/test/bundling/hello_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hello_world/bundle.golden_symbols.json @@ -308,9 +308,6 @@ { "name": "UnsubscriptionError" }, - { - "name": "VERSION" - }, { "name": "ViewEncapsulation" }, diff --git a/packages/core/test/bundling/hydration/bundle.golden_symbols.json b/packages/core/test/bundling/hydration/bundle.golden_symbols.json index c8969c443dd5d..991d4eb214787 100644 --- a/packages/core/test/bundling/hydration/bundle.golden_symbols.json +++ b/packages/core/test/bundling/hydration/bundle.golden_symbols.json @@ -461,9 +461,6 @@ { "name": "UnsubscriptionError" }, - { - "name": "VERSION" - }, { "name": "ViewEncapsulation" }, diff --git a/packages/core/test/bundling/router/bundle.golden_symbols.json b/packages/core/test/bundling/router/bundle.golden_symbols.json index 76dfcabed1825..69d34116aef94 100644 --- a/packages/core/test/bundling/router/bundle.golden_symbols.json +++ b/packages/core/test/bundling/router/bundle.golden_symbols.json @@ -707,9 +707,6 @@ { "name": "UrlTree" }, - { - "name": "VERSION" - }, { "name": "VE_ViewContainerRef" }, diff --git a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json index dcb30854fe202..dfdb82119cb42 100644 --- a/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json +++ b/packages/core/test/bundling/standalone_bootstrap/bundle.golden_symbols.json @@ -365,9 +365,6 @@ { "name": "UnsubscriptionError" }, - { - "name": "VERSION" - }, { "name": "ViewEncapsulation" }, diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 7b16c8a0c5e3f..4edf8b3c148e2 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -479,9 +479,6 @@ { "name": "UnsubscriptionError" }, - { - "name": "VERSION" - }, { "name": "VE_ViewContainerRef" }, From 3a689c20509ed5972b3831988fc0014d293d0cc3 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Sat, 16 Dec 2023 19:24:16 +0100 Subject: [PATCH 20/27] fix(compiler): correctly intercept index in loop tracking function (#53604) The for loop tracking function doesn't allow references to local template variables, aside from `$index` and the item which are passed in as parameters. We enforce this by rewriting all variable references to the components scope. The problem is that the logic that rewrites the references first walks the view tree and then checks if the variable is `$index` or the item. This is problematic in nested for loops, because it'll find the `$index` of the parent. These changes resolve the issue by checking for `$index` and the item first. Fixes #53600. PR Close #53604 --- .../GOLDEN_PARTIAL.js | 51 +++++++++++++++++++ .../TEST_CASES.json | 17 +++++++ .../nested_for_tracking_function.ts | 19 +++++++ .../nested_for_tracking_function_template.js | 44 ++++++++++++++++ .../compiler/src/render3/view/template.ts | 12 ++--- 5 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/nested_for_tracking_function.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/nested_for_tracking_function_template.js diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/GOLDEN_PARTIAL.js index ea5163c842130..ca395e33ba22a 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/GOLDEN_PARTIAL.js @@ -1926,3 +1926,54 @@ export declare class MyApp { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: nested_for_tracking_function.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyApp { + constructor() { + this.items = []; + this.trackByGrandparent = (item, index) => index; + this.trackByParent = (item, index) => index; + this.trackByChild = (item, index) => index; + } +} +MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` + @for (grandparent of items; track trackByGrandparent(grandparent, $index)) { + @for (parent of grandparent.items; track trackByParent(parent, $index)) { + @for (child of parent.items; track trackByChild(child, $index)) { + + } + } + } + `, isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{ + type: Component, + args: [{ + template: ` + @for (grandparent of items; track trackByGrandparent(grandparent, $index)) { + @for (parent of grandparent.items; track trackByParent(parent, $index)) { + @for (child of parent.items; track trackByChild(child, $index)) { + + } + } + } + `, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: nested_for_tracking_function.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyApp { + items: any[]; + trackByGrandparent: (item: any, index: number) => number; + trackByParent: (item: any, index: number) => number; + trackByChild: (item: any, index: number) => number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/TEST_CASES.json index 8e5b68d6c1d25..b3f688adde156 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/TEST_CASES.json @@ -617,6 +617,23 @@ } ], "skipForTemplatePipeline": true + }, + { + "description": "should generate tracking function in a nested for loop", + "inputFiles": [ + "nested_for_tracking_function.ts" + ], + "expectations": [ + { + "files": [ + { + "expected": "nested_for_tracking_function_template.js", + "generated": "nested_for_tracking_function.js" + } + ], + "failureMessage": "Incorrect template" + } + ] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/nested_for_tracking_function.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/nested_for_tracking_function.ts new file mode 100644 index 0000000000000..9d01b5e589877 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/nested_for_tracking_function.ts @@ -0,0 +1,19 @@ +import {Component} from '@angular/core'; + +@Component({ + template: ` + @for (grandparent of items; track trackByGrandparent(grandparent, $index)) { + @for (parent of grandparent.items; track trackByParent(parent, $index)) { + @for (child of parent.items; track trackByChild(child, $index)) { + + } + } + } + `, +}) +export class MyApp { + items: any[] = []; + trackByGrandparent = (item: any, index: number) => index; + trackByParent = (item: any, index: number) => index; + trackByChild = (item: any, index: number) => index; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/nested_for_tracking_function_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/nested_for_tracking_function_template.js new file mode 100644 index 0000000000000..81eac05a0b24f --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/nested_for_tracking_function_template.js @@ -0,0 +1,44 @@ +function _forTrack0($index, $item) { + return this.trackByGrandparent($item, $index); +} + +function _forTrack1($index, $item) { + return this.trackByParent($item, $index); +} + +function _forTrack2($index, $item) { + return this.trackByChild($item, $index); +} + +function MyApp_For_1_For_1_For_1_Template(rf, ctx) {} + +function MyApp_For_1_For_1_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵrepeaterCreate(0, MyApp_For_1_For_1_For_1_Template, 0, 0, null, null, _forTrack2, true); + } + if (rf & 2) { + const $parent_r7$ = ctx.$implicit; + $r3$.ɵɵrepeater($parent_r7$.items); + } +} + +function MyApp_For_1_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵrepeaterCreate(0, MyApp_For_1_For_1_Template, 2, 0, null, null, _forTrack1, true); + } + if (rf & 2) { + const $grandparent_r1$ = ctx.$implicit; + $r3$.ɵɵrepeater($grandparent_r1$.items); + } +} + +… + +function MyApp_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵrepeaterCreate(0, MyApp_For_1_Template, 2, 0, null, null, _forTrack0, true); + } + if (rf & 2) { + $r3$.ɵɵrepeater(ctx.items); + } +} diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index 1a2093ae421be..f01864620948e 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -2550,11 +2550,16 @@ export class BindingScope implements LocalResolver { class TrackByBindingScope extends BindingScope { private componentAccessCount = 0; - constructor(parentScope: BindingScope, private globalAliases: Record) { + constructor(parentScope: BindingScope, private globalOverrides: Record) { super(parentScope.bindingLevel + 1, parentScope); } override get(name: string): o.Expression|null { + // Intercept any overridden globals. + if (this.globalOverrides.hasOwnProperty(name)) { + return o.variable(this.globalOverrides[name]); + } + let current: BindingScope|null = this.parent; // Prevent accesses of template variables outside the `for` loop. @@ -2565,11 +2570,6 @@ class TrackByBindingScope extends BindingScope { current = current.parent; } - // Intercept any aliased globals. - if (this.globalAliases[name]) { - return o.variable(this.globalAliases[name]); - } - // When the component scope is accessed, we redirect it through `this`. this.componentAccessCount++; return o.variable('this').prop(name); From 4c8e8e3714d524b4a2c9e2ec10a1df6e3a4d58cf Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Fri, 15 Dec 2023 14:44:54 -0800 Subject: [PATCH 21/27] refactor(compiler): support multiple statements in host listener (#53596) Support multiple statements in a host listener, like we do for listeners in the template. Also adds a test to verify the behavior. PR Close #53596 --- .../r3_view_compiler_listener/TEST_CASES.json | 13 ++++++++++++- .../multiple_statements.js | 13 +++++++++++++ .../multiple_statements.ts | 12 ++++++++++++ .../compiler/src/template/pipeline/src/ingest.ts | 6 ++---- 4 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/multiple_statements.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/multiple_statements.ts diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/TEST_CASES.json index 7db4e79715bf0..0abfef1b21450 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/TEST_CASES.json @@ -333,6 +333,17 @@ "failureMessage": "Incorrect template" } ] + }, + { + "description": "should generate listener with multiple statements", + "inputFiles": [ + "multiple_statements.ts" + ], + "expectations": [ + { + "failureMessage": "Incorrect template" + } + ] } ] -} \ No newline at end of file +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/multiple_statements.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/multiple_statements.js new file mode 100644 index 0000000000000..5f5f068734f6b --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/multiple_statements.js @@ -0,0 +1,13 @@ +hostBindings: function MyComponent_HostBindings(rf, ctx) { + if (rf & 1) { + i0.ɵɵlistener("click", function MyComponent_click_HostBindingHandler($event) { $event.preventDefault(); return $event.target.blur(); }); + } +} +… +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵelementStart(0, "div", 0); + i0.ɵɵlistener("click", function MyComponent_Template_div_click_0_listener($event) { $event.preventDefault(); return $event.target.blur(); }); + i0.ɵɵelementEnd(); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/multiple_statements.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/multiple_statements.ts new file mode 100644 index 0000000000000..f72abf7c6c337 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/multiple_statements.ts @@ -0,0 +1,12 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'my-component', + standalone: true, + host: {'(click)': '$event.preventDefault(); $event.target.blur()'}, + template: ` +
+ ` +}) +export class MyComponent { +} diff --git a/packages/compiler/src/template/pipeline/src/ingest.ts b/packages/compiler/src/template/pipeline/src/ingest.ts index 7dbc486ae0222..52e087a3be521 100644 --- a/packages/compiler/src/template/pipeline/src/ingest.ts +++ b/packages/compiler/src/template/pipeline/src/ingest.ts @@ -127,11 +127,9 @@ export function ingestHostEvent(job: HostBindingCompilationJob, event: e.ParsedE const [phase, target] = event.type === e.ParsedEventType.Regular ? [null, event.targetOrPhase] : [event.targetOrPhase, null]; const eventBinding = ir.createListenerOp( - job.root.xref, new ir.SlotHandle(), event.name, null, [], phase, target, true, + job.root.xref, new ir.SlotHandle(), event.name, null, + makeListenerHandlerOps(job.root, event.handler, event.handlerSpan), phase, target, true, event.sourceSpan); - // TODO: Can this be a chain? - eventBinding.handlerOps.push(ir.createStatementOp(new o.ReturnStatement( - convertAst(event.handler.ast, job, event.sourceSpan), event.handlerSpan))); job.root.create.push(eventBinding); } From 2ef1bb960e2a7eaba4e63ac8f0f595a534e1caa5 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Fri, 15 Dec 2023 15:07:49 -0800 Subject: [PATCH 22/27] test(compiler): Add a test for handling of duplicate bindings (#53596) Adds a test for handling of duplicate bindings. Fow now we replicate the TDB behavior in template pipeline, which is: For style and class text attributes, only keep the last one. For all other text attributes, add all of the values to the consts array. PR Close #53596 --- .../attribute_bindings/TEST_CASES.json | 12 ++++++ .../attribute_bindings/duplicate_bindings.js | 19 +++++++++ .../attribute_bindings/duplicate_bindings.ts | 19 +++++++++ .../src/template/pipeline/src/emit.ts | 2 + .../pipeline/src/phases/const_collection.ts | 12 +++++- .../src/phases/deduplicate_text_bindings.ts | 41 +++++++++++++++++++ 6 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.ts create mode 100644 packages/compiler/src/template/pipeline/src/phases/deduplicate_text_bindings.ts diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/TEST_CASES.json index b028b3300da8c..a43ab9eb44f36 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/TEST_CASES.json @@ -110,6 +110,18 @@ ] } ] + }, + { + "description": "should handle duplicate bindings", + "inputFiles": [ + "duplicate_bindings.ts" + ], + "expectations": [ + { + "failureMessage": "Incorrect handling of duplicate bindings" + } + ], + "focusTest": true } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.js new file mode 100644 index 0000000000000..a3e52af04608c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.js @@ -0,0 +1,19 @@ +consts: [["aria-label", "hello", "aria-label", "hi"], [2, "height", "0"], [1, "cls2"], [3, "tabindex"]], +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵelement(0, "div", 0); + i0.ɵɵelementStart(1, "div", 1); + i0.ɵɵelement(2, "div", 2)(3, "div")(4, "div", 3)(5, "div")(6, "div"); + i0.ɵɵelementEnd(); + } + if (rf & 2) { + i0.ɵɵadvance(3); + i0.ɵɵattribute("aria-label", ctx.value1)("aria-label", ctx.value2); + i0.ɵɵadvance(1); + i0.ɵɵproperty("tabindex", ctx.value1)("tabindex", ctx.value2); + i0.ɵɵadvance(1); + i0.ɵɵclassMap(ctx.value2); + i0.ɵɵadvance(1); + i0.ɵɵstyleMap(ctx.value2); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.ts new file mode 100644 index 0000000000000..fac09c9f1996d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.ts @@ -0,0 +1,19 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'my-component', + standalone: true, + template: ` +
+
+
+
+
+
+
+ `, +}) +export class MyComponent { + value1: any; + value2: any; +} diff --git a/packages/compiler/src/template/pipeline/src/emit.ts b/packages/compiler/src/template/pipeline/src/emit.ts index babf9ccd26c81..8bfbcbf9280a3 100644 --- a/packages/compiler/src/template/pipeline/src/emit.ts +++ b/packages/compiler/src/template/pipeline/src/emit.ts @@ -25,6 +25,7 @@ import {collectElementConsts} from './phases/const_collection'; import {convertI18nBindings} from './phases/convert_i18n_bindings'; import {createDeferDepsFns} from './phases/create_defer_deps_fns'; import {createI18nContexts} from './phases/create_i18n_contexts'; +import {deduplicateTextBindings} from './phases/deduplicate_text_bindings'; import {configureDeferInstructions} from './phases/defer_configs'; import {resolveDeferTargetNames} from './phases/defer_resolve_targets'; import {collapseEmptyInstructions} from './phases/empty_elements'; @@ -92,6 +93,7 @@ const phases: Phase[] = [ {kind: Kind.Tmpl, fn: emitNamespaceChanges}, {kind: Kind.Tmpl, fn: propagateI18nBlocks}, {kind: Kind.Tmpl, fn: wrapI18nIcus}, + {kind: Kind.Both, fn: deduplicateTextBindings}, {kind: Kind.Both, fn: specializeStyleBindings}, {kind: Kind.Both, fn: specializeBindings}, {kind: Kind.Both, fn: extractAttributes}, diff --git a/packages/compiler/src/template/pipeline/src/phases/const_collection.ts b/packages/compiler/src/template/pipeline/src/phases/const_collection.ts index 193579e858c3e..0db2db1a99ea9 100644 --- a/packages/compiler/src/template/pipeline/src/phases/const_collection.ts +++ b/packages/compiler/src/template/pipeline/src/phases/const_collection.ts @@ -25,7 +25,8 @@ export function collectElementConsts(job: CompilationJob): void { for (const unit of job.units) { for (const op of unit.create) { if (op.kind === ir.OpKind.ExtractedAttribute) { - const attributes = allElementAttributes.get(op.target) || new ElementAttributes(); + const attributes = + allElementAttributes.get(op.target) || new ElementAttributes(job.compatibility); allElementAttributes.set(op.target, attributes); attributes.add(op.bindingKind, op.name, op.expression, op.trustedValueFn); ir.OpList.remove(op); @@ -102,6 +103,8 @@ class ElementAttributes { return this.byKind.get(ir.BindingKind.I18n) ?? FLYWEIGHT_ARRAY; } + constructor(private compatibility: ir.CompatibilityMode) {} + isKnown(kind: ir.BindingKind, name: string, value: o.Expression|null) { const nameToValue = this.known.get(kind) ?? new Set(); this.known.set(kind, nameToValue); @@ -114,7 +117,12 @@ class ElementAttributes { add(kind: ir.BindingKind, name: string, value: o.Expression|null, trustedValueFn: o.Expression|null): void { - if (this.isKnown(kind, name, value)) { + // In compatibility mode, we allow duplicates for attributes to replicate the behavior in + // TemplateDefinitionBuilder. + const allowDuplicates = this.compatibility === ir.CompatibilityMode.TemplateDefinitionBuilder ? + kind === ir.BindingKind.Attribute : + false; + if (this.isKnown(kind, name, value) && !allowDuplicates) { return; } diff --git a/packages/compiler/src/template/pipeline/src/phases/deduplicate_text_bindings.ts b/packages/compiler/src/template/pipeline/src/phases/deduplicate_text_bindings.ts new file mode 100644 index 0000000000000..63f54abc431cb --- /dev/null +++ b/packages/compiler/src/template/pipeline/src/phases/deduplicate_text_bindings.ts @@ -0,0 +1,41 @@ +/** + * @license + * Copyright Google LLC 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 + */ + +import * as ir from '../../ir'; +import type {CompilationJob} from '../compilation'; + +/** + * Deduplicate text bindings, e.g.
+ */ +export function deduplicateTextBindings(job: CompilationJob): void { + const seen = new Map>(); + for (const unit of job.units) { + for (const op of unit.update.reversed()) { + if (op.kind === ir.OpKind.Binding && op.isTextAttribute) { + const seenForElement = seen.get(op.target) || new Set(); + if (seenForElement.has(op.name)) { + if (job.compatibility === ir.CompatibilityMode.TemplateDefinitionBuilder) { + // For most duplicated attributes, TemplateDefinitionBuilder lists all of the values in + // the consts array. However, for style and class attributes it only keeps the last one. + // We replicate that behavior here since it has actual consequences for apps with + // duplicate class or style attrs. + if (op.name === 'style' || op.name === 'class') { + ir.OpList.remove(op); + } + } else { + // TODO: Determine the correct behavior. It would probably make sense to merge multiple + // style and class attributes. Alternatively we could just throw an error, as HTML + // doesn't permit duplicate attributes. + } + } + seenForElement.add(op.name); + seen.set(op.target, seenForElement); + } + } + } +} From 73faf94273a1527304d57da5ae7f294a5adf1452 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Fri, 15 Dec 2023 16:44:40 -0800 Subject: [PATCH 23/27] refactor(compiler): Allow duplicate style and class consts (#53596) Further refine the template pipeline's behavior w.r.t. duplicate values in the consts array to better align its behavior with TDB. In particular this means allowing duplicate values for classes and styles. PR Close #53596 --- .../attribute_bindings/GOLDEN_PARTIAL.js | 45 +++++++++++++ .../attribute_bindings/TEST_CASES.json | 3 +- .../attribute_bindings/duplicate_bindings.js | 6 +- .../attribute_bindings/duplicate_bindings.ts | 1 + .../style_bindings/TEST_CASES.json | 67 +++++++++++++++---- .../duplicate_style_bindings.js | 1 + .../duplicate_style_bindings.ts | 11 +++ .../pipeline/src/phases/const_collection.ts | 13 ++-- 8 files changed, 123 insertions(+), 24 deletions(-) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/duplicate_style_bindings.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/duplicate_style_bindings.ts diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/GOLDEN_PARTIAL.js index 6c67a2a7f674c..8d364b9a2ee5a 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/GOLDEN_PARTIAL.js @@ -370,3 +370,48 @@ export declare class MyModule { static ɵinj: i0.ɵɵInjectorDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: duplicate_bindings.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, isStandalone: true, selector: "my-component", ngImport: i0, template: ` +
+
+
+
+
+
+
+ `, isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, decorators: [{ + type: Component, + args: [{ + selector: 'my-component', + standalone: true, + template: ` +
+
+
+
+
+
+
+ `, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: duplicate_bindings.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + value1: any; + value2: any; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/TEST_CASES.json index a43ab9eb44f36..969072755b861 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/TEST_CASES.json @@ -120,8 +120,7 @@ { "failureMessage": "Incorrect handling of duplicate bindings" } - ], - "focusTest": true + ] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.js index a3e52af04608c..f1756177ae0a0 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.js @@ -1,10 +1,12 @@ -consts: [["aria-label", "hello", "aria-label", "hi"], [2, "height", "0"], [1, "cls2"], [3, "tabindex"]], +consts: [["aria-label", "hello", "aria-label", "hi"], [2, "height", "0"], [1, "cls2"], [3, "tabindex"], [3, "click"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "div", 0); i0.ɵɵelementStart(1, "div", 1); i0.ɵɵelement(2, "div", 2)(3, "div")(4, "div", 3)(5, "div")(6, "div"); - i0.ɵɵelementEnd(); + i0.ɵɵelementStart(7, "div", 4); + i0.ɵɵlistener("click", function MyComponent_Template_div_click_7_listener($event) { return $event.stopPropagation(); })("click", function MyComponent_Template_div_click_7_listener($event) { return $event.preventDefault(); }); + i0.ɵɵelementEnd()(); } if (rf & 2) { i0.ɵɵadvance(3); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.ts index fac09c9f1996d..433791c424c3f 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.ts +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/duplicate_bindings.ts @@ -11,6 +11,7 @@ import {Component} from '@angular/core';
+
`, }) export class MyComponent { diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/TEST_CASES.json index d20129bc03d4a..8727df0f74766 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/TEST_CASES.json @@ -3,71 +3,110 @@ "cases": [ { "description": "should create style instructions on the element", - "inputFiles": ["style_binding.ts"], + "inputFiles": [ + "style_binding.ts" + ], "expectations": [ { "failureMessage": "Incorrect template", - "files": ["style_binding.js"] + "files": [ + "style_binding.js" + ] } ] }, { "description": "should correctly count the total slots required when style/class bindings include interpolation", - "inputFiles": ["binding_slots.ts"], + "inputFiles": [ + "binding_slots.ts" + ], "expectations": [ { "failureMessage": "Incorrect template", - "files": ["binding_slots.js"] + "files": [ + "binding_slots.js" + ] } ] }, { "description": "should place initial, multi, singular and application followed by attribute style instructions in the template code in that order", - "inputFiles": ["binding_slots_interpolations.ts"], + "inputFiles": [ + "binding_slots_interpolations.ts" + ], "expectations": [ { "failureMessage": "Incorrect template", - "files": ["binding_slots_interpolations.js"] + "files": [ + "binding_slots_interpolations.js" + ] } ] }, { "description": "should assign a sanitizer instance to the element style allocation instruction if any url-based properties are detected", - "inputFiles": ["style_ordering.ts"], + "inputFiles": [ + "style_ordering.ts" + ], "expectations": [ { "failureMessage": "Incorrect template", - "files": ["style_ordering.js"] + "files": [ + "style_ordering.js" + ] } ] }, { "description": "should support [style.foo.suffix] style bindings with a suffix", - "inputFiles": ["style_binding_suffixed.ts"], + "inputFiles": [ + "style_binding_suffixed.ts" + ], "expectations": [ { "failureMessage": "Incorrect template", - "files": ["style_binding_suffixed.js"] + "files": [ + "style_binding_suffixed.js" + ] } ] }, { "description": "should not create instructions for empty style bindings", - "inputFiles": ["empty_style_bindings.ts"], + "inputFiles": [ + "empty_style_bindings.ts" + ], "expectations": [ { "failureMessage": "Incorrect template", - "files": ["empty_style_bindings.js"] + "files": [ + "empty_style_bindings.js" + ] } ] }, { "description": "should support style bindings with a colon at the start of the applied style", - "inputFiles": ["colon_style.ts"], + "inputFiles": [ + "colon_style.ts" + ], "expectations": [ { "failureMessage": "Incorrect template", - "files": ["colon_style.js"] + "files": [ + "colon_style.js" + ] + } + ] + }, + { + "description": "should handle duplicate style bindings", + "inputFiles": [ + "duplicate_style_bindings.ts" + ], + "expectations": [ + { + "failureMessage": "Incorrect template" } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/duplicate_style_bindings.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/duplicate_style_bindings.js new file mode 100644 index 0000000000000..f14313c6b3426 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/duplicate_style_bindings.js @@ -0,0 +1 @@ +consts: [[1, "cls1", "cls1", 2, "width", "1px", "width", "10px"]] diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/duplicate_style_bindings.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/duplicate_style_bindings.ts new file mode 100644 index 0000000000000..53e7b3d4ac621 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/duplicate_style_bindings.ts @@ -0,0 +1,11 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'my-component', + standalone: true, + template: ` +
+ `, +}) +export class MyComponent { +} diff --git a/packages/compiler/src/template/pipeline/src/phases/const_collection.ts b/packages/compiler/src/template/pipeline/src/phases/const_collection.ts index 0db2db1a99ea9..9941f54f01f5e 100644 --- a/packages/compiler/src/template/pipeline/src/phases/const_collection.ts +++ b/packages/compiler/src/template/pipeline/src/phases/const_collection.ts @@ -117,12 +117,13 @@ class ElementAttributes { add(kind: ir.BindingKind, name: string, value: o.Expression|null, trustedValueFn: o.Expression|null): void { - // In compatibility mode, we allow duplicates for attributes to replicate the behavior in - // TemplateDefinitionBuilder. - const allowDuplicates = this.compatibility === ir.CompatibilityMode.TemplateDefinitionBuilder ? - kind === ir.BindingKind.Attribute : - false; - if (this.isKnown(kind, name, value) && !allowDuplicates) { + // TemplateDefinitionBuilder puts duplicate attribute, class, and style values into the consts + // array. This seems inefficient, we can probably keep just the first one or the last value + // (whichever actually gets applied when multiple values are listed for the same attribute). + const allowDuplicates = this.compatibility === ir.CompatibilityMode.TemplateDefinitionBuilder && + (kind === ir.BindingKind.Attribute || kind === ir.BindingKind.ClassName || + kind === ir.BindingKind.StyleProperty); + if (!allowDuplicates && this.isKnown(kind, name, value)) { return; } From d485cfef28ab4143a31cb42b7a4bb3f99cc2dd00 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Mon, 18 Dec 2023 13:06:01 -0800 Subject: [PATCH 24/27] test(compiler): Update partial golden files (#53596) Update partial golden files to reflect newly added tests PR Close #53596 --- .../attribute_bindings/GOLDEN_PARTIAL.js | 2 ++ .../GOLDEN_PARTIAL.js | 32 +++++++++++++++++++ .../style_bindings/GOLDEN_PARTIAL.js | 31 ++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/GOLDEN_PARTIAL.js index 8d364b9a2ee5a..b12233e1c6423 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/attribute_bindings/GOLDEN_PARTIAL.js @@ -386,6 +386,7 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "
+
`, isInline: true }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, decorators: [{ type: Component, @@ -400,6 +401,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE
+
`, }] }] }); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/GOLDEN_PARTIAL.js index 269122652384e..e3c96ad6f59c0 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/GOLDEN_PARTIAL.js @@ -818,3 +818,35 @@ export declare class AppModule { static ɵinj: i0.ɵɵInjectorDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: multiple_statements.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, isStandalone: true, selector: "my-component", host: { listeners: { "click": "$event.preventDefault(); $event.target.blur()" } }, ngImport: i0, template: ` +
+ `, isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, decorators: [{ + type: Component, + args: [{ + selector: 'my-component', + standalone: true, + host: { '(click)': '$event.preventDefault(); $event.target.blur()' }, + template: ` +
+ ` + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: multiple_statements.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/GOLDEN_PARTIAL.js index 176417a9be8ec..526423b8e87df 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/style_bindings/GOLDEN_PARTIAL.js @@ -354,3 +354,34 @@ export declare class MyComponent { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: duplicate_style_bindings.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, isStandalone: true, selector: "my-component", ngImport: i0, template: ` +
+ `, isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, decorators: [{ + type: Component, + args: [{ + selector: 'my-component', + standalone: true, + template: ` +
+ `, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: duplicate_style_bindings.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + From 8460a4380cdb76725a675b451b63043c4d6ab629 Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Mon, 18 Dec 2023 13:01:12 -0800 Subject: [PATCH 25/27] test(compiler): Fix handling of deceptively named attributes (#53626) It's possible for the user to create a host attrbiute binding with a name that makes it _look_ like a class binding `{['class.foo']: ''}`, we were previously treating these as actual class property bindings. This change fixes the logic so that only true property bindings cam be converted to class property bindings. Note: A user who added an attribute like the above almost certainly intended to create an actual class property binding. It would be nice if we could add a diagnostic to warn them about this. PR Close #53626 --- .../host_bindings/GOLDEN_PARTIAL.js | 53 +++++++++++++++++++ .../host_bindings/TEST_CASES.json | 18 +++++++ .../host_bindings/deceptive_attrs.pipeline.js | 9 ++++ .../host_bindings/deceptive_attrs.template.js | 9 ++++ .../host_bindings/deceptive_attrs.ts | 27 ++++++++++ .../src/phases/host_style_property_parsing.ts | 2 +- 6 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/deceptive_attrs.pipeline.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/deceptive_attrs.template.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/deceptive_attrs.ts diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js index f8a773740b75a..da86e0a834d97 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js @@ -1007,3 +1007,56 @@ export declare class MyComponent { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: deceptive_attrs.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, isStandalone: true, selector: "my-comp", host: { attributes: { "class.is-compact": "false", "style.width": "0", "attr.tabindex": "5" } }, ngImport: i0, template: '', isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, decorators: [{ + type: Component, + args: [{ + selector: 'my-comp', + standalone: true, + template: '', + host: { + ['class.is-compact']: 'false', + ['style.width']: '0', + ['attr.tabindex']: '5', + } + }] + }] }); +export class MyComponent2 { +} +MyComponent2.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent2, deps: [], target: i0.ɵɵFactoryTarget.Component }); +MyComponent2.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent2, isStandalone: true, selector: "my-comp-2", host: { properties: { "class.is-compact": "false", "style.width": "0", "attr.tabindex": "5" } }, ngImport: i0, template: '', isInline: true }); +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent2, decorators: [{ + type: Component, + args: [{ + selector: 'my-comp-2', + standalone: true, + template: '', + host: { + '[class.is-compact]': 'false', + '[style.width]': '0', + '[attr.tabindex]': '5', + } + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: deceptive_attrs.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} +export declare class MyComponent2 { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/TEST_CASES.json index 68f7914f131d3..5ca1b0bef87d7 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/TEST_CASES.json @@ -367,6 +367,24 @@ ] } ] + }, + { + "description": "should handle deceptive attribute names", + "inputFiles": [ + "deceptive_attrs.ts" + ], + "expectations": [ + { + "failureMessage": "Invalid host binding code", + "files": [ + { + "generated": "deceptive_attrs.js", + "expected": "deceptive_attrs.template.js", + "templatePipelineExpected": "deceptive_attrs.pipeline.js" + } + ] + } + ] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/deceptive_attrs.pipeline.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/deceptive_attrs.pipeline.js new file mode 100644 index 0000000000000..1f8e244691a5f --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/deceptive_attrs.pipeline.js @@ -0,0 +1,9 @@ +hostAttrs: ["class.is-compact", "false", "style.width", "0", "attr.tabindex", "5"], +… +hostBindings: function MyComponent2_HostBindings(rf, ctx) { + if (rf & 2) { + i0.ɵɵstyleProp("width", 0); + i0.ɵɵclassProp("is-compact", false); + i0.ɵɵattribute("tabindex", 5); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/deceptive_attrs.template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/deceptive_attrs.template.js new file mode 100644 index 0000000000000..b628c765f9467 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/deceptive_attrs.template.js @@ -0,0 +1,9 @@ +hostAttrs: ["class.is-compact", "false", "style.width", "0", "attr.tabindex", "5"], +… +hostBindings: function MyComponent2_HostBindings(rf, ctx) { + if (rf & 2) { + i0.ɵɵattribute("tabindex", 5); + i0.ɵɵstyleProp("width", 0); + i0.ɵɵclassProp("is-compact", false); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/deceptive_attrs.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/deceptive_attrs.ts new file mode 100644 index 0000000000000..1b9dca682f2bc --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/deceptive_attrs.ts @@ -0,0 +1,27 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'my-comp', + standalone: true, + template: '', + host: { + ['class.is-compact']: 'false', + ['style.width']: '0', + ['attr.tabindex']: '5', + } +}) +export class MyComponent { +} + +@Component({ + selector: 'my-comp-2', + standalone: true, + template: '', + host: { + '[class.is-compact]': 'false', + '[style.width]': '0', + '[attr.tabindex]': '5', + } +}) +export class MyComponent2 { +} diff --git a/packages/compiler/src/template/pipeline/src/phases/host_style_property_parsing.ts b/packages/compiler/src/template/pipeline/src/phases/host_style_property_parsing.ts index 87b8b1b8071af..c42dfd2fcfe57 100644 --- a/packages/compiler/src/template/pipeline/src/phases/host_style_property_parsing.ts +++ b/packages/compiler/src/template/pipeline/src/phases/host_style_property_parsing.ts @@ -25,7 +25,7 @@ const BANG_IMPORTANT = '!important'; */ export function parseHostStyleProperties(job: CompilationJob): void { for (const op of job.root.update) { - if (op.kind !== ir.OpKind.Binding) { + if (!(op.kind === ir.OpKind.Binding && op.bindingKind === ir.BindingKind.Property)) { continue; } From b40bb22a66a3c2ae478778f3ef0be0ce971a1b2c Mon Sep 17 00:00:00 2001 From: Jessica Janiuk Date: Mon, 18 Dec 2023 15:42:15 -0500 Subject: [PATCH 26/27] fix(migrations): cf migration - improve import declaration handling (#53622) This should make the import declaration symbol removal a bit more robust and handle more than just CommonModule safely. PR Close #53622 --- .../identifier-lookup.ts | 4 +- .../control-flow-migration/migration.ts | 1 + .../control-flow-migration/types.ts | 9 ++- .../control-flow-migration/util.ts | 7 +-- .../test/control_flow_migration_spec.ts | 62 +++++++++++++++++++ 5 files changed, 74 insertions(+), 9 deletions(-) diff --git a/packages/core/schematics/ng-generate/control-flow-migration/identifier-lookup.ts b/packages/core/schematics/ng-generate/control-flow-migration/identifier-lookup.ts index b3f84bd2af93c..89602be4f7850 100644 --- a/packages/core/schematics/ng-generate/control-flow-migration/identifier-lookup.ts +++ b/packages/core/schematics/ng-generate/control-flow-migration/identifier-lookup.ts @@ -9,10 +9,10 @@ import ts from 'typescript'; export function lookupIdentifiersInSourceFile( - sourceFile: ts.SourceFile, name: string): Set { + sourceFile: ts.SourceFile, names: string[]): Set { const results = new Set(); const visit = (node: ts.Node): void => { - if (ts.isIdentifier(node) && node.text === name) { + if (ts.isIdentifier(node) && names.includes(node.text)) { results.add(node); } ts.forEachChild(node, visit); diff --git a/packages/core/schematics/ng-generate/control-flow-migration/migration.ts b/packages/core/schematics/ng-generate/control-flow-migration/migration.ts index 1d2d1790a8dc8..15636b62218e8 100644 --- a/packages/core/schematics/ng-generate/control-flow-migration/migration.ts +++ b/packages/core/schematics/ng-generate/control-flow-migration/migration.ts @@ -82,6 +82,7 @@ export function migrateTemplate( // and in that case we can't safely remove the common module import. componentFile.verifyCanRemoveImports(); } + file.verifyCanRemoveImports(); errors = [ ...ifResult.errors, diff --git a/packages/core/schematics/ng-generate/control-flow-migration/types.ts b/packages/core/schematics/ng-generate/control-flow-migration/types.ts index d7ab008e2348f..658abf0d5f91a 100644 --- a/packages/core/schematics/ng-generate/control-flow-migration/types.ts +++ b/packages/core/schematics/ng-generate/control-flow-migration/types.ts @@ -23,6 +23,13 @@ export const endMarker = '✢'; export const startI18nMarker = '⚈'; export const endI18nMarker = '⚉'; +export const importRemovals = [ + 'NgIf', 'NgIfElse', 'NgIfThenElse', 'NgFor', 'NgForOf', 'NgForTrackBy', 'NgSwitch', + 'NgSwitchCase', 'NgSwitchDefault' +]; + +export const importWithCommonRemovals = [...importRemovals, 'CommonModule']; + function allFormsOf(selector: string): string[] { return [ selector, @@ -308,7 +315,7 @@ export class AnalyzedFile { // skip this check entirely if (this.removeCommonModule) { const importDeclaration = this.importRanges.find(r => r.type === 'importDeclaration'); - const instances = lookupIdentifiersInSourceFile(this.sourceFile, 'CommonModule'); + const instances = lookupIdentifiersInSourceFile(this.sourceFile, importWithCommonRemovals); let foundImportDeclaration = false; let count = 0; for (let range of this.importRanges) { diff --git a/packages/core/schematics/ng-generate/control-flow-migration/util.ts b/packages/core/schematics/ng-generate/control-flow-migration/util.ts index 71550111bb103..d5ee9717bc790 100644 --- a/packages/core/schematics/ng-generate/control-flow-migration/util.ts +++ b/packages/core/schematics/ng-generate/control-flow-migration/util.ts @@ -10,13 +10,8 @@ import {Attribute, Element, HtmlParser, Node, ParseTreeResult, visitAll} from '@ import {dirname, join} from 'path'; import ts from 'typescript'; -import {AnalyzedFile, CommonCollector, ElementCollector, ElementToMigrate, endI18nMarker, endMarker, i18nCollector, ParseResult, startI18nMarker, startMarker, Template, TemplateCollector} from './types'; +import {AnalyzedFile, CommonCollector, ElementCollector, ElementToMigrate, endI18nMarker, endMarker, i18nCollector, importRemovals, importWithCommonRemovals, ParseResult, startI18nMarker, startMarker, Template, TemplateCollector} from './types'; -const importRemovals = [ - 'NgIf', 'NgIfElse', 'NgIfThenElse', 'NgFor', 'NgForOf', 'NgForTrackBy', 'NgSwitch', - 'NgSwitchCase', 'NgSwitchDefault' -]; -const importWithCommonRemovals = [...importRemovals, 'CommonModule']; const startMarkerRegex = new RegExp(startMarker, 'gm'); const endMarkerRegex = new RegExp(endMarker, 'gm'); const startI18nMarkerRegex = new RegExp(startI18nMarker, 'gm'); diff --git a/packages/core/schematics/test/control_flow_migration_spec.ts b/packages/core/schematics/test/control_flow_migration_spec.ts index 069b8a224d401..e55a691d9bb9f 100644 --- a/packages/core/schematics/test/control_flow_migration_spec.ts +++ b/packages/core/schematics/test/control_flow_migration_spec.ts @@ -5087,6 +5087,68 @@ describe('control flow migration', () => { expect(actual).toBe(expected); }); + + it('should not remove imports when mismatch in counts', async () => { + writeFile('/comp.ts', [ + `import {CommonModule} from '@angular/common';`, + `import {Component, NgModule, Pipe, PipeTransform} from '@angular/core';`, + `@Component({`, + ` selector: 'description',`, + ` template: \`{{getDescription()}}\`,`, + `})`, + `export class DescriptionController {`, + ` getDescription(): string {`, + ` return 'stuff';`, + ` }`, + `}`, + ``, + `@Pipe({name: 'description'})`, + `export class DescriptionPipe implements PipeTransform {`, + ` transform(nameString?: string): string {`, + ` return nameString ?? '';`, + ` }`, + `}`, + `@NgModule({`, + ` declarations: [DescriptionController, DescriptionPipe],`, + ` imports: [CommonModule],`, + ` providers: [],`, + ` exports: [DescriptionController, DescriptionPipe],`, + `})`, + `export class DescriptionModule {}`, + ].join('\n')); + + await runMigration(); + const actual = tree.readContent('/comp.ts'); + const expected = [ + `import {CommonModule} from '@angular/common';`, + `import {Component, NgModule, Pipe, PipeTransform} from '@angular/core';`, + `@Component({`, + ` selector: 'description',`, + ` template: \`{{getDescription()}}\`,`, + `})`, + `export class DescriptionController {`, + ` getDescription(): string {`, + ` return 'stuff';`, + ` }`, + `}`, + ``, + `@Pipe({name: 'description'})`, + `export class DescriptionPipe implements PipeTransform {`, + ` transform(nameString?: string): string {`, + ` return nameString ?? '';`, + ` }`, + `}`, + `@NgModule({`, + ` declarations: [DescriptionController, DescriptionPipe],`, + ` imports: [CommonModule],`, + ` providers: [],`, + ` exports: [DescriptionController, DescriptionPipe],`, + `})`, + `export class DescriptionModule {}`, + ].join('\n'); + + expect(actual).toBe(expected); + }); }); describe('no migration needed', () => { From 8bf752539f6b4b5955e3d61a76423910af55181b Mon Sep 17 00:00:00 2001 From: Jessica Janiuk Date: Mon, 18 Dec 2023 16:21:21 -0500 Subject: [PATCH 27/27] fix(migrations): cf migration - preserve indentation on attribute strings (#53625) During formatting, attribute indentation is changed, and that can affect internationalized strings. This fix detects if an attribute value string is left open and skips formatting on those lines. PR Close #53625 --- .../control-flow-migration/util.ts | 36 ++++++++- .../test/control_flow_migration_spec.ts | 77 +++++++++++++++++++ 2 files changed, 110 insertions(+), 3 deletions(-) diff --git a/packages/core/schematics/ng-generate/control-flow-migration/util.ts b/packages/core/schematics/ng-generate/control-flow-migration/util.ts index d5ee9717bc790..23d3bd3ce1624 100644 --- a/packages/core/schematics/ng-generate/control-flow-migration/util.ts +++ b/packages/core/schematics/ng-generate/control-flow-migration/util.ts @@ -582,6 +582,19 @@ export function formatTemplate(tmpl: string, templateType: string): string { //
||
]*\/>)[^>]*>?/; + // regex for matching an attribute string that was left open at the endof a line + // so we can ensure we have the proper indent + //
+ const closeAttrDoubleRegex = /^\s*([^><]|\\")*"/; + const closeAttrSingleRegex = /^\s*([^><]|\\')*'/; + // regex for matching a self closing html element that has no /> // const selfClosingRegex = new RegExp(`^\\s*<(${selfClosingList}).+\\/?>`); @@ -620,6 +633,8 @@ export function formatTemplate(tmpl: string, templateType: string): string { let i18nDepth = 0; let inMigratedBlock = false; let inI18nBlock = false; + let inAttribute = false; + let isDoubleQuotes = false; for (let [index, line] of lines.entries()) { depth += [...line.matchAll(startMarkerRegex)].length - [...line.matchAll(endMarkerRegex)].length; @@ -633,7 +648,7 @@ export function formatTemplate(tmpl: string, templateType: string): string { lineWasMigrated = true; } if ((line.trim() === '' && index !== 0 && index !== lines.length - 1) && - (inMigratedBlock || lineWasMigrated) && !inI18nBlock) { + (inMigratedBlock || lineWasMigrated) && !inI18nBlock && !inAttribute) { // skip blank lines except if it's the first line or last line // this preserves leading and trailing spaces if they are already present continue; @@ -655,10 +670,25 @@ export function formatTemplate(tmpl: string, templateType: string): string { indent = indent.slice(2); } - const newLine = - inI18nBlock ? line : mindent + (line.trim() !== '' ? indent : '') + line.trim(); + // if a line ends in an unclosed attribute, we need to note that and close it later + if (!inAttribute && openAttrDoubleRegex.test(line)) { + inAttribute = true; + isDoubleQuotes = true; + } else if (!inAttribute && openAttrSingleRegex.test(line)) { + inAttribute = true; + isDoubleQuotes = false; + } + + const newLine = (inI18nBlock || inAttribute) ? + line : + mindent + (line.trim() !== '' ? indent : '') + line.trim(); formatted.push(newLine); + if ((inAttribute && isDoubleQuotes && closeAttrDoubleRegex.test(line)) || + (inAttribute && !isDoubleQuotes && closeAttrSingleRegex.test(line))) { + inAttribute = false; + } + // this matches any self closing element that actually has a /> if (closeMultiLineElRegex.test(line)) { // multi line self closing tag diff --git a/packages/core/schematics/test/control_flow_migration_spec.ts b/packages/core/schematics/test/control_flow_migration_spec.ts index e55a691d9bb9f..04c95d46ffe01 100644 --- a/packages/core/schematics/test/control_flow_migration_spec.ts +++ b/packages/core/schematics/test/control_flow_migration_spec.ts @@ -4755,6 +4755,83 @@ describe('control flow migration', () => { expect(actual).toBe(expected); }); + + it('should indent multi-line attribute strings to the right place', async () => { + writeFile('/comp.ts', ` + import {Component} from '@angular/core'; + import {NgIf} from '@angular/common'; + + @Component({ + templateUrl: './comp.html' + }) + class Comp { + show = false; + } + `); + + writeFile('/comp.html', [ + `
show
`, + ``, + ` Content here`, + ``, + ].join('\n')); + + await runMigration(); + const actual = tree.readContent('/comp.html'); + const expected = [ + `@if (show) {`, + `
show
`, + `}`, + ``, + ` Content here`, + ``, + ].join('\n'); + + expect(actual).toBe(expected); + }); + + it('should indent multi-line attribute strings as single quotes to the right place', + async () => { + writeFile('/comp.ts', ` + import {Component} from '@angular/core'; + import {NgIf} from '@angular/common'; + + @Component({ + templateUrl: './comp.html' + }) + class Comp { + show = false; + } + `); + + writeFile('/comp.html', [ + `
show
`, + ``, + ` Content here`, + ``, + ].join('\n')); + + await runMigration(); + const actual = tree.readContent('/comp.html'); + const expected = [ + `@if (show) {`, + `
show
`, + `}`, + ``, + ` Content here`, + ``, + ].join('\n'); + + expect(actual).toBe(expected); + }); }); describe('imports', () => {