Skip to content
Merged
Show file tree
Hide file tree
Changes from 113 commits
Commits
Show all changes
119 commits
Select commit Hold shift + click to select a range
d99e932
Migrate from webpack to Vite 8
silverwind Mar 13, 2026
89b16a1
Use content-hashed filenames with Vite manifest for cache busting
silverwind Mar 13, 2026
90374c1
Hash all asset filenames and remove AssetVersion
silverwind Mar 13, 2026
a3d579e
Merge branch 'main' into vite
silverwind Mar 13, 2026
dea0436
Rename template helper `AssetPath` to `GetAssetPath` and clean up
silverwind Mar 13, 2026
581bd1b
cleanup
silverwind Mar 13, 2026
a419e10
Fix webcomponents manifest entry not written
silverwind Mar 13, 2026
e701562
inline
silverwind Mar 13, 2026
2a198b0
Remove duplicate `webcomponents-blocking.ts`
silverwind Mar 13, 2026
6533de7
Address review comments on `vite.config.ts`
silverwind Mar 13, 2026
e9554c7
Add upstream issue link for ENABLE_SOURCEMAP comment
silverwind Mar 13, 2026
df3c17a
add ref
silverwind Mar 13, 2026
6215c5e
Simplify ENABLE_SOURCEMAP handling
silverwind Mar 13, 2026
d6b34b3
Restore ENABLE_SOURCEMAP=reduced mode with Vite plugin
silverwind Mar 13, 2026
17ab78e
Restore original ENABLE_SOURCEMAP variable names from webpack config
silverwind Mar 13, 2026
c160f2b
Merge branch 'main' into vite
silverwind Mar 13, 2026
5fb3d15
Update modules/public/manifest.go
silverwind Mar 13, 2026
c52a9da
Restore ENABLE_SOURCEMAP=reduced mode with Vite plugin
silverwind Mar 13, 2026
3fcea7d
Apply suggestion from @silverwind
silverwind Mar 13, 2026
4a83e14
Build index.js as blocking IIFE, load index-domready as deferred module
silverwind Mar 13, 2026
43df852
Use global jQuery, remove jquery-global plugin, simplify CSS handling
silverwind Mar 13, 2026
e13f106
Use AssetFS() for Vite manifest reading
silverwind Mar 13, 2026
745cc7d
Simplify manifest loading, remove htmx from IIFE
silverwind Mar 14, 2026
70568dd
Use Vite's Manifest type for manifest handling
silverwind Mar 14, 2026
e0b1ad8
Add jQuery to vitest setup for global $ in tests
silverwind Mar 14, 2026
14744ac
Update stale webpack references to vite
silverwind Mar 14, 2026
ef8811f
forbid jquery imports because of single global instance
silverwind Mar 14, 2026
fee04f3
Suppress plugin timing warnings for worker builds
silverwind Mar 14, 2026
93ce326
Extract shared rolldownOptions for main, IIFE and worker builds
silverwind Mar 14, 2026
ae24785
use function
silverwind Mar 14, 2026
b5d5e46
Remove reduced sourcemap mode, simplify to true/false
silverwind Mar 14, 2026
bc05ab5
fix comment
wxiaoguang Mar 15, 2026
467dde9
add jQuery check to devtest page
wxiaoguang Mar 15, 2026
6ae005d
Use atomic.Pointer for manifest state to fix data race in dev mode
silverwind Mar 15, 2026
f3a8a19
Move htmx to IIFE globals, forbid `htmx.org` imports
silverwind Mar 15, 2026
e66d7b4
eslint tweaks
silverwind Mar 15, 2026
f0cf85d
C O M M E N T
wxiaoguang Mar 15, 2026
be08926
Stub XPathEvaluator in vitest setup for htmx compatibility
silverwind Mar 15, 2026
1487b7a
fix parseManifest
wxiaoguang Mar 15, 2026
1efff94
Stub XPathEvaluator in vitest setup for htmx compatibility
silverwind Mar 15, 2026
412ba3c
Merge tiny mermaid parser chunks via `manualChunks`
silverwind Mar 15, 2026
303328b
fix lint
silverwind Mar 15, 2026
18070c4
Use `codeSplitting` instead of deprecated `manualChunks`
silverwind Mar 15, 2026
c098499
Merge mermaid diagram chunks into single `mermaid-core` chunk
silverwind Mar 15, 2026
9ff8789
Remove redundant `minSize: 0` from mermaid-core group
silverwind Mar 15, 2026
4c5b77c
Remove unnecessary priority from vue-runtime group
silverwind Mar 15, 2026
c6f6ea8
add UnencryptedHTTP2
wxiaoguang Mar 15, 2026
2040535
Merge mermaid chunks without making them static imports
silverwind Mar 15, 2026
592b67b
fmt
silverwind Mar 15, 2026
e768b5c
comment
silverwind Mar 15, 2026
89f8907
Remove shared chunk dependencies from swagger entry
silverwind Mar 15, 2026
1dacad8
Move relative-time to own
silverwind Mar 15, 2026
93e92fa
Clean up vite config
silverwind Mar 15, 2026
80a31ec
Add citation-js codeSplitting group, update comment
silverwind Mar 15, 2026
f9201be
Merge branch 'main' into vite
silverwind Mar 15, 2026
2101c2e
explicit minify
silverwind Mar 15, 2026
0648217
Restore process.env.NODE_ENV define for IIFE build, remove citation-j…
silverwind Mar 15, 2026
4746fdd
Merge remote-tracking branch 'origin/main' into vite
silverwind Mar 17, 2026
4797006
Remove stale fomantic.css import
silverwind Mar 17, 2026
8bbb950
Merge remote-tracking branch 'origin/main' into vite
silverwind Mar 23, 2026
687dfd3
Merge branch 'main' into vite
silverwind Mar 23, 2026
d890d9a
Apply suggestion from @silverwind
silverwind Mar 23, 2026
16eaa6c
Apply suggestion from @silverwind
silverwind Mar 23, 2026
e9df9c2
disable custom codeSplitting
silverwind Mar 23, 2026
5e037c8
Increase default `STATIC_CACHE_TIME` from 6h to 30 days
silverwind Mar 23, 2026
f0d12ba
Rename index-domready to index, index to iife
silverwind Mar 24, 2026
11d0561
Merge branch 'main' into vite
silverwind Mar 24, 2026
efb2a40
Remove unnecessary renderBuiltUrl hook
silverwind Mar 24, 2026
3952938
Move base option into commonViteOpts
silverwind Mar 24, 2026
77a7e31
Restore initGlobalErrorHandler as side effect in bootstrap.ts
silverwind Mar 24, 2026
0c625ab
Update services/webtheme/webtheme.go
wxiaoguang Mar 24, 2026
8146f74
fine tune iife and global error handling
wxiaoguang Mar 24, 2026
6b576a1
Merge branch 'main' into vite
wxiaoguang Mar 24, 2026
e81cda9
show the importance of "do not import a module twice"
wxiaoguang Mar 24, 2026
88ac604
Revert default `STATIC_CACHE_TIME` back to 6h
silverwind Mar 24, 2026
6a4b3ef
Replace tippy.js with custom popup in overflow-menu
silverwind Mar 24, 2026
b7a2b0a
Extract `showGlobalErrorMessage` to `modules/message.ts`
silverwind Mar 24, 2026
46f6a7e
Move error handling functions to `modules/errors.ts`
silverwind Mar 24, 2026
a6ed941
Merge branch 'main' into vite
silverwind Mar 26, 2026
31ac727
Merge remote-tracking branch 'origin/main' into vite
silverwind Mar 27, 2026
deb54ff
update vite
silverwind Mar 27, 2026
77921c8
Add Vite dev server mode with reverse proxy
silverwind Mar 27, 2026
a6f4c7b
Migrate all asset URLs to AssetURL, remove pre-build step
silverwind Mar 27, 2026
9b0c5f2
Rename AssetURL to AssetPath, unexport GetAssetPath
silverwind Mar 27, 2026
1b18e4d
Fix test function name to satisfy Go naming convention
silverwind Mar 27, 2026
8220760
Use manifest for theme name resolution, support custom themes in dev …
silverwind Mar 27, 2026
79ca39c
Merge remote-tracking branch 'origin/main' into vite
silverwind Mar 27, 2026
084ffd5
Fix shared worker path, rename back to eventsource.sharedworker
silverwind Mar 27, 2026
836c1bf
Refactor getManifestPaths to getManifestData
silverwind Mar 27, 2026
98c081a
remove unneeded arg
silverwind Mar 27, 2026
937ee84
clarify hashed path, origin path, asset path, asset name
wxiaoguang Mar 27, 2026
55e9840
clean up
wxiaoguang Mar 27, 2026
4bf588b
fix test
wxiaoguang Mar 27, 2026
6ec7ab3
Merge branch 'main' into vite
silverwind Mar 28, 2026
31e833a
Clean up isViteDevRequest: remove ?raw, keep ?import
silverwind Mar 28, 2026
f881422
Rename AssetPath to AssetURI
silverwind Mar 28, 2026
85f2c97
Scope ?import check to /assets/ and /public/assets/ paths
silverwind Mar 28, 2026
642f0d3
Restore ENABLE_SOURCEMAP=reduced support
silverwind Mar 28, 2026
6267fc5
remove "/assets/" check, rename sharedWorkerPath to sharedWorkerUri
wxiaoguang Mar 28, 2026
4930648
fix comment
wxiaoguang Mar 28, 2026
38c6a56
break middleware chain-call when the request should be handled by vit…
wxiaoguang Mar 28, 2026
c8dfb5a
fix comment
wxiaoguang Mar 28, 2026
779ef1f
Merge branch 'main' into vite
wxiaoguang Mar 28, 2026
bf007fe
mark vite connection as long polling to avoid error logs
wxiaoguang Mar 28, 2026
60b8de1
fix ViteDevMiddleware
wxiaoguang Mar 28, 2026
9345dc1
avoid vite request log flood
wxiaoguang Mar 28, 2026
90f7a54
fine tune vite dev proxy
wxiaoguang Mar 28, 2026
6db4523
add debug header and error response for vite proxy
wxiaoguang Mar 28, 2026
147197c
tell developers to run make watch-frontend
wxiaoguang Mar 28, 2026
7ab77f0
unify script importing
wxiaoguang Mar 29, 2026
64b4969
security
wxiaoguang Mar 29, 2026
690ca24
fix lint
wxiaoguang Mar 29, 2026
f8f9fb1
fix lint
wxiaoguang Mar 29, 2026
bc22661
Merge branch 'main' into vite
wxiaoguang Mar 29, 2026
214ab0c
Remove Gitpod integration
silverwind Mar 29, 2026
ca37154
Merge branch 'main' into vite
GiteaBot Mar 29, 2026
f2d119a
Apply suggestion from @silverwind
silverwind Mar 29, 2026
743458b
Merge branch 'main' into vite
GiteaBot Mar 29, 2026
1027575
Merge branch 'main' into vite
GiteaBot Mar 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ cpu.out
/yarn-error.log
/npm-debug.log*
/.pnpm-store
/public/assets/.vite
/public/assets/js
/public/assets/css
/public/assets/fonts
Expand All @@ -87,8 +88,6 @@ cpu.out
/VERSION
/.air

# Files and folders that were previously generated
/public/assets/img/webpack

# Snapcraft
/gitea_a*.txt
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1
# Build frontend on the native platform to avoid QEMU-related issues with esbuild/webpack
# Build frontend on the native platform to avoid QEMU-related issues with nodejs ecosystem
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.26-alpine3.23 AS frontend-build
RUN apk --no-cache add build-base git nodejs pnpm
WORKDIR /src
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.rootless
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1
# Build frontend on the native platform to avoid QEMU-related issues with esbuild/webpack
# Build frontend on the native platform to avoid QEMU-related issues with nodejs ecosystem
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.26-alpine3.23 AS frontend-build
RUN apk --no-cache add build-base git nodejs pnpm
WORKDIR /src
Expand Down
33 changes: 16 additions & 17 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/r
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration,$(shell $(GO) list ./... | grep -v /vendor/))
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...)

WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f)
WEBPACK_CONFIGS := webpack.config.ts tailwind.config.ts
WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css
WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts
FRONTEND_SOURCES := $(shell find web_src/js web_src/css -type f)
FRONTEND_CONFIGS := vite.config.ts tailwind.config.ts
FRONTEND_DEST := public/assets/.vite/manifest.json
FRONTEND_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts public/assets/.vite

BINDATA_DEST_WILDCARD := modules/migration/bindata.* modules/public/bindata.* modules/options/bindata.* modules/templates/bindata.*

Expand Down Expand Up @@ -199,7 +199,7 @@ git-check:

.PHONY: clean-all
clean-all: clean ## delete backend, frontend and integration files
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
rm -rf $(FRONTEND_DEST_ENTRIES) node_modules

.PHONY: clean
clean: ## delete backend and integration files
Expand Down Expand Up @@ -380,9 +380,8 @@ watch: ## watch everything and continuously rebuild
@bash tools/watch.sh

.PHONY: watch-frontend
watch-frontend: node_modules ## watch frontend files and continuously rebuild
@rm -rf $(WEBPACK_DEST_ENTRIES)
NODE_ENV=development $(NODE_VARS) pnpm exec webpack --watch --progress
watch-frontend: node_modules ## start vite dev server for frontend
NODE_ENV=development $(NODE_VARS) pnpm exec vite

.PHONY: watch-backend
watch-backend: ## watch backend files and continuously rebuild
Expand Down Expand Up @@ -645,7 +644,7 @@ install: $(wildcard *.go)
build: frontend backend ## build everything

.PHONY: frontend
frontend: $(WEBPACK_DEST) ## build frontend files
frontend: $(FRONTEND_DEST) ## build frontend files

.PHONY: backend
backend: generate-backend $(EXECUTABLE) ## build backend files
Expand All @@ -672,7 +671,7 @@ ifneq ($(and $(STATIC),$(findstring pam,$(TAGS))),)
endif
CGO_ENABLED="$(CGO_ENABLED)" CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@

$(EXECUTABLE_E2E): $(GO_SOURCES) $(WEBPACK_DEST)
$(EXECUTABLE_E2E): $(GO_SOURCES) $(FRONTEND_DEST)
CGO_ENABLED=1 $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TEST_TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@

.PHONY: release
Expand Down Expand Up @@ -776,15 +775,15 @@ update-py: node_modules ## update py dependencies
uv sync
@touch .venv

.PHONY: webpack
webpack: $(WEBPACK_DEST) ## build webpack files
.PHONY: vite
vite: $(FRONTEND_DEST) ## build vite files

$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) pnpm-lock.yaml
$(FRONTEND_DEST): $(FRONTEND_SOURCES) $(FRONTEND_CONFIGS) pnpm-lock.yaml
@$(MAKE) -s node_modules
@rm -rf $(WEBPACK_DEST_ENTRIES)
@echo "Running webpack..."
@BROWSERSLIST_IGNORE_OLD_DATA=true $(NODE_VARS) pnpm exec webpack
@touch $(WEBPACK_DEST)
@rm -rf $(FRONTEND_DEST_ENTRIES)
@echo "Running vite build..."
@$(NODE_VARS) pnpm exec vite build
@touch $(FRONTEND_DEST)

.PHONY: svg
svg: node_modules ## build svg files
Expand Down
8 changes: 6 additions & 2 deletions eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,11 @@ export default defineConfig([
'no-restricted-exports': [0],
'no-restricted-globals': [2, ...restrictedGlobals],
'no-restricted-properties': [2, ...restrictedProperties],
'no-restricted-imports': [0],
'no-restricted-imports': [2, {paths: [
{name: 'jquery', message: 'Use the global $ instead', allowTypeImports: true},
{name: 'htmx.org', message: 'Use the global htmx instead', allowTypeImports: true},
{name: 'idiomorph/htmx', message: 'Loaded in globals.ts', allowTypeImports: true},
]}],
'no-restricted-syntax': [2, 'WithStatement', 'ForInStatement', 'LabeledStatement', 'SequenceExpression'],
'no-return-assign': [0],
'no-script-url': [2],
Expand Down Expand Up @@ -1014,6 +1018,6 @@ export default defineConfig([
},
{
files: ['web_src/**/*'],
languageOptions: {globals: {...globals.browser, ...globals.webpack}},
languageOptions: {globals: {...globals.browser, ...globals.jquery, htmx: false}},
},
]);
5 changes: 5 additions & 0 deletions modules/graceful/server_http.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import (

func newHTTPServer(network, address, name string, handler http.Handler) (*Server, ServeFunction) {
server := NewServer(network, address, name)
protocols := http.Protocols{}
protocols.SetHTTP1(true)
protocols.SetHTTP2(true) // HTTP/2 can only be used when Gitea is configured to use TLS
protocols.SetUnencryptedHTTP2(true) // Allow HTTP/2 without TLS, in case Gitea is behind a reverse proxy
httpServer := http.Server{
Protocols: &protocols,
Handler: handler,
BaseContext: func(net.Listener) context.Context { return GetManager().HammerContext() },
}
Expand Down
11 changes: 5 additions & 6 deletions modules/markup/external/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"io"

"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
Expand Down Expand Up @@ -61,19 +62,17 @@ func (p *openAPIRenderer) Render(ctx *markup.RenderContext, input io.Reader, out
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="%s/assets/css/swagger.css?v=%s">
<link rel="stylesheet" href="%s">
</head>
<body>
<div id="swagger-ui"><textarea class="swagger-spec-content" data-spec-filename="%s">%s</textarea></div>
<script src="%s/assets/js/swagger.js?v=%s"></script>
<script type="module" src="%s"></script>
</body>
</html>`,
setting.StaticURLPrefix,
setting.AssetVersion,
public.AssetURI("css/swagger.css"),
html.EscapeString(ctx.RenderOptions.RelativePath),
html.EscapeString(util.UnsafeBytesToString(content)),
setting.StaticURLPrefix,
setting.AssetVersion,
public.AssetURI("js/swagger.js"),
))
return err
}
7 changes: 4 additions & 3 deletions modules/markup/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (

"code.gitea.io/gitea/modules/htmlutil"
"code.gitea.io/gitea/modules/markup/internal"
"code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/util"
Expand Down Expand Up @@ -237,10 +238,10 @@ func RenderWithRenderer(ctx *RenderContext, renderer Renderer, input io.Reader,
return renderIFrame(ctx, extOpts.ContentSandbox, output)
}
// else: this is a standalone page, fallthrough to the real rendering, and add extra JS/CSS
extraStyleHref := setting.AppSubURL + "/assets/css/external-render-iframe.css"
extraScriptSrc := setting.AppSubURL + "/assets/js/external-render-iframe.js"
extraStyleHref := public.AssetURI("css/external-render-iframe.css")
extraScriptSrc := public.AssetURI("js/external-render-iframe.js")
// "<script>" must go before "<link>", to make Golang's http.DetectContentType() can still recognize the content as "text/html"
extraHeadHTML = htmlutil.HTMLFormat(`<script src="%s"></script><link rel="stylesheet" href="%s">`, extraScriptSrc, extraStyleHref)
extraHeadHTML = htmlutil.HTMLFormat(`<script type="module" src="%s"></script><link rel="stylesheet" href="%s">`, extraScriptSrc, extraStyleHref)
}

ctx.usedByRender = true
Expand Down
156 changes: 156 additions & 0 deletions modules/public/manifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package public

import (
"io"
"path"
"sync"
"sync/atomic"
"time"

"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)

type manifestEntry struct {
File string `json:"file"`
Name string `json:"name"`
IsEntry bool `json:"isEntry"`
CSS []string `json:"css"`
}

type manifestDataStruct struct {
paths map[string]string // unhashed path -> hashed path
names map[string]string // hashed path -> entry name
modTime int64
checkTime time.Time
}

var (
manifestData atomic.Pointer[manifestDataStruct]
manifestFS = sync.OnceValue(AssetFS)
)

const manifestPath = "assets/.vite/manifest.json"

func parseManifest(data []byte) (map[string]string, map[string]string) {
var manifest map[string]manifestEntry
if err := json.Unmarshal(data, &manifest); err != nil {
log.Error("Failed to parse frontend manifest: %v", err)
return nil, nil
}

paths := make(map[string]string)
names := make(map[string]string)
for _, entry := range manifest {
if !entry.IsEntry || entry.Name == "" {
continue
}
// Build unhashed key from file path: "js/index.js", "css/theme-gitea-dark.css"
dir := path.Dir(entry.File)
ext := path.Ext(entry.File)
key := dir + "/" + entry.Name + ext
paths[key] = entry.File
names[entry.File] = entry.Name
// Map associated CSS files, e.g. "css/index.css" -> "css/index.B3zrQPqD.css"
for _, css := range entry.CSS {
cssKey := path.Dir(css) + "/" + entry.Name + path.Ext(css)
paths[cssKey] = css
names[css] = entry.Name
}
}
return paths, names
}

func reloadManifest(existingData *manifestDataStruct) *manifestDataStruct {
now := time.Now()
data := existingData
if data != nil && now.Sub(data.checkTime) < time.Second {
// a single request triggers multiple calls to getHashedPath
// do not check the manifest file too frequently
return data
}

f, err := manifestFS().Open(manifestPath)
if err != nil {
log.Error("Failed to open frontend manifest: %v", err)
return data
}
defer f.Close()

fi, err := f.Stat()
if err != nil {
log.Error("Failed to stat frontend manifest: %v", err)
return data
}

needReload := data == nil || fi.ModTime().UnixNano() != data.modTime
if !needReload {
return data
}
manifestContent, err := io.ReadAll(f)
if err != nil {
log.Error("Failed to read frontend manifest: %v", err)
return data
}
return storeManifestFromBytes(manifestContent, fi.ModTime().UnixNano(), now)
}

func storeManifestFromBytes(manifestContent []byte, modTime int64, checkTime time.Time) *manifestDataStruct {
paths, names := parseManifest(manifestContent)
data := &manifestDataStruct{
paths: paths,
names: names,
modTime: modTime,
checkTime: checkTime,
}
manifestData.Store(data)
return data
}

func getManifestData() *manifestDataStruct {
data := manifestData.Load()

// In production the manifest is immutable (embedded in the binary).
// In dev mode, check if it changed on disk (for watch-frontend).
if data == nil || !setting.IsProd {
data = reloadManifest(data)
}
if data == nil {
data = &manifestDataStruct{}
}
return data
}

// getHashedPath resolves an unhashed asset path (origin path) to its content-hashed path from the frontend manifest.
// Example: getHashedPath("js/index.js") returns "js/index.C6Z2MRVQ.js"
// Falls back to returning the input path unchanged if the manifest is unavailable.
func getHashedPath(originPath string) string {
data := getManifestData()
if p, ok := data.paths[originPath]; ok {
return p
}
return originPath
}

// AssetURI returns the URI for a frontend asset.
// It may return a relative path or a full URL depending on the StaticURLPrefix setting.
// In Vite dev mode, known entry points are mapped to their source paths
// so the reverse proxy serves them from the Vite dev server.
// In production, it resolves the content-hashed path from the manifest.
func AssetURI(originPath string) string {
if src := viteDevSourceURL(originPath); src != "" {
return src
}
return setting.StaticURLPrefix + "/assets/" + getHashedPath(originPath)
}

// AssetNameFromHashedPath returns the asset entry name for a given hashed asset path.
// Example: returns "theme-gitea-dark" for "css/theme-gitea-dark.CyAaQnn5.css".
// Returns empty string if the path is not found in the manifest.
func AssetNameFromHashedPath(hashedPath string) string {
return getManifestData().names[hashedPath]
}
Loading