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

initial support for next/image on netlify #138

Merged
merged 2 commits into from
Jan 13, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ The plugin can be found on [npm here](https://www.npmjs.com/package/@netlify/plu
- [Using Netlify Identity](#using-netlify-identity)
- [Caveats](#caveats)
- [Fallbacks for Pages with `getStaticPaths`](#fallbacks-for-pages-with-getstaticpaths)
- [next/image](#next/image)
- [Credits](#credits)
- [Showcase](#showcase)

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

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

### next/image

Our existing solution for next/image is not very performant. We have performance improvements on our roadmap, dependent on internal work.

In the meantime, we recommend using a cloud provider like Cloudinary ([see the Next.js docs](https://nextjs.org/docs/basic-features/image-optimization#loader)).

## Credits

Expand Down
3 changes: 3 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const prepareFolders = require("./lib/steps/prepareFolders");
const copyPublicFiles = require("./lib/steps/copyPublicFiles");
const copyNextAssets = require("./lib/steps/copyNextAssets");
const setupPages = require("./lib/steps/setupPages");
const setupImageFunction = require("./lib/steps/setupImageFunction");
const setupRedirects = require("./lib/steps/setupRedirects");
const {
NETLIFY_PUBLISH_PATH,
Expand All @@ -26,6 +27,8 @@ const nextOnNetlify = (options = {}) => {

setupPages({ functionsPath, publishPath });

setupImageFunction(functionsPath);

setupRedirects(publishPath);
};

Expand Down
5 changes: 5 additions & 0 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ const FUNCTION_TEMPLATE_PATH = join(TEMPLATES_DIR, "netlifyFunction.js");
// This is the file where custom redirects can be configured
const CUSTOM_REDIRECTS_PATH = join(".", "_redirects");

// This is the name used for copying our imageFunction template and for
// creating the next/image redirect
const NEXT_IMAGE_FUNCTION_NAME = "next_image";

module.exports = {
NETLIFY_PUBLISH_PATH,
NETLIFY_FUNCTIONS_PATH,
Expand All @@ -37,4 +41,5 @@ module.exports = {
TEMPLATES_DIR,
FUNCTION_TEMPLATE_PATH,
CUSTOM_REDIRECTS_PATH,
NEXT_IMAGE_FUNCTION_NAME,
};
16 changes: 16 additions & 0 deletions lib/steps/setupImageFunction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const { copySync } = require("fs-extra");
const { join } = require("path");
const { NEXT_IMAGE_FUNCTION_NAME, TEMPLATES_DIR } = require("../config");

// Move our next/image function into the correct functions directory
const setupImageFunction = (functionsPath) => {
const functionName = `${NEXT_IMAGE_FUNCTION_NAME}.js`;
const functionDirectory = join(functionsPath, functionName);

copySync(join(TEMPLATES_DIR, "imageFunction.js"), functionDirectory, {
overwrite: false,
errorOnExist: true,
});
};

module.exports = setupImageFunction;
13 changes: 11 additions & 2 deletions lib/steps/setupRedirects.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const { join } = require("path");
const { existsSync, readFileSync, writeFileSync } = require("fs-extra");
const { logTitle, logItem } = require("../helpers/logger");
const { CUSTOM_REDIRECTS_PATH } = require("../config");
const {
CUSTOM_REDIRECTS_PATH,
NEXT_IMAGE_FUNCTION_NAME,
} = require("../config");
const getSortedRoutes = require("../helpers/getSortedRoutes");
const getNetlifyRoutes = require("../helpers/getNetlifyRoutes");
const isRootCatchAllRedirect = require("../helpers/isRootCatchAllRedirect");
Expand Down Expand Up @@ -29,8 +32,14 @@ const setupRedirects = (publishPath) => {
...require("../pages/withoutProps/redirects"),
];

// Add next/image redirect to our image function
nextRedirects.push({
route: "/_next/image* url=:url w=:width q=:quality",
target: `/.netlify/functions/${NEXT_IMAGE_FUNCTION_NAME}?url=:url&w=:width&q=:quality`,
});

// Add _redirect section heading
if (nextRedirects.length >= 1) redirects.push("# Next-on-Netlify Redirects");
redirects.push("# Next-on-Netlify Redirects");

// Sort routes: More-specific routes (e.g., static routing) precede
// less-specific routes (e.g., catch-all)
Expand Down
26 changes: 26 additions & 0 deletions lib/templates/imageFunction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const jimp = require("jimp");

// Function used to mimic next/image and sharp
exports.handler = async (event) => {
const { url, w = 500, q = 75 } = event.queryStringParameters;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about these defaults but i think it's fine since next_image should almost always provide w

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we probably want some reasonable default just in case so we don't end up trying to process 6MB images at full res or whatever, but I think you're right that it should always be set

const width = parseInt(w);
const quality = parseInt(q);

const imageUrl = url.startsWith("/")
? `${process.env.URL || "http://localhost:8888"}${url}`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about this ||, but i think it's fine since ntl dev uses 8888 as its default port right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is probably okay in the short term, but the port is configurable. quick fix is the create a .env with URL=http://localhost:5432 or whatever to override locally

: url;
const image = await jimp.read(imageUrl);

image.resize(width, jimp.AUTO).quality(quality);

const imageBuffer = await image.getBufferAsync(image.getMIME());

return {
statusCode: 200,
headers: {
"Content-Type": image.getMIME(),
},
body: imageBuffer.toString("base64"),
isBase64Encoded: true,
};
};
Loading