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

Commit 87e4057

Browse files
committed
Refactor next-on-netlify: Break out into functions
Refactor the next-on-netlify.js file into several functions that are easier to manage. next-on-netlify now creates one Netlify Function per SSR page and API endpoint. This is probably better than bundling all pages into one function. Custom _redirects files are no longer merged into the next-on-netlify redirects. This is noted in the README and will be re-implemented, if demand arises.
1 parent 24d549f commit 87e4057

17 files changed

+366
-591
lines changed

README.md

+12-10
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ We're almost done! We just have to tell Netlify how to build our NextJS app, whe
6262
```toml
6363
[build]
6464
command = "npm run build"
65-
functions = "functions"
66-
publish = "out"
65+
functions = "out_functions"
66+
publish = "out_publish"
6767
```
6868

6969
We're done. Let's deploy 🚀🚀🚀
@@ -91,8 +91,8 @@ Then, add the following `[dev]` block to your `netlify.toml`:
9191
# ...
9292

9393
[dev]
94-
functions = "functions"
95-
publish = "out"
94+
functions = "out_functions"
95+
publish = "out_publish"
9696
# We manually set the framework to static, otherwise Netlify automatically
9797
# detects NextJS and redirects do not work.
9898
# Read more: https://github.com/netlify/cli/blob/master/docs/netlify-dev.md#project-detection
@@ -116,18 +116,20 @@ From now on, whenever you want to preview your application locally, just run:
116116
1. `npm run build`: This will run `next build` to build your NextJS app and `next-on-netlify` to prepare your NextJS app for compatibility with Netlify
117117
1. `netlify dev`: This will emulate Netlify on your computer and let you preview your app on `http://localhost:8888`.
118118

119-
#### Add Custom Redirects
119+
#### Custom Netlify Redirects
120120

121-
You can define custom redirects in a `_redirects` file in the `public/` folder. When you run `next-on-netlify`, your redirects will be merged with the redirects defined by `next-on-netlify`. Note that your redirects take priority.
121+
You can define custom redirects in the `netlify.toml` file.
122+
Routes defined by `next-on-netlify` take precedence over routes
123+
defined in `netlify.toml`.
122124

123-
Or you can define custom redirects in the `netlify.toml` file.
125+
In the past, it was possible to define custom redirects in a `_redirects` file. This is not possible anymore. Let me know if you have a need for this feature and we can add it back.
124126

125127
[Read more about Netlify redirects here](https://docs.netlify.com/routing/redirects/).
126128

129+
#### Custom Netlify Functions
127130

128-
## Limitations
129-
130-
next-on-netlify has only been tested on NextJS version 9 and above.
131+
`next-on-netlify` creates one Netlify Function for each of your
132+
SSR pages and API endpoints. It is currently not possible to create custom Netlify Functions. Let me know if you have a need for this feature and we can add it.
131133

132134
## Credits
133135

cypress/fixtures/netlify.toml

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
[build]
22
command = "npm run build"
3-
functions = "functions"
4-
publish = "out"
3+
functions = "out_functions"
4+
publish = "out_publish"
55

66
[dev]
77
# We manually set the framework to static, otherwise Netlify automatically
88
# detects NextJS and redirects do not work.
99
# Read more: https://github.com/netlify/cli/blob/master/docs/netlify-dev.md#project-detection
1010
framework = "#static"
11-
functions = "functions"
12-
publish = "out"
11+
functions = "out_functions"
12+
publish = "out_publish"

lib/collectNextjsPages.js

-50
This file was deleted.

lib/config.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const { join } = require('path')
2+
const getNextDistDir = require('./getNextDistDir')
3+
4+
5+
// This is where next-on-netlify will place all static files.
6+
// The publish key in netlify.toml should point to this folder.
7+
const NETLIFY_PUBLISH_PATH = join(".", "out_publish/")
8+
9+
// This is where next-on-netlify will place all Netlify Functions.
10+
// The functions key in netlify.toml should point to this folder.
11+
const NETLIFY_FUNCTIONS_PATH = join(".", "out_functions/")
12+
13+
// This is where static assets, such as images, can be placed. They will be
14+
// copied as-is to the Netlify publish folder.
15+
const PUBLIC_PATH = join(".", "public/")
16+
17+
// This is the file where NextJS can be configured
18+
const NEXT_CONFIG_PATH = join(".", "next.config.js")
19+
20+
// This is the folder that NextJS builds to; default is .next
21+
const NEXT_DIST_DIR = getNextDistDir({ nextConfigPath: NEXT_CONFIG_PATH })
22+
23+
// This is the Netlify Function template that wraps all SSR pages
24+
const FUNCTION_TEMPLATE_PATH = join(__dirname, "netlifyFunctionTemplate.js")
25+
26+
27+
module.exports = {
28+
NETLIFY_PUBLISH_PATH,
29+
NETLIFY_FUNCTIONS_PATH,
30+
PUBLIC_PATH,
31+
NEXT_CONFIG_PATH,
32+
NEXT_DIST_DIR,
33+
FUNCTION_TEMPLATE_PATH,
34+
}

lib/copyNextAssets.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const { join } = require('path')
2+
const { copySync } = require('fs-extra')
3+
const { NETLIFY_PUBLISH_PATH, NEXT_DIST_DIR } = require('./config')
4+
5+
// Copy the NextJS' static assets from NextJS distDir to Netlify publish folder.
6+
// These need to be available for NextJS to work.
7+
const copyNextAssets = () => {
8+
console.log(`\x1b[1m💼 Copying static NextJS assets to ${NETLIFY_PUBLISH_PATH}\x1b[22m`)
9+
copySync(
10+
join(NEXT_DIST_DIR, "static"),
11+
join(NETLIFY_PUBLISH_PATH, "_next", "static"),
12+
{
13+
overwrite: false,
14+
errorOnExist: true
15+
}
16+
)
17+
}
18+
19+
module.exports = copyNextAssets

lib/copyPublicFiles.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const { existsSync, copySync } = require('fs-extra')
2+
const { NETLIFY_PUBLISH_PATH, PUBLIC_PATH } = require('./config')
3+
4+
// Copy files from public folder to Netlify publish folder
5+
const copyPublicFiles = () => {
6+
// Abort if no public/ folder
7+
if(!existsSync(PUBLIC_PATH))
8+
return
9+
10+
// Perform copy operation
11+
console.log(`\x1b[1m🌍️ Copying ${PUBLIC_PATH} folder to ${NETLIFY_PUBLISH_PATH}\x1b[22m`)
12+
copySync(PUBLIC_PATH, NETLIFY_PUBLISH_PATH)
13+
}
14+
15+
module.exports = copyPublicFiles

lib/getNetlifyFunctionName.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const path = require('path')
2+
3+
// Generate the Netlify Function name for the given file path.
4+
// The file path will be pages/directory/subdirectory/[id].js
5+
// The function name will be next_directory_subdirectory_[id]
6+
// We do this because every function needs to be at the top-level, i.e.
7+
// nested functions are not allowed.
8+
const getNetlifyFunctionName = (filePath) => {
9+
10+
// Remove pages/ from file path:
11+
// pages/directory/page.js > directory/page.js
12+
const relativeFilePath = path.relative("pages", filePath)
13+
14+
// Extract directory path and file name without extension
15+
const { dir: directoryPath, name } = path.parse(relativeFilePath)
16+
17+
// Combine next, directory path, and file name:
18+
// next/directory/page
19+
const filePathWithoutExtension = path.join("next", directoryPath, name)
20+
21+
// Replace slashes with underscores:
22+
// next/directory/page > next_directory_page
23+
let functionName = filePathWithoutExtension.split(path.sep).join("_")
24+
25+
// Netlify Function names may not contain periods or square brackets.
26+
// To be safe, we keep only alphanumeric characters and underscores.
27+
// See: https://community.netlify.com/t/failed-to-upload-functions-file-function-too-large/3549/8
28+
functionName = functionName.replace(/[^\w\d]/g, "")
29+
30+
return functionName
31+
}
32+
33+
module.exports = getNetlifyFunctionName

lib/routerTemplate.js renamed to lib/netlifyFunctionTemplate.js

+7-38
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,13 @@
11
// TEMPLATE: This file will be copied to the Netlify functions directory when
2-
// running next-on-netlify/run.js
2+
// running next-on-netlify
33

4-
const compat = require("next-aws-lambda")
5-
const { routes } = require("./routes.json")
4+
// Compatibility wrapper for NextJS page
5+
const compat = require("next-aws-lambda")
6+
// Load the NextJS page
7+
const page = require("./nextJsPage")
68

7-
// We have to require all pages here so that Netlify's zip-it-and-ship-it
8-
// method will bundle them. That makes them available for our dynamic require
9-
// statement later.
10-
// We wrap this require in if(false) to make sure it is *not* executed when the
11-
// function runs.
12-
if (false) {
13-
require("./allPages")
14-
}
15-
16-
// Look through all routes and check each regex against the request URL
17-
const getRoute = path => {
18-
route = routes.find(({ regex }) => {
19-
const re = new RegExp(regex, "i");
20-
return re.test(path);
21-
})
22-
23-
// Return the route or the error page
24-
return route || { file: "pages/_error.js" }
25-
}
26-
27-
// There are some minor differences between Netlify and AWS, such as AWS having
28-
// support for multi-value headers.
29-
// next-aws-lambda is made for AWS, so we need to resolve those differences to
30-
// make everything work with Netlify.
9+
// next-aws-lambda is made for AWS. There are some minor differences between
10+
// Netlify and AWS which we resolve here.
3111
const callbackHandler = callback => (
3212
// The callbackHandler wraps the callback
3313
(argument, response) => {
@@ -57,17 +37,6 @@ exports.handler = (event, context, callback) => {
5737
const { path } = event
5838
console.log("[request]", path)
5939

60-
// Identify the file to render
61-
const { file } = getRoute(path)
62-
console.log("[render] ", file)
63-
64-
// Load the page to render
65-
// Do not do this: const page = require(`./${file}`)
66-
// Otherwise, Netlify's zip-it-and-ship-it will attempt to bundle "./"
67-
// into the function's zip folder and the build will fail
68-
const pathToFile = `./${file}`
69-
const page = require(pathToFile)
70-
7140
// Render the page
7241
compat(page)(
7342
{

lib/prepareFolders.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const { emptyDirSync } = require('fs-extra')
2+
const { NETLIFY_PUBLISH_PATH, NETLIFY_FUNCTIONS_PATH } = require('./config')
3+
4+
// Empty existing publish and functions folders
5+
const prepareFolders = () => {
6+
console.log("\x1b[1m🚀 Next on Netlify 🚀\x1b[22m")
7+
console.log(" Functions directory:", NETLIFY_PUBLISH_PATH)
8+
console.log(" Publish directory: ", NETLIFY_FUNCTIONS_PATH)
9+
console.log(" Make sure these are set in your netlify.toml file.")
10+
11+
emptyDirSync(NETLIFY_PUBLISH_PATH)
12+
emptyDirSync(NETLIFY_FUNCTIONS_PATH)
13+
}
14+
15+
module.exports = prepareFolders

lib/readPagesManifest.js

-47
This file was deleted.

lib/setupHtmlPages.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const path = require('path')
2+
const { join } = path
3+
const { copySync, existsSync, readJSONSync } = require('fs-extra')
4+
const { NEXT_DIST_DIR, NETLIFY_PUBLISH_PATH } = require('./config')
5+
6+
// Identify all pages that have been pre-rendered and copy each one to the
7+
// Netlify publish directory.
8+
const setupHtmlPages = () => {
9+
console.log(`\x1b[1m🔥 Writing pre-rendered HTML pages to ${NETLIFY_PUBLISH_PATH}\x1b[22m`)
10+
11+
// Read pages manifest that tells us which pages exist
12+
const pagesManifest = readJSONSync(
13+
join(NEXT_DIST_DIR, "serverless", "pages-manifest.json")
14+
)
15+
16+
// Eliminate duplicates from pages manifest
17+
const files = Object.values(pagesManifest)
18+
const uniqueFiles = [...new Set(files)]
19+
20+
// Identify HTML pages
21+
const htmlFiles = uniqueFiles.filter(file => file.endsWith(".html"))
22+
23+
// Copy each page to the Netlify publish directory
24+
htmlFiles.forEach(file => {
25+
console.log(" ", file)
26+
27+
// The path to the file, relative to the pages directory
28+
const relativePath = path.relative("pages", file)
29+
30+
copySync(
31+
join(NEXT_DIST_DIR, "serverless", "pages",relativePath),
32+
join(NETLIFY_PUBLISH_PATH, relativePath),
33+
{
34+
overwrite: false,
35+
errorOnExist: true
36+
}
37+
)
38+
})
39+
40+
// Copy 404.html to directory root
41+
// This is a temporary workaround. Currenly, Netlify (deployed) expects the
42+
// 404.html file in the publish folder, while netlify-cli dev expects it at
43+
// the directory root. In order to cover both cases, we copy the 404.html to
44+
// both locations until this is fixed.
45+
if(existsSync(join(NETLIFY_PUBLISH_PATH, "404.html")))
46+
copySync(join(NETLIFY_PUBLISH_PATH, "404.html"), join(".", "404.html"))
47+
}
48+
49+
module.exports = setupHtmlPages

0 commit comments

Comments
 (0)