Skip to content

Commit 86889bf

Browse files
committed
feat: check rewrites against static routes
1 parent e46de5c commit 86889bf

File tree

4 files changed

+119
-9
lines changed

4 files changed

+119
-9
lines changed

plugin/src/helpers/files.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,4 +404,24 @@ export const movePublicFiles = async ({
404404
await copy(publicDir, `${publish}/`)
405405
}
406406
}
407+
408+
export const writeStaticRouteManifest = async ({
409+
appDir,
410+
outdir,
411+
publish,
412+
}: {
413+
appDir: string
414+
outdir?: string
415+
publish: string
416+
}): Promise<void> => {
417+
const publicDir = outdir ? join(appDir, outdir, 'public') : join(appDir, 'public')
418+
const fileList = existsSync(publicDir) ? await globby(['**/*'], { cwd: publicDir }) : []
419+
420+
const pagesManifest = await readJson(join(publish, 'server', 'pages-manifest.json'))
421+
422+
const staticRoutes = Object.keys(pagesManifest).filter((route) => !isDynamicRoute(route))
423+
424+
await writeJson(join(publish, 'public-manifest.json'), [...fileList.map((file) => `/${file}`), ...staticRoutes])
425+
}
426+
407427
/* eslint-enable max-lines */

plugin/src/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ import {
1616
generateCustomHeaders,
1717
} from './helpers/config'
1818
import { updateConfig, writeEdgeFunctions, loadMiddlewareManifest } from './helpers/edge'
19-
import { moveStaticPages, movePublicFiles, patchNextFiles, unpatchNextFiles } from './helpers/files'
19+
import {
20+
moveStaticPages,
21+
movePublicFiles,
22+
patchNextFiles,
23+
unpatchNextFiles,
24+
writeStaticRouteManifest,
25+
} from './helpers/files'
2026
import { generateFunctions, setupImageFunction, generatePagesResolver } from './helpers/functions'
2127
import { generateRedirects, generateStaticRedirects } from './helpers/redirects'
2228
import { shouldSkip, isNextAuthInstalled } from './helpers/utils'
@@ -112,6 +118,8 @@ const plugin: NetlifyPlugin = {
112118

113119
await movePublicFiles({ appDir, outdir, publish })
114120

121+
await writeStaticRouteManifest({ appDir, outdir, publish })
122+
115123
await patchNextFiles(basePath)
116124

117125
if (!process.env.SERVE_STATIC_FILES_FROM_ORIGIN) {

plugin/src/templates/edge/router.test.ts

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,25 @@ import {
1111
} from './router.ts'
1212
import manifest from './test-routes-manifest.json' assert { type: 'json' }
1313

14+
const staticRoutes = new Set([
15+
'/blog/data.json',
16+
'/static/hello.txt',
17+
'/_error',
18+
'/_app',
19+
'/auto-export/another',
20+
'/api/hello',
21+
'/hello-again',
22+
'/docs/v2/more/now-for-github',
23+
'/hello',
24+
'/nav',
25+
'/multi-rewrites',
26+
'/redirect-override',
27+
'/with-params',
28+
'/overridden',
29+
'/_document',
30+
'/404',
31+
])
32+
1433
Deno.test('rewrites paths', () => {
1534
const rule = {
1635
source: '/catchall-query/:path*',
@@ -360,15 +379,47 @@ Deno.test('redirects', async (t) => {
360379
assertEquals(result.status, 307)
361380
})
362381
})
382+
363383
Deno.test('rewrites', async (t) => {
364-
await t.step('simple rewrite', () => {
365-
const result = applyRewrites(new Request('http://localhost/to-another'), manifest.rewrites.afterFiles as Rewrite[])
384+
await t.step('afterFiles rewrite', () => {
385+
const result = applyRewrites({
386+
request: new Request('http://localhost/to-another'),
387+
rules: manifest.rewrites.afterFiles as Rewrite[],
388+
checkStaticRoutes: true,
389+
staticRoutes,
390+
})
366391
assert(result)
367392
assertEquals(result.url, 'http://localhost/another/one')
368393
})
369-
await t.step('with params', () => {
370-
const result = applyRewrites(new Request('http://localhost/test/hello'), manifest.rewrites.afterFiles as Rewrite[])
394+
await t.step('afterFiles with params', () => {
395+
const result = applyRewrites({
396+
request: new Request('http://localhost/test/hello'),
397+
rules: manifest.rewrites.afterFiles as Rewrite[],
398+
checkStaticRoutes: true,
399+
staticRoutes,
400+
})
371401
assert(result)
372402
assertEquals(result.url, 'http://localhost/hello')
373403
})
404+
405+
await t.step('afterFiles matching static file', () => {
406+
const result = applyRewrites({
407+
request: new Request('http://localhost/hello-world'),
408+
rules: manifest.rewrites.afterFiles as Rewrite[],
409+
checkStaticRoutes: true,
410+
staticRoutes,
411+
})
412+
assert(result)
413+
assertEquals(result.url, 'http://localhost/static/hello.txt')
414+
})
415+
416+
await t.step('non-matching', () => {
417+
const result = applyRewrites({
418+
request: new Request('http://localhost/no-match'),
419+
rules: manifest.rewrites.afterFiles as Rewrite[],
420+
checkStaticRoutes: true,
421+
staticRoutes,
422+
})
423+
assertFalse(result)
424+
})
374425
})

plugin/src/templates/edge/router.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,40 @@ export function applyRedirects(request: Request, rules: Redirect[]): Response |
208208
return false
209209
}
210210

211-
export function applyRewrites(request: Request, rules: Rewrite[]): Request | false {
211+
export function applyRewrites({
212+
request,
213+
rules,
214+
staticRoutes,
215+
checkStaticRoutes,
216+
}: {
217+
request: Request
218+
rules: Rewrite[]
219+
checkStaticRoutes: boolean
220+
staticRoutes?: Set<string>
221+
}): Request | false {
222+
let result: Request | false = false
223+
224+
if (checkStaticRoutes && !staticRoutes) {
225+
throw new Error('staticRoutes must be provided if checkStaticRoutes is true')
226+
}
227+
212228
// Apply all rewrite rules in order
213-
return rules.reduce((request, rule) => {
214-
return applyRewriteRule({ request, rule }) || request
215-
}, request)
229+
for (const rule of rules) {
230+
const rewritten = applyRewriteRule({ request, rule })
231+
if (rewritten) {
232+
result = rewritten
233+
// If a static route is matched, then we exit early
234+
if (checkStaticRoutes && staticRoutes!.has(new URL(result.url).pathname)) {
235+
return result
236+
}
237+
}
238+
}
239+
return result
240+
}
241+
242+
export function matchesStaticRoute(route: string, staticRoutes: Set<string>, staticFiles: Set<string>): boolean {
243+
if (staticFiles.has(route)) {
244+
return true
245+
}
246+
return staticRoutes.has(route.endsWith('/') ? route.slice(0, -1) : route)
216247
}

0 commit comments

Comments
 (0)