Skip to content

Move the asset build from Laravel Mix to Vite - #562

Merged
rbruhn merged 2 commits into
developmentfrom
vite-migration
Oct 12, 2026
Merged

rbruhn merged 2 commits into
developmentfrom
vite-migration

Conversation

@rbruhn

@rbruhn rbruhn commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Moves the asset build from Laravel Mix (webpack) to Vite, and takes the open Dependabot JavaScript updates with it. Part of #521 (its Vite and laravel-echo items; dropping Bootstrap and jQuery stays open). Replaces Dependabot #552–#561.

Packages

  • Removed: laravel-mix, webpack, sass-loader, resolve-url-loader, and the unused tailwindcss, @tailwindcss/forms, cross-env, alpinejs and codemirror (nothing imported or configured them; Livewire ships its own Alpine).
  • Added: vite 8.3, laravel-vite-plugin 3.2, and bootbox 5.5.3 from npm instead of the vendored minified 5.0.0 copy.
  • Updated: laravel-echo 1.19 → 2.6.1, axios 1.12 → 1.20, pusher-js 8.4 → 8.6, plus sass, postcss, autoprefixer, jquery-ui and the other minor versions from Bump the javascript-minor-and-patch group with 9 updates #552.
  • The removed Mix toolchain (webpack-dev-server, webpack-dev-middleware, svgo, node-forge, …) accounts for most of the open Dependabot alerts on build tooling. Those alerts close once this reaches main.

package.json scripts are now yarn dev (Vite dev server) and yarn build (production build into public/build).

JavaScript

The bundles are now ES modules, which run after the page is parsed and in strict mode. Changes that keep the old behaviour:

  • Globals first: jquery-global.js sets window.$/window.jQuery, and admin-globals.js/front-globals.js set ClipboardJS, bootstrap, bootbox and ConfettiGenerator. They're imported before the plugins and page scripts, since imports run in order.
  • require() → import, and the Reverb settings move from process.env.MIX_REVERB_* to import.meta.env.VITE_REVERB_*.
  • jQuery UI: the prebuilt jquery-ui/dist/jquery-ui.js. The individual widget files only load their dependencies through AMD, which webpack supported and Vite doesn't.
  • jqGrid: as a CommonJS module it exports a setup function instead of registering itself, so jqgrid-setup.js calls it with the global jQuery.
  • ui.multiselect.js (a modified copy that adds data-selected-value): its UMD wrapper is replaced by a direct call, so it registers the same way in dev and in the build.
  • Implicit globals made explicit: notify, initializeClock, getTimeRemaining, clockDiv (common.js) and makeSelect, formPost, formPostWithModalClose, toggleGeoLocateCommunityForm, checkDuplicates, checkRequiredValues, renumber_geolocate (admin-modals.js) are now assigned to window; in strict mode the bare assignments would throw. amChartBingo.js's $body/timeout became locals.
  • Group invite modal: admin-modals.js imports GroupInviteManager itself. The modal body arrives through jQuery's load(), and a module script inside it would run after the load callback that initializes the manager.
  • Removed: jquery.form.min.js (nothing calls ajaxForm/ajaxSubmit).

Blade

  • mix(...) and the six asset('js/amChart*.min.js') tags become @vite(...).
  • Inline scripts that use jQuery, DataTables or ConfettiGenerator while the page loads are type="module", and the DataTables CDN scripts before them are defer, so they run after the bundles in the same order as before: admin and front bingo/show, admin event/show, group/show, project/statistics, front bingo/card and wedigbio/index. The livewire:init listeners (event, expedition and project pages) stay classic scripts, so they register before Livewire fires the event.
  • Sass imports drop Mix's ~ prefix.

Images

resources/images moves to public/images and is tracked in git, replacing Mix's copy step. Nothing writes to public/images at runtime; uploads are in storage/app/public.

Six of the 61 images aren't used anywhere, so they move to resources/unused/public/images/ (same folders) instead, leaving 55 in public/images:

  • contributors/fsu.png: not referenced (the site uses fsu-logo.svg).
  • contributors/flmnh.png and contributors/uf.png: only in commented-out markup in front/layout/contributors.blade.php.
  • icons/apple-icon.png and icons/apple-icon-precomposed.png: not referenced (devices only look for /apple-touch-icon.png at the site root).
  • avatars/small/missing.png: only named by the missing_avatar_small config key, which nothing reads; the key is removed.

Deploy

  • CI (deploy-environment.yml): yarn build with VITE_REVERB_* from the same GitHub variables, and the package step checks that public/build/manifest.json exists instead of copying Mix's folders.
  • deploy.php: public/css, public/js, public/fonts, public/images and public/mix-manifest.json are no longer shared, so each release keeps its own build and a rollback gets the assets it was built with. Filament publishes its own files into public/css, public/js and public/fonts on every deploy (artisan:filament:assets).
  • New artisan:responsecache:clear after deploy:publish. Cached pages name the build's hashed files, which only exist in the release that built them; the existing cache:clear runs before the symlink switch, so pages cached by the old release in between would point at files the new release doesn't have.

Other

  • TestCase uses withoutVite() instead of withoutMix().
  • env.example uses VITE_REVERB_*. Local .env files need the same rename (MIX_REVERB_* → VITE_REVERB_*).
  • .gitignore: /public/build and /public/hot instead of /public/images and /public/mix-manifest.json.
  • Docs and guidelines (.ai/guidelines/frontend.md, CLAUDE.md, AGENTS.md, docs/architecture.md, docs/operations.md) describe Vite.

Tested locally (biospex.test, production build)

  • yarn build succeeds (about 2 s). php artisan test --compact: 629 passed, with no build present, as in CI. Pint passes.
  • Front: home, projects, a project home (map and charts render the same as production), expeditions (switching to Completed shows the confetti), events. Echo 2 connects to the local Reverb. No console errors, and every image and build file loads.
  • Admin: all plugins register (jQuery UI widgets, multiselect, jqGrid, bootstrap-select, notify, datetimepicker, Summernote, Bootstrap modals, bootbox, ClipboardJS). Subjects jqGrid (25 rows, pager, filters), expedition create (jqGrid, Livewire managers), event create (datetimepickers), project edit (Summernote, Livewire managers), group page and project statistics (DataTables with their search labels, chart), group invite modal (Livewire component responds), a bootbox confirm (opens and cancels). No console errors.

Not tested locally: the reconcile page (no expedition with reconcile data locally) and yarn dev against https://biospex.test.

To verify on development

See #548.

Vite 8 with laravel-vite-plugin replaces Laravel Mix and webpack. The bundles are ES modules: jQuery and the libraries the pages use are set as globals first, jQuery UI comes from its prebuilt bundle, jqGrid and the multiselect widget are registered explicitly, implicit globals are assigned to window, and inline scripts that use them at load time are modules after deferred CDN plugins.

Blade views use @Vite, static images move to public/images, and each release keeps its own public/build. The deploy builds with yarn build and VITE_REVERB_* and clears the response cache after the symlink switch.

Takes the Dependabot JavaScript updates: laravel-echo 2, axios 1.20, pusher-js 8.6 and minor versions, bootbox from npm, and drops the unused Tailwind, cross-env, Alpine and CodeMirror packages.
contributors/fsu.png, flmnh.png and uf.png, icons/apple-icon.png and apple-icon-precomposed.png, and avatars/small/missing.png aren't referenced by live code. The unused missing_avatar_small config key goes too.
@rbruhn
rbruhn merged commit 6d3ddfc into development Oct 12, 2026
2 checks passed
@rbruhn
rbruhn deleted the vite-migration branch October 12, 2026 18:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant