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

Commit 5f2134f

Browse files
committed
Allow the main index route to be ssg rendered with getStaticProps
Previously, the / route having associated data would cause the build to fail as it would attempt to reference an /.html file instead of /index.html This also fixes a stability issue with the snapshot test for _redirects, which are not necessarily generated in a stable order.
1 parent 9bf3e9f commit 5f2134f

File tree

9 files changed

+188
-20
lines changed

9 files changed

+188
-20
lines changed

lib/allNextJsPages.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ const getAllPages = () => {
3434
return
3535

3636
// Skip page if it is actually an SSG page
37-
if(route in staticSsgPages || route in dynamicSsgPages)
37+
const normalizedRoute = route === "/index" ? "/" : route
38+
if(normalizedRoute in staticSsgPages || normalizedRoute in dynamicSsgPages)
3839
return
3940

4041
// Check if we already have a page pointing to this file
@@ -62,7 +63,7 @@ const getAllPages = () => {
6263

6364
// Parse SSG pages
6465
Object.entries(staticSsgPages).forEach(([ route, { dataRoute }]) => {
65-
pages.push(new Page({ route, type: "ssg", dataRoute }))
66+
pages.push(new Page({ route, type: "ssg", dataRoute, alternativeRoutes: route === "/" ? ["/index"] : [] }))
6667
})
6768
Object.entries(dynamicSsgPages).forEach(([ route, { dataRoute, fallback }]) => {
6869
// Ignore pages without fallback, these are already handled by the
@@ -100,6 +101,18 @@ class Page {
100101
return this.type === "ssg-fallback"
101102
}
102103

104+
routeFile(ext) {
105+
return `${this.route.replace(/^\/$/, '/index')}${ext}`
106+
}
107+
108+
get htmlFile() {
109+
return this.routeFile(".html")
110+
}
111+
112+
get jsonFile() {
113+
return this.routeFile(".json")
114+
}
115+
103116
// Return route and alternative routes as array
104117
get routesAsArray() {
105118
return [this.route, ...this.alternativeRoutes]

lib/setupRedirects.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const setupRedirects = () => {
4242
}
4343
// SSG pages
4444
else if (page.isSsg()) {
45-
to = `${page.route}.html`
45+
to = page.htmlFile
4646
}
4747
// SSG fallback pages (for non pre-rendered paths)
4848
else if (page.isSsgFallback()) {

lib/setupSsgPages.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ const setupSsgPages = () => {
1818
// Copy pre-rendered SSG pages to Netlify publish folder
1919
console.log(" ", "1. Copying pre-rendered SSG pages to", NETLIFY_PUBLISH_PATH)
2020

21-
ssgPages.forEach(({ route }) => {
22-
const filePath = join("pages", `${route}.html`)
21+
ssgPages.forEach(({ htmlFile }) => {
22+
const filePath = join("pages", htmlFile)
2323
console.log(" ", " ", filePath)
2424

2525
copySync(
2626
join(NEXT_DIST_DIR, "serverless", filePath),
27-
join(NETLIFY_PUBLISH_PATH, `${route}.html`),
27+
join(NETLIFY_PUBLISH_PATH, htmlFile),
2828
{
2929
overwrite: false,
3030
errorOnExist: true
@@ -36,8 +36,8 @@ const setupSsgPages = () => {
3636
const nextDataFolder = join(NETLIFY_PUBLISH_PATH, "_next", "data/")
3737
console.log(" ", "2. Copying SSG page data to", nextDataFolder)
3838

39-
ssgPages.forEach(({ route, dataRoute }) => {
40-
const dataPath = join("pages", `${route}.json`)
39+
ssgPages.forEach(({ jsonFile, dataRoute }) => {
40+
const dataPath = join("pages", jsonFile)
4141
console.log(" ", " ", dataPath)
4242

4343
copySync(

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/__snapshots__/defaults.test.js.snap

+11-11
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@
22

33
exports[`Routing creates Netlify redirects 1`] = `
44
"# Next-on-Netlify Redirects
5-
/api/static /.netlify/functions/next_api_static 200
6-
/getServerSideProps/static /.netlify/functions/next_getServerSideProps_static 200
7-
/_next/data/%BUILD_ID%/getServerSideProps/static.json /.netlify/functions/next_getServerSideProps_static 200
8-
/index /.netlify/functions/next_index 200
95
/ /.netlify/functions/next_index 200
10-
/static /static.html 200
116
/404 /404.html 200
7+
/_next/data/%BUILD_ID%/getServerSideProps/:id.json /.netlify/functions/next_getServerSideProps_id 200
8+
/_next/data/%BUILD_ID%/getServerSideProps/static.json /.netlify/functions/next_getServerSideProps_static 200
9+
/_next/data/%BUILD_ID%/getStaticProps/withFallback/:id.json /.netlify/functions/next_getStaticProps_withFallback_id 200
10+
/api/shows/* /.netlify/functions/next_api_shows_params 200
11+
/api/shows/:id /.netlify/functions/next_api_shows_id 200
12+
/api/static /.netlify/functions/next_api_static 200
13+
/getServerSideProps/:id /.netlify/functions/next_getServerSideProps_id 200
14+
/getServerSideProps/static /.netlify/functions/next_getServerSideProps_static 200
1215
/getStaticProps/1 /getStaticProps/1.html 200
1316
/getStaticProps/2 /getStaticProps/2.html 200
1417
/getStaticProps/static /getStaticProps/static.html 200
1518
/getStaticProps/withFallback/3 /getStaticProps/withFallback/3.html 200
1619
/getStaticProps/withFallback/4 /getStaticProps/withFallback/4.html 200
17-
/api/shows/:id /.netlify/functions/next_api_shows_id 200
18-
/api/shows/* /.netlify/functions/next_api_shows_params 200
19-
/getServerSideProps/:id /.netlify/functions/next_getServerSideProps_id 200
20-
/_next/data/%BUILD_ID%/getServerSideProps/:id.json /.netlify/functions/next_getServerSideProps_id 200
2120
/getStaticProps/withFallback/:id /.netlify/functions/next_getStaticProps_withFallback_id 200
22-
/_next/data/%BUILD_ID%/getStaticProps/withFallback/:id.json /.netlify/functions/next_getStaticProps_withFallback_id 200
23-
/shows/:id /.netlify/functions/next_shows_id 200
21+
/index /.netlify/functions/next_index 200
2422
/shows/* /.netlify/functions/next_shows_params 200
23+
/shows/:id /.netlify/functions/next_shows_id 200
24+
/static /static.html 200
2525
/static/:id /static/[id].html 200"
2626
`;

tests/defaults.test.js

+3
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ describe('Routing',() => {
172172
// Replace non-persistent build ID with placeholder
173173
redirects = redirects.replace(/\/_next\/data\/[^\/]+\//g, "/_next/data/%BUILD_ID%/")
174174

175+
// Sort contents for a stable comparison
176+
redirects = redirects.split(/\n/).sort().join("\n")
177+
175178
// Check that redirects match
176179
expect(redirects).toMatchSnapshot()
177180
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Link from 'next/link'
2+
3+
const Page = ({ now }) => (
4+
<div>
5+
<h1>Date.now(): {now}</h1>
6+
7+
<Link href="/static">
8+
<a>Static page</a>
9+
</Link>
10+
</div>
11+
)
12+
13+
export async function getStaticProps(context) {
14+
return {
15+
props: {
16+
now: Date.now()
17+
}
18+
}
19+
}
20+
21+
export default Page
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Link from 'next/link'
2+
3+
const Page = ({ now }) => (
4+
<div>
5+
<h1>Date.now(): {now}</h1>
6+
7+
<Link href="/">
8+
<a>Index page</a>
9+
</Link>
10+
</div>
11+
)
12+
13+
export async function getStaticProps(context) {
14+
return {
15+
props: {
16+
now: Date.now()
17+
}
18+
}
19+
}
20+
21+
export default Page

tests/staticIndexPages.test.js

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Test that next-on-netlify does not crash when pre-rendering index.js file
2+
// with getStaticProps.
3+
4+
const { parse, join } = require('path')
5+
const { copySync, emptyDirSync, existsSync,
6+
readdirSync, readFileSync, readJsonSync } = require('fs-extra')
7+
const npmRunBuild = require("./helpers/npmRunBuild")
8+
9+
// The name of this test file (without extension)
10+
const FILENAME = parse(__filename).name
11+
12+
// The directory which will be used for testing.
13+
// We simulate a NextJS app within that directory, with pages, and a
14+
// package.json file.
15+
const PROJECT_PATH = join(__dirname, "builds", FILENAME)
16+
17+
// The directory that contains the fixtures, such as NextJS pages,
18+
// NextJS config, and package.json
19+
const FIXTURE_PATH = join(__dirname, "fixtures")
20+
21+
// Capture the output of `npm run build` to verify successful build
22+
let BUILD_OUTPUT
23+
24+
beforeAll(
25+
async () => {
26+
// Clear project directory
27+
emptyDirSync(PROJECT_PATH)
28+
emptyDirSync(join(PROJECT_PATH, "pages"))
29+
30+
// Copy NextJS pages and config
31+
copySync(
32+
join(FIXTURE_PATH, "pages-with-static-props-index"),
33+
join(PROJECT_PATH, "pages")
34+
)
35+
copySync(
36+
join(FIXTURE_PATH, "next.config.js"),
37+
join(PROJECT_PATH, "next.config.js")
38+
)
39+
40+
// Copy package.json
41+
copySync(
42+
join(FIXTURE_PATH, "package.json"),
43+
join(PROJECT_PATH, "package.json")
44+
)
45+
46+
// Invoke `npm run build`: Build Next and run next-on-netlify
47+
const { stdout } = await npmRunBuild({ directory: PROJECT_PATH })
48+
BUILD_OUTPUT = stdout
49+
},
50+
// time out after 180 seconds
51+
180 * 1000
52+
)
53+
54+
describe('Next', () => {
55+
test('builds successfully', () => {
56+
// NextJS output
57+
expect(BUILD_OUTPUT).toMatch("Creating an optimized production build...")
58+
expect(BUILD_OUTPUT).toMatch("Automatically optimizing pages...")
59+
expect(BUILD_OUTPUT).toMatch("First Load JS shared by all")
60+
61+
// Next on Netlify output
62+
expect(BUILD_OUTPUT).toMatch("Next on Netlify")
63+
expect(BUILD_OUTPUT).toMatch("Success! All done!")
64+
})
65+
})
66+
67+
describe('Static Pages', () => {
68+
test('copies static pages to output directory', () => {
69+
const OUTPUT_PATH = join(PROJECT_PATH, "out_publish")
70+
71+
expect(existsSync(join(OUTPUT_PATH, "index.html"))).toBe(true)
72+
expect(existsSync(join(OUTPUT_PATH, "static.html"))).toBe(true)
73+
})
74+
75+
test('copies static assets to out_publish/_next/ directory', () => {
76+
const dirs = readdirSync(join(PROJECT_PATH, "out_publish", "_next", "static"))
77+
78+
expect(dirs.length).toBe(3)
79+
expect(dirs).toContain("chunks")
80+
expect(dirs).toContain("runtime")
81+
})
82+
})
83+
84+
describe('404 Page', () => {
85+
test('copies 404.html to output directory', () => {
86+
const OUTPUT_PATH = join(PROJECT_PATH, "out_publish")
87+
88+
expect(existsSync(join(OUTPUT_PATH, "404.html"))).toBe(true)
89+
})
90+
91+
// This is required for 404.html to work on netlify-dev
92+
test('copies 404.html to directory root', () => {
93+
expect(existsSync(join(PROJECT_PATH, "404.html"))).toBe(true)
94+
})
95+
})
96+
97+
describe('Routing',() => {
98+
test('creates Netlify redirects', async () => {
99+
// Read _redirects file
100+
const contents = readFileSync(join(PROJECT_PATH, "out_publish", "_redirects"))
101+
102+
// Convert contents into an array, each line being one element
103+
const redirects = contents.toString().split("\n")
104+
105+
// Check that routes are present
106+
expect(redirects).toContain("/ /index.html 200")
107+
expect(redirects).toContain("/index /index.html 200")
108+
expect(redirects).toContain("/static /static.html 200")
109+
})
110+
})

0 commit comments

Comments
 (0)