@@ -251,6 +251,10 @@ const WORKER_RUNTIME_JS = `
251251 return headers;
252252 }
253253
254+ function useIndexJson(env) {
255+ return (env.STATIK_USE_INDEX_JSON || 'true').toLowerCase() === 'true';
256+ }
257+
254258 // -------------------------
255259 // Routing helpers
256260 // -------------------------
@@ -358,49 +362,86 @@ const WORKER_RUNTIME_JS = `
358362 // -------------------------
359363
360364 // From concrete route ('/', '/posts', '/users/1') to canonical R2 key.
361- function r2KeyForRoute(concreteRoute) {
362- if (concreteRoute === '/') return 'index.json';
365+ function r2KeyForRoute(concreteRoute, env) {
366+ const useIndex = useIndexJson(env);
367+
368+ if (concreteRoute === '/') {
369+ // root
370+ return useIndex ? 'index.json' : 'index';
371+ }
372+
363373 const clean = concreteRoute.replace(/^\\/+/, '');
364- return clean + '/index.json';
374+
375+ if (useIndex) {
376+ // old behavior: posts -> posts/index.json
377+ return clean + '/index.json';
378+ }
379+
380+ // flat mode: posts -> posts
381+ return clean;
365382 }
366383
367384 // From request path ('/', '/posts', '/posts/index.json') to R2 key,
368- // obeying STATIK_PRETTY_ROUTES.
385+ // obeying STATIK_PRETTY_ROUTES and STATIK_USE_INDEX_JSON .
369386 function r2KeyFromRequestPath(pathname, env) {
370387 const pretty = isPrettyRoutes(env);
388+ const useIndex = useIndexJson(env);
371389
390+ // Root handling
372391 if (!pathname || pathname === '/') {
373- return 'index.json';
392+ return useIndex ? 'index.json' : 'index ';
374393 }
375394
376395 let p = pathname;
377396 if (p.startsWith('/')) p = p.slice(1);
378397
379- // If user explicitly asks for .json, always respect it
398+ // If user explicitly asks for .json, always respect it,
399+ // but in flat mode we map \`/foo.json\` -> \`foo\`.
380400 if (p.endsWith('.json')) {
381- return p;
401+ if (useIndex) {
402+ // index.json mode: the key really is '...json'
403+ return p;
404+ }
405+ // flat mode: drop '.json' and read from bare key
406+ return p.slice(0, -5);
382407 }
383408
384- // Pretty mode : '/posts' -> 'posts/index.json'
409+ // Pretty routes : '/posts' is valid
385410 if (pretty) {
386- return p + '/index.json';
411+ return useIndex ? p + '/index.json' : p ;
387412 }
388413
389- // Strict index mode: only '/foo/index.json' works
390- // '/foo' without .json is not valid
391- return null;
414+ // Non-pretty mode:
415+ // - index.json mode: require explicit '/foo/index.json' (handled above)
416+ // - flat mode: treat '/foo' as key 'foo'
417+ return useIndex ? null : p;
392418 }
393419
394420 // All public paths that should map to a given route
395421 // (for cache purge, etc.)
396422 function publicPathsForRoute(route, env) {
397423 const pretty = isPrettyRoutes(env);
424+ const useIndex = useIndexJson(env);
425+
398426 if (route === '/') {
399- return pretty ? ['/', '/index.json'] : ['/index.json'];
427+ if (useIndex) {
428+ // root index.json plus pretty '/'
429+ return pretty ? ['/', '/index.json'] : ['/index.json'];
430+ }
431+ // flat root: underlying key 'index', but we still may serve '/', '/index', '/index.json'
432+ return ['/', '/index', '/index.json'];
400433 }
434+
401435 const base = route; // e.g. '/posts', '/users/1'
402- const jsonPath = base + '/index.json';
403- return pretty ? [base, jsonPath] : [jsonPath];
436+
437+ if (useIndex) {
438+ const jsonPath = base + '/index.json';
439+ return pretty ? [base, jsonPath] : [jsonPath];
440+ }
441+
442+ // flat keys: underlying key is 'posts' or 'users/1'
443+ // we purge both pretty path and .json alias
444+ return [base, base + '.json'];
404445 }
405446
406447 async function writeJsonToR2(env, key, text, extraMeta = {}) {
@@ -536,7 +577,7 @@ const WORKER_RUNTIME_JS = `
536577 assertSerializable(value);
537578
538579 const text = pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value);
539- const key = r2KeyForRoute(route);
580+ const key = r2KeyForRoute(route, env );
540581 const etag = await digestETag(text);
541582
542583 await writeJsonToR2(env, key, text, { route, etag });
@@ -602,7 +643,7 @@ const WORKER_RUNTIME_JS = `
602643 const value = await r.mod.data(ctx);
603644 assertSerializable(value);
604645 const text = pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value);
605- const key = r2KeyForRoute(r.concreteRoute);
646+ const key = r2KeyForRoute(r.concreteRoute, env );
606647 const etag = await digestETag(text);
607648 await writeJsonToR2(env, key, text, { route: r.concreteRoute, etag });
608649
0 commit comments