That's weird, I never had this with my applications.
Can you show the your vite.config.js file ?
Hello CoMmunity,
[TLDR] After "npm run build", my local website searches for /build/assets/Home-CAF9ffrI.css but real file is under /build/assets/Home-CAF9ffrI-b1476d91.css. A hash is added to the file but not in the manifest.json
I am struggling so much building to production my Laravel-Vue-Inertia app. For now everything was running ultra well in dev mode using vitejs. "npm run dev" or "vite" commands work perfectly fine and all the css / js / vue components are displaying ok. Now that i am building to prod "npm run build" , the compilation is successful but the website is a blank page with 404 on all assets url.
In the browser console there is 404 for http://myvirtualhost.local/build/assets/Home-CAF9ffrI.css but the file is under http://myvirtualhost.local/build/assets/Home-CAF9ffrI-b1476d91.css A hash b1476d91 is added by vite on the compilation time i guess, but in the manifest.json I have somehting like
"css": [ "assets/Home-CAF9ffrI.css" ]
and in the built entry point js, i also have the filename without the hash.
I tried to manually update the JS entry point by updating all references to their references with hash and then the website works...
So what did i miss in the config to have such a behavior ?
Thank you very much for help
Please find below the main files : app.blade.php , vite.config.js, example of one entry point elearning.ts
------ app.blade.php -------
{{-- Inline script to detect system dark mode preference and apply it immediately --}}
<script>
(function() {
const appearance = '{{ $appearance ?? "system" }}';
if (appearance === 'system') {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (prefersDark) {
document.documentElement.classList.add('dark');
}
}
})();
</script>
@if (!app()->isProduction())
<title inertia>{{ config('app.name') }}</title>
@endif
<!--
-->
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="icon" href="/favicon.png" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
@php
if( request()->routeIs('admin.*') ){
$entry = 'admin';
}else if(request()->routeIs('elearning.*') ){
$entry = 'elearning';
} else {
$entry = 'web';
}
@endphp
@vite('resources/js/' . $entry . '.ts')
@inertiaHead
---------- vite.config.js -------------
import vue from "@vitejs/plugin-vue"; import laravel from "laravel-vite-plugin"; import path from "path"; import { defineConfig } from "vite"; import compression from "vite-plugin-compression"; import purge from "@erbelion/vite-plugin-laravel-purgecss"; import { visualizer } from "rollup-plugin-visualizer"; import { laravelTranslationsPlugin } from "./vite/translation"; import { wayfinder } from "@laravel/vite-plugin-wayfinder";
export default defineConfig({ server: { host: "0.0.0.0", // important for Docker port: 5173, strictPort: true, hmr: { host: "localhost", // or your local IP for dev }, }, plugins: [ purge({ paths: ["resources/{js,views}/**/*.{blade.php,svelte,vue,html,ts}"], safelist: [], }), visualizer({ open: true, // Automatically opens the visualizer in your browser }), laravel({ input: ["resources/js/web.ts", "resources/js/elearning.ts"], ssr: "resources/js/ssr.ts", refresh: true, }), vue({ template: { transformAssetUrls: { base: null, includeAbsolute: false, }, }, }), wayfinder(), laravelTranslationsPlugin(), //typescriptTransformer(), compression({ algorithm: "gzip" }), compression({ algorithm: "brotliCompress" }), ], resolve: { alias: { "@": path.resolve(__dirname, "./resources/js"), }, }, build: { sourcemap: false, rollupOptions: { input: { web: path.resolve(__dirname, "resources/js/web.ts"), elearning: path.resolve(__dirname, "resources/js/elearning.ts"), }, output: { manualChunks(id) { // sépare toutes les dépendances node_modules dans un chunk vendor if (id.includes("node_modules")) { return "vendor"; } }, }, }, }, });
---------- baseEntrypoint.ts -------
import { createInertiaApp } from "@inertiajs/vue3"; import { resolvePageComponent } from "laravel-vite-plugin/inertia-helpers"; import { createApp, h } from "vue"; import { initializeTheme } from "./composables/useAppearance"; import { createI18n } from "vue-i18n"; import fr from "@/i18n/fr.json"; import { Tooltip } from "bootstrap";
const appName = import.meta.env.VITE_APP_NAME;
export function bootstrapLayout(options: { pagesDir: string; pageGlob: Record<string, any>; }) {
const i18n = createI18n({
legacy: false,
locale: "fr",
fallbackLocale: "fr",
messages: { fr },
});
function initializeTooltips() {
const tooltipTriggerList = document.querySelectorAll(
'[data-bs-toggle="tooltip"]',
);
tooltipTriggerList.forEach((el) => new Tooltip(el));
}
createInertiaApp({
title: (title) => (title ? `${title} - ${appName}` : appName),
resolve: (name) =>
resolvePageComponent(`./pages/${name}.vue`, options.pageGlob),
setup({ el, App, props, plugin }) {
const app = createApp({ render: () => h(App, props) })
.use(plugin)
.use(i18n);
app.mount(el);
initializeTooltips();
initializeTheme();
},
progress: {
color: "#3b86ec",
includeCSS: true,
showSpinner: false,
},
});
}
----- elearning.ts ----------
import './assets/scss/Elearning/main.scss';
import { bootstrapLayout } from "./bootstapLayout"; import { DefineComponent } from "vue";
baseEntrypoint({ pagesDir: "Elearning", pageGlob: import.meta.glob<DefineComponent>("./pages/Elearning/**/*.vue"), });
Please or to participate in this conversation.