Skip to content

Commit

Permalink
[next]: normalize source path for special app-dir metadata files (#11579
Browse files Browse the repository at this point in the history
)

When specifying a functions configuration in `vercel.json`, we attempt to find source files for all entrypoints.  This attempts to normalize those source paths for some special cases so that we don't show a build warning for completely normal usage.

- `/_not-found` is an entrypoint automatically inserted by Next.js -- if we don't find a source file for it, don't warn, as the user might not have added one
- special metadata files like `favicon.ico` and `opengraph-image.<ext>` will be bundled as `favicon.ico/route.js` but the source file will be the raw extension.
  • Loading branch information
ztanner committed May 13, 2024
1 parent c224fb1 commit 67afc26
Show file tree
Hide file tree
Showing 13 changed files with 91 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/bright-grapes-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@vercel/next': patch
---

normalize source file locations for special metadata files
56 changes: 49 additions & 7 deletions packages/next/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,13 @@ async function getSourceFilePathFromPage({
}
}

// if we got here, and didn't find a source not-found file, then it was the one injected
// by Next.js. There's no need to warn or return a source file in this case, as it won't have
// any configuration applied to it.
if (page === '/_not-found/page') {
return '';
}

console.log(
`WARNING: Unable to find source file for page ${page} with extensions: ${extensionsToTry.join(
', '
Expand Down Expand Up @@ -1565,15 +1572,13 @@ export async function getPageLambdaGroups({
}

if (config && config.functions) {
// `pages` are normalized without route groups (e.g., /app/(group)/page.js).
// we keep track of that mapping in `inversedAppPathManifest`
// `getSourceFilePathFromPage` needs to use the path from source to properly match the config
const pageFromManifest = inversedAppPathManifest?.[routeName];
const sourceFile = await getSourceFilePathFromPage({
workPath: entryPath,
// since this function is used by both `pages` and `app`, the manifest might not be provided
// so fallback to normal behavior of just checking the `page`.
page: pageFromManifest ?? page,
page: normalizeSourceFilePageFromManifest(
routeName,
page,
inversedAppPathManifest
),
pageExtensions,
});

Expand Down Expand Up @@ -1657,6 +1662,43 @@ export async function getPageLambdaGroups({
return groups;
}

// `pages` are normalized without route groups (e.g., /app/(group)/page.js).
// we keep track of that mapping in `inversedAppPathManifest`
// `getSourceFilePathFromPage` needs to use the path from source to properly match the config
function normalizeSourceFilePageFromManifest(
routeName: string,
page: string,
inversedAppPathManifest?: Record<string, string>
) {
const pageFromManifest = inversedAppPathManifest?.[routeName];
if (!pageFromManifest) {
// since this function is used by both `pages` and `app`, the manifest might not be provided
// so fallback to normal behavior of just checking the `page`.
return page;
}

const metadataConventions = [
'/favicon.',
'/icon.',
'/apple-icon.',
'/opengraph-image.',
'/twitter-image.',
'/sitemap.',
'/robots.',
];

// these special metadata files for will not contain `/route` or `/page` suffix, so return the routeName as-is.
const isSpecialFile = metadataConventions.some(convention =>
routeName.startsWith(convention)
);

if (isSpecialFile) {
return routeName;
}

return pageFromManifest;
}

export const outputFunctionFileSizeInfo = (
pages: string[],
pseudoLayer: PseudoLayer,
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions packages/next/test/fixtures/00-app-dir-no-ppr/app/layout.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export const metadata = {
metadataBase: new URL(process.env.VERCEL_PROJECT_PRODUCTION_URL ?? 'http://localhost:3000'),
};

export default function Root({ children }) {
return (
<html className="this-is-the-document-html">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Alt text for opengraph-image
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions packages/next/test/fixtures/00-app-dir-no-ppr/app/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
User-Agent: *
Disallow:
7 changes: 7 additions & 0 deletions packages/next/test/fixtures/00-app-dir-no-ppr/app/sitemap.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://vercel.com/</loc>
<lastmod>2023-03-06T18:04:14.008Z</lastmod>
</url>
</urlset>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Alt text for twitter image
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions packages/next/test/integration/integration-1.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,26 @@ async function hashAllFiles(files) {
// experimental appDir currently requires Node.js >= 16
if (parseInt(process.versions.node.split('.')[0], 10) >= 16) {
it('should build with app-dir correctly', async () => {
const origLog = console.log;
const origError = console.error;
const caughtLogs = [];

console.log = function (...args) {
caughtLogs.push(args.join(' '));
origLog.apply(this, args);
};
console.error = function (...args) {
caughtLogs.push(args.join(' '));
origError.apply(this, args);
};

const { buildResult } = await runBuildLambda(
path.join(__dirname, '../fixtures/00-app-dir-no-ppr')
);

console.log = origLog;
console.error = origError;

const lambdas = new Set();

for (const key of Object.keys(buildResult.output)) {
Expand Down Expand Up @@ -123,6 +139,12 @@ if (parseInt(process.versions.node.split('.')[0], 10) >= 16) {
expect(buildResult.output['dashboard.rsc'].fallback.fsPath).toMatch(
/server\/app\/dashboard\.rsc$/
);

expect(
caughtLogs.some(log =>
log.includes('WARNING: Unable to find source file for page')
)
).toBeFalsy();
// TODO: re-enable after index/index handling is corrected
// expect(buildResult.output['dashboard/index/index'].type).toBe('Prerender');
// expect(buildResult.output['dashboard/index/index'].fallback.fsPath).toMatch(
Expand Down

0 comments on commit 67afc26

Please sign in to comment.