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

Commit b4c85b9

Browse files
lindsaylevinejlengstorf
andauthoredJan 13, 2021
initial support for next/image on netlify (#138)
* initial support for next/image on netlify * copy change to next/image caveat in readme Co-authored-by: Jason Lengstorf <[email protected]> Co-authored-by: Jason Lengstorf <[email protected]>
1 parent 853983b commit b4c85b9

14 files changed

+616
-28
lines changed
 

‎README.md

+6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ The plugin can be found on [npm here](https://www.npmjs.com/package/@netlify/plu
5050
- [Using Netlify Identity](#using-netlify-identity)
5151
- [Caveats](#caveats)
5252
- [Fallbacks for Pages with `getStaticPaths`](#fallbacks-for-pages-with-getstaticpaths)
53+
- [next/image](#next/image)
5354
- [Credits](#credits)
5455
- [Showcase](#showcase)
5556

@@ -256,6 +257,11 @@ With `next-on-netlify`, when navigating to a path that is not defined in `getSta
256257

257258
For more on this, see: [Issue #7](https://github.com/netlify/next-on-netlify/issues/7#issuecomment-636883539)
258259

260+
### next/image
261+
262+
Our existing solution for next/image is not very performant. We have performance improvements on our roadmap, dependent on internal work.
263+
264+
To get better performance now, we recommend using a cloud provider like Cloudinary ([see the Next.js docs](https://nextjs.org/docs/basic-features/image-optimization#loader)).
259265

260266
## Credits
261267

‎index.js

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const prepareFolders = require("./lib/steps/prepareFolders");
22
const copyPublicFiles = require("./lib/steps/copyPublicFiles");
33
const copyNextAssets = require("./lib/steps/copyNextAssets");
44
const setupPages = require("./lib/steps/setupPages");
5+
const setupImageFunction = require("./lib/steps/setupImageFunction");
56
const setupRedirects = require("./lib/steps/setupRedirects");
67
const {
78
NETLIFY_PUBLISH_PATH,
@@ -26,6 +27,8 @@ const nextOnNetlify = (options = {}) => {
2627

2728
setupPages({ functionsPath, publishPath });
2829

30+
setupImageFunction(functionsPath);
31+
2932
setupRedirects(publishPath);
3033
};
3134

‎lib/config.js

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ const FUNCTION_TEMPLATE_PATH = join(TEMPLATES_DIR, "netlifyFunction.js");
2828
// This is the file where custom redirects can be configured
2929
const CUSTOM_REDIRECTS_PATH = join(".", "_redirects");
3030

31+
// This is the name used for copying our imageFunction template and for
32+
// creating the next/image redirect
33+
const NEXT_IMAGE_FUNCTION_NAME = "next_image";
34+
3135
module.exports = {
3236
NETLIFY_PUBLISH_PATH,
3337
NETLIFY_FUNCTIONS_PATH,
@@ -37,4 +41,5 @@ module.exports = {
3741
TEMPLATES_DIR,
3842
FUNCTION_TEMPLATE_PATH,
3943
CUSTOM_REDIRECTS_PATH,
44+
NEXT_IMAGE_FUNCTION_NAME,
4045
};

‎lib/steps/setupImageFunction.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const { copySync } = require("fs-extra");
2+
const { join } = require("path");
3+
const { NEXT_IMAGE_FUNCTION_NAME, TEMPLATES_DIR } = require("../config");
4+
5+
// Move our next/image function into the correct functions directory
6+
const setupImageFunction = (functionsPath) => {
7+
const functionName = `${NEXT_IMAGE_FUNCTION_NAME}.js`;
8+
const functionDirectory = join(functionsPath, functionName);
9+
10+
copySync(join(TEMPLATES_DIR, "imageFunction.js"), functionDirectory, {
11+
overwrite: false,
12+
errorOnExist: true,
13+
});
14+
};
15+
16+
module.exports = setupImageFunction;

‎lib/steps/setupRedirects.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
const { join } = require("path");
22
const { existsSync, readFileSync, writeFileSync } = require("fs-extra");
33
const { logTitle, logItem } = require("../helpers/logger");
4-
const { CUSTOM_REDIRECTS_PATH } = require("../config");
4+
const {
5+
CUSTOM_REDIRECTS_PATH,
6+
NEXT_IMAGE_FUNCTION_NAME,
7+
} = require("../config");
58
const getSortedRoutes = require("../helpers/getSortedRoutes");
69
const getNetlifyRoutes = require("../helpers/getNetlifyRoutes");
710
const isRootCatchAllRedirect = require("../helpers/isRootCatchAllRedirect");
@@ -29,8 +32,14 @@ const setupRedirects = (publishPath) => {
2932
...require("../pages/withoutProps/redirects"),
3033
];
3134

35+
// Add next/image redirect to our image function
36+
nextRedirects.push({
37+
route: "/_next/image* url=:url w=:width q=:quality",
38+
target: `/.netlify/functions/${NEXT_IMAGE_FUNCTION_NAME}?url=:url&w=:width&q=:quality`,
39+
});
40+
3241
// Add _redirect section heading
33-
if (nextRedirects.length >= 1) redirects.push("# Next-on-Netlify Redirects");
42+
redirects.push("# Next-on-Netlify Redirects");
3443

3544
// Sort routes: More-specific routes (e.g., static routing) precede
3645
// less-specific routes (e.g., catch-all)

‎lib/templates/imageFunction.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const jimp = require("jimp");
2+
3+
// Function used to mimic next/image and sharp
4+
exports.handler = async (event) => {
5+
const { url, w = 500, q = 75 } = event.queryStringParameters;
6+
const width = parseInt(w);
7+
const quality = parseInt(q);
8+
9+
const imageUrl = url.startsWith("/")
10+
? `${process.env.URL || "http://localhost:8888"}${url}`
11+
: url;
12+
const image = await jimp.read(imageUrl);
13+
14+
image.resize(width, jimp.AUTO).quality(quality);
15+
16+
const imageBuffer = await image.getBufferAsync(image.getMIME());
17+
18+
return {
19+
statusCode: 200,
20+
headers: {
21+
"Content-Type": image.getMIME(),
22+
},
23+
body: imageBuffer.toString("base64"),
24+
isBase64Encoded: true,
25+
};
26+
};

‎package-lock.json

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

‎package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@
5353
"dependencies": {
5454
"@sls-next/lambda-at-edge": "^1.5.2",
5555
"commander": "^6.0.0",
56-
"fs-extra": "^9.0.1"
56+
"fs-extra": "^9.0.1",
57+
"jimp": "^0.16.1"
5758
},
5859
"husky": {
5960
"hooks": {

‎tests/__snapshots__/defaults.test.js.snap

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ exports[`Routing creates Netlify redirects 1`] = `
2323
/_next/data/%BUILD_ID%/getStaticProps/withRevalidate/1.json /.netlify/functions/next_getStaticProps_withRevalidate_id 200
2424
/_next/data/%BUILD_ID%/getStaticProps/withRevalidate/2.json /.netlify/functions/next_getStaticProps_withRevalidate_id 200
2525
/_next/data/%BUILD_ID%/getStaticProps/withRevalidate/withFallback/:id.json /.netlify/functions/next_getStaticProps_withRevalidate_withFallback_id 200
26+
/_next/image* url=:url w=:width q=:quality /.netlify/functions/next_image?url=:url&w=:width&q=:quality 200
2627
/api/shows/:id /.netlify/functions/next_api_shows_id 200
2728
/api/shows/:params/* /.netlify/functions/next_api_shows_params 200
2829
/api/static /.netlify/functions/next_api_static 200

‎tests/__snapshots__/i18n.test.js.snap

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ exports[`Routing creates Netlify redirects 1`] = `
4848
/_next/data/%BUILD_ID%/getStaticProps/withFallback/:slug/* /.netlify/functions/next_getStaticProps_withFallback_slug 200
4949
/_next/data/%BUILD_ID%/getStaticProps/withFallbackBlocking/:id.json /.netlify/functions/next_getStaticProps_withFallbackBlocking_id 200
5050
/_next/data/%BUILD_ID%/getStaticProps/withRevalidate/withFallback/:id.json /.netlify/functions/next_getStaticProps_withRevalidate_withFallback_id 200
51+
/_next/image* url=:url w=:width q=:quality /.netlify/functions/next_image?url=:url&w=:width&q=:quality 200
5152
/api/shows/:id /.netlify/functions/next_api_shows_id 200
5253
/api/shows/:params/* /.netlify/functions/next_api_shows_params 200
5354
/api/static /.netlify/functions/next_api_static 200

‎tests/__snapshots__/optionalCatchAll.test.js.snap

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ exports[`Routing creates Netlify redirects 1`] = `
55
/_next/data/%BUILD_ID%/page.json /.netlify/functions/next_page 200
66
/_next/data/%BUILD_ID%/index.json /.netlify/functions/next_all 200
77
/_next/data/%BUILD_ID%/* /.netlify/functions/next_all 200
8+
/_next/image* url=:url w=:width q=:quality /.netlify/functions/next_image?url=:url&w=:width&q=:quality 200
89
/page /.netlify/functions/next_page 200
910
/ /.netlify/functions/next_all 200
1011
/_next/* /_next/:splat 200

‎tests/defaults.test.js

+8
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,14 @@ describe("API Pages", () => {
132132
});
133133
});
134134

135+
describe("next/image", () => {
136+
const functionsDir = join(PROJECT_PATH, "out_functions");
137+
138+
test("sets up next_image as a function in every project by default", () => {
139+
expect(existsSync(join(functionsDir, "next_image.js"))).toBe(true);
140+
});
141+
});
142+
135143
describe("SSG Pages with getStaticProps", () => {
136144
test("creates pre-rendered HTML file in output directory", () => {
137145
const OUTPUT_PATH = join(PROJECT_PATH, "out_publish");

‎tests/preRenderedIndexPages.test.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,17 @@ describe("404 Page", () => {
6363
});
6464

6565
describe("Routing", () => {
66-
test("does not create any redirects", async () => {
66+
test("adds next_image redirect only", async () => {
6767
// Read _redirects file
6868
const contents = readFileSync(
6969
join(PROJECT_PATH, "out_publish", "_redirects")
7070
);
71-
const redirects = contents.toString();
71+
const redirects = contents.toString().trim().split(/\n/);
7272

7373
// Check that no redirects are present
74-
expect(redirects).toEqual("");
74+
expect(redirects[0]).toEqual("# Next-on-Netlify Redirects");
75+
expect(redirects[1]).toEqual(
76+
"/_next/image* url=:url w=:width q=:quality /.netlify/functions/next_image?url=:url&w=:width&q=:quality 200"
77+
);
7578
});
7679
});

‎tests/staticIndexPages.test.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ describe("Routing", () => {
8787
expect(redirects[3]).toEqual(
8888
"/_next/data/%BUILD_ID%/static.json /.netlify/functions/next_static 200! Cookie=__prerender_bypass,__next_preview_data"
8989
);
90-
expect(redirects[4]).toEqual(
90+
// [4] is the next_image redirect
91+
expect(redirects[5]).toEqual(
9192
"/static /.netlify/functions/next_static 200! Cookie=__prerender_bypass,__next_preview_data"
9293
);
9394
});

0 commit comments

Comments
 (0)
This repository has been archived.