Skip to content

Commit 820e495

Browse files
committed
Keep redirect lookups from crashing SSR pages
Dynamic Astro routes were reading Netlify redirect config through a cwd-relative path, which is fragile inside a serverless runtime and was taking detail pages down with 500s before render. Resolve netlify.toml by searching from the module directory and current working directory, and fail open in request-time redirect lookup so a config read problem does not block page rendering. Constraint: Netlify serverless cwd is not guaranteed to be the repo root Rejected: Inline redirects into route modules | would duplicate platform config and drift from source of truth Rejected: Leave redirect lookup hard-failing | one config read failure should not take down unrelated pages Confidence: medium Scope-risk: narrow Reversibility: clean Directive: Keep redirect config lookup independent of process cwd anywhere server code reads deploy config files Tested: vitest ./test/prebuild/config-node.test.js; pnpm run netlify-build Not-tested: live Netlify production deploy before push
1 parent d026a54 commit 820e495

3 files changed

Lines changed: 85 additions & 3 deletions

File tree

helpers/astro/request.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,13 @@ export async function applyResponseDefaults ( Astro ) {
5656
export async function catchRedirectResponse ( Astro ) {
5757
const requestUrl = new URL( Astro.request.url )
5858

59-
const netlifyRedirectUrl = await getNetlifyRedirect( requestUrl.pathname )
59+
let netlifyRedirectUrl = null
60+
61+
try {
62+
netlifyRedirectUrl = await getNetlifyRedirect( requestUrl.pathname )
63+
} catch ( error ) {
64+
console.warn( `Skipping redirect lookup for ${ requestUrl.pathname }`, error )
65+
}
6066

6167
// console.log('netlifyRedirectUrl', netlifyRedirectUrl)
6268

@@ -67,4 +73,3 @@ export async function catchRedirectResponse ( Astro ) {
6773
return null
6874
}
6975

70-

helpers/config-node.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import TOML from '@iarna/toml'
22
import fs from 'fs-extra'
3+
import path from 'path'
4+
import { fileURLToPath } from 'url'
35

46
import pkg from '~/package.json'
57
import { publicRuntimeConfig } from '~/helpers/public-runtime-config.mjs'
@@ -8,6 +10,7 @@ import { getRouteType } from '~/helpers/app-derived.js'
810

911

1012
export const siteUrl = getSiteUrl()
13+
const currentModuleDirectory = path.dirname( fileURLToPath( import.meta.url ) )
1114

1215
export const nuxtHead = {
1316
// this htmlAttrs you need
@@ -113,8 +116,41 @@ export const nuxtHead = {
113116

114117

115118

119+
export async function getNetlifyConfigPath () {
120+
const searchDirectories = new Set()
121+
122+
// Local dev usually runs from repo root, but deployed serverless
123+
// functions may execute from a nested working directory.
124+
for ( const baseDirectory of [
125+
process.cwd(),
126+
currentModuleDirectory,
127+
] ) {
128+
let directory = baseDirectory
129+
130+
while ( true ) {
131+
searchDirectories.add( directory )
132+
133+
const parentDirectory = path.dirname( directory )
134+
135+
if ( parentDirectory === directory ) break
136+
137+
directory = parentDirectory
138+
}
139+
}
140+
141+
for ( const directory of searchDirectories ) {
142+
const configPath = path.join( directory, 'netlify.toml' )
143+
144+
if ( await fs.pathExists( configPath ) ) {
145+
return configPath
146+
}
147+
}
148+
149+
throw new Error( 'Could not find netlify.toml' )
150+
}
151+
116152
export async function getNetlifyConfig () {
117-
const configPath = './netlify.toml'
153+
const configPath = await getNetlifyConfigPath()
118154
const tomlContent = await fs.readFile(configPath, 'utf-8')
119155
const netlifyConfig = TOML.parse(tomlContent)
120156

test/prebuild/config-node.test.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import fs from 'fs-extra'
2+
import os from 'os'
3+
import path from 'path'
4+
import { afterEach, describe, expect, it } from 'vitest'
5+
6+
import {
7+
getNetlifyConfigPath,
8+
getNetlifyRedirect
9+
} from '~/helpers/config-node.js'
10+
11+
const originalCwd = process.cwd()
12+
13+
afterEach(() => {
14+
process.chdir( originalCwd )
15+
})
16+
17+
describe( 'netlify config helpers', () => {
18+
it( 'resolves netlify.toml even when cwd is outside the repo root', async () => {
19+
const tempDirectory = await fs.mkdtemp( path.join( os.tmpdir(), 'doesitarm-netlify-' ) )
20+
21+
process.chdir( tempDirectory )
22+
23+
const configPath = await getNetlifyConfigPath()
24+
25+
expect( configPath ).toBe( path.join( originalCwd, 'netlify.toml' ) )
26+
})
27+
28+
it( 'loads redirects when cwd is outside the repo root', async () => {
29+
const tempDirectory = await fs.mkdtemp( path.join( os.tmpdir(), 'doesitarm-netlify-' ) )
30+
31+
process.chdir( tempDirectory )
32+
33+
const redirect = await getNetlifyRedirect( '/app/electron' )
34+
35+
expect( redirect ).toMatchObject({
36+
from: '/app/electron',
37+
to: '/app/electron-framework',
38+
status: 301
39+
})
40+
})
41+
})

0 commit comments

Comments
 (0)