[next] fix app dir edge functions with basePath (#10465)

x-ref: https://github.com/vercel/vercel/pull/10394
This commit is contained in:
JJ Kasper
2023-09-07 15:03:12 -07:00
committed by GitHub
parent 335fd70a68
commit caaba0d685
13 changed files with 159 additions and 24 deletions

View File

@@ -0,0 +1,5 @@
---
"@vercel/next": patch
---
Fix Next.js with `basePath` + Edge runtime + App Router on a top level `page.jsx`

View File

@@ -91,6 +91,7 @@ import {
getOperationType,
isApiPage,
getFunctionsConfigManifest,
normalizeEdgeFunctionPath,
} from './utils';
export const version = 2;
@@ -2720,7 +2721,15 @@ async function getServerlessPages(params: {
for (const edgeFunctionFile of Object.keys(
middlewareManifest?.functions ?? {}
)) {
const edgePath = (edgeFunctionFile.slice(1) || 'index') + '.js';
let edgePath =
middlewareManifest?.functions?.[edgeFunctionFile].name ||
edgeFunctionFile;
edgePath = normalizeEdgeFunctionPath(
edgePath,
params.appPathRoutesManifest || {}
);
edgePath = (edgePath || 'index') + '.js';
delete normalizedAppPaths[edgePath];
delete pages[edgePath];
}

View File

@@ -1245,10 +1245,9 @@ export async function serverBuild({
if (ogRoute.endsWith('/route')) {
continue;
}
route = path.posix.join(
'./',
entryDirectory,
route === '/' ? '/index' : route
route = normalizeIndexOutput(
path.posix.join('./', entryDirectory, route === '/' ? '/index' : route),
true
);
if (lambdas[route]) {

View File

@@ -2344,7 +2344,7 @@ export function normalizeIndexOutput(
outputName: string,
isServerMode: boolean
) {
if (outputName !== '/index' && isServerMode) {
if (outputName !== 'index' && outputName !== '/index' && isServerMode) {
return outputName.replace(/\/index$/, '');
}
return outputName;
@@ -2521,6 +2521,29 @@ function normalizeRegions(regions: Regions): undefined | string | string[] {
return newRegions;
}
export function normalizeEdgeFunctionPath(
shortPath: string,
appPathRoutesManifest: Record<string, string>
) {
if (
shortPath.startsWith('app/') &&
(shortPath.endsWith('/page') ||
shortPath.endsWith('/route') ||
shortPath === 'app/_not-found')
) {
const ogRoute = shortPath.replace(/^app\//, '/');
shortPath = (
appPathRoutesManifest[ogRoute] ||
shortPath.replace(/(^|\/)(page|route)$/, '')
).replace(/^\//, '');
if (!shortPath || shortPath === '/') {
shortPath = 'index';
}
}
return shortPath;
}
export async function getMiddlewareBundle({
entryPath,
outputDirectory,
@@ -2699,27 +2722,19 @@ export async function getMiddlewareBundle({
// app/index/page -> index/index
if (shortPath.startsWith('pages/')) {
shortPath = shortPath.replace(/^pages\//, '');
} else if (
shortPath.startsWith('app/') &&
(shortPath.endsWith('/page') ||
shortPath.endsWith('/route') ||
shortPath === 'app/_not-found')
) {
const ogRoute = shortPath.replace(/^app\//, '/');
shortPath = (
appPathRoutesManifest[ogRoute] ||
shortPath.replace(/(^|\/)(page|route)$/, '')
).replace(/^\//, '');
if (!shortPath || shortPath === '/') {
shortPath = 'index';
}
} else {
shortPath = normalizeEdgeFunctionPath(shortPath, appPathRoutesManifest);
}
if (routesManifest?.basePath) {
shortPath = path.posix
.join(routesManifest.basePath, shortPath)
.replace(/^\//, '');
shortPath = normalizeIndexOutput(
path.posix.join(
'./',
routesManifest?.basePath,
shortPath.replace(/^\//, '')
),
true
);
}
worker.edgeFunction.name = shortPath;

View File

@@ -0,0 +1,7 @@
export const runtime = 'edge';
const Home = () => {
return <div>another</div>;
};
export default Home;

View File

@@ -0,0 +1,7 @@
export const runtime = 'edge';
const Home = () => {
return <div>dynamic</div>;
};
export default Home;

View File

@@ -0,0 +1,11 @@
export const runtime = 'edge';
const Layout = ({ children }) => {
return (
<html>
<body>{children}</body>
</html>
);
};
export default Layout;

View File

@@ -0,0 +1,7 @@
export const runtime = 'edge';
const Home = () => {
return <div>Home</div>;
};
export default Home;

View File

@@ -0,0 +1,7 @@
export const runtime = 'edge';
const Home = () => {
return <div>test</div>;
};
export default Home;

View File

@@ -0,0 +1,7 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
basePath: '/test',
};
module.exports = nextConfig;

View File

@@ -0,0 +1,8 @@
{
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
},
"ignoreNextjsUpdates": true
}

View File

@@ -0,0 +1,4 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@vercel/next" }]
}

View File

@@ -553,3 +553,52 @@ it('Should de-dupe correctly when limit is close (uncompressed)', async () => {
expect(lambdas.size).toBe(2);
expect(lambdas.size).toBeLessThan(totalLambdas);
});
it('should handle edge functions in app with basePath', async () => {
const {
buildResult: { output },
} = await runBuildLambda(path.join(__dirname, 'edge-app-dir-basepath'));
console.error(output);
expect(output['test']).toBeDefined();
expect(output['test']).toBeDefined();
expect(output['test'].type).toBe('EdgeFunction');
expect(output['test'].type).toBe('EdgeFunction');
expect(output['test/another']).toBeDefined();
expect(output['test/another.rsc']).toBeDefined();
expect(output['test/another'].type).toBe('EdgeFunction');
expect(output['test/another.rsc'].type).toBe('EdgeFunction');
expect(output['test/dynamic/[slug]']).toBeDefined();
expect(output['test/dynamic/[slug].rsc']).toBeDefined();
expect(output['test/dynamic/[slug]'].type).toBe('EdgeFunction');
expect(output['test/dynamic/[slug].rsc'].type).toBe('EdgeFunction');
expect(output['test/dynamic/[slug]']).toBeDefined();
expect(output['test/dynamic/[slug].rsc']).toBeDefined();
expect(output['test/dynamic/[slug]'].type).toBe('EdgeFunction');
expect(output['test/dynamic/[slug].rsc'].type).toBe('EdgeFunction');
expect(output['test/test']).toBeDefined();
expect(output['test/test.rsc']).toBeDefined();
expect(output['test/test'].type).toBe('EdgeFunction');
expect(output['test/test.rsc'].type).toBe('EdgeFunction');
expect(output['test/_not-found']).toBeDefined();
expect(output['test/_not-found'].type).toBe('Lambda');
const lambdas = new Set();
const edgeFunctions = new Set();
for (const item of Object.values(output)) {
if (item.type === 'Lambda') {
lambdas.add(item);
} else if (item.type === 'EdgeFunction') {
edgeFunctions.add(item);
}
}
expect(lambdas.size).toBe(1);
expect(edgeFunctions.size).toBe(4);
});