Files
Obsidian-Vault/Work/Projects/Laravel-v12-Migration/Laravel-12-Migration-Problems-and-Solutions.md
2026-02-24 08:10:57 +01:00

37 KiB

Laravel 12 Migration: Problems & Solutions Reference

Compiled from 6 real project migrations (Contentmakers, QLS Handling, Deversspecialist, Cleanshopping, Goos, HGBI). Intended audience: an AI assistant performing the next Laravel 12 upgrade, or a human developer doing it manually.


How to Use This Document

Read this before starting any Laravel 11 → 12 migration. Every item below is a real, observed problem — not hypothetical. The document is organized by upgrade area. Each entry follows the pattern:

  • Problem: what went wrong
  • Root Cause: why it happened
  • Fix: what resolved it
  • Projects affected: which projects hit this issue

Prompt for AI Assistants

Before starting, place these two files in the project root (or provide them in the conversation context):

  1. This reference document (Laravel-12-Migration-Problems-and-Solutions.md)
  2. The update log template (Logs/TEMPLATE.md) — use it as the structure for the new project's update log

Copy-paste this prompt when starting a new project migration with an AI assistant:

Hi, we are going to update to the next version of Laravel and update everything that goes along with it. So please run composer outdated. Gather all the resources you need to do the updates and then make a plan.

There is a file "Laravel 12 Migration: Problems & Solutions Reference.md" — it contains all the errors and weirdness we have encountered in previous updates. Take these things into account if relevant for this project. And make an update log yourself (use the TEMPLATE.md as the structure) so we can later add that to the document for any other projects that need updating.

Then when the Composer work has been done, do npm outdated and do the same thing. Gather all the resources, make a plan, and keep track of all the exceptions and log anything out of the ordinary in the same markdown file.

If anything is unclear, use the AskUserQuestion skill. When in doubt, ask. Ask about approach, preferences, naming, styling, scope — anything. Multiple questions per interaction are encouraged. Do not assume — confirm with the user first.


Table of Contents

  1. #Pre-Flight / Preparation
  2. #Composer & Package Resolution
  3. #Laravel 12 Breaking Changes
  4. #Filament v3 → v5
  5. #Livewire v3 → v4
  6. #Laravel Passport 12 → 13
  7. #Tailwind CSS v3 → v4
  8. #PHPUnit & Pest Upgrades
  9. #ESLint Migration
  10. #NPM / Frontend Build
  11. #Inertia v1 → v2
  12. #Browser Testing
  13. #Post-Upgrade Verification Checklist
  14. #Recommended Upgrade Order

Pre-Flight / Preparation

1. Always clear bootstrap cache after major package changes

Problem: After removing, replacing, or upgrading packages, php artisan commands fail with Class ... ServiceProvider not found errors. Examples: Barryvdh\Debugbar\ServiceProvider, CodeZero\Localizer\LocalizerServiceProvider, Filament\Upgrade\UpgradeServiceProvider.

Root Cause: Stale bootstrap/cache/packages.php and bootstrap/cache/services.php retain references to old/removed service providers.

Fix: Delete both files and re-run discovery:

rm -f bootstrap/cache/packages.php bootstrap/cache/services.php
composer dump-autoload

Rule: Do this after every major Composer operation (package removal, replacement, or major version upgrade).

Projects affected: All 6 projects.


2. Composer cache path issues in sandboxed environments

Problem: composer outdated fails with Cannot create cache directory.

Fix: Run with COMPOSER_CACHE_DIR=/tmp/composer-cache composer outdated.

Projects affected: Deversspecialist.


3. Record baseline test count before starting

Problem: After PHPUnit major upgrades, test counts can silently drop (e.g., 14 → 3) because test discovery rules changed. Without knowing the baseline, this goes unnoticed.

Rule: Before any upgrade work, record the exact test count and assertion count. After each upgrade phase, verify the count has not dropped.

Projects affected: Contentmakers, Goos.


Composer & Package Resolution

4. codezero packages block Laravel 12

Problem: codezero/laravel-localized-routes (^4), codezero/laravel-localizer (^3), and codezero/laravel-uri-translator (^2) all hard-require illuminate/support ^10.0|^11.0. No stable release supports Laravel 12.

Fix:

  • Replace codezero/laravel-localized-routes with opgginc/codezero-laravel-localized-routes:^5.0 (maintained fork, same CodeZero\LocalizedRoutes namespace, drop-in replacement).
  • Remove codezero/laravel-localizer entirely (typically unused — verify use_localizer is false in config, or that no PHP code references CodeZero\Localizer).
  • Set codezero/composer-preload-files to false in allow-plugins.

Projects affected: All 6 projects.

5. codezero/composer-preload-files blocks Composer after removal

Problem: After removing codezero packages, the composer-preload-files plugin still exists in vendor as a transitive dependency. Composer refuses to run any command.

Fix: composer config --no-plugins allow-plugins.codezero/composer-preload-files false

Projects affected: Goos, Deversspecialist.

6. Localized routes: /en/ prefix no longer generated after fork switch

Problem: After switching to opgginc/codezero-laravel-localized-routes ^5, English locale redirects lose their /en/ prefix (e.g., /ds instead of /en/ds), even when omitted_locale is set to nl.

Status: Open / needs investigation per project. May be a config difference in the v5 fork.

Projects affected: Goos.

7. resend/resend-laravel must move to ^1.0 for Laravel 12

Problem: resend/resend-laravel ^0.14 or ^0.23 only supports Illuminate 10/11.

Fix: Upgrade to resend/resend-laravel ^1.0. No API changes encountered in practice.

Projects affected: Contentmakers, Deversspecialist.

8. laravel-lang packages need major version bump

Problem: laravel-lang/lang:^12 and laravel-lang/common:^3 are anchored to a publisher line that only supports Illuminate <=11.

Fix: Move to newer major lines compatible with Laravel 12 (check latest available versions at upgrade time).

Projects affected: Contentmakers.

9. saloonphp/laravel-http-sender removal breaks Saloon config

Problem: After removing the abandoned saloonphp/laravel-http-sender package, tests fail because config/saloon.php still references Saloon\HttpSender\HttpSender (from the removed package) or Saloon\Laravel\HttpSender (which does not exist in v3).

Fix: Change config/saloon.php sender class to Saloon\Http\Senders\GuzzleSender (the default in saloonphp/laravel-plugin v3).

Projects affected: HGBI.

10. spatie/icalendar-generator v3 enum syntax change

Problem: Call to undefined method Spatie\IcalendarGenerator\Enums\EventStatus::confirmed(). v3 switched from method-based enums to PHP 8.1 backed enums.

Fix: Change method calls to enum case access:

  • EventStatus::confirmed()EventStatus::Confirmed
  • Classification::private()Classification::Private

Projects affected: HGBI.

11. Composer post-update-cmd scripts can trigger Filament upgrade side effects

Problem: Running composer require or composer update triggers php artisan filament:upgrade via post-update-cmd, republishing Filament assets and creating large git diffs.

Fix: Treat as expected side effect. Group Filament public-asset changes and commit separately.

Rule: On upgrade branches, assume Composer package changes may create large Filament asset diffs.

Projects affected: Contentmakers, HGBI.


Laravel 12 Breaking Changes

10. HasUuids generates UUID v7 instead of v4

Problem: HasUuids trait now generates UUID v7 by default. This can break tests or code that expects UUID v4 format.

Fix: Use use HasVersion4Uuids as HasUuids; in affected models (e.g., Attachment, JobApplication).

Projects affected: Deversspecialist.

11. image validation rule excludes SVGs

Problem: The image validation rule no longer accepts SVG files in Laravel 12.

Status: Not encountered in practice across any project, but worth monitoring during testing.

12. route:list can throw due to controller-constructor abort guards

Problem: php artisan route:list --path=admin fails with NotFoundHttpException because the command instantiates controllers during route metadata collection, and some controllers have abort_unless(...) in their constructor.

Fix: Use php artisan about + tests/build for smoke checks instead. Long-term: move hard feature gates out of constructors into middleware.

Rule: Avoid abort_* calls in controller constructors.

Projects affected: Contentmakers.


Filament v3 → v5

You cannot skip v4. Filament must be upgraded v3 → v4 → v5 in sequence. Each step has its own rector script (vendor/bin/filament-v4, vendor/bin/filament-v5). Running the v5 rector on a v3 codebase will produce incorrect or incomplete results. The v4 rector must run first and the v4 packages must be installed before proceeding to v5.

Republish Filament assets if they were published. Many projects have published Filament assets (CSS, JS, fonts) to public/css/filament/, public/js/filament/, and public/fonts/filament/. After completing the Filament upgrade, run php artisan filament:upgrade --no-interaction to republish these assets for v5. If you skip this, the admin panel may load with broken or missing styles/scripts. This command also clears config, route, and view caches. Expect a large git diff from the republished assets — commit them separately.

13. Rector scripts require explicit directory input

Problem: vendor/bin/filament-v4 and vendor/bin/filament-v5 require interactive input (directory prompt). Running without input causes MissingInputException.

Fix: Pipe input: echo "app" | vendor/bin/filament-v4 and echo "modules" | vendor/bin/filament-v4. Run for EACH custom namespace directory separately: app/, cms/, commerce/, modules/, support/, etc.

Rule: Always enumerate all custom source roots before running Filament codemods.

Projects affected: All projects with Filament.

14. Rector scripts miss certain transformations

The rector/codemod tools do not catch everything. The following require manual fixes:

a. Filament\Pages\Actions\*Filament\Actions\*

Problem: Old action imports remain after rector. Fix: Grep for use Filament\Pages\Actions; and replace all matches with use Filament\Actions;. Projects affected: Contentmakers, Deversspecialist, Goos.

b. Filament\Forms\Set|GetFilament\Schemas\Components\Utilities\Set|Get

Fix: Search and replace across all Filament resource files. Projects affected: Contentmakers, Deversspecialist.

c. Form/Schema layout components moved to Filament\Schemas\...

Problem: Layout/utility classes moved namespaces but rector didn't update all imports. Fix: Migrate these imports:

Old New
Filament\Forms\Components\Fieldset Filament\Schemas\Components\Fieldset
Filament\Forms\Components\Grid Filament\Schemas\Components\Grid
Filament\Forms\Components\Section Filament\Schemas\Components\Section
Filament\Forms\Components\Tabs\Tab Filament\Schemas\Components\Tabs\Tab
Filament\Forms\Components\Component Filament\Schemas\Components\Component
Filament\Forms\Get Filament\Schemas\Components\Utilities\Get
Filament\Forms\Set Filament\Schemas\Components\Utilities\Set

Projects affected: Contentmakers.

d. form(Form $form): Formform(Schema $schema): Schema

Problem: Partial codemod application leaves old signatures in some resources/relation managers. Fix: Grep for use Filament\Forms\Form; and remove all remaining occurrences. Update method signatures. Projects affected: Contentmakers.

e. $navigationGroup and $navigationIcon type changes (Filament v4)

Problem: protected static ?string $navigationGroup must be protected static string | \UnitEnum | null $navigationGroup. Same for $navigationIconstring | \BackedEnum | null. Rector does NOT catch this. Fix: Search for protected static ?string $navigationGroup and $navigationIcon and update types manually. Not needed if the project uses method overrides instead of static properties. Projects affected: Deversspecialist.

15. TextInput\Mask builder class removed

Problem: Legacy closures typed as Forms\Components\TextInput\Mask are invalid in Filament v5.

Fix: Replace with v5-safe numeric/step/prefix configuration (e.g., ->numeric()->step(0.01)->minValue(0)->prefix('€')). Grep for TextInput\Mask and rewrite.

Projects affected: Contentmakers, HGBI.

16. Filament v5 Component::$container uninitialized error

Problem: Typed property Filament\Schemas\Components\Component::$container must not be accessed before initialization — thrown when calling getChildComponents() on Filament form components outside a Livewire context (e.g., during frontend page rendering via CMS Block systems).

Root Cause: Filament v5's getChildComponents() creates a Schema wrapper that requires getLivewire(), which fails outside Filament admin panel context.

Fix: Replace getChildComponents() with getDefaultChildComponents() which returns raw component arrays without requiring a Schema/Livewire container. In hasSettings() methods, use the safe getSchemaChildComponents() resolver and treat closure schemas as non-resolvable (false).

Projects affected: Contentmakers, Goos.

17. Field::make() signature change

Problem: make(string $name) changed to make(?string $name = null). Any subclass overriding make() with string $name = '' must update.

Fix: Change to make(?string $name = null) and pass $name ?? '' to parent.

Projects affected: Goos, HGBI.

18. ViewAction on ListRecords pages crashes in v5

Problem: ViewAction::make() in getHeaderActions() on a list page causes getViewAuthorizationResponse(): Argument #1 ($record) must be of type Model, null given.

Root Cause: In v5, ViewAction tries to authorize against a record, but list pages have no record.

Fix: Replace ViewAction::make() with Action::make('name') for navigation-only header actions.

Projects affected: Goos.

19. Filament v5 evaluates column labels during initialization

Problem: $livewire->tableFilters is null before filters are populated, causing "Trying to access array offset on null" errors when accessed in column labels/formatters.

Fix: Use null-safe access with ?? null or ?? 'default' defaults when accessing filter values in column labels.

Projects affected: Deversspecialist.

20. filament:upgrade creates large public asset diffs

Problem: php artisan filament:upgrade --no-interaction republishes many files under public/js/filament/*, public/css/filament/*, and public/fonts/filament/*.

Fix: Run once per upgrade pass, review/commit asset churn separately. Do not run repeatedly.

Projects affected: Contentmakers, Deversspecialist.

21. Filament v5 rector can print Rector cache deletion errors but still exit successfully

Problem: Commands report Unable to delete ... rector_cached_files ... No such file or directory but finish with success messaging.

Fix: Do not trust command success text alone. Run a post-codemod class-import existence scan and targeted runtime smoke checks.

Projects affected: Contentmakers.

22. Incompatible Filament plugins

Several third-party Filament plugins do not support v4 or v5:

Plugin Issue Fix
awcodes/filament-table-repeater No v4/v5 version Replace with standard Filament\Forms\Components\Repeater
johncarter/filament-focal-point-picker Only supports ^3.0|^4.0 Remove, replace with TextInput storing coordinates as string
filament/spatie-laravel-translatable-plugin Deprecated in v4, no v5 Replace with lara-zeus/spatie-translatable:^2.0

Note: lara-zeus/spatie-translatable requires Filament v5 (not v4). If upgrading v3→v4→v5, skip translatable during the v4 step and install with v5.

Projects affected: Deversspecialist, Goos, HGBI.

23. Post-codemod verification scan

After running all rector/codemod scripts, run these verification commands:

# Check for old Filament namespace imports that rector missed
grep -r "use Filament\\Pages\\Actions;" app/ cms/ commerce/ modules/ support/
grep -r "use Filament\\Forms\\Form;" app/ cms/ commerce/ modules/ support/
grep -r "use Filament\\Forms\\Components\\Fieldset;" app/ cms/ commerce/ modules/ support/
grep -r "use Filament\\Forms\\Get;" app/ cms/ commerce/ modules/ support/
grep -r "use Filament\\Forms\\Set;" app/ cms/ commerce/ modules/ support/
grep -r "TextInput\\Mask" app/ cms/ commerce/ modules/ support/
grep -r "protected static ?string \$navigationGroup" app/ cms/ commerce/ modules/ support/

Livewire v3 → v4

24. Config key renames

Old Key New Key
layout component_layout
lazy_placeholder component_placeholder

Projects affected: QLS Handling.

25. wire:model behavior unchanged (deferred by default)

wire:model without .live works the same in both v3 and v4 (deferred by default). No action needed.

26. Potential gotchas (not encountered but worth knowing)

  • wire:model.blur / .change behavior changed: in v4, these modifiers now control client-side state sync timing too. Add .live before them for old behavior: wire:model.live.blur.
  • Component tags must be closed: unclosed <livewire:component> tags are interpreted as slot content in v4.
  • @livewireStyles/@livewireScripts: still work with inject_assets => true.

Laravel Passport 12 → 13

27. Passport::hashClientSecrets() removed

Secrets are always hashed in v13. Remove all calls to Passport::hashClientSecrets().

Projects affected: QLS Handling.

28. Middleware renamed

Old New
CheckClientCredentials CheckToken
CheckClientCredentialsForAnyScope CheckTokenForAnyScope
CheckScopes CheckToken
CheckForAnyScope CheckTokenForAnyScope
CheckCredentials (abstract) ValidateToken

Projects affected: QLS Handling.

29. ClientRepository::create() now protected

Use named factory methods instead:

Old New
$repo->create(null, $name, '') $repo->createClientCredentialsGrantClient($name)
$repo->createPersonalAccessClient(...) $repo->createPersonalAccessGrantClient($name)

New methods return a Client with $client->plainSecret property containing the unhashed secret.

Projects affected: QLS Handling.

30. setSecretAttribute() removed

Passport 13 uses an Eloquent Attribute mutator that auto-hashes on assignment. Just set $client->secret = $value;.

Projects affected: QLS Handling.

31. No database migration required

Passport 13 is backward compatible with the old schema. ClientRepository::create() auto-detects columns via getColumnListing().

However, the Passport 13 factory writes to new column names, so you may need a custom ClientFactory that maps to the old schema until you migrate to the new schema.

Projects affected: QLS Handling.

32. Config cleanup

Remove deprecated config keys from config/passport.php:

  • client_uuids — UUIDs are now always used
  • personal_access_client — personal access clients table removed

Projects affected: QLS Handling.


Tailwind CSS v3 → v4

This must be its own separate step. Do not bundle Tailwind v4 into a single pass with other upgrades. The migration touches CSS architecture (PostCSS config, utility directives, theme tokens, custom classes) and frequently produces visual regressions that are invisible to automated tests. A developer must manually check the frontend after migration — open key pages in a browser and compare against the pre-upgrade state. Do not consider this step complete based on npm run build succeeding alone; a green build does not mean the layout is correct.

Always try the official upgrade tool first (npx @tailwindcss/upgrade). Even when it fails (and it often does — see #33), it still saves significant work by migrating directives, class syntax, and config structure. The effort spent fixing blockers to get the tool to run is almost always less than doing the entire migration manually. The workflow is: run the tool → fix whatever it chokes on → run it again → repeat until it succeeds → then do the manual cleanup for anything it missed.

33. Automated upgrade tool (@tailwindcss/upgrade) often fails

Problem: The upgrade tool fails with:

  • Cannot destructure property 'negative' (callback-based theme extensions)
  • variants is not a function (third-party plugins like @formkit/themes/tailwindcss)
  • Cannot apply unknown utility class 'pl-container' (@apply referencing custom classes)
  • Refuses dirty git state (run with --force)
  • Scans backup directories (node_modules.bak)

Recommended workflow:

  1. Always try the automated tool first
  2. If it errors, resolve blockers and re-run (do not immediately switch to manual migration)
  3. Comment out problematic custom classes temporarily if needed
  4. Run with --force on upgrade branches
  5. Unpack custom-class-in-custom-class patterns to raw utilities before running

Projects affected: Contentmakers, Deversspecialist, Goos, HGBI.

34. PostCSS plugin package split

Problem: Vite build fails with "trying to use tailwindcss directly as a PostCSS plugin".

Fix: Install @tailwindcss/postcss and update postcss.config.js:

// Old
module.exports = { plugins: { tailwindcss: {}, autoprefixer: {} } }
// New
module.exports = { plugins: { '@tailwindcss/postcss': {} } }

Remove autoprefixer package (no longer needed).

Projects affected: All 6 projects.

35. @layer base@utility for custom utility classes

Problem: Custom classes defined with @apply inside @layer base {} fail with Cannot apply unknown utility class.

Fix: Convert to @utility blocks instead of @layer base. Expand any custom-class-in-custom-class usage to raw utility classes.

Projects affected: Contentmakers, Deversspecialist, Goos, HGBI.

36. @import must precede all other statements

Problem: Warning about @import appearing after @theme {}.

Fix: Move all @import statements to the very top of the CSS file, right after @import "tailwindcss".

Projects affected: Deversspecialist, Goos.

37. Tailwind v4 silently overrides custom .container class

Problem: Major frontend layout degradation after upgrade. Tailwind v4 no longer supports corePlugins.container = false from the JS config in CSS-first mode. The core .container utility is generated and overrides the project's custom .container.

Fix: In resources/css/app.css, add:

@source not inline('container');
@source not inline('container!');

Verification: Built CSS should contain only the custom .container rule.

Projects affected: Contentmakers, HGBI.

38. Remove stale tailwind.config.js after migration

Problem: After migrating to CSS-first config, the old JS config file remains, creating ambiguity.

Fix: After verifying no @config references exist in the CSS, remove tailwind.config.js.

Projects affected: Contentmakers, Deversspecialist, Goos.

39. Blade-injected runtime colors require @theme inline

Problem: Color utilities tied to runtime CSS variables (e.g., from app.blade.php) behave inconsistently.

Fix: Use @theme inline instead of @theme for color tokens that reference runtime CSS variables:

@theme inline {
  --color-primary: var(--primary-color);
  --color-primary-medium: var(--primary-color);
}

Rule: After migration, grep for custom color utility names and verify each has a corresponding --color-* theme variable.

Projects affected: Contentmakers.

40. Removed TW v3 plugins (now built into v4)

These plugins no longer exist as separate packages in Tailwind v4:

  • @tailwindcss/forms
  • @tailwindcss/typography
  • @tailwindcss/aspect-ratio
  • autoprefixer

Remove them from devDependencies. Use --force if npm uninstall fails with peer dependency conflicts.

Projects affected: All 6 projects.

41. tailwindcss/defaultTheme module removed in TW v4

Problem: Cannot find module 'tailwindcss/defaultTheme' during upgrade tool or build. Config files that import defaultTheme for font family fallback stacks break.

Fix: Remove the import and inline the default font stack directly in the config.

Projects affected: HGBI.

42. Deprecated TW v3 opacity class patterns

Problem: TW v3 patterns like bg-gray-500 bg-opacity-75 and ring-black ring-opacity-5 are deprecated in v4. They still work but generate warnings and may break in future versions.

Fix: Replace with modern slash syntax:

  • bg-gray-500 bg-opacity-75bg-gray-500/75
  • ring-black ring-opacity-5ring-black/5

Grep for opacity- in Vue/Blade templates to find all instances.

Projects affected: HGBI.

43. eslint-plugin-tailwindcss is not Tailwind v4 compatible

Problem: Linting fails with ERR_PACKAGE_PATH_NOT_EXPORTED for tailwindcss/resolveConfig. The plugin requires tailwindcss ^3.4.0.

Fix: Remove eslint-plugin-tailwindcss from devDependencies and ESLint config. TW v4 support is still WIP upstream.

Watch out: If using strixi/laravel-quality or similar packages, they may re-add eslint-plugin-tailwindcss config on composer update. Remove again after Composer operations.

Projects affected: All 6 projects.


PHPUnit & Pest Upgrades

42. PHPUnit 12 drops /** @test */ annotation support

Problem: Tests using /** @test */ doc-comment annotations are silently not discovered. Test count drops without errors.

Fix: Replace all /** @test */ annotations with #[PHPUnit\Framework\Attributes\Test] attribute. Add use PHPUnit\Framework\Attributes\Test; import. Alternatively, prefix methods with test_.

Rule: After PHPUnit major upgrades, always compare test counts to the baseline.

Projects affected: Contentmakers, Goos.

43. Pest v4 requires protected setUp() in TestCase

Problem: Access level to Pest\Concerns\Testable::setUp() must be public (as in class Tests\TestCase) — fatal error on any test run.

Fix: Change public function setUp(): void to protected function setUp(): void in tests/TestCase.php.

Projects affected: Contentmakers, Goos.

44. Pint can break setUp() visibility

Problem: Running vendor/bin/pint can normalize setUp() method visibility in a way that conflicts with the TestCase chain, causing setUp() visibility conflict (must be public).

Fix: Remove no-op setUp() overrides from affected tests. Always re-run tests after Pint on upgrade branches.

Projects affected: Contentmakers.

45. PHPUnit XML config deprecation warning

Problem: Your XML configuration validates against a deprecated schema — non-blocking, cosmetic warning from PHPUnit 12.

Fix: Run --migrate-configuration if desired, or ignore (non-blocking).

Projects affected: Deversspecialist.

46. Hidden regressions surfaced after restoring test discovery

Problem: After fixing @test#[Test], previously-hidden test failures appear (e.g., Call to undefined function get_classes_in_namespace()).

Fix: Treat newly exposed failures as real upgrade regressions and fix them. Example: add missing helper functions locally if dependencies no longer provide them.

Projects affected: Contentmakers.


ESLint Migration

47. eslint-plugin-vue v10 requires flat config migration

Problem: Linting fails with legacy config errors (plugin:vue/vue3-recommended not found).

Fix: Create eslint.config.cjs (or .mjs) and migrate rules/plugins from .eslintrc.json. Keep ESLint on v9 (v10 may have additional peer compatibility issues with @typescript-eslint v8).

Rule: Treat ESLint plugin major upgrades as config-schema migrations, not package-only updates.

Projects affected: Contentmakers.

48. npm dependency graph deadlock during ESLint upgrades

Problem: npm install fails repeatedly with ERESOLVE could not resolve when upgrading ESLint plugins.

Fix: Regenerate the npm install tree cleanly — remove node_modules and package-lock.json, then reinstall.

Projects affected: Contentmakers.


NPM / Frontend Build

49. Ziggy v2 requires qs-esm npm package

Problem: Vite build fails with Rollup failed to resolve import "qs-esm" from Ziggy v2 JS source.

Fix: npm install qs-esm

Rule: After Ziggy major upgrades, run a full build immediately.

Projects affected: Contentmakers.

50. laravel-vite-plugin v2 requires Vite 7

Problem: npm install fails — laravel-vite-plugin ^2 has peer vite@"^7.0.0".

Fix: Upgrade vite to ^7.0.0. May need --force flag.

Projects affected: Deversspecialist, Goos, HGBI.

51. Clean npm install can expose missing direct dependencies

Problem: After lock regeneration, Vite build fails resolving packages (e.g., lodash, apexcharts) that were previously available transitively.

Fix: Add missing packages as explicit direct dependencies.

Rule: After lock regeneration, treat unresolved imports as missing direct dependency declarations.

Projects affected: Contentmakers, HGBI.

52. Duplicate npm dependency entries

Problem: npm warns about removing packages from dependencies in favor of devDependencies (or vice versa).

Fix: Let npm normalize. Avoid duplicating build-only packages across both sections in package.json.

Projects affected: Contentmakers.

53. @vuepic/vue-datepicker v12 breaking changes

Problem: Default export removed; locale and format props changed.

Fix:

// Old
import VueDatePicker from '@vuepic/vue-datepicker'
// New
import { VueDatePicker } from '@vuepic/vue-datepicker'

Additionally:

  • locale prop now requires a date-fns Locale object instead of a string: import {nl} from "date-fns/locale", change :locale="'nl'" to :locale="nl"
  • format prop renamed to formats with sub-properties: change format="dd-MM-yyyy" to :formats="{ input: 'dd-MM-yyyy' }"

Projects affected: Deversspecialist.


Inertia v1 → v2

54. SSR config change in vite.config.js

Problem: @inertiajs/server no longer exists in Inertia v2.

Fix: Change ssr.noExternal from ['@inertiajs/server'] to ['@inertiajs/vue3'] in vite.config.js.

Projects affected: Goos, HGBI.


Browser Testing

55. Pest browser tests need Playwright runtime

Problem: Browser tests fail on clean machines if Playwright browser binaries are not installed.

Fix: After npm install playwright, run npx playwright install (or npx playwright install chromium).

Rule: Include Playwright browser install in local + CI bootstrap.

Projects affected: Contentmakers, Goos.

56. Browser title assertions can be empty on Inertia pages

Problem: <title> can be empty at initial render timing on Inertia pages.

Fix: Use assertSourceHas(...) + smoke assertions instead of strict immediate title assertion.

Projects affected: Contentmakers.

57. assertSee() can fail on non-SSR Inertia pages

Problem: Text is present in response payload but not as immediate visible server-rendered DOM.

Fix: Use assertSourceHas(...) for payload-level assertions on client-rendered Inertia pages. Use assertNoSmoke() to catch JS errors and console logs.

Projects affected: Contentmakers, Goos.


Post-Upgrade Verification Checklist

Run these checks after completing all upgrade phases:

# 1. PHP tests (compare count to baseline!)
php artisan test --compact

# 2. Frontend build
npm run build

# 3. Code style
vendor/bin/pint --dirty

# 4. Filament assets
php artisan filament:upgrade --no-interaction

# 5. Application info
php artisan about

# 6. Remaining outdated packages
composer outdated --direct --format=json
npm outdated --json

# 7. ESLint (if configured)
npx eslint resources/js/app.js resources/js/ssr.js

# 8. Browser tests (if available)
./vendor/bin/pest tests/Browser/

# 9. Filament namespace scan (check for missed rector transforms)
grep -r "use Filament\\Pages\\Actions;" app/ cms/ commerce/ modules/ support/ 2>/dev/null
grep -r "use Filament\\Forms\\Form;" app/ cms/ commerce/ modules/ support/ 2>/dev/null
grep -r "use Filament\\Forms\\Get;" app/ cms/ commerce/ modules/ support/ 2>/dev/null
grep -r "use Filament\\Forms\\Set;" app/ cms/ commerce/ modules/ support/ 2>/dev/null

Note: php artisan route:list may fail if any controller constructor has abort_unless() calls. Use php artisan about instead for a quick framework verification.


Based on experience across all 5 projects, this order minimizes re-work:

  1. Pre-flight: Create branch, record baseline tests + build status
  2. Composer blockers: Replace/remove packages that block Laravel 12 (codezero, resend, laravel-lang)
  3. Laravel 12: Update laravel/framework ^12 + Symfony packages + debugbar + other direct deps
  4. Clear caches: Delete bootstrap/cache/packages.php and services.php, run composer dump-autoload
  5. Verify: Run tests + build
  6. Filament v3 → v4 → v5: Run rector for each step, for each custom directory. Fix missed transforms manually.
  7. Verify: Run tests + build
  8. Tailwind v3 → v4 (separate step, requires developer): Try automated tool first; fall back to manual. Update PostCSS config. Remove old plugins. A developer must visually verify the frontend after this step — open key pages in a browser and compare against the pre-upgrade state. A passing build does not guarantee correct layout.
  9. Verify: Run tests + build + manual visual check by developer
  10. Pest/PHPUnit upgrade: Update to Pest v4 / PHPUnit 12. Fix setUp() visibility, @test annotations.
  11. Verify: Run tests (count must match baseline!)
  12. NPM major updates: Upgrade remaining frontend packages. Fix breaking import changes.
  13. Verify: Run build + tests
  14. ESLint: Handle flat config migration if upgrading ESLint plugins
  15. Final verification: Full checklist above
  16. Browser smoke tests: Set up and run browser tests to catch runtime/rendering issues

Packages That Upgraded Cleanly

These packages upgraded without issues across multiple projects. No special handling needed — just update the version constraint and run composer update. Listed here so the AI does not waste time over-researching them.

Package Typical Upgrade Notes
darkaonline/l5-swagger 9 → 10 No code changes
spatie/eloquent-sortable 4 → 5 No code changes
spatie/laravel-data 4.14 → 4.19 Minor, constraint relaxation only
spatie/laravel-medialibrary 11.17 → 11.18 Minor
mollie/laravel-mollie 3 → 4 No code changes
symfony/http-client 7 → 8 No code changes (PHP 8.4 already satisfied)
symfony/mailgun-mailer 7 → 8 No code changes
barryvdh/laravel-debugbar 3 → 4 Only issue is stale autoload cache (see #1)
resend/resend-laravel 0.x → 1.x No API changes
jeffgreco13/filament-breezy 2 → 3 Works with Filament v5
ariaieboy/filament-currency 1 → 3 Works with Filament v5 + Laravel 12
amidesfahani/filament-tinyeditor 3 → 4 No code changes
ralphjsmit/laravel-filament-components 2 → 3 No code changes
filament/spatie-laravel-media-library-plugin 3 → 5 No code changes
filament/spatie-laravel-settings-plugin 3 → 5 No code changes
filament/spatie-laravel-tags-plugin 3 → 5 No code changes
nunomaduro/collision 7 → 8 No code changes
laravel/sanctum 3 → 4 No code changes
predis/predis 2 → 3 No code changes
muhammadhuzaifa/telescope-guzzle-watcher 3 → 4 No code changes
staudenmeir/belongs-to-through 2.16 → 2.17 No code changes
staudenmeir/eloquent-has-many-deep 1.20 → 1.21 No code changes
laravel-notification-channels/fcm 4 → 6 No code changes (skipped v5)
rollup-plugin-visualizer 5 → 6 No code changes
@vueuse/core 13 → 14 No code changes
vue-i18n 9 → 11 No code changes
uuid (npm) 11 → 13 No code changes

Projects Covered

Project Log File
Contentmakers Logs/upgrade-log-contentmakers.md
QLS Handling Logs/upgrade-log-qlshandling.md
Deversspecialist Logs/upgrade-log-deversspecialist.md, Logs/upgrade-log-deversspecialist-composer.md
Cleanshopping Logs/upgrade-log-cleanshopping.md
Goos Logs/upgrade-log-goos.md
HGBI Logs/upgrade-log-hgbi.md

Last updated: 2026-02-20