mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-10 04:22:12 +00:00
[cli] Support root-level Middleware file in vc dev (#7973)
Adds initial support for a root-level `middleware.js` / `middleware.ts` file in the `vercel dev` CLI command. This leverages the existing Edge Function invoking logic in `@vercel/node`'s `startDevServer()` function and applies the necessary response / rewrites / mutations to the HTTP request based on the result of the middleware invocation.
This commit is contained in:
@@ -425,10 +425,6 @@ export async function getBuildMatches(
|
|||||||
src = extensionless;
|
src = extensionless;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to escape brackets since `glob` will
|
|
||||||
// try to find a group otherwise
|
|
||||||
src = src.replace(/(\[|\])/g, '[$1]');
|
|
||||||
|
|
||||||
const files = fileList
|
const files = fileList
|
||||||
.filter(name => name === src || minimatch(name, src, { dot: true }))
|
.filter(name => name === src || minimatch(name, src, { dot: true }))
|
||||||
.map(name => join(cwd, name));
|
.map(name => join(cwd, name));
|
||||||
|
|||||||
18
packages/cli/src/util/dev/headers.ts
Normal file
18
packages/cli/src/util/dev/headers.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Headers } from 'node-fetch';
|
||||||
|
import { IncomingHttpHeaders, OutgoingHttpHeaders } from 'http';
|
||||||
|
|
||||||
|
export function nodeHeadersToFetchHeaders(
|
||||||
|
nodeHeaders: IncomingHttpHeaders | OutgoingHttpHeaders
|
||||||
|
): Headers {
|
||||||
|
const headers = new Headers();
|
||||||
|
for (const [name, value] of Object.entries(nodeHeaders)) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
for (const val of value) {
|
||||||
|
headers.append(name, val);
|
||||||
|
}
|
||||||
|
} else if (typeof value !== 'undefined') {
|
||||||
|
headers.set(name, String(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
|
import ms from 'ms';
|
||||||
import url, { URL } from 'url';
|
import url, { URL } from 'url';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
|
import fetch from 'node-fetch';
|
||||||
import plural from 'pluralize';
|
import plural from 'pluralize';
|
||||||
import rawBody from 'raw-body';
|
import rawBody from 'raw-body';
|
||||||
import listen from 'async-listen';
|
import listen from 'async-listen';
|
||||||
import minimatch from 'minimatch';
|
import minimatch from 'minimatch';
|
||||||
import ms from 'ms';
|
|
||||||
import httpProxy from 'http-proxy';
|
import httpProxy from 'http-proxy';
|
||||||
import { randomBytes } from 'crypto';
|
import { randomBytes } from 'crypto';
|
||||||
import serveHandler from 'serve-handler';
|
import serveHandler from 'serve-handler';
|
||||||
@@ -16,11 +17,11 @@ import path, { isAbsolute, basename, dirname, extname, join } from 'path';
|
|||||||
import once from '@tootallnate/once';
|
import once from '@tootallnate/once';
|
||||||
import directoryTemplate from 'serve-handler/src/directory';
|
import directoryTemplate from 'serve-handler/src/directory';
|
||||||
import getPort from 'get-port';
|
import getPort from 'get-port';
|
||||||
import { ChildProcess } from 'child_process';
|
|
||||||
import isPortReachable from 'is-port-reachable';
|
import isPortReachable from 'is-port-reachable';
|
||||||
import deepEqual from 'fast-deep-equal';
|
import deepEqual from 'fast-deep-equal';
|
||||||
import which from 'which';
|
import which from 'which';
|
||||||
import npa from 'npm-package-arg';
|
import npa from 'npm-package-arg';
|
||||||
|
import type { ChildProcess } from 'child_process';
|
||||||
|
|
||||||
import { getVercelIgnore, fileNameSymbol } from '@vercel/client';
|
import { getVercelIgnore, fileNameSymbol } from '@vercel/client';
|
||||||
import {
|
import {
|
||||||
@@ -90,6 +91,7 @@ import {
|
|||||||
import { ProjectEnvVariable, ProjectSettings } from '../../types';
|
import { ProjectEnvVariable, ProjectSettings } from '../../types';
|
||||||
import exposeSystemEnvs from './expose-system-envs';
|
import exposeSystemEnvs from './expose-system-envs';
|
||||||
import { treeKill } from '../tree-kill';
|
import { treeKill } from '../tree-kill';
|
||||||
|
import { nodeHeadersToFetchHeaders } from './headers';
|
||||||
|
|
||||||
const frontendRuntimeSet = new Set(
|
const frontendRuntimeSet = new Set(
|
||||||
frameworkList.map(f => f.useRuntime?.use || '@vercel/static-build')
|
frameworkList.map(f => f.useRuntime?.use || '@vercel/static-build')
|
||||||
@@ -593,7 +595,7 @@ export default class DevServer {
|
|||||||
await this.exit();
|
await this.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (warnings && warnings.length > 0) {
|
if (warnings?.length > 0) {
|
||||||
warnings.forEach(warning =>
|
warnings.forEach(warning =>
|
||||||
this.output.warn(warning.message, null, warning.link, warning.action)
|
this.output.warn(warning.message, null, warning.link, warning.action)
|
||||||
);
|
);
|
||||||
@@ -1337,32 +1339,6 @@ export default class DevServer {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
runDevMiddleware = async (
|
|
||||||
req: http.IncomingMessage,
|
|
||||||
res: http.ServerResponse
|
|
||||||
) => {
|
|
||||||
const { devMiddlewarePlugins } = await loadCliPlugins(
|
|
||||||
this.cwd,
|
|
||||||
this.output
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
for (let plugin of devMiddlewarePlugins) {
|
|
||||||
const result = await plugin.plugin.runDevMiddleware(req, res, this.cwd);
|
|
||||||
if (result.finished) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { finished: false };
|
|
||||||
} catch (e) {
|
|
||||||
return {
|
|
||||||
finished: true,
|
|
||||||
error: e,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serve project directory as a v2 deployment.
|
* Serve project directory as a v2 deployment.
|
||||||
*/
|
*/
|
||||||
@@ -1430,12 +1406,45 @@ export default class DevServer {
|
|||||||
let prevUrl = req.url;
|
let prevUrl = req.url;
|
||||||
let prevHeaders: HttpHeadersConfig = {};
|
let prevHeaders: HttpHeadersConfig = {};
|
||||||
|
|
||||||
/*
|
// Run the middleware file, if present, and apply any
|
||||||
const middlewareResult = await this.runDevMiddleware(req, res);
|
// mutations to the incoming request based on the
|
||||||
|
// result of the middleware invocation.
|
||||||
|
const middleware = [...this.buildMatches.values()].find(
|
||||||
|
m => m.config?.middleware === true
|
||||||
|
);
|
||||||
|
if (middleware) {
|
||||||
|
let startMiddlewareResult: StartDevServerResult | undefined;
|
||||||
|
// TODO: can we add some caching to prevent (re-)starting
|
||||||
|
// the middleware server for every HTTP request?
|
||||||
|
const { envConfigs, files, devCacheDir, cwd: workPath } = this;
|
||||||
|
try {
|
||||||
|
startMiddlewareResult =
|
||||||
|
await middleware.builderWithPkg.builder.startDevServer?.({
|
||||||
|
files,
|
||||||
|
entrypoint: middleware.entrypoint,
|
||||||
|
workPath,
|
||||||
|
repoRootPath: this.cwd,
|
||||||
|
config: middleware.config || {},
|
||||||
|
meta: {
|
||||||
|
isDev: true,
|
||||||
|
devCacheDir,
|
||||||
|
env: { ...envConfigs.runEnv },
|
||||||
|
buildEnv: { ...envConfigs.buildEnv },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
// `startDevServer()` threw an error. Most likely this means the dev
|
||||||
|
// server process exited before sending the port information message
|
||||||
|
// (missing dependency at runtime, for example).
|
||||||
|
if (err.code === 'ENOENT') {
|
||||||
|
err.message = `Command not found: ${chalk.cyan(
|
||||||
|
err.path,
|
||||||
|
...err.spawnargs
|
||||||
|
)}\nPlease ensure that ${cmd(err.path)} is properly installed`;
|
||||||
|
err.link = 'https://vercel.link/command-not-found';
|
||||||
|
}
|
||||||
|
|
||||||
if (middlewareResult) {
|
await this.sendError(
|
||||||
if (middlewareResult.error) {
|
|
||||||
this.sendError(
|
|
||||||
req,
|
req,
|
||||||
res,
|
res,
|
||||||
requestId,
|
requestId,
|
||||||
@@ -1444,23 +1453,105 @@ export default class DevServer {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (middlewareResult.finished) {
|
|
||||||
|
if (startMiddlewareResult) {
|
||||||
|
const { port, pid } = startMiddlewareResult;
|
||||||
|
this.devServerPids.add(pid);
|
||||||
|
|
||||||
|
const middlewareReqHeaders = nodeHeadersToFetchHeaders(req.headers);
|
||||||
|
|
||||||
|
// Add the Vercel platform proxy request headers
|
||||||
|
const proxyHeaders = this.getProxyHeaders(req, requestId, true);
|
||||||
|
for (const [name, value] of nodeHeadersToFetchHeaders(proxyHeaders)) {
|
||||||
|
middlewareReqHeaders.set(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const middlewareRes = await fetch(
|
||||||
|
`http://127.0.0.1:${port}${parsed.path}`,
|
||||||
|
{
|
||||||
|
headers: middlewareReqHeaders,
|
||||||
|
method: req.method,
|
||||||
|
redirect: 'manual',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (middlewareRes.status === 500) {
|
||||||
|
await this.sendError(
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
requestId,
|
||||||
|
'EDGE_FUNCTION_INVOCATION_FAILED',
|
||||||
|
500
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (middlewareResult.pathname) {
|
// Apply status code from middleware invocation,
|
||||||
const origUrl = url.parse(req.url || '/', true);
|
// for i.e. redirects or a custom 404 page
|
||||||
origUrl.pathname = middlewareResult.pathname;
|
res.statusCode = middlewareRes.status;
|
||||||
prevUrl = url.format(origUrl);
|
|
||||||
}
|
let rewritePath = '';
|
||||||
if (middlewareResult.query && prevUrl) {
|
let contentType = '';
|
||||||
const origUrl = url.parse(req.url || '/', true);
|
let shouldContinue = false;
|
||||||
delete origUrl.search;
|
const skipMiddlewareHeaders = new Set([
|
||||||
Object.assign(origUrl.query, middlewareResult.query);
|
'date',
|
||||||
prevUrl = url.format(origUrl);
|
'connection',
|
||||||
|
'content-length',
|
||||||
|
'transfer-encoding',
|
||||||
|
]);
|
||||||
|
for (const [name, value] of middlewareRes.headers) {
|
||||||
|
if (name === 'x-middleware-next') {
|
||||||
|
shouldContinue = value === '1';
|
||||||
|
} else if (name === 'x-middleware-rewrite') {
|
||||||
|
rewritePath = value;
|
||||||
|
shouldContinue = true;
|
||||||
|
} else if (name === 'content-type') {
|
||||||
|
contentType = value;
|
||||||
|
} else if (!skipMiddlewareHeaders.has(name)) {
|
||||||
|
// Any other kind of response header should be included
|
||||||
|
// on both the incoming HTTP request (for when proxying
|
||||||
|
// to another function) and the outgoing HTTP response.
|
||||||
|
res.setHeader(name, value);
|
||||||
|
req.headers[name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shouldContinue) {
|
||||||
|
const middlewareBody = await middlewareRes.buffer();
|
||||||
|
this.setResponseHeaders(res, requestId);
|
||||||
|
if (middlewareBody.length > 0) {
|
||||||
|
res.setHeader('content-length', middlewareBody.length);
|
||||||
|
if (contentType) {
|
||||||
|
res.setHeader('content-type', contentType);
|
||||||
|
}
|
||||||
|
res.end(middlewareBody);
|
||||||
|
} else {
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rewritePath) {
|
||||||
|
// TODO: add validation?
|
||||||
|
debug(`Detected rewrite path from middleware: "${rewritePath}"`);
|
||||||
|
prevUrl = rewritePath;
|
||||||
|
|
||||||
|
// Retain orginal pathname, but override query parameters from the rewrite
|
||||||
|
const beforeRewriteUrl = req.url || '/';
|
||||||
|
const rewriteUrlParsed = url.parse(beforeRewriteUrl, true);
|
||||||
|
delete rewriteUrlParsed.search;
|
||||||
|
rewriteUrlParsed.query = url.parse(rewritePath, true).query;
|
||||||
|
req.url = url.format(rewriteUrlParsed);
|
||||||
|
debug(
|
||||||
|
`Rewrote incoming HTTP URL from "${beforeRewriteUrl}" to "${req.url}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.killBuilderDevServer(pid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
for (const phase of phases) {
|
for (const phase of phases) {
|
||||||
statusCode = undefined;
|
statusCode = undefined;
|
||||||
@@ -2269,11 +2360,12 @@ async function findBuildMatch(
|
|||||||
if (!isIndex(match.src)) {
|
if (!isIndex(match.src)) {
|
||||||
return match;
|
return match;
|
||||||
} else {
|
} else {
|
||||||
// if isIndex === true and ends in .html, we're done. Otherwise, keep searching
|
// If isIndex === true and ends in `.html`, we're done.
|
||||||
bestIndexMatch = match;
|
// Otherwise, keep searching.
|
||||||
if (extname(match.src) === '.html') {
|
if (extname(match.src) === '.html') {
|
||||||
return bestIndexMatch;
|
return match;
|
||||||
}
|
}
|
||||||
|
bestIndexMatch = match;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2295,6 +2387,13 @@ async function shouldServe(
|
|||||||
config,
|
config,
|
||||||
builderWithPkg: { builder },
|
builderWithPkg: { builder },
|
||||||
} = match;
|
} = match;
|
||||||
|
|
||||||
|
// "middleware" file is not served as a regular asset,
|
||||||
|
// instead it gets invoked as part of the routing logic.
|
||||||
|
if (config?.middleware === true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const cleanSrc = src.endsWith('.html') ? src.slice(0, -5) : src;
|
const cleanSrc = src.endsWith('.html') ? src.slice(0, -5) : src;
|
||||||
const trimmedPath = requestPath.endsWith('/')
|
const trimmedPath = requestPath.endsWith('/')
|
||||||
? requestPath.slice(0, -1)
|
? requestPath.slice(0, -1)
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export default () => new Response(null, { status: 500 });
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
throw new Error('Middleware init error');
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default () => {
|
||||||
|
throw new Error('Middleware handler error');
|
||||||
|
};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export default req => {
|
||||||
|
const url = new URL(req.url);
|
||||||
|
return new Response(null, {
|
||||||
|
status: 302,
|
||||||
|
headers: {
|
||||||
|
location: `https://vercel.com${url.pathname}${url.search}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export default () => new Response('hi from middleware');
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export default (req, res) => {
|
||||||
|
res.json({
|
||||||
|
url: req.url,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export default () =>
|
||||||
|
new Response(null, {
|
||||||
|
headers: {
|
||||||
|
'x-middleware-rewrite': '/api/fn?from-middleware=true',
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<h1>Another</h1>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<h1>Index</h1>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
export default req => {
|
||||||
|
const url = new URL(req.url);
|
||||||
|
|
||||||
|
if (url.pathname === '/') {
|
||||||
|
// Pass-through "index.html" page
|
||||||
|
return new Response(null, {
|
||||||
|
headers: {
|
||||||
|
'x-middleware-next': '1',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything else goes to "another.html"
|
||||||
|
return new Response(null, {
|
||||||
|
headers: {
|
||||||
|
'x-middleware-rewrite': '/another.html',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -433,3 +433,75 @@ test(
|
|||||||
await testPath(404, '/i-do-not-exist');
|
await testPath(404, '/i-do-not-exist');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'[vercel dev] Middleware that returns a 200 response',
|
||||||
|
testFixtureStdio('middleware-response', async (testPath: any) => {
|
||||||
|
await testPath(200, '/', 'hi from middleware');
|
||||||
|
await testPath(200, '/another', 'hi from middleware');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'[vercel dev] Middleware that does basic rewrite',
|
||||||
|
testFixtureStdio('middleware-rewrite', async (testPath: any) => {
|
||||||
|
await testPath(200, '/', '<h1>Index</h1>');
|
||||||
|
await testPath(200, '/index', '<h1>Another</h1>');
|
||||||
|
await testPath(200, '/another', '<h1>Another</h1>');
|
||||||
|
await testPath(200, '/another.html', '<h1>Another</h1>');
|
||||||
|
await testPath(200, '/foo', '<h1>Another</h1>');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'[vercel dev] Middleware that rewrites with custom query params',
|
||||||
|
testFixtureStdio('middleware-rewrite-query', async (testPath: any) => {
|
||||||
|
await testPath(200, '/?foo=bar', '{"url":"/?from-middleware=true"}');
|
||||||
|
await testPath(
|
||||||
|
200,
|
||||||
|
'/another?foo=bar',
|
||||||
|
'{"url":"/another?from-middleware=true"}'
|
||||||
|
);
|
||||||
|
await testPath(
|
||||||
|
200,
|
||||||
|
'/api/fn?foo=bar',
|
||||||
|
'{"url":"/api/fn?from-middleware=true"}'
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'[vercel dev] Middleware that redirects',
|
||||||
|
testFixtureStdio('middleware-redirect', async (testPath: any) => {
|
||||||
|
await testPath(302, '/', null, {
|
||||||
|
location: 'https://vercel.com/',
|
||||||
|
});
|
||||||
|
await testPath(302, '/home', null, {
|
||||||
|
location: 'https://vercel.com/home',
|
||||||
|
});
|
||||||
|
await testPath(302, '/?foo=bar', null, {
|
||||||
|
location: 'https://vercel.com/?foo=bar',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'[vercel dev] Middleware with error in function handler',
|
||||||
|
testFixtureStdio('middleware-error-in-handler', async (testPath: any) => {
|
||||||
|
await testPath(500, '/', /EDGE_FUNCTION_INVOCATION_FAILED/);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'[vercel dev] Middleware with error at init',
|
||||||
|
testFixtureStdio('middleware-error-at-init', async (testPath: any) => {
|
||||||
|
await testPath(500, '/', /EDGE_FUNCTION_INVOCATION_FAILED/);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'[vercel dev] Middleware with an explicit 500 response',
|
||||||
|
testFixtureStdio('middleware-500-response', async (testPath: any) => {
|
||||||
|
await testPath(500, '/', /EDGE_FUNCTION_INVOCATION_FAILED/);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ import { Readable } from 'stream';
|
|||||||
import type { Bridge } from '@vercel/node-bridge/bridge';
|
import type { Bridge } from '@vercel/node-bridge/bridge';
|
||||||
import { getVercelLauncher } from '@vercel/node-bridge/launcher.js';
|
import { getVercelLauncher } from '@vercel/node-bridge/launcher.js';
|
||||||
import { VercelProxyResponse } from '@vercel/node-bridge/types';
|
import { VercelProxyResponse } from '@vercel/node-bridge/types';
|
||||||
import { streamToBuffer } from '@vercel/build-utils';
|
import { Config, streamToBuffer } from '@vercel/build-utils';
|
||||||
import exitHook from 'exit-hook';
|
import exitHook from 'exit-hook';
|
||||||
import { EdgeRuntime, Primitives, runServer } from 'edge-runtime';
|
import { EdgeRuntime, Primitives, runServer } from 'edge-runtime';
|
||||||
import { getConfig } from '@vercel/static-config';
|
import { getConfig } from '@vercel/static-config';
|
||||||
@@ -187,6 +187,7 @@ async function createEdgeEventHandler(
|
|||||||
|
|
||||||
return async function (request: IncomingMessage) {
|
return async function (request: IncomingMessage) {
|
||||||
const response = await fetch(server.url, {
|
const response = await fetch(server.url, {
|
||||||
|
redirect: 'manual',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
body: await serializeRequest(request),
|
body: await serializeRequest(request),
|
||||||
});
|
});
|
||||||
@@ -214,10 +215,15 @@ function parseRuntime(entrypoint: string): string | undefined {
|
|||||||
|
|
||||||
async function createEventHandler(
|
async function createEventHandler(
|
||||||
entrypoint: string,
|
entrypoint: string,
|
||||||
|
config: Config,
|
||||||
options: { shouldAddHelpers: boolean }
|
options: { shouldAddHelpers: boolean }
|
||||||
): Promise<(request: IncomingMessage) => Promise<VercelProxyResponse>> {
|
): Promise<(request: IncomingMessage) => Promise<VercelProxyResponse>> {
|
||||||
const runtime = parseRuntime(entrypoint);
|
const runtime = parseRuntime(entrypoint);
|
||||||
if (runtime === 'experimental-edge') {
|
|
||||||
|
// `middleware.js`/`middleware.ts` file is always run as
|
||||||
|
// an Edge Function, otherwise needs to be opted-in via
|
||||||
|
// `export const config = { runtime: 'experimental-edge' }`
|
||||||
|
if (config.middleware === true || runtime === 'experimental-edge') {
|
||||||
return createEdgeEventHandler(entrypoint);
|
return createEdgeEventHandler(entrypoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +247,9 @@ async function main() {
|
|||||||
await listen(proxyServer, 0, '127.0.0.1');
|
await listen(proxyServer, 0, '127.0.0.1');
|
||||||
|
|
||||||
const entryPointPath = join(process.cwd(), entrypoint!);
|
const entryPointPath = join(process.cwd(), entrypoint!);
|
||||||
handleEvent = await createEventHandler(entryPointPath, { shouldAddHelpers });
|
handleEvent = await createEventHandler(entryPointPath, config, {
|
||||||
|
shouldAddHelpers,
|
||||||
|
});
|
||||||
|
|
||||||
const address = proxyServer.address();
|
const address = proxyServer.address();
|
||||||
if (typeof process.send === 'function') {
|
if (typeof process.send === 'function') {
|
||||||
|
|||||||
@@ -522,7 +522,7 @@ async function doTypeCheck(
|
|||||||
{ entrypoint, workPath, meta = {} }: StartDevServerOptions,
|
{ entrypoint, workPath, meta = {} }: StartDevServerOptions,
|
||||||
projectTsConfig: string | null
|
projectTsConfig: string | null
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { devCacheDir = join(workPath, '.now', 'cache') } = meta;
|
const { devCacheDir = join(workPath, '.vercel', 'cache') } = meta;
|
||||||
const entrypointCacheDir = join(devCacheDir, 'node', entrypoint);
|
const entrypointCacheDir = join(devCacheDir, 'node', entrypoint);
|
||||||
|
|
||||||
// In order to type-check a single file, a standalone tsconfig
|
// In order to type-check a single file, a standalone tsconfig
|
||||||
|
|||||||
Reference in New Issue
Block a user