Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[now-next] Add /_next/data routes for getServerProps pages #3771

Merged
merged 10 commits into from Feb 26, 2020
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
56 changes: 35 additions & 21 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,29 @@ 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) {
dataRoutes.push({
src: dataRoute.dataRouteRegex.replace(
/^\^/,
`^${appMountPrefixNoTrailingSlash}`
),
// 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 data
// output
dest: path.join(
'/',
entryDirectory,
`${dataRoute.page}${
prerenderManifest.lazyRoutes[dataRoute.page] ? '.json' : ''
}`
),
});
}
}

if (routesManifest.pages404) {
hasPages404 = true;
}
Expand Down Expand Up @@ -521,13 +550,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 +653,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 @@ -975,19 +998,6 @@ export const build = async ({
Object.keys(prerenderManifest.lazyRoutes).forEach(route =>
onPrerenderRoute(route, true)
);

// Dynamic pages for lazy routes should be handled by the lambda flow.
Object.keys(prerenderManifest.lazyRoutes).forEach(lazyRoute => {
ijjk marked this conversation as resolved.
Show resolved Hide resolved
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),
});
});
}

const nextStaticFiles = await glob(
Expand Down Expand Up @@ -1086,6 +1096,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 +1118,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
@@ -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"
}
}
20 changes: 20 additions & 0 deletions packages/now-next/test/fixtures/21-server-props/pages/another.js
@@ -0,0 +1,20 @@
import React from 'react';

// eslint-disable-next-line camelcase
export async function unstable_getServerProps() {
return {
props: {
world: 'world',
time: new Date().getTime(),
},
};
}

export default ({ world, time }) => {
return (
<>
<p>hello: {world}</p>
<span>time: {time}</span>
</>
);
};
20 changes: 20 additions & 0 deletions packages/now-next/test/fixtures/21-server-props/pages/another2.js
@@ -0,0 +1,20 @@
import React from 'react';

// eslint-disable-next-line camelcase
export async function unstable_getServerProps() {
return {
props: {
world: 'world',
time: new Date().getTime(),
},
};
}

export default ({ world, time }) => {
return (
<>
<p>hello: {world}</p>
<span>time: {time}</span>
</>
);
};
@@ -0,0 +1,22 @@
import React from 'react';

// eslint-disable-next-line camelcase
export async function unstable_getServerProps ({ params }) {
return {
props: {
post: params.post,
comment: params.comment,
time: new Date().getTime(),
},
};
}

export default ({ post, comment, time }) => {
return (
<>
<p>Post: {post}</p>
<p>Comment: {comment}</p>
<span>time: {time}</span>
</>
);
};