Skip to content

Conversation

@gaoachao
Copy link
Collaborator

@gaoachao gaoachao commented Sep 12, 2025

A cherry-pick of #1458.
Follow up #1740, so we should merge it first.

Summary by CodeRabbit

  • New Features

    • Added a configurable React Lynx compatibility transform: JSX rewriting, component wrapping, event-prop normalization, runtime selection, dark-mode and related config options.
  • Tests

    • Expanded snapshot coverage for wrapping, embedded components, runtime changes, event props, view renames, constructor simplification, recursion, and scope/aliasing.
  • Chores

    • Broadened lint/format ignore patterns, added build/task config for the transform, updated workspace inclusion and build flags for WASM artifact.

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).
  • Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required).

@changeset-bot
Copy link

changeset-bot bot commented Sep 12, 2025

⚠️ No Changeset found

Latest commit: 0fb02d3

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 12, 2025

📝 Walkthrough

Walkthrough

Adds a new SWC React‑Lynx compatibility plugin (Rust + WASM), TypeScript declarations, build/turbo config and npm workspace inclusion, expanded repo ignore/exclude globs, and many new SWC test snapshots covering JSX transformations, event props, component wrapping, and constructor simplification.

Changes

Cohort / File(s) Summary
Repo config ignores
biome.jsonc, eslint.config.js, packages/react/.dprint.jsonc
Broadened ignore/exclude globs to include nested packages/react/transform/**/... snapshots and index.d.ts files; added // REMOVE ME markers and adjusted snapshot-exclude comments.
Workspace / build files
pnpm-workspace.yaml, packages/react/transform/swc-plugin-reactlynx-compat/turbo.json, packages/react/transform/swc-plugin-reactlynx-compat/build.js
Added workspace inclusion for packages/react/transform/*, new Turbo task for building the plugin WASM, and updated build script to compute/preserve RUSTFLAGS when invoking cargo.
Type declarations
packages/react/transform/swc-plugin-reactlynx-compat/index.d.ts
New TypeScript declaration file defining many plugin config interfaces and the SwcPluginReactLynxOptions aggregate.
SWC plugin core (Rust)
packages/react/transform/swc-plugin-reactlynx-compat/src/lib.rs, .../src/target.rs
New plugin entry process_transform, public config types (Either, CompatVisitorConfig, dark-mode types, etc.), visitor orchestration, runtime wiring, and TransformTarget enum with custom serde deserialization and tests.
Helper visitors (Rust)
packages/react/transform/swc-plugin-reactlynx-compat/src/is_component_class.rs, .../src/simplify_ctor_like_react_lynx_2.rs
New visitors: component-class detector (TransformVisitor) and constructor simplifier (CtorSimplifyVisitor) exposing detection flags and retained props/statements.
Tests snapshots (many)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/*.js
Added numerous SWC snapshot files exercising component wrapping, embedded wrappers, runtime-package changes, event prop transforms, view renaming, JSX member behavior, and constructor/state simplification scenarios.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Suggested reviewers

  • hzy
  • Yradex

Poem

A hop, a tweak, I scan the tree,
I sniffed the JSX and set it free.
WASM whiskers, visitor dance,
Snapshots sprout from every branch.
Carrots compiled — rejoice, perchance! 🥕🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "refactor: add swc-plugin-reactlynx-compat" is a short, single-sentence summary that directly describes the primary change (adding the swc-plugin-reactlynx-compat) and matches the PR objectives and file additions in the changeset.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/add-swc-plugin-reactlynx-compat

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@relativeci
Copy link

relativeci bot commented Sep 12, 2025

Web Explorer

#5235 Bundle Size — 364.39KiB (0%).

0fb02d3(current) vs 588819b main#5227(baseline)

Bundle metrics  Change 1 change
                 Current
#5235
     Baseline
#5227
No change  Initial JS 145.41KiB 145.41KiB
No change  Initial CSS 31.89KiB 31.89KiB
No change  Cache Invalidation 0% 0%
No change  Chunks 8 8
No change  Assets 8 8
Change  Modules 218(-0.46%) 219
No change  Duplicate Modules 16 16
No change  Duplicate Code 3.37% 3.37%
No change  Packages 4 4
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#5235
     Baseline
#5227
No change  JS 238.48KiB 238.48KiB
No change  Other 94.02KiB 94.02KiB
No change  CSS 31.89KiB 31.89KiB

Bundle analysis reportBranch refactor/add-swc-plugin-reactlyn...Project dashboard


Generated by RelativeCIDocumentationReport issue

@relativeci
Copy link

relativeci bot commented Sep 12, 2025

React Example

#5242 Bundle Size — 237.52KiB (0%).

0fb02d3(current) vs 588819b main#5234(baseline)

Bundle metrics  no changes
                 Current
#5242
     Baseline
#5234
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 162 162
No change  Duplicate Modules 65 65
No change  Duplicate Code 46.71% 46.71%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#5242
     Baseline
#5234
No change  IMG 145.76KiB 145.76KiB
No change  Other 91.76KiB 91.76KiB

Bundle analysis reportBranch refactor/add-swc-plugin-reactlyn...Project dashboard


Generated by RelativeCIDocumentationReport issue

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/react/transform/swc-plugin-reactlynx/src/lib.rs (1)

1-2: Empty plugin entrypoint — add a no-op transform or remove the crate from the workspace.

File: packages/react/transform/swc-plugin-reactlynx/src/lib.rs — this crate is listed in the workspace but exports nothing; add a minimal stub or remove the crate.

Suggested minimal stub:

+// Copyright 2025 The Lynx Authors. All rights reserved.
+use swc_core::ecma::ast::Program;
+use swc_core::plugin::{plugin_transform, proxies::TransformPluginProgramMetadata};
+
+#[plugin_transform]
+pub fn transform(program: Program, _metadata: TransformPluginProgramMetadata) -> Program {
+    program
+}
🧹 Nitpick comments (29)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_left_no_remove_component_element_attr.js (1)

1-1: Minor formatting consistency.

Ensure snapshots follow the repo’s formatting convention (semicolon usage and trailing newline) consistently across all new snapshots added in this PR.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_remove_component_attr_regex.js (1)

5-5: Clarify the intent of the “__a” edge case

If __a is intentionally treated as non-component for legacy compat, consider adding a brief note in the test source explaining that, or add an explicit lowercase tag case (e.g., <a .../>) in a separate test to make the distinction obvious.

I can draft a small companion test that asserts attrs are retained on lowercase intrinsic elements.

biome.jsonc (2)

38-38: Replace "REMOVE ME" with a tracked TODO.

Use a stable TODO with an issue link to avoid lingering markers in config.

-      // REMOVE ME
+      // TODO(maintainers): Temporary ignore for generated d.ts until lint rules are adjusted. Track in #<issue-id>.

42-42: Redundant specific ignore entry; wildcard already covers it.

"packages/react/transform/index.d.ts" above is now redundant given the /**/ pattern. Consider removing the specific line to reduce drift.

packages/react/transform/swc-plugin-reactlynx/.cargo/config.toml (1)

1-4: Alias looks good; consider WASM size/perf release tweaks.

Optional: set a release profile (in the crate or workspace Cargo.toml) to reduce .wasm size and improve load times.

Add to Cargo.toml (not this file):

[profile.release]
lto = "fat"
codegen-units = 1
opt-level = "s"
panic = "abort"
packages/react/transform/swc-plugin-reactlynx-compat/.cargo/config.toml (1)

1-4: Cargo alias is fine; consider deduping at workspace level.

Placing this alias in a root .cargo/config.toml would let both plugins share it and avoid drift.

packages/react/.dprint.jsonc (1)

15-20: Deduplicate snapshot ignore globs.

transform/tests/__swc_snapshots__/** is covered by the recursive glob below; remove the specific entry.

Apply:

-    "transform/tests/__swc_snapshots__/**",
eslint.config.js (2)

47-53: Consolidate snapshot ignores to recursive patterns.

Avoid duplicate specific globs; keep only the recursive variants.

Apply:

-      // REMOVE ME
-      'packages/react/transform/tests/__swc_snapshots__/**',
-      'packages/react/transform/__test__/**/__snapshots__/**',
-
-      'packages/react/transform/**/tests/__swc_snapshots__/**',
+      // Snapshots (recursive)
+      'packages/react/transform/**/tests/__swc_snapshots__/**',
+      'packages/react/transform/**/__test__/**/__snapshots__/**',

71-76: Trim redundant index.d.ts ignore.

The recursive ignore makes the specific one unnecessary.

Apply:

-      'packages/react/transform/index.d.ts',
       'packages/react/transform/index.cjs',
 
       'packages/react/transform/**/index.d.ts',
packages/react/transform/swc-plugin-reactlynx/Cargo.toml (2)

20-20: Trim __testing_transform from release feature set (optional).

__testing_transform is test-only; carrying it in the main feature list increases compile time. Consider moving it behind dev-dependencies or a crate feature used only in tests.

-swc_core = { workspace = true, features = ["base", "ecma_codegen", "ecma_parser", "ecma_minifier", "ecma_transforms_typescript", "ecma_utils", "ecma_quote", "ecma_transforms_react", "ecma_transforms_optimization", "css_parser", "css_ast", "css_visit", "css_codegen", "__visit", "__testing_transform", "ecma_plugin_transform"] }
+swc_core = { workspace = true, features = ["base", "ecma_codegen", "ecma_parser", "ecma_minifier", "ecma_transforms_typescript", "ecma_utils", "ecma_quote", "ecma_transforms_react", "ecma_transforms_optimization", "css_parser", "css_ast", "css_visit", "css_codegen", "__visit", "ecma_plugin_transform"] }

If tests rely on it, add under [dev-dependencies] swc_core with features = ["__testing_transform"].


18-19: Align serde_json default-features across reactlynx plugins (optional)

swc-plugin-reactlynx currently has serde_json = { workspace = true } while swc-plugin-reactlynx-compat sets default-features = false and packages/react/transform/Cargo.toml uses features = ["preserve_order"]. Either add default-features = false to swc-plugin-reactlynx to match compat (recommended for wasm size/behavior parity) or document why this crate intentionally differs. Files: packages/react/transform/Cargo.toml, packages/react/transform/swc-plugin-reactlynx/Cargo.toml, packages/react/transform/swc-plugin-reactlynx-compat/Cargo.toml.

packages/react/transform/swc-plugin-reactlynx-compat/src/simplify_ctor_like_react_lynx_2.rs (2)

36-37: Compile the regex once; current code recompiles per constructor.

Use a static Lazy<Regex> to avoid repeated compilation and reduce overhead.

-use regex::Regex;
+use once_cell::sync::Lazy;
+use regex::Regex;

+static RE_FLAG_IDENT: Lazy<Regex> = Lazy::new(|| Regex::new(r"^__[A-Z_]+__$").unwrap());
@@
-      let re = Regex::new(r"^__[A-Z_]+__$").unwrap();
       for stmt in &body.stmts {
@@
-          if re.captures(test_ident.sym.as_str()).is_some() {
+          if RE_FLAG_IDENT.is_match(test_ident.sym.as_str()) {
             self.remain_stmts.push(stmt.clone())
           }

115-127: Detect more dynamic patterns in state props.

  • Arrow functions are not explicitly flagged; rely on traversal is brittle. Mark them directly.
  • Treat new Foo() as dynamic similar to calls.
 struct CallExprDetectVisitor {
   pub has: bool,
 }
@@
 impl Visit for CallExprDetectVisitor {
   fn visit_call_expr(&mut self, _n: &CallExpr) {
     self.has = true;
   }
+  fn visit_new_expr(&mut self, _n: &NewExpr) {
+    self.has = true;
+  }
 }
@@
 struct FuncDetectVisitor {
   pub has: bool,
 }
@@
 impl Visit for FuncDetectVisitor {
   fn visit_function(&mut self, _n: &Function) {
     self.has = true;
   }
+  fn visit_arrow_expr(&mut self, _n: &ArrowExpr) {
+    self.has = true;
+  }
   fn visit_block_stmt_or_expr(&mut self, _n: &BlockStmtOrExpr) {
     self.has = true;
   }
 }

Also applies to: 139-157

packages/react/transform/swc-plugin-reactlynx-compat/src/is_component_class.rs (1)

21-27: Parity: mark JSX usage via createSnapshotInstance() calls.

In other transforms, this call is treated as JSX presence. Add a visit_call_expr to set has_jsx = true when callee ident is createSnapshotInstance.

 impl Visit for TransformVisitor {
   fn visit_jsx_element(&mut self, _n: &JSXElement) {
     self.has_jsx = true;
   }
   fn visit_jsx_fragment(&mut self, _n: &JSXFragment) {
     self.has_jsx = true;
   }
+  fn visit_call_expr(&mut self, n: &CallExpr) {
+    if let Callee::Expr(callee) = &n.callee {
+      if let Expr::Ident(ident) = &**callee {
+        if ident.sym == *"createSnapshotInstance" {
+          self.has_jsx = true;
+          return;
+        }
+      }
+    }
+    n.visit_children_with(self);
+  }
packages/react/transform/swc-plugin-reactlynx-compat/index.d.ts (3)

62-65: Deduplicate the repeated target union with a shared alias.

Improves maintainability and consistency with the Rust enum.

Within these ranges, replace the inline union with an alias:

-  target: 'LEPUS' | 'JS' | 'MIXED';
+  target: TransformTarget;

Add once near the top (outside these ranges):

export type TransformTarget = 'LEPUS' | 'JS' | 'MIXED';

Also applies to: 197-206, 218-218


217-220: Align visibility for runtimePkg in Worklet config.

DynamicImportVisitorConfig.runtimePkg is marked @internal but Worklet’s isn’t. Either make it internal too or document it.

   customGlobalIdentNames?: Array<string>;
   /** @internal */
   target: 'LEPUS' | 'JS' | 'MIXED';
-  runtimePkg: string;
+  /** @internal */
+  runtimePkg: string;

60-60: Consider allowing non-string literals in defineDCE.define.

Most define systems accept booleans/numbers. If you intend “as-code” replacement, keep string; otherwise broaden:

-  define: Record<string, string>;
+  define: Record<string, string | number | boolean>;

If kept as string, please add a note clarifying that values must be stringified code.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_transform_event_props_1.js (1)

1-1: Consider making this case distinct from _2 or justify duplication in test name

If the goal is coverage breadth, tweak one snapshot to exercise a different event prop combination; otherwise clarify via a comment in the test that duplication is intentional.

Apply if you want to diversify:

-const a = <view bindtap catchtap/>;
+const a = <view bindtap catchtap catchtouchstart/>;
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_is_import.js (1)

1-3: Prefer named import for __ComponentIsPolyfill to improve tree-shaking

Export is a named const (packages/react/runtime/src/internal.ts:54) — switch to a named import.

-import * as ReactLynx from "@lynx-js/react/internal";
-let c = <ReactLynx.__ComponentIsPolyfill is="xxxx"/>;
+import { __ComponentIsPolyfill } from "@lynx-js/react/internal";
+let c = <__ComponentIsPolyfill is="xxxx"/>;
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_embedded.js (1)

1-2: LGTM — PURE hints present; consider hoisting identical renderers to reduce duplication

Both wrappers use the same (__c)=>{__c} renderer. A future transform optimization could hoist this to a single const to reduce code size. Snapshot is fine as-is.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_handle_recursive.js (1)

1-4: LGTM — good recursive coverage of mixed intrinsic and custom elements

Nice mix of lowercase intrinsic (view/scroll-view) and capitalized View with different event props.

Optionally add a captured event (e.g., capturetap) in one node to cover that branch too.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element.js (1)

2-3: Duplicate snapshot entries — intentional?

Lines 2 and 3 are identical. If not meant to probe idempotency, consider removing one to reduce noise.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_compiler_only.js (1)

9-10: Potential duplicate snapshot lines.

Lines 9 and 10 are identical. If not deliberate, drop one to avoid brittle snapshots.

packages/react/transform/swc-plugin-reactlynx-compat/build.js (1)

21-27: Improve error reporting if the wasm artifact is missing.

Wrap copy in a try/catch to surface a clear message that points to the expected path.

-await fs.copyFile(
+try {
+  await fs.copyFile(
     path.resolve(
       __dirname,
       '../../../../target/wasm32-wasip1/release/swc_plugin_reactlynx_compat.wasm',
     ),
     path.resolve(__dirname, 'swc_plugin_reactlynx_compat.wasm'),
-);
+  );
+} catch (err) {
+  console.error('Failed to locate swc_plugin_reactlynx_compat.wasm. Did the Wasi build succeed?');
+  throw err;
+}
packages/react/transform/swc-plugin-reactlynx-compat/package.json (1)

1-22: Declare Node engine to match ESM + top-level await usage.

build.js relies on ESM and top-level await; pin an engine for deterministic CI.

   "scripts": {
     "build": "node ./build.js",
     "test:cargo": "cargo test"
-  }
+  },
+  "engines": {
+    "node": ">=18.17"
+  }

Given the package is "private": true, no changeset is required (per team learning), but ensure workspace tooling picks up the new package.

packages/react/transform/swc-plugin-reactlynx-compat/src/target.rs (2)

12-27: Consider also implementing Serialize/Display (optional).

Adding Serialize and Display/FromStr can simplify logging and CLI/config plumbing; not required here.

-#[derive(Debug, PartialEq, Clone, Copy)]
+#[derive(Debug, PartialEq, Clone, Copy, serde::Serialize)]
 pub enum TransformTarget {

22-25: Error message is fine; optional: list allowed values.

If you want a more actionable error, include the valid variants in the message (will require updating the test).

packages/react/transform/swc-plugin-reactlynx-compat/src/lib.rs (2)

849-852: Precompile per-pass regexes to reduce hot-path allocations.

Regex::new(...) inside attr loops is expensive. Hoist to statics.

Add once statics near the top:

static EVENT_ATTR_RE: Lazy<Regex> =
  Lazy::new(|| Regex::new(r"^(global-bind|bind|catch|capture-bind|capture-catch)([A-Za-z]+)$").unwrap());
static DATA_ATTR_RE:  Lazy<Regex> =
  Lazy::new(|| Regex::new(r"^data-([A-Za-z]+)$").unwrap());

Then replace the in-loop constructions with EVENT_ATTR_RE.is_match(...) and DATA_ATTR_RE.is_match(...).

Also applies to: 876-879


399-400: Remove unused state to avoid warnings.

is_old_runtime_pkg is set but never read.

-  is_old_runtime_pkg: bool,

And drop related assignments.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ad38e6 and 21b92f5.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (44)
  • .changeset/deep-ideas-nail.md (1 hunks)
  • .gitignore (1 hunks)
  • CODEOWNERS (1 hunks)
  • Cargo.toml (1 hunks)
  • biome.jsonc (1 hunks)
  • eslint.config.js (2 hunks)
  • packages/react/.dprint.jsonc (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/.cargo/config.toml (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/.gitignore (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/Cargo.toml (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/build.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/index.d.ts (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/package.json (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/src/is_component_class.rs (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/src/lib.rs (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/src/simplify_ctor_like_react_lynx_2.rs (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/src/target.rs (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_compiler_only.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_embedded.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_embedded_compiler_only.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_is_import.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_change_runtime_pkg.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_handle_recursive.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_left_no_remove_component_element_attr.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_handle_jsx_member_expression.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_in_scope.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_redeclaration.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_remove_component_attr_regex.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_rename_view.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_correct_order.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_macro.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_spread.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_transform_event_props_1.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_transform_event_props_2.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/turbo.json (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx/.cargo/config.toml (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx/.gitignore (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx/Cargo.toml (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx/build.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx/package.json (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx/src/lib.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
.changeset/*.md

📄 CodeRabbit inference engine (AGENTS.md)

For contributions, always generate a changeset and commit the resulting markdown file(s)

Files:

  • .changeset/deep-ideas-nail.md
🧠 Learnings (14)
📓 Common learnings
Learnt from: gaoachao
PR: lynx-family/lynx-stack#1736
File: .changeset/spotty-experts-smoke.md:1-3
Timestamp: 2025-09-12T09:43:04.810Z
Learning: In the lynx-family/lynx-stack repository, private packages (marked with "private": true in package.json) like lynx-js/react-transform don't require meaningful changeset entries even when their public APIs change, since they are not published externally and only affect internal development.
Learnt from: gaoachao
PR: lynx-family/lynx-stack#1714
File: packages/react/transform/Cargo.toml:19-19
Timestamp: 2025-09-10T11:42:36.855Z
Learning: In packages/react/transform/Cargo.toml, the crate uses serde derive macros (#[derive(Serialize, Deserialize)]) in multiple files including src/esbuild.rs and src/swc_plugin_extract_str/mod.rs, so the "derive" feature is required when migrating to workspace dependencies.
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1558
File: .changeset/solid-squids-fall.md:2-2
Timestamp: 2025-08-19T11:25:36.127Z
Learning: In the lynx-family/lynx-stack repository, changesets should use the exact package name from package.json#name, not generic or unscoped names. Each package has its own specific scoped name (e.g., "lynx-js/react-transform" for packages/react/transform).
📚 Learning: 2025-09-12T09:43:04.810Z
Learnt from: gaoachao
PR: lynx-family/lynx-stack#1736
File: .changeset/spotty-experts-smoke.md:1-3
Timestamp: 2025-09-12T09:43:04.810Z
Learning: In the lynx-family/lynx-stack repository, private packages (marked with "private": true in package.json) like lynx-js/react-transform don't require meaningful changeset entries even when their public APIs change, since they are not published externally and only affect internal development.

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx/.gitignore
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_is_import.js
  • packages/react/transform/swc-plugin-reactlynx/package.json
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_embedded.js
  • Cargo.toml
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_change_runtime_pkg.js
  • packages/react/transform/swc-plugin-reactlynx-compat/.gitignore
  • packages/react/transform/swc-plugin-reactlynx/Cargo.toml
  • packages/react/transform/swc-plugin-reactlynx-compat/Cargo.toml
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element.js
  • packages/react/transform/swc-plugin-reactlynx-compat/package.json
📚 Learning: 2025-09-12T09:43:04.810Z
Learnt from: gaoachao
PR: lynx-family/lynx-stack#1736
File: .changeset/spotty-experts-smoke.md:1-3
Timestamp: 2025-09-12T09:43:04.810Z
Learning: In the lynx-family/lynx-stack repository, empty changeset files (containing only `---\n\n---`) are used for internal changes that modify src/** files but don't require meaningful release notes, such as private package changes or testing-only modifications. This satisfies CI requirements without generating user-facing release notes.

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx/.gitignore
  • packages/react/transform/swc-plugin-reactlynx/package.json
  • packages/react/transform/swc-plugin-reactlynx/src/lib.rs
  • packages/react/transform/swc-plugin-reactlynx-compat/.gitignore
  • .changeset/deep-ideas-nail.md
📚 Learning: 2025-07-22T09:26:16.722Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1330
File: .changeset/olive-animals-attend.md:1-3
Timestamp: 2025-07-22T09:26:16.722Z
Learning: In the lynx-family/lynx-stack repository, CI checks require changesets when files matching the pattern "src/**" are modified (as configured in .changeset/config.json). For internal changes that don't need meaningful changesets, an empty changeset file is used to satisfy the CI requirement while not generating any release notes.

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx/.gitignore
  • packages/react/transform/swc-plugin-reactlynx-compat/.gitignore
  • .changeset/deep-ideas-nail.md
📚 Learning: 2025-09-10T11:42:36.855Z
Learnt from: gaoachao
PR: lynx-family/lynx-stack#1714
File: packages/react/transform/Cargo.toml:19-19
Timestamp: 2025-09-10T11:42:36.855Z
Learning: In packages/react/transform/Cargo.toml, the crate uses serde derive macros (#[derive(Serialize, Deserialize)]) in multiple files including src/esbuild.rs and src/swc_plugin_extract_str/mod.rs, so the "derive" feature is required when migrating to workspace dependencies.

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx-compat/.cargo/config.toml
  • packages/react/transform/swc-plugin-reactlynx/.cargo/config.toml
  • Cargo.toml
  • packages/react/transform/swc-plugin-reactlynx/Cargo.toml
  • packages/react/transform/swc-plugin-reactlynx-compat/Cargo.toml
  • packages/react/transform/swc-plugin-reactlynx-compat/src/target.rs
  • packages/react/transform/swc-plugin-reactlynx-compat/turbo.json
📚 Learning: 2025-08-27T12:42:01.095Z
Learnt from: upupming
PR: lynx-family/lynx-stack#1616
File: packages/webpack/cache-events-webpack-plugin/test/cases/not-cache-events/lazy-bundle/index.js:3-3
Timestamp: 2025-08-27T12:42:01.095Z
Learning: In webpack, properties like __webpack_require__.lynx_ce are injected during compilation/build time when webpack processes modules and generates bundles, not at runtime when dynamic imports execute. Tests for such properties don't need to wait for dynamic imports to complete.

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_is_import.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_change_runtime_pkg.js
  • packages/react/transform/swc-plugin-reactlynx-compat/build.js
  • packages/react/transform/swc-plugin-reactlynx/build.js
📚 Learning: 2025-08-12T16:09:32.413Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1497
File: packages/react/transform/tests/__swc_snapshots__/src/swc_plugin_snapshot/mod.rs/basic_full_static.js:9-10
Timestamp: 2025-08-12T16:09:32.413Z
Learning: In the Lynx stack, functions prefixed with `__` that are called in transformed code may be injected globally by the Lynx Engine at runtime rather than exported from the React runtime package. For example, `__CreateFrame` is injected globally by the Lynx Engine, not exported from lynx-js/react.

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_is_import.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_embedded.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_change_runtime_pkg.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_spread.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element.js
📚 Learning: 2025-08-13T09:20:00.936Z
Learnt from: upupming
PR: lynx-family/lynx-stack#1502
File: packages/react/testing-library/types/entry.d.ts:71-71
Timestamp: 2025-08-13T09:20:00.936Z
Learning: In lynx-js/react testing library, wrapper components must have children as a required prop because they are always called with `h(WrapperComponent, null, innerElement)` where innerElement is passed as children. The type `React.JSXElementConstructor<{ children: React.ReactNode }>` correctly requires children to be mandatory.

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_is_import.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_remove_component_attr_regex.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_embedded.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_change_runtime_pkg.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_embedded_compiler_only.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_handle_recursive.js
📚 Learning: 2025-08-06T13:28:57.182Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1453
File: vitest.config.ts:49-61
Timestamp: 2025-08-06T13:28:57.182Z
Learning: In the lynx-family/lynx-stack repository, the file `packages/react/testing-library/src/vitest.config.js` is source code for the testing library that gets exported for users, not a test configuration that should be included in the main vitest projects array.

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_is_import.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_handle_jsx_member_expression.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element.js
  • eslint.config.js
📚 Learning: 2025-08-19T11:25:36.127Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1558
File: .changeset/solid-squids-fall.md:2-2
Timestamp: 2025-08-19T11:25:36.127Z
Learning: In the lynx-family/lynx-stack repository, changesets should use the exact package name from package.json#name, not generic or unscoped names. Each package has its own specific scoped name (e.g., "lynx-js/react-transform" for packages/react/transform).

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx/package.json
  • packages/react/transform/swc-plugin-reactlynx-compat/package.json
📚 Learning: 2025-08-21T07:21:51.621Z
Learnt from: upupming
PR: lynx-family/lynx-stack#1562
File: packages/react/transform/src/swc_plugin_snapshot/jsx_helpers.rs:261-283
Timestamp: 2025-08-21T07:21:51.621Z
Learning: In packages/react/transform/src/swc_plugin_snapshot/jsx_helpers.rs, the team prefers to keep the original unreachable! logic for JSXSpreadChild in jsx_is_children_full_dynamic function rather than implementing defensive error handling.

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_handle_jsx_member_expression.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_remove_component_attr_regex.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_spread.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_left_no_remove_component_element_attr.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_embedded_compiler_only.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_handle_recursive.js
  • packages/react/transform/swc-plugin-reactlynx-compat/src/is_component_class.rs
  • packages/react/transform/swc-plugin-reactlynx-compat/src/lib.rs
📚 Learning: 2025-08-11T05:59:28.530Z
Learnt from: upupming
PR: lynx-family/lynx-stack#1305
File: packages/react/testing-library/src/plugins/vitest.ts:4-6
Timestamp: 2025-08-11T05:59:28.530Z
Learning: In the lynx-family/lynx-stack repository, the `packages/react/testing-library` package does not have `vite` as a direct dependency. It relies on `vitest` being available from the monorepo root and accesses Vite types through re-exports from `vitest/node`. Direct imports from `vite` should not be suggested for this package.

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_change_runtime_pkg.js
📚 Learning: 2025-08-11T05:57:18.212Z
Learnt from: upupming
PR: lynx-family/lynx-stack#1305
File: packages/testing-library/testing-environment/src/index.ts:255-258
Timestamp: 2025-08-11T05:57:18.212Z
Learning: In the ReactLynx testing environment (`packages/testing-library/testing-environment/src/index.ts`), the dual assignment pattern `target.console.method = console.method = () => {}` is required for rstest compatibility. This is because rstest provides `console` in an IIFE (Immediately Invoked Function Expression), and both the target and global console need to have these methods defined for proper test execution.

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element.js
📚 Learning: 2025-08-06T13:28:57.182Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1453
File: vitest.config.ts:49-61
Timestamp: 2025-08-06T13:28:57.182Z
Learning: In the lynx-family/lynx-stack repository, the file `packages/rspeedy/create-rspeedy/template-react-vitest-rltl-js/vitest.config.js` is a template file for scaffolding new Rspeedy projects, not a test configuration that should be included in the main vitest projects array.

Applied to files:

  • eslint.config.js
🧬 Code graph analysis (15)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_is_import.js (1)
packages/rspeedy/plugin-react/test/fixtures/special-var-name/ReactLynx/index.js (1)
  • ReactLynx (1-3)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_handle_jsx_member_expression.js (2)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_in_scope.js (4)
  • a (3-3)
  • a (5-5)
  • a (6-6)
  • View (2-2)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_rename_view.js (1)
  • a (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_remove_component_attr_regex.js (1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_is_import.js (1)
  • c (3-3)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_spread.js (3)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_remove_component_attr_regex.js (1)
  • c (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls.js (1)
  • c (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_macro.js (1)
  • c (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_rename_view.js (4)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_handle_recursive.js (1)
  • a (1-4)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_in_scope.js (3)
  • a (3-3)
  • a (5-5)
  • a (6-6)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_transform_event_props_1.js (1)
  • a (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_transform_event_props_2.js (1)
  • a (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/build.js (1)
packages/react/transform/swc-plugin-reactlynx/build.js (2)
  • __filename (10-10)
  • __dirname (11-11)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_embedded_compiler_only.js (1)
packages/react/transform/src/wasm.js (1)
  • view (24-28)
packages/react/transform/swc-plugin-reactlynx/build.js (1)
packages/react/transform/swc-plugin-reactlynx-compat/build.js (2)
  • __filename (10-10)
  • __dirname (11-11)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_redeclaration.js (1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_in_scope.js (4)
  • View (2-2)
  • a (3-3)
  • a (5-5)
  • a (6-6)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_in_scope.js (4)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_handle_recursive.js (1)
  • a (1-4)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_handle_jsx_member_expression.js (1)
  • a (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_redeclaration.js (1)
  • a (3-3)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_rename_view.js (1)
  • a (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_handle_recursive.js (6)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_handle_jsx_member_expression.js (1)
  • a (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_in_scope.js (4)
  • a (3-3)
  • a (5-5)
  • a (6-6)
  • View (2-2)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_redeclaration.js (1)
  • a (3-3)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_rename_view.js (1)
  • a (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_transform_event_props_1.js (1)
  • a (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_transform_event_props_2.js (1)
  • a (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_transform_event_props_1.js (2)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_handle_recursive.js (1)
  • a (1-4)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_transform_event_props_2.js (1)
  • a (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_transform_event_props_2.js (1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_handle_recursive.js (1)
  • a (1-4)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls.js (4)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_is_import.js (1)
  • c (3-3)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_remove_component_attr_regex.js (1)
  • c (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_macro.js (1)
  • c (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_spread.js (1)
  • c (1-1)
packages/react/transform/swc-plugin-reactlynx-compat/src/lib.rs (3)
packages/react/transform/swc-plugin-reactlynx-compat/src/is_component_class.rs (1)
  • new (12-18)
packages/react/transform/swc-plugin-reactlynx-compat/src/simplify_ctor_like_react_lynx_2.rs (1)
  • new (21-30)
packages/react/transform/index.d.ts (1)
  • DarkModeConfig (47-53)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build / Build (Ubuntu)
  • GitHub Check: build / Build (Windows)
  • GitHub Check: test-rust / Test (Ubuntu)
🔇 Additional comments (26)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_left_no_remove_component_element_attr.js (1)

1-1: Verify snapshot matches test intent (attributes preserved).

Test name suggests component element attributes should be preserved, but the snapshot shows <Component/>; with no attrs. Please confirm the input actually contains attributes and the transform is a no-op for them. If yes, the snapshot should include those attrs; otherwise, consider renaming the test for clarity.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_remove_component_attr_regex.js (2)

2-3: LGTM: component vs host coverage looks correct

Snapshot shows attrs removed on components (<A/>, <A.B/>, and with spread <A {...x}/>), and retained on host (<view .../>). This matches the intent of “remove component attr regex.”

Also applies to: 6-6, 7-7


4-4: Edit test source comment and regenerate snapshot

Update the FIXME in packages/react/transform/swc-plugin-reactlynx-compat/src/lib.rs (around line 1841) and re-run tests — do not edit the generated snapshot.

-// FIXME: the `is_component` check is align with previous version of ReactLynx for compat reason
+// FIXME: the `is_component` check is aligned with the previous ReactLynx behavior for compatibility
CODEOWNERS (1)

4-4: LGTM.

Scoping ownership of packages/react/transform/** to @gaoachao is clear and won’t affect existing patterns. If multiple owners are desired, append them here.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_handle_jsx_member_expression.js (1)

1-1: Snapshot addition looks correct.

Covers JSXMemberExpression; good guard to ensure no unintended rename/transform.

packages/react/transform/swc-plugin-reactlynx/.gitignore (1)

1-2: Ignore patterns are appropriate.

dist and top-level .wasm artifacts should be untracked for this package.

.gitignore (1)

10-10: Do not change .swc**/.swc; .swc already ignores nested .swc directories.
Git ignore patterns without a slash match names at any depth; root .gitignore already contains .swc (line 10) and a repo scan found no .swc dirs to add.

Likely an incorrect or invalid review comment.

packages/react/transform/swc-plugin-reactlynx-compat/.gitignore (1)

1-2: Ignore patterns are appropriate.

Matches the build outputs for this package.

.changeset/deep-ideas-nail.md (1)

1-3: ```shell
#!/bin/bash
set -euo pipefail

echo "=== Git branch ==="
git rev-parse --abbrev-ref HEAD || true
echo

BASE=$(git merge-base origin/main HEAD 2>/dev/null || git merge-base HEAD main 2>/dev/null || true)
echo "=== Merge base ==="
echo "$BASE"
echo

echo "=== Changed files (name-status) since merge base ==="
if [ -n "$BASE" ]; then
git diff --name-status "$BASE"..HEAD || true
else
git diff --name-status HEAD~10..HEAD || true
fi
echo

echo "=== Changed files (names) ==="
if [ -n "$BASE" ]; then
git diff --name-only "$BASE"..HEAD || true
else
git diff --name-only HEAD~10..HEAD || true
fi
echo

echo "=== .changeset/deep-ideas-nail.md content (if exists) ==="
if [ -f ".changeset/deep-ideas-nail.md" ]; then
sed -n '1,200p' .changeset/deep-ideas-nail.md
else
echo "MISSING"
fi
echo

echo "=== Listing .changeset directory ==="
ls -la .changeset || true
echo

echo "=== package.json files touched by this PR (in changed files) ==="
if [ -n "$BASE" ]; then
git diff --name-only "$BASE"..HEAD | rg '^(./)?package.json$' -n || echo "none"
else
git diff --name-only HEAD~10..HEAD | rg '^(.
/)?package.json$' -n || echo "none"
fi
echo

echo "=== All package.json files in repo (show name, private, version) ==="
if command -v fd >/dev/null 2>&1; then
PKGS=$(fd --hidden --type f 'package.json' || true)
else
PKGS=$(rg --files --hidden -g '!node_modules' 'package.json' || true)
fi

for f in $PKGS; do
echo "---- $f ----"
if command -v jq >/dev/null 2>&1; then
jq '{name: .name, private: .private, version: .version}' "$f" || cat "$f"
else
rg '"name"|"private"|"version"' -n "$f" || cat "$f"
fi
done
echo

echo "=== Search repo for 'swc-plugin' or '@lynx-js' ==="
rg -n --hidden 'swc-plugin' || true
rg -n --hidden '@lynx-js' || true
echo

echo "=== Show package.json for any packages whose name contains 'swc-plugin' ==="
if command -v jq >/dev/null 2>&1; then
for f in $PKGS; do
name=$(jq -r '.name // empty' "$f" 2>/dev/null || true)
if [ -n "$name" ] && echo "$name" | rg -i 'swc-plugin' -q 2>/dev/null; then
echo "---- $f ----"
jq '{name: .name, private: .private, version: .version, description: .description}' "$f" || true
fi
done
fi


</blockquote></details>
<details>
<summary>Cargo.toml (1)</summary><blockquote>

`22-22`: **Workspace already enables serde derive — no change needed.**

packages/react/transform/Cargo.toml already contains `serde = { workspace = true, features = ["derive"] }`; member crates (packages/react/transform/swc-plugin-reactlynx-compat/Cargo.toml, packages/react/transform/swc-plugin-reactlynx/Cargo.toml) use `serde = { workspace = true }` and will inherit the derive feature.

</blockquote></details>
<details>
<summary>packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_redeclaration.js (1)</summary><blockquote>

`1-4`: **LGTM: covers alias-based non-rename scenario.**

</blockquote></details>
<details>
<summary>packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_rename_view.js (1)</summary><blockquote>

`1-1`: **LGTM: minimal snapshot for rename case.**

</blockquote></details>
<details>
<summary>packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_in_scope.js (1)</summary><blockquote>

`1-6`: **LGTM: exercises scoped capitalized vs lowercase elements.**

</blockquote></details>
<details>
<summary>packages/react/transform/swc-plugin-reactlynx-compat/src/is_component_class.rs (1)</summary><blockquote>

`28-50`: ```shell
#!/bin/bash
set -euo pipefail
# Find the target file
file=$(rg --hidden --no-ignore --files | rg 'is_component_class.rs' || true)
if [ -z "$file" ]; then
  echo "FILE_NOT_FOUND"
  echo "Searching for related symbols..."
  rg -n --hidden --no-ignore -S 'struct\s+IsComponentClass|has_render_method|has_super_class|visit_class' || true
  exit 0
fi

echo "FOUND:$file"
echo "----- FILE (head) -----"
sed -n '1,240p' "$file" || true
echo "----- FILE (rest) -----"
sed -n '241,9999p' "$file" || true

echo "----- USAGES -----"
rg -n --hidden --no-ignore -S 'IsComponentClass|has_render_method|has_super_class' || true
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_change_runtime_pkg.js (1)

1-1: LGTM — snapshot reflects runtime remap; confirm subpath exists

Import path matches the legacy runtime mapping; repository search for '"legacy-react-runtime"' in package.json files produced no matches — confirm the package exports the "legacy-react-runtime" subpath or update the test fixture.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor.js (1)

1-9: LGTM — snapshot captures ctor simplification shape.

Matches expectations for class fields and minimal render.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls.js (1)

1-9: LGTM — ensures local decls don’t linger in state init.

Good targeted coverage for the “remain local decls” case.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_transform_event_props_2.js (1)

1-1: Duplicate snapshot — confirm intent or diversify

These snapshots are identical:

  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_transform_event_props_1.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_transform_event_props_2.js

If they should exercise different inputs, change one (e.g., use a different event like catchtouchstart or a non-boolean value); otherwise collapse into a single test.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_embedded_compiler_only.js (1)

1-1: LGTM — matches “compiler_only” expectation (no runtime wrapper)

Snapshot shows pure element nesting, consistent with a compiler-only pathway. Confirm the paired “embedded” test covers the runtime-wrapped variant.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_macro.js (1)

6-18: Macro identifiers look inconsistent — confirm spellings are intentional

rg finds both LEPUS and LE_P_US only in the snapshot file packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_macro.js; if unintentional, unify to a single macro in the source and regenerate the snapshot; if intentional, add a short comment in the test explaining the distinction.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_correct_order.js (1)

5-9: Snapshot correctly asserts last-write-wins for duplicate keys.

The duplicate "a" property verifies ordering semantics; keeping only the last value is correct for this case.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element.js (1)

7-10: Key stays on Component while other props move to wrapper — good.

This validates the intended split: key remains on , while style/className/id flow to the wrapper.

packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_spread.js (1)

6-11: Covers spread + shorthand mix in state initializer.

Good coverage to ensure locals used via shorthand (c) remain and spreads are preserved in order.

packages/react/transform/swc-plugin-reactlynx-compat/src/lib.rs (3)

520-566: Import handling and deprecation warnings look good.

Changing old runtime imports to ${new_runtime_pkg}/legacy-react-runtime and warning users aligns with the compat goals.


684-704: Target element detection logic is sound.

Covers both intrinsic lowercase tags and identifiers imported from components pkg; matches existing tests.


491-494: Bug: Option doesn’t have add_pure_comment; guard the call.

This will not compile as Option<C> doesn’t implement Comments. Use as_ref() and call when present.

-        self.comments.add_pure_comment(pure_span.lo);
+        if let Some(c) = self.comments.as_ref() {
+          c.add_pure_comment(pure_span.lo);
+        }

Likely an incorrect or invalid review comment.

Comment on lines +777 to +793
if self.opts.target == TransformTarget::LEPUS {
if let Some(remove_component_attr_regex) = &self.opts.remove_component_attr_regex {
let re = regex::Regex::new(remove_component_attr_regex).unwrap();
n.opening.attrs.retain(|p| {
if let JSXAttrOrSpread::JSXAttr(JSXAttr {
name: JSXAttrName::Ident(IdentName { sym, .. }),
..
}) = p
{
if re.is_match(sym.as_ref()) {
return false;
}
}
true
});
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid unwrap on user-provided regex.

A bad removeComponentAttrRegex will panic the transform. Validate and warn instead.

-        if let Some(remove_component_attr_regex) = &self.opts.remove_component_attr_regex {
-          let re = regex::Regex::new(remove_component_attr_regex).unwrap();
+        if let Some(remove_component_attr_regex) = &self.opts.remove_component_attr_regex {
+          let re = match regex::Regex::new(remove_component_attr_regex) {
+            Ok(r) => r,
+            Err(e) => {
+              if !self.opts.disable_deprecated_warning {
+                HANDLER.with(|handler| {
+                  handler
+                    .struct_span_warn(n.span, format!("Invalid removeComponentAttrRegex: {e}").as_str())
+                    .emit()
+                });
+              }
+              return;
+            }
+          };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if self.opts.target == TransformTarget::LEPUS {
if let Some(remove_component_attr_regex) = &self.opts.remove_component_attr_regex {
let re = regex::Regex::new(remove_component_attr_regex).unwrap();
n.opening.attrs.retain(|p| {
if let JSXAttrOrSpread::JSXAttr(JSXAttr {
name: JSXAttrName::Ident(IdentName { sym, .. }),
..
}) = p
{
if re.is_match(sym.as_ref()) {
return false;
}
}
true
});
}
}
if self.opts.target == TransformTarget::LEPUS {
if let Some(remove_component_attr_regex) = &self.opts.remove_component_attr_regex {
let re = match regex::Regex::new(remove_component_attr_regex) {
Ok(r) => r,
Err(e) => {
if !self.opts.disable_deprecated_warning {
HANDLER.with(|handler| {
handler
.struct_span_warn(n.span, format!("Invalid removeComponentAttrRegex: {e}").as_str())
.emit()
});
}
return;
}
};
n.opening.attrs.retain(|p| {
if let JSXAttrOrSpread::JSXAttr(JSXAttr {
name: JSXAttrName::Ident(IdentName { sym, .. }),
..
}) = p
{
if re.is_match(sym.as_ref()) {
return false;
}
}
true
});
}
}

Comment on lines +164 to +171
impl Visit for LocalDeclsDetectVisitor<'_> {
fn visit_ident(&mut self, _n: &Ident) {
if let Some(bindings) = self.bindings {
if bindings.contains(&_n.to_id()) {
self.has = true;
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

❓ Verification inconclusive

Avoid false positives from object literal keys.

Identifiers used as property keys (e.g., { foo: 1 }) aren’t references to local bindings. Skip visiting keys to prevent accidentally dropping valid props.

 impl Visit for LocalDeclsDetectVisitor<'_> {
+  fn visit_prop_name(&mut self, _n: &PropName) {
+    // Intentionally ignore keys; only values/spreads should influence `has`.
+  }
   fn visit_ident(&mut self, _n: &Ident) {
     if let Some(bindings) = self.bindings {
       if bindings.contains(&_n.to_id()) {
         self.has = true;
       }
     }
   }
 }

Please add/confirm a test where the constructor declares let foo = 1; but state has { foo: 123 } (as a literal key). The prop should be retained.


Skip object-literal property keys in LocalDeclsDetectVisitor

Override visit_prop_name so identifier keys (e.g. { foo: 1 }) don't count as uses of local bindings.

File: packages/react/transform/swc-plugin-reactlynx-compat/src/simplify_ctor_like_react_lynx_2.rs (lines 164-171)

 impl Visit for LocalDeclsDetectVisitor<'_> {
+  fn visit_prop_name(&mut self, _n: &PropName) {
+    // Intentionally ignore keys; only values/spreads should influence `has`.
+  }
   fn visit_ident(&mut self, _n: &Ident) {
     if let Some(bindings) = self.bindings {
       if bindings.contains(&_n.to_id()) {
         self.has = true;
       }
     }
   }
 }

Add/confirm a test where the constructor declares let foo = 1; but state uses a literal key { foo: 123 } — the prop must be retained.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
impl Visit for LocalDeclsDetectVisitor<'_> {
fn visit_ident(&mut self, _n: &Ident) {
if let Some(bindings) = self.bindings {
if bindings.contains(&_n.to_id()) {
self.has = true;
}
}
}
impl Visit for LocalDeclsDetectVisitor<'_> {
fn visit_prop_name(&mut self, _n: &PropName) {
// Intentionally ignore keys; only values/spreads should influence `has`.
}
fn visit_ident(&mut self, _n: &Ident) {
if let Some(bindings) = self.bindings {
if bindings.contains(&_n.to_id()) {
self.has = true;
}
}
}
}

Comment on lines 5 to 9
"build": {
"dependsOn": [],
"inputs": ["src/**/*.rs", "Cargo.toml", "build.js"],
"outputs": ["swc_plugin_reactlynx_compat.wasm"]
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Include Cargo.lock and .cargo config in task inputs for correct cache invalidation

When Rust deps or cargo config change, the build should rerun. Add Cargo.lock and .cargo/** to inputs.

Apply:

     "build": {
-      "dependsOn": [],
-      "inputs": ["src/**/*.rs", "Cargo.toml", "build.js"],
+      "dependsOn": [],
+      "inputs": ["src/**/*.rs", "Cargo.toml", "Cargo.lock", ".cargo/**", "build.js"],
       "outputs": ["swc_plugin_reactlynx_compat.wasm"]
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"build": {
"dependsOn": [],
"inputs": ["src/**/*.rs", "Cargo.toml", "build.js"],
"outputs": ["swc_plugin_reactlynx_compat.wasm"]
}
"build": {
"dependsOn": [],
"inputs": ["src/**/*.rs", "Cargo.toml", "Cargo.lock", ".cargo/**", "build.js"],
"outputs": ["swc_plugin_reactlynx_compat.wasm"]
}
🤖 Prompt for AI Agents
In packages/react/transform/swc-plugin-reactlynx-compat/turbo.json around lines
5 to 9, the build task's inputs omit Cargo.lock and .cargo/** which can change
dependency resolution and cargo config; update the "inputs" array to include
"Cargo.lock" and ".cargo/**" so changes to the lockfile or cargo config
invalidate cache and trigger rebuilds.

Comment on lines 17 to 18
serde = { workspace = true }
serde_json = { workspace = true }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enable serde derive feature.

Mirror the react/transform workspace requirement to prevent missing macro errors at compile time.

-serde = { workspace = true }
+serde = { workspace = true, features = ["derive"] }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
serde = { workspace = true }
serde_json = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
🤖 Prompt for AI Agents
In packages/react/transform/swc-plugin-reactlynx/Cargo.toml around lines 17-18,
the serde dependency must enable the derive feature to match the workspace
react/transform requirement; update the serde entry to include features =
["derive"] (e.g. serde = { workspace = true, features = ["derive"] }) so the
serde_derive macros are available at compile time while leaving serde_json as
workspace = true.

@codspeed-hq
Copy link

codspeed-hq bot commented Sep 12, 2025

CodSpeed Performance Report

Merging #1744 will degrade performances by 5.81%

Comparing refactor/add-swc-plugin-reactlynx-compat (0fb02d3) with main (588819b)

🎉 Hooray! codspeed-cpp just leveled up to 1.2.0!

A heads-up, this is a breaking change and it might affect your current performance baseline a bit. But here's the exciting part - it's packed with new, cool features and promises improved result stability 🥳!
Curious about what's new? Visit our releases page to delve into all the awesome details about this new version.

Summary

❌ 2 regressions
✅ 54 untouched

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Benchmarks breakdown

Benchmark BASE HEAD Change
transform 1000 effects 30.6 ms 32.5 ms -5.81%
basic-performance-large-css 10.5 ms 11.1 ms -5.48%

@gaoachao gaoachao force-pushed the refactor/add-swc-plugin-reactlynx-compat branch from 21b92f5 to 62a1447 Compare September 15, 2025 06:41
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
packages/react/transform/swc-plugin-reactlynx-compat/src/simplify_ctor_like_react_lynx_2.rs (1)

164-171: Do not treat object-literal keys as identifier uses (prevents false positives).

Keys like { foo: 1 } should not mark foo as a local use. Skip visiting property names in LocalDeclsDetectVisitor.

 impl Visit for LocalDeclsDetectVisitor<'_> {
+  fn visit_prop_name(&mut self, _n: &PropName) {
+    // Ignore keys; only values/spreads should influence `has`.
+  }
   fn visit_ident(&mut self, _n: &Ident) {
     if let Some(bindings) = self.bindings {
       if bindings.contains(&_n.to_id()) {
         self.has = true;
       }
     }
   }
 }
packages/react/transform/swc-plugin-reactlynx-compat/src/lib.rs (1)

777-793: Avoid unwrap on user-provided regex (graceful failure + warning).

Invalid removeComponentAttrRegex will panic. Validate and warn instead; then skip removal.

-        if let Some(remove_component_attr_regex) = &self.opts.remove_component_attr_regex {
-          let re = regex::Regex::new(remove_component_attr_regex).unwrap();
+        if let Some(remove_component_attr_regex) = &self.opts.remove_component_attr_regex {
+          let re = match regex::Regex::new(remove_component_attr_regex) {
+            Ok(r) => r,
+            Err(e) => {
+              if !self.opts.disable_deprecated_warning {
+                HANDLER.with(|handler| {
+                  handler
+                    .struct_span_warn(n.span, format!("Invalid removeComponentAttrRegex: {e}").as_str())
+                    .emit()
+                });
+              }
+              return;
+            }
+          };
🧹 Nitpick comments (5)
packages/react/transform/swc-plugin-reactlynx-compat/src/target.rs (3)

5-10: Provide a Default for TransformTarget (unblocks serde field defaults).

Add Default to make LEPUS the implicit target when omitted in config.

 #[derive(Debug, PartialEq, Clone, Copy)]
 pub enum TransformTarget {
   LEPUS,
   JS,
   MIXED,
 }
+
+impl Default for TransformTarget {
+  fn default() -> Self {
+    TransformTarget::LEPUS
+  }
+}

12-26: Consider deriving serde instead of a manual Deserialize impl.

Deriving with rename keeps behavior but simplifies code and avoids custom string plumbing. If you adopt this, adjust the unknown-variant test to assert is_err() rather than matching the exact message.

-impl<'de> Deserialize<'de> for TransformTarget {
-  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-  where
-    D: Deserializer<'de>,
-  {
-    let s = String::deserialize(deserializer)?;
-    match s.as_str() {
-      "LEPUS" => Ok(TransformTarget::LEPUS),
-      "JS" => Ok(TransformTarget::JS),
-      "MIXED" => Ok(TransformTarget::MIXED),
-      _ => Err(serde::de::Error::custom(format!(
-        "value `{s}` does not match any variant of TransformTarget"
-      ))),
-    }
-  }
-}
+#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone, Copy)]
+#[serde(rename_all = "UPPERCASE")]
+pub enum TransformTarget {
+  LEPUS,
+  JS,
+  MIXED,
+}

54-66: Test asserts shouldn’t depend on exact serde error strings.

Comparing the full string is brittle across serde versions. Prefer asserting Err(…) and optionally that err.to_string().contains("unknown") if you need signal.

-    if let Err(err) = result {
-      assert_eq!(
-        err.to_string(),
-        "value `unknown` does not match any variant of TransformTarget"
-      );
-    }
+    assert!(result.is_err());
packages/react/transform/swc-plugin-reactlynx-compat/src/simplify_ctor_like_react_lynx_2.rs (1)

36-56: Precompile the regex used in constructors.

Compiling Regex per constructor visit is avoidable. Use a static Lazy to cut overhead in large files.

-      let re = Regex::new(r"^__[A-Z_]+__$").unwrap();
+      static RE_MACRO_IDENT: once_cell::sync::Lazy<Regex> =
+        once_cell::sync::Lazy::new(|| Regex::new(r"^__[A-Z_]+__$").unwrap());
 ...
-          if re.captures(test_ident.sym.as_str()).is_some() {
+          if RE_MACRO_IDENT.is_match(test_ident.sym.as_str()) {
             self.remain_stmts.push(stmt.clone())
           }
packages/react/transform/swc-plugin-reactlynx-compat/src/lib.rs (1)

859-867: Doc vs. code: clarify handling of ref/key.

Comment says “ref and key should still be bound on Component,” which matches code (they are not moved), but it’s implied via omission. Consider a brief comment stating this branch intentionally does not move ref/key.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21b92f5 and 62a1447.

📒 Files selected for processing (29)
  • biome.jsonc (1 hunks)
  • eslint.config.js (2 hunks)
  • packages/react/.dprint.jsonc (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/index.d.ts (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/src/is_component_class.rs (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/src/lib.rs (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/src/simplify_ctor_like_react_lynx_2.rs (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/src/target.rs (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_compiler_only.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_embedded.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_embedded_compiler_only.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_is_import.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_change_runtime_pkg.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_handle_recursive.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_left_no_remove_component_element_attr.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_handle_jsx_member_expression.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_in_scope.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_not_rename_view_redeclaration.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_remove_component_attr_regex.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_rename_view.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_correct_order.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_macro.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_spread.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_transform_event_props_1.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_transform_event_props_2.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/turbo.json (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_rename_view.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_not_rename_view_redeclaration.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_simplify_ctor.js
🚧 Files skipped from review as they are similar to previous changes (22)
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_not_handle_jsx_member_expression.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_transform_event_props_2.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_change_runtime_pkg.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_add_component_element_embedded.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_simplify_ctor_not_remain_local_decls.js
  • biome.jsonc
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_handle_recursive.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_not_rename_view_in_scope.js
  • packages/react/transform/swc-plugin-reactlynx-compat/turbo.json
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_simplify_ctor_correct_order.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_left_no_remove_component_element_attr.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_remove_component_attr_regex.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_spread.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_add_component_element_embedded_compiler_only.js
  • packages/react/transform/swc-plugin-reactlynx-compat/src/is_component_class.rs
  • packages/react/transform/swc-plugin-reactlynx-compat/index.d.ts
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_add_component_element.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_transform_event_props_1.js
  • eslint.config.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_simplify_ctor_not_remain_local_decls_with_macro.js
  • packages/react/transform/swc-plugin-reactlynx-compat/tests/swc_snapshots/src/lib.rs/should_add_component_is_import.js
  • packages/react/.dprint.jsonc
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: upupming
PR: lynx-family/lynx-stack#1370
File: packages/webpack/cache-events-webpack-plugin/src/LynxCacheEventsRuntimeModule.ts:23-27
Timestamp: 2025-08-21T08:46:54.494Z
Learning: In Lynx webpack runtime modules, the team prioritizes performance and simplicity over defensive runtime error handling. They prefer relying on compile-time type safety (TypeScript) rather than adding runtime checks like try-catch blocks or type validation, especially for performance-critical code like cache event setup/cleanup functions.
Learnt from: gaoachao
PR: lynx-family/lynx-stack#1736
File: .changeset/spotty-experts-smoke.md:1-3
Timestamp: 2025-09-12T09:43:04.810Z
Learning: In the lynx-family/lynx-stack repository, private packages (marked with "private": true in package.json) like lynx-js/react-transform don't require meaningful changeset entries even when their public APIs change, since they are not published externally and only affect internal development.
📚 Learning: 2025-09-10T11:42:36.855Z
Learnt from: gaoachao
PR: lynx-family/lynx-stack#1714
File: packages/react/transform/Cargo.toml:19-19
Timestamp: 2025-09-10T11:42:36.855Z
Learning: In packages/react/transform/Cargo.toml, the crate uses serde derive macros (#[derive(Serialize, Deserialize)]) in multiple files including src/esbuild.rs and src/swc_plugin_extract_str/mod.rs, so the "derive" feature is required when migrating to workspace dependencies.

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx-compat/src/target.rs
📚 Learning: 2025-08-21T07:21:51.621Z
Learnt from: upupming
PR: lynx-family/lynx-stack#1562
File: packages/react/transform/src/swc_plugin_snapshot/jsx_helpers.rs:261-283
Timestamp: 2025-08-21T07:21:51.621Z
Learning: In packages/react/transform/src/swc_plugin_snapshot/jsx_helpers.rs, the team prefers to keep the original unreachable! logic for JSXSpreadChild in jsx_is_children_full_dynamic function rather than implementing defensive error handling.

Applied to files:

  • packages/react/transform/swc-plugin-reactlynx-compat/src/lib.rs
🧬 Code graph analysis (1)
packages/react/transform/swc-plugin-reactlynx-compat/src/lib.rs (4)
packages/react/transform/swc-plugin-reactlynx-compat/src/is_component_class.rs (1)
  • new (12-18)
packages/react/transform/swc-plugin-reactlynx-compat/src/simplify_ctor_like_react_lynx_2.rs (1)
  • new (21-30)
packages/react/transform/index.d.ts (1)
  • DarkModeConfig (47-53)
packages/react/transform/src/swc_plugin_snapshot/jsx_helpers.rs (1)
  • jsx_name (13-74)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: build / Build (Ubuntu)
  • GitHub Check: build / Build (Windows)
  • GitHub Check: test-rust / Test (Ubuntu)
🔇 Additional comments (1)
packages/react/transform/swc-plugin-reactlynx-compat/tests/__swc_snapshots__/src/lib.rs/should_add_component_element_compiler_only.js (1)

1-15: Confirm intentional duplicate snapshot lines (1↔2, 9↔10).

Looks correct overall (spread props remain unwrapped in compilerOnly; key stays on Component; style/id moved to wrapper). Please confirm duplicates are expected from distinct inputs, not an accidental double-append in the snapshot generator.

Comment on lines +488 to +494
wrap_call = match wrap_call {
Expr::Call(mut call) => {
let pure_span = Span::dummy_with_cmt();
self.comments.add_pure_comment(pure_span.lo);
call.span = pure_span;
Expr::Call(call)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix compile error: Option doesn’t have add_pure_comment.

self.comments is Option<C>. Guard before calling add_pure_comment.

-        let pure_span = Span::dummy_with_cmt();
-        self.comments.add_pure_comment(pure_span.lo);
+        let pure_span = Span::dummy_with_cmt();
+        if let Some(c) = &self.comments {
+          c.add_pure_comment(pure_span.lo);
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
wrap_call = match wrap_call {
Expr::Call(mut call) => {
let pure_span = Span::dummy_with_cmt();
self.comments.add_pure_comment(pure_span.lo);
call.span = pure_span;
Expr::Call(call)
}
wrap_call = match wrap_call {
Expr::Call(mut call) => {
let pure_span = Span::dummy_with_cmt();
if let Some(c) = &self.comments {
c.add_pure_comment(pure_span.lo);
}
call.span = pure_span;
Expr::Call(call)
}
🤖 Prompt for AI Agents
In packages/react/transform/swc-plugin-reactlynx-compat/src/lib.rs around lines
488 to 494, the code calls self.comments.add_pure_comment(...) but self.comments
is an Option<C>; guard the call by first checking for Some and only calling
add_pure_comment on the inner value (e.g. use if let Some(comments) =
self.comments.as_mut() { comments.add_pure_comment(pure_span.lo); } or match
self.comments { Some(ref mut c) => c.add_pure_comment(pure_span.lo), None => {},
} ), then proceed to set call.span and return Expr::Call(call).

@@ -1 +1,1870 @@
use convert_case::{Case, Casing};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of copying the whole file, can we re-use the existing mod or crate? E.g.:

  • crates/swc_plugin_* <-- pure Rust crates that contains swc plugins
  • packages/react/transform <-- napi-rs binding
  • packages/react/transform/swc-plugin-* <-- WASM plugin binding

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is theoretically achievable, it would require some refactoring costs:

  1. The current swc_plugin_* mods are not completely pure - they contain #[napi(object)]
  2. In the WASM plugin, we would need serde for serialization and deserialization
  3. If napi-rs bindings are to be completely removed in the future, this refactoring would be redundant

However, the current approach of directly copying source code has some drawbacks:

  1. Need to maintain consistency between two codebases
  2. Loss of commit history, making git blame unavailable

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current swc_plugin_* mods are not completely pure - they contain #[napi(object)]

Yes it is. So what you need to do is to extract the swc plugin into a pure Rust crate (which could be reused by napi and WASM plugin). And handling the napi binding in the napi crate.

If napi-rs bindings are to be completely removed in the future, this refactoring would be redundant

We may still making changes before finishing the refactor. And we could make pure Rust benchmark & tests after this refactor.

@gaoachao gaoachao force-pushed the refactor/add-swc-plugin-reactlynx-compat branch from 62a1447 to 58c71fd Compare September 15, 2025 09:30
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/react/transform/swc-plugin-reactlynx-compat/build.js (1)

17-19: Add portable cargo fallback(s) when the build-wasi alias isn’t configured.
Relying solely on the alias will fail on envs without that config. Fall back to explicit targets.

Apply:

-execSync('cargo build-wasi --release', {
-  env: { ...process.env, RUSTFLAGS },
-  stdio: 'inherit',
-});
+const env = { ...process.env, RUSTFLAGS };
+try {
+  execSync('cargo build-wasi --release', { env, stdio: 'inherit' });
+} catch {
+  try {
+    // Prefer the newer target first
+    execSync('cargo build --target wasm32-wasip1 --release', { env, stdio: 'inherit' });
+  } catch {
+    // Fallback for older toolchains
+    execSync('cargo build --target wasm32-wasi --release', { env, stdio: 'inherit' });
+  }
+}
🧹 Nitpick comments (1)
packages/react/transform/swc-plugin-reactlynx-compat/build.js (1)

22-28: Resolve artifact path robustly (CARGO_TARGET_DIR and -wasip1/-wasi).
Current copy path assumes default target dir and -wasip1; honor CARGO_TARGET_DIR and probe both triples.

Apply:

-await fs.copyFile(
-  path.resolve(
-    __dirname,
-    '../../../../target/wasm32-wasip1/release/swc_plugin_reactlynx_compat.wasm',
-  ),
-  path.resolve(__dirname, 'swc_plugin_reactlynx_compat.wasm'),
-);
+const targetDir =
+  process.env.CARGO_TARGET_DIR ??
+  path.resolve(__dirname, '../../../../target');
+const candidates = [
+  path.join(targetDir, 'wasm32-wasip1', 'release', 'swc_plugin_reactlynx_compat.wasm'),
+  path.join(targetDir, 'wasm32-wasi', 'release', 'swc_plugin_reactlynx_compat.wasm'),
+];
+let srcWasm = null;
+for (const p of candidates) {
+  try {
+    await fs.access(p);
+    srcWasm = p;
+    break;
+  } catch {}
+}
+if (!srcWasm) {
+  throw new Error(`swc_plugin_reactlynx_compat.wasm not found in: ${candidates.join(', ')}`);
+}
+await fs.copyFile(srcWasm, path.resolve(__dirname, 'swc_plugin_reactlynx_compat.wasm'));
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58c71fd and 0fb02d3.

📒 Files selected for processing (4)
  • packages/react/.dprint.jsonc (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/build.js (1 hunks)
  • packages/react/transform/swc-plugin-reactlynx-compat/turbo.json (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/react/transform/swc-plugin-reactlynx-compat/turbo.json
  • packages/react/.dprint.jsonc
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-08-06T08:25:15.392Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1447
File: pnpm-workspace.yaml:20-22
Timestamp: 2025-08-06T08:25:15.392Z
Learning: In pnpm, configDependencies in pnpm-workspace.yaml (used for plugins like pnpm/plugin-better-defaults and pnpm/plugin-trusted-deps) do not appear in or modify pnpm-lock.yaml. These plugins are managed separately by pnpm itself, not as part of the workspace's dependency tree. This is different from the internal configDependencies field that can appear within pnpm-lock.yaml for tracking configuration file dependencies.

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-08-06T08:25:15.392Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1447
File: pnpm-workspace.yaml:20-22
Timestamp: 2025-08-06T08:25:15.392Z
Learning: In pnpm, configDependencies in pnpm-workspace.yaml (used for plugins like pnpm/plugin-better-defaults and pnpm/plugin-trusted-deps) do not appear in or modify pnpm-lock.yaml. These plugins are managed separately by pnpm itself as configuration-level dependencies, not as part of the workspace's dependency tree. Running pnpm install after adding configDependencies will not change the lockfile.

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-08-11T05:59:28.530Z
Learnt from: upupming
PR: lynx-family/lynx-stack#1305
File: packages/react/testing-library/src/plugins/vitest.ts:4-6
Timestamp: 2025-08-11T05:59:28.530Z
Learning: In the lynx-family/lynx-stack repository, the `packages/react/testing-library` package does not have `vite` as a direct dependency. It relies on `vitest` being available from the monorepo root and accesses Vite types through re-exports from `vitest/node`. Direct imports from `vite` should not be suggested for this package.

Applied to files:

  • pnpm-workspace.yaml
📚 Learning: 2025-08-06T13:28:57.182Z
Learnt from: colinaaa
PR: lynx-family/lynx-stack#1453
File: vitest.config.ts:49-61
Timestamp: 2025-08-06T13:28:57.182Z
Learning: In the lynx-family/lynx-stack repository, the file `packages/react/testing-library/src/vitest.config.js` is source code for the testing library that gets exported for users, not a test configuration that should be included in the main vitest projects array.

Applied to files:

  • pnpm-workspace.yaml
🔇 Additional comments (3)
packages/react/transform/swc-plugin-reactlynx-compat/build.js (2)

13-15: Good: RUSTFLAGS are preserved and extended, not clobbered.
This keeps CI/user flags intact while adding the required link args.


22-28: ESM/top-level await — verified in CI. packages/react/transform/swc-plugin-reactlynx-compat/package.json sets "type": "module"; root package.json engines.node = ^22 || ^24 and CI workflows use actions/setup-node with node-version "22"/"24" — Node ≥18 covered.

pnpm-workspace.yaml (1)

11-11: Confirm intent: keep both parent package and child-glob, or drop the parent entry.
packages/react/transform is a package: @lynx-js/react-transform (private: true, version: 0.2.0).
File: pnpm-workspace.yaml — line 11 (contains " - packages/react/transform/*").
If you want the parent package plus any nested packages, keep both entries; if you only want nested packages, remove the parent entry to avoid accidental inclusion.

@gaoachao gaoachao closed this Sep 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants