diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75806aa..688b4a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + toolchain: nightly-2024-07-21 override: true - uses: Swatinem/rust-cache@v2 - uses: actions-rs/cargo@v1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5025210..dbeee81 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,7 @@ ## Install rust -You can follow instructions at ['Install Rust' page from the official rust website](https://www.rust-lang.org/tools/install) +You can follow instructions +at ['Install Rust' page from the official rust website](https://www.rust-lang.org/tools/install) ## Add wasm target to rust @@ -9,6 +10,7 @@ rustup target add wasm32-wasi ``` ## Running tests + ```bash # run all test suite cargo test @@ -26,19 +28,21 @@ cargo test jsx_ # (alias for `cargo build --target wasm32-wasi`) cargo build-wasi --release ``` + Then wasm binary would be on the path: `./target/wasm32-wasi/release/lingui_macro_plugin.wasm` -You can check it in your own project or in the `examples/nextjs-13` example in this repo by specifying full path to the WASM binary: +You can check it in your own project or in the `examples/nextjs-13` example in this repo by specifying full path to the +WASM binary: ```ts /** @type {import('next').NextConfig} */ const nextConfig = { - experimental: { - swcPlugins: [ - ['/Users/tim/projects/lingui-macro-plugin/target/wasm32-wasi/release/lingui_macro_plugin.wasm', {}], - ], - }, -}; + experimental: { + swcPlugins: [ + ['/Users/tim/projects/lingui-macro-plugin/target/wasm32-wasi/release/lingui_macro_plugin.wasm', {}], + ], + }, + }; module.exports = nextConfig; ``` @@ -48,3 +52,5 @@ module.exports = nextConfig; It's important to build a plugin with the same Rust version used to build SWC itself. This project uses `rust-toolchain` file in the root of project to define rust version. + +To update Rust, put new version into `rust-toolchain` and call `rustup update` command \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 41105f5..a994390 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,9 +29,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", diff --git a/rust-toolchain b/rust-toolchain index 49a8e2a..d49f143 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1,5 @@ -nightly-2023-03-20 +[toolchain] +channel = "nightly-2024-07-21" +components = [ + "cargo", +] \ No newline at end of file diff --git a/src/jsx_visitor.rs b/src/jsx_visitor.rs index df8ed0f..a98a18e 100644 --- a/src/jsx_visitor.rs +++ b/src/jsx_visitor.rs @@ -1,15 +1,13 @@ -use swc_core::ecma::{ - visit::{Visit, VisitWith}, -}; -use swc_core::ecma::ast::{*}; -use swc_core::common::DUMMY_SP; use crate::ast_utils::{get_jsx_attr, get_jsx_attr_value_as_string}; -use crate::tokens::{IcuChoice, ChoiceCase, CaseOrOffset, MsgToken, TagOpening}; -use regex::{Regex}; +use crate::macro_utils::MacroCtx; +use crate::tokens::{CaseOrOffset, ChoiceCase, IcuChoice, MsgToken, TagOpening}; use once_cell::sync::Lazy; +use regex::Regex; +use swc_core::common::DUMMY_SP; +use swc_core::ecma::ast::*; use swc_core::ecma::atoms::JsWord; +use swc_core::ecma::visit::{Visit, VisitWith}; use swc_core::plugin::errors::HANDLER; -use crate::macro_utils::{ MacroCtx}; pub struct TransJSXVisitor<'a> { pub tokens: Vec, @@ -20,12 +18,13 @@ impl<'a> TransJSXVisitor<'a> { pub fn new(ctx: &'a MacroCtx) -> TransJSXVisitor<'a> { TransJSXVisitor { tokens: Vec::new(), - ctx + ctx, } } } -static PLURAL_OPTIONS_WHITELIST: Lazy = Lazy::new(|| Regex::new(r"(_[\d\w]+|zero|one|two|few|many|other)").unwrap()); +static PLURAL_OPTIONS_WHITELIST: Lazy = + Lazy::new(|| Regex::new(r"(_[\d\w]+|zero|one|two|few|many|other)").unwrap()); static NUM_OPTION: Lazy = Lazy::new(|| Regex::new(r"_(\d+)").unwrap()); static WORD_OPTION: Lazy = Lazy::new(|| Regex::new(r"_(\w+)").unwrap()); @@ -37,45 +36,45 @@ static TRIM_END: Lazy = Lazy::new(|| Regex::new(r"[ ]+$").unwrap()); // taken from babel repo -> packages/babel-types/src/utils/react/cleanJSXElementLiteralChild.ts fn clean_jsx_element_literal_child(value: &str) -> String { - let lines: Vec<&str> = value.split('\n').collect(); - let mut last_non_empty_line = 0; + let lines: Vec<&str> = value.split('\n').collect(); + let mut last_non_empty_line = 0; - for (i, line) in lines.iter().enumerate() { - if line.trim().len() > 0 { - last_non_empty_line = i; + for (i, line) in lines.iter().enumerate() { + if line.trim().len() > 0 { + last_non_empty_line = i; + } } - } - let mut result = String::new(); + let mut result = String::new(); - for (i, line) in lines.iter().enumerate() { - let is_first_line = i == 0; - let is_last_line = i == lines.len() - 1; - let is_last_non_empty_line = i == last_non_empty_line; + for (i, line) in lines.iter().enumerate() { + let is_first_line = i == 0; + let is_last_line = i == lines.len() - 1; + let is_last_non_empty_line = i == last_non_empty_line; - // replace rendered whitespace tabs with spaces - let mut trimmed_line = line.replace("\t", " "); + // replace rendered whitespace tabs with spaces + let mut trimmed_line = line.replace("\t", " "); - // trim whitespace touching a newline - if !is_first_line { - trimmed_line = TRIM_START.replace(&trimmed_line, "").to_string(); - } + // trim whitespace touching a newline + if !is_first_line { + trimmed_line = TRIM_START.replace(&trimmed_line, "").to_string(); + } - // trim whitespace touching an endline - if !is_last_line { - trimmed_line = TRIM_END.replace(&trimmed_line, "").to_string();; - } + // trim whitespace touching an endline + if !is_last_line { + trimmed_line = TRIM_END.replace(&trimmed_line, "").to_string(); + } - if !trimmed_line.is_empty() { - if !is_last_non_empty_line { - trimmed_line.push(' '); - } + if !trimmed_line.is_empty() { + if !is_last_non_empty_line { + trimmed_line.push(' '); + } - result.push_str(&trimmed_line); + result.push_str(&trimmed_line); + } } - } - result + result } fn is_allowed_plural_option(key: &str) -> Option { @@ -114,12 +113,14 @@ impl<'a> TransJSXVisitor<'a> { tokens.push(MsgToken::String(string)); } - JSXAttrValue::JSXExprContainer(JSXExprContainer { expr: JSXExpr::Expr(exp), .. }) => { + JSXAttrValue::JSXExprContainer(JSXExprContainer { + expr: JSXExpr::Expr(exp), + .. + }) => { match exp.as_ref() { // some={"# books"} - Expr::Lit(Lit::Str(str)) => { - tokens.push(MsgToken::String(str.value.clone().to_string())) - } + Expr::Lit(Lit::Str(str)) => tokens + .push(MsgToken::String(str.value.clone().to_string())), // some={`# books ${name}`} Expr::Tpl(tpl) => { tokens.extend(self.ctx.tokenize_tpl(tpl)); @@ -132,9 +133,7 @@ impl<'a> TransJSXVisitor<'a> { tokens.extend(visitor.tokens) } - _ => { - tokens.push(MsgToken::Expression(exp.clone())) - } + _ => tokens.push(MsgToken::Expression(exp.clone())), } } @@ -143,11 +142,7 @@ impl<'a> TransJSXVisitor<'a> { } } - choices.push(CaseOrOffset::Case( - ChoiceCase { - tokens, - key, - })) + choices.push(CaseOrOffset::Case(ChoiceCase { tokens, key })) } } } @@ -174,21 +169,18 @@ impl<'a> Visit for TransJSXVisitor<'a> { if self.ctx.is_lingui_jsx_choice_cmp(&ident) { let value = match get_jsx_attr(&el, "value").and_then(|attr| attr.value.as_ref()) { - Some( - JSXAttrValue::JSXExprContainer( - JSXExprContainer { expr: JSXExpr::Expr(exp), .. } - ) - ) => { - exp.clone() - } - _ => { - Box::new(Expr::Lit(Lit::Null(Null { - span: DUMMY_SP - }))) - } + Some(JSXAttrValue::JSXExprContainer(JSXExprContainer { + expr: JSXExpr::Expr(exp), + .. + })) => exp.clone(), + _ => Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))), }; - let icu_method = self.ctx.get_ident_export_name(ident).unwrap().to_lowercase(); + let icu_method = self + .ctx + .get_ident_export_name(ident) + .unwrap() + .to_lowercase(); let choices = self.visit_icu_macro(el, &icu_method); self.tokens.push(MsgToken::IcuChoice(IcuChoice { @@ -214,25 +206,21 @@ impl<'a> Visit for TransJSXVisitor<'a> { } fn visit_jsx_closing_element(&mut self, _el: &JSXClosingElement) { - self.tokens.push( - MsgToken::TagClosing - ); + self.tokens.push(MsgToken::TagClosing); } fn visit_jsx_text(&mut self, el: &JSXText) { - - self.tokens.push( - MsgToken::String(clean_jsx_element_literal_child(&el.raw.to_string())) - ); + self.tokens + .push(MsgToken::String(clean_jsx_element_literal_child( + &el.raw.to_string(), + ))); } fn visit_jsx_expr_container(&mut self, cont: &JSXExprContainer) { if let JSXExpr::Expr(exp) = &cont.expr { match exp.as_ref() { Expr::Lit(Lit::Str(str)) => { - self.tokens.push( - MsgToken::String(str.value.to_string()) - ); + self.tokens.push(MsgToken::String(str.value.to_string())); } // todo write tests and validate @@ -241,9 +229,7 @@ impl<'a> Visit for TransJSXVisitor<'a> { if let Some(tokens) = self.ctx.try_tokenize_call_expr_as_choice_cmp(call) { self.tokens.extend(tokens); } else { - self.tokens.push( - MsgToken::Expression(exp.clone()) - ); + self.tokens.push(MsgToken::Expression(exp.clone())); } } @@ -252,18 +238,12 @@ impl<'a> Visit for TransJSXVisitor<'a> { } Expr::Tpl(tpl) => { - self.tokens.extend( - self.ctx.tokenize_tpl(tpl) - ); + self.tokens.extend(self.ctx.tokenize_tpl(tpl)); } _ => { - self.tokens.push( - MsgToken::Expression(exp.clone()) - ); + self.tokens.push(MsgToken::Expression(exp.clone())); } } } } } - - diff --git a/src/lib.rs b/src/lib.rs index 819210b..839b2b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(is_some_and)] - use std::collections::HashSet; use swc_core::common::DUMMY_SP; diff --git a/src/tests/jsx_icu.rs b/src/tests/jsx_icu.rs index f266b28..78dc8cb 100644 --- a/src/tests/jsx_icu.rs +++ b/src/tests/jsx_icu.rs @@ -1,8 +1,8 @@ -use crate::{to}; +use crate::to; to!( jsx_icu, - r#" + r#" import { Plural } from "@lingui/react/macro"; const ex1 = "#, - r#" import { Trans as Trans_ } from "@lingui/react"; @@ -33,7 +32,7 @@ const ex2 =
"#, - r#" import { Trans as Trans_ } from "@lingui/react"; @@ -57,7 +55,7 @@ to!( to!( jsx_plural_preserve_reserved_attrs, - r#" + r#" import { Plural } from "@lingui/react/macro"; "#, - r#" import { Trans as Trans_ } from "@lingui/react"; @@ -84,7 +81,7 @@ to!( to!( jsx_icu_nested, - r#" + r#" import { Plural, Trans } from "@lingui/react/macro"; @@ -96,7 +93,6 @@ to!( /> "#, - r#" import { Trans as Trans_ } from "@lingui/react"; @@ -110,7 +106,7 @@ to!( to!( jsx_trans_inside_plural, - r#" + r#" import { Trans, Plural } from '@lingui/macro'; ; "#, - r#" import { Trans as Trans_ } from "@lingui/react"; # slot added} other {<1># slots added}}"} id={"X8eyr1"} @@ -142,7 +137,7 @@ to!( to!( jsx_multivelel_nesting, - r#" + r#" import { Trans, Plural } from '@lingui/macro'; ; "#, - r#" import { Trans as Trans_ } from "@lingui/react"; A lot of them} />; "#, - r#" import { Trans as Trans_ } from "@lingui/react"; A lot of them}}"} @@ -216,7 +209,7 @@ to!( to!( jsx_icu_with_template_literal, - r#" + r#" import { Plural } from "@lingui/react/macro"; ; "#, - r#" import { Trans as Trans_ } from "@lingui/react"; Other} />; "#, - r#" import { Trans as Trans_ } from "@lingui/react"; Other}}"} id={"Imwef9"} values={{ @@ -262,7 +253,7 @@ to!( to!( jsx_select_with_expressions_in_cases, - r#" + r#" import { Select } from '@lingui/macro'; Other} />; "#, - r#" import { Trans as Trans_ } from "@lingui/react"; Other}}"} id={"4jX4Bx"} values={{ @@ -337,7 +326,7 @@ to!( to!( jsx_select_ordinal_with_offset_and_exact_matches, - r#" + r#" import { SelectOrdinal } from "@lingui/react/macro"; ; "#, - r#" import { Trans as Trans_ } from "@lingui/react"; A lot of them} /> "#, - r#" import { Trans as Trans_ } from "@lingui/react"; Line ending in non-breaking space.  text in element ; "#, - r#" -import { Trans } from "@lingui/react"; -text in element"} id={"CJuEhi"} components={{ +import { Trans as Trans_ } from "@lingui/react"; +text in element"} id={"CJuEhi"} components={{ 0: }}/>; "#