Search code examples
phpwordpressvite

Hot reload not fully working when using Vite/Laravel Vite Plugin inside Wordpress


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


Solution

  • 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