Skip to content

Commit 1aebcb5

Browse files
authored
fix: unwrap non-null assertion (x!) / satisfies in placeholders (parity with babel macro) (#247)
1 parent a7075a4 commit 1aebcb5

4 files changed

Lines changed: 63 additions & 9 deletions

File tree

crates/lingui_macro/src/macro_utils.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use swc_core::ecma::{ast::*, atoms::Atom};
99
use swc_core::plugin::errors::HANDLER;
1010

1111
fn expression_to_name(expr: &Expr, ctx: &mut MacroCtx) -> String {
12-
let expr = unwrap_ts_as_expr(expr);
12+
let expr = unwrap_ts_only_expr(expr);
1313

1414
match expr {
1515
Expr::Ident(ident) => ident.sym.to_string(),
@@ -33,7 +33,7 @@ fn expression_to_name(expr: &Expr, ctx: &mut MacroCtx) -> String {
3333
}
3434

3535
fn expression_to_value(expr: Box<Expr>) -> Box<Expr> {
36-
let unwrapped = unwrap_ts_as_expr(&expr);
36+
let unwrapped = unwrap_ts_only_expr(&expr);
3737

3838
match unwrapped {
3939
Expr::Object(object) => {
@@ -66,14 +66,18 @@ fn expression_to_value(expr: Box<Expr>) -> Box<Expr> {
6666
}
6767
}
6868

69-
// recursively expands TypeScript's as expressions until it reaches a real value
70-
fn unwrap_ts_as_expr(expr: &Expr) -> &Expr {
69+
// recursively unwraps TypeScript-only expression wrappers (`x as T`, `x!`,
70+
// `x satisfies T`) until it reaches a real value, so the inner expression drives
71+
// placeholder naming (e.g. `${x!}` → `{x}`, not `{0}`).
72+
fn unwrap_ts_only_expr(expr: &Expr) -> &Expr {
7173
let mut current = expr;
72-
while let Expr::TsAs(TsAsExpr {
73-
expr: inner_expr, ..
74-
}) = current
75-
{
76-
current = inner_expr;
74+
loop {
75+
current = match current {
76+
Expr::TsAs(TsAsExpr { expr, .. })
77+
| Expr::TsNonNull(TsNonNullExpr { expr, .. })
78+
| Expr::TsSatisfies(TsSatisfiesExpr { expr, .. }) => expr,
79+
_ => break,
80+
};
7781
}
7882
current
7983
}

crates/lingui_macro/tests/js_t.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,21 @@ to!(
228228
t`Hello World`
229229
"#
230230
);
231+
232+
// TS-only expression wrappers unwrap to a named placeholder (parity with `as`),
233+
// matching @lingui/babel-plugin-lingui-macro (see lingui/js-lingui#2622).
234+
to!(
235+
js_non_null_assertion_gets_named_placeholder,
236+
r#"
237+
import { t } from '@lingui/core/macro';
238+
t`Variable ${name!}`;
239+
"#
240+
);
241+
242+
to!(
243+
js_satisfies_gets_named_placeholder,
244+
r#"
245+
import { t } from '@lingui/core/macro';
246+
t`Variable ${name satisfies string}`;
247+
"#
248+
);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
source: crates/lingui_macro/tests/js_t.rs
3+
---
4+
import { t } from '@lingui/core/macro';
5+
t`Variable ${name!}`;
6+
7+
↓ ↓ ↓ ↓ ↓ ↓
8+
9+
import { i18n as $_i18n } from "@lingui/core";
10+
$_i18n._(/*i18n*/ {
11+
id: "xRRkAE",
12+
message: "Variable {name}",
13+
values: {
14+
name: name!
15+
}
16+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
source: crates/lingui_macro/tests/js_t.rs
3+
---
4+
import { t } from '@lingui/core/macro';
5+
t`Variable ${name satisfies string}`;
6+
7+
↓ ↓ ↓ ↓ ↓ ↓
8+
9+
import { i18n as $_i18n } from "@lingui/core";
10+
$_i18n._(/*i18n*/ {
11+
id: "xRRkAE",
12+
message: "Variable {name}",
13+
values: {
14+
name: name satisfies string
15+
}
16+
});

0 commit comments

Comments
 (0)