From 643021850aac7102ebc3ffeac7702a47f4de2d28 Mon Sep 17 00:00:00 2001 From: Matthew Beck Date: Thu, 23 Jul 2026 10:42:53 -0700 Subject: [PATCH 1/2] test(compiler-cli): add compliance coverage for under-tested emit behaviors Adds file-based compliance test cases for a number of compiler emit behaviors that are currently exercised only in ngtsc unit tests (or not covered at all) rather than in the compliance suite. Each case is added to the existing compliance directory that matches its area, alongside the related cases, rather than in a separate tree. New cases span: - NgModule (r3_compiler_compliance/ng_modules): the setNgModuleScope JIT scope call, injector providers (useClass/useExisting/useValue/multi/ useFactory), same-file ModuleWithProviders, standalone declarables in imports, exports-only re-exports, and nested-array / forwardRef injector imports. - DI: constructor injection flags (@Optional/@Self/@SkipSelf/@Host/ @Attribute) in r3_view_compiler_di, and @Injectable providedIn variants ('platform'/'any'/a module type) in service_decorator. - Directives/components (r3_compiler_compliance, r3_view_compiler_bindings, r3_view_compiler_styling, r3_view_compiler_input_outputs): @Input transforms, @HostBinding/@HostListener merged with the host object, host [class]/[style] bindings, exportAs read via a template-reference query, cross-file inheritance (InheritDefinitionFeature), and OnPush change detection / encapsulation metadata. - Signals (signal_inputs): input()/input.required()/model()/output(). - Templates: two-way binding to a model (r3_view_compiler_bindings), @defer with triggers (r3_view_compiler_deferred), multi-slot content projection with ngProjectAs (r3_compiler_compliance), and @for with track and @empty (r3_view_compiler_control_flow). - i18n (r3_view_compiler_i18n): $localize meaning/description/id metadata and placeholders, i18n attributes, plural and nested (select-in-plural) ICUs, and an i18n block wrapping @if/@else. - Sanitization: sanitizeUrlOrResourceUrl for ambiguous host src/href on an attribute-only directive (r3_view_compiler_bindings), trustConstantResourceUrl for static resource URLs and an SVG xlink:href binding (r3_compiler_compliance elements). Each case runs in full and/or local compilation mode, with expected files verified against the compiler output and golden partials generated. --- .../GOLDEN_PARTIAL.js | 132 ++++++ .../components_and_directives/TEST_CASES.json | 33 ++ .../change_detection.js | 6 + .../change_detection.local.js | 6 + .../change_detection.ts | 10 + .../content_projection/GOLDEN_PARTIAL.js | 58 +++ .../content_projection/TEST_CASES.json | 11 + .../ng_project_as_multi_slot.js | 13 + .../ng_project_as_multi_slot.ts | 22 + .../cross_file_inheritance.js | 6 + .../cross_file_inheritance.local.js | 6 + .../cross_file_inheritance.ts | 9 + .../cross_file_inheritance_base.ts | 6 + .../export_as_query.js | 20 + .../export_as_query.ts | 18 + .../elements/GOLDEN_PARTIAL.js | 60 +++ .../elements/TEST_CASES.json | 22 + .../elements/svg_xlink_href.js | 8 + .../elements/svg_xlink_href.ts | 9 + .../elements/trusted_constant_urls.js | 6 + .../elements/trusted_constant_urls.local.js | 6 + .../elements/trusted_constant_urls.ts | 10 + .../ng_modules/GOLDEN_PARTIAL.js | 438 ++++++++++++++++++ .../ng_modules/TEST_CASES.json | 77 +++ .../ng_modules/forwardref_imports.js | 9 + .../ng_modules/forwardref_imports.local.js | 9 + .../ng_modules/forwardref_imports.ts | 11 + .../ng_modules/injector_providers.js | 15 + .../ng_modules/injector_providers.ts | 25 + .../ng_modules/module_scope.js | 8 + .../ng_modules/module_scope.local.js | 8 + .../ng_modules/module_scope.ts | 34 ++ .../ng_modules/module_with_providers.js | 8 + .../ng_modules/module_with_providers.local.js | 8 + .../ng_modules/module_with_providers.ts | 18 + .../ng_modules/nested_array_imports.js | 8 + .../ng_modules/nested_array_imports.local.js | 8 + .../ng_modules/nested_array_imports.ts | 16 + .../ng_modules/reexport_only.js | 8 + .../ng_modules/reexport_only.local.js | 8 + .../ng_modules/reexport_only.ts | 11 + .../ng_modules/standalone_imports.js | 8 + .../ng_modules/standalone_imports.local.js | 8 + .../ng_modules/standalone_imports.ts | 27 ++ .../host_bindings/GOLDEN_PARTIAL.js | 91 ++++ .../host_bindings/TEST_CASES.json | 22 + .../host_bindings/host_decorators.js | 14 + .../host_bindings/host_decorators.ts | 25 + .../sanitization_ambiguous_url.js | 10 + .../sanitization_ambiguous_url.local.js | 10 + .../sanitization_ambiguous_url.ts | 15 + .../property_bindings/GOLDEN_PARTIAL.js | 46 ++ .../property_bindings/TEST_CASES.json | 11 + .../two_way_model_binding.js | 15 + .../two_way_model_binding.ts | 17 + .../GOLDEN_PARTIAL.js | 43 ++ .../TEST_CASES.json | 11 + .../for_track_fn_with_empty.js | 14 + .../for_track_fn_with_empty.ts | 15 + .../GOLDEN_PARTIAL.js | 64 +++ .../r3_view_compiler_deferred/TEST_CASES.json | 11 + .../deferred_all_triggers.js | 14 + .../deferred_all_triggers.ts | 25 + .../r3_view_compiler_di/di/GOLDEN_PARTIAL.js | 44 ++ .../r3_view_compiler_di/di/TEST_CASES.json | 11 + .../di/directive_injection.js | 7 + .../di/directive_injection.ts | 26 ++ .../r3_view_compiler_i18n/GOLDEN_PARTIAL.js | 30 ++ .../r3_view_compiler_i18n/TEST_CASES.json | 11 + .../blocks/GOLDEN_PARTIAL.js | 30 ++ .../blocks/TEST_CASES.json | 11 + .../blocks/conditional_placeholders.js | 24 + .../blocks/conditional_placeholders.ts | 10 + .../element_attributes/GOLDEN_PARTIAL.js | 38 ++ .../element_attributes/TEST_CASES.json | 11 + .../element_attributes/i18n_attributes.js | 10 + .../element_attributes/i18n_attributes.ts | 14 + .../icu_logic/GOLDEN_PARTIAL.js | 68 +++ .../icu_logic/TEST_CASES.json | 22 + .../icu_logic/plural_icu.js | 10 + .../icu_logic/plural_icu.ts | 9 + .../icu_logic/select_in_plural_icu.js | 7 + .../icu_logic/select_in_plural_icu.ts | 15 + .../message_metadata_placeholders.js | 8 + .../message_metadata_placeholders.ts | 10 + .../GOLDEN_PARTIAL.js | 46 ++ .../TEST_CASES.json | 11 + .../input_transforms.js | 6 + .../input_transforms.local.js | 6 + .../input_transforms.ts | 14 + .../host_bindings/GOLDEN_PARTIAL.js | 47 ++ .../host_bindings/TEST_CASES.json | 11 + .../host_style_class_bindings.js | 13 + .../host_style_class_bindings.local.js | 13 + .../host_style_class_bindings.ts | 17 + .../service_decorator/GOLDEN_PARTIAL.js | 61 +++ .../service_decorator/TEST_CASES.json | 11 + .../service_decorator/provided_in_variants.js | 16 + .../service_decorator/provided_in_variants.ts | 13 + .../signal_inputs/GOLDEN_PARTIAL.js | 40 ++ .../test_cases/signal_inputs/TEST_CASES.json | 11 + .../test_cases/signal_inputs/signal_apis.js | 6 + .../signal_inputs/signal_apis.local.js | 6 + .../test_cases/signal_inputs/signal_apis.ts | 15 + 104 files changed, 2509 insertions(+) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/change_detection.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/change_detection.local.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/change_detection.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/ng_project_as_multi_slot.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/ng_project_as_multi_slot.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance.local.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance_base.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/export_as_query.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/export_as_query.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/svg_xlink_href.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/svg_xlink_href.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/trusted_constant_urls.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/trusted_constant_urls.local.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/trusted_constant_urls.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forwardref_imports.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forwardref_imports.local.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forwardref_imports.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/injector_providers.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/injector_providers.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_scope.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_scope.local.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_scope.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_with_providers.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_with_providers.local.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_with_providers.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/nested_array_imports.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/nested_array_imports.local.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/nested_array_imports.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/reexport_only.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/reexport_only.local.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/reexport_only.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/standalone_imports.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/standalone_imports.local.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/standalone_imports.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_decorators.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_decorators.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization_ambiguous_url.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization_ambiguous_url.local.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization_ambiguous_url.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/two_way_model_binding.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/two_way_model_binding.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/for_track_fn_with_empty.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/for_track_fn_with_empty.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_all_triggers.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_all_triggers.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/directive_injection.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/directive_injection.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/conditional_placeholders.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/conditional_placeholders.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_attributes.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_attributes.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/plural_icu.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/plural_icu.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/select_in_plural_icu.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/select_in_plural_icu.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/message_metadata_placeholders.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/message_metadata_placeholders.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/input_transforms.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/input_transforms.local.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/input_transforms.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/host_style_class_bindings.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/host_style_class_bindings.local.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/host_style_class_bindings.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/service_decorator/provided_in_variants.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/service_decorator/provided_in_variants.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/signal_inputs/signal_apis.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/signal_inputs/signal_apis.local.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/signal_inputs/signal_apis.ts diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/GOLDEN_PARTIAL.js index 0c329de40b6f..3ac07f9a408c 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/GOLDEN_PARTIAL.js @@ -818,3 +818,135 @@ export declare class TestCmp { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: change_detection.js + ****************************************************************************************************/ +import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; +import * as i0 from "@angular/core"; +export class OnPushComponent { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: OnPushComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: OnPushComponent, isStandalone: true, selector: "on-push", ngImport: i0, template: '
hello
', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom, preserveWhitespaces: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: OnPushComponent, decorators: [{ + type: Component, + args: [{ + selector: 'on-push', + template: '
hello
', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.ShadowDom, + preserveWhitespaces: true, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: change_detection.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class OnPushComponent { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: export_as_query.js + ****************************************************************************************************/ +import { Component, Directive, ViewChild } from '@angular/core'; +import * as i0 from "@angular/core"; +export class FooDirective { + open() { } + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: FooDirective, isStandalone: true, selector: "[foo]", exportAs: ["foo"], ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooDirective, decorators: [{ + type: Directive, + args: [{ + selector: '[foo]', + exportAs: 'foo', + }] + }] }); +export class AppComponent { + ref; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: AppComponent, isStandalone: true, selector: "app-root", viewQueries: [{ propertyName: "ref", first: true, predicate: ["ref"], descendants: true }], ngImport: i0, template: '
', isInline: true, dependencies: [{ kind: "directive", type: FooDirective, selector: "[foo]", exportAs: ["foo"] }] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, decorators: [{ + type: Component, + args: [{ + selector: 'app-root', + imports: [FooDirective], + template: '
', + }] + }], propDecorators: { ref: [{ + type: ViewChild, + args: ['ref'] + }] } }); + +/**************************************************************************************************** + * PARTIAL FILE: export_as_query.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class FooDirective { + open(): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} +export declare class AppComponent { + ref?: FooDirective; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: cross_file_inheritance_base.js + ****************************************************************************************************/ +import { Directive, Input } from '@angular/core'; +import * as i0 from "@angular/core"; +export class BaseDirective { + value = ''; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: BaseDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: BaseDirective, isStandalone: true, inputs: { value: "value" }, ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: BaseDirective, decorators: [{ + type: Directive + }], propDecorators: { value: [{ + type: Input + }] } }); + +/**************************************************************************************************** + * PARTIAL FILE: cross_file_inheritance_base.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class BaseDirective { + value: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: cross_file_inheritance.js + ****************************************************************************************************/ +import { Directive } from '@angular/core'; +import { BaseDirective } from './cross_file_inheritance_base'; +import * as i0 from "@angular/core"; +export class ChildDirective extends BaseDirective { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ChildDirective, deps: null, target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: ChildDirective, isStandalone: false, selector: "[child]", usesInheritance: true, ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ChildDirective, decorators: [{ + type: Directive, + args: [{ + selector: '[child]', + standalone: false, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: cross_file_inheritance.d.ts + ****************************************************************************************************/ +import { BaseDirective } from './cross_file_inheritance_base'; +import * as i0 from "@angular/core"; +export declare class ChildDirective extends BaseDirective { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/TEST_CASES.json index 2ed4e00e646d..990f0fadaf04 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/TEST_CASES.json @@ -195,6 +195,39 @@ } ], "compilationModeFilter": ["full compile", "instruction compile"] + }, + { + "description": "should omit the default `OnPush` change detection in full mode, keep it in local mode, and emit `ShadowDom` encapsulation", + "inputFiles": ["change_detection.ts"], + "expectations": [ + { + "failureMessage": "Invalid change detection / encapsulation metadata", + "files": ["change_detection.js"] + } + ], + "compilationModeFilter": ["full compile", "local compile"] + }, + { + "description": "should read a directive through a view query on a template reference bound via `exportAs`", + "inputFiles": ["export_as_query.ts"], + "expectations": [ + { + "failureMessage": "Invalid exportAs / template-ref query", + "files": ["export_as_query.js"] + } + ], + "compilationModeFilter": ["full compile"] + }, + { + "description": "should inherit the definition and factory for a directive that extends a base directive in another file", + "inputFiles": ["cross_file_inheritance.ts", "cross_file_inheritance_base.ts"], + "expectations": [ + { + "failureMessage": "Invalid cross-file inheritance", + "files": ["cross_file_inheritance.js"] + } + ], + "compilationModeFilter": ["full compile", "local compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/change_detection.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/change_detection.js new file mode 100644 index 000000000000..f62bd0a35f57 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/change_detection.js @@ -0,0 +1,6 @@ +… +export class OnPushComponent { + static ɵfac = function OnPushComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || OnPushComponent)(); }; + static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: OnPushComponent, selectors: [["on-push"]], … encapsulation: 3 }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/change_detection.local.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/change_detection.local.js new file mode 100644 index 000000000000..73feb9b54d14 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/change_detection.local.js @@ -0,0 +1,6 @@ +… +export class OnPushComponent { + static ɵfac = function OnPushComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || OnPushComponent)(); }; + static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: OnPushComponent, selectors: [["on-push"]], … encapsulation: 3, changeDetection: ChangeDetectionStrategy.OnPush }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/change_detection.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/change_detection.ts new file mode 100644 index 000000000000..ee7ca14c6690 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/change_detection.ts @@ -0,0 +1,10 @@ +import {ChangeDetectionStrategy, Component, ViewEncapsulation} from '@angular/core'; + +@Component({ + selector: 'on-push', + template: '
hello
', + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.ShadowDom, + preserveWhitespaces: true, +}) +export class OnPushComponent {} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/GOLDEN_PARTIAL.js index 7d41875edfeb..1901e19e08e2 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/GOLDEN_PARTIAL.js @@ -529,3 +529,61 @@ export declare class TestComponent { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: ng_project_as_multi_slot.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class CardComponent { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: CardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: CardComponent, isStandalone: true, selector: "card-cmp", ngImport: i0, template: ` +
+
+ `, isInline: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: CardComponent, decorators: [{ + type: Component, + args: [{ + selector: 'card-cmp', + template: ` +
+
+ `, + }] + }] }); +export class AppComponent { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: AppComponent, isStandalone: true, selector: "app-root", ngImport: i0, template: ` + +

Title

+

Body

+
+ `, isInline: true, dependencies: [{ kind: "component", type: CardComponent, selector: "card-cmp" }] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, decorators: [{ + type: Component, + args: [{ + selector: 'app-root', + imports: [CardComponent], + template: ` + +

Title

+

Body

+
+ `, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: ng_project_as_multi_slot.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class CardComponent { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} +export declare class AppComponent { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/TEST_CASES.json index d38021991ef4..bf0868333012 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/TEST_CASES.json @@ -170,6 +170,17 @@ ] } ] + }, + { + "description": "should support multi-slot content projection with an ngProjectAs attribute", + "inputFiles": ["ng_project_as_multi_slot.ts"], + "expectations": [ + { + "failureMessage": "Invalid content projection", + "files": ["ng_project_as_multi_slot.js"] + } + ], + "compilationModeFilter": ["full compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/ng_project_as_multi_slot.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/ng_project_as_multi_slot.js new file mode 100644 index 000000000000..395b4e55792f --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/ng_project_as_multi_slot.js @@ -0,0 +1,13 @@ +const $c0$ = [[["", "card-title", ""]], "*"]; +… +i0.ɵɵprojectionDef($c0$); +… +i0.ɵɵprojection(1); +… +i0.ɵɵprojection(3, 1); +… +export class AppComponent { + … + static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AppComponent, selectors: [["app-root"]], decls: 5, vars: 0, consts: [["ngProjectAs", "[card-title]", 5, ["", "card-title", ""]]], … }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/ng_project_as_multi_slot.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/ng_project_as_multi_slot.ts new file mode 100644 index 000000000000..25a861e5e6e1 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/content_projection/ng_project_as_multi_slot.ts @@ -0,0 +1,22 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'card-cmp', + template: ` +
+
+ `, +}) +export class CardComponent {} + +@Component({ + selector: 'app-root', + imports: [CardComponent], + template: ` + +

Title

+

Body

+
+ `, +}) +export class AppComponent {} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance.js new file mode 100644 index 000000000000..13c23186044c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance.js @@ -0,0 +1,6 @@ +… +export class ChildDirective extends BaseDirective { + static ɵfac = /*@__PURE__*/ (() => { let ɵChildDirective_BaseFactory; return function ChildDirective_Factory(__ngFactoryType__) { return (ɵChildDirective_BaseFactory || (ɵChildDirective_BaseFactory = i0.ɵɵgetInheritedFactory(ChildDirective)))(__ngFactoryType__ || ChildDirective); }; })(); + static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: ChildDirective, selectors: [["", "child", ""]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature] }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance.local.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance.local.js new file mode 100644 index 000000000000..13c23186044c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance.local.js @@ -0,0 +1,6 @@ +… +export class ChildDirective extends BaseDirective { + static ɵfac = /*@__PURE__*/ (() => { let ɵChildDirective_BaseFactory; return function ChildDirective_Factory(__ngFactoryType__) { return (ɵChildDirective_BaseFactory || (ɵChildDirective_BaseFactory = i0.ɵɵgetInheritedFactory(ChildDirective)))(__ngFactoryType__ || ChildDirective); }; })(); + static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: ChildDirective, selectors: [["", "child", ""]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature] }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance.ts new file mode 100644 index 000000000000..a4a712ba89d9 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance.ts @@ -0,0 +1,9 @@ +import {Directive} from '@angular/core'; + +import {BaseDirective} from './cross_file_inheritance_base'; + +@Directive({ + selector: '[child]', + standalone: false, +}) +export class ChildDirective extends BaseDirective {} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance_base.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance_base.ts new file mode 100644 index 000000000000..b547d2303e82 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/cross_file_inheritance_base.ts @@ -0,0 +1,6 @@ +import {Directive, Input} from '@angular/core'; + +@Directive() +export class BaseDirective { + @Input() value = ''; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/export_as_query.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/export_as_query.js new file mode 100644 index 000000000000..fa16d810885c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/export_as_query.js @@ -0,0 +1,20 @@ +const $c0$ = ["ref"]; +… +export class FooDirective { + … + static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: FooDirective, selectors: [["", "foo", ""]], exportAs: ["foo"] }); +} +… +export class AppComponent { + … + static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AppComponent, selectors: [["app-root"]], viewQuery: function AppComponent_Query(rf, ctx) { + if (rf & 1) { + i0.ɵɵviewQuery($c0$, 5); + } + if (rf & 2) { + let $t$; + i0.ɵɵqueryRefresh($t$ = i0.ɵɵloadQuery()) && (ctx.ref = $t$.first); + } + }, … dependencies: [FooDirective], encapsulation: 2 }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/export_as_query.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/export_as_query.ts new file mode 100644 index 000000000000..c13d47663ad5 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/export_as_query.ts @@ -0,0 +1,18 @@ +import {Component, Directive, ViewChild} from '@angular/core'; + +@Directive({ + selector: '[foo]', + exportAs: 'foo', +}) +export class FooDirective { + open() {} +} + +@Component({ + selector: 'app-root', + imports: [FooDirective], + template: '
', +}) +export class AppComponent { + @ViewChild('ref') ref?: FooDirective; +} 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 9632a3fdfd3c..5132bcd4a1a3 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 @@ -780,3 +780,63 @@ export declare class MyComponent { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: svg_xlink_href.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class AppComponent { + icon = ''; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: AppComponent, isStandalone: true, selector: "app-root", ngImport: i0, template: ``, isInline: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, decorators: [{ + type: Component, + args: [{ + selector: 'app-root', + template: ``, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: svg_xlink_href.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class AppComponent { + icon: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: trusted_constant_urls.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class AppComponent { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: AppComponent, isStandalone: true, selector: "app-root", ngImport: i0, template: ` + + + `, isInline: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, decorators: [{ + type: Component, + args: [{ + selector: 'app-root', + template: ` + + + `, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: trusted_constant_urls.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class AppComponent { + 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 31f925ce05bd..0d6816870bcd 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 @@ -276,6 +276,28 @@ "failureMessage": "Incorrect generated template." } ] + }, + { + "description": "should handle an SVG xlink:href attribute binding", + "inputFiles": ["svg_xlink_href.ts"], + "expectations": [ + { + "failureMessage": "Invalid SVG xlink:href binding", + "files": ["svg_xlink_href.js"] + } + ], + "compilationModeFilter": ["full compile"] + }, + { + "description": "should trust static security-sensitive resource-URL attributes as constants", + "inputFiles": ["trusted_constant_urls.ts"], + "expectations": [ + { + "failureMessage": "Invalid trusted constant resource URL", + "files": ["trusted_constant_urls.js"] + } + ], + "compilationModeFilter": ["full compile", "local compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/svg_xlink_href.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/svg_xlink_href.js new file mode 100644 index 000000000000..ad851afacfd5 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/svg_xlink_href.js @@ -0,0 +1,8 @@ +… +i0.ɵɵnamespaceSVG(); +i0.ɵɵdomElementStart(0, "svg"); +i0.ɵɵdomElement(1, "use"); +i0.ɵɵdomElementEnd(); +… +i0.ɵɵattribute("href", ctx.icon, null, "xlink"); +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/svg_xlink_href.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/svg_xlink_href.ts new file mode 100644 index 000000000000..06f09822f690 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/svg_xlink_href.ts @@ -0,0 +1,9 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'app-root', + template: ``, +}) +export class AppComponent { + icon = ''; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/trusted_constant_urls.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/trusted_constant_urls.js new file mode 100644 index 000000000000..af97afe89dfd --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/trusted_constant_urls.js @@ -0,0 +1,6 @@ +… +export class AppComponent { + … + static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AppComponent, selectors: [["app-root"]], decls: 2, vars: 0, consts: [["src", i0.ɵɵtrustConstantResourceUrl `https://www.example.com/embed`], ["src", i0.ɵɵtrustConstantResourceUrl `https://www.example.com/plugin`]], … }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/trusted_constant_urls.local.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/trusted_constant_urls.local.js new file mode 100644 index 000000000000..af97afe89dfd --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/trusted_constant_urls.local.js @@ -0,0 +1,6 @@ +… +export class AppComponent { + … + static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AppComponent, selectors: [["app-root"]], decls: 2, vars: 0, consts: [["src", i0.ɵɵtrustConstantResourceUrl `https://www.example.com/embed`], ["src", i0.ɵɵtrustConstantResourceUrl `https://www.example.com/plugin`]], … }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/trusted_constant_urls.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/trusted_constant_urls.ts new file mode 100644 index 000000000000..59655b18d81e --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/elements/trusted_constant_urls.ts @@ -0,0 +1,10 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'app-root', + template: ` + + + `, +}) +export class AppComponent {} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/GOLDEN_PARTIAL.js index 588db738859b..9fda94fac8a9 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/GOLDEN_PARTIAL.js @@ -771,3 +771,441 @@ export declare class MyModule { static ɵinj: i0.ɵɵInjectorDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: injector_providers.js + ****************************************************************************************************/ +import { Injectable, InjectionToken, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class Base { +} +export class Impl extends Base { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: Impl, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); + static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: Impl }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: Impl, decorators: [{ + type: Injectable + }] }); +export class Legacy { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: Legacy, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); + static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: Legacy }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: Legacy, decorators: [{ + type: Injectable + }] }); +export const TOKEN = new InjectionToken('TOKEN'); +export const MULTI = new InjectionToken('MULTI'); +export class ProvidersModule { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ProvidersModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ProvidersModule }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ProvidersModule, providers: [ + Impl, + { provide: Base, useClass: Impl }, + { provide: Legacy, useExisting: Impl }, + { provide: TOKEN, useValue: 'hello' }, + { provide: MULTI, useValue: 'a', multi: true }, + { provide: MULTI, useValue: 'b', multi: true }, + { provide: 'STR', useFactory: (b) => b, deps: [Base] }, + ] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ProvidersModule, decorators: [{ + type: NgModule, + args: [{ + providers: [ + Impl, + { provide: Base, useClass: Impl }, + { provide: Legacy, useExisting: Impl }, + { provide: TOKEN, useValue: 'hello' }, + { provide: MULTI, useValue: 'a', multi: true }, + { provide: MULTI, useValue: 'b', multi: true }, + { provide: 'STR', useFactory: (b) => b, deps: [Base] }, + ], + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: injector_providers.d.ts + ****************************************************************************************************/ +import { InjectionToken } from '@angular/core'; +import * as i0 from "@angular/core"; +export declare abstract class Base { +} +export declare class Impl extends Base { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵprov: i0.ɵɵInjectableDeclaration; +} +export declare class Legacy { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵprov: i0.ɵɵInjectableDeclaration; +} +export declare const TOKEN: InjectionToken; +export declare const MULTI: InjectionToken; +export declare class ProvidersModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: module_scope.js + ****************************************************************************************************/ +import { Component, Directive, NgModule, Pipe } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyComponent, isStandalone: false, selector: "my-cmp", 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-cmp', + template: '', + standalone: false, + }] + }] }); +export class MyDirective { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyDirective, isStandalone: false, selector: "[my-dir]", ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyDirective, decorators: [{ + type: Directive, + args: [{ + selector: '[my-dir]', + standalone: false, + }] + }] }); +export class MyPipe { + transform(value) { + return value; + } + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); + static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyPipe, isStandalone: false, name: "myPipe" }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyPipe, decorators: [{ + type: Pipe, + args: [{ + name: 'myPipe', + standalone: false, + }] + }] }); +export class SharedModule { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: SharedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: SharedModule }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: SharedModule }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: SharedModule, decorators: [{ + type: NgModule, + args: [{}] + }] }); +export class MyModule { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, declarations: [MyComponent, MyDirective, MyPipe], imports: [SharedModule], exports: [MyComponent, SharedModule] }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, imports: [SharedModule, SharedModule] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyModule, decorators: [{ + type: NgModule, + args: [{ + declarations: [MyComponent, MyDirective, MyPipe], + imports: [SharedModule], + exports: [MyComponent, SharedModule], + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: module_scope.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} +export declare class MyDirective { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} +export declare class MyPipe { + transform(value: unknown): unknown; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} +export declare class SharedModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} +export declare class MyModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: module_with_providers.js + ****************************************************************************************************/ +import { InjectionToken, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export const CONFIG = new InjectionToken('CONFIG'); +export class LibModule { + static forRoot(value) { + return { + ngModule: LibModule, + providers: [{ provide: CONFIG, useValue: value }], + }; + } + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: LibModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: LibModule }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: LibModule }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: LibModule, decorators: [{ + type: NgModule, + args: [{}] + }] }); +export class AppModule { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppModule, imports: [LibModule] }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppModule, imports: [LibModule.forRoot('app')] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppModule, decorators: [{ + type: NgModule, + args: [{ + imports: [LibModule.forRoot('app')], + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: module_with_providers.d.ts + ****************************************************************************************************/ +import { InjectionToken, ModuleWithProviders } from '@angular/core'; +import * as i0 from "@angular/core"; +export declare const CONFIG: InjectionToken; +export declare class LibModule { + static forRoot(value: string): ModuleWithProviders; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} +export declare class AppModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: nested_array_imports.js + ****************************************************************************************************/ +import { NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class ModA { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModA, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModA }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModA }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModA, decorators: [{ + type: NgModule, + args: [{}] + }] }); +export class ModB { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModB, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModB }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModB }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModB, decorators: [{ + type: NgModule, + args: [{}] + }] }); +export const GROUP = [ModA, ModB]; +// A nested-array import element and a referenced-constant import element. The compiler emits the +// injector `imports` preserving these composite elements. +export class NestedModule { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: NestedModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: NestedModule, imports: [ModA, ModB, ModA, ModB] }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: NestedModule, imports: [[ModA, ModB], GROUP] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: NestedModule, decorators: [{ + type: NgModule, + args: [{ + imports: [[ModA, ModB], GROUP], + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: nested_array_imports.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class ModA { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} +export declare class ModB { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} +export declare const GROUP: (typeof ModA)[]; +export declare class NestedModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: reexport_only.js + ****************************************************************************************************/ +import { NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class ChildModule { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ChildModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ChildModule }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ChildModule }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ChildModule, decorators: [{ + type: NgModule, + args: [{}] + }] }); +// `ChildModule` is only exported (never imported). In full mode the compiler still adds it to the +// injector's `imports` so its providers remain available transitively. +export class ParentModule { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ParentModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ParentModule, exports: [ChildModule] }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ParentModule, imports: [ChildModule] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ParentModule, decorators: [{ + type: NgModule, + args: [{ + exports: [ChildModule], + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: reexport_only.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class ChildModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} +export declare class ParentModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: standalone_imports.js + ****************************************************************************************************/ +import { Component, Directive, NgModule, Pipe } from '@angular/core'; +import * as i0 from "@angular/core"; +export class StandaloneComponent { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: StandaloneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: StandaloneComponent, isStandalone: true, selector: "std-cmp", ngImport: i0, template: '', isInline: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: StandaloneComponent, decorators: [{ + type: Component, + args: [{ + selector: 'std-cmp', + template: '', + }] + }] }); +export class StandaloneDirective { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: StandaloneDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: StandaloneDirective, isStandalone: true, selector: "[std-dir]", ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: StandaloneDirective, decorators: [{ + type: Directive, + args: [{ + selector: '[std-dir]', + }] + }] }); +export class StandalonePipe { + transform(value) { + return value; + } + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: StandalonePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); + static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: StandalonePipe, isStandalone: true, name: "stdPipe" }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: StandalonePipe, decorators: [{ + type: Pipe, + args: [{ + name: 'stdPipe', + }] + }] }); +export class ConsumerModule { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ConsumerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ConsumerModule, imports: [StandaloneComponent, StandaloneDirective, StandalonePipe], exports: [StandaloneComponent, StandaloneDirective, StandalonePipe] }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ConsumerModule }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ConsumerModule, decorators: [{ + type: NgModule, + args: [{ + imports: [StandaloneComponent, StandaloneDirective, StandalonePipe], + exports: [StandaloneComponent, StandaloneDirective, StandalonePipe], + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: standalone_imports.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class StandaloneComponent { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} +export declare class StandaloneDirective { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} +export declare class StandalonePipe { + transform(value: unknown): unknown; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} +export declare class ConsumerModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: forwardref_imports.js + ****************************************************************************************************/ +import { forwardRef, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +// `AppModule` references `LaterModule` before it is declared, via `forwardRef`. In full mode the +// compiler resolves the forwardRef and emits the bare module reference in the injector imports. +export class AppModule { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppModule, imports: () => [LaterModule] }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppModule, imports: [forwardRef(() => LaterModule)] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppModule, decorators: [{ + type: NgModule, + args: [{ + imports: [forwardRef(() => LaterModule)], + }] + }] }); +export class LaterModule { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: LaterModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: LaterModule }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: LaterModule }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: LaterModule, decorators: [{ + type: NgModule, + args: [{}] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: forwardref_imports.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class AppModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} +export declare class LaterModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/TEST_CASES.json index bbef60a9321b..a8dd3fcac43c 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/TEST_CASES.json @@ -176,6 +176,83 @@ } ], "compilationModeFilter": ["full compile", "local compile"] + }, + { + "description": "should define an NgModule and injector with `useClass`, `useExisting`, `useValue`, `multi` and `useFactory` providers", + "inputFiles": ["injector_providers.ts"], + "expectations": [ + { + "failureMessage": "Invalid injector providers", + "files": ["injector_providers.js"] + } + ], + "compilationModeFilter": ["full compile"] + }, + { + "description": "should define the JIT NgModule scope with declarations, imports and exports", + "inputFiles": ["module_scope.ts"], + "expectations": [ + { + "failureMessage": "Invalid NgModule scope", + "files": ["module_scope.js"] + } + ], + "compilationModeFilter": ["full compile", "local compile"] + }, + { + "description": "should keep a same-file `forRoot()` `ModuleWithProviders` call in the injector and unwrap the module in the scope", + "inputFiles": ["module_with_providers.ts"], + "expectations": [ + { + "failureMessage": "Invalid ModuleWithProviders handling", + "files": ["module_with_providers.js"] + } + ], + "compilationModeFilter": ["full compile", "local compile"] + }, + { + "description": "should preserve nested-array and constant module imports in the injector and flatten them in the scope", + "inputFiles": ["nested_array_imports.ts"], + "expectations": [ + { + "failureMessage": "Invalid nested-array import handling", + "files": ["nested_array_imports.js"] + } + ], + "compilationModeFilter": ["full compile", "local compile"] + }, + { + "description": "should add a re-exported (exports-only) module to the injector imports", + "inputFiles": ["reexport_only.ts"], + "expectations": [ + { + "failureMessage": "Invalid re-exported module handling", + "files": ["reexport_only.js"] + } + ], + "compilationModeFilter": ["full compile", "local compile"] + }, + { + "description": "should exclude standalone declarables from the injector imports but keep them in the scope", + "inputFiles": ["standalone_imports.ts"], + "expectations": [ + { + "failureMessage": "Invalid standalone import handling", + "files": ["standalone_imports.js"] + } + ], + "compilationModeFilter": ["full compile", "local compile"] + }, + { + "description": "should keep a `forwardRef` module import in the injector and lower it to a closure in the scope", + "inputFiles": ["forwardref_imports.ts"], + "expectations": [ + { + "failureMessage": "Invalid forwardRef import handling", + "files": ["forwardref_imports.js"] + } + ], + "compilationModeFilter": ["full compile", "local compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forwardref_imports.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forwardref_imports.js new file mode 100644 index 000000000000..7b43469cb40a --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forwardref_imports.js @@ -0,0 +1,9 @@ +… +export class AppModule { + static ɵfac = function AppModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || AppModule)(); }; + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: AppModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [forwardRef(() => LaterModule)] }); +} +… +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(AppModule, { imports: () => [LaterModule] }); })(); +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forwardref_imports.local.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forwardref_imports.local.js new file mode 100644 index 000000000000..e4c23387b586 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forwardref_imports.local.js @@ -0,0 +1,9 @@ +… +export class AppModule { + static ɵfac = function AppModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || AppModule)(); }; + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: AppModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [forwardRef(() => LaterModule)] }); +} +… +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(AppModule, { imports: [forwardRef(() => LaterModule)] }); })(); +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forwardref_imports.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forwardref_imports.ts new file mode 100644 index 000000000000..fafea27d9b77 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forwardref_imports.ts @@ -0,0 +1,11 @@ +import {forwardRef, NgModule} from '@angular/core'; + +// `AppModule` references `LaterModule` before it is declared, via `forwardRef`. In full mode the +// compiler resolves the forwardRef and emits the bare module reference in the injector imports. +@NgModule({ + imports: [forwardRef(() => LaterModule)], +}) +export class AppModule {} + +@NgModule({}) +export class LaterModule {} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/injector_providers.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/injector_providers.js new file mode 100644 index 000000000000..b16c617f8f3c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/injector_providers.js @@ -0,0 +1,15 @@ +… +export class ProvidersModule { + static ɵfac = function ProvidersModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ProvidersModule)(); }; + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: ProvidersModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ providers: [ + Impl, + { provide: Base, useClass: Impl }, + { provide: Legacy, useExisting: Impl }, + { provide: TOKEN, useValue: 'hello' }, + { provide: MULTI, useValue: 'a', multi: true }, + { provide: MULTI, useValue: 'b', multi: true }, + { provide: 'STR', useFactory: …b… => b, deps: [Base] }, + ] }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/injector_providers.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/injector_providers.ts new file mode 100644 index 000000000000..521f669c5387 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/injector_providers.ts @@ -0,0 +1,25 @@ +import {Injectable, InjectionToken, NgModule} from '@angular/core'; + +export abstract class Base {} + +@Injectable() +export class Impl extends Base {} + +@Injectable() +export class Legacy {} + +export const TOKEN = new InjectionToken('TOKEN'); +export const MULTI = new InjectionToken('MULTI'); + +@NgModule({ + providers: [ + Impl, + {provide: Base, useClass: Impl}, + {provide: Legacy, useExisting: Impl}, + {provide: TOKEN, useValue: 'hello'}, + {provide: MULTI, useValue: 'a', multi: true}, + {provide: MULTI, useValue: 'b', multi: true}, + {provide: 'STR', useFactory: (b: Base) => b, deps: [Base]}, + ], +}) +export class ProvidersModule {} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_scope.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_scope.js new file mode 100644 index 000000000000..cf47f80a2baa --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_scope.js @@ -0,0 +1,8 @@ +… +export class MyModule { + static ɵfac = function MyModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MyModule)(); }; + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: MyModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [SharedModule, SharedModule] }); +} +… +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent, MyDirective, MyPipe], imports: [SharedModule], exports: [MyComponent, SharedModule] }); })(); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_scope.local.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_scope.local.js new file mode 100644 index 000000000000..d472731923f3 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_scope.local.js @@ -0,0 +1,8 @@ +… +export class MyModule { + static ɵfac = function MyModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MyModule)(); }; + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: MyModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [SharedModule, MyComponent, SharedModule] }); +} +… +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent, MyDirective, MyPipe], imports: [SharedModule], exports: [MyComponent, SharedModule] }); })(); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_scope.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_scope.ts new file mode 100644 index 000000000000..1809642c9398 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_scope.ts @@ -0,0 +1,34 @@ +import {Component, Directive, NgModule, Pipe} from '@angular/core'; + +@Component({ + selector: 'my-cmp', + template: '', + standalone: false, +}) +export class MyComponent {} + +@Directive({ + selector: '[my-dir]', + standalone: false, +}) +export class MyDirective {} + +@Pipe({ + name: 'myPipe', + standalone: false, +}) +export class MyPipe { + transform(value: unknown) { + return value; + } +} + +@NgModule({}) +export class SharedModule {} + +@NgModule({ + declarations: [MyComponent, MyDirective, MyPipe], + imports: [SharedModule], + exports: [MyComponent, SharedModule], +}) +export class MyModule {} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_with_providers.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_with_providers.js new file mode 100644 index 000000000000..f39408bba309 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_with_providers.js @@ -0,0 +1,8 @@ +… +export class AppModule { + static ɵfac = function AppModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || AppModule)(); }; + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: AppModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [LibModule.forRoot('app')] }); +} +… +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(AppModule, { imports: [LibModule] }); })(); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_with_providers.local.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_with_providers.local.js new file mode 100644 index 000000000000..7c021c93f02e --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_with_providers.local.js @@ -0,0 +1,8 @@ +… +export class AppModule { + static ɵfac = function AppModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || AppModule)(); }; + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: AppModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [LibModule.forRoot('app')] }); +} +… +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(AppModule, { imports: [LibModule.forRoot('app')] }); })(); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_with_providers.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_with_providers.ts new file mode 100644 index 000000000000..ed169bd6327f --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/module_with_providers.ts @@ -0,0 +1,18 @@ +import {InjectionToken, ModuleWithProviders, NgModule} from '@angular/core'; + +export const CONFIG = new InjectionToken('CONFIG'); + +@NgModule({}) +export class LibModule { + static forRoot(value: string): ModuleWithProviders { + return { + ngModule: LibModule, + providers: [{provide: CONFIG, useValue: value}], + }; + } +} + +@NgModule({ + imports: [LibModule.forRoot('app')], +}) +export class AppModule {} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/nested_array_imports.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/nested_array_imports.js new file mode 100644 index 000000000000..7de51009111d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/nested_array_imports.js @@ -0,0 +1,8 @@ +… +export class NestedModule { + static ɵfac = function NestedModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || NestedModule)(); }; + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: NestedModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [[ModA, ModB], GROUP] }); +} +… +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(NestedModule, { imports: [ModA, ModB, ModA, ModB] }); })(); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/nested_array_imports.local.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/nested_array_imports.local.js new file mode 100644 index 000000000000..bd6ce1f7a61c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/nested_array_imports.local.js @@ -0,0 +1,8 @@ +… +export class NestedModule { + static ɵfac = function NestedModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || NestedModule)(); }; + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: NestedModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [[ModA, ModB], GROUP] }); +} +… +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(NestedModule, { imports: [[ModA, ModB], GROUP] }); })(); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/nested_array_imports.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/nested_array_imports.ts new file mode 100644 index 000000000000..0adc636a2dc4 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/nested_array_imports.ts @@ -0,0 +1,16 @@ +import {NgModule} from '@angular/core'; + +@NgModule({}) +export class ModA {} + +@NgModule({}) +export class ModB {} + +export const GROUP = [ModA, ModB]; + +// A nested-array import element and a referenced-constant import element. The compiler emits the +// injector `imports` preserving these composite elements. +@NgModule({ + imports: [[ModA, ModB], GROUP], +}) +export class NestedModule {} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/reexport_only.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/reexport_only.js new file mode 100644 index 000000000000..5c187cfb30e5 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/reexport_only.js @@ -0,0 +1,8 @@ +… +export class ParentModule { + static ɵfac = function ParentModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ParentModule)(); }; + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: ParentModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [ChildModule] }); +} +… +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(ParentModule, { exports: [ChildModule] }); })(); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/reexport_only.local.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/reexport_only.local.js new file mode 100644 index 000000000000..5c187cfb30e5 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/reexport_only.local.js @@ -0,0 +1,8 @@ +… +export class ParentModule { + static ɵfac = function ParentModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ParentModule)(); }; + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: ParentModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [ChildModule] }); +} +… +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(ParentModule, { exports: [ChildModule] }); })(); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/reexport_only.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/reexport_only.ts new file mode 100644 index 000000000000..ef4c7af4207f --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/reexport_only.ts @@ -0,0 +1,11 @@ +import {NgModule} from '@angular/core'; + +@NgModule({}) +export class ChildModule {} + +// `ChildModule` is only exported (never imported). In full mode the compiler still adds it to the +// injector's `imports` so its providers remain available transitively. +@NgModule({ + exports: [ChildModule], +}) +export class ParentModule {} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/standalone_imports.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/standalone_imports.js new file mode 100644 index 000000000000..32c53e50e92c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/standalone_imports.js @@ -0,0 +1,8 @@ +… +export class ConsumerModule { + static ɵfac = function ConsumerModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ConsumerModule)(); }; + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: ConsumerModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({}); +} +… +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(ConsumerModule, { imports: [StandaloneComponent, StandaloneDirective, StandalonePipe], exports: [StandaloneComponent, StandaloneDirective, StandalonePipe] }); })(); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/standalone_imports.local.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/standalone_imports.local.js new file mode 100644 index 000000000000..35c2844ea213 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/standalone_imports.local.js @@ -0,0 +1,8 @@ +… +export class ConsumerModule { + static ɵfac = function ConsumerModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ConsumerModule)(); }; + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: ConsumerModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [StandaloneComponent, StandaloneDirective, StandalonePipe, StandaloneComponent, StandaloneDirective, StandalonePipe] }); +} +… +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(ConsumerModule, { imports: [StandaloneComponent, StandaloneDirective, StandalonePipe], exports: [StandaloneComponent, StandaloneDirective, StandalonePipe] }); })(); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/standalone_imports.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/standalone_imports.ts new file mode 100644 index 000000000000..7aee928ba66d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/standalone_imports.ts @@ -0,0 +1,27 @@ +import {Component, Directive, NgModule, Pipe} from '@angular/core'; + +@Component({ + selector: 'std-cmp', + template: '', +}) +export class StandaloneComponent {} + +@Directive({ + selector: '[std-dir]', +}) +export class StandaloneDirective {} + +@Pipe({ + name: 'stdPipe', +}) +export class StandalonePipe { + transform(value: unknown) { + return value; + } +} + +@NgModule({ + imports: [StandaloneComponent, StandaloneDirective, StandalonePipe], + exports: [StandaloneComponent, StandaloneDirective, StandalonePipe], +}) +export class ConsumerModule {} 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 993805dc69ac..51d3493c882d 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 @@ -1202,3 +1202,94 @@ export declare class MyComponent2 { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: host_decorators.js + ****************************************************************************************************/ +import { Directive, HostBinding, HostListener } from '@angular/core'; +import * as i0 from "@angular/core"; +export class HostStuffDirective { + active = false; + label = 'x'; + get isActive() { + return this.active; + } + ariaLabel = this.label; + onClick(event) { } + onResize() { } + onEnter() { } + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HostStuffDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: HostStuffDirective, isStandalone: true, selector: "[hostStuff]", host: { listeners: { "mouseenter": "onEnter()", "click": "onClick($event)", "window:resize": "onResize()" }, properties: { "attr.role": "\"button\"", "class.is-active": "this.isActive", "attr.aria-label": "this.ariaLabel" } }, ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HostStuffDirective, decorators: [{ + type: Directive, + args: [{ + selector: '[hostStuff]', + host: { + '[attr.role]': '"button"', + '(mouseenter)': 'onEnter()', + }, + }] + }], propDecorators: { isActive: [{ + type: HostBinding, + args: ['class.is-active'] + }], ariaLabel: [{ + type: HostBinding, + args: ['attr.aria-label'] + }], onClick: [{ + type: HostListener, + args: ['click', ['$event']] + }], onResize: [{ + type: HostListener, + args: ['window:resize'] + }] } }); + +/**************************************************************************************************** + * PARTIAL FILE: host_decorators.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class HostStuffDirective { + active: boolean; + label: string; + get isActive(): boolean; + ariaLabel: string; + onClick(event: Event): void; + onResize(): void; + onEnter(): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: sanitization_ambiguous_url.js + ****************************************************************************************************/ +import { Directive } from '@angular/core'; +import * as i0 from "@angular/core"; +// An attribute-only selector: the directive can be placed on ANY element, so `src`/`href` belong +// to both the URL and RESOURCE_URL security contexts. The compiler must defer the choice to +// runtime via ɵɵsanitizeUrlOrResourceUrl. +export class AmbiguousUrlDirective { + value = ''; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AmbiguousUrlDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: AmbiguousUrlDirective, isStandalone: true, selector: "[ambiguousUrl]", host: { properties: { "attr.src": "value", "attr.href": "value" } }, ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AmbiguousUrlDirective, decorators: [{ + type: Directive, + args: [{ + selector: '[ambiguousUrl]', + host: { + '[attr.src]': 'value', + '[attr.href]': 'value', + }, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: sanitization_ambiguous_url.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class AmbiguousUrlDirective { + value: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} + 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 d923b21fde57..f0c76ae55f45 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 @@ -310,6 +310,28 @@ ] } ] + }, + { + "description": "should support host bindings declared with @HostBinding and @HostListener merged with the host object", + "inputFiles": ["host_decorators.ts"], + "expectations": [ + { + "failureMessage": "Invalid host bindings merge", + "files": ["host_decorators.js"] + } + ], + "compilationModeFilter": ["full compile"] + }, + { + "description": "should support host bindings that sanitize an ambiguous src/href on an attribute-only directive", + "inputFiles": ["sanitization_ambiguous_url.ts"], + "expectations": [ + { + "failureMessage": "Invalid ambiguous-URL host sanitizer", + "files": ["sanitization_ambiguous_url.js"] + } + ], + "compilationModeFilter": ["full compile", "local compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_decorators.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_decorators.js new file mode 100644 index 000000000000..3ad5f3478223 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_decorators.js @@ -0,0 +1,14 @@ +… +export class HostStuffDirective { + … + static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: HostStuffDirective, selectors: [["", "hostStuff", ""]], hostVars: 4, hostBindings: function HostStuffDirective_HostBindings(rf, ctx) { + if (rf & 1) { + i0.ɵɵlistener("mouseenter", function HostStuffDirective_mouseenter_HostBindingHandler() { return ctx.onEnter(); })("click", function HostStuffDirective_click_HostBindingHandler($event) { return ctx.onClick($event); })("resize", function HostStuffDirective_resize_HostBindingHandler() { return ctx.onResize(); }, i0.ɵɵresolveWindow); + } + if (rf & 2) { + i0.ɵɵattribute("role", "button")("aria-label", ctx.ariaLabel); + i0.ɵɵclassProp("is-active", ctx.isActive); + } + } }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_decorators.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_decorators.ts new file mode 100644 index 000000000000..6f1a91ba613a --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_decorators.ts @@ -0,0 +1,25 @@ +import {Directive, HostBinding, HostListener} from '@angular/core'; + +@Directive({ + selector: '[hostStuff]', + host: { + '[attr.role]': '"button"', + '(mouseenter)': 'onEnter()', + }, +}) +export class HostStuffDirective { + active = false; + label = 'x'; + + @HostBinding('class.is-active') get isActive() { + return this.active; + } + + @HostBinding('attr.aria-label') ariaLabel = this.label; + + @HostListener('click', ['$event']) onClick(event: Event) {} + + @HostListener('window:resize') onResize() {} + + onEnter() {} +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization_ambiguous_url.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization_ambiguous_url.js new file mode 100644 index 000000000000..788725e6d091 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization_ambiguous_url.js @@ -0,0 +1,10 @@ +… +export class AmbiguousUrlDirective { + … + static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: AmbiguousUrlDirective, selectors: [["", "ambiguousUrl", ""]], hostVars: 2, hostBindings: function AmbiguousUrlDirective_HostBindings(rf, ctx) { + if (rf & 2) { + i0.ɵɵattribute("src", ctx.value, i0.ɵɵsanitizeUrlOrResourceUrl)("href", ctx.value, i0.ɵɵsanitizeUrlOrResourceUrl); + } + } }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization_ambiguous_url.local.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization_ambiguous_url.local.js new file mode 100644 index 000000000000..788725e6d091 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization_ambiguous_url.local.js @@ -0,0 +1,10 @@ +… +export class AmbiguousUrlDirective { + … + static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: AmbiguousUrlDirective, selectors: [["", "ambiguousUrl", ""]], hostVars: 2, hostBindings: function AmbiguousUrlDirective_HostBindings(rf, ctx) { + if (rf & 2) { + i0.ɵɵattribute("src", ctx.value, i0.ɵɵsanitizeUrlOrResourceUrl)("href", ctx.value, i0.ɵɵsanitizeUrlOrResourceUrl); + } + } }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization_ambiguous_url.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization_ambiguous_url.ts new file mode 100644 index 000000000000..c02207c9c0a0 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/sanitization_ambiguous_url.ts @@ -0,0 +1,15 @@ +import {Directive} from '@angular/core'; + +// An attribute-only selector: the directive can be placed on ANY element, so `src`/`href` belong +// to both the URL and RESOURCE_URL security contexts. The compiler must defer the choice to +// runtime via ɵɵsanitizeUrlOrResourceUrl. +@Directive({ + selector: '[ambiguousUrl]', + host: { + '[attr.src]': 'value', + '[attr.href]': 'value', + }, +}) +export class AmbiguousUrlDirective { + value = ''; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/GOLDEN_PARTIAL.js index e4b8e4d946a3..7c6bab78881e 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/GOLDEN_PARTIAL.js @@ -1012,3 +1012,49 @@ export declare class App { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: two_way_model_binding.js + ****************************************************************************************************/ +import { Component, Directive, model } from '@angular/core'; +import * as i0 from "@angular/core"; +export class ToggleDirective { + checked = model(false, /* @ts-ignore */ + ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ [])); + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ToggleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "0.0.0-PLACEHOLDER", type: ToggleDirective, isStandalone: true, selector: "[toggle]", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ToggleDirective, decorators: [{ + type: Directive, + args: [{ + selector: '[toggle]', + }] + }], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }] } }); +export class AppComponent { + isChecked = false; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: AppComponent, isStandalone: true, selector: "app-root", ngImport: i0, template: '
', isInline: true, dependencies: [{ kind: "directive", type: ToggleDirective, selector: "[toggle]", inputs: ["checked"], outputs: ["checkedChange"] }] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, decorators: [{ + type: Component, + args: [{ + selector: 'app-root', + imports: [ToggleDirective], + template: '
', + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: two_way_model_binding.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class ToggleDirective { + checked: import("@angular/core").ModelSignal; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} +export declare class AppComponent { + isChecked: boolean; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/TEST_CASES.json index 7daf95b5705a..c0ce3ef5a1f4 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/TEST_CASES.json @@ -192,6 +192,17 @@ "files": ["mixed_one_way_two_way_property_order.js"] } ] + }, + { + "description": "should generate two-way binding instructions for a signal model", + "inputFiles": ["two_way_model_binding.ts"], + "expectations": [ + { + "failureMessage": "Invalid two-way model binding", + "files": ["two_way_model_binding.js"] + } + ], + "compilationModeFilter": ["full compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/two_way_model_binding.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/two_way_model_binding.js new file mode 100644 index 000000000000..69ed9813a86c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/two_way_model_binding.js @@ -0,0 +1,15 @@ +… +export class AppComponent { + … + static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AppComponent, selectors: [["app-root"]], decls: 1, vars: 1, consts: [["toggle", "", 3, "checkedChange", "checked"]], template: function AppComponent_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵelementStart(0, "div", 0); + i0.ɵɵtwoWayListener("checkedChange", function AppComponent_Template_div_checkedChange_0_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.isChecked, $event) || (ctx.isChecked = $event); return $event; }); + i0.ɵɵelementEnd(); + } + if (rf & 2) { + i0.ɵɵtwoWayProperty("checked", ctx.isChecked); + } + }, dependencies: [ToggleDirective], encapsulation: 2 }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/two_way_model_binding.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/two_way_model_binding.ts new file mode 100644 index 000000000000..9e534355bdcf --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/two_way_model_binding.ts @@ -0,0 +1,17 @@ +import {Component, Directive, model} from '@angular/core'; + +@Directive({ + selector: '[toggle]', +}) +export class ToggleDirective { + checked = model(false); +} + +@Component({ + selector: 'app-root', + imports: [ToggleDirective], + template: '
', +}) +export class AppComponent { + isChecked = false; +} 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 acf2665caabd..79521641b627 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 @@ -2933,3 +2933,46 @@ export declare class MyApp { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: for_track_fn_with_empty.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class AppComponent { + items = []; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: AppComponent, isStandalone: true, selector: "app-root", ngImport: i0, template: ` + @for (item of items; track item.id; let i = $index) { +
{{ i }}: {{ item.name }}
+ } @empty { +
empty
+ } + `, isInline: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, decorators: [{ + type: Component, + args: [{ + selector: 'app-root', + template: ` + @for (item of items; track item.id; let i = $index) { +
{{ i }}: {{ item.name }}
+ } @empty { +
empty
+ } + `, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: for_track_fn_with_empty.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class AppComponent { + items: { + id: number; + name: string; + }[]; + 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 2c6c9c4f50ac..30d69ea465a1 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 @@ -805,6 +805,17 @@ ] } ] + }, + { + "description": "should generate a @for block with a track function and an @empty block", + "inputFiles": ["for_track_fn_with_empty.ts"], + "expectations": [ + { + "failureMessage": "Invalid @for repeater compilation", + "files": ["for_track_fn_with_empty.js"] + } + ], + "compilationModeFilter": ["full compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/for_track_fn_with_empty.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/for_track_fn_with_empty.js new file mode 100644 index 000000000000..1e8b750fa209 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/for_track_fn_with_empty.js @@ -0,0 +1,14 @@ +const $track$ = ($index, $item) => $item.id; +… +export class AppComponent { + … + static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AppComponent, selectors: [["app-root"]], decls: 3, vars: 1, template: function AppComponent_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵrepeaterCreate(0, AppComponent_For_1_Template, 2, 2, "div", null, $track$, false, AppComponent_ForEmpty_2_Template, 2, 0, "div"); + } + if (rf & 2) { + i0.ɵɵrepeater(ctx.items); + } + }, encapsulation: 2 }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/for_track_fn_with_empty.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/for_track_fn_with_empty.ts new file mode 100644 index 000000000000..0ecf0b571a29 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/for_track_fn_with_empty.ts @@ -0,0 +1,15 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'app-root', + template: ` + @for (item of items; track item.id; let i = $index) { +
{{ i }}: {{ item.name }}
+ } @empty { +
empty
+ } + `, +}) +export class AppComponent { + items: {id: number; name: string}[] = []; +} 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 45339e471448..8c29c6a6e880 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 @@ -1547,3 +1547,67 @@ export declare class MyApp { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: deferred_all_triggers.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class HeavyComponent { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HeavyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: HeavyComponent, isStandalone: true, selector: "heavy-cmp", ngImport: i0, template: 'heavy', isInline: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HeavyComponent, decorators: [{ + type: Component, + args: [{ + selector: 'heavy-cmp', + template: 'heavy', + }] + }] }); +export class AppComponent { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: AppComponent, isStandalone: true, selector: "app-root", ngImport: i0, template: ` + + @defer (on interaction(trigger); prefetch on idle) { + + } @placeholder (minimum 500ms) { + placeholder + } @loading (after 100ms; minimum 1s) { + loading + } @error { + error + } + `, isInline: true, deferBlockDependencies: [() => [HeavyComponent]] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, decorators: [{ + type: Component, + args: [{ + selector: 'app-root', + imports: [HeavyComponent], + template: ` + + @defer (on interaction(trigger); prefetch on idle) { + + } @placeholder (minimum 500ms) { + placeholder + } @loading (after 100ms; minimum 1s) { + loading + } @error { + error + } + `, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: deferred_all_triggers.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class HeavyComponent { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} +export declare class AppComponent { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + 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 103c1f8ef92d..92d0cc5a3bff 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 @@ -423,6 +423,17 @@ "failureMessage": "Incorrect template" } ] + }, + { + "description": "should generate a deferred block with an interaction trigger, prefetch on idle, and placeholder/loading parameters", + "inputFiles": ["deferred_all_triggers.ts"], + "expectations": [ + { + "failureMessage": "Invalid defer block compilation", + "files": ["deferred_all_triggers.js"] + } + ], + "compilationModeFilter": ["full compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_all_triggers.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_all_triggers.js new file mode 100644 index 000000000000..47806a3fbbdf --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_all_triggers.js @@ -0,0 +1,14 @@ +const $DepsFn$ = () => [HeavyComponent]; +… +export class AppComponent { + … + static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AppComponent, selectors: [["app-root"]], decls: 9, vars: 0, consts: [["trigger", ""], [1000, 100], [500]], template: function AppComponent_Template(rf, ctx) { + if (rf & 1) { + … + i0.ɵɵdefer(7, 3, $DepsFn$, 4, 5, 6, 1, 2, i0.ɵɵdeferEnableTimerScheduling); + i0.ɵɵdeferOnInteraction(0); + i0.ɵɵdeferPrefetchOnIdle(); + } + }, … }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_all_triggers.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_all_triggers.ts new file mode 100644 index 000000000000..7f501ba2211e --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/deferred_all_triggers.ts @@ -0,0 +1,25 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'heavy-cmp', + template: 'heavy', +}) +export class HeavyComponent {} + +@Component({ + selector: 'app-root', + imports: [HeavyComponent], + template: ` + + @defer (on interaction(trigger); prefetch on idle) { + + } @placeholder (minimum 500ms) { + placeholder + } @loading (after 100ms; minimum 1s) { + loading + } @error { + error + } + `, +}) +export class AppComponent {} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/GOLDEN_PARTIAL.js index 1f80e591e47f..6a7ce1242339 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/GOLDEN_PARTIAL.js @@ -435,3 +435,47 @@ export declare class MyModule { } export {}; +/**************************************************************************************************** + * PARTIAL FILE: directive_injection.js + ****************************************************************************************************/ +import { Attribute, Directive, ElementRef, Host, Inject, InjectionToken, Optional, Self, SkipSelf, } from '@angular/core'; +import * as i0 from "@angular/core"; +export const TOKEN = new InjectionToken('TOKEN'); +export class WithDepsDirective { + constructor(element, name, token, self, host) { } + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: WithDepsDirective, deps: [{ token: i0.ElementRef }, { token: 'name', attribute: true }, { token: TOKEN, optional: true }, { token: i0.ElementRef, self: true }, { token: i0.ElementRef, host: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: WithDepsDirective, isStandalone: true, selector: "[withDeps]", ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: WithDepsDirective, decorators: [{ + type: Directive, + args: [{ + selector: '[withDeps]', + }] + }], ctorParameters: () => [{ type: i0.ElementRef }, { type: undefined, decorators: [{ + type: Attribute, + args: ['name'] + }] }, { type: undefined, decorators: [{ + type: Inject, + args: [TOKEN] + }, { + type: Optional + }] }, { type: i0.ElementRef, decorators: [{ + type: Self + }] }, { type: i0.ElementRef, decorators: [{ + type: SkipSelf + }, { + type: Host + }] }] }); + +/**************************************************************************************************** + * PARTIAL FILE: directive_injection.d.ts + ****************************************************************************************************/ +import { ElementRef, InjectionToken } from '@angular/core'; +import * as i0 from "@angular/core"; +export declare const TOKEN: InjectionToken; +export declare class WithDepsDirective { + constructor(element: ElementRef, name: string, token: string | null, self: ElementRef, host: ElementRef); + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/TEST_CASES.json index 33a537c242d6..68a7248420f0 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/TEST_CASES.json @@ -188,6 +188,17 @@ ] } ] + }, + { + "description": "should encode the `@Optional`, `@Self`, `@SkipSelf`, `@Host` and `@Attribute` injection flags in a directive factory", + "inputFiles": ["directive_injection.ts"], + "expectations": [ + { + "failureMessage": "Invalid directive DI factory", + "files": ["directive_injection.js"] + } + ], + "compilationModeFilter": ["full compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/directive_injection.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/directive_injection.js new file mode 100644 index 000000000000..9e411e847731 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/directive_injection.js @@ -0,0 +1,7 @@ +… +export class WithDepsDirective { + … + static ɵfac = function WithDepsDirective_Factory(__ngFactoryType__) { … return new (__ngFactoryType__ || WithDepsDirective)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵinjectAttribute('name'), i0.ɵɵdirectiveInject(TOKEN, 8), i0.ɵɵdirectiveInject(i0.ElementRef, 2), i0.ɵɵdirectiveInject(i0.ElementRef, 5)); }; + static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: WithDepsDirective, selectors: [["", "withDeps", ""]] }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/directive_injection.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/directive_injection.ts new file mode 100644 index 000000000000..7f839b56a0b6 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_di/di/directive_injection.ts @@ -0,0 +1,26 @@ +import { + Attribute, + Directive, + ElementRef, + Host, + Inject, + InjectionToken, + Optional, + Self, + SkipSelf, +} from '@angular/core'; + +export const TOKEN = new InjectionToken('TOKEN'); + +@Directive({ + selector: '[withDeps]', +}) +export class WithDepsDirective { + constructor( + element: ElementRef, + @Attribute('name') name: string, + @Inject(TOKEN) @Optional() token: string | null, + @Self() self: ElementRef, + @SkipSelf() @Host() host: ElementRef, + ) {} +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/GOLDEN_PARTIAL.js index 7c0d70838f95..3ce4222cae28 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/GOLDEN_PARTIAL.js @@ -198,3 +198,33 @@ export declare class MyComponent { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: message_metadata_placeholders.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class AppComponent { + name = 'world'; + count = 0; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: AppComponent, isStandalone: true, selector: "app-root", ngImport: i0, template: `
Hello {{name}}, you have {{count}} new messages
`, isInline: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, decorators: [{ + type: Component, + args: [{ + selector: 'app-root', + template: `
Hello {{name}}, you have {{count}} new messages
`, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: message_metadata_placeholders.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class AppComponent { + name: string; + count: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/TEST_CASES.json index e12040bd2b61..97df096ffc28 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/TEST_CASES.json @@ -34,6 +34,17 @@ "inputFiles": [ "icu_and_i18n.ts" ] + }, + { + "description": "should support an i18n message with `meaning|description@@id` metadata and placeholders", + "inputFiles": ["message_metadata_placeholders.ts"], + "expectations": [ + { + "failureMessage": "Invalid i18n $localize message", + "files": ["message_metadata_placeholders.js"] + } + ], + "compilationModeFilter": ["full compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/GOLDEN_PARTIAL.js index c1598b8adef3..438b0b976f14 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/GOLDEN_PARTIAL.js @@ -202,3 +202,33 @@ export declare class MyApp { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: conditional_placeholders.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class AppComponent { + show = true; + name = 'x'; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: AppComponent, isStandalone: true, selector: "app-root", ngImport: i0, template: `
Before @if (show) {shown {{name}}} @else {hidden} after
`, isInline: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, decorators: [{ + type: Component, + args: [{ + selector: 'app-root', + template: `
Before @if (show) {shown {{name}}} @else {hidden} after
`, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: conditional_placeholders.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class AppComponent { + show: boolean; + name: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/TEST_CASES.json index b37e37bd4ec8..52106a3f505a 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/TEST_CASES.json @@ -80,6 +80,17 @@ ] } ] + }, + { + "description": "should support i18n inside an @if/@else block", + "inputFiles": ["conditional_placeholders.ts"], + "expectations": [ + { + "failureMessage": "Invalid i18n + control flow compilation", + "files": ["conditional_placeholders.js"] + } + ], + "compilationModeFilter": ["full compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/conditional_placeholders.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/conditional_placeholders.js new file mode 100644 index 000000000000..80ef49f3b54f --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/conditional_placeholders.js @@ -0,0 +1,24 @@ +function AppComponent_Conditional_2_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵi18n(0, 0, 1); + } + if (rf & 2) { + const $ctx$ = i0.ɵɵnextContext(); + i0.ɵɵi18nExp($ctx$.name); + i0.ɵɵi18nApply(0); + } +} +function AppComponent_Conditional_3_Template(rf, ctx) { + if (rf & 1) { + i0.ɵɵi18n(0, 0, 2); + } +} +… +i18n_0 = $localize `Before ${"\uFFFD*2:1\uFFFD"}:START_BLOCK_IF:shown ${"\uFFFD0:1\uFFFD"}:INTERPOLATION:${"\uFFFD/*2:1\uFFFD"}:CLOSE_BLOCK_IF:${"\uFFFD*3:2\uFFFD"}:START_BLOCK_ELSE:hidden${"\uFFFD/*3:2\uFFFD"}:CLOSE_BLOCK_ELSE: after`; +… +i0.ɵɵi18nStart(1, 0); +i0.ɵɵconditionalCreate(2, AppComponent_Conditional_2_Template, 1, 1)(3, AppComponent_Conditional_3_Template, 1, 0); +i0.ɵɵi18nEnd(); +… +i0.ɵɵconditional(ctx.show ? 2 : 3); +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/conditional_placeholders.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/conditional_placeholders.ts new file mode 100644 index 000000000000..70c2f4abfed4 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/blocks/conditional_placeholders.ts @@ -0,0 +1,10 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'app-root', + template: `
Before @if (show) {shown {{name}}} @else {hidden} after
`, +}) +export class AppComponent { + show = true; + name = 'x'; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/GOLDEN_PARTIAL.js index e68283ed4dc6..d3d459db96ed 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/GOLDEN_PARTIAL.js @@ -817,3 +817,41 @@ export declare class MyModule { static ɵinj: i0.ɵɵInjectorDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: i18n_attributes.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class AppComponent { + name = 'world'; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: AppComponent, isStandalone: true, selector: "app-root", ngImport: i0, template: `
`, isInline: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, decorators: [{ + type: Component, + args: [{ + selector: 'app-root', + template: `
`, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: i18n_attributes.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class AppComponent { + name: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/TEST_CASES.json index 85c0a316ac59..a5f7f0682922 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/TEST_CASES.json @@ -196,6 +196,17 @@ "extraChecks": ["verifyPlaceholdersIntegrity", "verifyUniqueConsts"] } ] + }, + { + "description": "should support interpolated i18n attributes (i18n-title/i18n-aria-label)", + "inputFiles": ["i18n_attributes.ts"], + "expectations": [ + { + "failureMessage": "Invalid i18n attributes compilation", + "files": ["i18n_attributes.js"] + } + ], + "compilationModeFilter": ["full compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_attributes.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_attributes.js new file mode 100644 index 000000000000..e0ebc9726600 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_attributes.js @@ -0,0 +1,10 @@ +… +i18n_0 = $localize `Hello ${"\uFFFD0\uFFFD"}:INTERPOLATION:`; +… +i18n_1 = $localize `Label ${"\uFFFD0\uFFFD"}:INTERPOLATION:`; +… +i0.ɵɵi18nAttributes(1, 0); +… +i0.ɵɵi18nExp(ctx.name)(ctx.name); +i0.ɵɵi18nApply(1); +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_attributes.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_attributes.ts new file mode 100644 index 000000000000..eff90b6e7991 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_attributes.ts @@ -0,0 +1,14 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'app-root', + template: `
`, +}) +export class AppComponent { + name = 'world'; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/GOLDEN_PARTIAL.js index 9a6ad438895c..203a215ffd5b 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/GOLDEN_PARTIAL.js @@ -935,3 +935,71 @@ export declare class MyComponent { static ɵcmp: i0.ɵɵComponentDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: plural_icu.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class AppComponent { + count = 0; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: AppComponent, isStandalone: true, selector: "app-root", ngImport: i0, template: `
{count, plural, =0 {no items} =1 {one item} other {{{count}} items}}
`, isInline: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, decorators: [{ + type: Component, + args: [{ + selector: 'app-root', + template: `
{count, plural, =0 {no items} =1 {one item} other {{{count}} items}}
`, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: plural_icu.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class AppComponent { + count: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: select_in_plural_icu.js + ****************************************************************************************************/ +import { Component } from '@angular/core'; +import * as i0 from "@angular/core"; +export class AppComponent { + count = 0; + gender = 'other'; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: AppComponent, isStandalone: true, selector: "app-root", ngImport: i0, template: `
+ {count, plural, + =0 {zero} + other {{gender, select, male {he} female {she} other {they}} has {{count}}} + } +
`, isInline: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AppComponent, decorators: [{ + type: Component, + args: [{ + selector: 'app-root', + template: `
+ {count, plural, + =0 {zero} + other {{gender, select, male {he} female {she} other {they}} has {{count}}} + } +
`, + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: select_in_plural_icu.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class AppComponent { + count: number; + gender: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/TEST_CASES.json index f3b3bbc10b44..784327a70579 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/TEST_CASES.json @@ -244,6 +244,28 @@ ] } ] + }, + { + "description": "should handle a plural ICU expression", + "inputFiles": ["plural_icu.ts"], + "expectations": [ + { + "failureMessage": "Invalid ICU plural compilation", + "files": ["plural_icu.js"] + } + ], + "compilationModeFilter": ["full compile"] + }, + { + "description": "should handle a select ICU nested inside a plural ICU", + "inputFiles": ["select_in_plural_icu.ts"], + "expectations": [ + { + "failureMessage": "Invalid nested ICU compilation", + "files": ["select_in_plural_icu.js"] + } + ], + "compilationModeFilter": ["full compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/plural_icu.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/plural_icu.js new file mode 100644 index 000000000000..f1746870ad4a --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/plural_icu.js @@ -0,0 +1,10 @@ +… +i18n_0 = $localize `{VAR_PLURAL, plural, =0 {no items} =1 {one item} other {{INTERPOLATION} items}}`; +… +i18n_0 = i0.ɵɵi18nPostprocess(i18n_0, { "INTERPOLATION": "\uFFFD1\uFFFD", "VAR_PLURAL": "\uFFFD0\uFFFD" }); +… +i0.ɵɵi18n(1, 0); +… +i0.ɵɵi18nExp(ctx.count)(ctx.count); +i0.ɵɵi18nApply(1); +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/plural_icu.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/plural_icu.ts new file mode 100644 index 000000000000..b41abc6098bd --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/plural_icu.ts @@ -0,0 +1,9 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'app-root', + template: `
{count, plural, =0 {no items} =1 {one item} other {{{count}} items}}
`, +}) +export class AppComponent { + count = 0; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/select_in_plural_icu.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/select_in_plural_icu.js new file mode 100644 index 000000000000..3deefe3c556f --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/select_in_plural_icu.js @@ -0,0 +1,7 @@ +… +i18n_0 = $localize `{VAR_PLURAL, plural, =0 {zero} other {{VAR_SELECT, select, male {he} female {she} other {they}} has {INTERPOLATION}}}`; +… +i18n_0 = i0.ɵɵi18nPostprocess(i18n_0, { "INTERPOLATION": "\uFFFD2\uFFFD", "VAR_PLURAL": "\uFFFD1\uFFFD", "VAR_SELECT": "\uFFFD0\uFFFD" }); +… +i0.ɵɵi18nExp(ctx.gender)(ctx.count)(ctx.count); +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/select_in_plural_icu.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/select_in_plural_icu.ts new file mode 100644 index 000000000000..519134c7aa7d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/select_in_plural_icu.ts @@ -0,0 +1,15 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'app-root', + template: `
+ {count, plural, + =0 {zero} + other {{gender, select, male {he} female {she} other {they}} has {{count}}} + } +
`, +}) +export class AppComponent { + count = 0; + gender = 'other'; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/message_metadata_placeholders.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/message_metadata_placeholders.js new file mode 100644 index 000000000000..5837331028fd --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/message_metadata_placeholders.js @@ -0,0 +1,8 @@ +… +i18n_0 = $localize `:site header|A greeting@@greetingId:Hello ${"\uFFFD0\uFFFD"}:INTERPOLATION:, you have ${"\uFFFD#2\uFFFD"}:START_BOLD_TEXT:${"\uFFFD1\uFFFD"}:INTERPOLATION_1:${"\uFFFD/#2\uFFFD"}:CLOSE_BOLD_TEXT: new ${"\uFFFD#3\uFFFD"}:START_LINK:messages${"\uFFFD/#3\uFFFD"}:CLOSE_LINK:`; +… +i0.ɵɵi18nStart(1, 0); +… +i0.ɵɵi18nExp(ctx.name)(ctx.count); +i0.ɵɵi18nApply(1); +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/message_metadata_placeholders.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/message_metadata_placeholders.ts new file mode 100644 index 000000000000..0c5a95cb8d7a --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/message_metadata_placeholders.ts @@ -0,0 +1,10 @@ +import {Component} from '@angular/core'; + +@Component({ + selector: 'app-root', + template: `
Hello {{name}}, you have {{count}} new messages
`, +}) +export class AppComponent { + name = 'world'; + count = 0; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/GOLDEN_PARTIAL.js index 11ed69b908d7..f9e942eebf57 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/GOLDEN_PARTIAL.js @@ -172,3 +172,49 @@ export declare class MyModule { static ɵinj: i0.ɵɵInjectorDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: input_transforms.js + ****************************************************************************************************/ +import { booleanAttribute, Directive, Input, numberAttribute } from '@angular/core'; +import * as i0 from "@angular/core"; +function toUpper(value) { + return value.toUpperCase(); +} +export class WithTransformsDirective { + disabled = false; + size = 0; + label = ''; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: WithTransformsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "0.0.0-PLACEHOLDER", type: WithTransformsDirective, isStandalone: true, selector: "[withTransforms]", inputs: { disabled: ["disabled", "disabled", booleanAttribute], size: ["size", "size", numberAttribute], label: ["labelText", "label", toUpper] }, ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: WithTransformsDirective, decorators: [{ + type: Directive, + args: [{ + selector: '[withTransforms]', + }] + }], propDecorators: { disabled: [{ + type: Input, + args: [{ transform: booleanAttribute }] + }], size: [{ + type: Input, + args: [{ transform: numberAttribute }] + }], label: [{ + type: Input, + args: [{ alias: 'labelText', transform: toUpper }] + }] } }); + +/**************************************************************************************************** + * PARTIAL FILE: input_transforms.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class WithTransformsDirective { + disabled: boolean; + size: number; + label: string; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; + static ngAcceptInputType_disabled: unknown; + static ngAcceptInputType_size: unknown; + static ngAcceptInputType_label: string; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/TEST_CASES.json index 73d514d206b2..be234165cfd0 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/TEST_CASES.json @@ -55,6 +55,17 @@ "full compile", "linked compile" ] + }, + { + "description": "should declare inputs with transform functions (booleanAttribute, numberAttribute and a custom function)", + "inputFiles": ["input_transforms.ts"], + "expectations": [ + { + "failureMessage": "Invalid input transform encoding", + "files": ["input_transforms.js"] + } + ], + "compilationModeFilter": ["full compile", "local compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/input_transforms.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/input_transforms.js new file mode 100644 index 000000000000..07f23fd84d53 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/input_transforms.js @@ -0,0 +1,6 @@ +… +export class WithTransformsDirective { + … + static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: WithTransformsDirective, selectors: [["", "withTransforms", ""]], inputs: { disabled: [2, "disabled", "disabled", booleanAttribute], size: [2, "size", "size", numberAttribute], label: [2, "labelText", "label", toUpper] } }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/input_transforms.local.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/input_transforms.local.js new file mode 100644 index 000000000000..07f23fd84d53 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/input_transforms.local.js @@ -0,0 +1,6 @@ +… +export class WithTransformsDirective { + … + static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: WithTransformsDirective, selectors: [["", "withTransforms", ""]], inputs: { disabled: [2, "disabled", "disabled", booleanAttribute], size: [2, "size", "size", numberAttribute], label: [2, "labelText", "label", toUpper] } }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/input_transforms.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/input_transforms.ts new file mode 100644 index 000000000000..a9a4818e0bb9 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_input_outputs/input_transforms.ts @@ -0,0 +1,14 @@ +import {booleanAttribute, Directive, Input, numberAttribute} from '@angular/core'; + +function toUpper(value: string): string { + return value.toUpperCase(); +} + +@Directive({ + selector: '[withTransforms]', +}) +export class WithTransformsDirective { + @Input({transform: booleanAttribute}) disabled = false; + @Input({transform: numberAttribute}) size = 0; + @Input({alias: 'labelText', transform: toUpper}) label = ''; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/GOLDEN_PARTIAL.js index 00d44c7928b4..b50f254bc704 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/GOLDEN_PARTIAL.js @@ -503,3 +503,50 @@ export declare class MyDirective { static ɵdir: i0.ɵɵDirectiveDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: host_style_class_bindings.js + ****************************************************************************************************/ +import { Directive, Input } from '@angular/core'; +import * as i0 from "@angular/core"; +export class HostStylingDirective { + cssClass = ''; + cssStyle = ''; + width = 0; + isActive = false; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HostStylingDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: HostStylingDirective, isStandalone: true, selector: "[hostStyling]", inputs: { cssClass: "cssClass", cssStyle: "cssStyle", width: "width", isActive: "isActive" }, host: { properties: { "class": "cssClass", "style": "cssStyle", "style.width.px": "width", "class.active": "isActive" } }, ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: HostStylingDirective, decorators: [{ + type: Directive, + args: [{ + selector: '[hostStyling]', + host: { + '[class]': 'cssClass', + '[style]': 'cssStyle', + '[style.width.px]': 'width', + '[class.active]': 'isActive', + }, + }] + }], propDecorators: { cssClass: [{ + type: Input + }], cssStyle: [{ + type: Input + }], width: [{ + type: Input + }], isActive: [{ + type: Input + }] } }); + +/**************************************************************************************************** + * PARTIAL FILE: host_style_class_bindings.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class HostStylingDirective { + cssClass: string; + cssStyle: string; + width: number; + isActive: boolean; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/TEST_CASES.json index 10dbdc5a5439..8d2eeeab9b22 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/TEST_CASES.json @@ -107,6 +107,17 @@ "failureMessage": "Invalid host binding code" } ] + }, + { + "description": "should generate host style/class instructions for [class]/[style] maps, a [style.width.px] unit binding and a [class.active] binding", + "inputFiles": ["host_style_class_bindings.ts"], + "expectations": [ + { + "failureMessage": "Invalid host style/class bindings", + "files": ["host_style_class_bindings.js"] + } + ], + "compilationModeFilter": ["full compile", "local compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/host_style_class_bindings.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/host_style_class_bindings.js new file mode 100644 index 000000000000..f5c80a62cae7 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/host_style_class_bindings.js @@ -0,0 +1,13 @@ +… +export class HostStylingDirective { + … + static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: HostStylingDirective, selectors: [["", "hostStyling", ""]], hostVars: 8, hostBindings: function HostStylingDirective_HostBindings(rf, ctx) { + if (rf & 2) { + i0.ɵɵstyleMap(ctx.cssStyle); + i0.ɵɵclassMap(ctx.cssClass); + i0.ɵɵstyleProp("width", ctx.width, "px"); + i0.ɵɵclassProp("active", ctx.isActive); + } + }, inputs: { cssClass: "cssClass", cssStyle: "cssStyle", width: "width", isActive: "isActive" } }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/host_style_class_bindings.local.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/host_style_class_bindings.local.js new file mode 100644 index 000000000000..f5c80a62cae7 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/host_style_class_bindings.local.js @@ -0,0 +1,13 @@ +… +export class HostStylingDirective { + … + static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: HostStylingDirective, selectors: [["", "hostStyling", ""]], hostVars: 8, hostBindings: function HostStylingDirective_HostBindings(rf, ctx) { + if (rf & 2) { + i0.ɵɵstyleMap(ctx.cssStyle); + i0.ɵɵclassMap(ctx.cssClass); + i0.ɵɵstyleProp("width", ctx.width, "px"); + i0.ɵɵclassProp("active", ctx.isActive); + } + }, inputs: { cssClass: "cssClass", cssStyle: "cssStyle", width: "width", isActive: "isActive" } }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/host_style_class_bindings.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/host_style_class_bindings.ts new file mode 100644 index 000000000000..48994895de36 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_styling/host_bindings/host_style_class_bindings.ts @@ -0,0 +1,17 @@ +import {Directive, Input} from '@angular/core'; + +@Directive({ + selector: '[hostStyling]', + host: { + '[class]': 'cssClass', + '[style]': 'cssStyle', + '[style.width.px]': 'width', + '[class.active]': 'isActive', + }, +}) +export class HostStylingDirective { + @Input() cssClass = ''; + @Input() cssStyle = ''; + @Input() width = 0; + @Input() isActive = false; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/service_decorator/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/service_decorator/GOLDEN_PARTIAL.js index 686178c642f1..b17682edea25 100644 --- a/packages/compiler-cli/test/compliance/test_cases/service_decorator/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/service_decorator/GOLDEN_PARTIAL.js @@ -121,3 +121,64 @@ export declare class MyService { static ɵprov: i0.ɵɵInjectableDeclaration>; } +/**************************************************************************************************** + * PARTIAL FILE: provided_in_variants.js + ****************************************************************************************************/ +import { Injectable, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class SomeModule { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: SomeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: SomeModule }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: SomeModule }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: SomeModule, decorators: [{ + type: NgModule, + args: [{}] + }] }); +export class PlatformService { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: PlatformService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); + static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: PlatformService, providedIn: 'platform' }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: PlatformService, decorators: [{ + type: Injectable, + args: [{ providedIn: 'platform' }] + }] }); +export class AnyService { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AnyService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); + static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AnyService, providedIn: 'any' }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: AnyService, decorators: [{ + type: Injectable, + args: [{ providedIn: 'any' }] + }] }); +export class ModuleScopedService { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleScopedService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); + static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleScopedService, providedIn: SomeModule }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleScopedService, decorators: [{ + type: Injectable, + args: [{ providedIn: SomeModule }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: provided_in_variants.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class SomeModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} +export declare class PlatformService { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵprov: i0.ɵɵInjectableDeclaration; +} +export declare class AnyService { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵprov: i0.ɵɵInjectableDeclaration; +} +export declare class ModuleScopedService { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵprov: i0.ɵɵInjectableDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/service_decorator/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/service_decorator/TEST_CASES.json index 3d7736292c87..a6cffc668b5d 100644 --- a/packages/compiler-cli/test/compliance/test_cases/service_decorator/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/service_decorator/TEST_CASES.json @@ -75,6 +75,17 @@ "failureMessage": "Incorrect service" } ] + }, + { + "description": "should compile a service with `providedIn` set to `'platform'`, `'any'` and a module type", + "inputFiles": ["provided_in_variants.ts"], + "expectations": [ + { + "failureMessage": "Invalid providedIn injectable", + "files": ["provided_in_variants.js"] + } + ], + "compilationModeFilter": ["full compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/service_decorator/provided_in_variants.js b/packages/compiler-cli/test/compliance/test_cases/service_decorator/provided_in_variants.js new file mode 100644 index 000000000000..41662be66acf --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/service_decorator/provided_in_variants.js @@ -0,0 +1,16 @@ +… +export class PlatformService { + … + static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: PlatformService, factory: PlatformService.ɵfac, providedIn: 'platform' }); +} +… +export class AnyService { + … + static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: AnyService, factory: AnyService.ɵfac, providedIn: 'any' }); +} +… +export class ModuleScopedService { + … + static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: ModuleScopedService, factory: ModuleScopedService.ɵfac, providedIn: SomeModule }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/service_decorator/provided_in_variants.ts b/packages/compiler-cli/test/compliance/test_cases/service_decorator/provided_in_variants.ts new file mode 100644 index 000000000000..58763af0c5a8 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/service_decorator/provided_in_variants.ts @@ -0,0 +1,13 @@ +import {Injectable, NgModule} from '@angular/core'; + +@NgModule({}) +export class SomeModule {} + +@Injectable({providedIn: 'platform'}) +export class PlatformService {} + +@Injectable({providedIn: 'any'}) +export class AnyService {} + +@Injectable({providedIn: SomeModule}) +export class ModuleScopedService {} diff --git a/packages/compiler-cli/test/compliance/test_cases/signal_inputs/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/GOLDEN_PARTIAL.js index 8787359935db..ee1dbe2bff69 100644 --- a/packages/compiler-cli/test/compliance/test_cases/signal_inputs/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/GOLDEN_PARTIAL.js @@ -169,3 +169,43 @@ export declare class TestDir { static ɵdir: i0.ɵɵDirectiveDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: signal_apis.js + ****************************************************************************************************/ +import { booleanAttribute, Directive, input, model, output } from '@angular/core'; +import * as i0 from "@angular/core"; +export class SignalApisDirective { + // Signal input, required signal input, aliased + transformed input. + value = input('', /* @ts-ignore */ + ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ [])); + id = input.required(/* @ts-ignore */ + ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ [])); + disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), alias: 'isDisabled', transform: booleanAttribute }); + // Two-way model and an event output. + checked = model(false, /* @ts-ignore */ + ...(ngDevMode ? [{ debugName: "checked" }] : /* istanbul ignore next */ [])); + changed = output(); + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: SignalApisDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "0.0.0-PLACEHOLDER", type: SignalApisDirective, isStandalone: true, selector: "[signalApis]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "isDisabled", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", changed: "changed" }, ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: SignalApisDirective, decorators: [{ + type: Directive, + args: [{ + selector: '[signalApis]', + }] + }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "isDisabled", required: false }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], changed: [{ type: i0.Output, args: ["changed"] }] } }); + +/**************************************************************************************************** + * PARTIAL FILE: signal_apis.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class SignalApisDirective { + value: import("@angular/core").InputSignal; + id: import("@angular/core").InputSignal; + disabled: import("@angular/core").InputSignalWithTransform; + checked: import("@angular/core").ModelSignal; + changed: import("@angular/core").OutputEmitterRef; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/signal_inputs/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/TEST_CASES.json index 86df9bd374ae..6d95387f509d 100644 --- a/packages/compiler-cli/test/compliance/test_cases/signal_inputs/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/TEST_CASES.json @@ -79,6 +79,17 @@ "failureMessage": "Incorrect definition" } ] + }, + { + "description": "should capture signal input, required input, model and output members", + "inputFiles": ["signal_apis.ts"], + "expectations": [ + { + "failureMessage": "Invalid signal API encoding", + "files": ["signal_apis.js"] + } + ], + "compilationModeFilter": ["full compile", "local compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/signal_inputs/signal_apis.js b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/signal_apis.js new file mode 100644 index 000000000000..35a19060f5ce --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/signal_apis.js @@ -0,0 +1,6 @@ +… +export class SignalApisDirective { + … + static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: SignalApisDirective, selectors: [["", "signalApis", ""]], inputs: { value: [1, "value"], id: [1, "id"], disabled: [1, "isDisabled", "disabled"], checked: [1, "checked"] }, outputs: { checked: "checkedChange", changed: "changed" } }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/signal_inputs/signal_apis.local.js b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/signal_apis.local.js new file mode 100644 index 000000000000..35a19060f5ce --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/signal_apis.local.js @@ -0,0 +1,6 @@ +… +export class SignalApisDirective { + … + static ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: SignalApisDirective, selectors: [["", "signalApis", ""]], inputs: { value: [1, "value"], id: [1, "id"], disabled: [1, "isDisabled", "disabled"], checked: [1, "checked"] }, outputs: { checked: "checkedChange", changed: "changed" } }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/signal_inputs/signal_apis.ts b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/signal_apis.ts new file mode 100644 index 000000000000..4a8bd29cf5ae --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/signal_apis.ts @@ -0,0 +1,15 @@ +import {booleanAttribute, Directive, input, model, output} from '@angular/core'; + +@Directive({ + selector: '[signalApis]', +}) +export class SignalApisDirective { + // Signal input, required signal input, aliased + transformed input. + value = input(''); + id = input.required(); + disabled = input(false, {alias: 'isDisabled', transform: booleanAttribute}); + + // Two-way model and an event output. + checked = model(false); + changed = output(); +} From 1c5c9d4a267a6eece42bc3d5d02e96cf7dd353f0 Mon Sep 17 00:00:00 2001 From: Matthew Beck Date: Thu, 23 Jul 2026 16:46:28 -0700 Subject: [PATCH 2/2] test(compiler-cli): add NgModule compliance tests for pipe precedence, declaration ordering, and forwardRef exports Adds compliance test coverage for: - Pipe resolution precedence across imported modules (last imported pipe is selected) - Directive/component ordering in component dependencies (imported module declarables before local declarations) - ForwardRef module unwrapping in NgModule exports to injector imports --- .../ng_modules/GOLDEN_PARTIAL.js | 286 ++++++++++++++++++ .../ng_modules/TEST_CASES.json | 33 ++ .../ng_modules/declarations_ordering.js | 10 + .../ng_modules/declarations_ordering.ts | 50 +++ .../ng_modules/forward_ref_export_module.js | 7 + .../ng_modules/forward_ref_export_module.ts | 9 + .../ng_modules/pipe_precedence_imports.js | 10 + .../ng_modules/pipe_precedence_imports.ts | 44 +++ 8 files changed, 449 insertions(+) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/declarations_ordering.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/declarations_ordering.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forward_ref_export_module.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forward_ref_export_module.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/pipe_precedence_imports.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/pipe_precedence_imports.ts diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/GOLDEN_PARTIAL.js index 9fda94fac8a9..126c0b1389f6 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/GOLDEN_PARTIAL.js @@ -1209,3 +1209,289 @@ export declare class LaterModule { static ɵinj: i0.ɵɵInjectorDeclaration; } +/**************************************************************************************************** + * PARTIAL FILE: pipe_precedence_imports.js + ****************************************************************************************************/ +import { Component, NgModule, Pipe } from '@angular/core'; +import * as i0 from "@angular/core"; +export class PipeA { + transform() { } + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: PipeA, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); + static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: PipeA, isStandalone: false, name: "number" }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: PipeA, decorators: [{ + type: Pipe, + args: [{ + name: 'number', + standalone: false, + }] + }] }); +export class ModuleA { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleA, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleA, declarations: [PipeA], exports: [PipeA] }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleA }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleA, decorators: [{ + type: NgModule, + args: [{ + declarations: [PipeA], + exports: [PipeA], + }] + }] }); +export class PipeB { + transform() { } + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: PipeB, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); + static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: PipeB, isStandalone: false, name: "number" }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: PipeB, decorators: [{ + type: Pipe, + args: [{ + name: 'number', + standalone: false, + }] + }] }); +export class ModuleB { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleB, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleB, declarations: [PipeB], exports: [PipeB] }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleB }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleB, decorators: [{ + type: NgModule, + args: [{ + declarations: [PipeB], + exports: [PipeB], + }] + }] }); +export class App { + count = 0; + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: App, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: App, isStandalone: false, selector: "app", ngImport: i0, template: '{{ count | number }}', isInline: true, dependencies: [{ kind: "pipe", type: PipeB, name: "number" }] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: App, decorators: [{ + type: Component, + args: [{ + selector: 'app', + template: '{{ count | number }}', + standalone: false, + }] + }] }); +export class ModuleC { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleC, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleC, declarations: [App], imports: [ModuleA, ModuleB] }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleC, imports: [ModuleA, ModuleB] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleC, decorators: [{ + type: NgModule, + args: [{ + imports: [ModuleA, ModuleB], + declarations: [App], + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: pipe_precedence_imports.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class PipeA { + transform(): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} +export declare class ModuleA { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} +export declare class PipeB { + transform(): void; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵpipe: i0.ɵɵPipeDeclaration; +} +export declare class ModuleB { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} +export declare class App { + count: number; + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} +export declare class ModuleC { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: declarations_ordering.js + ****************************************************************************************************/ +import { Component, Directive, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class DirectiveA { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: DirectiveA, deps: [], target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: DirectiveA, isStandalone: false, selector: "[dir]", ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: DirectiveA, decorators: [{ + type: Directive, + args: [{ + selector: '[dir]', + standalone: false, + }] + }] }); +export class ComponentA { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ComponentA, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: ComponentA, isStandalone: false, selector: "comp-a", ngImport: i0, template: '...', isInline: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ComponentA, decorators: [{ + type: Component, + args: [{ + selector: 'comp-a', + template: '...', + standalone: false, + }] + }] }); +export class ModuleA { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleA, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleA, declarations: [DirectiveA, ComponentA], exports: [DirectiveA, ComponentA] }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleA }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleA, decorators: [{ + type: NgModule, + args: [{ + declarations: [DirectiveA, ComponentA], + exports: [DirectiveA, ComponentA], + }] + }] }); +export class DirectiveB { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: DirectiveB, deps: [], target: i0.ɵɵFactoryTarget.Directive }); + static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: DirectiveB, isStandalone: false, selector: "[dir]", ngImport: i0 }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: DirectiveB, decorators: [{ + type: Directive, + args: [{ + selector: '[dir]', + standalone: false, + }] + }] }); +export class ComponentB { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ComponentB, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: ComponentB, isStandalone: false, selector: "comp-b", ngImport: i0, template: '...', isInline: true }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ComponentB, decorators: [{ + type: Component, + args: [{ + selector: 'comp-b', + template: '...', + standalone: false, + }] + }] }); +export class App { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: App, deps: [], target: i0.ɵɵFactoryTarget.Component }); + static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: App, isStandalone: false, selector: "app", ngImport: i0, template: ` +
+ + + `, isInline: true, dependencies: [{ kind: "directive", type: DirectiveA, selector: "[dir]" }, { kind: "component", type: ComponentA, selector: "comp-a" }, { kind: "directive", type: DirectiveB, selector: "[dir]" }, { kind: "component", type: ComponentB, selector: "comp-b" }] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: App, decorators: [{ + type: Component, + args: [{ + selector: 'app', + template: ` +
+ + + `, + standalone: false, + }] + }] }); +export class ModuleB { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleB, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleB, declarations: [DirectiveB, ComponentB, App], imports: [ModuleA] }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleB, imports: [ModuleA] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: ModuleB, decorators: [{ + type: NgModule, + args: [{ + imports: [ModuleA], + declarations: [DirectiveB, ComponentB, App], + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: declarations_ordering.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class DirectiveA { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} +export declare class ComponentA { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} +export declare class ModuleA { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} +export declare class DirectiveB { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; +} +export declare class ComponentB { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} +export declare class App { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵcmp: i0.ɵɵComponentDeclaration; +} +export declare class ModuleB { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} + +/**************************************************************************************************** + * PARTIAL FILE: forward_ref_export_module.js + ****************************************************************************************************/ +import { forwardRef, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class BarModule { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: BarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: BarModule }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: BarModule }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: BarModule, decorators: [{ + type: NgModule, + args: [{}] + }] }); +export class FooModule { + static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); + static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooModule, exports: [BarModule] }); + static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooModule, imports: [BarModule] }); +} +i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: FooModule, decorators: [{ + type: NgModule, + args: [{ + exports: [forwardRef(() => BarModule)], + }] + }] }); + +/**************************************************************************************************** + * PARTIAL FILE: forward_ref_export_module.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class BarModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} +export declare class FooModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/TEST_CASES.json index a8dd3fcac43c..60994b8d3e75 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/TEST_CASES.json +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/TEST_CASES.json @@ -253,6 +253,39 @@ } ], "compilationModeFilter": ["full compile", "local compile"] + }, + { + "description": "should respect imported module order when selecting Pipe (last imported Pipe is used)", + "inputFiles": ["pipe_precedence_imports.ts"], + "expectations": [ + { + "failureMessage": "Invalid pipe resolution precedence for imported modules", + "files": ["pipe_precedence_imports.js"] + } + ], + "compilationModeFilter": ["full compile"] + }, + { + "description": "should order directives and components from imported modules before local declarations", + "inputFiles": ["declarations_ordering.ts"], + "expectations": [ + { + "failureMessage": "Invalid directive/component ordering in component dependencies", + "files": ["declarations_ordering.js"] + } + ], + "compilationModeFilter": ["full compile"] + }, + { + "description": "should unwrap forwardRef module in NgModule exports to injector imports", + "inputFiles": ["forward_ref_export_module.ts"], + "expectations": [ + { + "failureMessage": "Invalid forwardRef module handling in NgModule exports", + "files": ["forward_ref_export_module.js"] + } + ], + "compilationModeFilter": ["full compile"] } ] } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/declarations_ordering.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/declarations_ordering.js new file mode 100644 index 000000000000..690216cae90d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/declarations_ordering.js @@ -0,0 +1,10 @@ +… +export class App { + … + static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ + … + dependencies: [DirectiveA, ComponentA, DirectiveB, ComponentB], + encapsulation: 2 + }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/declarations_ordering.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/declarations_ordering.ts new file mode 100644 index 000000000000..7dbc5a9f51ae --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/declarations_ordering.ts @@ -0,0 +1,50 @@ +import {Component, Directive, NgModule} from '@angular/core'; + +@Directive({ + selector: '[dir]', + standalone: false, +}) +export class DirectiveA {} + +@Component({ + selector: 'comp-a', + template: '...', + standalone: false, +}) +export class ComponentA {} + +@NgModule({ + declarations: [DirectiveA, ComponentA], + exports: [DirectiveA, ComponentA], +}) +export class ModuleA {} + +@Directive({ + selector: '[dir]', + standalone: false, +}) +export class DirectiveB {} + +@Component({ + selector: 'comp-b', + template: '...', + standalone: false, +}) +export class ComponentB {} + +@Component({ + selector: 'app', + template: ` +
+ + + `, + standalone: false, +}) +export class App {} + +@NgModule({ + imports: [ModuleA], + declarations: [DirectiveB, ComponentB, App], +}) +export class ModuleB {} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forward_ref_export_module.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forward_ref_export_module.js new file mode 100644 index 000000000000..c97e6cdc4a4d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forward_ref_export_module.js @@ -0,0 +1,7 @@ +… +export class FooModule { + … + static ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: FooModule }); + static ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [BarModule] }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forward_ref_export_module.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forward_ref_export_module.ts new file mode 100644 index 000000000000..129f0374d580 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/forward_ref_export_module.ts @@ -0,0 +1,9 @@ +import {forwardRef, NgModule} from '@angular/core'; + +@NgModule({}) +export class BarModule {} + +@NgModule({ + exports: [forwardRef(() => BarModule)], +}) +export class FooModule {} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/pipe_precedence_imports.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/pipe_precedence_imports.js new file mode 100644 index 000000000000..0968fb5ba33a --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/pipe_precedence_imports.js @@ -0,0 +1,10 @@ +… +export class App { + … + static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ + … + dependencies: [PipeB], + encapsulation: 2 + }); +} +… diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/pipe_precedence_imports.ts b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/pipe_precedence_imports.ts new file mode 100644 index 000000000000..0fcb2f09415e --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/ng_modules/pipe_precedence_imports.ts @@ -0,0 +1,44 @@ +import {Component, NgModule, Pipe} from '@angular/core'; + +@Pipe({ + name: 'number', + standalone: false, +}) +export class PipeA { + transform() {} +} + +@NgModule({ + declarations: [PipeA], + exports: [PipeA], +}) +export class ModuleA {} + +@Pipe({ + name: 'number', + standalone: false, +}) +export class PipeB { + transform() {} +} + +@NgModule({ + declarations: [PipeB], + exports: [PipeB], +}) +export class ModuleB {} + +@Component({ + selector: 'app', + template: '{{ count | number }}', + standalone: false, +}) +export class App { + count = 0; +} + +@NgModule({ + imports: [ModuleA, ModuleB], + declarations: [App], +}) +export class ModuleC {}