Synchro's avatar

Copying assets with Vite

The Mix to Vite migration guide mentions that Vite lacks the ability to copy static assets into place, and unfortunately I'm doing exactly that to make some static libraries available. I have this in my webpack.mix.js file:

mix.copy(
    [
        'node_modules/chart.js/dist/chart.js'
    ],
    'public/js/chart.js'
);
mix.copy(
    [
        'node_modules/chartjs-plugin-datalabels/dist/chartjs-plugin-datalabels.js'
    ],
    'public/js/chartdatalabels.js'
);
mix.copyDirectory('resources/fonts', 'public/fonts');

What should I replace that with to have it work with Vite? Is there something I can hook into npm install to do this?

0 likes
13 replies
Synchro's avatar

Thanks, that's really helpful, I didn't know that Vite even had plugins.

I have used that plugin and converted my Mix code to this (it doesn't require different options to handle folders instead of files):

viteStaticCopy({
    targets: [
        {
            src: 'node_modules/chart.js/dist/chart.js',
            dest: 'public/js/chart.js'
        },
        {
            src: 'node_modules/chartjs-plugin-datalabels/dist/chartjs-plugin-datalabels.js',
            dest: 'public/js/chartdatalabels.js'
        },
        {
            src: 'resources/fonts',
            dest: 'public/fonts'
        },
    ]
})

I can see what that's doing, but the destination folder is prefixed as public/build, so for example my chart.js file ends up in public/build/public/js/chart.js instead of public/js/chart.js, and thus my blade templates fail to find it. There are no docs on config options for this plugin, but on rummaging through the source I found config.root and config.build.outDir properties, however, adding them to the Vite config didn't change anything.

I'm not clear on the expected usage of all this. I see that Vite compiles my js and css into build/ as well, when my app (which as far as I'm aware is a stock Laravel config for this) is looking in the usual place in public/js and public/css. So while Vite appears to be working, nothing it generates is being used. Should I be referencing assets within build instead or is there something else I've missed?

Sinnbeck's avatar

@Synchro Yeah it always puts them inside /build. The reason is that it deletes that folder when you compile your assets. So if you set /public as the root, it would delete that folder, completely destroying your app.

So remove the /public part of the dest, and update all references in your blade templates

 dest: 'js/chart.js'
1 like
Synchro's avatar

@Sinnbeck Thanks. My layouts contain this:

<link rel="stylesheet" href="{{ asset('css/app.css') }}">
<script src="{{ asset('js/app.js') }}" defer></script>

I would replace these with references to build/assets/..., however, the Vite output files are named for cache-busting purposes like app.0e4e12b7.js and app.f1094d21.css, so I can't predict the filenames. Is there some magic that needs to be applied to load the correct files?

Overall, am I doing this wrong? The chart.js files get updated by npm, so they need to be updated dynamically when that happens, but the font files could be static inside the public folder. During npm run build I'm getting warnings about their location like this:

/fonts/RoobertCZ-Regular.ttf referenced in myapp/resources/css/app.css didn't resolve at build time, it will remain unchanged to be resolved at runtime
Synchro's avatar

@Sinnbeck Ah, of course! Thanks again. To summarise for posterity. My Vite config now contains:

viteStaticCopy({
    targets: [
        {
            src: 'node_modules/chart.js/dist/chart.js',
            dest: 'js'
        },
        {
            src: 'node_modules/chartjs-plugin-datalabels/dist/chartjs-plugin-datalabels.js',
            dest: 'js'
        },
        {
            src: 'resources/fonts/*',
            dest: 'fonts'
        },
    ]
})

My blade layouts now use the vite helper, which rewrites the paths to use versioning:

@vite(['resources/css/app.css', 'resources/js/app.js'])

My app.css refers to fonts inside the build folder like this:

    src: url(/build/fonts/somefont.otf) format("opentype")

and I'm loading my extra JS files in templates like this:

<script src="{{ asset('build/js/chart.js') }}"></script>

These last ones should really be versioned since they can be updated by npm, however, I've not figured out how to get Vite to do both copying and versioning at the same time, but that can wait.

Thanks very much for the help, @sinnbeck.

5 likes
mkirk's avatar

@Synchro Did you find out how to use versioning with the copied assets. I need this, too.

jeli98's avatar

you can use the manifest hash as the version number like this

asset($url . '?' . Vite::manifestHash())
maxi032's avatar

I have a similar problem too. It works when I do npm run build but not when I do npm run dev.

Please or to participate in this conversation.