From 31375c8718a5e8b37a7469e0d293325c08247da3 Mon Sep 17 00:00:00 2001 From: Cameron Date: Tue, 9 Jan 2024 03:22:27 +0000 Subject: [PATCH] feat(linter) eslint-plugin-next no-sync-scripts (#1953) --- crates/oxc_linter/src/rules.rs | 2 + .../src/rules/nextjs/no_sync_scripts.rs | 139 ++++++++++++++++++ .../src/snapshots/no_sync_scripts.snap | 23 +++ 3 files changed, 164 insertions(+) create mode 100644 crates/oxc_linter/src/rules/nextjs/no_sync_scripts.rs create mode 100644 crates/oxc_linter/src/snapshots/no_sync_scripts.snap diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 2512fa5534caa..1c616ff2c931c 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -277,6 +277,7 @@ mod nextjs { pub mod no_async_client_component; pub mod no_css_tags; pub mod no_img_element; + pub mod no_sync_scripts; pub mod no_title_in_document_head; } @@ -524,5 +525,6 @@ oxc_macros::declare_all_lint_rules! { nextjs::no_async_client_component, nextjs::no_css_tags, nextjs::no_img_element, + nextjs::no_sync_scripts, nextjs::no_title_in_document_head, } diff --git a/crates/oxc_linter/src/rules/nextjs/no_sync_scripts.rs b/crates/oxc_linter/src/rules/nextjs/no_sync_scripts.rs new file mode 100644 index 0000000000000..3dfc07abe86bd --- /dev/null +++ b/crates/oxc_linter/src/rules/nextjs/no_sync_scripts.rs @@ -0,0 +1,139 @@ +use oxc_ast::{ + ast::{JSXAttributeItem, JSXAttributeName, JSXElementName}, + AstKind, +}; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; +use rustc_hash::FxHashSet; + +use crate::{context::LintContext, rule::Rule, AstNode}; + +#[derive(Debug, Error, Diagnostic)] +#[error("eslint-plugin-next(no-sync-scripts): Prevent synchronous scripts.")] +#[diagnostic(severity(warning), help("See https://nextjs.org/docs/messages/no-sync-scripts"))] +struct NoSyncScriptsDiagnostic(#[label] pub Span); + +#[derive(Debug, Default, Clone)] +pub struct NoSyncScripts; + +declare_oxc_lint!( + /// ### What it does + /// + /// + /// ### Why is this bad? + /// + /// + /// ### Example + /// ```javascript + /// ``` + NoSyncScripts, + correctness +); + +impl Rule for NoSyncScripts { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + let AstKind::JSXOpeningElement(jsx_opening_element) = node.kind() else { return }; + + let JSXElementName::Identifier(jsx_opening_element_name) = &jsx_opening_element.name else { + return; + }; + + if jsx_opening_element_name.name.as_str() != "script" { + return; + } + + let attributes_hs = + jsx_opening_element + .attributes + .iter() + .filter_map(|v| { + if let JSXAttributeItem::Attribute(v) = v { + Some(&v.name) + } else { + None + } + }) + .filter_map(|v| { + if let JSXAttributeName::Identifier(v) = v { + Some(v.name.clone()) + } else { + None + } + }) + .collect::>(); + + if attributes_hs.contains("src") + && !attributes_hs.contains("async") + && !attributes_hs.contains("defer") + { + ctx.diagnostic(NoSyncScriptsDiagnostic(jsx_opening_element_name.span)); + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + r"import {Head} from 'next/document'; + + export class Blah extends Head { + render() { + return ( +
+

Hello title

+ +
+ ); + } + }", + r"import {Head} from 'next/document'; + + export class Blah extends Head { + render(props) { + return ( +
+

Hello title

+ +
+ ); + } + }", + ]; + + let fail = vec![ + r" + import {Head} from 'next/document'; + + export class Blah extends Head { + render() { + return ( +
+

Hello title

+ +
+ ); + } + }", + r" + import {Head} from 'next/document'; + + export class Blah extends Head { + render(props) { + return ( +
+

Hello title

+ +
+ ); + } + }", + ]; + + Tester::new_without_config(NoSyncScripts::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/no_sync_scripts.snap b/crates/oxc_linter/src/snapshots/no_sync_scripts.snap new file mode 100644 index 0000000000000..1724ac1927659 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/no_sync_scripts.snap @@ -0,0 +1,23 @@ +--- +source: crates/oxc_linter/src/tester.rs +expression: no_sync_scripts +--- + ⚠ eslint-plugin-next(no-sync-scripts): Prevent synchronous scripts. + ╭─[no_sync_scripts.tsx:8:1] + 8 │

Hello title

+ 9 │ + · ────── + 10 │ + ╰──── + help: See https://nextjs.org/docs/messages/no-sync-scripts + + ⚠ eslint-plugin-next(no-sync-scripts): Prevent synchronous scripts. + ╭─[no_sync_scripts.tsx:8:1] + 8 │

Hello title

+ 9 │ + · ────── + 10 │ + ╰──── + help: See https://nextjs.org/docs/messages/no-sync-scripts + +