Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

kanak09's avatar

kanak09 wrote a reply+100 XP

5mos ago

Vite is building assets with hash in filename but hash is missing in manifest.json and js entry point

Thanks all for your help.

@vincent15000 My vite file was included in my first message but the formatting is destroyed and doesnt format the whole file... Here it is again

START VITE.CONFIG.JS FILE

import vue from "@vitejs/plugin-vue"; import laravel from "laravel-vite-plugin"; import path from "path"; import { defineConfig, type UserConfig } from "vite"; import compression from "vite-plugin-compression"; import purge from "@erbelion/vite-plugin-laravel-purgecss"; import { laravelTranslationsPlugin } from "./vite/translation"; import { fixCssReferences } from "./vite/fixCSSReferences"; import { wayfinder } from "@laravel/vite-plugin-wayfinder";

const isProd = process.env.NODE_ENV === "production"; const isClever = process.env.PROD_ENV === "clevercloud";

return {
    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: {
                // Preserve all scoped component styles (data-v-* attributes)
                deep: [/data-v-.*$/],
                // Add specific component classes that might be purged
                standard: [
                    "password-toggle-btn",
                    "password-input-container",
                    "password-rules",
                    "rule-indicator",
                    "bullet",
                ],
                greedy: [
                    /^password-/, // Preserve all password-related classes
                    /\[data-v-/, // Preserve all scoped styles
                ],
            },
        }),
        laravel({
            input: ["resources/js/app.ts"],
            refresh: true,
        }),
        vue({
            template: {
                transformAssetUrls: {
                    base: null,
                    includeAbsolute: false,
                },
            },
        }),
        wayfinder(),
        laravelTranslationsPlugin(),
        fixCssReferences(), // Fix CSS references in manifest to use hashed filenames

        // Compression gzip/brotli uniquement si pas sur Clever Cloud
        isProd &&
            !isClever &&
            compression({
                algorithm: "gzip",
                compressionOptions: { level: 9 },
                ext: ".gz",
                threshold: 10240, // Only compress files larger than 10KB
            }),
        isProd &&
            !isClever &&
            compression({
                algorithm: "brotliCompress",
                compressionOptions: { level: 11 },
                ext: ".br",
                threshold: 10240,
            }),
    ].filter(Boolean),
    resolve: {
        alias: {
            "@": path.resolve(__dirname, "./resources/js"),
        },
    },
    build: {
        sourcemap: false,
        cssCodeSplit: true,
        chunkSizeWarningLimit: 800,
        minify: "esbuild", //terser
        target: "es2020",
        rollupOptions: {
            output: {
                manualChunks(id) {
                    // sépare toutes les dépendances node_modules dans un chunk vendor
                    if (id.includes("node_modules")) {
                        return "vendor";
                    }
                },
                //chunkFileNames: "assets/[name]-[hash].js",
                /*
            assetFileNames: (assetInfo) => {
                // Use names array instead of deprecated name property
                const name = assetInfo.names?.[0] || "";

                // For CSS files, return the exact name without hash placeholders
                // The fixCssReferences plugin will handle the hash mapping in manifest
                if (name.endsWith(".css")) {
                    return `assets/${name}`;
                }

                // For other assets (fonts, images), use content hash for cache busting
                return "assets/[name]-[hash][extname]";
            },
             */
            },
        },
    },
    optimizeDeps: {
        include: ["vue", "@inertiajs/vue3", "axios", "lodash"],
    },
};

});

END VITE.CONFIG.JS FILE

@mega_aleksandar I tried to remove the output but same result and as @jussimannisto said it should not interfer as it is a complete normal configuration, best example is for the vendor files.

Anyway for all, I found a solution. For sure it's not the best and it should not be like that, but thanks to AI I wrote a vite plugin that fixes my css references in the manifest regarding what has been generated and it works like a charm ! I know it's not a normal solution don't have choice for now.

If anyone has a brillant idea i still take it. Cheers

Here is the vite plugin for asset fixes

START VITE PLUGIN // Plugin to fix CSS references in manifest and chunks // Best practice: Keep hashed CSS files on disk for cache busting, // but ensure manifest.json correctly references them import { Plugin } from "vite";

export function fixCssReferences(): Plugin { return { name: 'fix-css-references', enforce: 'post', generateBundle(options, bundle) { // Map to store CSS filename mappings (non-hashed -> hashed) const cssMap: Map<string, string> = new Map();

        // First pass: Build a map of CSS files with their hashes
        for (const fileName in bundle) {
            if (fileName.endsWith('.css') && fileName.includes('-')) {
                // Extract the base name without hash (e.g., "Home-b1476d91.css" -> "Home.css")
                const baseName = fileName.replace(/-[a-f0-9]{8}\.css$/, '.css');
                cssMap.set(baseName, fileName);
            }
        }

        // Second pass: Update CSS references in all chunks
        for (const fileName in bundle) {
            const chunk = bundle[fileName];

            if (chunk.type === 'chunk' && 'viteMetadata' in chunk) {
                const metadata = chunk.viteMetadata as any;

                // Update CSS imports in metadata (importedCss is a Set)
                if (metadata?.importedCss && metadata.importedCss instanceof Set) {
                    const updatedCss = new Set<string>();

                    for (const cssFile of metadata.importedCss) {
                        const hashedVersion = cssMap.get(cssFile);
                        updatedCss.add(hashedVersion || cssFile);
                    }

                    metadata.importedCss = updatedCss;
                }
            }
        }
    },
};

}

END VITE PLUGIN