From 278629278092d0d3778aaf283c5135f9ca35929d Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Thu, 12 Nov 2020 19:43:35 +0000 Subject: [PATCH] test(compiler-cli): migrate i18n compliance tests (#39661) This commit provides the machinery for the new file-based compliance test approach for i18n tests, and migrates the i18n tests to this new format. PR Close #39661 --- .../compiler-cli/test/compliance/README.md | 52 +- .../test/compliance/full/full_compile_spec.ts | 6 +- .../compliance/linked/linked_compile_spec.ts | 11 +- .../partial/generate_golden_partial.ts | 7 +- .../element_attributes/GOLDEN_PARTIAL.js | 690 +++++++++++++++ .../element_attributes/TEST_CASES.json | 218 +++++ .../element_attributes/bound_attributes.js | 10 + .../element_attributes/bound_attributes.ts | 17 + .../element_attributes/empty_attributes.js | 7 + .../element_attributes/empty_attributes.ts | 14 + .../element_attributes/i18n_root_node.js | 15 + .../element_attributes/i18n_root_node.ts | 14 + .../element_attributes/interpolation_basic.js | 34 + .../element_attributes/interpolation_basic.ts | 22 + .../interpolation_complex_expressions.js | 21 + .../interpolation_complex_expressions.ts | 14 + .../interpolation_custom_config.js | 19 + .../interpolation_custom_config.ts | 15 + .../interpolation_nested_context.js | 35 + .../interpolation_nested_context.ts | 16 + .../element_attributes/invalid_i18n_meta.js | 18 + .../element_attributes/invalid_i18n_meta.ts | 16 + .../element_attributes/meaning_description.js | 67 ++ .../element_attributes/meaning_description.ts | 21 + .../element_attributes/ng-template_basic.js | 11 + .../element_attributes/ng-template_basic.ts | 14 + .../ng-template_interpolation.js | 17 + .../ng-template_interpolation.ts | 14 + .../ng-template_interpolation_structural.js | 28 + .../ng-template_interpolation_structural.ts | 14 + .../ng-template_structural.js | 27 + .../ng-template_structural.ts | 14 + .../element_attributes/static_attributes.js | 11 + .../element_attributes/static_attributes.ts | 14 + .../errors/GOLDEN_PARTIAL.js | 1 + .../errors/TEST_CASES.json | 56 ++ .../errors/nested_i18n_msg.ts | 16 + .../nested_i18n_msg_with_ng-containers.ts | 18 + .../errors/nested_i18n_msg_with_tags.ts | 18 + .../es5_support/GOLDEN_PARTIAL.js | 47 + .../es5_support/TEST_CASES.json | 20 + .../r3_view_compiler_i18n/es5_support/test.js | 3 + .../r3_view_compiler_i18n/es5_support/test.ts | 12 + .../icu_logic/GOLDEN_PARTIAL.js | 795 +++++++++++++++++ .../icu_logic/TEST_CASES.json | 229 +++++ .../icu_logic/bare_icu.js | 65 ++ .../icu_logic/bare_icu.ts | 20 + .../icu_logic/custom_interpolation.js | 18 + .../icu_logic/custom_interpolation.ts | 15 + .../icu_logic/different_contexts.js | 40 + .../icu_logic/different_contexts.ts | 19 + .../icu_logic/escape_quotes.js | 11 + .../icu_logic/escape_quotes.ts | 14 + .../icu_logic/expressions.js | 20 + .../icu_logic/expressions.ts | 14 + .../icu_logic/html_content.js | 27 + .../icu_logic/html_content.ts | 18 + .../icu_logic/icu_only.js | 17 + .../icu_logic/icu_only.ts | 14 + .../icu_logic/icu_with_interpolations.js | 40 + .../icu_logic/icu_with_interpolations.ts | 19 + .../icu_logic/keyword_spaces.js | 3 + .../icu_logic/keyword_spaces.ts | 17 + .../icu_logic/metadata.js | 1 + .../icu_logic/metadata.ts | 14 + .../icu_logic/multiple_icus.js | 22 + .../icu_logic/multiple_icus.ts | 17 + .../icu_logic/named_interpolations.js | 20 + .../icu_logic/named_interpolations.ts | 20 + .../icu_logic/nested_icu_in_other_block.js | 20 + .../icu_logic/nested_icu_in_other_block.ts | 21 + .../icu_logic/nested_icus.js | 21 + .../icu_logic/nested_icus.ts | 20 + .../icu_logic/shared_placeholder.js | 95 ++ .../icu_logic/shared_placeholder.ts | 22 + .../icu_logic/single_icu.js | 20 + .../icu_logic/single_icu.ts | 14 + .../GOLDEN_PARTIAL.js | 408 +++++++++ .../line_ending_normalization/TEST_CASES.json | 197 +++++ .../external_template_legacy.ts | 14 + .../external_template_legacy_normalized.ts | 14 + .../external_template_non_legacy.ts | 14 + .../inline_template_legacy.ts | 24 + .../inline_template_non_legacy.ts | 24 + .../legacy_nonnormalized.js | 10 + .../legacy_normalized.js | 10 + .../line_ending_normalization/non_legacy.js | 9 + .../line_ending_normalization/template.html | 11 + .../GOLDEN_PARTIAL.js | 86 ++ .../TEST_CASES.json | 39 + .../legacy_disabled.js | 6 + .../legacy_disabled.ts | 14 + .../legacy_enabled.js | 5 + .../legacy_enabled.ts | 14 + .../namespaces/GOLDEN_PARTIAL.js | 110 +++ .../namespaces/TEST_CASES.json | 33 + .../namespaces/foreign_object.js | 36 + .../namespaces/foreign_object.ts | 20 + .../namespaces/namespaced_div.js | 33 + .../namespaces/namespaced_div.ts | 20 + .../nested_nodes/GOLDEN_PARTIAL.js | 831 ++++++++++++++++++ .../nested_nodes/TEST_CASES.json | 229 +++++ .../nested_nodes/backtick_quotes.js | 9 + .../nested_nodes/backtick_quotes.ts | 12 + .../nested_nodes/bindings_in_content.js | 37 + .../nested_nodes/bindings_in_content.ts | 16 + .../comments_in_translated_text.js | 1 + .../comments_in_translated_text.ts | 14 + .../nested_nodes/directives.js | 33 + .../nested_nodes/directives.ts | 14 + .../nested_nodes/empty_content.js | 7 + .../nested_nodes/empty_content.ts | 18 + .../nested_nodes/escape_quotes.js | 9 + .../nested_nodes/escape_quotes.ts | 14 + .../nested_nodes/event_listeners.js | 15 + .../nested_nodes/event_listeners.ts | 14 + .../interpolation_complex_expressions.js | 22 + .../interpolation_complex_expressions.ts | 18 + .../interpolation_custom_config.js | 18 + .../interpolation_custom_config.ts | 15 + .../nested_nodes/named_interpolations.js | 31 + .../nested_nodes/named_interpolations.ts | 17 + .../nested_nodes/nested_elements.js | 37 + .../nested_nodes/nested_elements.ts | 27 + .../nested_elements_with_i18n_attributes.js | 48 + .../nested_elements_with_i18n_attributes.ts | 25 + .../nested_nodes/nested_templates.js | 40 + .../nested_nodes/nested_templates.ts | 24 + .../nested_nodes/nested_templates_context.js | 77 ++ .../nested_nodes/nested_templates_context.ts | 34 + .../nested_nodes/plain_text_messages.js | 29 + .../nested_nodes/plain_text_messages.ts | 18 + .../nested_nodes/self_closing.js | 45 + .../nested_nodes/self_closing.ts | 16 + .../GOLDEN_PARTIAL.js | 525 +++++++++++ .../ng-container_ng-template/TEST_CASES.json | 171 ++++ .../ng-container_ng-template/bare_icus.js | 34 + .../ng-container_ng-template/bare_icus.ts | 15 + .../child_elements.js | 37 + .../child_elements.ts | 17 + .../duplicate_content.js | 4 + .../duplicate_content.ts | 15 + .../ng-container_ng-template/icus.js | 34 + .../ng-container_ng-template/icus.ts | 15 + .../nested_ng-container_const.js | 1 + .../nested_ng-container_const.ts | 19 + .../nested_templates.js | 56 ++ .../nested_templates.ts | 24 + .../ng-container_with_non_text_content.js | 19 + .../ng-container_with_non_text_content.ts | 16 + .../self_closing_ng-container.js | 17 + .../self_closing_ng-container.ts | 16 + .../self_closing_tags.js | 27 + .../self_closing_tags.ts | 19 + .../single_ng-container.js | 21 + .../single_ng-container.ts | 14 + .../single_ng-template.js | 22 + .../single_ng-template.ts | 14 + .../structural_directives.js | 42 + .../structural_directives.ts | 15 + .../GOLDEN_PARTIAL.js | 176 ++++ .../TEST_CASES.json | 61 ++ .../icu_only.js | 20 + .../icu_only.ts | 14 + .../ng-container_ng-template.js | 22 + .../ng-container_ng-template.ts | 15 + .../self-closing_i18n_instructions/styles.js | 22 + .../self-closing_i18n_instructions/styles.ts | 15 + .../text_only_content.js | 13 + .../text_only_content.ts | 14 + .../GOLDEN_PARTIAL.js | 50 ++ .../TEST_CASES.json | 19 + .../preserve_inner_content.js | 31 + .../preserve_inner_content.ts | 18 + .../test_cases/test_case_schema.json | 114 ++- .../compliance/test_helpers/check_errors.ts | 24 + .../test_helpers/check_expectations.ts | 55 +- .../compliance/test_helpers/compile_test.ts | 60 +- .../compliance/test_helpers/expect_emit.ts | 59 +- .../test_helpers/expected_file_macros.ts | 107 +++ .../test_helpers/get_compliance_tests.ts | 101 ++- .../compliance/test_helpers/i18n_checks.ts | 89 ++ .../compliance/test_helpers/i18n_helpers.ts | 132 +++ .../compliance/test_helpers/test_runner.ts | 32 +- .../r3_view_compiler_i18n_spec.ts | 52 -- 185 files changed, 8803 insertions(+), 142 deletions(-) create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/GOLDEN_PARTIAL.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/TEST_CASES.json create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/bound_attributes.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/bound_attributes.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/empty_attributes.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/empty_attributes.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_root_node.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_root_node.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_basic.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_basic.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_custom_config.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_custom_config.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_nested_context.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_nested_context.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/invalid_i18n_meta.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/invalid_i18n_meta.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/meaning_description.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/meaning_description.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_basic.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_basic.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation_structural.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation_structural.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_structural.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_structural.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/static_attributes.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/static_attributes.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/GOLDEN_PARTIAL.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/TEST_CASES.json create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_ng-containers.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_tags.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/GOLDEN_PARTIAL.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/TEST_CASES.json create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/test.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/test.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/GOLDEN_PARTIAL.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/TEST_CASES.json create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/bare_icu.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/bare_icu.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/custom_interpolation.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/custom_interpolation.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/different_contexts.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/different_contexts.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/escape_quotes.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/escape_quotes.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/expressions.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/expressions.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_only.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_only.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_with_interpolations.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_with_interpolations.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/keyword_spaces.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/keyword_spaces.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/metadata.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/metadata.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/multiple_icus.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/multiple_icus.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/named_interpolations.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/named_interpolations.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icu_in_other_block.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icu_in_other_block.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icus.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icus.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/shared_placeholder.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/shared_placeholder.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/single_icu.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/single_icu.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/GOLDEN_PARTIAL.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/TEST_CASES.json create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy_normalized.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_non_legacy.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_legacy.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_non_legacy.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_nonnormalized.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_normalized.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/non_legacy.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/template.html create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/GOLDEN_PARTIAL.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/TEST_CASES.json create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_disabled.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_disabled.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_enabled.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_enabled.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/GOLDEN_PARTIAL.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/TEST_CASES.json create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/foreign_object.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/foreign_object.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/namespaced_div.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/namespaced_div.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/GOLDEN_PARTIAL.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/TEST_CASES.json create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/backtick_quotes.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/backtick_quotes.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/bindings_in_content.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/bindings_in_content.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/comments_in_translated_text.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/comments_in_translated_text.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/directives.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/directives.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/empty_content.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/empty_content.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/escape_quotes.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/escape_quotes.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/event_listeners.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/event_listeners.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_custom_config.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_custom_config.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/named_interpolations.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/named_interpolations.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements_with_i18n_attributes.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements_with_i18n_attributes.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates_context.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates_context.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/plain_text_messages.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/plain_text_messages.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/self_closing.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/self_closing.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/GOLDEN_PARTIAL.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/TEST_CASES.json create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/bare_icus.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/bare_icus.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/child_elements.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/child_elements.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/duplicate_content.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/duplicate_content.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/icus.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/icus.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_ng-container_const.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_ng-container_const.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_templates.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_templates.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/ng-container_with_non_text_content.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/ng-container_with_non_text_content.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_ng-container.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_ng-container.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_tags.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_tags.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-container.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-container.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-template.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-template.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/structural_directives.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/structural_directives.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/GOLDEN_PARTIAL.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/TEST_CASES.json create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/icu_only.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/icu_only.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/ng-container_ng-template.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/ng-container_ng-template.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/styles.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/styles.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/text_only_content.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/text_only_content.ts create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/GOLDEN_PARTIAL.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/TEST_CASES.json create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/preserve_inner_content.js create mode 100644 packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/preserve_inner_content.ts create mode 100644 packages/compiler-cli/test/compliance/test_helpers/check_errors.ts create mode 100644 packages/compiler-cli/test/compliance/test_helpers/expected_file_macros.ts create mode 100644 packages/compiler-cli/test/compliance/test_helpers/i18n_checks.ts create mode 100644 packages/compiler-cli/test/compliance/test_helpers/i18n_helpers.ts diff --git a/packages/compiler-cli/test/compliance/README.md b/packages/compiler-cli/test/compliance/README.md index 77dde089aa..d7f25bc316 100644 --- a/packages/compiler-cli/test/compliance/README.md +++ b/packages/compiler-cli/test/compliance/README.md @@ -38,6 +38,7 @@ Each test-case can specify: * A `description` of the test. * The `inputFiles` that will be compiled. * Additional `compilerOptions` and `angularCompilerOptions` that are passed to the compiler. +* Whether to exclude this test-case from partial compilation tests (`excludeFromPartialTests`). * A collection of `expectations` definitions that will be checked against the generated files. Note that there is a JSON schema for the `TEST_CASES.json` file stored at `test_cases/test_case_schema.json`. @@ -77,24 +78,53 @@ The paths are relative to the `TEST_CASES.json` file. If no `inputFiles` property is provided, the default is `["test.ts"]`. +Note that test-cases can share input files, but you should only do this if these input files are +going to be compiled using the same options. This is because only one version of the compiled input +file is retrieved from the golden partial file to be used in the linker tests. This can cause the +linker tests to fail if they are provided with a compiled file (from the golden partial) that was +compiled with different options to what are expected for that test-case. + ### Expectations -An expectation consists of a collection of expected `files` pairs, and a `failureMessage`, which -is displayed if the expectation check fails. +An expectation consists of a `failureMessage`, which is displayed if the expectation check fails, +a collection of expected `files` pairs and/or a collection of `expectedErrors`. -Each file-pair consists of a path to a `generated` file (relative to the build output folder), +Each expected file-pair consists of a path to a `generated` file (relative to the build output folder), and a path to an `expected` file (relative to the test case). The `generated` file is checked to see if it "matches" the `expected` file. The matching is resilient to whitespace and variable name changes. -If no `failureMessage` property is provided, the default is `"Incorrect generated output."`. - If no `files` property is provided, the default is a a collection of objects `{expected, generated}`, where `expected` and `generated` are computed by taking each path in the `inputFiles` collection and replacing the `.ts` extension with `.js`. +Each expected error must have a `message` property and, optionally, a `location` property. These are +parsed as regular expressions (so `.` and `(` etc must be escaped) and tested against the errors that +are returned as diagnostics from the compilation. + +If no `failureMessage` property is provided, the default is `"Incorrect generated output."`. + + +### Expected file format + +The expected files look like JavaScript but are actually specially formatted to allow matching +with the generated output. The generated and expected files are tokenized and then the tokens +are intelligently matched to check whether they are equivalent. + +* Whitespace tolerant - the tokens can be separated by any amount of whitespace +* Code skipping - you can skip sections of code in the generated output by adding an ellipsis + (…) to the expectation file. +* Identifier tolerant - identifiers in the expectation file that start and end with a dollar + (e.g. `$r3$`) will be matched against any identifier. But the matching will ensure that the + same identifier name appears consistently elsewhere in the file. +* Macro expansion - we can add macros to the expected files that will be expanded to blocks + of code dynamically. The following macros are defined in the + `test_helpers/expected_file_macros.ts` file: + * I18n messages - for example: + `__i18nMsg__('message string', [ ['placeholder', 'pair] ], { meta: 'properties'})`. + * Attribute markers - for example: `__AttributeMarker.Bindings__`. ## Running tests @@ -153,6 +183,18 @@ To debug generating the partial golden output use the following form of Bazel co yarn bazel run //packages/compiler-cli/test/compliance/test_cases:generate_partial_for_.debug ``` +The `path/to/test_case` is relative to the `test_cases` directory. So for this `TEST_CASES.json` file at: + +``` +packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_directives/directives/matching/TEST_CASES.json +``` + +The command to debug the test-cases would be: + +``` +yarn bazel run //packages/compiler-cli/test/compliance/test_cases:generate_partial_for_r3_view_compiler_directives/directives/matching.debug +``` + ### Focusing test-cases diff --git a/packages/compiler-cli/test/compliance/full/full_compile_spec.ts b/packages/compiler-cli/test/compliance/full/full_compile_spec.ts index 4e50602e67..340c89453d 100644 --- a/packages/compiler-cli/test/compliance/full/full_compile_spec.ts +++ b/packages/compiler-cli/test/compliance/full/full_compile_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {FileSystem} from '../../../src/ngtsc/file_system'; -import {compileTest} from '../test_helpers/compile_test'; +import {CompileResult, compileTest} from '../test_helpers/compile_test'; import {ComplianceTest} from '../test_helpers/get_compliance_tests'; import {runTests} from '../test_helpers/test_runner'; @@ -18,6 +18,6 @@ runTests('full compile', compileTests); * @param fs The mock file-system where the input files can be found. * @param test The compliance test whose input files should be compiled. */ -function compileTests(fs: FileSystem, test: ComplianceTest): void { - compileTest(fs, test.inputFiles, test.compilerOptions, test.angularCompilerOptions); +function compileTests(fs: FileSystem, test: ComplianceTest): CompileResult { + return compileTest(fs, test.inputFiles, test.compilerOptions, test.angularCompilerOptions); } diff --git a/packages/compiler-cli/test/compliance/linked/linked_compile_spec.ts b/packages/compiler-cli/test/compliance/linked/linked_compile_spec.ts index af99c45cd8..1dd362fb9c 100644 --- a/packages/compiler-cli/test/compliance/linked/linked_compile_spec.ts +++ b/packages/compiler-cli/test/compliance/linked/linked_compile_spec.ts @@ -9,7 +9,7 @@ import {PluginObj, transformSync} from '@babel/core'; import {createEs2015LinkerPlugin} from '../../../linker/babel'; import {AbsoluteFsPath, FileSystem} from '../../../src/ngtsc/file_system'; -import {getBuildOutputDirectory} from '../test_helpers/compile_test'; +import {CompileResult, getBuildOutputDirectory} from '../test_helpers/compile_test'; import {ComplianceTest} from '../test_helpers/get_compliance_tests'; import {parseGoldenPartial} from '../test_helpers/golden_partials'; import {runTests} from '../test_helpers/test_runner'; @@ -22,9 +22,13 @@ runTests('partial compile + link', linkPartials); * @param fs The mock file-system to use for linking the partials. * @param test The compliance test whose partials will be linked. */ -function linkPartials(fs: FileSystem, test: ComplianceTest): void { +function linkPartials(fs: FileSystem, test: ComplianceTest): CompileResult { const builtDirectory = getBuildOutputDirectory(fs); - const linkerPlugin = createEs2015LinkerPlugin(test.angularCompilerOptions); + const linkerPlugin = createEs2015LinkerPlugin({ + // By default we don't render legacy message ids in compliance tests. + enableI18nLegacyMessageIdFormat: false, + ...test.angularCompilerOptions + }); const goldenPartialPath = fs.resolve('/GOLDEN_PARTIAL.js'); if (!fs.exists(goldenPartialPath)) { throw new Error( @@ -40,6 +44,7 @@ function linkPartials(fs: FileSystem, test: ComplianceTest): void { applyLinker({fileName: partial.path, source: partial.content}, linkerPlugin); safeWrite(fs, fs.resolve(builtDirectory, partial.path), linkedSource); } + return {emittedFiles: [], errors: []}; } /** diff --git a/packages/compiler-cli/test/compliance/partial/generate_golden_partial.ts b/packages/compiler-cli/test/compliance/partial/generate_golden_partial.ts index 4daa4926e3..ed4c3ef51f 100644 --- a/packages/compiler-cli/test/compliance/partial/generate_golden_partial.ts +++ b/packages/compiler-cli/test/compliance/partial/generate_golden_partial.ts @@ -36,9 +36,10 @@ export function generateGoldenPartial(testConfigPath: string): void { */ function* compilePartials(fs: FileSystem, test: ComplianceTest): Generator { const builtDirectory = getBuildOutputDirectory(fs); - for (const generatedPath of compileTest( - fs, test.inputFiles, test.compilerOptions, - {compilationMode: 'partial', ...test.angularCompilerOptions})) { + for (const generatedPath of compileTest(fs, test.inputFiles, test.compilerOptions, { + compilationMode: 'partial', + ...test.angularCompilerOptions + }).emittedFiles) { yield { path: fs.relative(builtDirectory, generatedPath), content: fs.readFile(generatedPath), 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 new file mode 100644 index 0000000000..1813909de0 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/GOLDEN_PARTIAL.js @@ -0,0 +1,690 @@ +/**************************************************************************************************** + * PARTIAL FILE: meaning_description.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
Content A
+
Content B
+
Content C
+
Content D
+
Content E
+
Content F
+
Content G
+
Content H
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
Content A
+
Content B
+
Content C
+
Content D
+
Content E
+
Content F
+
Content G
+
Content H
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: meaning_description.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: ng-template_basic.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + +` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: ng-template_basic.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: ng-template_structural.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + Test +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + Test +` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: ng-template_structural.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: ng-template_interpolation.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + +` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: ng-template_interpolation.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: ng-template_interpolation_structural.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + +` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: ng-template_interpolation_structural.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: empty_attributes.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: empty_attributes.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: bound_attributes.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: bound_attributes.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: static_attributes.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: static_attributes.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_basic.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_basic.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_custom_config.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ `, isInline: true }, interpolation: ["{%", "%}"] }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ `, + interpolation: ['{%', '%}'], + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_custom_config.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_nested_context.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+
+
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+
+
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_nested_context.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_complex_expressions.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_complex_expressions.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_complex_expressions.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_complex_expressions.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: i18n_root_node.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
Some content
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
Some content
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: i18n_root_node.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: invalid_i18n_meta.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ Some content +
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ Some content +
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: invalid_i18n_meta.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + 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 new file mode 100644 index 0000000000..94f8aa18ec --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/TEST_CASES.json @@ -0,0 +1,218 @@ +{ + "$schema": "../../test_case_schema.json", + "cases": [ + { + "description": "should add the meaning and description as JsDoc comments and metadata blocks", + "inputFiles": [ + "meaning_description.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should support i18n attributes on explicit elements", + "inputFiles": [ + "ng-template_basic.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should support i18n attributes on explicit with structural directives", + "inputFiles": [ + "ng-template_structural.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should support i18n attributes with interpolations on explicit elements", + "inputFiles": [ + "ng-template_interpolation.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should support i18n attributes with interpolations on explicit elements with structural directives", + "inputFiles": [ + "ng-template_interpolation_structural.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should not create translations for empty attributes", + "inputFiles": [ + "empty_attributes.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should not create translations for bound attributes", + "inputFiles": [ + "bound_attributes.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should translate static attributes", + "inputFiles": [ + "static_attributes.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should support interpolation", + "inputFiles": [ + "interpolation_basic.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should support interpolation with custom interpolation config", + "inputFiles": [ + "interpolation_custom_config.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should correctly bind to context in nested template", + "inputFiles": [ + "interpolation_nested_context.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should support complex expressions in interpolation", + "inputFiles": [ + "interpolation_complex_expressions.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should support complex expressions in interpolation", + "inputFiles": [ + "interpolation_complex_expressions.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should work correctly when placed on i18n root node", + "inputFiles": [ + "i18n_root_node.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should sanitize ids and generate proper variable names", + "inputFiles": [ + "invalid_i18n_meta.ts" + ], + "angularCompilerOptions": { + "i18nUseExternalIds": false + }, + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + } + ] +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/bound_attributes.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/bound_attributes.js new file mode 100644 index 0000000000..1ab5dc6ea1 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/bound_attributes.js @@ -0,0 +1,10 @@ +consts: [[3, "title"]], +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelement(0, "div", 0); + } + if (rf & 2) { + $r3$.ɵɵproperty("title", ctx.title); + $r3$.ɵɵattribute("label", ctx.label); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/bound_attributes.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/bound_attributes.ts new file mode 100644 index 0000000000..b0e1b2b7d9 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/bound_attributes.ts @@ -0,0 +1,17 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/empty_attributes.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/empty_attributes.js new file mode 100644 index 0000000000..694cd41280 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/empty_attributes.js @@ -0,0 +1,7 @@ +consts: [["id", "static", "title", ""]], +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelement(0, "div", 0); + } +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/empty_attributes.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/empty_attributes.ts new file mode 100644 index 0000000000..02a680aa1e --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/empty_attributes.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_root_node.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_root_node.js new file mode 100644 index 0000000000..17134b16ce --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_root_node.js @@ -0,0 +1,15 @@ +consts: function() { + __i18nMsg__('Element title', [], {meaning: 'm', desc: 'd'}) + __i18nMsg__('Some content', [], {}) + return [ + ["title", $i18n_0$], + $i18n_1$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div", 0); + $r3$.ɵɵi18n(1, 1); + $r3$.ɵɵelementEnd(); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_root_node.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_root_node.ts new file mode 100644 index 0000000000..dd4272089f --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/i18n_root_node.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
Some content
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_basic.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_basic.js new file mode 100644 index 0000000000..70686f62a4 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_basic.js @@ -0,0 +1,34 @@ +decls: 5, +vars: 8, +consts: function() { + __i18nMsg__('static text', [], {}) + __i18nMsg__('intro {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {meaning: 'm', desc: 'd'}) + __i18nMsg__('{$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {meaning: 'm1', desc: 'd1'}) + __i18nMsg__('{$interpolation} and {$interpolation_1} and again {$interpolation_2}', [['interpolation', String.raw`\uFFFD0\uFFFD`],['interpolation_1', String.raw`\uFFFD1\uFFFD`],['interpolation_2', String.raw`\uFFFD2\uFFFD`]], {meaning: 'm2', desc: 'd2'}) + __i18nMsg__('{$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {}) +return [ + ["id", "dynamic-1", "aria-roledescription", $i18n_0$, __AttributeMarker.I18n__, + "title", "aria-label"], + ["title", $i18n_1$, "aria-label", $i18n_2$], + ["id", "dynamic-2", __AttributeMarker.I18n__, "title", "aria-roledescription"], + ["title", $i18n_3$, "aria-roledescription", $i18n_4$] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div", 0); + $r3$.ɵɵpipe(1, "uppercase"); + $r3$.ɵɵi18nAttributes(2, 1); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(3, "div", 2); + $r3$.ɵɵi18nAttributes(4, 3); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(1, 6, ctx.valueA))(ctx.valueB); + $r3$.ɵɵi18nApply(2); + $r3$.ɵɵadvance(3); + $r3$.ɵɵi18nExp(ctx.valueA)(ctx.valueB)(ctx.valueA + ctx.valueB)(ctx.valueC); + $r3$.ɵɵi18nApply(4); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_basic.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_basic.ts new file mode 100644 index 0000000000..799f34b30a --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_basic.ts @@ -0,0 +1,22 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.js new file mode 100644 index 0000000000..76daa891f9 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.js @@ -0,0 +1,21 @@ +decls: 2, +vars: 1, +consts: function() { + __i18nMsg__('{$interpolation} title', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {}) + return [ + [__AttributeMarker.I18n__, "title"], + ["title", $i18n_0$] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div", 0); + $r3$.ɵɵi18nAttributes(1, 1); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + let $tmp_0_0$ = null; + $r3$.ɵɵi18nExp(($tmp_0_0$ = ctx.valueA.getRawValue()) == null ? null : $tmp_0_0$.getTitle()); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.ts new file mode 100644 index 0000000000..7a87fab3a8 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_custom_config.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_custom_config.js new file mode 100644 index 0000000000..6280cce1b8 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_custom_config.js @@ -0,0 +1,19 @@ +consts: function() { + __i18nMsg__('intro {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {meaning: 'm', desc: 'd'}) + return [ + [__AttributeMarker.I18n__, "title"], + ["title", $i18n_0$] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div", 0); + $r3$.ɵɵpipe(1, "uppercase"); + $r3$.ɵɵi18nAttributes(2, 1); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(1, 1, ctx.valueA)); + $r3$.ɵɵi18nApply(2); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_custom_config.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_custom_config.ts new file mode 100644 index 0000000000..7e94667410 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_custom_config.ts @@ -0,0 +1,15 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ `, + interpolation: ['{%', '%}'], +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_nested_context.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_nested_context.js new file mode 100644 index 0000000000..179d4adafc --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_nested_context.js @@ -0,0 +1,35 @@ +function MyComponent_div_0_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵelementStart(1, "div", 1); + $r3$.ɵɵpipe(2, "uppercase"); + $r3$.ɵɵi18nAttributes(3, 2); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + const $outer_r1$ = ctx.$implicit; + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(2, 1, $outer_r1$)); + $r3$.ɵɵi18nApply(3); + } +} +… +decls: 1, +vars: 1, +consts: function() { + __i18nMsg__('different scope {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {meaning: 'm', desc: 'd'}) + return [ + [__AttributeMarker.Template__, "ngFor", "ngForOf"], + [__AttributeMarker.I18n__, "title"], + ["title", $i18n_0$] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtemplate(0, MyComponent_div_0_Template, 4, 3, "div", 0); + } + if (rf & 2) { + $r3$.ɵɵproperty("ngForOf", ctx.items); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_nested_context.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_nested_context.ts new file mode 100644 index 0000000000..5614807fe7 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_nested_context.ts @@ -0,0 +1,16 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+
+
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/invalid_i18n_meta.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/invalid_i18n_meta.js new file mode 100644 index 0000000000..692e4464b5 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/invalid_i18n_meta.js @@ -0,0 +1,18 @@ +// NOTE: Keeping raw content (avoiding `__i18nMsg__` macro) to illustrate message id sanitization. +let $I18N_0$; +if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + const $MSG_EXTERNAL_ID_WITH_INVALID_CHARS$$APP_SPEC_TS_1$ = goog.getMsg("Element title"); + $I18N_0$ = $MSG_EXTERNAL_ID_WITH_INVALID_CHARS$$APP_SPEC_TS_1$; +} else { + $I18N_0$ = $localize`:@@ID.WITH.INVALID.CHARS:Element title`; +} + +… + +let $I18N_2$; +if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + const $MSG_EXTERNAL_ID_WITH_INVALID_CHARS_2$$APP_SPEC_TS_4$ = goog.getMsg(" Some content "); + $I18N_2$ = $MSG_EXTERNAL_ID_WITH_INVALID_CHARS_2$$APP_SPEC_TS_4$; +} else { + $I18N_2$ = $localize`:@@ID.WITH.INVALID.CHARS.2: Some content `; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/invalid_i18n_meta.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/invalid_i18n_meta.ts new file mode 100644 index 0000000000..a0cf318d86 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/invalid_i18n_meta.ts @@ -0,0 +1,16 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ Some content +
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/meaning_description.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/meaning_description.js new file mode 100644 index 0000000000..eb142626e3 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/meaning_description.js @@ -0,0 +1,67 @@ +consts: + function() { + __i18nMsg__('Content A', [], {id: 'idA', meaning: 'meaningA', desc: 'descA'}) + __i18nMsg__('Title B', [], {id: 'idB', meaning: 'meaningB', desc: 'descB'}) + __i18nMsg__('Title C', [], {meaning: 'meaningC'}) + __i18nMsg__('Title D', [], {meaning: 'meaningD', desc: 'descD'}) + __i18nMsg__('Title E', [], {id: 'idE', desc: 'meaningE'}) + __i18nMsg__('Title F', [], {id: 'idF'}) + + // NOTE: Keeping this block as a raw string, since it checks escaping of special chars. + let $i18n_23$; + if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + /** + * @desc [BACKUP_${MESSAGE}_ID:idH]`desc + */ + const $MSG_EXTERNAL_idG$$APP_SPEC_TS_24$ = goog.getMsg("Title G"); + $i18n_23$ = $MSG_EXTERNAL_idG$$APP_SPEC_TS_24$; + } else { + $i18n_23$ = $localize`:[BACKUP_$\{MESSAGE}_ID\:idH]\`desc@@idG:Title G`; + } + + // NOTE: Keeping this block as a raw string, since it checks escaping of special chars. + let $i18n_7$; + if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + /** + * @desc Some text \' [BACKUP_MESSAGE_ID: xxx] + */ + const $MSG_EXTERNAL_idG$$APP_SPEC_TS_21$ = goog.getMsg("Content H"); + $i18n_7$ = $MSG_EXTERNAL_idG$$APP_SPEC_TS_21$; + } else { + $i18n_7$ = $localize`:Some text \\' [BACKUP_MESSAGE_ID\: xxx]:Content H`; + } + + return [ + $i18n_0$, ["title", $i18n_1$], ["title", $i18n_2$], ["title", $i18n_3$], + ["title", $i18n_4$], ["title", $i18n_5$], ["title", $i18n_6$], $i18n_7$ + ]; + }, + + template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(2, "div", 1); + $r3$.ɵɵtext(3, "Content B"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(4, "div", 2); + $r3$.ɵɵtext(5, "Content C"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(6, "div", 3); + $r3$.ɵɵtext(7, "Content D"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(8, "div", 4); + $r3$.ɵɵtext(9, "Content E"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(10, "div", 5); + $r3$.ɵɵtext(11, "Content F"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(12, "div", 6); + $r3$.ɵɵtext(13, "Content G"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(14, "div"); + $r3$.ɵɵi18n(15, 7); + $r3$.ɵɵelementEnd(); + } + } \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/meaning_description.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/meaning_description.ts new file mode 100644 index 0000000000..1566e9d798 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/meaning_description.ts @@ -0,0 +1,21 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
Content A
+
Content B
+
Content C
+
Content D
+
Content E
+
Content F
+
Content G
+
Content H
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_basic.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_basic.js new file mode 100644 index 0000000000..071543f472 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_basic.js @@ -0,0 +1,11 @@ +consts: function () { + __i18nMsg__('Hello', [], {}) + return [ + ["title", $i18n_0$] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 0, 0, "ng-template", 0); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_basic.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_basic.ts new file mode 100644 index 0000000000..6cf8b93dac --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_basic.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + +` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation.js new file mode 100644 index 0000000000..ac7dd60c4b --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation.js @@ -0,0 +1,17 @@ +consts: function() { + __i18nMsg__('Hello {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {}) + return [ + [__AttributeMarker.Bindings__, "title"], + ["title", $i18n_0$] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 0, 0, "ng-template", 0); + $r3$.ɵɵi18nAttributes(1, 1); + } + if (rf & 2) { + $r3$.ɵɵi18nExp(ctx.name); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation.ts new file mode 100644 index 0000000000..0a136319d0 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + +` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation_structural.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation_structural.js new file mode 100644 index 0000000000..680ba82786 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation_structural.js @@ -0,0 +1,28 @@ +function MyComponent_0_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtemplate(0, MyComponent_0_ng_template_0_Template, 0, 0, "ng-template", 1); + $r3$.ɵɵi18nAttributes(1, 2); + } + if (rf & 2) { + const $ctx_r2$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵi18nExp($ctx_r2$.name); + $r3$.ɵɵi18nApply(1); + } +} +… +consts: function() { + __i18nMsg__('Hello {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {}) + return [ + [__AttributeMarker.Template__, "ngIf"], + [__AttributeMarker.Bindings__, "title"], + ["title", $i18n_0$] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtemplate(0, MyComponent_0_Template, 2, 1, undefined, 0); + } + if (rf & 2) { + $r3$.ɵɵproperty("ngIf", true); + } +}, diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation_structural.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation_structural.ts new file mode 100644 index 0000000000..d61223b76a --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_interpolation_structural.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + +` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_structural.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_structural.js new file mode 100644 index 0000000000..b062013c2b --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_structural.js @@ -0,0 +1,27 @@ +function MyComponent_0_ng_template_0_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtext(0, "Test"); + } +} +function MyComponent_0_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtemplate(0, MyComponent_0_ng_template_0_Template, 1, 0, "ng-template", 1); + } +} +… +consts: function() { + __i18nMsg__('Hello', [], {}) + return [ + // NOTE: AttributeMarker.Template = 4 + [4, "ngIf"], + ["title", $i18n_0$] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtemplate(0, MyComponent_0_Template, 1, 0, undefined, 0); + } + if (rf & 2) { + $r3$.ɵɵproperty("ngIf", ctx.visible); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_structural.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_structural.ts new file mode 100644 index 0000000000..9c2a5b92a4 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/ng-template_structural.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + Test +` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/static_attributes.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/static_attributes.js new file mode 100644 index 0000000000..3905926e39 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/static_attributes.js @@ -0,0 +1,11 @@ +consts: function() { + __i18nMsg__('introduction', [], {meaning: 'm', desc: 'd'}) + return [ + ["id", "static", "title", $i18n_0$] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelement(0, "div", 0); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/static_attributes.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/static_attributes.ts new file mode 100644 index 0000000000..ef28844c12 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/static_attributes.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/GOLDEN_PARTIAL.js new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/GOLDEN_PARTIAL.js @@ -0,0 +1 @@ + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/TEST_CASES.json new file mode 100644 index 0000000000..14825ea3e3 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/TEST_CASES.json @@ -0,0 +1,56 @@ +{ + "$schema": "../../test_case_schema.json", + "cases": [ + { + "description": "should throw on nested i18n sections", + "inputFiles": [ + "nested_i18n_msg.ts" + ], + "excludeFromPartialTests": true, + "expectations": [ + { + "expectedErrors": [ + { + "message": "Cannot mark an element as translatable inside of a translatable section\\. Please remove the nested i18n marker\\.", + "location": "nested_i18n_msg\\.ts \\(7,5\\)" + } + ] + } + ] + }, + { + "description": "should throw on nested i18n sections with tags in between", + "inputFiles": [ + "nested_i18n_msg_with_tags.ts" + ], + "excludeFromPartialTests": true, + "expectations": [ + { + "expectedErrors": [ + { + "message": "Cannot mark an element as translatable inside of a translatable section\\. Please remove the nested i18n marker\\.", + "location": "nested_i18n_msg_with_tags\\.ts \\(8,7\\)" + } + ] + } + ] + }, + { + "description": "should throw on nested i18n sections represented with s", + "inputFiles": [ + "nested_i18n_msg_with_ng-containers.ts" + ], + "excludeFromPartialTests": true, + "expectations": [ + { + "expectedErrors": [ + { + "message": "Cannot mark an element as translatable inside of a translatable section\\. Please remove the nested i18n marker\\.", + "location": "nested_i18n_msg_with_ng-containers\\.ts \\(8,7\\)" + } + ] + } + ] + } + ] +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg.ts new file mode 100644 index 0000000000..533f1f9060 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg.ts @@ -0,0 +1,16 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+
Some content
+
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_ng-containers.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_ng-containers.ts new file mode 100644 index 0000000000..e5690686f2 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_ng-containers.ts @@ -0,0 +1,18 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + +
+ Some content +
+
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_tags.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_tags.ts new file mode 100644 index 0000000000..e8942340c2 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/errors/nested_i18n_msg_with_tags.ts @@ -0,0 +1,18 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+
+
Some content
+
+
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/GOLDEN_PARTIAL.js new file mode 100644 index 0000000000..a441458f15 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/GOLDEN_PARTIAL.js @@ -0,0 +1,47 @@ +/**************************************************************************************************** + * PARTIAL FILE: test.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +var MyComponent = /** @class */ (function () { + function MyComponent() { + } + MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; + MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '
Content A
', isInline: true } }); + return MyComponent; +}()); +export { MyComponent }; +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: '
Content A
', + }] + }], null, null); })(); +var MyModule = /** @class */ (function () { + function MyModule() { + } + MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); + MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); + return MyModule; +}()); +export { MyModule }; +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: test.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/TEST_CASES.json new file mode 100644 index 0000000000..16ed8ed14e --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/TEST_CASES.json @@ -0,0 +1,20 @@ +{ + "$schema": "../../test_case_schema.json", + "cases": [ + { + "description": "should generate ES5 compliant localized messages if the target is ES5", + "compilerOptions": { + "target": "ES5" + }, + "excludeFromPartialTests": true, + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + } + ] +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/test.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/test.js new file mode 100644 index 0000000000..3548b5410d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/test.js @@ -0,0 +1,3 @@ +var $I18N_0$; +… +$I18N_0$ = $localize(…__makeTemplateObject([":meaning:A|descA@@idA:Content A"], [":meaning\\:A|descA@@idA:Content A"])…); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/test.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/test.ts new file mode 100644 index 0000000000..6fa5159e31 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/es5_support/test.ts @@ -0,0 +1,12 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: '
Content A
', +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file 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 new file mode 100644 index 0000000000..a4aa0761fa --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/GOLDEN_PARTIAL.js @@ -0,0 +1,795 @@ +/**************************************************************************************************** + * PARTIAL FILE: single_icu.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
{gender, select, male {male} female {female} other {other}}
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
{gender, select, male {male} female {female} other {other}}
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: single_icu.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: escape_quotes.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
{gender, select, single {'single quotes'} double {"double quotes"} other {other}}
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
{gender, select, single {'single quotes'} double {"double quotes"} other {other}}
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: escape_quotes.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: icu_only.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + {age, select, 10 {ten} 20 {twenty} other {other}} +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + {age, select, 10 {ten} 20 {twenty} other {other}} +`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: icu_only.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: bare_icu.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
{gender, select, male {male} female {female} other {other}}
+
+ {age, select, 10 {ten} 20 {twenty} other {other}} +
+
+ You have {count, select, 0 {no emails} 1 {one email} other {{{count}} emails}}. +
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
{gender, select, male {male} female {female} other {other}}
+
+ {age, select, 10 {ten} 20 {twenty} other {other}} +
+
+ You have {count, select, 0 {no emails} 1 {one email} other {{{count}} emails}}. +
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: bare_icu.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: custom_interpolation.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
{age, select, 10 {ten} 20 {twenty} other {{% other %}}}
+`, isInline: true }, interpolation: ["{%", "%}"] }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
{age, select, 10 {ten} 20 {twenty} other {{% other %}}}
+`, + interpolation: ['{%', '%}'], + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: custom_interpolation.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: html_content.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ {gender, select, male {male - male} female {female female} other {
other
}} + Other content +
Another content
+
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ {gender, select, male {male - male} female {female female} other {
other
}} + Other content +
Another content
+
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: html_content.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: expressions.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
{gender, select, male {male of age: {{ ageA + ageB + ageC }}} female {female} other {other}}
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
{gender, select, male {male of age: {{ ageA + ageB + ageC }}} female {female} other {other}}
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: expressions.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: multiple_icus.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ {gender, select, male {male} female {female} other {other}} + {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}} +
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ {gender, select, male {male} female {female} other {other}} + {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}} +
+`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: multiple_icus.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: shared_placeholder.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ {gender, select, male {male} female {female} other {other}} +
+ {gender, select, male {male} female {female} other {other}} +
+
+ {gender, select, male {male} female {female} other {other}} +
+
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ {gender, select, male {male} female {female} other {other}} +
+ {gender, select, male {male} female {female} other {other}} +
+
+ {gender, select, male {male} female {female} other {other}} +
+
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: shared_placeholder.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: nested_icus.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ {gender, select, + male {male of age: {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}} + female {female} + other {other} + } +
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ {gender, select, + male {male of age: {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}} + female {female} + other {other} + } +
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: nested_icus.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: nested_icu_in_other_block.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
{count, plural, + =0 {zero} + =2 {{{count}} {name, select, + cat {cats} + dog {dogs} + other {animals}} !} + other {other - {{count}}} + }
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
{count, plural, + =0 {zero} + =2 {{{count}} {name, select, + cat {cats} + dog {dogs} + other {animals}} !} + other {other - {{count}}} + }
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: nested_icu_in_other_block.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: different_contexts.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ {gender, select, male {male} female {female} other {other}} + + {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}} + +
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ {gender, select, male {male} female {female} other {other}} + + {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}} + +
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: different_contexts.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: icu_with_interpolations.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ {gender, select, male {male {{ weight }}} female {female {{ height }}} other {other}} + + {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other: {{ otherAge }}}} + +
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ {gender, select, male {male {{ weight }}} female {female {{ height }}} other {other}} + + {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other: {{ otherAge }}}} + +
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: icu_with_interpolations.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: named_interpolations.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
{ + gender, + select, + male {male {{ weight // i18n(ph="PH_A") }}} + female {female {{ height // i18n(ph="PH_B") }}} + other {other {{ age // i18n(ph="PH WITH SPACES") }}} + }
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
{ + gender, + select, + male {male {{ weight // i18n(ph="PH_A") }}} + female {female {{ height // i18n(ph="PH_B") }}} + other {other {{ age // i18n(ph="PH WITH SPACES") }}} + }
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: named_interpolations.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: metadata.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
{count, select, 1 {one} other {more than one}}
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
{count, select, 1 {one} other {more than one}}
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: metadata.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: keyword_spaces.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ {count, select , 1 {one} other {more than one}} + {count, plural , =1 {one} other {more than one}} +
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ {count, select , 1 {one} other {more than one}} + {count, plural , =1 {one} other {more than one}} +
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: keyword_spaces.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + 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 new file mode 100644 index 0000000000..c040967c3d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/TEST_CASES.json @@ -0,0 +1,229 @@ +{ + "$schema": "../../test_case_schema.json", + "cases": [ + { + "description": "should handle single icus", + "inputFiles": [ + "single_icu" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should properly escape quotes in content", + "inputFiles": [ + "escape_quotes.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should support ICU-only templates", + "inputFiles": [ + "icu_only.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should generate i18n instructions for icus generated outside of i18n blocks", + "inputFiles": [ + "bare_icu.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should support interpolation with custom interpolation config", + "inputFiles": [ + "custom_interpolation.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle icus with html", + "inputFiles": [ + "html_content.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle icus with expressions", + "inputFiles": [ + "expressions.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle multiple icus in one block", + "inputFiles": [ + "multiple_icus.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle multiple icus that share same placeholder", + "inputFiles": [ + "shared_placeholder.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle nested icus", + "inputFiles": [ + "nested_icus.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "nested with interpolations in \"other\" blocks", + "inputFiles": [ + "nested_icu_in_other_block.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle icus in different contexts", + "inputFiles": [ + "different_contexts.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle icus with interpolations", + "inputFiles": [ + "icu_with_interpolations.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle icus with named interpolations", + "inputFiles": [ + "named_interpolations.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should attach metadata in case an ICU represents the whole message", + "inputFiles": [ + "metadata.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should produce proper messages when `select` or `plural` keywords have spaces after them", + "inputFiles": [ + "keyword_spaces.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + } + ] +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/bare_icu.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/bare_icu.js new file mode 100644 index 0000000000..a7feb44f70 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/bare_icu.js @@ -0,0 +1,65 @@ +function $MyComponent_div_2_Template$(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div", 3); + $r3$.ɵɵtext(1, " "); + $r3$.ɵɵi18n(2, 4); + $r3$.ɵɵtext(3, " "); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + const $ctx_r0$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵadvance(2); + $r3$.ɵɵi18nExp($ctx_r0$.age); + $r3$.ɵɵi18nApply(2); + } +} +… +function $MyComponent_div_3_Template$(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div", 5); + $r3$.ɵɵtext(1, " You have "); + $r3$.ɵɵi18n(2, 6); + $r3$.ɵɵtext(3, ". "); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + const $ctx_r1$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵadvance(2); + $r3$.ɵɵi18nExp($ctx_r1$.count)($ctx_r1$.count); + $r3$.ɵɵi18nApply(2); + } +} +… +decls: 4, +vars: 3, +consts: function() { + __i18nIcuMsg__('{VAR_SELECT, select, male {male} female {female} other {other}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]) __i18nIcuMsg__('{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]) + __i18nIcuMsg__('{VAR_SELECT, select, 0 {no emails} 1 {one email} other {{INTERPOLATION} emails}}', [ ['VAR_SELECT', String.raw`\uFFFD0\uFFFD`], ['INTERPOLATION', String.raw`\uFFFD1\uFFFD`]]) + return [ + $i18n_0$, + ["title", "icu only", __AttributeMarker.Template__, "ngIf"], + ["title", "icu and text", __AttributeMarker.Template__, "ngIf"], + ["title", "icu only"], + $i18n_1$, + ["title", "icu and text"], + $i18n_2$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵtemplate(2, $MyComponent_div_2_Template$, 4, 1, "div", 1); + $r3$.ɵɵtemplate(3, $MyComponent_div_3_Template$, 4, 2, "div", 2); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.gender); + $r3$.ɵɵi18nApply(1); + $r3$.ɵɵadvance(1); + $r3$.ɵɵproperty("ngIf", ctx.visible); + $r3$.ɵɵadvance(1); + $r3$.ɵɵproperty("ngIf", ctx.available); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/bare_icu.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/bare_icu.ts new file mode 100644 index 0000000000..10ed95ab1b --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/bare_icu.ts @@ -0,0 +1,20 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
{gender, select, male {male} female {female} other {other}}
+
+ {age, select, 10 {ten} 20 {twenty} other {other}} +
+
+ You have {count, select, 0 {no emails} 1 {one email} other {{{count}} emails}}. +
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/custom_interpolation.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/custom_interpolation.js new file mode 100644 index 0000000000..35c822c28a --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/custom_interpolation.js @@ -0,0 +1,18 @@ +consts: function() { + __i18nIcuMsg__('{VAR_SELECT, select, 10 {ten} 20 {twenty} other {{INTERPOLATION}}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`], ['INTERPOLATION', String.raw`\uFFFD1\uFFFD`]]) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.age)(ctx.other); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/custom_interpolation.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/custom_interpolation.ts new file mode 100644 index 0000000000..9c01465bf6 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/custom_interpolation.ts @@ -0,0 +1,15 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
{age, select, 10 {ten} 20 {twenty} other {{% other %}}}
+`, + interpolation: ['{%', '%}'], +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/different_contexts.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/different_contexts.js new file mode 100644 index 0000000000..0d5d84876c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/different_contexts.js @@ -0,0 +1,40 @@ +function MyComponent_span_2_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18nStart(0, 0, 1); + $r3$.ɵɵelement(1, "span"); + $r3$.ɵɵi18nEnd(); + } + if (rf & 2) { + const $ctx_r0$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp($ctx_r0$.age); + $r3$.ɵɵi18nApply(0); + } +} +… +decls: 3, +vars: 2, +consts: function() { + __i18nIcuMsg__('{VAR_SELECT, select, male {male} female {female} other {other}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]) + __i18nIcuMsg__('{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}', [['VAR_SELECT', String.raw`\uFFFD0:1\uFFFD`]]) + __i18nMsg__(' {$icu} {$startTagSpan} {$icu_1} {$closeTagSpan}', [['startTagSpan', String.raw`\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD`], ['closeTagSpan', String.raw`\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD`], ['icu', '$i18n_0$'], ['icu_1', '$i18n_1$']], {}) + return [ + $i18n_2$, + [__AttributeMarker.Template__, "ngIf"] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵtemplate(2, MyComponent_span_2_Template, 2, 1, "span", 1); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(2); + $r3$.ɵɵproperty("ngIf", ctx.ageVisible); + $r3$.ɵɵi18nExp(ctx.gender); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/different_contexts.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/different_contexts.ts new file mode 100644 index 0000000000..1f82608e06 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/different_contexts.ts @@ -0,0 +1,19 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ {gender, select, male {male} female {female} other {other}} + + {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}} + +
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/escape_quotes.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/escape_quotes.js new file mode 100644 index 0000000000..3f23331e0f --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/escape_quotes.js @@ -0,0 +1,11 @@ +let $I18N_0$; +if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + const $MSG_EXTERNAL_4166854826696768832$$APP_SPEC_TS_0$ = goog.getMsg("{VAR_SELECT, select, single {'single quotes'} double {\"double quotes\"} other {other}}"); + $I18N_0$ = $MSG_EXTERNAL_4166854826696768832$$APP_SPEC_TS_0$; +} +else { + $I18N_0$ = $localize `{VAR_SELECT, select, single {'single quotes'} double {"double quotes"} other {other}}`; +} +$I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { + "VAR_SELECT": "\uFFFD0\uFFFD" +}); \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/escape_quotes.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/escape_quotes.ts new file mode 100644 index 0000000000..65817503b2 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/escape_quotes.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
{gender, select, single {'single quotes'} double {"double quotes"} other {other}}
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/expressions.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/expressions.js new file mode 100644 index 0000000000..d9de76e0ac --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/expressions.js @@ -0,0 +1,20 @@ +decls: 2, +vars: 2, +consts: function() { + __i18nIcuMsg__('{VAR_SELECT, select, male {male of age: {INTERPOLATION}} female {female} other {other}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`], ['INTERPOLATION', String.raw`\uFFFD1\uFFFD`],]) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.gender)(ctx.ageA + ctx.ageB + ctx.ageC); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/expressions.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/expressions.ts new file mode 100644 index 0000000000..ded54469e7 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/expressions.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
{gender, select, male {male of age: {{ ageA + ageB + ageC }}} female {female} other {other}}
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.js new file mode 100644 index 0000000000..e606a62e26 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.js @@ -0,0 +1,27 @@ +decls: 5, +vars: 1, +consts: function() { + __i18nIcuMsg__('{VAR_SELECT, select, male {male - {START_BOLD_TEXT}male{CLOSE_BOLD_TEXT}} female {female {START_BOLD_TEXT}female{CLOSE_BOLD_TEXT}} other {{START_TAG_DIV}{START_ITALIC_TEXT}other{CLOSE_ITALIC_TEXT}{CLOSE_TAG_DIV}}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`], ['START_BOLD_TEXT', ''], ['CLOSE_BOLD_TEXT', ''], ['START_ITALIC_TEXT', ''], ['CLOSE_ITALIC_TEXT', ''], ['START_TAG_DIV', '
'], ['CLOSE_TAG_DIV', '
'],]) + __i18nMsg__(' {$icu} {$startBoldText}Other content{$closeBoldText}{$startTagDiv}{$startItalicText}Another content{$closeItalicText}{$closeTagDiv}', [['startBoldText', String.raw`\uFFFD#2\uFFFD`], ['closeBoldText', String.raw`\uFFFD/#2\uFFFD`], ['startTagDiv', String.raw`\uFFFD#3\uFFFD`], ['startItalicText', String.raw`\uFFFD#4\uFFFD`], ['closeItalicText', String.raw`\uFFFD/#4\uFFFD`], ['closeTagDiv', String.raw`\uFFFD/#3\uFFFD`], ['icu', '$I18N_0$']], {}) + return [ + $i18n_1$, + [__AttributeMarker.Classes__, "other"] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵelement(2, "b"); + $r3$.ɵɵelementStart(3, "div", 1); + $r3$.ɵɵelement(4, "i"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(4); + $r3$.ɵɵi18nExp(ctx.gender); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.ts new file mode 100644 index 0000000000..2739dfb294 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/html_content.ts @@ -0,0 +1,18 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ {gender, select, male {male - male} female {female female} other {
other
}} + Other content +
Another content
+
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_only.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_only.js new file mode 100644 index 0000000000..4928dde104 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_only.js @@ -0,0 +1,17 @@ +decls: 1, +vars: 1, +consts: function() { + __i18nIcuMsg__('{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18n(0, 0); + } + if (rf & 2) { + $r3$.ɵɵi18nExp(ctx.age); + $r3$.ɵɵi18nApply(0); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_only.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_only.ts new file mode 100644 index 0000000000..f243e3d692 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_only.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + {age, select, 10 {ten} 20 {twenty} other {other}} +`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_with_interpolations.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_with_interpolations.js new file mode 100644 index 0000000000..3b6fd36dca --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_with_interpolations.js @@ -0,0 +1,40 @@ +function MyComponent_span_2_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18nStart(0, 0, 1); + $r3$.ɵɵelement(1, "span"); + $r3$.ɵɵi18nEnd(); + } + if (rf & 2) { + const $ctx_r0$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp($ctx_r0$.age)($ctx_r0$.otherAge); + $r3$.ɵɵi18nApply(0); + } +} +… +decls: 3, +vars: 4, +consts: function() { + __i18nIcuMsg__('{VAR_SELECT, select, male {male {INTERPOLATION}} female {female {INTERPOLATION_1}} other {other}}',[ ['VAR_SELECT', String.raw`\uFFFD0\uFFFD`], ['INTERPOLATION', String.raw`\uFFFD1\uFFFD`], ['INTERPOLATION_1', String.raw`\uFFFD2\uFFFD`]]) + __i18nIcuMsg__('{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other: {INTERPOLATION}}}', [['VAR_SELECT', String.raw`\uFFFD0:1\uFFFD`], ['INTERPOLATION', String.raw`\uFFFD1:1\uFFFD`]]) + __i18nMsg__(' {$icu} {$startTagSpan} {$icu_1} {$closeTagSpan}', [['startTagSpan', String.raw`\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD`], ['closeTagSpan', String.raw`\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD`], ['icu', '$i18n_0$'], ['icu_1', '$i18n_1$']], {}) + return [ + $i18n_2$, + [__AttributeMarker.Template__, "ngIf"] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵtemplate(2, MyComponent_span_2_Template, 2, 2, "span", 1); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(2); + $r3$.ɵɵproperty("ngIf", ctx.ageVisible); + $r3$.ɵɵi18nExp(ctx.gender)(ctx.weight)(ctx.height); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_with_interpolations.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_with_interpolations.ts new file mode 100644 index 0000000000..07a10973bb --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_with_interpolations.ts @@ -0,0 +1,19 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ {gender, select, male {male {{ weight }}} female {female {{ height }}} other {other}} + + {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other: {{ otherAge }}}} + +
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/keyword_spaces.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/keyword_spaces.js new file mode 100644 index 0000000000..3c1b402228 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/keyword_spaces.js @@ -0,0 +1,3 @@ +__i18nIcuMsg__('{VAR_SELECT , select , 1 {one} other {more than one}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]) +__i18nIcuMsg__('{VAR_PLURAL , plural , =1 {one} other {more than one}}', [['VAR_PLURAL', String.raw`\uFFFD1\uFFFD`]]) + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/keyword_spaces.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/keyword_spaces.ts new file mode 100644 index 0000000000..e7c711f477 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/keyword_spaces.ts @@ -0,0 +1,17 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ {count, select , 1 {one} other {more than one}} + {count, plural , =1 {one} other {more than one}} +
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/metadata.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/metadata.js new file mode 100644 index 0000000000..5e2fe4b052 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/metadata.js @@ -0,0 +1 @@ +__i18nMsgWithPostprocess__('{VAR_SELECT, select, 1 {one} other {more than one}}', [], {meaning: 'meaningA', desc: 'descA', id: 'idA'}, [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]) diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/metadata.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/metadata.ts new file mode 100644 index 0000000000..5380a92f97 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/metadata.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
{count, select, 1 {one} other {more than one}}
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/multiple_icus.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/multiple_icus.js new file mode 100644 index 0000000000..98d544dad6 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/multiple_icus.js @@ -0,0 +1,22 @@ +decls: 2, +vars: 2, +consts: function() { + __i18nIcuMsg__('{VAR_SELECT, select, male {male} female {female} other {other}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]) + __i18nIcuMsg__('{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}', [['VAR_SELECT', String.raw`\uFFFD1\uFFFD`]]) + __i18nMsg__(' {$icu} {$icu_1} ', [['icu', '$i18n_0$'], ['icu_1', '$i18n_1$']], {}) + return [ + $i18n_2$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.gender)(ctx.age); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/multiple_icus.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/multiple_icus.ts new file mode 100644 index 0000000000..bfe6a15822 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/multiple_icus.ts @@ -0,0 +1,17 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ {gender, select, male {male} female {female} other {other}} + {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}} +
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/named_interpolations.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/named_interpolations.js new file mode 100644 index 0000000000..ef5dd0acbe --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/named_interpolations.js @@ -0,0 +1,20 @@ +decls: 2, +vars: 4, +consts: function() { + __i18nIcuMsg__('{VAR_SELECT, select, male {male {PH_A}} female {female {PH_B}} other {other {PH_WITH_SPACES}}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`], ['PH_A', String.raw`\uFFFD1\uFFFD`], ['PH_B', String.raw`\uFFFD2\uFFFD`], ['PH_WITH_SPACES', String.raw`\uFFFD3\uFFFD`]]) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.gender)(ctx.weight)(ctx.height)(ctx.age); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/named_interpolations.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/named_interpolations.ts new file mode 100644 index 0000000000..aa311c3354 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/named_interpolations.ts @@ -0,0 +1,20 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
{ + gender, + select, + male {male {{ weight // i18n(ph="PH_A") }}} + female {female {{ height // i18n(ph="PH_B") }}} + other {other {{ age // i18n(ph="PH WITH SPACES") }}} + }
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icu_in_other_block.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icu_in_other_block.js new file mode 100644 index 0000000000..40529096c3 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icu_in_other_block.js @@ -0,0 +1,20 @@ +decls: 2, +vars: 3, +consts: function() { + __i18nIcuMsg__('{VAR_PLURAL, plural, =0 {zero} =2 {{INTERPOLATION} {VAR_SELECT, select, cat {cats} dog {dogs} other {animals}} !} other {other - {INTERPOLATION}}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`], ['VAR_PLURAL', String.raw`\uFFFD1\uFFFD`], ['INTERPOLATION', String.raw`\uFFFD2\uFFFD`]]) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.name)(ctx.count)(ctx.count); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icu_in_other_block.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icu_in_other_block.ts new file mode 100644 index 0000000000..bbcdde3755 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icu_in_other_block.ts @@ -0,0 +1,21 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
{count, plural, + =0 {zero} + =2 {{{count}} {name, select, + cat {cats} + dog {dogs} + other {animals}} !} + other {other - {{count}}} + }
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icus.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icus.js new file mode 100644 index 0000000000..9d6b05d5c0 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icus.js @@ -0,0 +1,21 @@ +decls: 2, +vars: 2, +consts: function() { + __i18nIcuMsg__('{VAR_SELECT_1, select, male {male of age: {VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}} female {female} other {other}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`], ['VAR_SELECT_1', String.raw`\uFFFD1\uFFFD`]]) + __i18nMsg__(' {$icu} ', [['icu', '$i18n_0$']], {}) + return [ + $i18n_1$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.age)(ctx.gender); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icus.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icus.ts new file mode 100644 index 0000000000..0b267229ff --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/nested_icus.ts @@ -0,0 +1,20 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ {gender, select, + male {male of age: {age, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}} + female {female} + other {other} + } +
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/shared_placeholder.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/shared_placeholder.js new file mode 100644 index 0000000000..30d4a83e55 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/shared_placeholder.js @@ -0,0 +1,95 @@ +function MyComponent_div_3_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18nStart(0, 0, 1); + $r3$.ɵɵelement(1, "div"); + $r3$.ɵɵi18nEnd(); + } + if (rf & 2) { + const $ctx_r0$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp($ctx_r0$.gender); + $r3$.ɵɵi18nApply(0); + } +} +… +decls: 4, +vars: 3, +consts: function() { + // NOTE: Keeping raw content here to illustrate the difference in placeholders generated for goog.getMsg and $localize calls (see last i18n block). + let $I18N_1$; + if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + const $MSG_APP_SPEC_TS_1$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); + $I18N_1$ = $MSG_APP_SPEC_TS_1$; + } + else { + $I18N_1$ = $localize `{VAR_SELECT, select, male {male} female {female} other {other}}`; + } + $I18N_1$ = $r3$.ɵɵi18nPostprocess($I18N_1$, { + "VAR_SELECT": "\uFFFD0\uFFFD" + }); + + let $I18N_2$; + if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + const $MSG_APP_SPEC_TS_2$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); + $I18N_2$ = $MSG_APP_SPEC_TS_2$; + } + else { + $I18N_2$ = $localize `{VAR_SELECT, select, male {male} female {female} other {other}}`; + } + $I18N_2$ = $r3$.ɵɵi18nPostprocess($I18N_2$, { + "VAR_SELECT": "\uFFFD1\uFFFD" + }); + + let $I18N_4$; + if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + const $MSG_APP_SPEC_TS__4$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); + $I18N_4$ = $MSG_APP_SPEC_TS__4$; + } + else { + $I18N_4$ = $localize `{VAR_SELECT, select, male {male} female {female} other {other}}`; + } + $I18N_4$ = $r3$.ɵɵi18nPostprocess($I18N_4$, { + "VAR_SELECT": "\uFFFD0:1\uFFFD" + }); + + let $I18N_0$; + if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + const $MSG_APP_SPEC_TS_0$ = goog.getMsg(" {$icu} {$startTagDiv} {$icu} {$closeTagDiv}{$startTagDiv_1} {$icu} {$closeTagDiv}", { + "startTagDiv": "\uFFFD#2\uFFFD", + "closeTagDiv": "[\uFFFD/#2\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*3:1\uFFFD]", + "startTagDiv_1": "\uFFFD*3:1\uFFFD\uFFFD#1:1\uFFFD", + "icu": "\uFFFDI18N_EXP_ICU\uFFFD" + }); + $I18N_0$ = $MSG_APP_SPEC_TS_0$; + } + else { + $I18N_0$ = $localize ` ${"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: ${"\uFFFD#2\uFFFD"}:START_TAG_DIV: ${"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: ${"[\uFFFD/#2\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*3:1\uFFFD]"}:CLOSE_TAG_DIV:${"\uFFFD*3:1\uFFFD\uFFFD#1:1\uFFFD"}:START_TAG_DIV_1: ${"\uFFFDI18N_EXP_ICU\uFFFD"}:ICU: ${"[\uFFFD/#2\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*3:1\uFFFD]"}:CLOSE_TAG_DIV:`; + } + + $I18N_0$ = $r3$.ɵɵi18nPostprocess($I18N_0$, { + "ICU": [$I18N_1$, $I18N_2$, $I18N_4$] + }); + return [ + $i18n_0$, + [__AttributeMarker.Template__, "ngIf"] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵelement(2, "div"); + $r3$.ɵɵtemplate(3, MyComponent_div_3_Template, 2, 1, "div", 1); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(3); + $r3$.ɵɵproperty("ngIf", ctx.visible); + $r3$.ɵɵi18nExp(ctx.gender)(ctx.gender); + $r3$.ɵɵi18nApply(1); + } +} + +// NOTE: TODO(FW-635): this use-case is currently supported with file-based prefix for translation const names. +// NOTE: Translation statements caching is required to support this use-case with id-based consts. diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/shared_placeholder.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/shared_placeholder.ts new file mode 100644 index 0000000000..83649faea6 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/shared_placeholder.ts @@ -0,0 +1,22 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ {gender, select, male {male} female {female} other {other}} +
+ {gender, select, male {male} female {female} other {other}} +
+
+ {gender, select, male {male} female {female} other {other}} +
+
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/single_icu.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/single_icu.js new file mode 100644 index 0000000000..d7a520d8f4 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/single_icu.js @@ -0,0 +1,20 @@ +decls: 2, +vars: 1, +consts: function() { + __i18nIcuMsg__('{VAR_SELECT, select, male {male} female {female} other {other}}',[['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.gender); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/single_icu.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/single_icu.ts new file mode 100644 index 0000000000..55f82b75c3 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/single_icu.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
{gender, select, male {male} female {female} other {other}}
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/GOLDEN_PARTIAL.js new file mode 100644 index 0000000000..3cc02fb8f7 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/GOLDEN_PARTIAL.js @@ -0,0 +1,408 @@ +/**************************************************************************************************** + * PARTIAL FILE: inline_template_non_legacy.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+Some Message +{ + value, + select, + =0 { + zero + } +}
`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + // NOTE: This template has escaped `\r\n` line-endings markers that will be converted to real + // `\r\n` line-ending chars when loaded from the test file-system. + template: ` +
+Some Message +{ + value, + select, + =0 { + zero + } +}
` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: inline_template_non_legacy.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: inline_template_non_legacy.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+Some Message +{ + value, + select, + =0 { + zero + } +}
`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + // NOTE: This template has escaped `\r\n` line-endings markers that will be converted to real + // `\r\n` line-ending chars when loaded from the test file-system. + template: ` +
+Some Message +{ + value, + select, + =0 { + zero + } +}
` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: inline_template_non_legacy.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: external_template_non_legacy.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: "\n
\r\nSome Message\r\n{\r\n value,\r\n select,\r\n =0 {\r\n zero\r\n }\r\n}
", isInline: false } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + // NOTE: The template has escaped `\r\n` line-endings markers that will be converted to real + // `\r\n` line-ending chars when loaded from the test file-system. + templateUrl: 'template.html' + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: external_template_non_legacy.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: external_template_non_legacy.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: "\n
\r\nSome Message\r\n{\r\n value,\r\n select,\r\n =0 {\r\n zero\r\n }\r\n}
", isInline: false } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + // NOTE: The template has escaped `\r\n` line-endings markers that will be converted to real + // `\r\n` line-ending chars when loaded from the test file-system. + templateUrl: 'template.html' + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: external_template_non_legacy.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: inline_template_legacy.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+Some Message +{ + value, + select, + =0 { + zero + } +}
`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + // NOTE: This template has escaped `\r\n` line-endings markers that will be converted to real + // `\r\n` line-ending chars when loaded from the test file-system. + template: ` +
+Some Message +{ + value, + select, + =0 { + zero + } +}
` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: inline_template_legacy.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: inline_template_legacy.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+Some Message +{ + value, + select, + =0 { + zero + } +}
`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + // NOTE: This template has escaped `\r\n` line-endings markers that will be converted to real + // `\r\n` line-ending chars when loaded from the test file-system. + template: ` +
+Some Message +{ + value, + select, + =0 { + zero + } +}
` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: inline_template_legacy.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: external_template_legacy_normalized.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: "\n
\r\nSome Message\r\n{\r\n value,\r\n select,\r\n =0 {\r\n zero\r\n }\r\n}
", isInline: false } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + // NOTE: The template has escaped `\r\n` line-endings markers that will be converted to real + // `\r\n` line-ending chars when loaded from the test file-system. + templateUrl: 'template.html' + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: external_template_legacy_normalized.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: external_template_legacy.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: "\n
\r\nSome Message\r\n{\r\n value,\r\n select,\r\n =0 {\r\n zero\r\n }\r\n}
", isInline: false } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + // NOTE: The template has escaped `\r\n` line-endings markers that will be converted to real + // `\r\n` line-ending chars when loaded from the test file-system. + templateUrl: 'template.html' + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: external_template_legacy.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/TEST_CASES.json new file mode 100644 index 0000000000..9677da1a75 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/TEST_CASES.json @@ -0,0 +1,197 @@ +{ + "$schema": "../../test_case_schema.json", + "cases": [ + { + "description": "should normalize non-legacy message line endings in inline templates where i18nNormalizeLineEndingsInICUs is true", + "inputFiles": [ + "inline_template_non_legacy.ts" + ], + "angularCompilerOptions": { + "i18nNormalizeLineEndingsInICUs": true, + "enableI18nLegacyMessageIdFormat": false + }, + "expectations": [ + { + "files": [ + { + "generated": "inline_template_non_legacy.js", + "expected": "non_legacy.js" + } + ], + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should normalize non-legacy message line endings in inline templates where i18nNormalizeLineEndingsInICUs is false", + "inputFiles": [ + "inline_template_non_legacy.ts" + ], + "angularCompilerOptions": { + "i18nNormalizeLineEndingsInICUs": false, + "enableI18nLegacyMessageIdFormat": false + }, + "expectations": [ + { + "files": [ + { + "generated": "inline_template_non_legacy.js", + "expected": "non_legacy.js" + } + ], + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should normalize non-legacy message line endings in external templates where i18nNormalizeLineEndingsInICUs is true", + "inputFiles": [ + "external_template_non_legacy.ts" + ], + "angularCompilerOptions": { + "i18nNormalizeLineEndingsInICUs": true, + "enableI18nLegacyMessageIdFormat": false + }, + "expectations": [ + { + "files": [ + { + "generated": "external_template_non_legacy.js", + "expected": "non_legacy.js" + } + ], + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should normalize non-legacy line endings in external templates where i18nNormalizeLineEndingsInICUs is false", + "inputFiles": [ + "external_template_non_legacy.ts" + ], + "angularCompilerOptions": { + "i18nNormalizeLineEndingsInICUs": false, + "enableI18nLegacyMessageIdFormat": false + }, + "expectations": [ + { + "files": [ + { + "generated": "external_template_non_legacy.js", + "expected": "non_legacy.js" + } + ], + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should compute normalized legacy ids for messages in inline templates where i18nNormalizeLineEndingsInICUs is true", + "inputFiles": [ + "inline_template_legacy.ts" + ], + "angularCompilerOptions": { + "i18nNormalizeLineEndingsInICUs": true, + "enableI18nLegacyMessageIdFormat": true + }, + "expectations": [ + { + "files": [ + { + "generated": "inline_template_legacy.js", + "expected": "legacy_normalized.js" + } + ], + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should compute normalized legacy ids for messages in inline templates where i18nNormalizeLineEndingsInICUs is false", + "inputFiles": [ + "inline_template_legacy.ts" + ], + "angularCompilerOptions": { + "i18nNormalizeLineEndingsInICUs": false, + "enableI18nLegacyMessageIdFormat": true + }, + "expectations": [ + { + "files": [ + { + "generated": "inline_template_legacy.js", + "expected": "legacy_normalized.js" + } + ], + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should compute normalized legacy ids for messages in external templates where i18nNormalizeLineEndingsInICUs is true", + "inputFiles": [ + "external_template_legacy_normalized.ts" + ], + "angularCompilerOptions": { + "i18nNormalizeLineEndingsInICUs": true, + "enableI18nLegacyMessageIdFormat": true + }, + "expectations": [ + { + "files": [ + { + "generated": "external_template_legacy_normalized.js", + "expected": "legacy_normalized.js" + } + ], + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should compute non-normalized legacy ids for messages in external templates where i18nNormalizeLineEndingsInICUs is false", + "inputFiles": [ + "external_template_legacy.ts" + ], + "angularCompilerOptions": { + "i18nNormalizeLineEndingsInICUs": false, + "enableI18nLegacyMessageIdFormat": true + }, + "expectations": [ + { + "files": [ + { + "generated": "external_template_legacy.js", + "expected": "legacy_nonnormalized.js" + } + ], + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + } + ] +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy.ts new file mode 100644 index 0000000000..7e3a6f14d1 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + // NOTE: The template has escaped `\r\n` line-endings markers that will be converted to real + // `\r\n` line-ending chars when loaded from the test file-system. + templateUrl: 'template.html' +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy_normalized.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy_normalized.ts new file mode 100644 index 0000000000..7e3a6f14d1 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_legacy_normalized.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + // NOTE: The template has escaped `\r\n` line-endings markers that will be converted to real + // `\r\n` line-ending chars when loaded from the test file-system. + templateUrl: 'template.html' +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_non_legacy.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_non_legacy.ts new file mode 100644 index 0000000000..7e3a6f14d1 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/external_template_non_legacy.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + // NOTE: The template has escaped `\r\n` line-endings markers that will be converted to real + // `\r\n` line-ending chars when loaded from the test file-system. + templateUrl: 'template.html' +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_legacy.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_legacy.ts new file mode 100644 index 0000000000..4cce3c69a6 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_legacy.ts @@ -0,0 +1,24 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + // NOTE: This template has escaped `\r\n` line-endings markers that will be converted to real + // `\r\n` line-ending chars when loaded from the test file-system. + template: ` +
\r\n +Some Message\r\n +{\r\n + value,\r\n + select,\r\n + =0 {\r\n + zero\r\n + }\r\n +}
` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_non_legacy.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_non_legacy.ts new file mode 100644 index 0000000000..4cce3c69a6 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/inline_template_non_legacy.ts @@ -0,0 +1,24 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + // NOTE: This template has escaped `\r\n` line-endings markers that will be converted to real + // `\r\n` line-ending chars when loaded from the test file-system. + template: ` +
\r\n +Some Message\r\n +{\r\n + value,\r\n + select,\r\n + =0 {\r\n + zero\r\n + }\r\n +}
` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_nonnormalized.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_nonnormalized.js new file mode 100644 index 0000000000..6706670177 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_nonnormalized.js @@ -0,0 +1,10 @@ +// NOTE: The ids generated by the compiler are different if the template is external and we are not explicitly normalizing the line endings. +$I18N_0$ = $localize `:␟4f9ce2c66b187afd9898b25f6336d1eb2be8b5dc␟7326958852138509669:abc +def`; +… +$I18N_4$ = $localize `:␟70a685282be2d956e4db234fa3d985970672faa0␟4863953183043480207:{VAR_SELECT, select, =0 {zero + }}` +… +$I18N_3$ = $localize `:␟6a55b51b9bcf8f84b1b868c585ae09949668a72b␟2773178924738647105: +Some Message +${$I18N_4$}:ICU:`; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_normalized.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_normalized.js new file mode 100644 index 0000000000..7ad84559ff --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/legacy_normalized.js @@ -0,0 +1,10 @@ +// NOTE: The ids generated by the compiler are different if the template is external and we are not explicitly normalizing the line endings. +$I18N_0$ = $localize `:␟4f9ce2c66b187afd9898b25f6336d1eb2be8b5dc␟7326958852138509669:abc +def`; +… +$I18N_4$ = $localize `:␟b5fe162f4e47ab5b3e534491d30b715e0dff0f52␟4863953183043480207:{VAR_SELECT, select, =0 {zero + }}` +… +$I18N_3$ = $localize `:␟e31c7bc4db2f2e56dc40f005958055a02fd43a2e␟2773178924738647105: +Some Message +${$I18N_4$}:ICU:`; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/non_legacy.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/non_legacy.js new file mode 100644 index 0000000000..438d8776ee --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/non_legacy.js @@ -0,0 +1,9 @@ +$I18N_0$ = $localize `abc +def`; +… +$I18N_4$ = $localize `{VAR_SELECT, select, =0 {zero + }}` +… +$I18N_3$ = $localize ` +Some Message +${$I18N_4$}:ICU:`; diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/template.html b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/template.html new file mode 100644 index 0000000000..81f2da3e5c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/line_ending_normalization/template.html @@ -0,0 +1,11 @@ + +
\r\n +Some Message\r\n +{\r\n + value,\r\n + select,\r\n + =0 {\r\n + zero\r\n + }\r\n +}
\ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/GOLDEN_PARTIAL.js new file mode 100644 index 0000000000..d00c67d51c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/GOLDEN_PARTIAL.js @@ -0,0 +1,86 @@ +/**************************************************************************************************** + * PARTIAL FILE: legacy_enabled.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
Some Message
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
Some Message
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: legacy_enabled.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: legacy_disabled.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
Some Message
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
Some Message
+` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: legacy_disabled.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/TEST_CASES.json new file mode 100644 index 0000000000..c5ff12e140 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/TEST_CASES.json @@ -0,0 +1,39 @@ +{ + "$schema": "../../test_case_schema.json", + "cases": [ + { + "description": "should add legacy message ids if `enableI18nLegacyMessageIdFormat` is true", + "inputFiles": [ + "legacy_enabled.ts" + ], + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": true + }, + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should not add legacy message ids if `enableI18nLegacyMessageIdFormat` is false", + "inputFiles": [ + "legacy_disabled.ts" + ], + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false + }, + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + } + ] +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_disabled.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_disabled.js new file mode 100644 index 0000000000..5cd67b37f4 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_disabled.js @@ -0,0 +1,6 @@ +let $I18N_0$; +if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + … +} else { + $I18N_0$ = $localize`Some Message`; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_disabled.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_disabled.ts new file mode 100644 index 0000000000..014baabeee --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_disabled.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
Some Message
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_enabled.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_enabled.js new file mode 100644 index 0000000000..46587d0d12 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_enabled.js @@ -0,0 +1,5 @@ +let $I18N_0$; +if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { … } +else { + $I18N_0$ = $localize `:␟ec93160d6d6a8822214060dd7938bf821c22b226␟6795333002533525253:Some Message`; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_enabled.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_enabled.ts new file mode 100644 index 0000000000..014baabeee --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/localize_legacy_message_ids/legacy_enabled.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
Some Message
+` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/GOLDEN_PARTIAL.js new file mode 100644 index 0000000000..93fd6a839b --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/GOLDEN_PARTIAL.js @@ -0,0 +1,110 @@ +/**************************************************************************************************** + * PARTIAL FILE: foreign_object.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + + + + Count: 5 + + + +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + + + + Count: 5 + + + +`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: foreign_object.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: namespaced_div.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + + + + Count: 5 + + + +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + + + + Count: 5 + + + +`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: namespaced_div.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/TEST_CASES.json new file mode 100644 index 0000000000..029e24f3c3 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/TEST_CASES.json @@ -0,0 +1,33 @@ +{ + "$schema": "../../test_case_schema.json", + "cases": [ + { + "description": "should handle namespaces inside i18n blocks", + "inputFiles": [ + "foreign_object.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle namespaces on i18n block containers", + "inputFiles": [ + "namespaced_div.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + } + ] +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/foreign_object.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/foreign_object.js new file mode 100644 index 0000000000..b1c6dc2856 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/foreign_object.js @@ -0,0 +1,36 @@ + +consts: function() { + let $I18N_0$; + if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + const $MSG_EXTERNAL_7128002169381370313$$APP_SPEC_TS_1$ = goog.getMsg("{$startTagXhtmlDiv} Count: {$startTagXhtmlSpan}5{$closeTagXhtmlSpan}{$closeTagXhtmlDiv}", { + "startTagXhtmlDiv": "\uFFFD#3\uFFFD", + "startTagXhtmlSpan": "\uFFFD#4\uFFFD", + "closeTagXhtmlSpan": "\uFFFD/#4\uFFFD", + "closeTagXhtmlDiv": "\uFFFD/#3\uFFFD" + }); + $I18N_0$ = $MSG_EXTERNAL_7128002169381370313$$APP_SPEC_TS_1$; + } + else { + $I18N_0$ = $localize `${"\uFFFD#3\uFFFD"}:START_TAG__XHTML_DIV: Count: ${"\uFFFD#4\uFFFD"}:START_TAG__XHTML_SPAN:5${"\uFFFD/#4\uFFFD"}:CLOSE_TAG__XHTML_SPAN:${"\uFFFD/#3\uFFFD"}:CLOSE_TAG__XHTML_DIV:`; + } + return [ + ["xmlns", "http://www.w3.org/2000/svg"], + $i18n_0$, + ["xmlns", "http://www.w3.org/1999/xhtml"] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵnamespaceSVG(); + $r3$.ɵɵelementStart(0, "svg", 0); + $r3$.ɵɵelementStart(1, "foreignObject"); + $r3$.ɵɵi18nStart(2, 1); + $r3$.ɵɵnamespaceHTML(); + $r3$.ɵɵelementStart(3, "div", 2); + $r3$.ɵɵelement(4, "span"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementEnd(); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/foreign_object.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/foreign_object.ts new file mode 100644 index 0000000000..15ce810329 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/foreign_object.ts @@ -0,0 +1,20 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + + + + Count: 5 + + + +`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/namespaced_div.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/namespaced_div.js new file mode 100644 index 0000000000..b3305e6828 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/namespaced_div.js @@ -0,0 +1,33 @@ +consts: function() { + let $I18N_0$; + if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + const $MSG_EXTERNAL_7428861019045796010$$APP_SPEC_TS_1$ = goog.getMsg(" Count: {$startTagXhtmlSpan}5{$closeTagXhtmlSpan}", { + "startTagXhtmlSpan": "\uFFFD#4\uFFFD", + "closeTagXhtmlSpan": "\uFFFD/#4\uFFFD" + }); + $I18N_0$ = $MSG_EXTERNAL_7428861019045796010$$APP_SPEC_TS_1$; + } + else { + $I18N_0$ = $localize ` Count: ${"\uFFFD#4\uFFFD"}:START_TAG__XHTML_SPAN:5${"\uFFFD/#4\uFFFD"}:CLOSE_TAG__XHTML_SPAN:`; + } + return [ + ["xmlns", "http://www.w3.org/2000/svg"], + ["xmlns", "http://www.w3.org/1999/xhtml"], + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵnamespaceSVG(); + $r3$.ɵɵelementStart(0, "svg", 0); + $r3$.ɵɵelementStart(1, "foreignObject"); + $r3$.ɵɵnamespaceHTML(); + $r3$.ɵɵelementStart(2, "div", 1); + $r3$.ɵɵi18nStart(3, 2); + $r3$.ɵɵelement(4, "span"); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementEnd(); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/namespaced_div.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/namespaced_div.ts new file mode 100644 index 0000000000..2b8d51a865 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/namespaces/namespaced_div.ts @@ -0,0 +1,20 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + + + + Count: 5 + + + +`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/GOLDEN_PARTIAL.js new file mode 100644 index 0000000000..2e4706b644 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/GOLDEN_PARTIAL.js @@ -0,0 +1,831 @@ +/**************************************************************************************************** + * PARTIAL FILE: empty_content.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+
+
+ +
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+
+
+ +
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: empty_content.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: comments_in_translated_text.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
Some text
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
Some text
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: comments_in_translated_text.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: escape_quotes.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
Some text 'with single quotes', "with double quotes", \`with backticks\` and without quotes.
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
Some text 'with single quotes', "with double quotes", \`with backticks\` and without quotes.
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: escape_quotes.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: backtick_quotes.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '
`{{ count }}`
', isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: '
`{{ count }}`
', + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: backtick_quotes.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: plain_text_messages.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
My i18n block #1
+
My non-i18n block #1
+
My i18n block #2
+
My non-i18n block #2
+
My i18n block #3
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
My i18n block #1
+
My non-i18n block #1
+
My i18n block #2
+
My non-i18n block #2
+
My i18n block #3
+ `, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: plain_text_messages.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: named_interpolations.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ Named interpolation: {{ valueA // i18n(ph="PH_A") }} + Named interpolation with spaces: {{ valueB // i18n(ph="PH B") }} +
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ Named interpolation: {{ valueA // i18n(ph="PH_A") }} + Named interpolation with spaces: {{ valueB // i18n(ph="PH B") }} +
+`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: named_interpolations.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_custom_config.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
{% valueA %}
+ `, isInline: true }, interpolation: ["{%", "%}"] }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
{% valueA %}
+ `, + interpolation: ['{%', '%}'], + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_custom_config.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_complex_expressions.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ {{ valueA | async }} + {{ valueA?.a?.b }} + {{ valueA.getRawValue()?.getTitle() }} +
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ {{ valueA | async }} + {{ valueA?.a?.b }} + {{ valueA.getRawValue()?.getTitle() }} +
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: interpolation_complex_expressions.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: bindings_in_content.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
My i18n block #{{ one }}
+
My i18n block #{{ two | uppercase }}
+
My i18n block #{{ three + four + five }}
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
My i18n block #{{ one }}
+
My i18n block #{{ two | uppercase }}
+
My i18n block #{{ three + four + five }}
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: bindings_in_content.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: nested_elements.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ My i18n block #{{ one }} + Plain text in nested element +
+
+ My i18n block #{{ two | uppercase }} +
+
+ + More bindings in more nested element: {{ nestedInBlockTwo }} + +
+
+
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ My i18n block #{{ one }} + Plain text in nested element +
+
+ My i18n block #{{ two | uppercase }} +
+
+ + More bindings in more nested element: {{ nestedInBlockTwo }} + +
+
+
+`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: nested_elements.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: nested_elements_with_i18n_attributes.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ My i18n block #1 with value: {{ valueA }} + + Plain text in nested element (block #1) + +
+
+ My i18n block #2 with value {{ valueD | uppercase }} + + Plain text in nested element (block #2) + +
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ My i18n block #1 with value: {{ valueA }} + + Plain text in nested element (block #1) + +
+
+ My i18n block #2 with value {{ valueD | uppercase }} + + Plain text in nested element (block #2) + +
+`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: nested_elements_with_i18n_attributes.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: nested_templates.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ Some content +
+
+ Some other content {{ valueA }} +
+ More nested levels with bindings {{ valueB | uppercase }} +
+
+
+
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ Some content +
+
+ Some other content {{ valueA }} +
+ More nested levels with bindings {{ valueB | uppercase }} +
+
+
+
+`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: nested_templates.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: self_closing.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + + + + `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + + + + `, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: self_closing.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: nested_templates_context.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ Some content +
+ Some other content {{ valueA }} +
+ More nested levels with bindings {{ valueB | uppercase }} +
+ Content inside sub-template {{ valueC }} +
+ Bottom level element {{ valueD }} +
+
+
+
+
+ Some other content {{ valueE + valueF }} +
+ More nested levels with bindings {{ valueG | uppercase }} +
+
+
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ Some content +
+ Some other content {{ valueA }} +
+ More nested levels with bindings {{ valueB | uppercase }} +
+ Content inside sub-template {{ valueC }} +
+ Bottom level element {{ valueD }} +
+
+
+
+
+ Some other content {{ valueE + valueF }} +
+ More nested levels with bindings {{ valueG | uppercase }} +
+
+
+`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: nested_templates_context.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: directives.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
Some other content {{ valueA }}
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
Some other content {{ valueA }}
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: directives.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: event_listeners.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
Hello
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
Hello
+ ` + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: event_listeners.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/TEST_CASES.json new file mode 100644 index 0000000000..52c91f0f47 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/TEST_CASES.json @@ -0,0 +1,229 @@ +{ + "$schema": "../../test_case_schema.json", + "cases": [ + { + "description": "should not produce instructions for empty content", + "inputFiles": [ + "empty_content.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should ignore HTML comments within translated text", + "inputFiles": [ + "comments_in_translated_text.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should properly escape quotes in content", + "inputFiles": [ + "escape_quotes.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle interpolations wrapped in backticks", + "inputFiles": [ + "backtick_quotes.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle i18n attributes with plain-text content", + "inputFiles": [ + "plain_text_messages.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should support named interpolations", + "inputFiles": [ + "named_interpolations.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should support interpolation with custom interpolation config", + "inputFiles": [ + "interpolation_custom_config.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should support interpolations with complex expressions", + "inputFiles": [ + "interpolation_complex_expressions.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle i18n attributes with bindings in content", + "inputFiles": [ + "bindings_in_content.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle i18n attributes with bindings and nested elements in content", + "inputFiles": [ + "nested_elements.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle i18n attributes with bindings in content and element attributes", + "inputFiles": [ + "nested_elements_with_i18n_attributes.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle i18n attributes in nested templates", + "inputFiles": [ + "nested_templates.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should ignore i18n attributes on self-closing tags", + "inputFiles": [ + "self_closing.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle i18n context in nested templates", + "inputFiles": [ + "nested_templates_context.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle i18n attribute with directives", + "inputFiles": [ + "directives.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should generate event listeners instructions before i18n ones", + "inputFiles": [ + "event_listeners.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + } + ] +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/backtick_quotes.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/backtick_quotes.js new file mode 100644 index 0000000000..baa4f5a02c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/backtick_quotes.js @@ -0,0 +1,9 @@ +// NOTE: Keeping raw content (avoiding `__i18nMsg__` macro) to illustrate backticks escaping. +let $I18N_0$; +if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + const $MSG_APP_SPEC_TS_1$ = goog.getMsg("`{$interpolation}`", { "interpolation": "\uFFFD0\uFFFD" }); + $I18N_0$ = $MSG_APP_SPEC_TS_1$; +} +else { + $I18N_0$ = $localize `\`${"\uFFFD0\uFFFD"}:INTERPOLATION:\``; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/backtick_quotes.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/backtick_quotes.ts new file mode 100644 index 0000000000..6337d35ab5 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/backtick_quotes.ts @@ -0,0 +1,12 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: '
`{{ count }}`
', +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/bindings_in_content.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/bindings_in_content.js new file mode 100644 index 0000000000..73f772d5c5 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/bindings_in_content.js @@ -0,0 +1,37 @@ +decls: 7, +vars: 5, +consts: function() { + __i18nMsg__('My i18n block #{$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {}) + __i18nMsg__('My i18n block #{$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {}) + __i18nMsg__('My i18n block #{$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {}) + return [ + $i18n_0$, + $i18n_1$, + $i18n_2$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(2, "div"); + $r3$.ɵɵi18n(3, 1); + $r3$.ɵɵpipe(4, "uppercase"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(5, "div"); + $r3$.ɵɵi18n(6, 2); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.one); + $r3$.ɵɵi18nApply(1); + $r3$.ɵɵadvance(3); + $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(4, 3, ctx.two)); + $r3$.ɵɵi18nApply(3); + $r3$.ɵɵadvance(2); + $r3$.ɵɵi18nExp(ctx.three + ctx.four + ctx.five); + $r3$.ɵɵi18nApply(6); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/bindings_in_content.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/bindings_in_content.ts new file mode 100644 index 0000000000..4a4ea77d5d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/bindings_in_content.ts @@ -0,0 +1,16 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
My i18n block #{{ one }}
+
My i18n block #{{ two | uppercase }}
+
My i18n block #{{ three + four + five }}
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/comments_in_translated_text.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/comments_in_translated_text.js new file mode 100644 index 0000000000..947e4fac91 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/comments_in_translated_text.js @@ -0,0 +1 @@ +__i18nMsg__('Some text', [], {}) \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/comments_in_translated_text.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/comments_in_translated_text.ts new file mode 100644 index 0000000000..e38ed1f156 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/comments_in_translated_text.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
Some text
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/directives.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/directives.js new file mode 100644 index 0000000000..dea7a4dbdc --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/directives.js @@ -0,0 +1,33 @@ +function MyComponent_div_0_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18nStart(1, 1); + $r3$.ɵɵelement(2, "span"); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + const $ctx_r0$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵadvance(2); + $r3$.ɵɵi18nExp($ctx_r0$.valueA); + $r3$.ɵɵi18nApply(1); + } +} +… +decls: 1, +vars: 1, +consts: function() { + __i18nMsg__('Some other content {$startTagSpan}{$interpolation}{$closeTagSpan}', [['startTagSpan', String.raw`\uFFFD#2\uFFFD`], ['interpolation', String.raw`\uFFFD0\uFFFD`], ['closeTagSpan', String.raw`\uFFFD/#2\uFFFD`]], {}) + return [ + [__AttributeMarker.Template__, "ngIf"], + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtemplate(0, MyComponent_div_0_Template, 3, 1, "div", 0); + } + if (rf & 2) { + $r3$.ɵɵproperty("ngIf", ctx.visible); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/directives.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/directives.ts new file mode 100644 index 0000000000..5039e13dc6 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/directives.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
Some other content {{ valueA }}
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/empty_content.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/empty_content.js new file mode 100644 index 0000000000..ab2df11118 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/empty_content.js @@ -0,0 +1,7 @@ +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelement(0, "div"); + $r3$.ɵɵelement(1, "div"); + $r3$.ɵɵelement(2, "div"); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/empty_content.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/empty_content.ts new file mode 100644 index 0000000000..82470512df --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/empty_content.ts @@ -0,0 +1,18 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+
+
+ +
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/escape_quotes.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/escape_quotes.js new file mode 100644 index 0000000000..8c4e9a799d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/escape_quotes.js @@ -0,0 +1,9 @@ +// NOTE: Keeping raw content (avoiding `__i18nMsg__` macro) to illustrate quotes escaping. +let $I18N_0$; +if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + const $MSG_EXTERNAL_4924931801512133405$$APP_SPEC_TS_0$ = goog.getMsg("Some text 'with single quotes', \"with double quotes\", `with backticks` and without quotes."); + $I18N_0$ = $MSG_EXTERNAL_4924931801512133405$$APP_SPEC_TS_0$; +} +else { + $I18N_0$ = $localize `Some text 'with single quotes', "with double quotes", \`with backticks\` and without quotes.`; +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/escape_quotes.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/escape_quotes.ts new file mode 100644 index 0000000000..9fc9b76730 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/escape_quotes.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
Some text 'with single quotes', "with double quotes", \`with backticks\` and without quotes.
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/event_listeners.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/event_listeners.js new file mode 100644 index 0000000000..cbe50d21cb --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/event_listeners.js @@ -0,0 +1,15 @@ +consts: function() { + __i18nMsg__('Hello', [], {}) + return [ + [__AttributeMarker.Bindings__, "click"], + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div", 0); + $r3$.ɵɵlistener("click", function MyComponent_Template_div_click_0_listener() { return ctx.onClick(); }); + $r3$.ɵɵi18n(1, 1); + $r3$.ɵɵelementEnd(); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/event_listeners.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/event_listeners.ts new file mode 100644 index 0000000000..987a192757 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/event_listeners.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
Hello
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.js new file mode 100644 index 0000000000..5f577ba2fb --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.js @@ -0,0 +1,22 @@ +consts: function() { + __i18nMsg__(' {$interpolation} {$interpolation_1} {$interpolation_2} ', [['interpolation', String.raw`\uFFFD0\uFFFD`], ['interpolation_1', String.raw`\uFFFD1\uFFFD`], ['interpolation_2', String.raw`\uFFFD2\uFFFD`]], {}) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵpipe(2, "async"); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + let $tmp_2_0$ = null; + $r3$.ɵɵadvance(2); + $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(2, 3, ctx.valueA)) + (ctx.valueA == null ? null : ctx.valueA.a == null ? null : ctx.valueA.a.b) + (($tmp_2_0$ = ctx.valueA.getRawValue()) == null ? null : $tmp_2_0$.getTitle()); + $r3$.ɵɵi18nApply(1); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.ts new file mode 100644 index 0000000000..394b39565b --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.ts @@ -0,0 +1,18 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ {{ valueA | async }} + {{ valueA?.a?.b }} + {{ valueA.getRawValue()?.getTitle() }} +
+ ` +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_custom_config.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_custom_config.js new file mode 100644 index 0000000000..c13d6ff524 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_custom_config.js @@ -0,0 +1,18 @@ +consts: function() { + __i18nMsg__('{$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {}) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.valueA); + $r3$.ɵɵi18nApply(1); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_custom_config.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_custom_config.ts new file mode 100644 index 0000000000..efbd1e39ed --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_custom_config.ts @@ -0,0 +1,15 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
{% valueA %}
+ `, + interpolation: ['{%', '%}'], +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/named_interpolations.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/named_interpolations.js new file mode 100644 index 0000000000..4080bfa6f3 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/named_interpolations.js @@ -0,0 +1,31 @@ +// NOTE: Keeping raw content (avoiding `__i18nMsg__` macro) to illustrate how named interpolations are generated. +decls: 2, +vars: 2, +consts: function() { + let $I18N_0$; + if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + const $MSG_EXTERNAL_7597881511811528589$$APP_SPEC_TS_0$ = goog.getMsg(" Named interpolation: {$phA} Named interpolation with spaces: {$phB} ", { + "phA": "\uFFFD0\uFFFD", + "phB": "\uFFFD1\uFFFD" + }); + $I18N_0$ = $MSG_EXTERNAL_7597881511811528589$$APP_SPEC_TS_0$; + } + else { + $I18N_0$ = $localize ` Named interpolation: ${"\uFFFD0\uFFFD"}:PH_A: Named interpolation with spaces: ${"\uFFFD1\uFFFD"}:PH_B: `; + } + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.valueA)(ctx.valueB); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/named_interpolations.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/named_interpolations.ts new file mode 100644 index 0000000000..5ddfce4ae9 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/named_interpolations.ts @@ -0,0 +1,17 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ Named interpolation: {{ valueA // i18n(ph="PH_A") }} + Named interpolation with spaces: {{ valueB // i18n(ph="PH B") }} +
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements.js new file mode 100644 index 0000000000..1383ea526d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements.js @@ -0,0 +1,37 @@ +decls: 9, +vars: 5, +consts: function() { + __i18nMsg__(' My i18n block #{$interpolation} {$startTagSpan}Plain text in nested element{$closeTagSpan}', [['interpolation', String.raw`\uFFFD0\uFFFD`], ['startTagSpan', String.raw`\uFFFD#2\uFFFD`], ['closeTagSpan', String.raw`\uFFFD/#2\uFFFD`]], {}) + __i18nMsgWithPostprocess__(' My i18n block #{$interpolation} {$startTagDiv}{$startTagDiv}{$startTagSpan} More bindings in more nested element: {$interpolation_1} {$closeTagSpan}{$closeTagDiv}{$closeTagDiv}', [['interpolation', String.raw`\uFFFD0\uFFFD`], ['startTagDiv', String.raw`[\uFFFD#6\uFFFD|\uFFFD#7\uFFFD]`], ['startTagSpan', String.raw`\uFFFD#8\uFFFD`], ['interpolation_1', String.raw`\uFFFD1\uFFFD`], ['closeTagSpan', String.raw`\uFFFD/#8\uFFFD`], ['closeTagDiv', String.raw`[\uFFFD/#7\uFFFD|\uFFFD/#6\uFFFD]`]], {}, []) + return [ + $i18n_0$, + $i18n_1$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵelement(2, "span"); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(3, "div"); + $r3$.ɵɵi18nStart(4, 1); + $r3$.ɵɵpipe(5, "uppercase"); + $r3$.ɵɵelementStart(6, "div"); + $r3$.ɵɵelementStart(7, "div"); + $r3$.ɵɵelement(8, "span"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(2); + $r3$.ɵɵi18nExp(ctx.one); + $r3$.ɵɵi18nApply(1); + $r3$.ɵɵadvance(6); + $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(5, 3, ctx.two))(ctx.nestedInBlockTwo); + $r3$.ɵɵi18nApply(4); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements.ts new file mode 100644 index 0000000000..9a3d88576a --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements.ts @@ -0,0 +1,27 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ My i18n block #{{ one }} + Plain text in nested element +
+
+ My i18n block #{{ two | uppercase }} +
+
+ + More bindings in more nested element: {{ nestedInBlockTwo }} + +
+
+
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements_with_i18n_attributes.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements_with_i18n_attributes.js new file mode 100644 index 0000000000..dc28151431 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements_with_i18n_attributes.js @@ -0,0 +1,48 @@ +decls: 9, +vars: 7, +consts: function() { + __i18nMsg__('Span title {$interpolation} and {$interpolation_1}', [['interpolation', String.raw`\uFFFD0\uFFFD`], ['interpolation_1', String.raw`\uFFFD1\uFFFD`]], {}) + __i18nMsg__(' My i18n block #1 with value: {$interpolation} {$startTagSpan} Plain text in nested element (block #1) {$closeTagSpan}',[['interpolation', String.raw`\uFFFD0\uFFFD`], ['startTagSpan', String.raw`\uFFFD#2\uFFFD`], ['closeTagSpan', String.raw`\uFFFD/#2\uFFFD`]], {}) + __i18nMsg__('Span title {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {}) + __i18nMsg__(' My i18n block #2 with value {$interpolation} {$startTagSpan} Plain text in nested element (block #2) {$closeTagSpan}',[ ['interpolation', String.raw`\uFFFD0\uFFFD`], ['startTagSpan', String.raw`\uFFFD#7\uFFFD`], ['closeTagSpan', String.raw`\uFFFD/#7\uFFFD`]], {}) + return [ + $i18n_0$, + [__AttributeMarker.I18n__, "title"], + ["title", $i18n_1$], + $i18n_2$, + ["title", $i18n_3$] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵelementStart(2, "span", 1); + $r3$.ɵɵi18nAttributes(3, 2); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(4, "div"); + $r3$.ɵɵi18nStart(5, 3); + $r3$.ɵɵpipe(6, "uppercase"); + $r3$.ɵɵelementStart(7, "span", 1); + $r3$.ɵɵi18nAttributes(8, 4); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(2); + $r3$.ɵɵi18nExp(ctx.valueB)(ctx.valueC); + $r3$.ɵɵi18nApply(3); + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.valueA); + $r3$.ɵɵi18nApply(1); + $r3$.ɵɵadvance(4); + $r3$.ɵɵi18nExp(ctx.valueE); + $r3$.ɵɵi18nApply(8); + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(6, 5, ctx.valueD)); + $r3$.ɵɵi18nApply(5); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements_with_i18n_attributes.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements_with_i18n_attributes.ts new file mode 100644 index 0000000000..ba16f92f74 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_elements_with_i18n_attributes.ts @@ -0,0 +1,25 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ My i18n block #1 with value: {{ valueA }} + + Plain text in nested element (block #1) + +
+
+ My i18n block #2 with value {{ valueD | uppercase }} + + Plain text in nested element (block #2) + +
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates.js new file mode 100644 index 0000000000..487e38cd64 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates.js @@ -0,0 +1,40 @@ +function MyComponent_div_2_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵelementStart(1, "div"); + $r3$.ɵɵi18nStart(2, 1); + $r3$.ɵɵelement(3, "div"); + $r3$.ɵɵpipe(4, "uppercase"); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + const $ctx_r0$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵadvance(4); + $r3$.ɵɵi18nExp($ctx_r0$.valueA)($r3$.ɵɵpipeBind1(4, 2, $ctx_r0$.valueB)); + $r3$.ɵɵi18nApply(2); + } +} +… +decls: 3, +vars: 1, +consts: function() { + __i18nMsg__(' Some other content {$interpolation} {$startTagDiv} More nested levels with bindings {$interpolation_1} {$closeTagDiv}', [['interpolation', String.raw`\uFFFD0\uFFFD`], ['startTagDiv', String.raw`\uFFFD#3\uFFFD`], ['interpolation_1', String.raw`\uFFFD1\uFFFD`], ['closeTagDiv', String.raw`\uFFFD/#3\uFFFD`]], {}) + return [ + [__AttributeMarker.Template__, "ngIf"], + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵtext(1, " Some content "); + $r3$.ɵɵtemplate(2, MyComponent_div_2_Template, 5, 4, "div", 0); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(2); + $r3$.ɵɵproperty("ngIf", ctx.visible); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates.ts new file mode 100644 index 0000000000..4346db6723 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates.ts @@ -0,0 +1,24 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ Some content +
+
+ Some other content {{ valueA }} +
+ More nested levels with bindings {{ valueB | uppercase }} +
+
+
+
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates_context.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates_context.js new file mode 100644 index 0000000000..f867d9e282 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates_context.js @@ -0,0 +1,77 @@ +function MyComponent_div_2_div_4_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18nStart(0, 0, 2); + $r3$.ɵɵelementStart(1, "div"); + $r3$.ɵɵelement(2, "div"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵi18nEnd(); + } + if (rf & 2) { + const $ctx_r2$ = $r3$.ɵɵnextContext(2); + $r3$.ɵɵadvance(2); + $r3$.ɵɵi18nExp($ctx_r2$.valueC)($ctx_r2$.valueD); + $r3$.ɵɵi18nApply(0); + } +} +function MyComponent_div_2_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18nStart(0, 0, 1); + $r3$.ɵɵelementStart(1, "div"); + $r3$.ɵɵelementStart(2, "div"); + $r3$.ɵɵpipe(3, "uppercase"); + $r3$.ɵɵtemplate(4, MyComponent_div_2_div_4_Template, 3, 2, "div", 1); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵi18nEnd(); + } + if (rf & 2) { + const $ctx_r0$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵadvance(4); + $r3$.ɵɵproperty("ngIf", $ctx_r0$.exists); + $r3$.ɵɵi18nExp($ctx_r0$.valueA)($r3$.ɵɵpipeBind1(3, 3, $ctx_r0$.valueB)); + $r3$.ɵɵi18nApply(0); + } +} +… +function MyComponent_div_3_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18nStart(0, 0, 3); + $r3$.ɵɵelementStart(1, "div"); + $r3$.ɵɵelement(2, "div"); + $r3$.ɵɵpipe(3, "uppercase"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵi18nEnd(); + } + if (rf & 2) { + const $ctx_r1$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵadvance(3); + $r3$.ɵɵi18nExp($ctx_r1$.valueE + $ctx_r1$.valueF)($r3$.ɵɵpipeBind1(3, 2, $ctx_r1$.valueG)); + $r3$.ɵɵi18nApply(0); + } +} +… +decls: 4, +vars: 2, +consts: function() { + __i18nMsgWithPostprocess__(' Some content {$startTagDiv_2} Some other content {$interpolation} {$startTagDiv} More nested levels with bindings {$interpolation_1} {$startTagDiv_1} Content inside sub-template {$interpolation_2} {$startTagDiv} Bottom level element {$interpolation_3} {$closeTagDiv}{$closeTagDiv}{$closeTagDiv}{$closeTagDiv}{$startTagDiv_3} Some other content {$interpolation_4} {$startTagDiv} More nested levels with bindings {$interpolation_5} {$closeTagDiv}{$closeTagDiv}', [['startTagDiv_2', String.raw`\uFFFD*2:1\uFFFD\uFFFD#1:1\uFFFD`], [ 'closeTagDiv', String.raw`[\uFFFD/#2:2\uFFFD|\uFFFD/#1:2\uFFFD\uFFFD/*4:2\uFFFD|\uFFFD/#2:1\uFFFD|\uFFFD/#1:1\uFFFD\uFFFD/*2:1\uFFFD|\uFFFD/#2:3\uFFFD|\uFFFD/#1:3\uFFFD\uFFFD/*3:3\uFFFD]`], ['startTagDiv_3', String.raw`\uFFFD*3:3\uFFFD\uFFFD#1:3\uFFFD`], ['interpolation', String.raw`\uFFFD0:1\uFFFD`], ['startTagDiv', String.raw`[\uFFFD#2:1\uFFFD|\uFFFD#2:2\uFFFD|\uFFFD#2:3\uFFFD]`], ['interpolation_1', String.raw`\uFFFD1:1\uFFFD`], ['startTagDiv_1', String.raw`\uFFFD*4:2\uFFFD\uFFFD#1:2\uFFFD`], ['interpolation_2', String.raw`\uFFFD0:2\uFFFD`], ['interpolation_3', String.raw`\uFFFD1:2\uFFFD`], ['interpolation_4', String.raw`\uFFFD0:3\uFFFD`], ['interpolation_5', String.raw`\uFFFD1:3\uFFFD`]], {}, []) + return [ + $i18n_0$, + [__AttributeMarker.Template__, "ngIf"] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵtemplate(2, MyComponent_div_2_Template, 5, 5, "div", 1); + $r3$.ɵɵtemplate(3, MyComponent_div_3_Template, 4, 4, "div", 1); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(2); + $r3$.ɵɵproperty("ngIf", ctx.visible); + $r3$.ɵɵadvance(1); + $r3$.ɵɵproperty("ngIf", !ctx.visible); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates_context.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates_context.ts new file mode 100644 index 0000000000..2522a66b19 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/nested_templates_context.ts @@ -0,0 +1,34 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ Some content +
+ Some other content {{ valueA }} +
+ More nested levels with bindings {{ valueB | uppercase }} +
+ Content inside sub-template {{ valueC }} +
+ Bottom level element {{ valueD }} +
+
+
+
+
+ Some other content {{ valueE + valueF }} +
+ More nested levels with bindings {{ valueG | uppercase }} +
+
+
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/plain_text_messages.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/plain_text_messages.js new file mode 100644 index 0000000000..5852580456 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/plain_text_messages.js @@ -0,0 +1,29 @@ +consts: function() { + __i18nMsg__('My i18n block #1', [], {}) + __i18nMsg__('My i18n block #2', [], {}) + __i18nMsg__('My i18n block #3', [], {}) + return [ + $i18n_0$, + $i18n_1$, + $i18n_2$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(2, "div"); + $r3$.ɵɵtext(3, "My non-i18n block #1"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(4, "div"); + $r3$.ɵɵi18n(5, 1); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(6, "div"); + $r3$.ɵɵtext(7, "My non-i18n block #2"); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(8, "div"); + $r3$.ɵɵi18n(9, 2); + $r3$.ɵɵelementEnd(); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/plain_text_messages.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/plain_text_messages.ts new file mode 100644 index 0000000000..aaa3272f22 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/plain_text_messages.ts @@ -0,0 +1,18 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
My i18n block #1
+
My non-i18n block #1
+
My i18n block #2
+
My non-i18n block #2
+
My i18n block #3
+ `, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/self_closing.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/self_closing.js new file mode 100644 index 0000000000..e7e404a0a2 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/self_closing.js @@ -0,0 +1,45 @@ +function MyComponent_img_1_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelement(0, "img", 0); + } +} +… +function MyComponent_img_2_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "img", 3); + $r3$.ɵɵi18nAttributes(1, 4); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + const $ctx_r1$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵi18nExp($ctx_r1$.id); + $r3$.ɵɵi18nApply(1); + } +} +… +decls: 3, +vars: 2, +consts: function() { + __i18nMsg__('App logo #{$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {}) + return [ + ["src", "logo.png"], + ["src", "logo.png", __AttributeMarker.Template__, "ngIf"], + ["src", "logo.png", __AttributeMarker.Bindings__, "title", + __AttributeMarker.Template__, "ngIf"], + ["src", "logo.png", __AttributeMarker.I18n__, "title"], + ["title", $i18n_0$] + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelement(0, "img", 0); + $r3$.ɵɵtemplate(1, MyComponent_img_1_Template, 1, 0, "img", 1); + $r3$.ɵɵtemplate(2, MyComponent_img_2_Template, 2, 1, "img", 2); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵproperty("ngIf", ctx.visible); + $r3$.ɵɵadvance(1); + $r3$.ɵɵproperty("ngIf", ctx.visible); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/self_closing.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/self_closing.ts new file mode 100644 index 0000000000..fc454da0d0 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/self_closing.ts @@ -0,0 +1,16 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + + + + `, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/GOLDEN_PARTIAL.js new file mode 100644 index 0000000000..04b038c45b --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/GOLDEN_PARTIAL.js @@ -0,0 +1,525 @@ +/**************************************************************************************************** + * PARTIAL FILE: single_ng-container.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + Some content: {{ valueA | uppercase }} +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + Some content: {{ valueA | uppercase }} +`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: single_ng-container.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: single_ng-template.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + Some content: {{ valueA | uppercase }} +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + Some content: {{ valueA | uppercase }} +`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: single_ng-template.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: child_elements.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ Template content: {{ valueA | uppercase }} + Container content: {{ valueB | uppercase }} +
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ Template content: {{ valueA | uppercase }} + Container content: {{ valueB | uppercase }} +
+`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: child_elements.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: bare_icus.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + {gender, select, male {male} female {female} other {other}} + {age, select, 10 {ten} 20 {twenty} other {other}} +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + {gender, select, male {male} female {female} other {other}} + {age, select, 10 {ten} 20 {twenty} other {other}} +`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: bare_icus.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: nested_templates.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ + Template A: {{ valueA | uppercase }} + + Template B: {{ valueB }} + + Template C: {{ valueC }} + + + +
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ + Template A: {{ valueA | uppercase }} + + Template B: {{ valueB }} + + Template C: {{ valueC }} + + + +
+`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: nested_templates.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: icus.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + {gender, select, male {male} female {female} other {other}} + {age, select, 10 {ten} 20 {twenty} other {other}} +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + {gender, select, male {male} female {female} other {other}} + {age, select, 10 {ten} 20 {twenty} other {other}} +`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: icus.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: self_closing_tags.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + + is my logo #1 + + + is my logo #2 + +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + + is my logo #1 + + + is my logo #2 + +`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: self_closing_tags.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: duplicate_content.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
Test
+
Test
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
Test
+
Test
+`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: duplicate_content.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: self_closing_ng-container.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ Hello there +
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ Hello there +
+`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: self_closing_ng-container.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: ng-container_with_non_text_content.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ Hello there ! +
+`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ Hello there ! +
+`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: ng-container_with_non_text_content.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: structural_directives.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + Content A + Content B +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + Content A + Content B +`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: structural_directives.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/TEST_CASES.json new file mode 100644 index 0000000000..be4468cc5f --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/TEST_CASES.json @@ -0,0 +1,171 @@ +{ + "$schema": "../../test_case_schema.json", + "cases": [ + { + "description": "should handle single translation message using ", + "inputFiles": [ + "single_ng-container.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle single translation message using ", + "inputFiles": [ + "single_ng-template.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should be able to act as child elements inside i18n block", + "inputFiles": [ + "child_elements.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle ICUs outside of translatable sections", + "inputFiles": [ + "bare_icus.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should correctly propagate i18n context through nested templates", + "inputFiles": [ + "nested_templates.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should work with ICUs", + "inputFiles": [ + "icus.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle self-closing tags as content", + "inputFiles": [ + "self_closing_tags.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should not emit duplicate i18n consts for nested s", + "inputFiles": [], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should not emit duplicate i18n consts for elements with the same content", + "inputFiles": [ + "duplicate_content.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should generate a self-closing container instruction for ng-container inside i18n", + "inputFiles": [ + "self_closing_ng-container.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should not generate a self-closing container instruction for ng-container with non-text ", + "inputFiles": [ + "ng-container_with_non_text_content.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should handle structural directives", + "inputFiles": [ + "structural_directives.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + } + ] +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/bare_icus.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/bare_icus.js new file mode 100644 index 0000000000..e619ab5068 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/bare_icus.js @@ -0,0 +1,34 @@ +function MyComponent_ng_template_0_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18n(0, 1); + } + if (rf & 2) { + const $ctx_r0$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵi18nExp($ctx_r0$.gender); + $r3$.ɵɵi18nApply(0); + } +} +… +decls: 3, +vars: 1, +consts: function() { + __i18nIcuMsg__('{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]) + __i18nIcuMsg__('{VAR_SELECT, select, male {male} female {female} other {other}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]) + return [ + $i18n_0$, + $i18n_1$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 1, 1, "ng-template"); + $r3$.ɵɵelementContainerStart(1); + $r3$.ɵɵi18n(2, 0); + $r3$.ɵɵelementContainerEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(2); + $r3$.ɵɵi18nExp(ctx.age); + $r3$.ɵɵi18nApply(2); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/bare_icus.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/bare_icus.ts new file mode 100644 index 0000000000..5ede4fd032 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/bare_icus.ts @@ -0,0 +1,15 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + {gender, select, male {male} female {female} other {other}} + {age, select, 10 {ten} 20 {twenty} other {other}} +`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/child_elements.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/child_elements.js new file mode 100644 index 0000000000..6d76814a54 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/child_elements.js @@ -0,0 +1,37 @@ +function MyComponent_ng_template_2_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18n(0, 0, 1); + $r3$.ɵɵpipe(1, "uppercase"); + } + if (rf & 2) { + const $ctx_r0$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(1, 1, $ctx_r0$.valueA)); + $r3$.ɵɵi18nApply(0); + } +} +… +decls: 5, +vars: 3, +consts: function() { + __i18nMsg__('{$startTagNgTemplate}Template content: {$interpolation}{$closeTagNgTemplate}{$startTagNgContainer}Container content: {$interpolation_1}{$closeTagNgContainer}', [['startTagNgTemplate', String.raw`\uFFFD*2:1\uFFFD`], ['closeTagNgTemplate', String.raw`\uFFFD/*2:1\uFFFD`], ['startTagNgContainer', String.raw`\uFFFD#3\uFFFD`], ['interpolation_1', String.raw`\uFFFD0\uFFFD`], ['closeTagNgContainer', String.raw`\uFFFD/#3\uFFFD`], ['interpolation', String.raw`\uFFFD0:1\uFFFD`]], {}) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵtemplate(2, MyComponent_ng_template_2_Template, 2, 3, "ng-template"); + $r3$.ɵɵelementContainer(3); + $r3$.ɵɵpipe(4, "uppercase"); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(4); + $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(4, 1, ctx.valueB)); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/child_elements.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/child_elements.ts new file mode 100644 index 0000000000..363f9ab63b --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/child_elements.ts @@ -0,0 +1,17 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ Template content: {{ valueA | uppercase }} + Container content: {{ valueB | uppercase }} +
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/duplicate_content.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/duplicate_content.js new file mode 100644 index 0000000000..0ad3c81e57 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/duplicate_content.js @@ -0,0 +1,4 @@ +// NOTE: TODO(FW-635): currently we generate unique consts for each i18n block even though it might contain the same content. This should be optimized by translation statements caching, that can be implemented in the future. +__i18nMsg__('Test', [], {}) +__i18nMsg__('Test', [], {}) + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/duplicate_content.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/duplicate_content.ts new file mode 100644 index 0000000000..9a86dcf737 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/duplicate_content.ts @@ -0,0 +1,15 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
Test
+
Test
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/icus.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/icus.js new file mode 100644 index 0000000000..7207b20e49 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/icus.js @@ -0,0 +1,34 @@ +function MyComponent_ng_template_2_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18n(0, 1); + } + if (rf & 2) { + const $ctx_r0$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵi18nExp($ctx_r0$.age); + $r3$.ɵɵi18nApply(0); + } +} +… +decls: 3, +vars: 1, +consts: function() { + __i18nIcuMsg__('{VAR_SELECT, select, male {male} female {female} other {other}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]) + __i18nIcuMsg__('{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]) + return [ + $i18n_0$, + $i18n_1$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementContainerStart(0); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementContainerEnd(); + $r3$.ɵɵtemplate(2, MyComponent_ng_template_2_Template, 1, 1, "ng-template"); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.gender); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/icus.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/icus.ts new file mode 100644 index 0000000000..3eb4e1de4e --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/icus.ts @@ -0,0 +1,15 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + {gender, select, male {male} female {female} other {other}} + {age, select, 10 {ten} 20 {twenty} other {other}} +`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_ng-container_const.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_ng-container_const.js new file mode 100644 index 0000000000..f3a7c62823 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_ng-container_const.js @@ -0,0 +1 @@ +__i18nMsg__(' Root content {$startTagNgContainer} Nested content {$closeTagNgContainer}', [['startTagNgContainer', String.raw`\uFFFD*1:1\uFFFD\uFFFD#1:1\uFFFD`], ['closeTagNgContainer', String.raw`\uFFFD/#1:1\uFFFD\uFFFD/*1:1\uFFFD`]], {}) diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_ng-container_const.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_ng-container_const.ts new file mode 100644 index 0000000000..bd67fbe80c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_ng-container_const.ts @@ -0,0 +1,19 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + + Root content + + Nested content + + +`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_templates.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_templates.js new file mode 100644 index 0000000000..dd692ebf42 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_templates.js @@ -0,0 +1,56 @@ +function MyComponent_ng_template_2_ng_template_2_ng_template_1_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18n(0, 0, 3); + } + if (rf & 2) { + const $ctx_r2$ = $r3$.ɵɵnextContext(3); + $r3$.ɵɵi18nExp($ctx_r2$.valueC); + $r3$.ɵɵi18nApply(0); + } +} +function MyComponent_ng_template_2_ng_template_2_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18nStart(0, 0, 2); + $r3$.ɵɵtemplate(1, MyComponent_ng_template_2_ng_template_2_ng_template_1_Template, 1, 1, "ng-template"); + $r3$.ɵɵi18nEnd(); + } + if (rf & 2) { + const $ctx_r1$ = $r3$.ɵɵnextContext(2); + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp($ctx_r1$.valueB); + $r3$.ɵɵi18nApply(0); + } +} +… +function MyComponent_ng_template_2_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18nStart(0, 0, 1); + $r3$.ɵɵpipe(1, "uppercase"); + $r3$.ɵɵtemplate(2, MyComponent_ng_template_2_ng_template_2_Template, 2, 1, "ng-template"); + $r3$.ɵɵi18nEnd(); + } + if (rf & 2) { + const $ctx_r0$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵadvance(2); + $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(1, 1, $ctx_r0$.valueA)); + $r3$.ɵɵi18nApply(0); + } +} +… +decls: 3, +vars: 0, +consts: function() { + __i18nMsgWithPostprocess__('{$startTagNgTemplate} Template A: {$interpolation} {$startTagNgTemplate} Template B: {$interpolation_1} {$startTagNgTemplate} Template C: {$interpolation_2} {$closeTagNgTemplate}{$closeTagNgTemplate}{$closeTagNgTemplate}', [['startTagNgTemplate', String.raw`[\uFFFD*2:1\uFFFD|\uFFFD*2:2\uFFFD|\uFFFD*1:3\uFFFD]`], ['closeTagNgTemplate', String.raw`[\uFFFD/*1:3\uFFFD|\uFFFD/*2:2\uFFFD|\uFFFD/*2:1\uFFFD]`], ['interpolation', String.raw`\uFFFD0:1\uFFFD`], ['interpolation_1', String.raw`\uFFFD0:2\uFFFD`], ['interpolation_2', String.raw`\uFFFD0:3\uFFFD`]], {}, []) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵtemplate(2, MyComponent_ng_template_2_Template, 3, 3, "ng-template"); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_templates.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_templates.ts new file mode 100644 index 0000000000..81691f9685 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/nested_templates.ts @@ -0,0 +1,24 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ + Template A: {{ valueA | uppercase }} + + Template B: {{ valueB }} + + Template C: {{ valueC }} + + + +
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/ng-container_with_non_text_content.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/ng-container_with_non_text_content.js new file mode 100644 index 0000000000..de6725ad4d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/ng-container_with_non_text_content.js @@ -0,0 +1,19 @@ +decls: 4, +vars: 0, +consts: function() { + __i18nMsg__(' Hello {$startTagNgContainer}there {$startTagStrong}!{$closeTagStrong}{$closeTagNgContainer}', [['startTagNgContainer', String.raw`\uFFFD#2\uFFFD`], ['startTagStrong', String.raw`\uFFFD#3\uFFFD`], ['closeTagStrong', String.raw`\uFFFD/#3\uFFFD`], ['closeTagNgContainer', String.raw`\uFFFD/#2\uFFFD`]], {}) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵelementContainerStart(2); + $r3$.ɵɵelement(3, "strong"); + $r3$.ɵɵelementContainerEnd(); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/ng-container_with_non_text_content.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/ng-container_with_non_text_content.ts new file mode 100644 index 0000000000..efbc996f0d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/ng-container_with_non_text_content.ts @@ -0,0 +1,16 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ Hello there ! +
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_ng-container.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_ng-container.js new file mode 100644 index 0000000000..af6da8ad8b --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_ng-container.js @@ -0,0 +1,17 @@ +decls: 3, +vars: 0, +consts: function() { + __i18nMsg__(' Hello {$startTagNgContainer}there{$closeTagNgContainer}', [['startTagNgContainer', String.raw`\uFFFD#2\uFFFD`], ['closeTagNgContainer', String.raw`\uFFFD/#2\uFFFD`]], {}) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵelementContainer(2); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_ng-container.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_ng-container.ts new file mode 100644 index 0000000000..c7f684a4e4 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_ng-container.ts @@ -0,0 +1,16 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ Hello there +
+`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_tags.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_tags.js new file mode 100644 index 0000000000..4e71499265 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_tags.js @@ -0,0 +1,27 @@ +function MyComponent_ng_template_3_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18nStart(0, 2); + $r3$.ɵɵelement(1, "img", 1); + $r3$.ɵɵi18nEnd(); + } +} +… +consts: function() { + __i18nMsg__('{$tagImg} is my logo #1 ', [['tagImg', String.raw`\uFFFD#2\uFFFD\uFFFD/#2\uFFFD`]], {}) + __i18nMsg__('{$tagImg} is my logo #2 ', [['tagImg', String.raw`\uFFFD#1\uFFFD\uFFFD/#1\uFFFD`]], {}) + return [ + $i18n_0$, + ["src", "logo.png", "title", "Logo"], + $i18n_1$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementContainerStart(0); + $r3$.ɵɵi18nStart(1, 0); + $r3$.ɵɵelement(2, "img", 1); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementContainerEnd(); + $r3$.ɵɵtemplate(3, MyComponent_ng_template_3_Template, 2, 0, "ng-template"); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_tags.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_tags.ts new file mode 100644 index 0000000000..d956652490 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/self_closing_tags.ts @@ -0,0 +1,19 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + + is my logo #1 + + + is my logo #2 + +`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-container.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-container.js new file mode 100644 index 0000000000..f9c6f7f924 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-container.js @@ -0,0 +1,21 @@ +decls: 3, +vars: 3, +consts: function() { + __i18nMsg__('Some content: {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {}) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementContainerStart(0); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵpipe(2, "uppercase"); + $r3$.ɵɵelementContainerEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(2); + $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(2, 1, ctx.valueA)); + $r3$.ɵɵi18nApply(1); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-container.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-container.ts new file mode 100644 index 0000000000..21575e742d --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-container.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + Some content: {{ valueA | uppercase }} +`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-template.js new file mode 100644 index 0000000000..e12d3eb8ce --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-template.js @@ -0,0 +1,22 @@ +function MyComponent_ng_template_0_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18n(0, 0); + $r3$.ɵɵpipe(1, "uppercase"); + } + if (rf & 2) { + const $ctx_r0$ = $r3$.ɵɵnextContext(); + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(1, 1, $ctx_r0$.valueA)); + $r3$.ɵɵi18nApply(0); + } +} +… +decls: 1, vars: 0, consts: function() { + __i18nMsg__('Some content: {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], {}) + return [$i18n_0$]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 2, 3, "ng-template"); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-template.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-template.ts new file mode 100644 index 0000000000..dcb4f24988 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/single_ng-template.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + Some content: {{ valueA | uppercase }} +`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/structural_directives.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/structural_directives.js new file mode 100644 index 0000000000..30a01d43ab --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/structural_directives.js @@ -0,0 +1,42 @@ +// NOTE: applying structural directives to is typically user error, but it is technically allowed, so we need to support it. +function MyComponent_0_ng_template_0_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18n(0, 1); + } +} +function MyComponent_0_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtemplate(0, MyComponent_0_ng_template_0_Template, 1, 0, "ng-template"); + } +} +… +function MyComponent_ng_container_1_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementContainerStart(0); + $r3$.ɵɵi18n(1, 2); + $r3$.ɵɵelementContainerEnd(); + } +} +… +decls: 2, +vars: 2, +consts: function() { + __i18nMsg__('Content A', [], {}) + __i18nMsg__('Content B', [], {}) + return [ + [__AttributeMarker.Template__, "ngIf"], + $i18n_0$, + $i18n_1$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtemplate(0, MyComponent_0_Template, 1, 0, undefined, 0); + $r3$.ɵɵtemplate(1, MyComponent_ng_container_1_Template, 2, 0, "ng-container", 0); + } + if (rf & 2) { + $r3$.ɵɵproperty("ngIf", ctx.someFlag); + $r3$.ɵɵadvance(1); + $r3$.ɵɵproperty("ngIf", ctx.someFlag); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/structural_directives.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/structural_directives.ts new file mode 100644 index 0000000000..2a49e48c67 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/ng-container_ng-template/structural_directives.ts @@ -0,0 +1,15 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + Content A + Content B +`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/GOLDEN_PARTIAL.js new file mode 100644 index 0000000000..d5c39853fb --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/GOLDEN_PARTIAL.js @@ -0,0 +1,176 @@ +/**************************************************************************************************** + * PARTIAL FILE: text_only_content.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
My i18n block #1
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
My i18n block #1
+ `, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: text_only_content.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: icu_only.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
{age, select, 10 {ten} 20 {twenty} other {other}}
+ `, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
{age, select, 10 {ten} 20 {twenty} other {other}}
+ `, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: icu_only.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: ng-container_ng-template.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + My i18n block #1 + My i18n block #2 +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + My i18n block #1 + My i18n block #2 +`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: ng-container_ng-template.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + +/**************************************************************************************************** + * PARTIAL FILE: styles.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` + Text #1 + Text #2 +`, isInline: true } }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` + Text #1 + Text #2 +`, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: styles.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/TEST_CASES.json new file mode 100644 index 0000000000..e819f4790c --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/TEST_CASES.json @@ -0,0 +1,61 @@ +{ + "$schema": "../../test_case_schema.json", + "cases": [ + { + "description": "should be generated with text-only content", + "inputFiles": [ + "text_only_content.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should be generated for ICU-only i18n blocks", + "inputFiles": [ + "icu_only.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should be generated within and blocks", + "inputFiles": [ + "ng-container_ng-template.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + }, + { + "description": "should not be generated in case we have styling instructions", + "inputFiles": [ + "styles.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + } + ] +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/icu_only.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/icu_only.js new file mode 100644 index 0000000000..cf8c5c9b73 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/icu_only.js @@ -0,0 +1,20 @@ +decls: 2, +vars: 1, +consts: function() { + __i18nIcuMsg__('{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}', [['VAR_SELECT', String.raw`\uFFFD0\uFFFD`]]) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + } + if (rf & 2) { + $r3$.ɵɵadvance(1); + $r3$.ɵɵi18nExp(ctx.age); + $r3$.ɵɵi18nApply(1); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/icu_only.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/icu_only.ts new file mode 100644 index 0000000000..d536823bcf --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/icu_only.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
{age, select, 10 {ten} 20 {twenty} other {other}}
+ `, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/ng-container_ng-template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/ng-container_ng-template.js new file mode 100644 index 0000000000..6fbaa56d5a --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/ng-container_ng-template.js @@ -0,0 +1,22 @@ +function MyComponent_ng_template_0_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵi18n(0, 1); + } +} +… +consts: function() { + __i18nMsg__('My i18n block #2', [], {}) + __i18nMsg__('My i18n block #1', [], {}) + return [ + $i18n_0$, + $i18n_1$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtemplate(0, MyComponent_ng_template_0_Template, 1, 0, "ng-template"); + $r3$.ɵɵelementContainerStart(1); + $r3$.ɵɵi18n(2, 0); + $r3$.ɵɵelementContainerEnd(); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/ng-container_ng-template.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/ng-container_ng-template.ts new file mode 100644 index 0000000000..d9bec7fa83 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/ng-container_ng-template.ts @@ -0,0 +1,15 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + My i18n block #1 + My i18n block #2 +`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/styles.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/styles.js new file mode 100644 index 0000000000..26ad3ad31a --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/styles.js @@ -0,0 +1,22 @@ +decls: 4, +vars: 0, +consts: function() { + __i18nMsg__('Text #1', [] , {}) + __i18nMsg__('Text #2', [] , {}) + return [ + [__AttributeMarker.Classes__, "myClass"], + $i18n_0$, + [__AttributeMarker.Styles__, "padding", "10px"], + $i18n_1$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "span", 0); + $r3$.ɵɵi18n(1, 1); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵelementStart(2, "span", 2); + $r3$.ɵɵi18n(3, 3); + $r3$.ɵɵelementEnd(); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/styles.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/styles.ts new file mode 100644 index 0000000000..eb0502df5b --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/styles.ts @@ -0,0 +1,15 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` + Text #1 + Text #2 +`, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/text_only_content.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/text_only_content.js new file mode 100644 index 0000000000..5128c429ed --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/text_only_content.js @@ -0,0 +1,13 @@ +consts: function() { + __i18nMsg__('My i18n block #1', [] , {}) + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵelementStart(0, "div"); + $r3$.ɵɵi18n(1, 0); + $r3$.ɵɵelementEnd(); + } +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/text_only_content.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/text_only_content.ts new file mode 100644 index 0000000000..277139ce04 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/self-closing_i18n_instructions/text_only_content.ts @@ -0,0 +1,14 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
My i18n block #1
+ `, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/GOLDEN_PARTIAL.js new file mode 100644 index 0000000000..02ed4e72c5 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/GOLDEN_PARTIAL.js @@ -0,0 +1,50 @@ +/**************************************************************************************************** + * PARTIAL FILE: preserve_inner_content.js + ****************************************************************************************************/ +import { Component, NgModule } from '@angular/core'; +import * as i0 from "@angular/core"; +export class MyComponent { +} +MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ` +
+ Some text + Text inside span +
+`, isInline: true }, preserveWhitespaces: true }); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{ + type: Component, + args: [{ + selector: 'my-component', + template: ` +
+ Some text + Text inside span +
+`, + preserveWhitespaces: true, + }] + }], null, null); })(); +export class MyModule { +} +MyModule.ɵmod = i0.ɵɵdefineNgModule({ type: MyModule }); +MyModule.ɵinj = i0.ɵɵdefineInjector({ factory: function MyModule_Factory(t) { return new (t || MyModule)(); } }); +(function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(MyModule, { declarations: [MyComponent] }); })(); +/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyModule, [{ + type: NgModule, + args: [{ declarations: [MyComponent] }] + }], null, null); })(); + +/**************************************************************************************************** + * PARTIAL FILE: preserve_inner_content.d.ts + ****************************************************************************************************/ +import * as i0 from "@angular/core"; +export declare class MyComponent { + static ɵfac: i0.ɵɵFactoryDef; + static ɵcmp: i0.ɵɵComponentDefWithMeta; +} +export declare class MyModule { + static ɵmod: i0.ɵɵNgModuleDefWithMeta; + static ɵinj: i0.ɵɵInjectorDef; +} + diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/TEST_CASES.json b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/TEST_CASES.json new file mode 100644 index 0000000000..86bd244c34 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/TEST_CASES.json @@ -0,0 +1,19 @@ +{ + "$schema": "../../test_case_schema.json", + "cases": [ + { + "description": "should keep inner content of i18n block as is", + "inputFiles": [ + "preserve_inner_content.ts" + ], + "expectations": [ + { + "extraChecks": [ + "verifyPlaceholdersIntegrity", + "verifyUniqueConsts" + ] + } + ] + } + ] +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/preserve_inner_content.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/preserve_inner_content.js new file mode 100644 index 0000000000..32f8225e82 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/preserve_inner_content.js @@ -0,0 +1,31 @@ +consts: function() { + // NOTE: Keeping raw content (avoiding `i18nMsg`) to illustrate message layout in case of whitespace preserving mode. + let $I18N_0$; + if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + const $MSG_EXTERNAL_963542717423364282$$APP_SPEC_TS_0$ = goog.getMsg("\n Some text\n {$startTagSpan}Text inside span{$closeTagSpan}\n ", { + "startTagSpan": "\uFFFD#3\uFFFD", + "closeTagSpan": "\uFFFD/#3\uFFFD" + }); + $I18N_0$ = $MSG_EXTERNAL_963542717423364282$$APP_SPEC_TS_0$; + } + else { + $I18N_0$ = $localize ` + Some text + ${"\uFFFD#3\uFFFD"}:START_TAG_SPAN:Text inside span${"\uFFFD/#3\uFFFD"}:CLOSE_TAG_SPAN: + `; + } + return [ + $i18n_0$ + ]; +}, +template: function MyComponent_Template(rf, ctx) { + if (rf & 1) { + $r3$.ɵɵtext(0, "\n "); + $r3$.ɵɵelementStart(1, "div"); + $r3$.ɵɵi18nStart(2, 0); + $r3$.ɵɵelement(3, "span"); + $r3$.ɵɵi18nEnd(); + $r3$.ɵɵelementEnd(); + $r3$.ɵɵtext(4, "\n"); + } +} diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/preserve_inner_content.ts b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/preserve_inner_content.ts new file mode 100644 index 0000000000..89028fd734 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/whitespace_preserving_mode/preserve_inner_content.ts @@ -0,0 +1,18 @@ +import {Component, NgModule} from '@angular/core'; + +@Component({ + selector: 'my-component', + template: ` +
+ Some text + Text inside span +
+`, + preserveWhitespaces: true, +}) +export class MyComponent { +} + +@NgModule({declarations: [MyComponent]}) +export class MyModule { +} \ No newline at end of file diff --git a/packages/compiler-cli/test/compliance/test_cases/test_case_schema.json b/packages/compiler-cli/test/compliance/test_cases/test_case_schema.json index 22cfe7e179..925811f117 100644 --- a/packages/compiler-cli/test/compliance/test_cases/test_case_schema.json +++ b/packages/compiler-cli/test/compliance/test_cases/test_case_schema.json @@ -20,6 +20,11 @@ "description": "This will be used as the message in an `it()` clause.", "type": "string" }, + "excludeFromPartialTests": { + "title": "If set to true then do not check this test-case expectations in partial tests.", + "type": "boolean", + "default": false + }, "inputFiles": { "title": "A collection of source files to compile", "type": "array", @@ -44,46 +49,91 @@ "title": "A collection of expected-generated file path pairs", "type": "array", "items": { - "title": "A pair of expected-generated file paths to be compared", - "type": "object", - "required": [ - "expected", - "generated" - ], - "properties": { - "expected": { - "title": "A path (relative to the test case) where an file containing expected output can be found", - "type": "string" + "anyOf": [ + { + "title": "A pair of expected-generated file paths to be compared", + "type": "object", + "required": [ + "expected", + "generated" + ], + "properties": { + "expected": { + "title": "A path (relative to the test case) where a file containing expected output can be found", + "type": "string" + }, + "generated": { + "title": "A path (relative to the build output directory) where the compiled file can be found.", + "type": "string" + } + } }, - "generated": { - "title": "A path (relative to the build output directory) where the compiled file can be found.", - "type": "string" + { + "type": "string", + "title": "A path (relative to the test case) where an file containing expected output can be found. (The generated path is inferred from this.)" } + ] + }, + "expectedErrors": { + "title": "A collection of expected error messages for this test-case.", + "type": "array", + "items": { + "title": "An array of strings that should appear in the error message.", + "type": "object", + "properties": { + "message": { + "title": "A regular expression that should match the error message", + "type": "string" + }, + "location": { + "title": "An optional regular expression that should match the location of the error", + "type": "string" + } + }, + "required": [ + "message" + ] + } + }, + "extraChecks": { + "title": "Additional checks to run against the generated code.", + "type": "array", + "items": { + "anyOf": [ + { + "title": "The name of a function to run as an additional check on the source.", + "type": "string" + }, + { + "title": "An array whose first element is the name of a function to run as an additional check, and subsequent elements are args for this function", + "type": "array" + } + ] } } } } + }, + "compilerOptions": { + "title": "Additional options to pass to the TypeScript compiler", + "type": "object" + }, + "angularCompilerOptions": { + "title": "Additional options to pass to the Angular compiler", + "type": "object" + }, + "focusTest": { + "title": "Set to true to focus on this test - equivalent to jasmine's `fit()` function", + "type": "boolean" + }, + "excludeTest": { + "title": "Set to true to exclude this test - equivalent to jasmine's `xit()` function", + "type": "boolean" } - }, - "compilerOptions": { - "title": "Additional options to pass to the TypeScript compiler", - "type": "object" - }, - "angularCompilerOptions": { - "title": "Additional options to pass to the Angular compiler", - "type": "object" - }, - "focusTest": { - "title": "Set to true to focus on this test - equivalent to jasmine's `fit()` function", - "type": "boolean" - }, - "excludeTest": { - "title": "Set to true to exclude this test - equivalent to jasmine's `xit()` function", - "type": "boolean" } - } - }, - "minItems": 1 + }, + "minItems": 1 + } } } } diff --git a/packages/compiler-cli/test/compliance/test_helpers/check_errors.ts b/packages/compiler-cli/test/compliance/test_helpers/check_errors.ts new file mode 100644 index 0000000000..169c038db9 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_helpers/check_errors.ts @@ -0,0 +1,24 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {inspect} from 'util'; +import {ExpectedError} from './get_compliance_tests'; + +export function checkErrors( + testPath: string, failureMessage: string, expectedErrors: ExpectedError[], + actualErrors: string[]): void { + for (const expectedError of expectedErrors) { + if (!actualErrors.some( + actualError => expectedError.message.test(actualError) && + expectedError.location.test(actualError))) { + throw new Error( + `When checking expected errors for test case at "${testPath}"\n` + failureMessage + '\n' + + `Expected errors: ${inspect(expectedErrors)}\n` + + `Actual errors: ${inspect(actualErrors)}.`); + } + } +} diff --git a/packages/compiler-cli/test/compliance/test_helpers/check_expectations.ts b/packages/compiler-cli/test/compliance/test_helpers/check_expectations.ts index c80d194702..a39d1d9976 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/check_expectations.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/check_expectations.ts @@ -6,9 +6,18 @@ * found in the LICENSE file at https://angular.io/license */ import {FileSystem} from '../../../src/ngtsc/file_system'; -import {getBuildOutputDirectory} from './compile_test'; + +import {getBuildOutputDirectory, getRootDirectory} from './compile_test'; import {expectEmit} from './expect_emit'; -import {ExpectedFile} from './get_compliance_tests'; +import {replaceMacros} from './expected_file_macros'; +import {ExpectedFile, ExtraCheck} from './get_compliance_tests'; +import {verifyPlaceholdersIntegrity, verifyUniqueConsts} from './i18n_checks'; + +type ExtraCheckFunction = (generated: string, ...extraArgs: any[]) => boolean; +const EXTRA_CHECK_FUNCTIONS: Record = { + verifyPlaceholdersIntegrity, + verifyUniqueConsts, +}; /** * Check that each of the generated files matches the expected files. @@ -19,10 +28,11 @@ import {ExpectedFile} from './get_compliance_tests'; * @param expectedFiles The list of expected-generated pairs to compare. */ export function checkExpectations( - fs: FileSystem, testPath: string, failureMessage: string, expectedFiles: ExpectedFile[]): void { + fs: FileSystem, testPath: string, failureMessage: string, expectedFiles: ExpectedFile[], + extraChecks: ExtraCheck[]): void { const builtDirectory = getBuildOutputDirectory(fs); for (const expectedFile of expectedFiles) { - const expectedPath = fs.resolve(expectedFile.expected); + const expectedPath = fs.resolve(getRootDirectory(fs), expectedFile.expected); if (!fs.exists(expectedPath)) { throw new Error(`The expected file at ${ expectedPath} does not exist. Please check the TEST_CASES.json file for this test case.`); @@ -30,16 +40,45 @@ export function checkExpectations( const generatedPath = fs.resolve(builtDirectory, expectedFile.generated); if (!fs.exists(generatedPath)) { - throw new Error(`The generated file at ${ - generatedPath} does not exist. Perhaps there is no matching input source file in the TEST_CASES.json file for this test case.`); + const error = new Error( + `The generated file at ${generatedPath} does not exist.\n` + + 'Perhaps there is no matching input source file in the TEST_CASES.json file for this test case.\n' + + 'Or maybe you need to regenerate the GOLDEN_PARTIAL.js file by running:\n\n' + + ` bazel run //packages/compiler-cli/test/compliance/test_cases:${ + testPath}.golden.update`); + // Clear the stack so that we get a nice error message + error.stack = ''; + throw error; } - - const expected = fs.readFile(expectedPath); + const expected = replaceMacros(fs.readFile(expectedPath)); const generated = fs.readFile(generatedPath); expectEmit( generated, expected, `When checking against expected file "${testPath}/${expectedFile.expected}"\n` + failureMessage); + + runExtraChecks(testPath, generated, extraChecks); + } +} + +function runExtraChecks( + testPath: string, generated: string, extraChecks: (string|[string, ...any])[]): void { + for (const check of extraChecks) { + let fnName: string; + let args: any[]; + if (Array.isArray(check)) { + [fnName, ...args] = check; + } else { + fnName = check; + args = []; + } + const fn = EXTRA_CHECK_FUNCTIONS[fnName]; + if (fn === undefined) { + throw new Error( + `Unknown extra-check function: "${fnName}" in ${testPath}.\n` + + `Possible choices are: ${Object.keys(EXTRA_CHECK_FUNCTIONS).map(f => `\n - ${f}`)}.`); + } + expect(fn(generated, ...args)).toBe(true); } } diff --git a/packages/compiler-cli/test/compliance/test_helpers/compile_test.ts b/packages/compiler-cli/test/compliance/test_helpers/compile_test.ts index 297dd3181c..2716dbcf99 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/compile_test.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/compile_test.ts @@ -10,7 +10,7 @@ import * as ts from 'typescript'; import {AbsoluteFsPath, FileSystem, NgtscCompilerHost} from '../../../src/ngtsc/file_system'; import {initMockFileSystem} from '../../../src/ngtsc/file_system/testing'; import {loadStandardTestFiles, loadTestDirectory} from '../../../src/ngtsc/testing'; -import {performCompilation} from '../../../src/perform_compile'; +import {Diagnostics, performCompilation} from '../../../src/perform_compile'; import {CompilerOptions} from '../../../src/transformers/api'; import {ConfigOptions} from './get_compliance_tests'; @@ -26,9 +26,16 @@ export function initMockTestFileSystem(realTestPath: AbsoluteFsPath): FileSystem const testFiles = loadStandardTestFiles(); fs.init(testFiles); loadTestDirectory(fs, realTestPath, getRootDirectory(fs)); + monkeyPatchReadFile(fs); return fs; } +/** The result of compiling a test-case. */ +export interface CompileResult { + emittedFiles: AbsoluteFsPath[]; + errors: string[]; +} + /** * Compile the input source `files` stored in `fs`, writing the generated files to `fs`. * @@ -40,17 +47,16 @@ export function initMockTestFileSystem(realTestPath: AbsoluteFsPath): FileSystem */ export function compileTest( fs: FileSystem, files: string[], compilerOptions: ConfigOptions|undefined, - angularCompilerOptions: ConfigOptions|undefined): AbsoluteFsPath[] { + angularCompilerOptions: ConfigOptions|undefined): CompileResult { const rootDir = getRootDirectory(fs); const outDir = getBuildOutputDirectory(fs); const options = getOptions(rootDir, outDir, compilerOptions, angularCompilerOptions); const rootNames = files.map(f => fs.resolve(f)); const host = new NgtscCompilerHost(fs, options); const {diagnostics, emitResult} = performCompilation({rootNames, host, options}); - if (diagnostics.length > 0) { - console.warn(diagnostics.map(d => d.messageText).join('\n')); - } - return emitResult!.emittedFiles!.map(p => fs.resolve(rootDir, p)); + const emittedFiles = emitResult ? emitResult.emittedFiles!.map(p => fs.resolve(rootDir, p)) : []; + const errors = parseDiagnostics(diagnostics); + return {errors, emittedFiles}; } /** @@ -86,6 +92,12 @@ export function getBuildOutputDirectory(fs: FileSystem): AbsoluteFsPath { function getOptions( rootDir: AbsoluteFsPath, outDir: AbsoluteFsPath, compilerOptions: ConfigOptions|undefined, angularCompilerOptions: ConfigOptions|undefined): CompilerOptions { + const convertedCompilerOptions = ts.convertCompilerOptionsFromJson(compilerOptions, rootDir); + if (convertedCompilerOptions.errors.length > 0) { + throw new Error( + 'Invalid compilerOptions in test-case::\n' + + convertedCompilerOptions.errors.map(d => d.messageText).join('\n')); + } return { emitDecoratorMetadata: true, experimentalDecorators: true, @@ -104,9 +116,43 @@ function getOptions( module: ts.ModuleKind.ES2015, moduleResolution: ts.ModuleResolutionKind.NodeJs, typeRoots: ['node_modules/@types'], - ...ts.convertCompilerOptionsFromJson({compilerOptions}, rootDir).options, + ...convertedCompilerOptions.options, enableIvy: true, ivyTemplateTypeCheck: false, + enableI18nLegacyMessageIdFormat: false, ...angularCompilerOptions, }; } + +/** + * Replace escaped line-ending markers (\r\n) with real line-ending characters. + * + * This allows us to simulate, more reliably, files that have `\r\n` line-endings. + * (See `line_ending_normalization` test cases.) + */ +function monkeyPatchReadFile(fs: FileSystem): void { + const originalReadFile = fs.readFile; + fs.readFile = (path: AbsoluteFsPath): string => { + const file = originalReadFile.call(fs, path); + return file.replace(/\\r\\n\r?\n/g, '\r\n'); + }; +} + +/** + * Parse the `diagnostics` to extract an error message string. + * + * The error message includes the location if available. + * + * @param diagnostics The diagnostics to parse. + */ +function parseDiagnostics(diagnostics: Diagnostics): string[] { + return diagnostics.map(diagnostic => { + const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); + if ('file' in diagnostic && diagnostic.file !== undefined && diagnostic.start !== undefined) { + const {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); + return `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`; + } else { + return message; + } + }); +} diff --git a/packages/compiler-cli/test/compliance/test_helpers/expect_emit.ts b/packages/compiler-cli/test/compliance/test_helpers/expect_emit.ts index 1c41c4ecda..9340631949 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/expect_emit.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/expect_emit.ts @@ -8,17 +8,21 @@ import {escapeRegExp} from '@angular/compiler/src/util'; const IDENTIFIER = /[A-Za-z_$ɵ][A-Za-z0-9_$]*/; +const COMMENT_START = /\/\*/; +const COMMENT_END = /\*\//; const OPERATOR = - /!|\?|%|\*|\/|\^|&&?|\|\|?|\(|\)|\{|\}|\[|\]|:|;|<=?|>=?|={1,3}|!==?|=>|\+\+?|--?|@|,|\.|\.\.\.|`|\\'/; + /!|\?|%|\*|\/|\^|&&?|\|\|?|\(|\)|\{|\}|\[|\]|:|;|<=?|>=?|={1,3}|!==?|=>|\+\+?|--?|@|,|\.|\.\.\.|\\`|\\'/; const STRING = /'(\\'|[^'])*'|"(\\"|[^"])*"/; -const BACKTICK_STRING = /\\`(([\s\S]*?)(\$\{[^}]*?\})?)*?[^\\]\\`/; +const INLINE_BACKTICK_STRING = /`(([\s\S]*?)(\$\{[^}]*?\})?)*?[^\\]`/; +const SINGLE_BACKTICK_STRING = new RegExp('^' + INLINE_BACKTICK_STRING.source); const BACKTICK_INTERPOLATION = /(\$\{[^}]*\})/; const NUMBER = /\d+/; const ELLIPSIS = '…'; const TOKEN = new RegExp( - `\\s*((${IDENTIFIER.source})|(${BACKTICK_STRING.source})|(${OPERATOR.source})|(${ - STRING.source})|${NUMBER.source}|${ELLIPSIS})\\s*`, + `\\s*((${COMMENT_START.source})|(${COMMENT_END.source})|(${IDENTIFIER.source})|(${ + INLINE_BACKTICK_STRING.source})|(${OPERATOR.source})|(${STRING.source})|${NUMBER.source}|${ + ELLIPSIS})\\s*`, 'y'); type Piece = string|RegExp; @@ -34,20 +38,28 @@ function tokenize(text: string): Piece[] { let match: RegExpMatchArray|null; let tokenizedTextEnd = 0; + let inComment = false; const pieces: Piece[] = []; - while ((match = TOKEN.exec(text)) !== null) { - const [fullMatch, token] = match; + const [, token] = match; if (token === 'IDENT') { pieces.push(IDENTIFIER); } else if (token === ELLIPSIS) { pieces.push(SKIP); - } else if (match = BACKTICK_STRING.exec(token)) { - pieces.push(...tokenizeBackTickString(token)); + } else if (match = SINGLE_BACKTICK_STRING.exec(token)) { + if (inComment) { + // We are in a comment block so just treat a backtick as a normal token. + // Store the token and reset the matcher. + pieces.push('`'); + TOKEN.lastIndex = tokenizedTextEnd + 1; + } else { + pieces.push(...tokenizeBackTickString(token)); + } } else { + updateCommentState(token); pieces.push(token); } - tokenizedTextEnd += fullMatch.length; + tokenizedTextEnd = TOKEN.lastIndex; } if (pieces.length === 0 || tokenizedTextEnd < text.length) { @@ -63,6 +75,14 @@ function tokenize(text: string): Piece[] { TOKEN.lastIndex = lastIndex; return pieces; + + function updateCommentState(token: string) { + if (token === '/*') { + inComment = true; + } else if (token === '*/') { + inComment = false; + } + } } /** @@ -72,10 +92,7 @@ function tokenize(text: string): Piece[] { */ function tokenizeBackTickString(str: string): Piece[] { const pieces: Piece[] = ['`']; - // Unescape backticks that are inside the backtick string - // (we had to double escape them in the test string so they didn't look like string markers) - str = str.replace(/\\\\\\`/g, '\\`'); - const backTickPieces = str.slice(2, -2).split(BACKTICK_INTERPOLATION); + const backTickPieces = str.slice(1, -1).split(BACKTICK_INTERPOLATION); backTickPieces.forEach((backTickPiece) => { if (BACKTICK_INTERPOLATION.test(backTickPiece)) { // An interpolation so tokenize this expression @@ -89,6 +106,11 @@ function tokenizeBackTickString(str: string): Piece[] { return pieces; } +const RESET = '\x1b[0m'; +const BLUE = '\x1b[36m'; +const RED = '\x1b[31m'; +const GREEN = '\x1b[32m'; + export function expectEmit( source: string, expected: string, description: string, assertIdentifiers?: {[name: string]: RegExp}) { @@ -117,14 +139,17 @@ export function expectEmit( `...${fullContext.substr(-contextLength)}` : fullContext; throw new Error( - `${description}:\nFailed to find "${expectedPiece}" after "${context}" in:\n'${ - source.substr( - 0, last)}[<---HERE expected "${expectedPiece}"]${source.substr(last)}'`); - return; + `${RED}${description}:\n${RESET}${BLUE}Failed to find${RESET} "${expectedPiece}"\n` + + `${BLUE}After ${RESET}"${context}"\n` + + `${BLUE}In generated file:${RESET}\n\n` + + `${source.substr(0, last)}` + + `${RED}[[[ <<<<---HERE expected "${GREEN}${expectedPiece}${RED}" ]]]${RESET}` + + `${source.substr(last)}`); } else { last = (m.index || 0) + m[0].length; } } + throw new Error( `Test helper failure: Expected expression failed but the reporting logic could not find where it failed in: ${ source}`); diff --git a/packages/compiler-cli/test/compliance/test_helpers/expected_file_macros.ts b/packages/compiler-cli/test/compliance/test_helpers/expected_file_macros.ts new file mode 100644 index 0000000000..73a7c17ed4 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_helpers/expected_file_macros.ts @@ -0,0 +1,107 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {AttributeMarker} from '@angular/compiler/src/core'; +import {i18nIcuMsg, i18nMsg, i18nMsgWithPostprocess, Placeholder} from './i18n_helpers'; + +const EXPECTED_FILE_MACROS: [RegExp, (...args: string[]) => string][] = [ + [ + // E.g. `__i18nMsg__('message string', [ ['placeholder', 'pair'] ], { meta: 'properties'})` + macroFn(/__i18nMsg__/, stringParam(), arrayParam(), objectParam()), + (_match, message, placeholders, meta) => + i18nMsg(message, parsePlaceholders(placeholders), parseMetaProperties(meta)), + ], + [ + // E.g. `__i18nMsgWithPostprocess__('message', [ ['placeholder', 'pair'] ], { meta: 'props'})` + macroFn(/__i18nMsgWithPostprocess__/, stringParam(), arrayParam(), objectParam(), arrayParam()), + (_match, message, placeholders, meta, postProcessPlaceholders) => i18nMsgWithPostprocess( + message, parsePlaceholders(placeholders), parseMetaProperties(meta), + parsePlaceholders(postProcessPlaceholders)), + ], + [ + // E.g. `__i18nIcuMsg__('message string', [ ['placeholder', 'pair'] ])` + macroFn(/__i18nIcuMsg__/, stringParam(), arrayParam()), + (_match, message, placeholders) => i18nIcuMsg(message, parsePlaceholders(placeholders)), + ], + [ + // E.g. `__AttributeMarker.Bindings__` + /__AttributeMarker\.([^_]+)__/g, + (_match, member) => getAttributeMarker(member), + ], +]; + +/** + * Replace any known macros in the expected content with the result of evaluating the macro. + * + * @param expectedContent The content to process. + */ +export function replaceMacros(expectedContent: string): string { + for (const [regex, replacer] of EXPECTED_FILE_MACROS) { + expectedContent = expectedContent.replace(regex, replacer); + } + return expectedContent; +} + +function parsePlaceholders(str: string): Placeholder[] { + const placeholders = eval(`(${str})`); + if (!Array.isArray(placeholders) || + !placeholders.every( + p => Array.isArray(p) && p.length === 2 && typeof p[0] === 'string' && + typeof p[1] === 'string')) { + throw new Error('Expected an array of Placeholder arrays (`[string, string]`) but got ' + str); + } + return placeholders; +} + +function parseMetaProperties(str: string): Record { + const obj = eval(`(${str})`); + if (typeof obj !== 'object') { + throw new Error(`Expected an object of properties but got:\n\n${str}.`); + } + for (const key in obj) { + if (typeof obj[key] !== 'string') { + throw new Error(`Expected an object whose values are strings, but property ${key} has type ${ + typeof obj[key]}, when parsing:\n\n${str}`); + } + } + return obj; +} + +const AttributeMarkerMap: Record = { + NamespaceURI: AttributeMarker.NamespaceURI, + Classes: AttributeMarker.Classes, + Styles: AttributeMarker.Styles, + Bindings: AttributeMarker.Bindings, + Template: AttributeMarker.Template, + ProjectAs: AttributeMarker.ProjectAs, + I18n: AttributeMarker.I18n, +}; + +function getAttributeMarker(member: string): string { + const marker = AttributeMarkerMap[member]; + if (typeof marker !== 'number') { + throw new Error('Unknown AttributeMarker: ' + member); + } + return `${marker}`; +} + +function stringParam() { + return /'([^']*?[^\\])'/; +} +function arrayParam() { + return /(\[.*?\])/; +} +function objectParam() { + return /(\{[^}]*\})/; +} + +function macroFn(fnName: RegExp, ...args: RegExp[]): RegExp { + const ws = /[\s\r\n]*/.source; + return new RegExp( + ws + fnName.source + '\\(' + args.map(r => `${ws}${r.source}${ws}`).join(',') + '\\)' + ws, + 'g'); +} diff --git a/packages/compiler-cli/test/compliance/test_helpers/get_compliance_tests.ts b/packages/compiler-cli/test/compliance/test_helpers/get_compliance_tests.ts index 4e4710727e..065415311f 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/get_compliance_tests.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/get_compliance_tests.ts @@ -5,7 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {AbsoluteFsPath, NodeJSFileSystem, PathSegment} from '../../../src/ngtsc/file_system'; +import {AbsoluteFsPath, FileSystem, NodeJSFileSystem, PathSegment} from '../../../src/ngtsc/file_system'; const fs = new NodeJSFileSystem(); const basePath = fs.resolve(__dirname, '../test_cases'); @@ -31,7 +31,7 @@ export function* getAllComplianceTests(): Generator { export function* getComplianceTests(testConfigPath: string): Generator { const absTestConfigPath = fs.resolve(basePath, testConfigPath); const realTestPath = fs.dirname(absTestConfigPath); - const testConfigJSON = JSON.parse(fs.readFile(absTestConfigPath)).cases; + const testConfigJSON = loadTestCasesFile(fs, absTestConfigPath, basePath).cases; const testConfig = Array.isArray(testConfigJSON) ? testConfigJSON : [testConfigJSON]; for (const test of testConfig) { const inputFiles = getStringArrayOrDefault(test, 'inputFiles', realTestPath, ['test.ts']); @@ -40,6 +40,8 @@ export function* getComplianceTests(testConfigPath: string): Generator /[^.][^d]\.ts$/.test(f)); + const tsFiles = inputFiles.filter(f => f.endsWith('.ts') && !f.endsWith('.d.ts')); const defaultFiles = tsFiles.map(inputFile => { const outputFile = inputFile.replace(/\.ts$/, '.js'); return {expected: outputFile, generated: outputFile}; }); if (typeof value === 'undefined') { - return [{failureMessage: defaultFailureMessage, files: defaultFiles}]; + return [{ + failureMessage: defaultFailureMessage, + files: defaultFiles, + expectedErrors: [], + extraChecks: [] + }]; } if (!Array.isArray(value)) { @@ -116,10 +147,12 @@ function parseExpectations( testPath}`); } - const failureMessage = expectation.failureMessage ?? defaultFailureMessage; + const failureMessage: string = expectation.failureMessage ?? defaultFailureMessage; + const expectedErrors = parseExpectedErrors(expectation.expectedErrors, testPath); + const extraChecks = parseExtraChecks(expectation.extraChecks, testPath); if (typeof expectation.files === 'undefined') { - return {failureMessage, files: defaultFiles}; + return {failureMessage, files: defaultFiles, expectedErrors, extraChecks}; } if (!Array.isArray(expectation.files)) { @@ -127,7 +160,7 @@ function parseExpectations( i}].files" property in TEST_CASES.json - expected array of "expected files": ${ testPath}`); } - const files = expectation.files.map((file: any) => { + const files: ExpectedFile[] = expectation.files.map((file: any) => { if (typeof file === 'string') { return {expected: file, generated: file}; } @@ -140,10 +173,42 @@ function parseExpectations( testPath}`); }); - return {failureMessage, files}; + return {failureMessage, files, expectedErrors, extraChecks}; }); } +function parseExpectedErrors(expectedErrors: any = [], testPath: AbsoluteFsPath): ExpectedError[] { + if (!Array.isArray(expectedErrors)) { + throw new Error( + 'Test has invalid "expectedErrors" property in TEST_CASES.json - expected an array: ' + + testPath); + } + + return expectedErrors.map(error => { + if (typeof error !== 'object' || typeof error.message !== 'string' || + (error.location && typeof error.location !== 'string')) { + throw new Error( + `Test has invalid "expectedErrors" property in TEST_CASES.json - expected an array of ExpectedError objects: ` + + testPath); + } + return {message: parseRegExp(error.message), location: parseRegExp(error.location)}; + }); +} + +function parseExtraChecks(extraChecks: any = [], testPath: AbsoluteFsPath): ExtraCheck[] { + if (!Array.isArray(extraChecks) || + !extraChecks.every(i => typeof i === 'string' || Array.isArray(i))) { + throw new Error( + `Test has invalid "extraChecks" property in TEST_CASES.json - expected an array of strings or arrays: ` + + testPath); + } + return extraChecks; +} + +function parseRegExp(str: string|undefined): RegExp { + return new RegExp(str || ''); +} + function getConfigOptions( container: any, property: string, testPath: AbsoluteFsPath): ConfigOptions|undefined { const options = container[property]; @@ -178,6 +243,8 @@ export interface ComplianceTest { angularCompilerOptions?: ConfigOptions; /** A list of paths to source files that should be compiled for this test case. */ inputFiles: string[]; + /** If set to true then do not check expectations for this test-case in partial tests. */ + excludeFromPartialTests: boolean; /** A list of expectations to check for this test case. */ expectations: Expectation[]; /** If set to `true`, then focus on this test (equivalent to jasmine's 'fit()`). */ @@ -191,6 +258,10 @@ export interface Expectation { failureMessage: string; /** A list of pairs of paths to expected and generated files to compare. */ files: ExpectedFile[]; + /** A collection of errors that should be reported when compiling the generated file. */ + expectedErrors: ExpectedError[]; + /** Additional checks to run against the generated code. */ + extraChecks: ExtraCheck[]; } /** @@ -201,6 +272,20 @@ export interface ExpectedFile { generated: string; } +/** + * Regular expressions that should match an error message. + */ +export interface ExpectedError { + message: RegExp; + location: RegExp; +} + +/** + * The name (or name and arguments) of a function to call to run additional checks against the + * generated code. + */ +export type ExtraCheck = (string|[string, ...any]); + /** * Options to pass to configure the compiler. */ diff --git a/packages/compiler-cli/test/compliance/test_helpers/i18n_checks.ts b/packages/compiler-cli/test/compliance/test_helpers/i18n_checks.ts new file mode 100644 index 0000000000..d66a64eba8 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_helpers/i18n_checks.ts @@ -0,0 +1,89 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +const EXTRACT_GENERATED_TRANSLATIONS_REGEXP = + /const\s*(.*?)\s*=\s*goog\.getMsg\("(.*?)",?\s*(.*?)\)/g; + +/** + * Verify that placeholders in translation strings match placeholders in the object defined in the + * `goog.getMsg()` function arguments. + */ +export function verifyPlaceholdersIntegrity(output: string): boolean { + const translations = extractTranslations(output); + translations.forEach(([msg, args]) => { + const bodyPhs = extractPlaceholdersFromMsg(msg); + const argsPhs = extractPlaceholdersFromArgs(args); + if (bodyPhs.size !== argsPhs.size || diff(bodyPhs, argsPhs).size) { + return false; + } + }); + return true; +} + +/** + * Verify that all the variables initialized with `goog.getMsg()` calls have + * unique names. + */ +export function verifyUniqueConsts(output: string): boolean { + extract( + output, EXTRACT_GENERATED_TRANSLATIONS_REGEXP, + (current: string[], state: Set): string => { + const key = current[1]; + if (state.has(key)) { + throw new Error(`Duplicate const ${key} found in generated output!`); + } + return key; + }); + return true; +} + + +/** + * Extract pairs of `[msg, placeholders]`, in calls to `goog.getMsg()`, from the `source`. + * + * @param source The source code to parse. + */ +function extractTranslations(source: string): Set { + return extract( + source, EXTRACT_GENERATED_TRANSLATIONS_REGEXP, + ([, , msg, placeholders]) => [msg, placeholders]); +} + +/** + * Extract placeholder names (of the form `{$PLACEHOLDER}`) from the `msg`. + * + * @param msg The text of the message to parse. + */ +function extractPlaceholdersFromMsg(msg: string): Set { + const regex = /{\$(.*?)}/g; + return extract(msg, regex, ([, placeholders]) => placeholders); +} + +/** + * Extract the placeholder names (of the form `"PLACEHOLDER": "XXX"`) from the body of the argument + * provided as `args`. + * + * @param args The body of an object literal containing placeholder info. + */ +function extractPlaceholdersFromArgs(args: string): Set { + const regex = /\s+"(.+?)":\s*".*?"/g; + return extract(args, regex, ([, placeholders]) => placeholders); +} + +function extract( + from: string, regex: RegExp, transformFn: (match: string[], state: Set) => T): Set { + const result = new Set(); + let item; + while ((item = regex.exec(from)) !== null) { + result.add(transformFn(item, result)); + } + return result; +} + +function diff(a: Set, b: Set): Set { + return new Set(Array.from(a).filter(x => !b.has(x))); +} diff --git a/packages/compiler-cli/test/compliance/test_helpers/i18n_helpers.ts b/packages/compiler-cli/test/compliance/test_helpers/i18n_helpers.ts new file mode 100644 index 0000000000..c9fd5491a0 --- /dev/null +++ b/packages/compiler-cli/test/compliance/test_helpers/i18n_helpers.ts @@ -0,0 +1,132 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** + * Unique message id index that is needed to avoid different i18n vars with the same name to appear + * in the i18n block while generating an output string (used to verify compiler-generated code). + */ +let msgIndex = 0; + +/** + * Generate a string that represents expected i18n block content for a simple message. + */ +export function i18nMsg(message: string, placeholders: Placeholder[], meta: Meta): string { + const varName = `$I18N_${msgIndex++}$`; + const closurePlaceholders = i18nPlaceholdersToString(placeholders); + const locMessageWithPlaceholders = i18nMsgInsertLocalizePlaceholders(message, placeholders); + return ` + let ${varName}; + if (typeof ngI18nClosureMode !== "undefined" && ngI18nClosureMode) { + ${i18nMsgClosureMeta(meta)} + const $MSG_EXTERNAL_${msgIndex}$ = goog.getMsg("${message}"${closurePlaceholders}); + ${varName} = $MSG_EXTERNAL_${msgIndex}$; + } + else { + ${varName} = $localize \`${i18nMsgLocalizeMeta(meta)}${locMessageWithPlaceholders}\`; + }`; +} + +/** + * Generate a string that represents expected i18n block content for a message that requires + * post-processing. + */ +export function i18nMsgWithPostprocess( + message: string, placeholders: Placeholder[], meta: Meta, + postprocessPlaceholders: Placeholder[]): string { + const varName = `$I18N_${msgIndex}$`; + const ppPlaceholders = i18nPlaceholdersToString(postprocessPlaceholders); + return String.raw` + ${i18nMsg(message, placeholders, meta)} + ${varName} = $r3$.ɵɵi18nPostprocess($${varName}$${ppPlaceholders}); + `; +} + +/** + * Generates a string that represents expected i18n block content for an ICU. + */ +export function i18nIcuMsg(message: string, placeholders: Placeholder[]): string { + return i18nMsgWithPostprocess(message, [], {}, placeholders); +} + +/** + * Describes placeholder type used in tests. + * + * Note: the type is an array (not an object), since it's important to preserve the order of + * placeholders (so that we can compare it with generated output). + */ +export type Placeholder = [string, string]; + +/** + * Describes message metadata object. + */ +interface Meta { + desc?: string; + meaning?: string; + id?: string; +} + + +/** + * Convert a set of placeholders to a string (as it's expected from compiler). + */ +function i18nPlaceholdersToString(placeholders: Placeholder[]): string { + if (placeholders.length === 0) return ''; + const result = placeholders.map(([key, value]) => `"${key}": ${quotedValue(value)}`); + return `, { ${result.join(',')} }`; +} + +/** + * Transform a message in a Closure format to a $localize version. + */ +function i18nMsgInsertLocalizePlaceholders(message: string, placeholders: Placeholder[]): string { + if (placeholders.length > 0) { + message = message.replace(/{\$(.*?)}/g, function(_, name) { + const value = placeholders.find(([k, _]) => k === name)![1]; + // e.g. startDivTag -> START_DIV_TAG + const key = name.replace(/[A-Z]/g, (ch: string) => '_' + ch).toUpperCase(); + return '$' + + `{${quotedValue(value)}}:${key}:`; + }); + } + return message; +} + +/** + * Generate a string that represents expected Closure metadata output comment. + */ +function i18nMsgClosureMeta(meta?: Meta): string { + if (!meta || !(meta.desc || meta.meaning)) return ''; + return ` + /** + ${meta.desc ? '* @desc ' + meta.desc : ''} + ${meta.meaning ? '* @meaning ' + meta.meaning : ''} + */ + `; +} + +/** + * Generate a string that represents expected $localize metadata output. + */ +function i18nMsgLocalizeMeta(meta?: Meta): string { + if (!meta) return ''; + let localizeMeta = ''; + if (meta.meaning) localizeMeta += `${meta.meaning}|`; + if (meta.desc) localizeMeta += meta.desc; + if (meta.id) localizeMeta += `@@${meta.id}`; + return localizeMeta !== '' ? `:${localizeMeta}:` : ''; +} + +/** + * Wrap a string into quotes if needed. + * + * Note: if `value` starts with `$` it is a special case in tests when ICU reference is used as a + * placeholder value. Such special cases should not be wrapped in quotes. + */ +function quotedValue(value: string): string { + return value.startsWith('$') ? value : `"${value}"`; +} diff --git a/packages/compiler-cli/test/compliance/test_helpers/test_runner.ts b/packages/compiler-cli/test/compliance/test_helpers/test_runner.ts index b02abe3797..59b48d3506 100644 --- a/packages/compiler-cli/test/compliance/test_helpers/test_runner.ts +++ b/packages/compiler-cli/test/compliance/test_helpers/test_runner.ts @@ -7,8 +7,9 @@ */ import {FileSystem} from '../../../src/ngtsc/file_system'; import {checkExpectations} from '../test_helpers/check_expectations'; -import {initMockTestFileSystem} from '../test_helpers/compile_test'; +import {CompileResult, initMockTestFileSystem} from '../test_helpers/compile_test'; import {ComplianceTest, getAllComplianceTests} from '../test_helpers/get_compliance_tests'; +import {checkErrors} from './check_errors'; /** * Set up jasmine specs for each of the compliance tests. @@ -16,16 +17,39 @@ import {ComplianceTest, getAllComplianceTests} from '../test_helpers/get_complia * @param type A description of the type of tests being run. * @param compileFn The function that will do the compilation of the source files */ -export function runTests(type: string, compileFn: (fs: FileSystem, test: ComplianceTest) => void) { +export function runTests( + type: 'partial compile + link'|'full compile', + compileFn: (fs: FileSystem, test: ComplianceTest) => CompileResult) { + const isPartial = type === 'partial compile + link'; + describe(`compliance tests (${type})`, () => { for (const test of getAllComplianceTests()) { + if (isPartial && test.excludeFromPartialTests) { + continue; + } + describe(`[${test.relativePath}]`, () => { const itFn = test.focusTest ? fit : test.excludeTest ? xit : it; itFn(test.description, () => { + if (isPartial && test.compilerOptions?.target === 'ES5') { + throw new Error( + `The "${type}" scenario does not support ES5 output.\n` + + `Did you mean to set \`"excludeFromPartialTests": true\` in "${ + test.relativePath}"?`); + } + const fs = initMockTestFileSystem(test.realTestPath); - compileFn(fs, test); + const {errors} = compileFn(fs, test); for (const expectation of test.expectations) { - checkExpectations(fs, test.relativePath, expectation.failureMessage, expectation.files); + if (expectation.expectedErrors.length > 0) { + checkErrors( + test.relativePath, expectation.failureMessage, expectation.expectedErrors, + errors); + } else { + checkExpectations( + fs, test.relativePath, expectation.failureMessage, expectation.files, + expectation.extraChecks); + } } }); }); diff --git a/packages/compiler-cli/test/compliance_old/r3_view_compiler_i18n_spec.ts b/packages/compiler-cli/test/compliance_old/r3_view_compiler_i18n_spec.ts index c59a86b836..e87b65c0a5 100644 --- a/packages/compiler-cli/test/compliance_old/r3_view_compiler_i18n_spec.ts +++ b/packages/compiler-cli/test/compliance_old/r3_view_compiler_i18n_spec.ts @@ -783,58 +783,6 @@ describe('i18n support in the template compiler', () => { verify(input, output); }); - it('should correctly bind to context in nested template', () => { - const input = ` -
-
-
- `; - - const i18n_0 = i18nMsg( - 'different scope {$interpolation}', [['interpolation', String.raw`\uFFFD0\uFFFD`]], - {meaning: 'm', desc: 'd'}); - - const output = String.raw` - function MyComponent_div_0_Template(rf, ctx) { - if (rf & 1) { - $r3$.ɵɵelementStart(0, "div"); - $r3$.ɵɵelementStart(1, "div", 1); - $r3$.ɵɵpipe(2, "uppercase"); - $r3$.ɵɵi18nAttributes(3, 2); - $r3$.ɵɵelementEnd(); - $r3$.ɵɵelementEnd(); - } - if (rf & 2) { - const $outer_r1$ = ctx.$implicit; - $r3$.ɵɵadvance(1); - $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(2, 1, $outer_r1$)); - $r3$.ɵɵi18nApply(3); - } - } - … - decls: 1, - vars: 1, - consts: function() { - ${i18n_0} - return [ - [${AttributeMarker.Template}, "ngFor", "ngForOf"], - [${AttributeMarker.I18n}, "title"], - ["title", $i18n_0$] - ]; - }, - template: function MyComponent_Template(rf, ctx) { - if (rf & 1) { - $r3$.ɵɵtemplate(0, MyComponent_div_0_Template, 4, 3, "div", 0); - } - if (rf & 2) { - $r3$.ɵɵproperty("ngForOf", ctx.items); - } - } - `; - - verify(input, output); - }); - it('should work correctly when placed on i18n root node', () => { const input = `
Some content