Skip to content

fix: address codex-bot follow-ups for 2026-03-21 merged PRs#600

Merged
kdy1 merged 10 commits into
mainfrom
kdy1/codex-bot-reviews-20260321
Mar 21, 2026
Merged

fix: address codex-bot follow-ups for 2026-03-21 merged PRs#600
kdy1 merged 10 commits into
mainfrom
kdy1/codex-bot-reviews-20260321

Conversation

@kdy1
Copy link
Copy Markdown
Member

@kdy1 kdy1 commented Mar 21, 2026

Summary

What Changed

Validation

  • cargo test -p swc_plugin_loadable_components --test fixture -- --ignored
  • cargo test -p swc_plugin_graphql_codegen_client_preset
  • cargo test -p swc_emotion --test fixture -- --ignored
  • pnpm -C /Users/kdy1/.codex/worktrees/17e6/plugins/packages/formatjs test

All passed (formatjs has an existing non-blocking Vitest warning about an un-awaited rejects assertion).

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 21, 2026

⚠️ No Changeset found

Latest commit: a210f2d

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.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

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

@claude
Copy link
Copy Markdown

claude Bot commented Mar 21, 2026

Code Review

Overall this is a solid follow-up PR with good test coverage. A few items worth flagging:


Potential Issues

1. apply_naming_convention wildcard behavior change (graphql-codegen-client-preset)

Before: unknown conventions fell back to to_pascal_case(s)
After: unknown conventions preserve the original name

This is a subtle breaking change in behavior for anyone using a custom naming convention string who relied on the implicit PascalCase fallback. The intent (honoring unknown conventions as-is) is reasonable, but it's worth calling out explicitly in the PR description or changelog, since existing users with typos in their convention string (e.g. "change-case-all#PascalCase" vs "change-case-all#pascalCase") will now get silently different output instead of PascalCase.

2. Pre-existing unwrap() on parent() (graphql-codegen-client-preset lib.rs)

let file_s_dirname = file_full_path.parent().unwrap();

This was there before this PR, but the new Windows absolute-path branch makes it more reachable with unusual inputs (e.g. filename = "C:\\" — the drive root has no parent). Not a regression from this PR, but worth noting for a future hardening pass.

3. is_absolute_path not shown in diff

The diff references is_absolute_path(&filename) but the helper function's implementation isn't visible in the diff. If it only checks for C:/-style paths, UNC paths (\\server\share\...) and \\?\...-style extended paths would fall through to the cwd.push(filename) branch and likely produce wrong results. Worth verifying the implementation covers those cases.


Minor Observations

Emotion — create_tagged_tpl_label_arg: The semicolon-appending logic is gated on both sourcemap == true AND !label.ends_with(';'). It's a bit surprising that labels only get terminated when sourcemaps are enabled — is there a case where a label without a sourcemap comment would also benefit from the semicolon? The condition seems correct based on the existing fixture outputs, just worth a brief comment explaining why it's sourcemap-scoped.

Loadable Components — iter().rev() for SSR override: This is a clean fix. The comment explaining object-literal order semantics is helpful.

FormatJS — member-expression component matching: The implementation looks correct for ReactIntl.FormattedMessage. Deeply nested member expressions (e.g. A.B.FormattedMessage) would only match on the rightmost property name, which appears to be the intended behavior and consistent with how other tools handle this.


Test Coverage

Good additions:

  • windows_absolute_filename_path_gets_correct_relative_import_path directly exercises the new code path
  • naming_convention_keep_preserves_original_name / naming_convention_unknown_preserves_original_name give confidence in the changed wildcard behavior
  • The loadable-components fixture for aliased imports catches the over-matching bug cleanly

One gap: there's no test for the NamingConventionOption::String path with "change-case-all#pascalCase" after the enum refactor (only the object form is tested). A quick roundtrip test would confirm the default string path still works end-to-end through process_transform.


Summary

The fixes are well-reasoned and the test coverage is solid. The main things to address before merging:

  1. Verify is_absolute_path handles UNC/extended paths (or document the known limitation).
  2. Consider whether the wildcard fallback behavior change in apply_naming_convention warrants a note in the changelog.

Everything else looks good to me.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 72d7d66c94

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +704 to +708
let is_match = if signature.from.is_none() {
default_spec.local.sym == signature.name
} else {
signature.is_default_specifier()
|| default_spec.local.sym == signature.name
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Restrict default-import matches to name: "default"

When a signature has a concrete from and a non-default name, this branch still treats any default import whose local binding happens to equal signature.name as a match. With a config like { from: "my-loadable", name: "lazy" }, import lazy from "my-loadable" will now be rewritten even though only import { lazy } ... should match; the README and Signature::is_default_specifier() still reserve name: "default" for default imports. That changes runtime behavior for wrappers that expose both a default export and named helpers.

Useful? React with 👍 / 👎.

Comment on lines +998 to +1000
JSXElementName::JSXMemberExpr(member_expr) => {
if !self.component_names.contains(&*member_expr.prop.sym) {
return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate member-expression components against their namespace

This now extracts any JSX member expression whose final property name matches a tracked component, without checking what object it comes from. In projects that render unrelated components such as <UI.FormattedMessage> or <Messages.FormattedMessage>, the visitor will start deleting description and injecting an id even though those components are not from react-intl. Before this change, those member-expression components were left alone, so this is a new false-positive path.

Useful? React with 👍 / 👎.

@claude
Copy link
Copy Markdown

claude Bot commented Mar 21, 2026

Code Review

Thanks for this follow-up PR addressing multiple bot review items. The changes are well-structured with good test coverage. Here is my analysis by package.


graphql-codegen-client-preset

** (new helper) — looks good**
The implementation correctly handles Unix paths (/foo) and Windows drive-letter paths (C:/foo, C:\foo). One minor gap: UNC paths (\\server\share or //server/share) are not detected as absolute. After the replace('\\', "/") normalization, //server/share would pass through undetected as a relative path, though this is an unlikely real-world concern for a WASM plugin context.

file_full_path.parent().unwrap()
This unwrap() exists in the pre-PR code too and will panic if the resolved path is a filesystem root (e.g. C:/ or /). Not introduced here, but worth noting for a future hardening pass.

NamingConventionOption enum — correct approach
Using #[serde(untagged)] is the idiomatic way to accept either a string or object. The as_type_name_convention method correctly extracts typeNames from the object variant and falls back to the default. Tests cover both the object-with-typeNames and object-without-typeNames cases.

Behavior change: unknown conventions now preserve original name
Previously any unrecognized convention string silently fell through to PascalCase. Now it preserves the original name:

// Before
_ => to_pascal_case(s),

// After  
_ => s.to_string(),

This is more correct, but it is a silent breaking change for users who were relying on the old fall-through PascalCase behavior with a custom convention string. Consider documenting this in the PR description or a CHANGELOG entry.

Minor style nit: The "keep" arm and _ arm have identical bodies (s.to_string()). Keeping the explicit "keep" arm is fine for self-documentation, but a comment on the _ arm (like the one already there) makes the intent clear enough either way.


packages/emotion

create_tagged_tpl_label_arg — clean extraction
Extracting the semicolon-termination logic into its own method is the right call. The !label.ends_with(';') guard prevents double semicolons. Well done.

CssPropCallee enum defined inside the method
Defining an enum inside a function is valid Rust and keeps it local to its use. Just noting it — not a bug.

Span change for PURE comment placement
Switching from container_span.lo (the { of JSXExprContainer) to expr.span().lo() (the actual expression start) is more precise and correct for sourcemap / tooling attribution. Good fix.

Namespace import support for css prop — looks correct
The CssPropCallee::Namespace variant correctly builds a MemberExpr callee for emotionReact.css. The fixture update reflects the expected output accurately.


packages/formatjs

JSXMemberExpr matching
Matching on the rightmost prop.sym for member expressions like ReactIntl.FormattedMessage is the correct semantic: only the local component name is relevant. One edge case to be aware of: deeply nested member expressions like a.b.FormattedMessage will also match if FormattedMessage is in component_names. This is probably intentional and consistent, but worth a comment in the code if not already there.

ast: true test update
The updated assertion (/defaultMessage: \[/) correctly reflects the AST-mode output. The old assertion tested string concatenation in non-AST mode; this now tests the AST array output, which is the right thing to validate for the #532 regression.


packages/loadable-components

iter().rev() for ssr detection — correct
The reverse iteration correctly implements "last-write-wins" semantics for object literal keys. The logic:

  • For { ssr: false, ssr: true }: reversed iteration finds ssr: true first → returns false (do not skip SSR). Correct.
  • For { ssr: false, ...opts }: reversed iteration hits the spread first → return false (conservative, do not skip). Correct — the spread could override ssr.
  • For { ...opts, ssr: false }: reversed iteration hits ssr: false first → return true. Correct — explicit override wins.

The fixture at simple import/should transform when ssr false is overridden/ validates the { ssr: false, ...opts } case with opts = { ssr: true }. This is a good regression test.

Aliased import fix
The distinction between source-less (from: None) and source-bound signatures is now explicit:

let is_match = if signature.from.is_none() {
    default_spec.local.sym == signature.name   // must match configured local name
} else {
    signature.is_default_specifier() || default_spec.local.sym == signature.name
};

This prevents over-matching unrelated default imports for source-less signatures. The fixture aliased import/should not transform other default imports/ covers this well.

**Signature name change ("default")** in the test helper is correct — the fixture was testing the aliased-import scenario, so the signature name should be the actual identifier, not the string "default"`.


README updates

The jest README change is accurate — the plugin is specifically a Jest mock-hoisting transform, not a general babel-jest replacement. This is a meaningful clarification.


Summary

  • All changes look correct and address the original issues
  • Test coverage is solid across all changed packages
  • One thing to consider documenting: the naming-convention behavior change (unknown → preserve-original instead of → PascalCase) is a breaking change for any user with a custom convention string

Overall this is a high-quality follow-up. No blocking issues found.

@kdy1 kdy1 merged commit 9ab2a34 into main Mar 21, 2026
11 checks passed
@kdy1 kdy1 deleted the kdy1/codex-bot-reviews-20260321 branch March 21, 2026 02:38
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a210f2df39

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +459 to +463
PackageMeta::Namespace(module) => module
.exported_names
.iter()
.find(|item| matches!(item.kind, ExprKind::Css))
.map(|item| CssPropCallee::Namespace {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Match css-prop namespace calls only on a css export

In the new namespace-import css-prop path, this picks the first export whose kind is ExprKind::Css, but that enum is also used for keyframes aliases (packages/emotion/transform/src/import_map.rs:43-51). If a project uses an import-map package with import * as emo from "pkg" and the only mapped emotion-style export is keyframes, <div css={{...}} /> will now be rewritten to emo.keyframes(...), which returns an animation name instead of serialized styles. The namespace match here needs to require an actual css export, not any ExprKind::Css entry.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant