Skip to content

feat(migrate): cover the remaining v9 breaking changes - #31355

Open
ShaneK wants to merge 2 commits into
major-9.0from
chore/migrations
Open

feat(migrate): cover the remaining v9 breaking changes#31355
ShaneK wants to merge 2 commits into
major-9.0from
chore/migrations

Conversation

@ShaneK

@ShaneK ShaneK commented Aug 13, 2026

Copy link
Copy Markdown
Member

Issue number: internal


What is the current behavior?

Currently, @ionic/migrate covers 15 of the breaking changes in the v9 upgrade guide. The Angular moduleResolution change and @ionic/core's new exports field aren't in there, and neither are about a dozen others.

There's also a gap for vanilla apps: detectFrameworks only knows @ionic/angular, @ionic/react, and @ionic/vue, so a vanilla app prints "No @ionic/{angular,react,vue} dependency found" and exits. Every migration marked framework: 'core' is unreachable for the apps it was written for.

What is the new behavior?

This PR adds 14 migrations, taking the registry to 29 (12 auto-fix, 17 report-only). The full table is in the new packages/migrate/docs/v9.md. The README is version-agnostic now, so the next major gets its own page instead of a rewrite.

We also detect @ionic/core as a framework, so vanilla apps run the core migrations and get their pin bumped. We gate the version check on the binding package when there's one, because an app on a v9 binding with a stale @ionic/core pin is already migrated, and taking the lowest major would re-run the single-shot import rewrite and corrupt it.

Does this introduce a breaking change?

  • Yes
  • No

Other information

Note: this PR is based off of the end result of ionic-docs#4620. Ten of the docs anchors these migrations link to only exist on that branch.

Two v9 changes stay uncovered on purpose, with the reasoning in docs/v9.md: React Router's /* suffix for nested routes, and which components Angular 22's OnPush default affects. We do flag the Angular 22 version itself.

@ShaneK
ShaneK requested a review from a team as a code owner August 13, 2026 20:05
@ShaneK
ShaneK requested a review from OS-jacobbell August 13, 2026 20:05
@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
ionic-framework Ready Ready Preview Aug 13, 2026 8:05pm

Request Review

@codeCraft-Ritik codeCraft-Ritik left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Really thorough PR, @ShaneK. A few things I appreciated while going through this:

The vanilla app gap fix — detectFrameworks() excluding core meant every framework: 'core' migration was dead code for the apps it was written for. Adding @ionic/core to the detection map and introducing sourceMajor() is the right solution. The binding-over-core priority prevents the corruption scenario where a v9 Angular app with a stale @ionic/core@^8 pin would re-select destructive migrations.

core-modal-handle's brace-balancing — Scoping handleBehavior checks to the enclosing options object (via enclosingObject()) is a thoughtful touch. Without it, one opted-out sheet modal would silence every other sheet in the file. The Vue .vue test case (template opt-out shouldn't suppress script block findings) is a great edge case to cover.

core-form-structure's rule ordering — Putting the slot rules before the broader wrapper rule so the guide's own example selector (ion-input .input-wrapper .native-wrapper [slot="start"]) gets the .input-start answer instead of .input-control shows attention to the developer experience of the report output.

ScanMatch union type — Letting a single migration emit per-finding docsUrl overrides is a clean abstraction. core-select-events linking ionChange findings to one anchor and selected role findings to another is exactly the kind of granularity that saves someone's debugging time.

Minor note: the core-textarea-height scan for 56px scoped to files mentioning ion-textarea is a pragmatic tradeoff — 56px is too common to flag globally, but it does mean a stylesheet that sets the height on a selector like .my-textarea-wrapper without the tag name would be missed. Probably fine for real-world usage though.

Solid work taking the registry to 29 with full fixture-backed tests. 👏

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants