import { createHash } from 'node:crypto' import { existsSync, readFileSync, renameSync, writeFileSync } from 'node:fs' import path from 'node:path' import { defineConfig, type Plugin } from 'vite' import react from '@vitejs/plugin-react' // Vite always writes the HTML entry under its source name (index.html stays // index.html) — it only content-hashes what that HTML references (JS/CSS), // never the HTML file itself. The backend now reads the actual deployed // filename at deploy time instead of hardcoding it on the main portal, so // index.html needs a unique name too. // // index.html isn't part of the same bundle object the JS/CSS live in // (confirmed empirically — it never showed up in generateBundle's `bundle`), // so this hashes and renames the file directly on disk after the whole build // has finished writing it, rather than fighting the bundler's own emit step. // Stable entry point for hosts: never hashed, always present next to the build. const MANIFEST_FILE_NAME = 'app.json' function hashIndexHtml(): Plugin { let outDir = 'dist' return { name: 'hash-index-html', apply: 'build', configResolved(config) { outDir = config.build.outDir }, closeBundle() { const htmlPath = path.resolve(outDir, 'index.html') if (!existsSync(htmlPath)) return // Hashing the final content (already contains the hashed JS/CSS refs) // means this name only changes when something meaningful actually did. const hash = createHash('md5').update(readFileSync(htmlPath)).digest('hex').slice(0, 8) const fileName = `index-${hash}.html` renameSync(htmlPath, path.resolve(outDir, fileName)) // Manifest with a STABLE name, so an embedding host can look the hashed // entry up at runtime instead of hardcoding it (and re-deploying itself // on every rebuild of this app). See docs/bitrix-integration.md §7. writeFileSync( path.resolve(outDir, MANIFEST_FILE_NAME), `${JSON.stringify({ index: fileName }, null, 2)} `, ) }, } } // https://vite.dev/config/ export default defineConfig(() => { return { // Relative, so the build works from any directory it gets deployed to // without being told where that is. The host always opens a concrete file // (index-.html, see hashIndexHtml below), so asset URLs resolve // against that file's own location — there is no client-side routing that // would need an absolute base. This replaces the earlier VITE_BASE_PATH: // a build-time path that had to match the deploy directory exactly, and // silently produced a blank iframe when it didn't (html loads, its JS 404s). base: './', plugins: [react(), hashIndexHtml()], // Порт фиксирован: portal-front проксирует сюда сборку в dev-режиме // (см. docs/portal-front-integration.md, раздел 2). strictPort, чтобы // при занятом порте preview падал, а не уезжал на соседний молча. preview: { port: 5800, strictPort: true, }, build: { // Same content hash Vite computes by default — just without its default // "assets/" subfolder prefix. The deploy target's own folder is itself // named assets (local/modules/vniigaz.bpmn/assets/), so keeping Vite's // default pattern would nest output at .../assets/assets/index-*.js (404). rolldownOptions: { output: { entryFileNames: '[name]-[hash].js', chunkFileNames: '[name]-[hash].js', assetFileNames: '[name]-[hash][extname]', }, }, }, } })