Skip to content

Commit

Permalink
Add eslint rule for not allowing styled-jsx in _document.js (#32678)
Browse files Browse the repository at this point in the history
## Bug

- [ ] Related issues linked
fixes #32656



Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
  • Loading branch information
src200 and ijjk committed May 23, 2022
1 parent 2f9e241 commit 11ad65e
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/basic-features/eslint.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ Next.js provides an ESLint plugin, [`eslint-plugin-next`](https://www.npmjs.com/
| ✔️ | [next/inline-script-id](/docs/messages/inline-script-id.md) | Enforce id attribute on next/script components with inline content |
| ✔️ | next/no-typos | Ensure no typos were made declaring [Next.js's data fetching function](/docs/basic-features/data-fetching/overview.md) |
| ✔️ | [next/next-script-for-ga](/docs/messages/next-script-for-ga.md) | Use the Script component to defer loading of the script until necessary. |
| ✔️ | [next/no-styled-jsx-in-document](/docs/messages/no-styled-jsx-in-document.md) | styled-jsx should not be used in \_document |

- ✔: Enabled in the recommended configuration

Expand Down
4 changes: 4 additions & 0 deletions errors/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,10 @@
"title": "no-server-import-in-page",
"path": "/errors/no-server-import-in-page.md"
},
{
"title": "no-styled-jsx-in-document",
"path": "/errors/no-styled-jsx-in-document.md"
},
{
"title": "no-sync-scripts",
"path": "/errors/no-sync-scripts.md"
Expand Down
41 changes: 41 additions & 0 deletions errors/no-styled-jsx-in-document.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# No styled-jsx in Document

### Why This Error Occurred

Custom CSS like `styled-jsx` is not allowed in a [Custom Document](https://nextjs.org/docs/advanced-features/custom-document).

### Possible Ways to Fix It

If you need shared CSS for all of your pages, take a look at the [Custom `App`](https://nextjs.org/docs/advanced-features/custom-app) file or define a custom layout.

For example, consider the following stylesheet named `styles.css`:

```css
body {
font-family: 'SF Pro Text', 'SF Pro Icons', 'Helvetica Neue', 'Helvetica',
'Arial', sans-serif;
padding: 20px 20px 60px;
max-width: 680px;
margin: 0 auto;
}
```

Create a `pages/_app.{js,tsx}` file if not already present. Then, import the `styles.css` file.

```jsx
import '../styles.css'

// This default export is required in a new `pages/_app.js` file.
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
```

These styles (`styles.css`) will apply to all pages and components in your application.

### Useful links

- [Custom Document Caveats](https://nextjs.org/docs/advanced-features/custom-document#caveats)
- [Layouts](https://nextjs.org/docs/basic-features/layouts)
- [Built in CSS Support](https://nextjs.org/docs/basic-features/built-in-css-support)
- [Custom `App`](https://nextjs.org/docs/advanced-features/custom-app)
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 @@ -14,6 +14,7 @@ module.exports = {
'no-head-import-in-document': require('./rules/no-head-import-in-document'),
'no-script-component-in-head': require('./rules/no-script-component-in-head'),
'no-server-import-in-page': require('./rules/no-server-import-in-page'),
'no-styled-jsx-in-document': require('./rules/no-styled-jsx-in-document'),
'no-typos': require('./rules/no-typos'),
'no-duplicate-head': require('./rules/no-duplicate-head'),
'inline-script-id': require('./rules/inline-script-id'),
Expand All @@ -39,6 +40,7 @@ module.exports = {
'@next/next/no-document-import-in-page': 2,
'@next/next/no-head-import-in-document': 2,
'@next/next/no-script-component-in-head': 2,
'@next/next/no-styled-jsx-in-document': 2,
'@next/next/no-server-import-in-page': 2,
'@next/next/no-typos': 1,
'@next/next/no-duplicate-head': 2,
Expand Down
46 changes: 46 additions & 0 deletions packages/eslint-plugin-next/lib/rules/no-styled-jsx-in-document.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const path = require('path')

module.exports = {
meta: {
docs: {
description: 'Disallow using custom styled-jsx inside pages/_document.js',
recommended: true,
url: 'https://nextjs.org/docs/messages/no-styled-jsx-in-document',
},
fixable: 'code',
},
create: function (context) {
return {
JSXOpeningElement(node) {
const document = context.getFilename().split('pages')[1]
if (!document) {
return
}
const { name, dir } = path.parse(document)

if (
!(
name.startsWith('_document') ||
(dir === '/_document' && name === 'index')
)
) {
return
}

if (
node.name.name === 'style' &&
node.attributes.find(
(attr) => attr.type === 'JSXAttribute' && attr.name.name === 'jsx'
)
) {
context.report({
node,
message: `styled-jsx can not be used inside pages/_document.js. See https://nextjs.org/docs/messages/no-styled-jsx-in-document.`,
})
}
},
}
},
}

module.exports.schema = []
125 changes: 125 additions & 0 deletions test/unit/eslint-plugin-next/no-styled-jsx-in-document.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import rule from '@next/eslint-plugin-next/lib/rules/no-styled-jsx-in-document'
import { RuleTester } from 'eslint'
;(RuleTester as any).setDefaultConfig({
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
modules: true,
jsx: true,
},
},
})
const ruleTester = new RuleTester()

ruleTester.run('no-styled-jsx-in-document', rule, {
valid: [
{
filename: 'pages/_document.js',
code: `import Document, { Html, Head, Main, NextScript } from 'next/document'
export class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}`,
},
{
filename: 'pages/_document.js',
code: `import Document, { Html, Head, Main, NextScript } from 'next/document'
export class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<style>{"\
body{\
color:red;\
}\
"}</style>
<style {...{nonce: '123' }}></style>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}`,
},
{
filename: 'pages/index.js',
code: `
export default function Page() {
return (
<>
<p>Hello world</p>
<style jsx>{\`
p {
color: orange;
}
\`}</style>
</>
)
}
`,
},
],

invalid: [
{
filename: 'pages/_document.js',
code: `
import Document, { Html, Head, Main, NextScript } from 'next/document'
export class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<style jsx>{"\
body{\
color:red;\
}\
"}</style>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}`,
errors: [
{
message:
'styled-jsx can not be used inside pages/_document.js. See https://nextjs.org/docs/messages/no-styled-jsx-in-document.',
},
],
},
],
})

0 comments on commit 11ad65e

Please sign in to comment.