From 4afba657dd553bc53a377fa4815b08ece59d6a2f Mon Sep 17 00:00:00 2001 From: Danila Beseda Date: Thu, 3 Sep 2026 15:06:43 +0300 Subject: [PATCH 1/3] feat: add public path fallback --- README.md | 38 ++++ src/common/config.ts | 81 ++++++++ src/common/models/index.ts | 47 +++++ src/common/webpack/config.ts | 32 ++- src/common/webpack/public-path-fallback.js | 226 +++++++++++++++++++++ 5 files changed, 415 insertions(+), 9 deletions(-) create mode 100644 src/common/webpack/public-path-fallback.js diff --git a/README.md b/README.md index 3a282b0..ff63dd1 100755 --- a/README.md +++ b/README.md @@ -214,6 +214,8 @@ Set it to wherever the `dist/public/build` output is actually served: `'/'` if a If dynamic imports 404 at `/build/js/...` while other assets load from `/js/...`, or HMR breaks in dev, set this global — changing `publicPath` in config will not help. +If the runtime should retry another base URL when a chunk fails to load from this one, see [`publicPathFallback`](#production-build). + #### Options All paths must be specified relative `rootDir` of the project. @@ -358,6 +360,42 @@ With this `{rootDir}/src/ui/tsconfig.json`: - `publicPath` (`string`) - public path to access files from the browser - `compress` (`boolean`) - upload also gzip and brotli compressed versions of files - `additionalPattern` (`string[]`) — patterns for uploading additional files. By default, only files generated by webpack are loaded. +- `publicPathFallback` (`Array}>`) — ordered public paths to retry a failed async chunk from. Disabled by default. + + On failure, the runtime switches to and sticks with the next candidate for all later loads, so only the first failing chunk pays for a degraded CDN. + + `window.__PUBLIC_PATH__` holds the current public path and is always tried first automatically — moved to the front of the list (or de-duplicated there) whether or not it's also configured. So `publicPathFallback` should only ever list backups; a single entry is already useful. + + Each entry is a plain string, or an object: + + - `publicPath` (`string`) — the backup base URL. + - `hosts` (`string | RegExp | Array`, optional) — restricts the entry to hosts it applies to, matched against `location.hostname`. A string must match exactly, a `RegExp` is tested against it; matching is always case-insensitive. Omitted means every host. + + ```ts + import {defineConfig} from '@gravity-ui/app-builder'; + + export default defineConfig({ + client: { + publicPathFallback: [ + {publicPath: 'https://cdn.example.ru/build/', hosts: /\.ru$/}, + {publicPath: 'https://cdn.example.kz/build/', hosts: 'app.example.kz'}, + '/build/', + ], + }, + }); + // on app.example.ru: window.__PUBLIC_PATH__ -> https://cdn.example.ru/build/ -> /build/ + // on app.example.kz: window.__PUBLIC_PATH__ -> https://cdn.example.kz/build/ -> /build/ + // anywhere else: window.__PUBLIC_PATH__ -> /build/ + ``` + + Independent of `cdn`, which only handles uploading static — use this whether the upload is done by app-builder or by something else entirely. + + If you add the local public path as a candidate, use whatever your build actually serves from — `/build/` by default, but `publicPathPrefix` and module federation asset isolation both change it. + + Covers only lazy JS chunks and their async CSS. **Not covered**: initial `