I have been enhancing a WordPress theme by moving the bundling over to Vite, following the instructions left in the answer on this post - How to properly create wp enqueue and functions script to run vite frontend
Now, I'm using a newer version of Vite etc. and the issue I'm facing is primarily around npm run dev
. Everything bundles absolutely fine when I run npm run build
, so I'm pretty sure its the hot reload that's the problem.
Here's my package.json
in my WP theme:
"scripts": {
"dev": "vite",
"watch": "npm run dev",
"build": "vite build",
"production": "vite build"
},
"devDependencies": {
"@alpinejs/collapse": "^3.13.5",
"@fancyapps/ui": "^5.0.30",
"@jeffreyvr/tailwindcss-tailpress": "^2.0.0",
"@splidejs/splide": "^4.1.4",
"@splidejs/splide-extension-auto-scroll": "^0.5.3",
"@tailwindcss/forms": "^0.5.7",
"@vitejs/plugin-vue": "^5.0.4",
"alpinejs": "^3.12.0",
"aos": "^3.0.0-beta.6",
"autoprefixer": "^10.4.0",
"browser-sync": "^2.26.14",
"browser-sync-webpack-plugin": "^2.3.0",
"cross-env": "^6.0.3",
"dotenv": "^16.0.3",
"laravel-vite-plugin": "^1.0.2",
"postcss": "^8.2.10",
"postcss-import": "^14.0.0",
"postcss-nested": "^5.0.3",
"postcss-nested-ancestors": "^2.0.0",
"resolve-url-loader": "^3.1.2",
"tailwindcss": "^3.4.0",
"vite": "^5.2.10"
}
The below is what's in my vite.config.mjs
file:
import {defineConfig} from "vite"
import path from 'path'
import vue from '@vitejs/plugin-vue'
import laravel from 'laravel-vite-plugin'
import dotenv from 'dotenv'
dotenv.config({
path: path.resolve(__dirname, '../../../../.env')
});
const domain = process.env.WP_HOME.replace(/^https?:\/\//i, '');
const domainName = domain.split('/')[0];
export default defineConfig(() => ({
base: '',
build: {
emptyOutDir: true,
manifest: true,
outDir: 'build',
assetsDir: 'assets',
},
plugins: [
laravel({
publicDirectory: 'build',
input: [
'resources/js/app.js',
'resources/css/app.css',
'resources/css/editor-style.css'
],
refresh: [
'**.php',
'**.vue',
'**.css',
]
}),
vue({
template: {
transformAssetUrls: {
// The Vue plugin will re-write asset URLs, when referenced
// in Single File Components, to point to the Laravel web
// server. Setting this to `null` allows the Laravel plugin
// to instead re-write asset URLs to point to the Vite
// server instead.
base: null,
// The Vue plugin will parse absolute URLs and treat them
// as absolute paths to files on disk. Setting this to
// `false` will leave absolute URLs un-touched so they can
// reference assets in the public directly as expected.
includeAbsolute: false,
},
},
}),
],
server: {
https: false,
host: domainName,
},
resolve: {
alias: [
{
find: /~(.+)/,
replacement: process.cwd() + '/node_modules/$1'
},
]
}
}));
Now, when I run npm run dev
and I go to my site, I get this error in the console:
Uncaught SyntaxError: Cannot use import statement outside a module (at app.js:1:1)
And the file in question (from the console) looks like this:
import Alpine from "/node_modules/.vite/deps/alpinejs.js?v=9c9d644e"
import collapse from "/node_modules/.vite/deps/@alpinejs_collapse.js?v=9c9d644e"
import AOS from "/node_modules/.vite/deps/aos.js?v=9c9d644e"
import Splide from "/node_modules/.vite/deps/@splidejs_splide.js?v=9c9d644e"
// import { AutoScroll } from '@splidejs/splide-extension-auto-scroll'
import {Fancybox} from "/node_modules/.vite/deps/@fancyapps_ui.js?v=9c9d644e"
import "/node_modules/@fancyapps/ui/dist/fancybox/fancybox.css"
window.Alpine = Alpine
Alpine.plugin(collapse)
Alpine.start()
document.addEventListener('DOMContentLoaded', function() {
AOS.init()
Fancybox.bind("[data-fancybox]")
})
And this is where I'm stuck, I'm not really sure how to get around this. Has anyone got any ideas on where I might be going wrong at all?
Thank you
After trying for quite a while to find a work around, I eventually found a post from a company called SouthcoastWeb who have forked the laravel-vite-plugin and made a WordPress version. I followed their instructions (with a few modifications, I'll show those below) and now everything works smoothly - https://southcoastweb.co.uk/open-source-software/wp-vite/
The steps I took:
Firstly, I decided to fork their WordPress plugin and install it from my own GitHub via composer as a must-use
plugin. I then installed the NPM package as directed on their instructions.
Next, I set my vite.config.mjs
file up like this. You'll notice I have some extra bits in with dotenv
etc. - this is because I'm using Roots/Bedrock and wanted to read my URL from the .env
:
import { defineConfig } from "vite";
import { wordpress } from "wordpress-vite-plugin";
import path from 'path'
import vue from '@vitejs/plugin-vue'
import dotenv from 'dotenv'
dotenv.config({
path: path.resolve(__dirname, '../../../../.env')
});
const domain = process.env.WP_HOME.replace(/^https?:\/\//i, '');
const domainName = domain.split('/')[0];
export default defineConfig(() => ({
plugins: [
wordpress({
input: [
'resources/js/app.js',
'resources/css/app.css',
'resources/css/editor-style.css'
],
refresh: [
'**.php',
'**.vue',
'**.css',
],
namespace: "theme-vite",
}),
vue({
template: {
transformAssetUrls: {
base: false,
includeAbsolute: false
}
}
})
],
server: {
https: false,
host: domainName,
},
optimizeDeps: {
include: [
'vue',
]
},
}));
From here, I then set up a vite.php
file inside a functions
subdirectory and then injected it into my main functions.php
file using require_once(__DIR__ . '/functions/vite.php');
- I only do this to keep things separated, but here's the code:
<?php
use EvoMark\WpVite\WpVite;
$wpVite = new WpVite();
$wpViteAdmin = new WpVite();
$wpVite->enqueue([
'input' => ['resources/js/app.js', 'resources/css/app.css'],
'namespace' => 'theme-vite'
]);
$wpViteAdmin->enqueue([
'input' => ['resources/css/editor-style.css'],
'namespace' => 'theme-vite',
'admin' => true,
]);
You'll notice that I've instantiated two instances of the WpVite
class; one for frontend files and one for the admin styles. This is mainly because the static usage (WpVite::enqueue
) in their example wasn't working for me for some reason (PHP 8.2) so I decided to go down this route which works absolutely fine.
And in terms of the scripts inside package.json
, I just added a couple extra to account for other instances that people may have used over different tools/setups:
"scripts": {
"dev": "vite",
"watch": "npm run dev",
"build": "vite build",
"production": "vite build"
},
I hope this helps someone! Like I say, the original post I linked in the question worked brilliantly, however I think there must be some breaking changes in Vite v5 and laravel-vite-plugin v1 that just don't work well with those instructions anymore.
Andy