Skip to content

feat: add support for using image config from next.config.js #518

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 12, 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
4 changes: 3 additions & 1 deletion docs/image-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ The Essential Next.js plugin includes a function to generate images for `next/im

By default, images are returned in the same format as the source image if they are in JPEG, PNG, WebP or AVIF format. If you are only targeting modern browsers and want to live life on the edge, you can [set the environment variable](https://docs.netlify.com/configure-builds/environment-variables/) `FORCE_WEBP_OUTPUT` to `"true"`, and it will return all images in WebP format. This will often lead to significant improvements in file size. However you should not use this if you need to support older browsers, as `next/image` does not support picture tag source fallback and images will appear broken. Check [browser support](https://caniuse.com/webp) to see if you are happy to do this.

If you want to use remote images in `next/image`, you will need to add the image domains to an allow list. Setting them in `images.domains` in `next.config.js` is not supported: instead you should set the environment variable `NEXT_IMAGE_ALLOWED_DOMAINS` to a comma-separated list of domains, e.g. `NEXT_IMAGE_ALLOWED_DOMAINS="placekitten.com,unsplash.com"`.
If you want to use remote images in `next/image`, you will need to add the image domains to an allow list. Add the required domains to the `images.domains` array in `next.config.js`.

In previous versions of the Essential Next.js plugin you needed to set the environment variable `NEXT_IMAGE_ALLOWED_DOMAINS` rather than using the Next config file. This is no longer required, and will be removed in a future version.
6 changes: 2 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,9 @@ module.exports = {
checkNxConfig({ netlifyConfig, packageJson, nextConfig, failBuild, constants })
}

if (nextConfig.images.domains.length !== 0 && !process.env.NEXT_IMAGE_ALLOWED_DOMAINS) {
if (process.env.NEXT_IMAGE_ALLOWED_DOMAINS) {
console.log(
`Image domains set in next.config.js are ignored.\nPlease set the env variable NEXT_IMAGE_ALLOWED_DOMAINS to "${nextConfig.images.domains.join(
',',
)}" instead`,
`The Essential Next.js plugin now supports reading image domains from your Next config, so using NEXT_IMAGE_ALLOWED_DOMAINS is now deprecated. Please set images.domains in next.config.js instead, and remove the NEXT_IMAGE_ALLOWED_DOMAINS variable.`,
)
}
await restoreCache({ cache: utils.cache, distDir: nextConfig.distDir })
Expand Down
6 changes: 5 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const chokidar = require('chokidar')
const debounceFn = require('debounce-fn')
const execa = require('execa')

const getNextConfig = require('../helpers/getNextConfig')

const { NETLIFY_PUBLISH_PATH, NETLIFY_FUNCTIONS_PATH, SRC_FILES } = require('./lib/config')
const { logTitle } = require('./lib/helpers/logger')
const copyNextAssets = require('./lib/steps/copyNextAssets')
Expand Down Expand Up @@ -31,7 +33,9 @@ const build = async (functionsPath, publishPath, nextRoot) => {

await setupPages({ functionsPath, publishPath })

setupImageFunction(functionsPath)
const { images } = await getNextConfig()

await setupImageFunction(functionsPath, images)

await setupRedirects(publishPath)

Expand Down
13 changes: 6 additions & 7 deletions src/lib/steps/setupImageFunction.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
const { join } = require('path')

const { copySync } = require('fs-extra')
const { copyFile, writeJSON, ensureDir } = require('fs-extra')

const { NEXT_IMAGE_FUNCTION_NAME, TEMPLATES_DIR } = require('../config')

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

copySync(join(TEMPLATES_DIR, 'imageFunction.js'), functionDirectory, {
overwrite: false,
errorOnExist: true,
})
await ensureDir(functionDirectory)
await writeJSON(join(functionDirectory, 'imageconfig.json'), imageconfig)
await copyFile(join(TEMPLATES_DIR, 'imageFunction.js'), join(functionDirectory, functionName))
}

module.exports = setupImageFunction
12 changes: 9 additions & 3 deletions src/lib/templates/imageFunction.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const imageType = require('image-type')
const isSvg = require('is-svg')
const etag = require('etag')
const imageSize = require('image-size')
const config = require('./imageconfig.json')
// 6MB is hard max Lambda response size
const MAX_RESPONSE_SIZE = 6291456

Expand Down Expand Up @@ -47,9 +48,14 @@ const handler = async (event) => {
} else {
isRemoteImage = true
// Remote images need to be in the allowlist
const allowedDomains = process.env.NEXT_IMAGE_ALLOWED_DOMAINS
? process.env.NEXT_IMAGE_ALLOWED_DOMAINS.split(',').map((domain) => domain.trim())
: []
let allowedDomains = config.images?.domains || []

if (process.env.NEXT_IMAGE_ALLOWED_DOMAINS) {
console.log('Combining `NEXT_IMAGE_ALLOWED_DOMAINS` with any domains found in `next.config.js`')
allowedDomains = allowedDomains.concat(
process.env.NEXT_IMAGE_ALLOWED_DOMAINS.split(',').map((domain) => domain.trim()),
)
}

if (!allowedDomains.includes(new URL(parsedUrl).hostname)) {
return {
Expand Down
7 changes: 6 additions & 1 deletion src/tests/defaults.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,13 @@ describe('next/image', () => {
const functionsDir = join(PROJECT_PATH, 'out_functions')

test('sets up next_image as a function in every project by default', () => {
expect(existsSync(join(functionsDir, 'next_image.js'))).toBe(true)
expect(existsSync(join(functionsDir, 'next_image', 'next_image.js'))).toBe(true)
})

test('injects the image plugin config into the function', () => {
expect(readJsonSync(join(functionsDir, 'next_image', 'imageconfig.json')).domains).toEqual(["placekitten.com"])
})

})

describe('SSG Pages with getStaticProps', () => {
Expand Down
3 changes: 3 additions & 0 deletions src/tests/fixtures/next.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
module.exports = {
target: "serverless",
images: {
domains: ["placekitten.com"]
}
};