Skip to content

fix(dev): icon codemod keeps the use-client directive first and reports multi-line dynamic spans (KEA-7886) - #17410

Merged
diegosouzapw merged 1 commit into
feat/new-uikitfrom
feat/new-uikit-codemod-fix
Oct 21, 2026
Merged

diegosouzapw merged 1 commit into
feat/new-uikitfrom
feat/new-uikit-codemod-fix

Conversation

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Track B of the Keak CI UI Kit migration (KEA-7886). Fixes two bugs in scripts/dev/codemod-material-icons.mjs that lotes 1 and 2a ran into. Every remaining lote would hit them too.

Bug 1: import lands above "use client"

/** Health page. */
"use client";
export const A = () => <span className="material-symbols-outlined">search</span>;

Before: import Icon from "@/shared/components/Icon"; went in at offset 0, above the directive. The directive was then no longer the first statement, so Next.js treated the module as a server component and both dev and build broke. Unit tests stayed green. The old code only recognised "use client"; when it was byte 0 of the file.

Fix: a new directivePrologue() skips leading whitespace and ////* */ comments plus any "use …"/'use …' directive lines, and the import goes in right after the last directive line. Files with no directive behave as before (import at the top). As a guard, transformSource throws when the output's directive prologue differs from the input's, so it can never emit an import above a directive.

Bug 2: multi-line dynamic spans neither converted nor reported

<span
  className={cn("material-symbols-outlined", active && "text-primary")}
>{icon}</span>

Before: leftover detection matched <span…material-symbols-outlined one line at a time, so a tag spread over several lines matched nothing. The span stayed as it was, nothing reported it, and the script exited 0.

Fix: leftovers now come from scanning whole JSX opening tags (it understands braces, strings and template literals, so => and a > b don't end the tag). Each leftover is reported as manual: file:line at the line of its <span, and the CLI exits 2. Multi-line spans whose className is static ("…", {"…"}, or {`…`} without ${}) get their whitespace normalised and then go through the existing single-line transform, so they convert. Anything else is reported.

Tests

tests/unit/codemod-material-icons.test.ts: 4 → 10 cases. Written first and confirmed failing (5 red), then green. The existing 4 cases are unchanged.

  • docblock + "use client" + Material span: the directive is still the first statement, and the import comes after it and before the existing imports
  • 'use client', "use server";, 'use client'; behind line and block comments
  • no directive: import still at the very top (regression guard)
  • template-literal and cn(...) multi-line dynamic spans: reported at lines 4 and 10, code untouched
  • multi-line static template-literal span: converted to <Icon name="search" size={18} className="text-text-muted" />
  • CLI on a temp dir with a multi-line cn(...) span: exit 2 and manual: …/Multi.tsx:2

Validation: node --import tsx/esm --test tests/unit/codemod-material-icons.test.ts (10/10), npx prettier --check on both files, npx eslint (the test is clean; the .mjs script is under an ESLint ignore pattern), npm run typecheck:core. The codemod was not run on src/ in this PR.

⚠️ base-red inherited: #17219

…ts multi-line dynamic spans (KEA-7886)

The import is now inserted after the directive prologue (leading comments + "use client"/'use server' lines) instead of at offset 0, with a guard that throws if the output's directives differ from the input's. Leftover detection scans whole JSX opening tags (brace/string aware) instead of single lines, so multi-line dynamic spans are reported at their <span line and the CLI exits 2; multi-line spans with a static className (including {`...`} without ${}) are normalised and converted.
@diegosouzapw
diegosouzapw merged commit 7e43404 into feat/new-uikit Oct 21, 2026
3 checks passed
@diegosouzapw
diegosouzapw deleted the feat/new-uikit-codemod-fix branch October 22, 2026 04:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant