chore(ui): change /devtest to /-/demo (#11019)

It has always been largely used for showcasing UI elements but that name didn't work too well for it.

Testing:
Some of existing tests depend on these pages, making it redundant to create extra tests.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/11019
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
0ko 2026-01-26 13:12:25 +01:00
parent 89996009b7
commit 3cafb7fa6c
28 changed files with 71 additions and 73 deletions

View file

@ -664,7 +664,6 @@ var (
"user", // user login/activate/settings, etc "user", // user login/activate/settings, etc
"admin", "admin",
"devtest",
"explore", "explore",
"issues", "issues",
"pulls", "pulls",

View file

@ -367,7 +367,7 @@ var ignoredErrorMessage = []string{
// Test_CmdForgejo_Actions // Test_CmdForgejo_Actions
`DB: No dedicated replica host defined; falling back to primary DSN for replica connections`, `DB: No dedicated replica host defined; falling back to primary DSN for replica connections`,
// TestDevtestErrorpages // TestDemoErrorPages
`ErrorPage() [E] Example error: Example error`, `ErrorPage() [E] Example error: Example error`,
} }

View file

@ -2,7 +2,7 @@
// Copyright 2025 The Forgejo Authors. All rights reserved. // Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
package devtest package demo
import ( import (
"errors" "errors"
@ -18,9 +18,9 @@ import (
"forgejo.org/services/context" "forgejo.org/services/context"
) )
// List all devtest templates, they will be used for e2e tests for the UI components // List all demo templates, they will be used for e2e tests for the UI components
func List(ctx *context.Context) { func List(ctx *context.Context) {
templateNames, err := templates.AssetFS().ListFiles("devtest", true) templateNames, err := templates.AssetFS().ListFiles("demo", true)
if err != nil { if err != nil {
ctx.ServerError("AssetFS().ListFiles", err) ctx.ServerError("AssetFS().ListFiles", err)
return return
@ -33,7 +33,7 @@ func List(ctx *context.Context) {
} }
} }
ctx.Data["SubNames"] = subNames ctx.Data["SubNames"] = subNames
ctx.HTML(http.StatusOK, "devtest/list") ctx.HTML(http.StatusOK, "demo/list")
} }
func FetchActionTest(ctx *context.Context) { func FetchActionTest(ctx *context.Context) {
@ -90,5 +90,5 @@ func Tmpl(ctx *context.Context) {
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
} }
ctx.HTML(http.StatusOK, base.TplName("devtest"+path.Clean("/"+ctx.Params("sub")))) ctx.HTML(http.StatusOK, base.TplName("demo"+path.Clean("/"+ctx.Params("sub"))))
} }

View file

@ -28,7 +28,7 @@ import (
"forgejo.org/routers/common" "forgejo.org/routers/common"
"forgejo.org/routers/web/admin" "forgejo.org/routers/web/admin"
"forgejo.org/routers/web/auth" "forgejo.org/routers/web/auth"
"forgejo.org/routers/web/devtest" "forgejo.org/routers/web/demo"
"forgejo.org/routers/web/events" "forgejo.org/routers/web/events"
"forgejo.org/routers/web/explore" "forgejo.org/routers/web/explore"
"forgejo.org/routers/web/feed" "forgejo.org/routers/web/feed"
@ -1720,10 +1720,12 @@ func registerRoutes(m *web.Route) {
} }
if !setting.IsProd { if !setting.IsProd {
m.Any("/devtest", devtest.List) m.Group("/-", func() {
m.Any("/devtest/fetch-action-test", devtest.FetchActionTest) m.Any("/demo", demo.List)
m.Any("/devtest/{sub}", devtest.Tmpl) m.Any("/demo/fetch-action-test", demo.FetchActionTest)
m.Get("/devtest/error/{errcode}", devtest.ErrorPage) m.Any("/demo/{sub}", demo.Tmpl)
m.Get("/demo/error/{errcode}", demo.ErrorPage)
}, ignSignIn)
} }
m.NotFound(func(w http.ResponseWriter, req *http.Request) { m.NotFound(func(w http.ResponseWriter, req *http.Request) {

View file

@ -28,8 +28,8 @@ export default {
prefix: 'tw-', prefix: 'tw-',
important: true, // the frameworks are mixed together, so tailwind needs to override other framework's styles important: true, // the frameworks are mixed together, so tailwind needs to override other framework's styles
content: [ content: [
isProduction && '!./templates/devtest/**/*', isProduction && '!./templates/demo/**/*',
isProduction && '!./web_src/js/standalone/devtest.js', isProduction && '!./web_src/js/standalone/demo.js',
'!./templates/swagger/v1_json.tmpl', '!./templates/swagger/v1_json.tmpl',
'!./templates/user/auth/oidc_wellknown.tmpl', '!./templates/user/auth/oidc_wellknown.tmpl',
'./templates/**/*.tmpl', './templates/**/*.tmpl',

View file

@ -194,8 +194,8 @@
{{ctx.Locale.Tr "help"}} {{ctx.Locale.Tr "help"}}
</a> </a>
</li> </li>
{{$showDevtest := not .RunModeIsProd}} {{$showDemo := not .RunModeIsProd}}
{{if or .IsAdmin $showDevtest}} {{if or .IsAdmin $showDemo}}
<hr> <hr>
{{end}} {{end}}
{{if .IsAdmin}} {{if .IsAdmin}}
@ -206,11 +206,11 @@
</a> </a>
</li> </li>
{{end}} {{end}}
{{if $showDevtest}} {{if $showDemo}}
<li> <li>
<a href="{{AppSubUrl}}/devtest"> <a href="{{AppSubUrl}}/-/demo">
{{svg "octicon-beaker"}} {{svg "octicon-beaker"}}
Development pages Demo pages
</a> </a>
</li> </li>
{{end}} {{end}}

View file

@ -1,6 +1,6 @@
{{template "base/head" .}} {{template "base/head" .}}
<div class="page-content devtest ui container"> <div class="page-content ui container">
<h1>Buttons</h1> <h1>Buttons</h1>
<p> <p>

View file

@ -1,6 +1,6 @@
{{template "base/head" .}} {{template "base/head" .}}
<div class="page-content devtest ui container"> <div class="page-content ui container">
<h1>Dropdown</h1> <h1>Dropdown</h1>
a.k.a. overflow menu, ellipsis menu a.k.a. overflow menu, ellipsis menu

View file

@ -1,5 +1,5 @@
{{template "base/head" .}} {{template "base/head" .}}
<div class="page-content devtest ui container"> <div class="page-content ui container">
{{template "base/alert" .}} {{template "base/alert" .}}
<div> <div>
<h1>link-action</h1> <h1>link-action</h1>

View file

@ -1,6 +1,6 @@
{{template "base/head" .}} {{template "base/head" .}}
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/devtest.css?v={{AssetVersion}}"> <link rel="stylesheet" href="{{AssetUrlPrefix}}/css/demo.css?v={{AssetVersion}}">
<div class="page-content devtest"> <div class="page-content">
<div class="ui container"> <div class="ui container">
<h1>Flex List (standalone)</h1> <h1>Flex List (standalone)</h1>
<div class="divider"></div> <div class="divider"></div>

View file

@ -1,5 +1,5 @@
{{template "base/head" .}} {{template "base/head" .}}
<div class="page-content devtest ui container"> <div class="page-content ui container">
{{template "base/alert" .}} {{template "base/alert" .}}
<div id="test-modal-form-1" class="ui mini modal"> <div id="test-modal-form-1" class="ui mini modal">

View file

@ -1,6 +1,6 @@
{{template "base/head" .}} {{template "base/head" .}}
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/devtest.css?v={{AssetVersion}}"> <link rel="stylesheet" href="{{AssetUrlPrefix}}/css/demo.css?v={{AssetVersion}}">
<div class="page-content devtest ui container"> <div class="page-content ui container">
<div> <div>
<h1>Link</h1> <h1>Link</h1>
<div> <div>
@ -21,7 +21,7 @@
State: State:
<label><input type="checkbox" name="button-state-disabled" value="disabled">disabled</label> <label><input type="checkbox" name="button-state-disabled" value="disabled">disabled</label>
</div> </div>
<div id="devtest-button-samples"> <div id="demo-button-samples">
<ul class="button-sample-groups"> <ul class="button-sample-groups">
<li class="sample-group"> <li class="sample-group">
<h2>General purpose:</h2> <h2>General purpose:</h2>
@ -65,7 +65,7 @@
</li> </li>
</ul> </ul>
<script type="module"> <script type="module">
const $buttons = $('#devtest-button-samples').find('button.ui'); const $buttons = $('#demo-button-samples').find('button.ui');
const $buttonStyles = $('input[name*="button-style"]'); const $buttonStyles = $('input[name*="button-style"]');
$buttonStyles.on('click', () => $buttonStyles.map((_ ,el) => $buttons.toggleClass(el.value, el.checked))); $buttonStyles.on('click', () => $buttonStyles.map((_ ,el) => $buttons.toggleClass(el.value, el.checked)));
@ -315,6 +315,6 @@
<button class="{{if true}}tw-bg-red{{end}} tw-p-5 tw-border tw-rounded hover:tw-bg-blue active:tw-bg-yellow">Button</button> <button class="{{if true}}tw-bg-red{{end}} tw-p-5 tw-border tw-rounded hover:tw-bg-blue active:tw-bg-yellow">Button</button>
</div> </div>
<script src="{{AssetUrlPrefix}}/js/devtest.js?v={{AssetVersion}}"></script> <script src="{{AssetUrlPrefix}}/js/demo.js?v={{AssetVersion}}"></script>
</div> </div>
{{template "base/footer" .}} {{template "base/footer" .}}

View file

@ -1,6 +1,6 @@
{{template "base/head" .}} {{template "base/head" .}}
<div class="page-content devtest ui container"> <div class="page-content ui container">
<h1>Hashbox (shabox)</h1> <h1>Hashbox (shabox)</h1>
<h2>Unsigned</h2> <h2>Unsigned</h2>

View file

@ -1,6 +1,6 @@
{{template "base/head" .}} {{template "base/head" .}}
<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/devtest.css?v={{AssetVersion}}"> <link rel="stylesheet" href="{{AssetUrlPrefix}}/css/demo.css?v={{AssetVersion}}">
<div class="page-content devtest ui container"> <div class="page-content ui container">
<div> <div>
<h1>Label</h1> <h1>Label</h1>
<div class="flex-text-block tw-my-2"> <div class="flex-text-block tw-my-2">

View file

@ -1,7 +1,7 @@
{{template "base/head" .}} {{template "base/head" .}}
<div role="main" class="page-content ui container"> <div role="main" class="page-content ui container">
<h1>Development pages</h1> <h1>Demo pages</h1>
<p>Various pages that may be helpful in development or testing</p> <p>Various pages that may be helpful in development or testing</p>
@ -9,7 +9,7 @@
<h2>Components</h2> <h2>Components</h2>
<ul> <ul>
{{range .SubNames}} {{range .SubNames}}
<li><a href="{{AppSubUrl}}/devtest/{{.}}">{{.}}</a></li> <li><a href="{{AppSubUrl}}/-/demo/{{.}}">{{.}}</a></li>
{{end}} {{end}}
</ul> </ul>
</article> </article>
@ -17,9 +17,9 @@
<article> <article>
<h2>Error pages</h2> <h2>Error pages</h2>
<ul> <ul>
<li><a href="{{AppSubUrl}}/devtest/error/404">Not found</a></li> <li><a href="{{AppSubUrl}}/-/demo/error/404">Not found</a></li>
<li><a href="{{AppSubUrl}}/devtest/error/413">Quota exhaustion</a></li> <li><a href="{{AppSubUrl}}/-/demo/error/413">Quota exhaustion</a></li>
<li><a href="{{AppSubUrl}}/devtest/error/500">Server error</a></li> <li><a href="{{AppSubUrl}}/-/demo/error/500">Server error</a></li>
</ul> </ul>
</article> </article>
</div> </div>

View file

@ -1,6 +1,6 @@
{{template "base/head" .}} {{template "base/head" .}}
<div class="page-content devtest ui container"> <div class="page-content ui container">
<h1>Modals</h1> <h1>Modals</h1>
<div class="button-sequence"> <div class="button-sequence">

View file

@ -1,11 +1,11 @@
{{template "base/head" .}} {{template "base/head" .}}
<div class="page-content devtest"> <div class="page-content">
<div class="tw-flex"> <div class="tw-flex">
<div class="tw-w-4/5"> <div class="tw-w-4/5">
hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello
</div> </div>
<div class="tw-w-1/5"> <div class="tw-w-1/5">
{{template "devtest/tmplerr-sub" .}} {{template "demo/tmplerr-sub" .}}
</div> </div>
</div> </div>
</div> </div>

View file

@ -49,7 +49,7 @@ test('Button visuals', async ({browser}) => {
const context = await browser.newContext({javaScriptEnabled: false}); const context = await browser.newContext({javaScriptEnabled: false});
const page = await context.newPage(); const page = await context.newPage();
const response = await page.goto('/devtest/buttons'); const response = await page.goto('/-/demo/buttons');
expect(response?.status()).toBe(200); expect(response?.status()).toBe(200);
const transparent = 'rgba(0, 0, 0, 0)'; const transparent = 'rgba(0, 0, 0, 0)';

View file

@ -5,7 +5,7 @@
// templates/shared/user/actions_menu.tmpl // templates/shared/user/actions_menu.tmpl
// templates/org/header.tmpl // templates/org/header.tmpl
// templates/explore/search.tmpl // templates/explore/search.tmpl
// templates/devtest/dropdown.tmpl // templates/demo/dropdown.tmpl
// web_src/js/modules/dropdown.ts // web_src/js/modules/dropdown.ts
// @watch end // @watch end
@ -228,12 +228,12 @@ test.describe(`Visual properties`, () => {
expect(await inactiveItem.evaluate((el) => getComputedStyle(el).backgroundColor)).toBe('rgba(0, 0, 0, 0)'); expect(await inactiveItem.evaluate((el) => getComputedStyle(el).backgroundColor)).toBe('rgba(0, 0, 0, 0)');
}); });
test('Devtest', async ({browser}) => { test('Demo page', async ({browser}) => {
const context = await browser.newContext({javaScriptEnabled: false}); const context = await browser.newContext({javaScriptEnabled: false});
const page = await context.newPage(); const page = await context.newPage();
// `/devtest` has dropdowns with various combinations of items // `/-/demo` has dropdowns with various combinations of items
await page.goto('/devtest/dropdown'); await page.goto('/-/demo/dropdown');
// Dropdown with just 3 items and nothing special // Dropdown with just 3 items and nothing special
await page.locator(`#dropdown-1 > summary`).click(); await page.locator(`#dropdown-1 > summary`).click();

View file

@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// @watch start // @watch start
// templates/devtest/modal.tmpl // templates/demo/modal.tmpl
// templates/repo/editor/edit.tmpl // templates/repo/editor/edit.tmpl
// templates/repo/editor/patch.tmpl // templates/repo/editor/patch.tmpl
// web_src/js/features/repo-editor.js // web_src/js/features/repo-editor.js
@ -50,7 +50,7 @@ test('Dialog modal', async ({page}) => {
test('Dialog modal: width', async ({page, isMobile}) => { test('Dialog modal: width', async ({page, isMobile}) => {
// This test doesn't need JS and runs a little faster without it // This test doesn't need JS and runs a little faster without it
await page.goto('/devtest/modal'); await page.goto('/-/demo/modal');
// Open modal with short content // Open modal with short content
const shortModal = page.locator('#short-modal'); const shortModal = page.locator('#short-modal');

View file

@ -14,11 +14,11 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// `/devtest/error/{errcode}` provides a convenient way of testing various // `/-/demo/error/{errcode}` provides a convenient way of testing various
// error pages sometimes which can be hard to reach otherwise. // error pages sometimes which can be hard to reach otherwise.
// This file is a test of various attributes on those pages. // This file is a test of various attributes on those pages.
func enableDevtest() func() { func enableDemoPages() func() {
resetIsProd := test.MockVariableValue(&setting.IsProd, false) resetIsProd := test.MockVariableValue(&setting.IsProd, false)
resetRoutes := test.MockVariableValue(&testWebRoutes, routers.NormalRoutes()) resetRoutes := test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())
return func() { return func() {
@ -27,13 +27,13 @@ func enableDevtest() func() {
} }
} }
func TestDevtestErrorpages(t *testing.T) { func TestDemoErrorPages(t *testing.T) {
defer enableDevtest()() defer enableDemoPages()()
t.Run("Server error", func(t *testing.T) { t.Run("Server error", func(t *testing.T) {
// `/devtest/error/x` returns 500 for any x by default. // `/-/demo/error/x` returns 500 for any x by default.
// `/500` is simply for good look here // `/500` is simply for good look here
req := NewRequest(t, "GET", "/devtest/error/500") req := NewRequest(t, "GET", "/-/demo/error/500")
resp := MakeRequest(t, req, http.StatusInternalServerError) resp := MakeRequest(t, req, http.StatusInternalServerError)
doc := NewHTMLParser(t, resp.Body) doc := NewHTMLParser(t, resp.Body)
assert.Equal(t, "500", doc.Find(".error-code").Text()) assert.Equal(t, "500", doc.Find(".error-code").Text())
@ -42,7 +42,7 @@ func TestDevtestErrorpages(t *testing.T) {
t.Run("Page not found", t.Run("Page not found",
func(t *testing.T) { func(t *testing.T) {
req := NewRequest(t, "GET", "/devtest/error/404"). req := NewRequest(t, "GET", "/-/demo/error/404").
// Without this header `notFoundInternal` returns plaintext error message // Without this header `notFoundInternal` returns plaintext error message
SetHeader("Accept", "text/html") SetHeader("Accept", "text/html")
resp := MakeRequest(t, req, http.StatusNotFound) resp := MakeRequest(t, req, http.StatusNotFound)
@ -53,7 +53,7 @@ func TestDevtestErrorpages(t *testing.T) {
t.Run("Quota exhaustion", t.Run("Quota exhaustion",
func(t *testing.T) { func(t *testing.T) {
req := NewRequest(t, "GET", "/devtest/error/413") req := NewRequest(t, "GET", "/-/demo/error/413")
resp := MakeRequest(t, req, http.StatusRequestEntityTooLarge) resp := MakeRequest(t, req, http.StatusRequestEntityTooLarge)
doc := NewHTMLParser(t, resp.Body) doc := NewHTMLParser(t, resp.Body)
assert.Equal(t, "413", doc.Find(".error-code").Text()) assert.Equal(t, "413", doc.Find(".error-code").Text())

View file

@ -76,7 +76,7 @@ func TestNavbarItems(t *testing.T) {
defer test.MockVariableValue(&setting.IsProd, false)() defer test.MockVariableValue(&setting.IsProd, false)()
page := NewHTMLParser(t, regularUser.MakeRequest(t, NewRequest(t, "GET", testPage), http.StatusOK).Body) page := NewHTMLParser(t, regularUser.MakeRequest(t, NewRequest(t, "GET", testPage), http.StatusOK).Body)
page.AssertElement(t, `details.dropdown a[href="/devtest"]`, true) page.AssertElement(t, `details.dropdown a[href="/-/demo"]`, true)
testNavbarUserMenuActiveItem(t, regularUser, "/user/settings") testNavbarUserMenuActiveItem(t, regularUser, "/user/settings")
testNavbarUserMenuActiveItem(t, adminUser, "/admin") testNavbarUserMenuActiveItem(t, adminUser, "/admin")
@ -95,7 +95,7 @@ func TestNavbarItems(t *testing.T) {
{`details.dropdown a[href="/notifications/subscriptions"]`, true}, {`details.dropdown a[href="/notifications/subscriptions"]`, true},
{`details.dropdown a[href="/user/settings"]`, true}, {`details.dropdown a[href="/user/settings"]`, true},
{`details.dropdown a[href="/admin"]`, false}, {`details.dropdown a[href="/admin"]`, false},
{`details.dropdown a[href="/devtest"]`, false}, {`details.dropdown a[href="/-/demo"]`, false},
{`details.dropdown a[href="https://forgejo.org/docs/latest/"]`, true}, {`details.dropdown a[href="https://forgejo.org/docs/latest/"]`, true},
{`details.dropdown a[data-url="/user/logout"]`, true}, {`details.dropdown a[data-url="/user/logout"]`, true},
} }
@ -114,7 +114,7 @@ func TestNavbarItems(t *testing.T) {
{`details.dropdown a[href="/notifications/subscriptions"]`, true}, {`details.dropdown a[href="/notifications/subscriptions"]`, true},
{`details.dropdown a[href="/user/settings"]`, true}, {`details.dropdown a[href="/user/settings"]`, true},
{`details.dropdown a[href="/admin"]`, true}, {`details.dropdown a[href="/admin"]`, true},
{`details.dropdown a[href="/devtest"]`, false}, {`details.dropdown a[href="/-/demo"]`, false},
{`details.dropdown a[href="https://forgejo.org/docs/latest/"]`, true}, {`details.dropdown a[href="https://forgejo.org/docs/latest/"]`, true},
{`details.dropdown a[data-url="/user/logout"]`, true}, {`details.dropdown a[data-url="/user/logout"]`, true},
} }

View file

@ -171,9 +171,9 @@ func TestGlobalTwoFactorRequirement(t *testing.T) {
assert.Greater(t, htmlDoc.Find(".navbar-left > a.item").Length(), 1) // show the Logo, and other links assert.Greater(t, htmlDoc.Find(".navbar-left > a.item").Length(), 1) // show the Logo, and other links
assert.Greater(t, htmlDoc.Find(".navbar-right details.dropdown a").Length(), 1) assert.Greater(t, htmlDoc.Find(".navbar-right details.dropdown a").Length(), 1)
// 500 page // demo pages are using ignSignIn and are expected to be accessible with loginAllowed
reset := enableDevtest() reset := enableDemoPages()
req = NewRequest(t, "GET", "/devtest/error/500") req = NewRequest(t, "GET", "/-/demo/error/500")
req.Header.Add("Accept", "text/html") req.Header.Add("Accept", "text/html")
resp = session.MakeRequest(t, req, http.StatusInternalServerError) resp = session.MakeRequest(t, req, http.StatusInternalServerError)
htmlDoc = NewHTMLParser(t, resp.Body) htmlDoc = NewHTMLParser(t, resp.Body)
@ -195,14 +195,12 @@ func TestGlobalTwoFactorRequirement(t *testing.T) {
assert.Equal(t, 1, userLinks.Length()) // only logout link assert.Equal(t, 1, userLinks.Length()) // only logout link
assert.Equal(t, "Sign out", strings.TrimSpace(userLinks.Text())) assert.Equal(t, "Sign out", strings.TrimSpace(userLinks.Text()))
// 500 page // demo pages are using ignSignIn and should redirect like any other pages if 2FA is required but missing
reset := enableDevtest() reset := enableDemoPages()
req = NewRequest(t, "GET", "/devtest/error/500") req = NewRequest(t, "GET", "/-/demo/error/500")
req.Header.Add("Accept", "text/html") req.Header.Add("Accept", "text/html")
resp = session.MakeRequest(t, req, http.StatusInternalServerError) resp = session.MakeRequest(t, req, http.StatusSeeOther)
htmlDoc = NewHTMLParser(t, resp.Body) assert.Equal(t, "/user/settings/security", resp.Header().Get("Location"))
assert.Equal(t, 1, htmlDoc.Find(".navbar-left > a.item").Length())
htmlDoc.AssertElement(t, ".navbar-right", false)
reset() reset()
// 2fa page // 2fa page

View file

@ -117,7 +117,6 @@ func TestRenameReservedUsername(t *testing.T) {
"avatar", "avatar",
"avatars", "avatars",
"captcha", "captcha",
"devtest",
"explore", "explore",
"favicon.ico", "favicon.ico",
"ghost", "ghost",

View file

@ -132,9 +132,9 @@ export default {
fileURLToPath(new URL('web_src/js/features/eventsource.sharedworker.js', import.meta.url)), fileURLToPath(new URL('web_src/js/features/eventsource.sharedworker.js', import.meta.url)),
], ],
...(!isProduction && { ...(!isProduction && {
devtest: [ demo: [
fileURLToPath(new URL('web_src/js/standalone/devtest.js', import.meta.url)), fileURLToPath(new URL('web_src/js/standalone/demo.js', import.meta.url)),
fileURLToPath(new URL('web_src/css/standalone/devtest.css', import.meta.url)), fileURLToPath(new URL('web_src/css/standalone/demo.css', import.meta.url)),
], ],
}), }),
...themes, ...themes,