diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index fb5ab9e32a4fd..980303009e10b 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -350,6 +350,7 @@ mod nextjs { pub mod no_before_interactive_script_outside_document; pub mod no_css_tags; pub mod no_document_import_in_page; + pub mod no_duplicate_head; pub mod no_head_element; pub mod no_head_import_in_document; pub mod no_img_element; @@ -693,6 +694,7 @@ oxc_macros::declare_all_lint_rules! { nextjs::no_css_tags, nextjs::no_head_element, nextjs::no_head_import_in_document, + nextjs::no_duplicate_head, nextjs::no_img_element, nextjs::no_script_component_in_head, nextjs::no_sync_scripts, diff --git a/crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs b/crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs new file mode 100644 index 0000000000000..b8224b874905a --- /dev/null +++ b/crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs @@ -0,0 +1,185 @@ +use oxc_ast::AstKind; +use oxc_diagnostics::miette::{miette, LabeledSpan, Severity}; +use oxc_macros::declare_oxc_lint; +use oxc_semantic::Reference; + +use crate::{context::LintContext, rule::Rule}; + +#[derive(Debug, Default, Clone)] +pub struct NoDuplicateHead; + +declare_oxc_lint!( + /// ### What it does + /// Prevent duplicate usage of
in pages/_document.js. + /// + /// ### Why is this bad? + /// This can cause unexpected behavior in your application. + /// + /// ### Example + /// ```javascript + /// import Document, { Html, Head, Main, NextScript } from 'next/document' + /// class MyDocument extends Document { + /// static async getInitialProps(ctx) { + /// } + /// render() { + /// return ( + /// + /// + /// + ///+ ///
`" + )); + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + "import Document, { Html, Head, Main, NextScript } from 'next/document' + + class MyDocument extends Document { + static async getInitialProps(ctx) { + //... + } + + render() { + return ( + +
+ + ) + } + } + + export default MyDocument + ", + r#"import Document, { Html, Head, Main, NextScript } from 'next/document' + + class MyDocument extends Document { + render() { + return ( + +
+ + + + + ) + } + } + + export default MyDocument + "#, + ]; + + let fail = vec![ + " + import Document, { Html, Main, NextScript } from 'next/document' + import Head from 'next/head' + + class MyDocument extends Document { + render() { + return ( + +
+
+
+ + ) + } + } + + export default MyDocument + ", + r#" + import Document, { Html, Main, NextScript } from 'next/document' + import Head from 'next/head' + + class MyDocument extends Document { + render() { + return ( + +
+ + + +
` + ╭─[no_duplicate_head.tsx:9:19] + 8 │ + 9 │
+ · ──── + 10 │
+ · ──── + 11 │
+ · ──── + 12 │ + ╰──── + help: Only use a single `
` component in your custom document in `pages/_document.js`. See: https://nextjs.org/docs/messages/no-duplicate-head + + ⚠ eslint-plugin-next(no-duplicate-head): Do not include multiple instances of `
` + ╭─[no_duplicate_head.tsx:9:19] + 8 │ + 9 │
+ · ──── + 10 │ + ╰──── + ╭─[no_duplicate_head.tsx:20:19] + 19 │ + 20 │
+ · ──── + 21 │ ` component in your custom document in `pages/_document.js`. See: https://nextjs.org/docs/messages/no-duplicate-head