Skip to content

Commit

Permalink
fix(perf): exclude /_next/static/ dir from function
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
serhalp committed Jan 29, 2025
1 parent 8e852ca commit f1e2011
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/build/templates/handler-monorepo.tmpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,9 @@ export default async function (req, context) {
export const config = {
path: '/*',
preferStatic: true,
excludedPath: [
// We use `preferStatic: true` so we already won't run this on *existing* static assets,
// but by excluding this entire path we also avoid invoking the function just to 404.
'/_next/static/*',
],
}
5 changes: 5 additions & 0 deletions src/build/templates/handler.tmpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,9 @@ export default async function handler(req, context) {
export const config = {
path: '/*',
preferStatic: true,
excludedPath: [
// We use `preferStatic: true` so we already won't run this on *existing* static assets,
// but by excluding this entire path we also avoid invoking the function just to 404.
'/_next/static/*',
],
}
26 changes: 26 additions & 0 deletions tests/e2e/simple-app.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { expect, type Locator } from '@playwright/test'
import { nextVersionSatisfies } from '../utils/next-version-helpers.mjs'
import { test } from '../utils/playwright-helpers.js'
import { join } from 'node:path'
import { readdir } from 'node:fs/promises'

const expectImageWasLoaded = async (locator: Locator) => {
expect(await locator.evaluate((img: HTMLImageElement) => img.naturalHeight)).toBeGreaterThan(0)
Expand Down Expand Up @@ -282,3 +284,27 @@ test('can require CJS module that is not bundled', async ({ simple }) => {
expect(parsedBody.notBundledCJSModule.isBundled).toEqual(false)
expect(parsedBody.bundledCJSModule.isBundled).toEqual(true)
})

test('serves a 200 for an existing static asset without invoking a function', async ({
page,
simple,
}) => {
// Since assets are hashed, we can't hardcode anything here. Find something to fetch.
const [staticAsset] = await readdir(join(simple.isolatedFixtureRoot, '.next', 'static', 'chunks'))
expect(staticAsset).toBeDefined()

const response = await page.goto(`${simple.url}/_next/static/chunks/${staticAsset}`)

expect(response?.status()).toBe(200)
expect(response?.headers()).not.toHaveProperty('x-nf-function-type')
})

test('serves a 404 for a nonexistent static asset without invoking a function', async ({
page,
simple,
}) => {
const response = await page.goto(`${simple.url}/_next/static/stale123abcdef.js`)

expect(response?.status()).toBe(404)
expect(response?.headers()).not.toHaveProperty('x-nf-function-type')
})
2 changes: 2 additions & 0 deletions tests/utils/create-e2e-fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface DeployResult {
deployID: string
url: string
logs: string
isolatedFixtureRoot: string
}

type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'berry'
Expand Down Expand Up @@ -91,6 +92,7 @@ export const createE2EFixture = async (fixture: string, config: E2EConfig = {})
cleanup: _cleanup,
deployID: result.deployID,
url: result.url,
isolatedFixtureRoot,
}
} catch (error) {
await _cleanup(true)
Expand Down

0 comments on commit f1e2011

Please sign in to comment.