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

Commit

Permalink
ESLint Plugin: Prevent bad imports of next/document and next/head (ve…
Browse files Browse the repository at this point in the history
…rcel#24832)

Adds lint rules to the Next.js ESLint plugin to:

- Disallow importing `next/head` inside `pages/_document.js`
- Disallow importing `next/document` outside of `pages/_document.js`

Both rules will be surfaced as **errors** within the recommended config of the plugin.

Fixes vercel#13712 vercel#13958
  • Loading branch information
housseindjirdeh authored May 10, 2021
1 parent 8571510 commit c252fbc
Show file tree
Hide file tree
Showing 8 changed files with 283 additions and 0 deletions.
8 changes: 8 additions & 0 deletions errors/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,10 @@
},
{ "title": "no-cache", "path": "/errors/no-cache.md" },
{ "title": "no-css-tags", "path": "/errors/no-css-tags.md" },
{
"title": "no-document-import-in-page",
"path": "/errors/no-document-import-in-page.md"
},
{
"title": "no-document-title",
"path": "/errors/no-document-title.md"
Expand All @@ -263,6 +267,10 @@
"title": "no-document-viewport-meta",
"path": "/errors/no-document-viewport-meta.md"
},
{
"title": "no-head-import-in-document",
"path": "/errors/no-head-import-in-document.md"
},
{
"title": "no-html-link-for-pages",
"path": "/errors/no-html-link-for-pages.md"
Expand Down
24 changes: 24 additions & 0 deletions errors/no-document-import-in-page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# No Document Import in Page

### Why This Error Occurred

`next/document` was imported in a page outside of `pages/_document.js`. This can cause unexpected issues in your application.

### Possible Ways to Fix It

Only import and use `next/document` within `pages/_document.js` to override the default `Document` component:

```jsx
// pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {
//...
}

export default MyDocument
```

### Useful Links

- [Custom Document](https://nextjs.org/docs/advanced-features/custom-document)
34 changes: 34 additions & 0 deletions errors/no-head-import-in-document.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# No Head Import in Document

### Why This Error Occurred

`next/head` was imported in `pages/_document.js`. This can cause unexpected issues in your application.

### Possible Ways to Fix It

Only import and use `next/document` within `pages/_document.js` to override the default `Document` component. If you are importing `next/head` to use the `Head` component, import it from `next/document` instead in order to modify `<head>` code across all pages:

```jsx
// pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {
static async getInitialProps(ctx) {
//...
}

render() {
return (
<Html>
<Head></Head>
</Html>
)
}
}

export default MyDocument
```

### Useful Links

- [Custom Document](https://nextjs.org/docs/advanced-features/custom-document)
4 changes: 4 additions & 0 deletions packages/eslint-plugin-next/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ module.exports = {
'google-font-display': require('./rules/google-font-display'),
'google-font-preconnect': require('./rules/google-font-preconnect'),
'link-passhref': require('./rules/link-passhref'),
'no-document-import-in-page': require('./rules/no-document-import-in-page'),
'no-head-import-in-document': require('./rules/no-head-import-in-document'),
},
configs: {
recommended: {
Expand All @@ -23,6 +25,8 @@ module.exports = {
'@next/next/google-font-display': 1,
'@next/next/google-font-preconnect': 1,
'@next/next/link-passhref': 1,
'@next/next/no-document-import-in-page': 2,
'@next/next/no-head-import-in-document': 2,
},
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const path = require('path')

module.exports = {
meta: {
docs: {
description:
'Disallow importing next/document outside of pages/document.js',
recommended: true,
},
},
create: function (context) {
return {
ImportDeclaration(node) {
if (node.source.value !== 'next/document') {
return
}

const page = context.getFilename().split('pages')[1]
if (!page || path.parse(page).name === '_document') {
return
}

context.report({
node,
message: `next/document should not be imported outside of pages/_document.js. See https://nextjs.org/docs/messages/no-document-import-in-page.`,
})
},
}
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const path = require('path')

module.exports = {
meta: {
docs: {
description: 'Disallow importing next/head in pages/document.js',
recommended: true,
},
},
create: function (context) {
return {
ImportDeclaration(node) {
if (node.source.value !== 'next/head') {
return
}

const document = context.getFilename().split('pages')[1]
if (!document || path.parse(document).name !== '_document') {
return
}

context.report({
node,
message: `next/head should not be imported in pages${document}. Import Head from next/document instead. See https://nextjs.org/docs/messages/no-head-import-in-document.`,
})
},
}
},
}
66 changes: 66 additions & 0 deletions test/eslint-plugin-next/no-document-import-in-page.unit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const rule = require('@next/eslint-plugin-next/lib/rules/no-document-import-in-page')

const RuleTester = require('eslint').RuleTester

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

var ruleTester = new RuleTester()
ruleTester.run('no-document-import-in-page', rule, {
valid: [
{
code: `import Document from "next/document"
export default class MyDocument extends Document {
render() {
return (
<Html>
</Html>
);
}
}
`,
filename: 'pages/_document.js',
},
{
code: `import Document from "next/document"
export default class MyDocument extends Document {
render() {
return (
<Html>
</Html>
);
}
}
`,
filename: 'pages/_document.tsx',
},
],
invalid: [
{
code: `import Document from "next/document"
export const Test = () => (
<p>Test</p>
)
`,
filename: 'pages/test.js',
errors: [
{
message:
'next/document should not be imported outside of pages/_document.js. See https://nextjs.org/docs/messages/no-document-import-in-page.',
type: 'ImportDeclaration',
},
],
},
],
})
88 changes: 88 additions & 0 deletions test/eslint-plugin-next/no-head-import-in-document.unit.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const rule = require('@next/eslint-plugin-next/lib/rules/no-head-import-in-document')

const RuleTester = require('eslint').RuleTester

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

var ruleTester = new RuleTester()
ruleTester.run('no-head-import-in-document', rule, {
valid: [
{
code: `import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
//...
}
render() {
return (
<Html>
<Head>
</Head>
</Html>
)
}
}
export default MyDocument
`,
filename: 'pages/_document.tsx',
},
{
code: `import Head from "next/head";
export default function IndexPage() {
return (
<Head>
<title>My page title</title>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
);
}
`,
filename: 'pages/index.tsx',
},
],
invalid: [
{
code: `
import Document, { Html, Main, NextScript } from 'next/document'
import Head from 'next/head'
class MyDocument extends Document {
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
`,
filename: 'pages/_document.js',
errors: [
{
message:
'next/head should not be imported in pages/_document.js. Import Head from next/document instead. See https://nextjs.org/docs/messages/no-head-import-in-document.',
type: 'ImportDeclaration',
},
],
},
],
})

0 comments on commit c252fbc

Please sign in to comment.