diff --git a/packages/eslint-plugin-next/src/rules/no-img-element.ts b/packages/eslint-plugin-next/src/rules/no-img-element.ts index 01511b96fec9a..2c3f5f2c92e13 100644 --- a/packages/eslint-plugin-next/src/rules/no-img-element.ts +++ b/packages/eslint-plugin-next/src/rules/no-img-element.ts @@ -1,3 +1,4 @@ +import path = require('path') import { defineRule } from '../utils/define-rule' const url = 'https://nextjs.org/docs/messages/no-img-element' @@ -15,6 +16,14 @@ export = defineRule({ schema: [], }, create(context) { + // Get relative path of the file + const relativePath = context.filename + .replace(path.sep, '/') + .replace(context.cwd, '') + .replace(/^\//, '') + + const isAppDir = /^(src\/)?app\//.test(relativePath) + return { JSXOpeningElement(node) { if (node.name.name !== 'img') { @@ -29,6 +38,14 @@ export = defineRule({ return } + // If is metadata route files, ignore + // e.g. opengraph-image.js, twitter-image.js, icon.js + if ( + isAppDir && + /\/opengraph-image|twitter-image|icon\.\w+$/.test(relativePath) + ) + return + context.report({ node, message: `Using \`\` could result in slower LCP and higher bandwidth. Consider using \`\` from \`next/image\` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: ${url}`, diff --git a/test/unit/eslint-plugin-next/no-img-element.test.ts b/test/unit/eslint-plugin-next/no-img-element.test.ts index 9f636883b7d59..8477bace54c2b 100644 --- a/test/unit/eslint-plugin-next/no-img-element.test.ts +++ b/test/unit/eslint-plugin-next/no-img-element.test.ts @@ -55,6 +55,46 @@ const tests = { ); } }`, + { + code: `\ +import { ImageResponse } from "next/og"; + +export default function icon() { + return new ImageResponse( + ( + avatar + ) + ); +} +`, + filename: `src/app/icon.js`, + }, + { + code: `\ +import { ImageResponse } from "next/og"; + +export default function Image() { + return new ImageResponse( + ( + avatar + ) + ); +} +`, + filename: `app/opengraph-image.tsx`, + }, ], invalid: [ { @@ -91,6 +131,27 @@ const tests = { }`, errors: [{ message, type: 'JSXOpeningElement' }], }, + { + code: `\ +import { ImageResponse } from "next/og"; + +export default function Image() { +return new ImageResponse( + ( + avatar + ) +); +} +`, + filename: `some/non-metadata-route-image.tsx`, + errors: [{ message, type: 'JSXOpeningElement' }], + }, ], }