This is an automated email from the ASF dual-hosted git repository.

vatsrahul1001 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new eff567c7e60 UI: Rewrite modulepreload hrefs to the api-server static 
path (#67548)
eff567c7e60 is described below

commit eff567c7e607ecd744d4ec4dc5e7a6fedb33663c
Author: Pierre Jeambrun <[email protected]>
AuthorDate: Tue May 26 15:56:44 2026 +0200

    UI: Rewrite modulepreload hrefs to the api-server static path (#67548)
    
    * UI: Fix Monaco workers crashing in production mode
    
    `?url` does not run the Vite worker pipeline. For `editor.worker.js` it
    inlines the raw source as a base64 data URL (the source is below the
    default `assetsInlineLimit`), and the inlined module still has bare
    specifier imports it cannot resolve in a Worker context. For
    `json.worker.js` it copies a partly-bundled file whose top-level
    `import '../../editor/editor.worker.js'` resolves to a path that does
    not exist in the build output. Both cases manifest at runtime as
    "Could not create web worker(s). Falling back to loading web worker
    code in main thread" followed by a stream of fallback fetch errors.
    
    Switch to `?worker&url`: Vite runs the file through the worker pipeline
    (bundling all dependencies into a self-contained IIFE) and the URL
    query suffix returns the resulting URL as a string instead of a Worker
    constructor. The Blob shim from #67352 then imports a properly bundled
    worker, restoring real worker-thread execution.
    
    * UI: Rewrite modulepreload hrefs to the api-server static path
    
    Vite emits both `<script src="./assets/...">` and
    `<link rel="modulepreload" href="./assets/...">` for chunks in the
    built `index.html`. The `transform-url-src` plugin only rewrote
    `src="./assets/"` to `src="./static/assets/"`, leaving the
    modulepreload `href`s untouched. Behind the api-server's `/static/`
    mount those preload requests hit the SPA HTML fallback and the
    browser fails them with `Expected a JavaScript-or-Wasm module script
    but the server responded with a MIME type of "text/html"`. The chunks
    themselves still load via `<script src>` so the app works, but every
    page load wastes three round-trips on the failed preloads and floods
    the console.
    
    Apply the same `static/` rewrite to `href="./assets/"` and switch all
    three rewrites to `replaceAll` so multiple matches (one per chunk) are
    handled — `replace` with a string only touches the first occurrence.
    
    ---------
    
    Co-authored-by: Rahul Vats <[email protected]>
---
 .../src/components/MonacoEditor/configureMonaco.ts   | 20 ++++++++++++--------
 airflow-core/src/airflow/ui/vite.config.ts           |  5 ++++-
 2 files changed, 16 insertions(+), 9 deletions(-)

diff --git 
a/airflow-core/src/airflow/ui/src/components/MonacoEditor/configureMonaco.ts 
b/airflow-core/src/airflow/ui/src/components/MonacoEditor/configureMonaco.ts
index 2ad53eb09a6..911cc489df1 100644
--- a/airflow-core/src/airflow/ui/src/components/MonacoEditor/configureMonaco.ts
+++ b/airflow-core/src/airflow/ui/src/components/MonacoEditor/configureMonaco.ts
@@ -35,15 +35,19 @@ const loadMonacoModules = async () => {
     import("monaco-editor/esm/vs/base/browser/ui/codicons/codiconStyles"),
   ]).then(([api]) => api);
 
-  // Resolve the workers as plain URLs (not Vite `?worker` constructors). In 
dev mode
-  // the SPA shell is served by the airflow api-server while Vite serves 
assets on a
-  // different origin, and `new Worker(crossOriginUrl, { type: "module" })` is 
rejected
-  // by the browser. Wrapping the cross-origin URL in a same-origin Blob shim 
that just
-  // re-imports it sidesteps the restriction (CORS still permits the inner 
import). In
-  // production the worker is same-origin and the shim is harmless.
+  // Resolve the bundled worker URLs (`?worker&url` runs the worker through 
Vite's worker
+  // pipeline — bundling all dependencies — and returns the resulting URL as a 
string,
+  // unlike `?url` which would treat the file as a raw asset and either inline 
its source
+  // as a data URL (assetsInlineLimit) or copy it without bundling its 
imports, leaving
+  // unresolved bare specifiers at runtime). In dev mode the SPA shell is 
served by the
+  // airflow api-server while Vite serves assets on a different origin, and
+  // `new Worker(crossOriginUrl, { type: "module" })` is rejected by the 
browser.
+  // Wrapping the cross-origin URL in a same-origin Blob shim that just 
re-imports it
+  // sidesteps the restriction (CORS still permits the inner import). In 
production the
+  // worker is same-origin and the shim is harmless.
   const workerUrls = Promise.all([
-    import("monaco-editor/esm/vs/editor/editor.worker.js?url").then((module) 
=> module.default),
-    
import("monaco-editor/esm/vs/language/json/json.worker.js?url").then((module) 
=> module.default),
+    
import("monaco-editor/esm/vs/editor/editor.worker.js?worker&url").then((module) 
=> module.default),
+    
import("monaco-editor/esm/vs/language/json/json.worker.js?worker&url").then((module)
 => module.default),
   ]);
 
   const languageContributions = Promise.all([
diff --git a/airflow-core/src/airflow/ui/vite.config.ts 
b/airflow-core/src/airflow/ui/vite.config.ts
index 4d1c8dc2932..546e5cc1f02 100644
--- a/airflow-core/src/airflow/ui/vite.config.ts
+++ b/airflow-core/src/airflow/ui/vite.config.ts
@@ -37,7 +37,10 @@ export default defineConfig({
     {
       name: "transform-url-src",
       transformIndexHtml: (html) =>
-        html.replace(`src="./assets/`, 
`src="./static/assets/`).replace(`href="/`, `href="./`),
+        html
+          .replaceAll(`src="./assets/`, `src="./static/assets/`)
+          .replaceAll(`href="./assets/`, `href="./static/assets/`)
+          .replaceAll(`href="/`, `href="./`),
     },
     // Keep Monaco's codicon CSS as a real CSS file (rather than inlined into 
JS).
     // The codicon stylesheet references `codicon.ttf` with a CSS-relative URL 
— when

Reply via email to