Skip to content
This repository has been archived by the owner on May 10, 2021. It is now read-only.

Commit

Permalink
Support for SSG preview mode (#50)
Browse files Browse the repository at this point in the history
* add support for preview mode of pre-rendered pages using getStaticProps

* remove README caveat

* Add Cypress tests: Optional catch-all route at root-level

Test that prerendered pages and static files from /public are correctly 
served under the redirect engine used with cookie-based redirects when 
having an optional catch-all route at root-level.

* Cleanup: Remove unnecessary require

We do not need `existsSync` in the `setupNetlifyFunctionForPage` helper.

* miscellaneous finishing touches for ssg preview mode

* move preview mode note in readme and update isroutewithfallback helper

Co-authored-by: Finn Woelm <[email protected]>
  • Loading branch information
lindsaylevine and FinnWoelm authored Oct 27, 2020
1 parent 2ee0f0d commit c23aee5
Show file tree
Hide file tree
Showing 25 changed files with 470 additions and 43 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ jspm_packages

# OS
.DS_Store

# Local Netlify folder
.netlify
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
- [Custom Netlify Redirects](#custom-netlify-redirects)
- [Custom Netlify Functions](#custom-netlify-functions)
- [Caveats](#caveats)
- [Preview Mode](#preview-mode)
- [Fallbacks for Pages with `getStaticPaths`](#fallbacks-for-pages-with-getstaticpaths)
- [Credits](#credits)
- [Showcase](#showcase)
Expand Down Expand Up @@ -157,6 +156,12 @@ From now on, whenever you want to preview your application locally, just run:
1. `npm run build`: This will run `next build` to build your Next.js app and `next-on-netlify` to prepare your Next.js app for compatibility with Netlify
1. `netlify dev`: This will emulate Netlify on your computer and let you preview your app on `http://localhost:8888`.

*Note:*

Preview Mode is not yet available locally, running `netlify dev`, for static pages without revalidate or fallback. This will be supported soon.

For now, Preview Mode *is* supported in production for all Next.js page types.

#### Custom Netlify Redirects

You can define custom redirects in a `_redirects` and/or in your `netlify.toml` file.
Expand All @@ -175,10 +180,6 @@ SSR pages and API endpoints. It is currently not possible to create custom Netli

## Caveats

### Preview Mode

[Next.js Preview Mode](https://nextjs.org/docs/advanced-features/preview-mode) does not work on pages that are pre-rendered (pages with `getStaticProps`). Netlify currently does not support cookie-based redirects, which are needed for supporting preview mode on pre-rendered pages. Preview mode works correctly on any server-side-rendered pages (pages with `getInitialProps` or `getServerSideProps`). See: [Issue #10](https://github.com/netlify/next-on-netlify/issues/10)

### Fallbacks for Pages with `getStaticPaths`

[Fallback pages](https://nextjs.org/docs/basic-features/data-fetching#fallback-true) behave differently with `next-on-netlify` than they do with Next.js. On Next.js, when navigating to a path that is not defined in `getStaticPaths`, it first displays the fallback page. Next.js then generates the HTML in the background and caches it for future requests.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const Page = () => <p>root-level optional-catch-all</p>;

export async function getServerSideProps() {
return {
props: {},
};
}

export default Page;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const Static = () => <p>static page</p>;

export async function getStaticProps() {
return {
props: {},
};
}

export default Static;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const Static = () => <p>static page in subfolder</p>;

export async function getStaticPaths() {
return {
paths: [
{
params: {
id: "static",
},
},
{
params: {
id: "test",
},
},
],
fallback: true,
};
}

export async function getStaticProps() {
return {
props: {},
};
}

export default Static;
12 changes: 12 additions & 0 deletions cypress/fixtures/pages/api/enterPreviewStatic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default async function preview(req, res) {
const { query } = req;
const { id } = query;

// Enable Preview Mode by setting the cookies
res.setPreviewData({});

// Redirect to the path from the fetched post
// We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities
res.writeHead(307, { Location: `/previewTest/static` });
res.end();
}
5 changes: 5 additions & 0 deletions cypress/fixtures/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ const Index = ({ shows }) => (
<a>previewTest/222</a>
</Link>
</li>
<li>
<Link href="/previewTest/static">
<a>previewTest/static</a>
</Link>
</li>
</ul>

<h1>6. Static Pages Stay Static</h1>
Expand Down
45 changes: 45 additions & 0 deletions cypress/fixtures/pages/previewTest/static.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Link from "next/link";

const StaticTest = ({ number }) => {
return (
<div>
<p>
This page uses getStaticProps() and is SSRed when in preview mode.
<br />
<br />
By default, it shows the TV show by ID (as static HTML).
<br />
But when in preview mode, it shows person by ID instead (SSRed).
</p>

<hr />

<h1>Number: {number}</h1>

<Link href="/">
<a>Go back home</a>
</Link>
</div>
);
};

export const getStaticProps = async ({ preview }) => {
let number;

// In preview mode, use odd number
if (preview) {
number = 3;
}
// In normal mode, use even number
else {
number = 4;
}

return {
props: {
number,
},
};
};

export default StaticTest;
1 change: 1 addition & 0 deletions cypress/fixtures/public/file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a text file
1 change: 1 addition & 0 deletions cypress/fixtures/public/folder/file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a text file in a folder
1 change: 1 addition & 0 deletions cypress/fixtures/public/subfolder/file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a text file in a subfolder
70 changes: 67 additions & 3 deletions cypress/integration/default_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -529,12 +529,18 @@ describe("API endpoint", () => {
});

describe("Preview Mode", () => {
it("redirects to preview test page", () => {
it("redirects to preview test page with dynamic route", () => {
cy.visit("/api/enterPreview?id=999");

cy.url().should("include", "/previewTest/999");
});

it("redirects to static preview test page", () => {
cy.visit("/api/enterPreviewStatic");

cy.url().should("include", "/previewTest/static");
});

it("sets cookies on client", () => {
Cypress.Cookies.debug(true);
cy.getCookie("__prerender_bypass").should("not.exist");
Expand All @@ -546,7 +552,18 @@ describe("Preview Mode", () => {
cy.getCookie("__next_preview_data").should("not.be", null);
});

it("renders page in preview mode", () => {
it("sets cookies on client with static redirect", () => {
Cypress.Cookies.debug(true);
cy.getCookie("__prerender_bypass").should("not.exist");
cy.getCookie("__next_preview_data").should("not.exist");

cy.visit("/api/enterPreviewStatic");

cy.getCookie("__prerender_bypass").should("not.be", null);
cy.getCookie("__next_preview_data").should("not.be", null);
});

it("renders serverSideProps page in preview mode", () => {
cy.visit("/api/enterPreview?id=999");

if (Cypress.env("DEPLOY") === "local") {
Expand All @@ -557,7 +574,15 @@ describe("Preview Mode", () => {
cy.get("p").should("contain", "Sebastian Lacause");
});

it("can move in and out of preview mode", () => {
it("renders staticProps page in preview mode", () => {
// cypress local (aka netlify dev) doesn't support cookie-based redirects
if (Cypress.env("DEPLOY") !== "local") {
cy.visit("/api/enterPreviewStatic");
cy.get("h1").should("contain", "Number: 3");
}
});

it("can move in and out of preview mode for SSRed page", () => {
cy.visit("/api/enterPreview?id=999");

if (Cypress.env("DEPLOY") === "local") {
Expand All @@ -582,6 +607,45 @@ describe("Preview Mode", () => {
cy.get("h1").should("contain", "Show #222");
cy.get("p").should("contain", "Happyland");
});

it("can move in and out of preview mode for static page", () => {
if (Cypress.env("DEPLOY") !== "local") {
cy.visit("/api/enterPreviewStatic");
cy.window().then((w) => (w.noReload = true));

cy.get("h1").should("contain", "Number: 3");

cy.contains("Go back home").click();

// Verify that we're still in preview mode
cy.contains("previewTest/static").click();
cy.get("h1").should("contain", "Number: 3");

cy.window().should("have.property", "noReload", true);

// Exit preview mode
cy.visit("/api/exitPreview");

// TO-DO: test if this is the static html?
// Verify that we're no longer in preview mode
cy.contains("previewTest/static").click();
cy.get("h1").should("contain", "Number: 4");
}
});

it("hits the prerendered html out of preview mode and netlify function in preview mode", () => {
if (Cypress.env("DEPLOY") !== "local") {
cy.request("/previewTest/static").then((response) => {
expect(response.headers["cache-control"]).to.include("public");
});

cy.visit("/api/enterPreviewStatic");

cy.request("/previewTest/static").then((response) => {
expect(response.headers["cache-control"]).to.include("private");
});
}
});
});

describe("pre-rendered HTML pages", () => {
Expand Down
Loading

0 comments on commit c23aee5

Please sign in to comment.