Skip to content

Commit

Permalink
[now-next] Add /_next/data routes for getServerProps pages (#3771)
Browse files Browse the repository at this point in the history
This adds the `/_next/data` routes for `getServerProps` pages if they exist

x-ref: vercel/next.js#10077
x-ref: vercel/next.js#10622
  • Loading branch information
ijjk committed Feb 26, 2020
1 parent 7dde9c8 commit 45066cd
Show file tree
Hide file tree
Showing 16 changed files with 382 additions and 23 deletions.
4 changes: 1 addition & 3 deletions packages/now-cli/test/unit.js
Expand Up @@ -69,9 +69,7 @@ const getStaticFiles = async (dir, isBuilds = false) => {
const normalizeWindowsPaths = files => {
if (process.platform === 'win32') {
const prefix = 'D:/a/now/now/packages/now-cli/test/fixtures/unit/';
return files.map(f =>
f.replace(/\\/g, '/').slice(prefix.length)
);
return files.map(f => f.replace(/\\/g, '/').slice(prefix.length));
}
return files;
};
Expand Down
78 changes: 59 additions & 19 deletions packages/now-next/src/index.ts
Expand Up @@ -18,7 +18,7 @@ import {
runNpmInstall,
runPackageJsonScript,
} from '@now/build-utils';
import { Route, Source } from '@now/routing-utils';
import { Route } from '@now/routing-utils';
import {
convertHeaders,
convertRedirects,
Expand Down Expand Up @@ -336,11 +336,17 @@ export const build = async ({
env.NODE_OPTIONS = `--max_old_space_size=${memoryToConsume}`;
await runPackageJsonScript(entryPath, shouldRunScript, { ...spawnOpts, env });

const appMountPrefixNoTrailingSlash = path.posix
.join('/', entryDirectory)
.replace(/\/+$/, '');

const routesManifest = await getRoutesManifest(entryPath, realNextVersion);
const prerenderManifest = await getPrerenderManifest(entryPath);
const headers: Route[] = [];
const rewrites: Route[] = [];
const redirects: Route[] = [];
const nextBasePathRoute: Route[] = [];
const dataRoutes: Route[] = [];
let nextBasePath: string | undefined;
// whether they have enabled pages/404.js as the custom 404 page
let hasPages404 = false;
Expand All @@ -356,6 +362,35 @@ export const build = async ({
headers.push(...convertHeaders(routesManifest.headers));
}

if (routesManifest.dataRoutes) {
// Load the /_next/data routes for both dynamic SSG and SSP pages.
// These must be combined and sorted to prevent conflicts
for (const dataRoute of routesManifest.dataRoutes) {
const ssgDataRoute = prerenderManifest.lazyRoutes[dataRoute.page];

// we don't need to add routes for non-lazy SSG routes since
// they have outputs which would override the routes anyways
if (prerenderManifest.routes[dataRoute.page]) {
continue;
}

dataRoutes.push({
src: dataRoute.dataRouteRegex.replace(
/^\^/,
`^${appMountPrefixNoTrailingSlash}`
),
dest: path.join(
'/',
entryDirectory,
// make sure to route SSG data route to the data prerender
// output, we don't do this for SSP routes since they don't
// have a separate data output
(ssgDataRoute && ssgDataRoute.dataRoute) || dataRoute.page
),
});
}
}

if (routesManifest.pages404) {
hasPages404 = true;
}
Expand Down Expand Up @@ -521,13 +556,8 @@ export const build = async ({
const prerenders: { [key: string]: Prerender | FileFsRef } = {};
const staticPages: { [key: string]: FileFsRef } = {};
const dynamicPages: string[] = [];
const dynamicDataRoutes: Array<Source> = [];
let static404Page: string | undefined;

const appMountPrefixNoTrailingSlash = path.posix
.join('/', entryDirectory)
.replace(/\/+$/, '');

if (isLegacy) {
const filesAfterBuild = await glob('**', entryPath);

Expand Down Expand Up @@ -629,7 +659,6 @@ export const build = async ({

const pages = await glob('**/*.js', pagesDir);
const staticPageFiles = await glob('**/*.html', pagesDir);
const prerenderManifest = await getPrerenderManifest(entryPath);

Object.keys(staticPageFiles).forEach((page: string) => {
const pathname = page.replace(/\.html$/, '');
Expand Down Expand Up @@ -976,18 +1005,25 @@ export const build = async ({
onPrerenderRoute(route, true)
);

// Dynamic pages for lazy routes should be handled by the lambda flow.
Object.keys(prerenderManifest.lazyRoutes).forEach(lazyRoute => {
const { dataRouteRegex, dataRoute } = prerenderManifest.lazyRoutes[
lazyRoute
];
dynamicDataRoutes.push({
// Next.js provided data route regex
src: dataRouteRegex.replace(/^\^/, `^${appMountPrefixNoTrailingSlash}`),
// Location of lambda in builder output
dest: path.posix.join(entryDirectory, dataRoute),
// We still need to use lazyRoutes if the dataRoutes field
// isn't available for backwards compatibility
if (!(routesManifest && routesManifest.dataRoutes)) {
// Dynamic pages for lazy routes should be handled by the lambda flow.
Object.keys(prerenderManifest.lazyRoutes).forEach(lazyRoute => {
const { dataRouteRegex, dataRoute } = prerenderManifest.lazyRoutes[
lazyRoute
];
dataRoutes.push({
// Next.js provided data route regex
src: dataRouteRegex.replace(
/^\^/,
`^${appMountPrefixNoTrailingSlash}`
),
// Location of lambda in builder output
dest: path.posix.join(entryDirectory, dataRoute),
});
});
});
}
}

const nextStaticFiles = await glob(
Expand Down Expand Up @@ -1086,6 +1122,7 @@ export const build = async ({
continue: true,
},
{ src: path.join('/', entryDirectory, '_next(?!/data(?:/|$))(?:/.*)?') },

// Next.js page lambdas, `static/` folder, reserved assets, and `public/`
// folder
{ handle: 'filesystem' },
Expand All @@ -1107,7 +1144,10 @@ export const build = async ({
...rewrites,
// Dynamic routes
...dynamicRoutes,
...dynamicDataRoutes,

// /_next/data routes for getServerProps/getStaticProps pages
...dataRoutes,

// Custom Next.js 404 page (TODO: do we want to remove this?)
...(isLegacy
? []
Expand Down
1 change: 1 addition & 0 deletions packages/now-next/src/utils.ts
Expand Up @@ -316,6 +316,7 @@ export type RoutesManifest = {
regex: string;
}[];
version: number;
dataRoutes?: Array<{ page: string; dataRouteRegex: string }>;
};

export async function getRoutesManifest(
Expand Down
15 changes: 15 additions & 0 deletions packages/now-next/test/fixtures/05-spr-support/now.json
Expand Up @@ -84,6 +84,21 @@
"x-now-cache": "/HIT|STALE/"
}
},
{
"path": "/_next/data/testing-build-id/blog/post-4.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{ "delay": 2000 },
{
"path": "/_next/data/testing-build-id/blog/post-4.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "/HIT|STALE/"
}
},
{
"path": "/blog/post-1/comment-1",
"status": 200,
Expand Down
15 changes: 15 additions & 0 deletions packages/now-next/test/fixtures/18-ssg-fallback-support/now.json
Expand Up @@ -82,6 +82,21 @@
"x-now-cache": "/HIT|STALE/"
}
},
{
"path": "/_next/data/testing-build-id/blog/post-4.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{ "delay": 2000 },
{
"path": "/_next/data/testing-build-id/blog/post-4.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "/HIT|STALE/"
}
},
{
"path": "/blog/post-3",
"status": 200,
Expand Down
@@ -1,6 +1,6 @@
{
"dependencies": {
"next": "9.2.2-canary.16",
"next": "9.2.3-canary.13",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
Expand Down
@@ -0,0 +1,5 @@
module.exports = {
generateBuildId() {
return 'testing-build-id';
},
};
164 changes: 164 additions & 0 deletions packages/now-next/test/fixtures/21-server-props/now.json
@@ -0,0 +1,164 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/next" }],
"probes": [
{
"path": "/lambda",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{
"path": "/forever",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{ "delay": 2000 },
{
"path": "/forever",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{
"path": "/another",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{ "delay": 2000 },
{
"path": "/another",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{
"path": "/blog/post-1",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{ "delay": 2000 },
{
"path": "/blog/post-1",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{
"path": "/blog/post-2",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{ "delay": 2000 },
{
"path": "/blog/post-2",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{
"path": "/blog/post-3",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{ "delay": 2000 },
{
"path": "/blog/post-3",
"status": 200,
"responseHeaders": {
"x-now-cache": "/MISS/"
}
},
{
"path": "/blog/post-1/comment-1",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{
"path": "/blog/post-2/comment-2",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{
"path": "/blog/post-3/comment-3",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{
"path": "/_next/data/testing-build-id/lambda.json",
"status": 404
},
{
"path": "/_next/data/testing-build-id/another.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "/MISS/"
}
},
{
"path": "/_next/data/testing-build-id/another2.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{ "delay": 2000 },
{
"path": "/_next/data/testing-build-id/another2.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{
"path": "/_next/data/testing-build-id/blog/post-1.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "/MISS/"
}
},
{
"path": "/_next/data/testing-build-id/blog/post-1.json",
"status": 200,
"mustContain": "post-1"
},
{
"path": "/_next/data/testing-build-id/blog/post-1337/comment-1337.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{
"path": "/_next/data/testing-build-id/blog/post-1337/comment-1337.json",
"status": 200,
"mustContain": "comment-1337"
},
{
"path": "/_next/data/testing-build-id/blog/post-1337/comment-1337.json",
"status": 200,
"mustContain": "post-1337"
}
]
}
7 changes: 7 additions & 0 deletions packages/now-next/test/fixtures/21-server-props/package.json
@@ -0,0 +1,7 @@
{
"dependencies": {
"next": "9.2.3-canary.13",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

0 comments on commit 45066cd

Please sign in to comment.