Skip to content
This repository has been archived by the owner on Jun 21, 2023. It is now read-only.

Commit

Permalink
ESLint Plugin: Disallow <title> in Head from next/document (vercel#24868
Browse files Browse the repository at this point in the history
)

Adds lint rule warning to the Next.js ESLint plugin to disallow `<title>` in `Head` imported from `next/document`.
  • Loading branch information
housseindjirdeh authored May 7, 2021
1 parent b2ba983 commit 06d16a3
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 0 deletions.
4 changes: 4 additions & 0 deletions errors/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,10 @@
"title": "no-sync-scripts",
"path": "/errors/no-sync-scripts.md"
},
{
"title": "no-title-in-document-head",
"path": "/errors/no-title-in-document-head.md"
},
{
"title": "no-unwanted-polyfillio",
"path": "/errors/no-unwanted-polyfillio.md"
Expand Down
30 changes: 30 additions & 0 deletions errors/no-title-in-document-head.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# No Title in Document Head

### Why This Error Occurred

A `<title>` element was defined within the `Head` component imported from `next/document`, which should only be used for any `<head>` code that is common for all pages. Title tags should be defined at the page-level using `next/head`.

### Possible Ways to Fix It

Within a page or component, import and use `next/head` to define a page title:

```jsx
import Head from 'next/head'

export class Home {
render() {
return (
<div>
<Head>
<title>My page title</title>
</Head>
</div>
)
}
}
```

### Useful links

- [next/head](https://nextjs.org/docs/api-reference/next/head)
- [Custom Document](https://nextjs.org/docs/advanced-features/custom-document)
2 changes: 2 additions & 0 deletions packages/eslint-plugin-next/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module.exports = {
'no-sync-scripts': require('./rules/no-sync-scripts'),
'no-html-link-for-pages': require('./rules/no-html-link-for-pages'),
'no-unwanted-polyfillio': require('./rules/no-unwanted-polyfillio'),
'no-title-in-document-head': require('./rules/no-title-in-document-head'),
},
configs: {
recommended: {
Expand All @@ -13,6 +14,7 @@ module.exports = {
'@next/next/no-sync-scripts': 1,
'@next/next/no-html-link-for-pages': 1,
'@next/next/no-unwanted-polyfillio': 1,
'@next/next/no-title-in-document-head': 1,
},
},
},
Expand Down
48 changes: 48 additions & 0 deletions packages/eslint-plugin-next/lib/rules/no-title-in-document-head.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module.exports = {
meta: {
docs: {
description: 'Disallow using <title> with Head from next/document',
},
},
create: function (context) {
let headFromNextDocument = false
return {
ImportDeclaration(node) {
if (node.source.value === 'next/document') {
if (node.specifiers.some(({ local }) => local.name === 'Head')) {
headFromNextDocument = true
}
}
},
JSXElement(node) {
if (!headFromNextDocument) {
return
}

if (
node.openingElement &&
node.openingElement.name &&
node.openingElement.name.name !== 'Head'
) {
return
}

const titleTag = node.children.find(
(child) =>
child.openingElement &&
child.openingElement.name &&
child.openingElement.name.type === 'JSXIdentifier' &&
child.openingElement.name.name === 'title'
)

if (titleTag) {
context.report({
node: titleTag,
message:
'Titles should be defined at the page-level using next/head. See https://nextjs.org/docs/messages/no-title-in-document-head.',
})
}
},
}
},
}
70 changes: 70 additions & 0 deletions test/eslint-plugin-next/no-title-in-document-head.unit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const rule = require('@next/eslint-plugin-next/lib/rules/no-title-in-document-head')
const RuleTester = require('eslint').RuleTester

RuleTester.setDefaultConfig({
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
modules: true,
jsx: true,
},
},
})

var ruleTester = new RuleTester()
ruleTester.run('no-title-in-document-head', rule, {
valid: [
`import Head from "next/head";
class Test {
render() {
return (
<Head>
<title>My page title</title>
</Head>
);
}
}`,

`import Document, { Html, Head } from "next/document";
class MyDocument extends Document {
render() {
return (
<Html>
<Head>
</Head>
</Html>
);
}
}
export default MyDocument;
`,
],

invalid: [
{
code: `
import { Head } from "next/document";
class Test {
render() {
return (
<Head>
<title>My page title</title>
</Head>
);
}
}`,
errors: [
{
message:
'Titles should be defined at the page-level using next/head. See https://nextjs.org/docs/messages/no-title-in-document-head.',
type: 'JSXElement',
},
],
},
],
})

0 comments on commit 06d16a3

Please sign in to comment.