@@ -31,6 +31,7 @@ export default defineConfig(({ mode }) => {
3131 blogPostsPlugin ( ) ,
3232 marketingSearchIndexPlugin ( { source : 'vocs' } ) ,
3333 marketingPages ( ) ,
34+ developersProxyRouteNormalization ( ) ,
3435 vocs ( ) ,
3536 Icons ( { compiler : 'jsx' , jsx : 'react' } ) ,
3637 react ( ) ,
@@ -64,6 +65,101 @@ export default defineConfig(({ mode }) => {
6465
6566const marketingRoutes = [ '/' , '/build' , '/blog' , '/performance' ]
6667
68+ function developersProxyRouteNormalization ( ) : Plugin {
69+ return {
70+ name : 'tempo-developers-proxy-route-normalization' ,
71+ enforce : 'post' ,
72+ transform ( code , id ) {
73+ if ( id !== '\0virtual:vite-rsc-waku/client-entry' ) return
74+
75+ return code
76+ . replace (
77+ "import { Router } from 'waku/router/client';" ,
78+ "import { Router, unstable_parseRoute } from 'waku/router/client';" ,
79+ )
80+ . replace (
81+ 'const rootElement = createElement(StrictMode, null, createElement(Router));' ,
82+ `const developersPrefix = '/developers';
83+ const shouldUseDevelopersPrefix = () => window.location.pathname === developersPrefix || window.location.pathname.startsWith(developersPrefix + '/');
84+ const publicDevelopersPath = (path) => {
85+ if (path === '/') return developersPrefix;
86+ if (path.startsWith(developersPrefix + '/')) return path;
87+ return developersPrefix + path;
88+ };
89+ const normalizeDevelopersRoute = (route) => {
90+ if (route.path === '/developers') return { ...route, path: '/' };
91+ if (route.path.startsWith('/developers/')) {
92+ return { ...route, path: route.path.slice('/developers'.length) || '/' };
93+ }
94+ return route;
95+ };
96+ const getUnprefixedInternalLink = (event) => {
97+ if (!shouldUseDevelopersPrefix()) return;
98+ const link = event.target instanceof Element ? event.target.closest('a[href^="/"]') : null;
99+ if (!(link instanceof HTMLAnchorElement)) return;
100+ const href = link.getAttribute('href');
101+ if (!href || href.startsWith('//') || href.startsWith(developersPrefix + '/')) return;
102+ if (href.startsWith('/assets/') || href.startsWith('/fonts/') || href.startsWith('/RSC/')) return;
103+ return { link, href };
104+ };
105+ const originalFetch = window.fetch.bind(window);
106+ window.fetch = async (input, init) => {
107+ const response = await originalFetch(input, init);
108+ if (!shouldUseDevelopersPrefix()) return response;
109+ const requestUrl = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
110+ if (!requestUrl.includes('/RSC/')) return response;
111+ const clone = response.clone();
112+ const contentType = clone.headers.get('content-type') || '';
113+ if (!contentType.includes('text/plain')) return response;
114+ const text = await clone.text();
115+ const normalized = text
116+ .replaceAll('route:/developers/', 'route:/')
117+ .replaceAll('route:/developers"', 'route:/"')
118+ .replaceAll('route:/developers,', 'route:/,')
119+ .replaceAll('route:/developers\\n', 'route:/\\n');
120+ if (normalized === text) return response;
121+ return new Response(normalized, {
122+ status: response.status,
123+ statusText: response.statusText,
124+ headers: response.headers,
125+ });
126+ };
127+ document.addEventListener(
128+ 'click',
129+ (event) => {
130+ const target = getUnprefixedInternalLink(event);
131+ if (!target) return;
132+ event.preventDefault();
133+ event.stopImmediatePropagation();
134+ window.location.assign(publicDevelopersPath(target.href));
135+ },
136+ true,
137+ );
138+ for (const eventName of ['pointerover', 'mouseover']) {
139+ document.addEventListener(
140+ eventName,
141+ (event) => {
142+ if (!getUnprefixedInternalLink(event)) return;
143+ event.stopImmediatePropagation();
144+ },
145+ true,
146+ );
147+ }
148+
149+ const initialRoute = normalizeDevelopersRoute(unstable_parseRoute(new URL(window.location.href)));
150+ const rootElement = createElement(
151+ StrictMode,
152+ null,
153+ createElement(Router, {
154+ initialRoute,
155+ unstable_routeInterceptor: normalizeDevelopersRoute,
156+ }),
157+ );` ,
158+ )
159+ } ,
160+ }
161+ }
162+
67163function isMarketingPath ( pathname : string ) {
68164 const normalized = pathname . replace ( / \/ $ / , '' ) || '/'
69165 // Let requests for actual files (e.g. /blog/foo.svg) fall through to Vite's
0 commit comments