|
5 | 5 |
|
6 | 6 | use oxc_allocator::Allocator; |
7 | 7 | use oxc_angular_compiler::{ |
8 | | - AngularVersion, R3Node, ResolvedResources, TransformOptions as ComponentTransformOptions, |
| 8 | + AngularVersion, CompilationMode, R3Node, ResolvedResources, |
| 9 | + TransformOptions as ComponentTransformOptions, |
9 | 10 | output::ast::FunctionExpr, |
10 | 11 | output::emitter::JsEmitter, |
11 | 12 | parser::html::HtmlParser, |
@@ -5902,6 +5903,73 @@ fn test_i18n_legacy_message_ids_untrimmed_attribute_and_nested_icu() { |
5902 | 5903 | assert_contains(&js, ":ICU@@5870918674674044418:"); |
5903 | 5904 | } |
5904 | 5905 |
|
| 5906 | +/// A meaning or description goes in a JSDoc comment, so `*/` in it would end the comment and |
| 5907 | +/// leave the rest as code. Angular's `tagToString` throws `JSDoc text cannot contain "/*" and |
| 5908 | +/// "*/"`, and the template is reported with that error instead of compiled. |
| 5909 | +#[test] |
| 5910 | +fn test_i18n_meta_that_would_close_its_jsdoc_comment_is_an_error() { |
| 5911 | + for template in [ |
| 5912 | + r#"<p i18n="m|d */ steal() /*">Hello</p>"#, |
| 5913 | + r#"<img i18n-alt="*/ } steal(); { /*|d" alt="Logo" />"#, |
| 5914 | + r#"<p i18n="m /* x|d">Hello</p>"#, |
| 5915 | + ] { |
| 5916 | + let allocator = Allocator::default(); |
| 5917 | + let source = format!( |
| 5918 | + "import {{ Component }} from '@angular/core';\n\ |
| 5919 | + @Component({{ selector: 'app-repro', template: `{template}` }})\n\ |
| 5920 | + export class Repro {{}}\n" |
| 5921 | + ); |
| 5922 | + let result = transform_angular_file(&allocator, "repro.ts", &source, None, None); |
| 5923 | + let messages: Vec<String> = result.diagnostics.iter().map(|d| d.to_string()).collect(); |
| 5924 | + assert!( |
| 5925 | + messages.iter().any(|m| m.contains(r#"JSDoc text cannot contain "/*" and "*/""#)), |
| 5926 | + "{template}: {messages:?}" |
| 5927 | + ); |
| 5928 | + assert!(!result.code.contains("/**"), "{}", result.code); |
| 5929 | + } |
| 5930 | +} |
| 5931 | + |
| 5932 | +/// Angular throws only when it prints the JSDoc, so `*/` is fine where no message is emitted, |
| 5933 | +/// and in partial compilation, which emits the template source. Angular 22.2 `ngc` compiles |
| 5934 | +/// each of these. |
| 5935 | +#[test] |
| 5936 | +fn test_i18n_meta_that_would_close_its_jsdoc_comment_is_fine_with_no_message() { |
| 5937 | + let full = ComponentTransformOptions::default(); |
| 5938 | + let partial = ComponentTransformOptions { |
| 5939 | + compilation_mode: CompilationMode::Partial, |
| 5940 | + ..ComponentTransformOptions::default() |
| 5941 | + }; |
| 5942 | + for (template, options) in [ |
| 5943 | + (r#"<p i18n="*/x"></p>"#, &full), |
| 5944 | + (r#"<p i18n="*/x"> </p>"#, &full), |
| 5945 | + (r#"<p i18n="*/x"><!-- c --></p>"#, &full), |
| 5946 | + (r#"<ng-container i18n="*/x"></ng-container>"#, &full), |
| 5947 | + (r#"<ng-content i18n="*/x"></ng-content>"#, &full), |
| 5948 | + (r#"<p title="a" i18n-title="*/x" i18n-title="ok">x</p>"#, &full), |
| 5949 | + (r#"<img i18n-alt="*/x" />"#, &full), |
| 5950 | + (r#"<img i18n-alt="*/x" [alt]="alt" />"#, &full), |
| 5951 | + (r#"<p i18n="m|d */ steal() /*">Hello</p>"#, &partial), |
| 5952 | + ] { |
| 5953 | + let allocator = Allocator::default(); |
| 5954 | + let source = format!( |
| 5955 | + "import {{ Component }} from '@angular/core';\n\ |
| 5956 | + @Component({{ selector: 'app-repro', template: `{template}` }})\n\ |
| 5957 | + export class Repro {{ alt = ''; }}\n" |
| 5958 | + ); |
| 5959 | + let result = transform_angular_file(&allocator, "repro.ts", &source, Some(options), None); |
| 5960 | + assert!(!result.has_errors(), "{template}: {:?}", result.diagnostics); |
| 5961 | + assert!(result.code.contains("Repro"), "{}", result.code); |
| 5962 | + } |
| 5963 | +} |
| 5964 | + |
| 5965 | +/// `@` in a meaning or description is escaped, as Angular's `tagToString` does, so it does not |
| 5966 | +/// start a JSDoc tag. |
| 5967 | +#[test] |
| 5968 | +fn test_i18n_meta_at_sign_is_escaped_in_jsdoc() { |
| 5969 | + let js = compile_i18n_component(r#"<p i18n="m@x|mail a@b.c">Hello</p>"#); |
| 5970 | + assert_contains(&js, r"/** @desc mail a\@b.c @meaning m\@x */"); |
| 5971 | +} |
| 5972 | + |
5905 | 5973 | /// `consts` with i18n initializers is an arrow function with a block body, and a message is |
5906 | 5974 | /// a native `$localize` tagged template. Angular 22.2 `ngc` (ES2022): |
5907 | 5975 | /// consts: () => { let i18n_0; ... i18n_0 = $localize `:@@close:Close`; } return [...]; } |
|
0 commit comments