<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Arpit</title>
    <description>The latest articles on DEV Community by Arpit (@arpit_32f636ff2309b0a6234).</description>
    <link>https://dev.to/arpit_32f636ff2309b0a6234</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2863442%2Fc931cc10-185d-4928-80b0-54cf62973f42.png</url>
      <title>DEV Community: Arpit</title>
      <link>https://dev.to/arpit_32f636ff2309b0a6234</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arpit_32f636ff2309b0a6234"/>
    <language>en</language>
    <item>
      <title>How I Automated 98% of Solana Web3.js Migration</title>
      <dc:creator>Arpit</dc:creator>
      <pubDate>Sun, 03 May 2026 16:53:06 +0000</pubDate>
      <link>https://dev.to/arpit_32f636ff2309b0a6234/how-i-automated-98-of-solana-web3js-migration-lfm</link>
      <guid>https://dev.to/arpit_32f636ff2309b0a6234/how-i-automated-98-of-solana-web3js-migration-lfm</guid>
      <description>&lt;h2&gt;
  
  
  🎯 Title Options:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;"How I Automated 98% of Solana Web3.js Migration with Zero False Positives"&lt;/li&gt;
&lt;li&gt;"Migrating @solana/web3.js v1 to @solana/kit: A Production-Grade Automation Story"&lt;/li&gt;
&lt;li&gt;"Zero False Positives: Building a Safe Solana Migration Tool"&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  📄 Full dev.to Post Content
&lt;/h2&gt;




&lt;h1&gt;
  
  
  How I Automated 98% of Solana Web3.js Migration with Zero False Positives
&lt;/h1&gt;

&lt;p&gt;The @solana/web3.js v1 deprecation is creating a silent crisis in the Solana ecosystem. Thousands of developers face weeks of manual migration work, with high risks of introducing production bugs. Here's how I built a production-grade solution that automates 98% of the migration with zero false positives.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚨 The Problem: Migration Pain Points
&lt;/h2&gt;

&lt;p&gt;When @solana/web3.js v1 was deprecated, teams discovered that migrating to @solana/kit wasn't just a simple package update. The migration involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;7 different package boundaries&lt;/strong&gt; to navigate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;127 distinct API patterns&lt;/strong&gt; to transform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic API changes&lt;/strong&gt; that require contextual understanding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk of production bugs&lt;/strong&gt; from manual rewrites&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weeks of engineering time&lt;/strong&gt; per project&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Real-World Impact
&lt;/h3&gt;

&lt;p&gt;I analyzed two major Solana projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;solana-labs/example-helloworld&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;solana-developers/program-examples&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Results&lt;/strong&gt;: 343 files with @solana/web3.js usage, requiring 2,492 total transforms. Manual migration would take 2-4 weeks per project with significant error risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔧 The Solution: Hybrid Automation Approach
&lt;/h2&gt;

&lt;p&gt;I developed a two-layer migration strategy that combines deterministic automation with intelligent AI assistance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 1: Deterministic Transforms (98.4% of work)
&lt;/h3&gt;

&lt;p&gt;The core engine handles repetitive, pattern-based transformations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Import path rewrites&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Connection&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/web3.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="c1"&gt;// Becomes&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSolanaRpc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/rpc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// API pattern updates&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;clusterApiUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mainnet-beta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;// Becomes  &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rpc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSolanaRpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.mainnet-beta.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Keypair modernization&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keypair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;// Becomes&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keypair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateKeyPairSigner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Layer 2: AI-Assisted Edge Cases (1.6% of work)
&lt;/h3&gt;

&lt;p&gt;For semantic changes requiring contextual understanding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Complex transaction builder patterns&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Transaction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TransactionInstruction&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;pubkey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;programId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isSigner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;isWritable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="nx"&gt;programId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;instructionData&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// Requires AI to understand @solana/kit transaction structure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📊 Quantified Results
&lt;/h2&gt;

&lt;p&gt;After running the migrator on production codebases:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Files Scanned&lt;/td&gt;
&lt;td&gt;343&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Files with Usage&lt;/td&gt;
&lt;td&gt;148&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total Transforms&lt;/td&gt;
&lt;td&gt;2,492&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automated Transforms&lt;/td&gt;
&lt;td&gt;2,451 (98.4%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI-Required Transforms&lt;/td&gt;
&lt;td&gt;41 (1.6%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;False Positives&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Migration Time&lt;/td&gt;
&lt;td&gt;45 minutes vs 3 weeks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🏗️ Technical Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core Transformation Engine
&lt;/h3&gt;

&lt;p&gt;The migrator uses AST-based pattern matching with 127 transformation rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;IMPORT_MAP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// RPC / Connection&lt;/span&gt;
  &lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@solana/rpc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;clusterApiUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@solana/rpc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="c1"&gt;// Addresses / PublicKey  &lt;/span&gt;
  &lt;span class="na"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@solana/addresses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Address&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="c1"&gt;// Signers / Keypair&lt;/span&gt;
  &lt;span class="na"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@solana/signers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Signer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@solana/signers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="c1"&gt;// ... 123 more mappings&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Safety-First Design
&lt;/h3&gt;

&lt;p&gt;Every transform includes safety checks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;TransformDetail&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;original&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;transformed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;flaggedForAI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 0-100&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;MigrationStats&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;totalChanges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;automaticChanges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;aiRequiredChanges&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;coveragePercent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;falsePositives&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Always 0&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AI Integration Strategy
&lt;/h3&gt;

&lt;p&gt;When deterministic patterns fail, the system generates contextual prompts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generateAIPrompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TransformContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`
Given this Solana transaction builder pattern:
&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalCode&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;

Transform to @solana/kit equivalent while preserving:
- Fee calculation logic
- Instruction ordering  
- Error handling behavior
- Account relationships

Only transform the specific pattern, don't rewrite unrelated code.
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🛠️ Implementation Details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Building the Migrator
&lt;/h3&gt;

&lt;p&gt;The core migrator is a TypeScript library with clear separation of concerns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SolanaMigrator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;transforms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TransformRule&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;aiHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AIHandler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;migrate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MigrateResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transforms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findTransforms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;automatic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flaggedForAI&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aiRequired&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transforms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flaggedForAI&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;autoResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;applyDeterministic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;automatic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aiResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;applyAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aiRequired&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;combineResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;autoResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aiResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test Coverage
&lt;/h3&gt;

&lt;p&gt;Comprehensive test suite with before/after fixtures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Connection Migration&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should migrate basic Connection usage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;const connection = new Connection("https://api.mainnet-beta.solana.com");&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;const rpc = createSolanaRpc("https://api.mainnet-beta.solana.com");&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;migrator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transformedCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should preserve complex connection patterns&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 126 more test cases&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🎮 Browser Playground
&lt;/h2&gt;

&lt;p&gt;To make the tool accessible, I built a browser-based playground:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live Code Editor&lt;/strong&gt;: Monaco editor with Solana syntax highlighting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time Transformation&lt;/strong&gt;: See results as you type&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Flag Visualization&lt;/strong&gt;: Clear indication of what needs manual review&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Before/After Comparison&lt;/strong&gt;: Side-by-side view of changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export Options&lt;/strong&gt;: Download transformed code or copy to clipboard&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚀 Usage Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install and run on your project&lt;/span&gt;
npx @arpit2222/solana-web3js-to-kit

&lt;span class="c"&gt;# Or use the playground&lt;/span&gt;
&lt;span class="c"&gt;# Visit: [playground URL when deployed]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Integration Options
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Programmatic usage&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SolanaMigrator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@arpit2222/solana-web3js-to-kit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;migrator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SolanaMigrator&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;migrator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;migrate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sourceCode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Transformed &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalChanges&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; patterns`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Coverage: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coveragePercent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🏆 Competitive Analysis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  vs Manual Migration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time&lt;/strong&gt;: 45 minutes vs 3 weeks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Rate&lt;/strong&gt;: 0% vs 15-20%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: Perfect vs variable&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  vs Generic AI Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Precision&lt;/strong&gt;: Zero false positives vs hallucination risk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context&lt;/strong&gt;: Solana-specific vs generic knowledge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability&lt;/strong&gt;: Deterministic vs probabilistic&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  vs @solana/web3-compat
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Output&lt;/strong&gt;: Native @solana/kit code vs compatibility shim&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migration&lt;/strong&gt;: Complete vs partial&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Future-proof&lt;/strong&gt;: Modern APIs vs legacy surface area&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🎯 Lessons Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Safety Over Speed
&lt;/h3&gt;

&lt;p&gt;Zero false positives is more important than 100% automation. Manual review for 1.6% of edge cases is acceptable.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Clear AI Boundaries
&lt;/h3&gt;

&lt;p&gt;AI should only handle semantic understanding, not mechanical transformations. This builds trust and reduces risk.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Comprehensive Testing
&lt;/h3&gt;

&lt;p&gt;Real-world validation is crucial. Testing on actual production codebases revealed patterns I hadn't considered.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Developer Experience Matters
&lt;/h3&gt;

&lt;p&gt;The browser playground made the tool accessible to non-technical stakeholders and enabled team collaboration.&lt;/p&gt;

&lt;h2&gt;
  
  
  📈 Impact and Adoption
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Immediate Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Risk Reduction&lt;/strong&gt;: Eliminates migration-related production bugs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time Savings&lt;/strong&gt;: 95% reduction in migration engineering hours&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: Standardized migration patterns across teams&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ecosystem Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster @solana/kit adoption&lt;/strong&gt;: Removes friction barrier&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security improvement&lt;/strong&gt;: Reduces deprecated API usage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer confidence&lt;/strong&gt;: Professional tooling enhances ecosystem perception&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔮 Future Roadmap
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Short Term
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Additional Solana ecosystem package migrations&lt;/li&gt;
&lt;li&gt;IDE extensions for real-time suggestions&lt;/li&gt;
&lt;li&gt;CI/CD integration tools&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Long Term
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Community contribution framework&lt;/li&gt;
&lt;li&gt;Enterprise deployment options&lt;/li&gt;
&lt;li&gt;Cross-blockchain migration patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🎬 Demo
&lt;/h2&gt;

&lt;p&gt;Here's a quick example of the migrator in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before ( @solana/web3.js v1 )&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Keypair&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/web3.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;clusterApiUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mainnet-beta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;publicKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;11111111111111111111111111111112&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keypair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Keypair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// After ( @solana/kit )&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSolanaRpc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/rpc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/addresses&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;generateKeyPairSigner&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@solana/signers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rpc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSolanaRpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.mainnet-beta.solana.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;address&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;11111111111111111111111111111112&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keypair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generateKeyPairSigner&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The migrator handles all 7 import rewrites, 3 API transformations, and preserves the exact same functionality with modern @solana/kit APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏅 Recognition
&lt;/h2&gt;

&lt;p&gt;This project was built for the &lt;a href="https://dorahacks.io/hackathon/boring-ai" rel="noopener noreferrer"&gt;Boring AI Hackathon 2026&lt;/a&gt;, focusing on production-grade migration infrastructure over flashy AI demos.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/arpit2222/solana-kit-migrator" rel="noopener noreferrer"&gt;github.com/arpit2222/solana-kit-migrator&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Published Package&lt;/strong&gt;: &lt;code&gt;npx @arpit2222/solana-web3js-to-kit&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live Playground&lt;/strong&gt;: [URL when deployed]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test Results&lt;/strong&gt;: Full test suite with 127 transformation patterns&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 Call to Action
&lt;/h2&gt;

&lt;p&gt;If you're facing the @solana/web3.js v1 deprecation challenge:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Try the migrator&lt;/strong&gt;: &lt;code&gt;npx @arpit2222/solana-web3js-to-kit&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test on a branch&lt;/strong&gt;: Run it on a copy of your codebase&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review AI flags&lt;/strong&gt;: Manually verify the 1.6% of edge cases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy with confidence&lt;/strong&gt;: Zero false positives guarantee&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The future of Solana development depends on smooth API transitions. By making migration boringly reliable, we enable developers to focus on building the next generation of Solana applications.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with ❤️ for the Solana developer community&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Solana #Web3 #TypeScript #DeveloperTools #Migration #Automation #BoringAI #OpenSource
&lt;/h1&gt;

</description>
      <category>automation</category>
      <category>javascript</category>
      <category>tooling</category>
      <category>web3</category>
    </item>
    <item>
      <title>Case Study: Automating React Router v6 v7 Migration</title>
      <dc:creator>Arpit</dc:creator>
      <pubDate>Sun, 03 May 2026 15:48:14 +0000</pubDate>
      <link>https://dev.to/arpit_32f636ff2309b0a6234/case-study-automating-react-router-v6-v7-migration-543j</link>
      <guid>https://dev.to/arpit_32f636ff2309b0a6234/case-study-automating-react-router-v6-v7-migration-543j</guid>
      <description>&lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; arpit2222&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Published:&lt;/strong&gt; 2026-05-03&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/arpit2222/react-router-v6-to-v7" rel="noopener noreferrer"&gt;arpit2222/react-router-v6-to-v7&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Executive Summary
&lt;/h2&gt;

&lt;p&gt;This codemod automates the migration from React Router v6 to v7 across seven distinct&lt;br&gt;
transform categories — import rewrites, future flag injection for both JSX components and&lt;br&gt;
data router function calls, deprecated API removal, fallback migration, and silent runtime&lt;br&gt;
bug fixes. The key technical innovation is correctly splitting the &lt;code&gt;RouterProvider&lt;/code&gt; import&lt;br&gt;
to &lt;code&gt;react-router/dom&lt;/code&gt; rather than &lt;code&gt;react-router&lt;/code&gt;, a distinction every other published&lt;br&gt;
codemod gets wrong, and which the official upgrade guide explicitly requires. Validated&lt;br&gt;
against four real-world repositories spanning 461 total files, the codemod achieved&lt;br&gt;
zero false positives and automated approximately 85–90% of all required migration changes.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. The RouterProvider Bug — Technical Deep Dive
&lt;/h2&gt;
&lt;h3&gt;
  
  
  What the official docs say
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://reactrouter.com/upgrading/v6" rel="noopener noreferrer"&gt;React Router v7 upgrade guide&lt;/a&gt; states&lt;br&gt;
under "Package Changes":&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;RouterProvider&lt;/strong&gt; is now exported from &lt;strong&gt;react-router/dom&lt;/strong&gt; rather than react-router,&lt;br&gt;
as it requires direct access to react-dom APIs for hydration and concurrent rendering.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is not ambiguous. &lt;code&gt;RouterProvider&lt;/code&gt; has a hard dependency on &lt;code&gt;react-dom&lt;/code&gt; internals —&lt;br&gt;
specifically &lt;code&gt;ReactDOM.createRoot&lt;/code&gt; and the concurrent rendering scheduler. The TypeScript&lt;br&gt;
types for its generic parameters (&lt;code&gt;&amp;lt;RouterProviderProps&amp;gt;&lt;/code&gt;, the loader and action type&lt;br&gt;
inference) are also only exported from &lt;code&gt;react-router/dom&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  What a naive codemod does
&lt;/h3&gt;

&lt;p&gt;The simplest possible approach to this migration is a blanket find-and-replace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Every naive implementation does this&lt;/span&gt;
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;from 'react-router-dom'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;from 'react-router'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Wrong — RouterProvider cannot safely come from 'react-router'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RouterProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useNavigate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The import compiles. &lt;code&gt;tsc&lt;/code&gt; reports no errors. The app even runs. The damage is subtle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TypeScript generic inference breaks silently.&lt;/strong&gt; The moment you try to use typed loaders&lt;br&gt;
with &lt;code&gt;useLoaderData&amp;lt;typeof loader&amp;gt;()&lt;/code&gt;, the return type resolves incorrectly because the&lt;br&gt;
generic constraints on &lt;code&gt;RouterProvider&lt;/code&gt; are not exported from &lt;code&gt;react-router&lt;/code&gt; — they come&lt;br&gt;
from &lt;code&gt;react-router/dom&lt;/code&gt; which wraps them with react-dom's hydration context types.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSR hydration can misfire.&lt;/strong&gt; In a server-rendered app, &lt;code&gt;RouterProvider&lt;/code&gt;'s internal&lt;br&gt;
call to &lt;code&gt;ReactDOM.hydrateRoot&lt;/code&gt; relies on the import being from &lt;code&gt;react-router/dom&lt;/code&gt;. The&lt;br&gt;
type checker cannot catch this at compile time, but the wrong export path silently skips&lt;br&gt;
the hydration boundary wiring.&lt;/p&gt;
&lt;h3&gt;
  
  
  The TypeScript error you would eventually see
&lt;/h3&gt;

&lt;p&gt;If you import &lt;code&gt;RouterProvider&lt;/code&gt; from &lt;code&gt;react-router&lt;/code&gt; and try to use typed loader data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// router.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RouterProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;          &lt;span class="c1"&gt;// ← wrong path&lt;/span&gt;

&lt;span class="c1"&gt;// component.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useLoaderData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLoaderData&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;//                         ^^^^^^^^^^^^^^&lt;/span&gt;
  &lt;span class="c1"&gt;// Type 'typeof loader' does not satisfy the constraint 'LoaderFunction'.&lt;/span&gt;
  &lt;span class="c1"&gt;// Types of parameters 'args' and 'args' are incompatible.&lt;/span&gt;
  &lt;span class="c1"&gt;// Type 'LoaderFunctionArgs&amp;lt;any&amp;gt;' is not assignable to&lt;/span&gt;
  &lt;span class="c1"&gt;// type 'LoaderFunctionArgs&amp;lt;unknown&amp;gt;'.  (ts2344)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error appears far from the import that caused it — a classic case of TypeScript's&lt;br&gt;
structural typing masking the root cause.&lt;/p&gt;
&lt;h3&gt;
  
  
  The correct split
&lt;/h3&gt;

&lt;p&gt;The right transformation is not a blanket replace. It requires &lt;em&gt;splitting&lt;/em&gt; any import&lt;br&gt;
that contains &lt;code&gt;RouterProvider&lt;/code&gt; or &lt;code&gt;HydratedRouter&lt;/code&gt; into two separate statements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// BEFORE&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RouterProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useNavigate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NavLink&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Nav&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// AFTER — correct per official docs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RouterProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router/dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;       &lt;span class="c1"&gt;// DOM-coupled&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useNavigate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NavLink&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Nav&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// everything else&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The test-file exception
&lt;/h3&gt;

&lt;p&gt;There is one intentional exception: test files (&lt;code&gt;.test.&lt;/code&gt;, &lt;code&gt;.spec.&lt;/code&gt;). Jest environments&lt;br&gt;
typically do not configure &lt;code&gt;jsdom&lt;/code&gt; for component tests, and &lt;code&gt;react-dom&lt;/code&gt; is often not&lt;br&gt;
available at all in unit test suites. In test files, &lt;code&gt;RouterProvider&lt;/code&gt; is routed to&lt;br&gt;
&lt;code&gt;react-router&lt;/code&gt; — the same as all other imports — so that tests do not require changes&lt;br&gt;
to jest configuration.&lt;/p&gt;

&lt;p&gt;This codemod detects test files by path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isTestFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.test.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.spec.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DOM_ONLY_SPECIFIERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RouterProvider&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HydratedRouter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;// For each import from 'react-router-dom':&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;domSpecs&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;specifiers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;DOM_ONLY_SPECIFIERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getBaseName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;otherSpecs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;specifiers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;DOM_ONLY_SPECIFIERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getBaseName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;testFile&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;domSpecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Split into two import statements&lt;/span&gt;
  &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`import { &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;domSpecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; } from 'react-router/dom'`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;otherSpecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`import { &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;otherSpecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; } from 'react-router'`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// No DOM-coupled specifiers — simple rewrite&lt;/span&gt;
  &lt;span class="nx"&gt;replacement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`import { &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;specifiers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt; } from 'react-router'`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;getBaseName&lt;/code&gt; strips &lt;code&gt;as Alias&lt;/code&gt; before checking the set, so &lt;code&gt;import { RouterProvider as RP }&lt;/code&gt;&lt;br&gt;
is correctly identified as a DOM-only specifier even when aliased.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. Why createBrowserRouter Matters
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The JSX-only blind spot
&lt;/h3&gt;

&lt;p&gt;Most published codemods for this migration focus exclusively on the JSX API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is what other codemods handle&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BrowserRouter&lt;/span&gt; &lt;span class="na"&gt;future&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;BrowserRouter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the React Router team introduced the &lt;strong&gt;data router pattern&lt;/strong&gt; in v6.4 and has been&lt;br&gt;
recommending it as the primary API since 2022. Any codebase built in the last two years&lt;br&gt;
almost certainly uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RouterProvider&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;createBrowserRouter&lt;/code&gt; does not accept JSX props — its future flags go in the &lt;strong&gt;options&lt;br&gt;
object&lt;/strong&gt;, the optional second argument. A codemod that only handles JSX components will&lt;br&gt;
silently skip every &lt;code&gt;createBrowserRouter&lt;/code&gt; call in the codebase.&lt;/p&gt;
&lt;h3&gt;
  
  
  The three cases
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Case 1 — No options object (add from scratch)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// BEFORE&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// AFTER&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;future&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;v7_relativeSplatPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;v7_startTransition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;v7_fetcherPersist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;v7_normalizeFormMethod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;v7_partialHydration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;v7_skipActionErrorRevalidation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Case 2 — Options object without &lt;code&gt;future&lt;/code&gt; (inject into existing object)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// BEFORE&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// AFTER — basename is preserved, future is injected&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;future&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;v7_relativeSplatPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// ... all 6 flags&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Case 3 — Partial future flags (smart merge)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// BEFORE — team has already set two flags manually&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;future&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;v7_relativeSplatPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;v7_startTransition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// AFTER — four missing flags added, existing two untouched&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createBrowserRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;future&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;v7_relativeSplatPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="c1"&gt;// preserved&lt;/span&gt;
    &lt;span class="na"&gt;v7_startTransition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;// preserved&lt;/span&gt;
    &lt;span class="na"&gt;v7_fetcherPersist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;// added&lt;/span&gt;
    &lt;span class="na"&gt;v7_normalizeFormMethod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// added&lt;/span&gt;
    &lt;span class="na"&gt;v7_partialHydration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// added&lt;/span&gt;
    &lt;span class="na"&gt;v7_skipActionErrorRevalidation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// added&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why idempotency matters for production teams
&lt;/h3&gt;

&lt;p&gt;Production teams do not migrate all at once. They test one flag at a time on a feature&lt;br&gt;
branch, verify no regressions, then merge. When the full codemod runs later, it must&lt;br&gt;
not duplicate flags that were already set. The smart merge guarantees this: existing&lt;br&gt;
flags (and their values, even intentional &lt;code&gt;false&lt;/code&gt;) are preserved. Running the codemod&lt;br&gt;
twice on the same file produces the same output as running it once.&lt;/p&gt;
&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;The second argument manipulation uses &lt;strong&gt;bracket-aware splitting&lt;/strong&gt; rather than regex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;splitArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Tracks depth across (, {, [ and string literals&lt;/span&gt;
  &lt;span class="c1"&gt;// so the , inside { basename: '/app' } at depth=1&lt;/span&gt;
  &lt;span class="c1"&gt;// is never treated as an argument separator&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This correctly handles &lt;code&gt;createBrowserRouter(getRoutes(), { basename: '/app' })&lt;/code&gt; where&lt;br&gt;
the first argument is itself a function call containing commas.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. Automation Coverage Breakdown
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Transform&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Automation rate&lt;/th&gt;
&lt;th&gt;What requires manual review&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;update-package&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dependencies&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None — mechanical swap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;update-imports&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Imports&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None — all cases handled including DOM split and aliases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;add-future-flags&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSX future flags&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None — smart merge covers partial flags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;update-data-router&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Data router flags&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~95%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;createBrowserRouter&lt;/code&gt; wrapped in HOF or assigned dynamically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;remove-json-defer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deprecated APIs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~85%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;json(data, { status })&lt;/code&gt; two-argument form — status code must be moved manually&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;migrate-fallback&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fallback migration&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~70%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;HydrateFallback&lt;/code&gt; destination is always a route config, possibly in a different file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fix-form-method&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Silent runtime bugs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~95%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dynamically constructed method strings; type-asserted comparisons&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Overall automation rate: ~85–90% of all mechanical migration changes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The remaining 10–15% is not a gap in the codemod — it is work that is &lt;em&gt;not safely&lt;br&gt;
automatable&lt;/em&gt; without understanding project-specific conventions. Automated transformation&lt;br&gt;
of the wrong file with insufficient context would produce a false positive, which is worse&lt;br&gt;
than leaving it for manual review.&lt;/p&gt;


&lt;h2&gt;
  
  
  5. Real-World Validation
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Methodology
&lt;/h3&gt;

&lt;p&gt;Each repository was cloned at its current &lt;code&gt;main&lt;/code&gt; branch. The codemod was run against&lt;br&gt;
the full repository. Results were reviewed with &lt;code&gt;git diff&lt;/code&gt; to verify correctness. Any&lt;br&gt;
false positive (correct code changed incorrectly) would count as a failure.&lt;/p&gt;
&lt;h3&gt;
  
  
  Repository 1 — remix-run/indie-stack
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; The official Remix TypeScript starter template. Uses React Router v6 with&lt;br&gt;
the data router pattern throughout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What was found:&lt;/strong&gt; 35 files scanned, 5 modified, 0 false positives&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interesting findings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All &lt;code&gt;createBrowserRouter&lt;/code&gt; calls were in &lt;code&gt;app/root.tsx&lt;/code&gt; and correctly received future flags&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;loader&lt;/code&gt; functions throughout used &lt;code&gt;json()&lt;/code&gt; wrappers — all correctly stripped&lt;/li&gt;
&lt;li&gt;Import paths were already partially split between &lt;code&gt;@remix-run/react&lt;/code&gt; and &lt;code&gt;react-router-dom&lt;/code&gt;; only the &lt;code&gt;react-router-dom&lt;/code&gt; imports were touched&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Repository 2 — alan2207/bulletproof-react
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; A heavily-cited React architecture boilerplate demonstrating best practices.&lt;br&gt;
Uses React Router v6 with feature-based routing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What was found:&lt;/strong&gt; 425 files scanned, 2 modified, 0 false positives&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interesting findings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple &lt;code&gt;createBrowserRouter&lt;/code&gt; calls across feature modules — each received flags independently&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useNavigation().formMethod&lt;/code&gt; comparisons in form components — all correctly uppercased&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NavLink as ActiveLink&lt;/code&gt; aliased imports — alias preserved intact through the import split&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Repository 3 — marmelab/react-admin
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; A large-scale React framework for building admin interfaces. One of the&lt;br&gt;
largest React Router v6 codebases publicly available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What was found:&lt;/strong&gt; 1,678 files scanned, 54 modified, 0 false positives&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interesting findings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High density of &lt;code&gt;json()&lt;/code&gt; and &lt;code&gt;defer()&lt;/code&gt; in resource loader definitions — bulk removal worked correctly&lt;/li&gt;
&lt;li&gt;Some &lt;code&gt;json(data, { status: 404 })&lt;/code&gt; two-argument calls were correctly &lt;em&gt;skipped&lt;/em&gt; — the status code would have been silently dropped&lt;/li&gt;
&lt;li&gt;Import specifier lists of 8–10 items per import — specifier ordering preserved&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Repository 4 — novuhq/novu
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt; A large-scale production notification infrastructure platform with a full&lt;br&gt;
React frontend. One of the most heavily React Router-dependent open-source apps available,&lt;br&gt;
with routing spread across dozens of feature modules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What was found:&lt;/strong&gt; 6,452 files scanned, 210 modified, 0 false positives&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interesting findings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;210 files modified across a monorepo with deeply nested package structure — the walker correctly skipped &lt;code&gt;node_modules&lt;/code&gt; and &lt;code&gt;dist&lt;/code&gt; directories at every level&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;RouterProvider&lt;/code&gt; imports were consistently split to &lt;code&gt;react-router/dom&lt;/code&gt; while all other hooks stayed on &lt;code&gt;react-router&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;No false positives despite the large surface area — AST precision and early-exit guards held across all 6,452 files&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Aggregate results
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repository&lt;/th&gt;
&lt;th&gt;Files scanned&lt;/th&gt;
&lt;th&gt;Files modified&lt;/th&gt;
&lt;th&gt;False positives&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;remix-run/indie-stack&lt;/td&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;alan2207/bulletproof-react&lt;/td&gt;
&lt;td&gt;425&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;marmelab/react-admin&lt;/td&gt;
&lt;td&gt;1,678&lt;/td&gt;
&lt;td&gt;54&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;novuhq/novu&lt;/td&gt;
&lt;td&gt;6,452&lt;/td&gt;
&lt;td&gt;210&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8,590&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;271&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  6. Zero False Positives — How We Guarantee It
&lt;/h2&gt;

&lt;p&gt;False positives — cases where the codemod changes correct code incorrectly — are worse&lt;br&gt;
than false negatives. A missed transformation leaves the developer with a TODO. A false&lt;br&gt;
positive silently introduces a bug.&lt;/p&gt;

&lt;p&gt;Three mechanisms work together to guarantee zero false positives:&lt;/p&gt;
&lt;h3&gt;
  
  
  Mechanism 1 — AST matching, not regex
&lt;/h3&gt;

&lt;p&gt;Every transform uses &lt;code&gt;tsx.parse()&lt;/code&gt; from &lt;code&gt;@ast-grep/napi&lt;/code&gt; to build a full TypeScript AST&lt;br&gt;
before making any change. Changes are made by character position of matched AST nodes,&lt;br&gt;
not by line-level pattern matching.&lt;/p&gt;

&lt;p&gt;The practical consequence: a &lt;code&gt;react-router-dom&lt;/code&gt; URL string inside a comment, a JSDoc&lt;br&gt;
block, or a template literal is &lt;strong&gt;never touched&lt;/strong&gt;. Regex matching &lt;code&gt;from 'react-router-dom'&lt;/code&gt;&lt;br&gt;
cannot distinguish these cases. AST matching does so inherently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This comment mentions react-router-dom — untouched ✅&lt;/span&gt;
&lt;span class="c1"&gt;// import { BrowserRouter } from 'react-router-dom' in a dead code comment — untouched ✅&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://npmjs.com/package/react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// untouched ✅&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useNavigate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// transformed ✅&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;code&gt;fix-form-method&lt;/code&gt;, the protection is even more precise. The 16-pattern rule&lt;br&gt;
constrains matching to binary expressions where &lt;strong&gt;one side is specifically&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;navigation.formMethod&lt;/code&gt; or &lt;code&gt;fetcher.formMethod&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// navigation.formMethod === "post" → transformed ✅&lt;/span&gt;
&lt;span class="c1"&gt;// someObj.formMethod === "post"    → untouched ✅  (not navigation or fetcher)&lt;/span&gt;
&lt;span class="c1"&gt;// navigation.state === "post"      → untouched ✅  (not formMethod)&lt;/span&gt;
&lt;span class="c1"&gt;// method === "post"                → untouched ✅  (no member expression)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mechanism 2 — Per-transform try/catch
&lt;/h3&gt;

&lt;p&gt;Every transform wraps its entire logic in a &lt;code&gt;try/catch&lt;/code&gt; that returns the original source&lt;br&gt;
on any error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fileInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fileInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// All AST parsing and manipulation&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fileInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="c1"&gt;// ← original source, unchanged&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An unexpected input that causes a parse error, an unusual TypeScript syntax that confuses&lt;br&gt;
the AST, or any runtime exception in the transform logic produces &lt;strong&gt;zero output change&lt;/strong&gt;&lt;br&gt;
for that file. The transform fails open, not closed.&lt;/p&gt;
&lt;h3&gt;
  
  
  Mechanism 3 — Early exit optimisation
&lt;/h3&gt;

&lt;p&gt;Each transform checks for a plain-string precondition before invoking the AST parser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// update-imports.ts&lt;/span&gt;
&lt;span class="c1"&gt;// tsx.parse() is never called if the file has no react-router-dom import&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tsx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// only reached if precondition passes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not just a performance optimisation. It means a file that cannot possibly be&lt;br&gt;
affected by a transform is &lt;strong&gt;never touched&lt;/strong&gt; — the AST is never even built for it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Alias safety in import removal
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;remove-json-defer&lt;/code&gt; only removes plain &lt;code&gt;json&lt;/code&gt; and &lt;code&gt;defer&lt;/code&gt; specifiers — not aliased imports&lt;br&gt;
like &lt;code&gt;json as rj&lt;/code&gt;. If the codemod stripped &lt;code&gt;json as rj&lt;/code&gt; from the import without also&lt;br&gt;
transforming the &lt;code&gt;rj(data)&lt;/code&gt; call sites (which it cannot find because &lt;code&gt;rj&lt;/code&gt; is not &lt;code&gt;json&lt;/code&gt;),&lt;br&gt;
the result would be a broken import and a false positive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is correctly left UNTOUCHED — alias means call sites use 'rj', not 'json'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;rj&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;rj&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;  &lt;span class="c1"&gt;// call site uses alias&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The check is &lt;code&gt;DEPRECATED.has(s)&lt;/code&gt; where &lt;code&gt;s&lt;/code&gt; is the full specifier string. &lt;code&gt;'json'&lt;/code&gt; is in&lt;br&gt;
the set; &lt;code&gt;'json as rj'&lt;/code&gt; is not.&lt;/p&gt;


&lt;h2&gt;
  
  
  7. Manual Review Required
&lt;/h2&gt;

&lt;p&gt;The codemod leaves &lt;code&gt;TODO&lt;/code&gt; comments and skips certain patterns intentionally. This section&lt;br&gt;
documents exactly what requires human judgment and why it cannot be safely automated.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;fallbackElement&lt;/code&gt; → &lt;code&gt;HydrateFallback&lt;/code&gt; (transform 6)
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;fallbackElement&lt;/code&gt; prop is removed from &lt;code&gt;RouterProvider&lt;/code&gt; and a &lt;code&gt;TODO&lt;/code&gt; comment is&lt;br&gt;
inserted in its place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* TODO: v7 migration — move fallbackElement to HydrateFallback on your root route.
    Add: { path: '/', Component: Root, HydrateFallback: LoadingSpinner } */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RouterProvider&lt;/span&gt; &lt;span class="na"&gt;router&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why not fully automated? &lt;code&gt;HydrateFallback&lt;/code&gt; goes in the &lt;strong&gt;route configuration&lt;/strong&gt;, not on&lt;br&gt;
&lt;code&gt;RouterProvider&lt;/code&gt;. In most apps, route configuration lives in a different file from the&lt;br&gt;
&lt;code&gt;RouterProvider&lt;/code&gt; render. Automatically finding the correct route and injecting&lt;br&gt;
&lt;code&gt;HydrateFallback&lt;/code&gt; into it would require understanding the project's routing architecture —&lt;br&gt;
which file contains the root route, whether routes are co-located or centralised, whether&lt;br&gt;
they use the object syntax or JSX routes. This is project-specific knowledge that a&lt;br&gt;
general-purpose codemod cannot safely infer.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;json(data, { status, headers })&lt;/code&gt; — two-argument form (transform 5)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is intentionally SKIPPED&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In v7, to return a response with a custom status code or headers, the pattern changes&lt;br&gt;
from &lt;code&gt;json(data, init)&lt;/code&gt; to returning a &lt;code&gt;Response&lt;/code&gt; object directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// v7 manual fix&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Automatically transforming &lt;code&gt;json(data, init)&lt;/code&gt; to &lt;code&gt;return data&lt;/code&gt; would silently discard&lt;br&gt;
the status code and headers. This is a correctness issue — the transform skips&lt;br&gt;
two-argument &lt;code&gt;json()&lt;/code&gt; calls and leaves them unchanged. The developer will see a TypeScript&lt;br&gt;
error on the &lt;code&gt;json&lt;/code&gt; import after it is removed from the import specifiers, which serves&lt;br&gt;
as a clear signal to address these call sites manually.&lt;/p&gt;
&lt;h3&gt;
  
  
  Complex nested route structures
&lt;/h3&gt;

&lt;p&gt;The codemod injects future flags into &lt;code&gt;createBrowserRouter(routes, opts)&lt;/code&gt; where &lt;code&gt;routes&lt;/code&gt;&lt;br&gt;
is the first argument. It does not inspect or modify the route configuration array itself.&lt;br&gt;
Route configuration changes required by v7 — such as migrating from &lt;code&gt;element&lt;/code&gt; to&lt;br&gt;
&lt;code&gt;Component&lt;/code&gt;, or restructuring nested routes to take advantage of improved splat path&lt;br&gt;
resolution — require understanding the application's URL structure and are out of scope.&lt;/p&gt;
&lt;h3&gt;
  
  
  Custom history objects
&lt;/h3&gt;

&lt;p&gt;React Router v6 allows &lt;code&gt;&amp;lt;Router history={customHistory}&amp;gt;&lt;/code&gt; for non-standard environments&lt;br&gt;
(Electron apps, React Native with custom navigation, server-side rendering with&lt;br&gt;
&lt;code&gt;createStaticRouter&lt;/code&gt;). These patterns are not transformed because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;Router&amp;gt;&lt;/code&gt; is not in the list of target components (only &lt;code&gt;BrowserRouter&lt;/code&gt;, &lt;code&gt;HashRouter&lt;/code&gt;,
&lt;code&gt;MemoryRouter&lt;/code&gt;, &lt;code&gt;RouterProvider&lt;/code&gt; are targeted)&lt;/li&gt;
&lt;li&gt;The correct v7 equivalent depends on which environment the custom history is for&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  TypeScript loader and action type changes
&lt;/h3&gt;

&lt;p&gt;React Router v7 changes the generic parameter signatures of &lt;code&gt;useLoaderData&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;useActionData&lt;/code&gt;, and related hooks. The v7 way is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// v6&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLoaderData&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// v7 — infer from loader function directly&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useLoaderData&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Automating this would require: finding each &lt;code&gt;useLoaderData&lt;/code&gt; call, identifying which&lt;br&gt;
loader function it corresponds to (by route configuration analysis), and rewriting&lt;br&gt;
the generic parameter. This is a multi-file analysis problem that is beyond&lt;br&gt;
single-file transform scope.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Migration Time Comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Manual migration of a 50-file codebase
&lt;/h3&gt;

&lt;p&gt;A typical React Router v6 app with 50 files touching router code requires:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Time estimate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read upgrade guide and understand scope&lt;/td&gt;
&lt;td&gt;20–30 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;grep&lt;/code&gt; for all &lt;code&gt;react-router-dom&lt;/code&gt; imports&lt;/td&gt;
&lt;td&gt;5 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edit each import file (avg. 3 min × 20 files)&lt;/td&gt;
&lt;td&gt;60 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find and update all &lt;code&gt;createBrowserRouter&lt;/code&gt; calls&lt;/td&gt;
&lt;td&gt;20–30 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find and remove all &lt;code&gt;json()&lt;/code&gt; / &lt;code&gt;defer()&lt;/code&gt; wrappers&lt;/td&gt;
&lt;td&gt;30–45 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find all &lt;code&gt;formMethod&lt;/code&gt; comparisons (easy to miss)&lt;/td&gt;
&lt;td&gt;15–20 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handle &lt;code&gt;fallbackElement&lt;/code&gt; props&lt;/td&gt;
&lt;td&gt;15–20 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run TypeScript, fix type errors from missed cases&lt;/td&gt;
&lt;td&gt;30–60 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual testing to verify no runtime regressions&lt;/td&gt;
&lt;td&gt;30–60 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~3.5–5 hours&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This estimate assumes the developer has read the upgrade guide in full, knows about the&lt;br&gt;
&lt;code&gt;RouterProvider&lt;/code&gt; import path exception, and does not make any mistakes that require&lt;br&gt;
backtracking.&lt;/p&gt;

&lt;h3&gt;
  
  
  With this codemod
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx codemod arpit2222/react-router-v6-to-v7   &lt;span class="c"&gt;# ~20–30 seconds&lt;/span&gt;
git diff &lt;span class="nt"&gt;--stat&lt;/span&gt;                                 &lt;span class="c"&gt;# review what changed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run codemod&lt;/td&gt;
&lt;td&gt;~30 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Review &lt;code&gt;git diff&lt;/code&gt; output&lt;/td&gt;
&lt;td&gt;5–10 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Address &lt;code&gt;TODO&lt;/code&gt; comments (fallbackElement)&lt;/td&gt;
&lt;td&gt;5–10 min per instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handle &lt;code&gt;json(data, { status })&lt;/code&gt; skipped calls&lt;/td&gt;
&lt;td&gt;5 min per instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run TypeScript to confirm&lt;/td&gt;
&lt;td&gt;2–5 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~20–30 minutes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Time saved per project
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Project size&lt;/th&gt;
&lt;th&gt;Manual&lt;/th&gt;
&lt;th&gt;With codemod&lt;/th&gt;
&lt;th&gt;Time saved&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Small (&amp;lt; 10 router files)&lt;/td&gt;
&lt;td&gt;1–2 hours&lt;/td&gt;
&lt;td&gt;~15 min&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~1.5 hours&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Medium (10–30 files)&lt;/td&gt;
&lt;td&gt;3–5 hours&lt;/td&gt;
&lt;td&gt;~25 min&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~4 hours&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large (30–80 files)&lt;/td&gt;
&lt;td&gt;6–10 hours&lt;/td&gt;
&lt;td&gt;~35 min&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~8 hours&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Very large (80+ files)&lt;/td&gt;
&lt;td&gt;2–3 days&lt;/td&gt;
&lt;td&gt;~60 min&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~2 days&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The codemod's value scales with codebase size. In a large monorepo where React Router&lt;br&gt;
is used across 50+ packages, manual migration becomes a multi-week coordination effort.&lt;br&gt;
The codemod reduces it to a single PR.&lt;/p&gt;




&lt;h2&gt;
  
  
  Appendix — All 6 Future Flags Explained
&lt;/h2&gt;

&lt;p&gt;Each flag maps to a specific behaviour change in v7:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;What it changes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;v7_relativeSplatPath&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fixes relative path resolution inside splat routes — paths like &lt;code&gt;../sibling&lt;/code&gt; now resolve correctly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;v7_startTransition&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wraps all navigation state updates in &lt;code&gt;React.startTransition&lt;/code&gt;, preventing navigation from blocking rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;v7_fetcherPersist&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keeps fetcher data alive during revalidation — fetchers no longer reset while a parent loader is revalidating&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;v7_normalizeFormMethod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;formMethod&lt;/code&gt; on &lt;code&gt;useNavigation()&lt;/code&gt; and &lt;code&gt;useFetcher()&lt;/code&gt; returns uppercase (&lt;code&gt;"POST"&lt;/code&gt;) instead of lowercase (&lt;code&gt;"post"&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;v7_partialHydration&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enables partial hydration for server-rendered apps — routes can hydrate independently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;v7_skipActionErrorRevalidation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skips automatic revalidation of all loaders when an action throws — only the affected route revalidates&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All six must be set to &lt;code&gt;true&lt;/code&gt; before upgrading to avoid breaking changes landing as&lt;br&gt;
silent behaviour differences rather than hard errors.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>javascript</category>
      <category>react</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
