Skip to content

fix(cli): promote-tokenizer-presentation puts presentation before a spread - #9826

Closed
imdreamrunner wants to merge 1 commit into
mainfrom
fix/tokenizer-codemod-spread-order
Closed

imdreamrunner wants to merge 1 commit into
mainfrom
fix/tokenizer-codemod-spread-order

Conversation

@imdreamrunner

Copy link
Copy Markdown
Contributor

User impact

Take a builder who runs astryx upgrade on code that spreads props into a Tokenizer and also passes experimental={{presentation: x}} after the spread, for example <Tokenizer {...props} experimental={{presentation: 'bottom-sheet'}} />. If props carries its own presentation, that value wins today: the top-level prop wins over the experimental one. The staged promote-tokenizer-presentation codemod (#9796) writes presentation="bottom-sheet" where experimental was, after the spread. The moved value then replaces the spread's, so the surface that lost starts to win. After this PR, the codemod keeps the result the same.

Problem and solution fit

  • Why does the defect occur? The codemod puts the stable attribute at the position of experimental. In JSX, a later attribute replaces an earlier spread's key.
  • Why does it harm the task? A codemod must keep the old observable result (spec:AST-017/FR29c). Here it silently changes which presentation renders.
  • Why does that matter? astryx upgrade --apply runs without review of each call site, so the change would reach production unnoticed.

The fix puts the stable attribute before the first spread. A presentation in the spread then still wins, and without one the moved value applies. A spread after experimental can replace the whole experimental object, so the codemod still leaves that case to a person, with a TODO.

Expected behavior and authority

  • spec:AST-017/FR29c: "A mechanical migration … preserves the old observable result as its baseline and reports every uncertain or blocked case."
  • component:Tokenizer DEC-3: when both are set, the top-level presentation wins over experimental.presentation.

Smallest restoration

The change is the insertion index in promote-tokenizer-presentation.mjs, plus its header comment, one updated test and a Changeset. Nothing else changes. #9797 makes the same change in the PowerSearch codemod it adds.

Evidence

  • Reproduction before the change: <Tokenizer {...args} label="Tags" experimental={{presentation: 'bottom-sheet'}} /> becomes <Tokenizer {...args} label="Tags" presentation="bottom-sheet" />, so a presentation in args loses.
  • Result after the change: <Tokenizer presentation="bottom-sheet" {...args} label="Tags" />.
  • Representative unchanged path: with no spread, the attribute still takes the place of experimental. With a spread after experimental, the codemod still leaves a TODO.
  • Regression test or other mutation-sensitive proof: with the old codemod source, the updated test fails ("expected … to contain '<Tokenizer presentation="bottom-sheet" {...args} label="Tags" />'"). It passes with the fix.

Scope

  • One defect is restored.
  • Separable contradictory or unsettled tagalongs were removed or split.
  • No new public API, visual representation, default, or interaction model is hidden under the bug-fix label.
  • Public text and artifacts contain no internal Meta context.

Testing

  • The CLI codemod suites (84 files, 1,045 tests) pass.
  • codemod-verify.mjs passes, because no consumer file changes. check:changesets and Changeset coverage (it ships to @astryxdesign/cli, which the Changeset names) pass, and strict lint reports 0 errors.

@vercel

vercel Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Nov 3, 2026 10:57pm UTC

Request Review

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 3, 2026
@imdreamrunner

Copy link
Copy Markdown
Contributor Author

/approve-spec 20a6a01

@astracat-bot

astracat-bot Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟡 Reviewed (for maintainers only) Nov 3, 2026, 11:04 PM UTC

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Requesting changes. Moving presentation before the first spread fixes the case where the spread sets a value, but it breaks the case where the spread's presentation is undefined. For a spread the codemod cannot read, no position is provable.

  • promote-tokenizer-presentation.mjs rewrites <Tokenizer {...args} experimental={{presentation: 'bottom-sheet'}} /> to <Tokenizer presentation="bottom-sheet" {...args} /> with no TODO, yet when args has its own presentation: undefined the spread now overwrites the moved value and Tokenizer falls back to the adaptive default (the inline field on a desktop) where the original call rendered the bottom sheet, so a spread before experimental should keep the deprecated spelling with the TODO, as spec:AST-017 FR29c and FR29d ask for spreads a codemod cannot prove.

Then update the spread test to expect the TODO, and make the header comment, meta.description and the Changeset match.

[Automated review]

Comment on lines +168 to +172
let insertAt = value.properties.length === 0 ? index : index + 1;
if (firstSpread !== -1) {
insertAt = firstSpread;
}
opening.attributes.splice(insertAt, 0, presentation);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Before the spread, a spread with its own presentation: undefined overwrites the moved value. Tokenizer then resolves presentationProp ?? experimental?.presentation ?? 'adaptive' to 'adaptive', because experimental is gone, where the original call rendered the bottom sheet. After the spread, a defined presentation loses instead. Neither position is provable for an unknown spread, so please treat every spread as unprovable in isProvable (drop the index > attributes.indexOf(prop) condition) and keep the deprecated spelling with the TODO.

);
expect(output).toContain(
'<Tokenizer {...args} label="Tags" presentation="bottom-sheet" />',
'<Tokenizer presentation="bottom-sheet" {...args} label="Tags" />',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This pins a TODO-free rewrite past a spread whose contents the codemod cannot see. With the spread treated as unprovable, this case should expect the TODO and the kept experimental={{presentation: 'bottom-sheet'}}.

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

PR Analysis Report

🔎 Preview · 20a6a01

Storybook · Sandbox · built from this PR's head

Package size: @astryxdesign/cli 3.32 MB packed (+91 B); no new dependencies.

API changes: none in this pull request

Committed API report behind this build: core 1 row; run pnpm api:report.

Not run on this PR: add ci:run-visual, ci:run-a11y or ci:run-rtl to run them.


Generated by PR Enrichment workflow | View full report

@imdreamrunner

Copy link
Copy Markdown
Contributor Author

Closing: the staged promote-tokenizer-presentation codemod is being removed together with the experimental presentation option, so there is no spread placement left to fix.

@github-actions
github-actions Bot deleted the fix/tokenizer-codemod-spread-order branch November 4, 2026 06:04

This branch was successfully deployed

1 active deployment
Preview — 20a6a01a Deployed Nov 3, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant