Skip to content

Commit a26a681

Browse files
committed
fix(perf): exclude /_next/static/ dir from function
See inline comment. This only matters because of browsers requesting assets from previous deploys or bots crawling for garbage. This will avoid unnecessary function invocations in those cases, improving performance and reducing customer bills in some cases.
1 parent 4bcb34f commit a26a681

File tree

4 files changed

+38
-0
lines changed

4 files changed

+38
-0
lines changed

Diff for: src/build/templates/handler-monorepo.tmpl.js

+5
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,9 @@ export default async function (req, context) {
5353
export const config = {
5454
path: '/*',
5555
preferStatic: true,
56+
excludedPath: [
57+
// We use `preferStatic: true` so we already won't run this on *existing* static assets,
58+
// but by excluding this entire path we also avoid invoking the function just to 404.
59+
'/_next/static/*',
60+
],
5661
}

Diff for: src/build/templates/handler.tmpl.js

+5
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,9 @@ export default async function handler(req, context) {
4646
export const config = {
4747
path: '/*',
4848
preferStatic: true,
49+
excludedPath: [
50+
// We use `preferStatic: true` so we already won't run this on *existing* static assets,
51+
// but by excluding this entire path we also avoid invoking the function just to 404.
52+
'/_next/static/*',
53+
],
4954
}

Diff for: tests/e2e/simple-app.test.ts

+26
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { expect, type Locator } from '@playwright/test'
22
import { nextVersionSatisfies } from '../utils/next-version-helpers.mjs'
33
import { test } from '../utils/playwright-helpers.js'
4+
import { join } from 'node:path'
5+
import { readdir } from 'node:fs/promises'
46

57
const expectImageWasLoaded = async (locator: Locator) => {
68
expect(await locator.evaluate((img: HTMLImageElement) => img.naturalHeight)).toBeGreaterThan(0)
@@ -282,3 +284,27 @@ test('can require CJS module that is not bundled', async ({ simple }) => {
282284
expect(parsedBody.notBundledCJSModule.isBundled).toEqual(false)
283285
expect(parsedBody.bundledCJSModule.isBundled).toEqual(true)
284286
})
287+
288+
test('serves a 200 for an existing static asset without invoking a function', async ({
289+
page,
290+
simple,
291+
}) => {
292+
// Since assets are hashed, we can't hardcode anything here. Find something to fetch.
293+
const [staticAsset] = await readdir(join(simple.isolatedFixtureRoot, '.next', 'static', 'chunks'))
294+
expect(staticAsset).toBeDefined()
295+
296+
const response = await page.goto(`${simple.url}/_next/static/chunks/${staticAsset}`)
297+
298+
expect(response?.status()).toBe(200)
299+
expect(response?.headers()).not.toHaveProperty('x-nf-function-type')
300+
})
301+
302+
test('serves a 404 for a nonexistent static asset without invoking a function', async ({
303+
page,
304+
simple,
305+
}) => {
306+
const response = await page.goto(`${simple.url}/_next/static/stale123abcdef.js`)
307+
308+
expect(response?.status()).toBe(404)
309+
expect(response?.headers()).not.toHaveProperty('x-nf-function-type')
310+
})

Diff for: tests/utils/create-e2e-fixture.ts

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface DeployResult {
2121
deployID: string
2222
url: string
2323
logs: string
24+
isolatedFixtureRoot: string
2425
}
2526

2627
type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'berry'
@@ -92,6 +93,7 @@ export const createE2EFixture = async (fixture: string, config: E2EConfig = {})
9293
cleanup: _cleanup,
9394
deployID: result.deployID,
9495
url: result.url,
96+
isolatedFixtureRoot,
9597
}
9698
} catch (error) {
9799
await _cleanup(true)

0 commit comments

Comments
 (0)