Skip to content

Commit

Permalink
feat(linter) eslint-plugin-next no-sync-scripts (oxc-project#1953)
Browse files Browse the repository at this point in the history
  • Loading branch information
camc314 authored and IWANABETHATGUY committed May 29, 2024
1 parent 136c3d7 commit 31375c8
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 0 deletions.
2 changes: 2 additions & 0 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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,
}
139 changes: 139 additions & 0 deletions crates/oxc_linter/src/rules/nextjs/no_sync_scripts.rs
Original file line number Diff line number Diff line change
@@ -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::<FxHashSet<_>>();

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 (
<div>
<h1>Hello title</h1>
<script src='https://blah.com' async></script>
</div>
);
}
}",
r"import {Head} from 'next/document';
export class Blah extends Head {
render(props) {
return (
<div>
<h1>Hello title</h1>
<script {...props} ></script>
</div>
);
}
}",
];

let fail = vec![
r"
import {Head} from 'next/document';
export class Blah extends Head {
render() {
return (
<div>
<h1>Hello title</h1>
<script src='https://blah.com'></script>
</div>
);
}
}",
r"
import {Head} from 'next/document';
export class Blah extends Head {
render(props) {
return (
<div>
<h1>Hello title</h1>
<script src={props.src}></script>
</div>
);
}
}",
];

Tester::new_without_config(NoSyncScripts::NAME, pass, fail).test_and_snapshot();
}
23 changes: 23 additions & 0 deletions crates/oxc_linter/src/snapshots/no_sync_scripts.snap
Original file line number Diff line number Diff line change
@@ -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 │ <h1>Hello title</h1>
9<script src='https://blah.com'></script>
· ──────
10</div>
╰────
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 │ <h1>Hello title</h1>
9<script src={props.src}></script>
· ──────
10</div>
╰────
help: See https://nextjs.org/docs/messages/no-sync-scripts


0 comments on commit 31375c8

Please sign in to comment.