Skip to content

Commit f1e2011

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 8e852ca commit f1e2011

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
@@ -20,6 +20,7 @@ export interface DeployResult {
2020
deployID: string
2121
url: string
2222
logs: string
23+
isolatedFixtureRoot: string
2324
}
2425

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

0 commit comments

Comments
 (0)