[dev] log middleware errors in vc dev (#8267)

Middleware server setup wasn't logging errors the same way that dev server setup was. This meant that middleware instantiation errors (like invalid config) would cause requests to 500, but no errors to be logged to the console.

This PR updates the invalid config error, makes sure errors in this area are logged out, and adds a test for this behavior.

**It may be appropriate to fail the deploy (and crash `vc dev`) in this case instead, though. What do others think?**

---

During `vc dev` with middleware that has an invalid `config.matcher` value...

Before: You see a 500 response in the browser and no output in the terminal.

After: You see a 500 response in the browser and this output in the terminal:

```
Error! Middleware's `config.matcher` values must start with "/". Received: not-a-valid-matcher
```

---

Related Issue: https://github.com/vercel/edge-functions/issues/220
This commit is contained in:
Sean Massa
2022-08-02 15:01:42 -05:00
committed by GitHub
parent 08a83a94f8
commit be5308b137
6 changed files with 49 additions and 6 deletions

View File

@@ -1556,6 +1556,8 @@ export default class DevServer {
(err as any).link = 'https://vercel.link/command-not-found'; (err as any).link = 'https://vercel.link/command-not-found';
} }
this.output.prettyError(err);
await this.sendError( await this.sendError(
req, req,
res, res,

View File

@@ -0,0 +1,7 @@
export const config = {
matcher: 'not-a-valid-matcher',
};
export default function middleware(request, _event) {
return new Response(null);
}

View File

@@ -227,7 +227,7 @@ test('[vercel dev] should handle syntax errors thrown in edge functions', async
expect(await res.text()).toMatch( expect(await res.text()).toMatch(
/<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g /<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g
); );
expect(stderr).toMatch(/Failed to instantiate edge runtime./g); expect(stderr).toMatch(/Failed to compile user code for edge runtime./g);
expect(stderr).toMatch(/Unexpected end of file/g); expect(stderr).toMatch(/Unexpected end of file/g);
expect(stderr).toMatch( expect(stderr).toMatch(
/Failed to complete request to \/api\/edge-error-syntax: Error: socket hang up/g /Failed to complete request to \/api\/edge-error-syntax: Error: socket hang up/g
@@ -307,6 +307,35 @@ test('[vercel dev] should handle missing handler errors thrown in edge functions
} }
}); });
test('[vercel dev] should handle invalid middleware config', async () => {
const dir = fixture('middleware-matchers-invalid');
const { dev, port, readyResolver } = await testFixture(dir);
try {
await readyResolver;
let res = await fetch(`http://localhost:${port}/api/whatever`, {
method: 'GET',
headers: {
Accept:
'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
},
});
validateResponseHeaders(res);
const { stderr } = await dev.kill('SIGTERM');
expect(await res.text()).toMatch(
/<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g
);
expect(stderr).toMatch(
/Middleware's `config.matcher` .+ Received: not-a-valid-matcher/g
);
} finally {
await dev.kill('SIGTERM');
}
});
test('[vercel dev] should support request body', async () => { test('[vercel dev] should support request body', async () => {
const dir = fixture('node-request-body'); const dir = fixture('node-request-body');
const { dev, port, readyResolver } = await testFixture(dir); const { dev, port, readyResolver } = await testFixture(dir);

View File

@@ -348,7 +348,10 @@ function testFixtureStdio(
: []), : []),
'deploy', 'deploy',
...(process.env.VERCEL_CLI_VERSION ...(process.env.VERCEL_CLI_VERSION
? ['--build-env', `VERCEL_CLI_VERSION=${process.env.VERCEL_CLI_VERSION}`] ? [
'--build-env',
`VERCEL_CLI_VERSION=${process.env.VERCEL_CLI_VERSION}`,
]
: []), : []),
'--public', '--public',
'--debug', '--debug',
@@ -430,7 +433,7 @@ function testFixtureStdio(
); );
} }
if (stderr.includes('Command failed') || stderr.includes('Error!')) { if (stderr.includes('Command failed')) {
dev.kill('SIGTERM'); dev.kill('SIGTERM');
throw new Error(`Failed for "${directory}" with stderr "${stderr}".`); throw new Error(`Failed for "${directory}" with stderr "${stderr}".`);
} }

View File

@@ -224,7 +224,7 @@ async function compileUserCode(
} catch (error) { } catch (error) {
// We can't easily show a meaningful stack trace from ncc -> edge-runtime. // We can't easily show a meaningful stack trace from ncc -> edge-runtime.
// So, stick with just the message for now. // So, stick with just the message for now.
console.error(`Failed to instantiate edge runtime.`); console.error(`Failed to compile user code for edge runtime.`);
logError(error); logError(error);
return undefined; return undefined;
} }

View File

@@ -14,12 +14,14 @@ export function getRegExpFromMatchers(matcherOrMatchers: unknown): string {
function getRegExpFromMatcher(matcher: unknown): string { function getRegExpFromMatcher(matcher: unknown): string {
if (typeof matcher !== 'string') { if (typeof matcher !== 'string') {
throw new Error( throw new Error(
'`matcher` must be a path matcher or an array of path matchers' "Middleware's `config.matcher` must be a path matcher (string) or an array of path matchers (string[])"
); );
} }
if (!matcher.startsWith('/')) { if (!matcher.startsWith('/')) {
throw new Error('`matcher`: path matcher must start with /'); throw new Error(
`Middleware's \`config.matcher\` values must start with "/". Received: ${matcher}`
);
} }
const re = pathToRegexp(matcher); const re = pathToRegexp(matcher);