Skip to content

Commit

Permalink
perf(linter): avoid unnecessary work in nextjs:no_duplicate_head rule
Browse files Browse the repository at this point in the history
  • Loading branch information
overlookmotel committed Jul 25, 2024
1 parent 3f2041f commit 30720d0
Showing 1 changed file with 37 additions and 11 deletions.
48 changes: 37 additions & 11 deletions crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use oxc_ast::AstKind;
use oxc_diagnostics::{LabeledSpan, OxcDiagnostic};
use oxc_macros::declare_oxc_lint;
use oxc_span::GetSpan;

use crate::{context::LintContext, rule::Rule};

Expand Down Expand Up @@ -57,21 +58,46 @@ impl Rule for NoDuplicateHead {
}

let nodes = ctx.nodes();
let labels = symbols
.get_resolved_references(symbol_id)
.filter(|r| r.is_read())
.filter(|r| {
let kind = nodes.ancestors(r.node_id()).nth(2).map(|node_id| nodes.kind(node_id));
matches!(kind, Some(AstKind::JSXOpeningElement(_)))
})
.map(|reference| ctx.semantic().reference_span(reference))
.map(LabeledSpan::underline)
.collect::<Vec<_>>();
// 1 x `<Head>` is fine, more than 1 is not.
// Avoid allocating a `Vec` for common case where only a single `<Head>` is found.
let mut first_node_id = None;
let mut node_ids = vec![];
for reference in symbols.get_resolved_references(symbol_id) {
if !reference.is_read() {
continue;
}

if !matches!(
nodes.ancestors(reference.node_id()).nth(2).map(|node_id| nodes.kind(node_id)),
Some(AstKind::JSXOpeningElement(_))
) {
continue;
}

let node_id = reference.node_id();
if first_node_id.is_none() {
// First `<Head>` found
first_node_id = Some(node_id);
} else if node_ids.is_empty() {
// 2nd `<Head>` found - populate `node_ids` with both
node_ids.extend([first_node_id.unwrap(), node_id]);
} else {
// Further `<Head>` found - add to `node_ids`
node_ids.push(node_id);
}
}

if labels.len() <= 1 {
// `node_ids` is empty if 0 or 1 `<Head>` found
if node_ids.is_empty() {
return;
}

let labels = node_ids
.into_iter()
.map(|node_id| ctx.semantic().nodes().kind(node_id).span())
.map(LabeledSpan::underline)
.collect::<Vec<_>>();

ctx.diagnostic(
OxcDiagnostic::warn("Do not include multiple instances of `<Head/>`")
.with_help("Only use a single `<Head />` component in your custom document in `pages/_document.js`. See: https://nextjs.org/docs/messages/no-duplicate-head")
Expand Down

0 comments on commit 30720d0

Please sign in to comment.