From 6a317890c1b13dea42e9181e85413b0d3c148c05 Mon Sep 17 00:00:00 2001 From: Andrea De Pirro Date: Mon, 12 Oct 2026 15:58:44 +0200 Subject: [PATCH 1/2] Stop vite build --watch from rebuilding in a loop Tailwind's content globs match the built `_static/*.js` files, and the Tailwind Vite plugin watches every file it scans. Each watch build wrote `_static`, which started the next build, so `./dev.sh isolated-watch` never settled. The watcher now ignores `_static` and `node_modules`. Co-Authored-By: Claude Opus 5.5 --- src/Elastic.Documentation.Site/vite.config.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Elastic.Documentation.Site/vite.config.ts b/src/Elastic.Documentation.Site/vite.config.ts index 9ca63e3364..a68b73d034 100644 --- a/src/Elastic.Documentation.Site/vite.config.ts +++ b/src/Elastic.Documentation.Site/vite.config.ts @@ -30,6 +30,12 @@ export default defineConfig({ sourcemap: true, // Razor resolves `main.js` to the hashed entry through `.vite/manifest.json`. manifest: true, + // Tailwind's content globs match the built `_static/*.js`, and the plugin watches every + // scanned file, so without this each `vite build --watch` output triggers another build. + // Only set under --watch: any non-null value turns a plain `vite build` into a watcher. + watch: process.argv.includes('--watch') + ? { exclude: ['**/_static/**', '**/node_modules/**'] } + : null, rollupOptions: { input: { main: path.resolve(__dirname, 'Assets/main.ts'), From c90ecf165538b5f30ec2dd62abeefff16125f4d0 Mon Sep 17 00:00:00 2001 From: Andrea De Pirro Date: Tue, 13 Oct 2026 08:01:47 +0200 Subject: [PATCH 2/2] Test that the Vite watch options apply only under --watch A one-shot `vite build` must exit, or the MSBuild frontend step hangs. Under `--watch`, the watcher must ignore `_static`, or each build triggers the next one. Co-Authored-By: Claude Opus 5.5 --- .../vite.config.test.ts | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/Elastic.Documentation.Site/vite.config.test.ts diff --git a/src/Elastic.Documentation.Site/vite.config.test.ts b/src/Elastic.Documentation.Site/vite.config.test.ts new file mode 100644 index 0000000000..37427992a3 --- /dev/null +++ b/src/Elastic.Documentation.Site/vite.config.test.ts @@ -0,0 +1,41 @@ +/** + * @jest-environment node + */ +import type { UserConfig } from 'vite' + +jest.mock('vite', () => ({ defineConfig: (config: unknown) => config })) +jest.mock('@tailwindcss/vite', () => () => ({ name: 'tailwindcss' })) +jest.mock('@vitejs/plugin-react', () => () => ({ name: 'react' })) +jest.mock('vite-plugin-svgr', () => () => ({ name: 'svgr' })) + +const loadConfig = async (argv: string[]): Promise => { + const originalArgv = process.argv + process.argv = ['node', 'vite', ...argv] + try { + let config: UserConfig = {} + await jest.isolateModulesAsync(async () => { + config = (await import('./vite.config')).default as UserConfig + }) + return config + } finally { + process.argv = originalArgv + } +} + +describe('vite.config build.watch', () => { + it('leaves watch unset for a one-shot build so `vite build` exits', async () => { + const config = await loadConfig(['build']) + + expect(config.build?.watch).toBeNull() + }) + + it('excludes the build output under --watch so writes do not trigger a rebuild', async () => { + const config = await loadConfig(['build', '--watch']) + + expect(config.build?.watch).toEqual( + expect.objectContaining({ + exclude: expect.arrayContaining(['**/_static/**']), + }) + ) + }) +})