Skip to content

Commit 616fe8d

Browse files
committed
refactor: consolidate React globals and improve fetch cache handling
- Add React global initialization with createElement, Fragment, and Suspense in component eval setup - Extract tag validation logic into reusable extractValidTags function for fetch operations - Include normalized tags in fetch cache key generation for proper cache differentiation - Update RSC renderer extension to depend on init_react for proper React setup - Improve module reload error handling with better match patterns for task results - Remove unused file_path parameter from reload_module_internal function - Enhance fetch cache merging to combine tags from multiple requests and maintain sorted order - Update deploy utils to support configurable start scripts with backup preservation - Improve navigation utils and props extractor with better type handling and edge cases
1 parent 7ffde77 commit 616fe8d

19 files changed

Lines changed: 168 additions & 63 deletions

File tree

crates/rari/src/rsc/rendering/core/js/component_eval_setup.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
11
/* eslint-disable no-use-before-define, no-var, vars-on-top */
2+
if (!globalThis.React) {
3+
globalThis.React = {
4+
createElement(type, props, ...children) {
5+
const element = {
6+
$typeof: Symbol.for('react.transitional.element'),
7+
type,
8+
props: props || {},
9+
key: props?.key || null,
10+
}
11+
if (children.length > 0)
12+
element.props = { ...element.props, children: children.length === 1 ? children[0] : children }
13+
14+
return element
15+
},
16+
Fragment: Symbol.for('react.fragment'),
17+
Suspense: Symbol.for('react.suspense'),
18+
}
19+
}
20+
221
if (typeof _jsx === 'undefined')
322
var _jsx = globalThis['~react']?.jsxRuntime?.jsx || (() => null)
423
if (typeof _jsxs === 'undefined')

crates/rari/src/runtime/ext/rsc_renderer/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use deno_core::{Extension, extension};
33

44
extension!(
55
init_rsc_renderer,
6-
deps = [rari, init_rsc_modules],
6+
deps = [rari, init_react, init_rsc_modules],
77
esm_entry_point = "ext:init_rsc_renderer/init_rsc_renderer.js",
88
esm = [
99
dir "src/runtime/ext/rsc_renderer",

crates/rari/src/runtime/ext/web/init_fetch.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ function resolveRequestMeta(input, init = {}) {
2424
}
2525
}
2626

27+
function extractValidTags(init) {
28+
const tags = init?.rari?.tags ?? init?.next?.tags
29+
if (tags && Array.isArray(tags)) {
30+
return tags
31+
.filter(tag => typeof tag === 'string' && tag.trim().length > 0)
32+
.map(tag => tag.trim())
33+
}
34+
35+
return []
36+
}
37+
2738
function generateCacheKey(input, init) {
2839
const { url, method, headers } = resolveRequestMeta(input, init)
2940

@@ -74,7 +85,14 @@ function generateCacheKey(input, init) {
7485
}
7586
}
7687

77-
return `${method}:${url}:${headersStr}:${bodyStr}`
88+
const validTags = extractValidTags(init)
89+
let tagsStr = ''
90+
if (validTags.length > 0) {
91+
const normalizedTags = validTags.toSorted((a, b) => a.localeCompare(b))
92+
tagsStr = `:tags:${JSON.stringify(normalizedTags)}`
93+
}
94+
95+
return `${method}:${url}:${headersStr}:${bodyStr}${tagsStr}`
7896
}
7997

8098
function shouldCache(input, init, meta) {
@@ -115,7 +133,7 @@ async function fetchWithRustCache(input, init, meta) {
115133

116134
const tags = init?.rari?.tags ?? init?.next?.tags
117135
if (tags && Array.isArray(tags)) {
118-
const validTags = tags.filter(tag => typeof tag === 'string' && tag.trim().length > 0)
136+
const validTags = extractValidTags(init)
119137
if (validTags.length > 0) {
120138
options.tags = JSON.stringify(validTags)
121139
}

crates/rari/src/runtime/module_reload/manager.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ impl ModuleReloadManager {
133133
});
134134

135135
let request = ModuleReloadRequest::new(component_id.clone(), file_path.clone());
136+
137+
self.debounce_manager.cancel_pending(&component_id).await;
138+
136139
self.debounce_manager.add_pending(component_id.clone(), request, handle).await;
137140

138141
Ok(())
@@ -175,7 +178,7 @@ impl ModuleReloadManager {
175178
loop {
176179
attempts += 1;
177180

178-
match self.reload_module_internal(component_id, file_path).await {
181+
match self.reload_module_internal(component_id).await {
179182
Ok(_) => return Ok(()),
180183
Err(e) if attempts >= max_attempts => {
181184
return Err(RariError::module_reload(ModuleReloadError::MaxRetriesExceeded {
@@ -192,11 +195,7 @@ impl ModuleReloadManager {
192195
}
193196
}
194197

195-
async fn reload_module_internal(
196-
&self,
197-
component_id: &str,
198-
_file_path: &Path,
199-
) -> Result<(), RariError> {
198+
async fn reload_module_internal(&self, component_id: &str) -> Result<(), RariError> {
200199
self.runtime.as_ref().ok_or_else(|| {
201200
RariError::module_reload(ModuleReloadError::RuntimeNotAvailable {
202201
message: "Runtime not available".to_string(),
@@ -235,8 +234,14 @@ impl ModuleReloadManager {
235234
}
236235

237236
for handle in handles {
238-
if let Err(e) = handle.await {
239-
error!(error = %e, "Batch reload task failed");
237+
match handle.await {
238+
Ok(Ok(())) => {}
239+
Ok(Err(e)) => {
240+
error!(error = %e, "Module reload failed in batch");
241+
}
242+
Err(e) => {
243+
error!(error = %e, "Batch reload task failed");
244+
}
240245
}
241246
}
242247
}

crates/rari/src/server/middleware/request_context.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,20 @@ impl RequestContext {
151151
let mut guard = fetch_lock.lock().await;
152152

153153
if let Some(result) = guard.as_ref() {
154-
return result.clone();
154+
let mut cloned_result = result.clone()?;
155+
156+
let mut tag_set: FxHashSet<String> = cloned_result.tags.iter().cloned().collect();
157+
tag_set.extend(tags.iter().cloned());
158+
let mut merged_tags: Vec<String> = tag_set.into_iter().collect();
159+
merged_tags.sort();
160+
cloned_result.tags = merged_tags;
161+
162+
{
163+
let mut cache = self.fetch_cache.lock();
164+
cache.put(cache_key.clone(), cloned_result.clone());
165+
}
166+
167+
return Ok(cloned_result);
155168
}
156169

157170
struct CleanupGuard<'a> {

packages/deploy/src/utils.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,14 @@ export function updatePackageJsonForProvider(cwd: string, config: ProviderConfig
186186

187187
packageJson.scripts = packageJson.scripts || {}
188188

189-
if (packageJson.scripts.start && packageJson.scripts.start !== 'rari start') {
189+
const newStart = config.startScript || 'rari start'
190+
if (packageJson.scripts.start && packageJson.scripts.start !== newStart && !packageJson.scripts['start:original']) {
190191
logWarn(`Existing start script found: "${packageJson.scripts.start}"`)
191-
logWarn('Backing up to start:original and replacing with "rari start"')
192+
logWarn(`Backing up to start:original and replacing with "${newStart}"`)
192193
packageJson.scripts['start:original'] = packageJson.scripts.start
193194
}
194195

195-
packageJson.scripts.start = config.startScript || 'rari start'
196+
packageJson.scripts.start = newStart
196197
packageJson.scripts['start:local'] = 'rari start'
197198
packageJson.scripts[`deploy:${config.providerName.toLowerCase()}`] = config.deployScript
198199

packages/rari/src/image/Image.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ function buildImageUrl(
5151

5252
function extractImageProps(src: string | StaticImageData, width?: number, height?: number, blurDataURL?: string) {
5353
const imgSrc = typeof src === 'string' ? src : src.src
54-
const imgWidth = width || (typeof src !== 'string' ? src.width : undefined)
55-
const imgHeight = height || (typeof src !== 'string' ? src.height : undefined)
56-
const imgBlurDataURL = blurDataURL || (typeof src !== 'string' ? src.blurDataURL : undefined)
54+
const imgWidth = width ?? (typeof src !== 'string' ? src.width : undefined)
55+
const imgHeight = height ?? (typeof src !== 'string' ? src.height : undefined)
56+
const imgBlurDataURL = blurDataURL ?? (typeof src !== 'string' ? src.blurDataURL : undefined)
5757

5858
return { imgSrc, imgWidth, imgHeight, imgBlurDataURL }
5959
}
@@ -101,14 +101,19 @@ function useImageLazyLoad(
101101
loading: 'lazy' | 'eager',
102102
) {
103103
const shouldLoadImmediately = shouldPreload || unoptimized || loading === 'eager'
104-
const [isVisible, setIsVisible] = useState(shouldLoadImmediately)
105104
const prevShouldLoadImmediatelyRef = useRef(shouldLoadImmediately)
106105

107-
if (shouldLoadImmediately && !prevShouldLoadImmediatelyRef.current) {
108-
prevShouldLoadImmediatelyRef.current = true
109-
setIsVisible(true)
106+
const getInitialVisibility = () => {
107+
if (shouldLoadImmediately && !prevShouldLoadImmediatelyRef.current) {
108+
prevShouldLoadImmediatelyRef.current = true
109+
return true
110+
}
111+
112+
return shouldLoadImmediately
110113
}
111114

115+
const [isVisible, setIsVisible] = useState(getInitialVisibility)
116+
112117
useEffect(() => {
113118
prevShouldLoadImmediatelyRef.current = shouldLoadImmediately
114119
}, [shouldLoadImmediately])
@@ -289,7 +294,7 @@ function OptimizedImage({
289294
const mainSrc = loader
290295
? loader({ src: finalSrc, width: defaultWidth, quality })
291296
: buildImageUrl(finalSrc, defaultWidth, quality)
292-
const shouldUseSrcSet = sizesArray.length > 1 || sizesArray[0] !== defaultWidth
297+
const shouldUseSrcSet = !imgWidth || sizesArray.length > 1 || sizesArray[0] !== defaultWidth
293298

294299
const imgElement = (
295300
<img

packages/rari/src/proxy/execute-proxy.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { RariRequest } from './RariRequest'
44
import { processProxyResult } from './shared/utils'
55

66
export async function executeProxy(simpleRequest: SimpleRequest): Promise<SimpleProxyResult> {
7+
const waitUntilPromises: Promise<unknown>[] = []
8+
79
try {
810
const executor = getProxyExecutor()
911

@@ -19,7 +21,6 @@ export async function executeProxy(simpleRequest: SimpleRequest): Promise<Simple
1921
headers: new Headers(simpleRequest.headers),
2022
})
2123

22-
const waitUntilPromises: Promise<unknown>[] = []
2324
const event = {
2425
waitUntil: (promise: Promise<unknown>) => {
2526
waitUntilPromises.push(promise)
@@ -32,6 +33,13 @@ export async function executeProxy(simpleRequest: SimpleRequest): Promise<Simple
3233

3334
const result = await proxyFn(rariRequest, event)
3435

36+
return await processProxyResult(result)
37+
}
38+
catch (error) {
39+
console.error('[rari] Proxy: executeProxy failed:', error)
40+
return { continue: true }
41+
}
42+
finally {
3543
if (waitUntilPromises.length > 0) {
3644
void Promise.allSettled(waitUntilPromises).then((results) => {
3745
results.forEach((result, index) => {
@@ -41,11 +49,5 @@ export async function executeProxy(simpleRequest: SimpleRequest): Promise<Simple
4149
})
4250
})
4351
}
44-
45-
return await processProxyResult(result)
46-
}
47-
catch (error) {
48-
console.error('[rari] Proxy: executeProxy failed:', error)
49-
return { continue: true }
5052
}
5153
}

packages/rari/src/proxy/matcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ function checkHostCondition(
7171
request: RariRequest,
7272
key: string,
7373
): string | null {
74-
return request.rariUrl.hostname === key ? key : null
74+
return request.rariUrl.hostname === key ? request.rariUrl.hostname : null
7575
}
7676

7777
function getConditionActualValue(

packages/rari/src/proxy/shared/utils.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export interface SimpleProxyResult {
1515
responseHeaders?: Record<string, string>
1616
response?: {
1717
status: number
18-
headers: Record<string, string>
18+
headers: Record<string, string | string[]>
1919
body?: string
2020
}
2121
}
@@ -47,7 +47,7 @@ export function checkForRewrite(result: ResponseLike | null): SimpleProxyResult
4747
}
4848

4949
export function checkForRedirect(result: ResponseLike | null): SimpleProxyResult | null {
50-
if (!result || !result.status)
50+
if (!result || result.status == null)
5151
return null
5252

5353
const location = result.headers?.get?.('location')
@@ -97,11 +97,18 @@ export function handleContinueWithHeaders(result: ResponseLike): SimpleProxyResu
9797
}
9898

9999
export async function handleDirectResponse(result: ResponseLike): Promise<SimpleProxyResult> {
100-
const headers: Record<string, string> = {}
100+
const headers: Record<string, string | string[]> = {}
101101

102102
if (result.headers?.forEach) {
103103
result.headers.forEach((value: string, key: string) => {
104-
headers[key] = value
104+
const lowerKey = key.toLowerCase()
105+
if (headers[lowerKey]) {
106+
const existing = headers[lowerKey]
107+
headers[lowerKey] = Array.isArray(existing) ? [...existing, value] : [existing, value]
108+
}
109+
else {
110+
headers[lowerKey] = value
111+
}
105112
})
106113
}
107114

@@ -150,7 +157,7 @@ export async function processProxyResult(result: ResponseLike | null): Promise<S
150157
if (continueHeader === 'true')
151158
return handleContinueWithHeaders(result)
152159

153-
if (result.status)
160+
if (result.status != null)
154161
return await handleDirectResponse(result)
155162

156163
return { continue: true }

0 commit comments

Comments
 (0)