Conversation
Replace webpack with Vite 8 (Rolldown) as the frontend bundler. Key changes: - Replace webpack.config.ts with vite.config.ts using Rolldown - Use native ES modules with import maps for cache busting - Build web components as a separate blocking IIFE bundle to prevent flash of unstyled content (same behavior as webpack's blocking script) - Update all dynamic imports from webpack chunk comments to standard dynamic import() syntax - Replace process.env.TEST with import.meta.env.MODE - Add vite/client types to tsconfig.json - Update Monaco error suppression regex for new chunk names - Rename WEBPACK_* Makefile variables to FRONTEND_* Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
Replace query-string cache busting (?v=) with content-hashed entry filenames for JS and CSS assets. Vite now generates a manifest (.vite/manifest.json) mapping unhashed to hashed paths. A new Go-side AssetPath function reads the manifest and resolves paths in templates. This eliminates the need for the importmap workaround that was required because chunks imported the entry module without the ?v= query parameter. - Add modules/public/manifest.go with mtime-based cache invalidation so dev mode auto-detects frontend rebuilds - Add AssetPath template function for hashed path resolution - Update all templates to use AssetPath instead of ?v=AssetVersion - Pass sharedWorkerPath via window.config for the SharedWorker URL - Rename eventsource.sharedworker to sharedworker - Fix markup_external_test.go for type="module" and hashed paths Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
- Add content hash to webcomponents.js IIFE build and append its entry to the Vite manifest - Use AssetPath for swagger asset paths in openapi.go renderer - Strip content hash from theme CSS filenames in webtheme.go to correctly extract internal theme names - Remove AssetVersion variable and template function, now fully replaced by content-hashed filenames via manifest - Rename AssetPath to GetAssetPath Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
- Rename AssetPath to GetAssetPath in template helper and all templates - Clean up stale webcomponents files before IIFE rebuild - Add comment clarifying TypeError catch for navigation-during-import Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
`build()` returns an array for IIFE lib builds, so the `'output' in result` check silently skipped the manifest append. Handle both array and single-object return types. Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
|
But this will break Firefox support? |
Why should it? I've already tested everything in Firefox. The only new browser requirement is module scripts which are supported in Firefox since 2018. |
The webcomponents directory had both index.ts and webcomponents-blocking.ts with identical content. Remove the duplicate and use index.ts as the entry. Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR migrates Gitea’s frontend build pipeline from Webpack to Vite 8, switching to content-hashed asset filenames resolved at runtime via a manifest to improve build performance and cache correctness.
Changes:
- Replace Webpack configuration/build flow with a new Vite 8 build (including manifest generation and hashed outputs).
- Update backend + templates to resolve asset filenames via
public.GetAssetPath/GetAssetPathinstead of?v={{AssetVersion}}. - Adjust frontend entrypoints and dynamic imports for Vite, including a dedicated blocking webcomponents bundle and updated Monaco worker wiring.
Reviewed changes
Copilot reviewed 54 out of 60 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| webpack.config.ts | Removed legacy Webpack build configuration. |
| vite.config.ts | Added Vite 8 build config, manifest output, webcomponents IIFE build, and license generation. |
| package.json | Replaced Webpack deps with Vite/Vite Vue plugin and related tooling. |
| pnpm-lock.yaml | Lockfile updates reflecting bundler/tooling migration. |
| types.d.ts | Removed webpack-specific module typing. |
| tsconfig.json | Switched TS global types from webpack/module to vite/client. |
| eslint.config.ts | Dropped globals.webpack for web sources. |
| Makefile | Replaced webpack build/watch targets with Vite equivalents and new outputs. |
| .gitignore | Ignored Vite manifest output directory under public/assets/.vite. |
| web_src/js/webcomponents/webcomponents-blocking.ts | New blocking webcomponents entry used for FOUC avoidance. |
| web_src/js/vitest.setup.ts | Updated unit test window config to remove webpack globals and add shared worker path field. |
| web_src/js/utils/testhelper.ts | Updated “unit test mode” detection for Vite/Vitest. |
| web_src/js/utils/dom.test.ts | Adjusted Vitest expectation API usage. |
| web_src/js/utils.test.ts | Adjusted Vitest expectation API usage. |
| web_src/js/standalone/swagger.ts | Ensured standalone CSS is imported from the entrypoint for Vite bundling. |
| web_src/js/standalone/external-render-iframe.ts | Ensured standalone CSS is imported from the entrypoint for Vite bundling. |
| web_src/js/standalone/devtest.ts | Ensured standalone CSS is imported from the entrypoint for Vite bundling. |
| web_src/js/render/plugins/pdf-viewer.ts | Removed webpack chunk annotations; rely on Vite dynamic import. |
| web_src/js/render/plugins/3d-viewer.ts | Removed webpack chunk annotations; rely on Vite dynamic import. |
| web_src/js/modules/sortable.ts | Removed webpack chunk annotation; keep dynamic import for code-splitting. |
| web_src/js/modules/monaco.ts | New Monaco module that wires Vite ?worker imports and exports Monaco API. |
| web_src/js/markup/refissue.ts | Removed webpack chunk annotation; use Vite dynamic import for Vue component. |
| web_src/js/markup/mermaid.ts | Removed webpack chunk annotations; use Vite dynamic import. |
| web_src/js/markup/math.ts | Switched KaTeX + CSS loading to plain dynamic imports for Vite. |
| web_src/js/markup/asciicast.ts | Switched asciinema player + CSS loading to plain dynamic imports for Vite. |
| web_src/js/index.ts | Moved CSS imports into the JS entry; updated dynamic import error handling. |
| web_src/js/globals.d.ts | Updated window.config shape; added MonacoEnvironment typing and *?worker module declaration. |
| web_src/js/features/tribute.ts | Removed webpack chunk annotation; rely on Vite dynamic import. |
| web_src/js/features/stopwatch.ts | Switched SharedWorker URL construction to use manifest-resolved worker path. |
| web_src/js/features/notification.ts | Switched SharedWorker URL construction to use manifest-resolved worker path. |
| web_src/js/features/sharedworker.ts | Added new SharedWorker implementation for event streaming. |
| web_src/js/features/repo-issue-pull.ts | Removed webpack chunk annotation for Vue component dynamic import. |
| web_src/js/features/repo-findfile.ts | Removed webpack chunk annotation for Vue component dynamic import. |
| web_src/js/features/recent-commits.ts | Removed webpack chunk annotation for Vue component dynamic import. |
| web_src/js/features/heatmap.ts | Removed webpack chunk annotation for Vue component dynamic import. |
| web_src/js/features/dropzone.ts | Switched Dropzone + CSS loading to plain dynamic imports for Vite. |
| web_src/js/features/contributors.ts | Removed webpack chunk annotation for Vue component dynamic import. |
| web_src/js/features/comp/Cropper.ts | Removed webpack chunk annotation; rely on Vite dynamic import. |
| web_src/js/features/comp/ComboMarkdownEditor.ts | Switched EasyMDE + CSS loading to plain dynamic imports for Vite. |
| web_src/js/features/colorpicker.ts | Switched color picker + CSS loading to plain dynamic imports for Vite. |
| web_src/js/features/codeeditor.ts | Updated Monaco import to use the new modules/monaco.ts wrapper. |
| web_src/js/features/code-frequency.ts | Removed webpack chunk annotation for Vue component dynamic import. |
| web_src/js/features/citation.ts | Removed webpack chunk annotations; rely on Vite dynamic imports. |
| web_src/js/features/captcha.ts | Removed webpack chunk annotation; rely on Vite dynamic import. |
| web_src/js/bootstrap.ts | Removed webpack public path setup; adjusted ignore patterns and asset base URL logic. |
| templates/swagger/ui.tmpl | Updated Swagger assets to use GetAssetPath and module script. |
| templates/status/500.tmpl | Updated “minimal template functions” comment to reference GetAssetPath. |
| templates/shared/combomarkdowneditor.tmpl | Converted inline script to module script. |
| templates/repo/diff/box.tmpl | Converted inline script to module script and adjusted file-tree visibility init. |
| templates/devtest/devtest-header.tmpl | Updated devtest CSS link to GetAssetPath. |
| templates/devtest/devtest-footer.tmpl | Updated devtest JS to module script + GetAssetPath. |
| templates/base/head_style.tmpl | Updated core CSS and theme CSS to use GetAssetPath. |
| templates/base/head_script.tmpl | Removed AssetVersion usage; added sharedWorkerPath config; added blocking webcomponents script and module index.js script via GetAssetPath. |
| modules/templates/helper.go | Exposed GetAssetPath to templates (replacing AssetVersion). |
| modules/setting/server.go | Removed AssetVersion from server settings initialization. |
| modules/public/manifest.go | Added Vite manifest loader/cache + GetAssetPath resolver. |
| modules/public/manifest_test.go | Added unit tests for manifest parsing and GetAssetPath fallback behavior. |
| modules/markup/render.go | Updated external render iframe asset URLs to resolve via GetAssetPath and module script. |
| modules/markup/external/openapi.go | Updated OpenAPI renderer to use GetAssetPath-resolved swagger assets and module script. |
| tests/integration/markup_external_test.go | Updated expected HTML to match module script + manifest-resolved asset paths. |
| services/webtheme/webtheme.go | Stripped Vite content hash from theme internal names when loading theme metadata. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
- Add comment explaining ENABLE_SOURCEMAP=reduced backward compatibility - Add dev-licenses-stub plugin so licenses.txt exists in dev builds Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
The 720h default is too aggressive for non-hashed assets like avatars and custom files. A targeted long cache for content-hashed manifest assets can be added separately. Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
Yes, can't have shared module-scope variables between iife (classic) and module scripts. Here is a breakdown what is in the IIFE:
jQuery and HTMX should be eliminated separately and I think I can do something about tippy, will try. |
I believe there should be one entry js like before. All modules should be in it.
Otherwise, even if you fixed "tippy" now, there will be "tippy-2" or "tippy-3" in the future, and doubled imported will cause bugs which are hard to debug or fix. |
We still need the IIFE (classic) + Module split for now because:
|
|
Could probably use |
Remove tippy.js and @popperjs/core dependencies from the iife bundle by replacing the overflow-menu dropdown with a lightweight custom popup using absolute positioning, CSS border-triangle arrow, and native click-outside/keyboard handling. This reduces the iife bundle by ~97 KB (uncompressed). Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
|
Tippy dependency removed
|
Move the function out of bootstrap.ts into a side-effect-free module so the ESM bundle can import it without re-triggering the global error handler initialization. Fix overflow-menu popup item styling to override fomantic specificity. Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
|
b7a2b0a should fix all remaining issues:
|
Extract showGlobalErrorMessage, shouldIgnoreError, and processWindowErrorEvent into a side-effect-free module. This keeps bootstrap.ts minimal (just the init guard and side effect) and prevents the ESM bundle from re-triggering the global error handler. Co-Authored-By: Claude (Opus 4.6) <noreply@anthropic.com>
|
Cleaned up the bootstrap some more in 46f6a7e, so it no longer has exports and no one will be tempted to import functions from it (which would trigger the double registration bug because of the side-effect code). Error-related functions now live in Also removed the |
There was a problem hiding this comment.
Pull request overview
Migrates Gitea’s frontend asset pipeline from Webpack to Vite 8, introducing content-hashed asset filenames resolved via a Vite manifest and adjusting how/when browser JS is loaded (blocking IIFE bootstrap + deferred module entrypoints).
Changes:
- Replace Webpack build configuration with a Vite (Rolldown) build, including a separate IIFE build step and manifest generation.
- Switch templates/backend renderers to resolve hashed assets via
GetAssetPath(manifest-backed) instead of?v=cache-busting. - Refactor frontend bootstrapping (IIFE entry + module entry), update dynamic imports, workers, and related tests/setup.
Reviewed changes
Copilot reviewed 73 out of 79 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| webpack.config.ts | Removed Webpack configuration (migration to Vite). |
| vite.config.ts | Adds Vite/Rolldown build config, manifest, IIFE build plugin, license output, CSS filtering. |
| package.json | Swaps Webpack deps for Vite deps and related tooling updates. |
| Makefile | Replaces Webpack build/watch targets with Vite build/watch targets. |
| tsconfig.json | Switches TS ambient types from webpack/module to vite/client. |
| eslint.config.ts | Tightens restricted imports (jquery/htmx) and updates globals for new setup. |
| types.d.ts | Removes webpack license-checker module declaration. |
| .gitignore | Ignores Vite manifest directory under public/assets/.vite. |
| Dockerfile | Updates frontend build comment to be Node ecosystem–focused (not webpack-specific). |
| Dockerfile.rootless | Same as Dockerfile (comment update). |
| templates/base/head_script.tmpl | Removes AssetVersion usage, injects sharedWorkerPath, loads iife.js from manifest. |
| templates/base/footer.tmpl | Loads index.js as a module script via manifest path. |
| templates/base/head_style.tmpl | Updates CSS links to use GetAssetPath (manifest resolved). |
| templates/swagger/ui.tmpl | Updates swagger JS/CSS links to use manifest paths and module script. |
| templates/devtest/devtest-header.tmpl | Uses manifest CSS path and adds jQuery presence check. |
| templates/devtest/devtest-footer.tmpl | Loads devtest JS as module via manifest path. |
| templates/status/500.tmpl | Updates template comment to reflect GetAssetPath instead of AssetVersion. |
| modules/templates/helper.go | Exposes GetAssetPath in templates and removes AssetVersion. |
| modules/setting/server.go | Removes setting.AssetVersion generation/storage. |
| modules/public/manifest.go | Adds manifest parser/reloader and GetAssetPath resolver. |
| modules/public/manifest_test.go | Adds unit tests for manifest parsing and fallback behavior. |
| services/webtheme/webtheme.go | Strips Vite content hash from theme internal names when enumerating themes. |
| modules/markup/render.go | Uses GetAssetPath and module script tag for external-render-iframe assets. |
| modules/markup/external/openapi.go | Uses GetAssetPath and module script tag for swagger assets. |
| tests/integration/markup_external_test.go | Updates expected HTML output to include manifest-based asset paths and module script. |
| modules/graceful/server_http.go | Enables HTTP protocol selection including unencrypted HTTP/2 (h2c). |
| web_src/js/iife.ts | New IIFE entrypoint for render-blocking bootstrap/globals/webcomponents/user-settings. |
| web_src/js/index.ts | Replaces previous domready split; runs init pipeline and htmx error toasts; dispatches index-ready. |
| web_src/js/index-domready.ts | Removed old domready chunk entry. |
| web_src/js/htmx.ts | Removed old initHtmx module; htmx is now configured via globals.ts. |
| web_src/js/globals.ts | Loads global jQuery + htmx + idiomorph and configures htmx globally. |
| web_src/js/globals.d.ts | Updates window.config shape, adds MonacoEnvironment typing, adds *?worker module typing. |
| web_src/js/bootstrap.ts | Moves global error handling to modules/errors.ts and adapts init semantics. |
| web_src/js/modules/errors.ts | New lightweight global error utilities and window error event processing. |
| web_src/js/modules/errors.test.ts | Updates tests to target the new errors.ts module and new ignore patterns. |
| web_src/js/vitest.setup.ts | Updates vitest setup for Vite (polyfills + dynamic import for globals). |
| web_src/js/utils/testhelper.ts | Updates unit-test detection for Vite (MODE === 'test'). |
| web_src/js/utils/dom.test.ts | Updates assertion API usage (toThrow vs toThrowError). |
| web_src/js/utils.test.ts | Same assertion API adjustment. |
| web_src/js/modules/monaco.ts | New Monaco wrapper to configure worker loading via Vite ?worker imports. |
| web_src/js/features/codeeditor.ts | Switches Monaco dynamic import to the new wrapper module. |
| web_src/js/features/sharedworker.ts | Adds shared worker entry logic for EventSource multiplexing under Vite output. |
| web_src/js/features/notification.ts | Updates SharedWorker URL construction to use manifest-provided worker path. |
| web_src/js/features/stopwatch.ts | Same SharedWorker URL update for stopwatch updates. |
| web_src/js/standalone/swagger.ts | Inlines CSS import for Vite and uses fetch directly for spec retrieval. |
| web_src/js/standalone/external-render-iframe.ts | Inlines CSS import for Vite. |
| web_src/js/standalone/devtest.ts | Inlines CSS import for Vite. |
| web_src/js/render/plugins/pdf-viewer.ts | Removes webpack chunk naming comment from dynamic import. |
| web_src/js/render/plugins/3d-viewer.ts | Same as pdf-viewer (import comment removal). |
| web_src/js/modules/sortable.ts | Removes webpack chunk naming comment from dynamic import. |
| web_src/js/features/tribute.ts | Removes webpack chunk naming comment from dynamic import. |
| web_src/js/features/repo-findfile.ts | Removes webpack chunk naming comment from Vue dynamic import. |
| web_src/js/features/recent-commits.ts | Same as above (Vue dynamic import). |
| web_src/js/features/contributors.ts | Same as above (Vue dynamic import). |
| web_src/js/features/heatmap.ts | Same as above (Vue dynamic import). |
| web_src/js/features/repo-issue-pull.ts | Same as above (Vue dynamic import). |
| web_src/js/features/comp/Cropper.ts | Removes webpack chunk naming comment from dynamic import. |
| web_src/js/features/comp/ComboMarkdownEditor.ts | Removes webpack chunk naming comments from dynamic imports (JS + CSS). |
| web_src/js/features/colorpicker.ts | Removes webpack chunk naming comments from dynamic imports (JS + CSS). |
| web_src/js/features/citation.ts | Removes webpack chunk naming comments from dynamic imports (multiple packages). |
| web_src/js/features/dropzone.ts | Removes webpack chunk naming comments from dynamic imports (JS + CSS). |
| web_src/js/features/captcha.ts | Removes webpack chunk naming comment from dynamic import. |
| web_src/js/markup/refissue.ts | Removes webpack chunk naming comment from Vue dynamic import. |
| web_src/js/markup/mermaid.ts | Removes webpack chunk naming comments from dynamic imports. |
| web_src/js/markup/math.ts | Removes webpack chunk naming comments from dynamic imports (JS + CSS). |
| web_src/js/markup/asciicast.ts | Removes webpack chunk naming comments from dynamic imports (JS + CSS). |
| web_src/js/modules/fomantic.ts | Removes direct jquery import to rely on global jquery. |
| web_src/js/modules/fomantic/base.ts | Removes direct jquery import to rely on global jquery. |
| web_src/js/modules/fomantic/dimmer.ts | Removes direct jquery import to rely on global jquery. |
| web_src/js/modules/fomantic/dropdown.ts | Removes direct jquery import to rely on global jquery. |
| web_src/js/modules/fomantic/modal.ts | Removes direct jquery import to rely on global jquery. |
| web_src/js/modules/fomantic/tab.ts | Removes direct jquery import to rely on global jquery. |
| web_src/js/modules/fomantic/transition.ts | Removes direct jquery import to rely on global jquery. |
| web_src/js/features/common-page.ts | Updates global error import to new modules/errors.ts. |
| web_src/js/webcomponents/overflow-menu.ts | Replaces tippy-based overflow menu with custom popup (DOM + ARIA + click-outside). |
| web_src/js/webcomponents/README.md | Updates guidance to reference vite.config.ts instead of webpack config. |
| web_src/css/base.css | Adds CSS for the new overflow-menu popup styling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Make manifest test hermetic by setting checkTime to prevent reload - Add String(err) fallback in error handler to prevent crash on non-Error rejections - Remove duplicate @vitejs/plugin-vue from devDependencies Co-Authored-By: Claude (claude-opus-4-6) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 73 out of 79 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
web_src/js/standalone/swagger.ts:1
- This now assumes the response is always JSON and successful. If the request returns a non-2xx status (or non-JSON body),
res.json()will throw and the UI may fail without a clear message. Consider checkingres.okand throwing a descriptive error (or showing a user-facing message) before parsing JSON.
web_src/js/webcomponents/overflow-menu.ts:1 - The Tab handling creates a focus trap inside a non-modal popup menu (Tab never leaves the menu). For menu-button patterns, Tab typically closes the menu and moves focus to the next focusable element. Consider changing the Tab behavior to close the popup (and let the browser continue normal tabbing), keeping Arrow keys for intra-menu navigation.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
So far I was unable to reproduce this and I already had Claude looking into Vite internals with no success at trying to create a minimal reproduction case. What file were you editing? CSS or TS? In any case, it's not a huge issue, the next save will fix it. |
tippy.js is no longer in the IIFE bundle, so the define is unnecessary. Vite handles this automatically for module builds. Co-Authored-By: Claude (claude-opus-4-6) <noreply@anthropic.com>
|
Why close this one? I think it's useful. |
|
I was doing a branch cleanup and the branch was accidentially deleted, continue in #37002. |
Replace webpack with Vite 8 as the frontend bundler. Frontend build is around 3-4 times faster than before. Will work on all platforms including riscv64 (via wasm). `iife.js` is a classic render-blocking script in `<head>` (handles web components/early DOM setup). `index.js` is loaded as a `type="module"` script in the footer. All other JS chunks are also module scripts (supported in all browsers since 2018). Entry filenames are content-hashed (e.g. `index.C6Z2MRVQ.js`) and resolved at runtime via the Vite manifest, eliminating the `?v=` cache busting (which was unreliable in some scenarios like vscode dev build). Replaces: #36896 Fixes: #17793 Signed-off-by: silverwind <me@silverwind.io> Signed-off-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Replace webpack with Vite 8 as the frontend bundler. Frontend build is around 3-4 times faster than before. Will work on all platforms including riscv64 (via wasm).
iife.jsis a classic render-blocking script in<head>(handles web components/early DOM setup).index.jsis loaded as atype="module"script in the footer. All other JS chunks are also module scripts (supported in all browsers since 2018).Entry filenames are content-hashed (e.g.
index.C6Z2MRVQ.js) and resolved at runtime via the Vite manifest, eliminating the?v=cache busting (which was unreliable in some scenarios like vscode dev build).Fixes: #17793
Docs: https://gitea.com/gitea/docs/pulls/364