diff --git a/.github/codecov.yml b/.github/codecov.yml index de4391876ed4c..0eeaf91b9ae43 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -22,7 +22,5 @@ ignore: - "crates/oxc_wasm" # Remove this once wasm is completed - "crates/oxc_diagnostics" - "crates/oxc_type_synthesis" - - "crates/oxc_query" # Not aiming for test coverage right now with @u9g - - "crates/oxc_linter_plugin" - "crates/oxc_transformer" # not ready - "crates/oxc_js_regex" # not ready diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml index 6720afba0daab..b1c3377eacb7d 100644 --- a/.github/workflows/conformance.yml +++ b/.github/workflows/conformance.yml @@ -13,7 +13,6 @@ on: - 'crates/oxc/**' - 'crates/oxc_cli/**' - 'crates/oxc_linter/**' - - 'crates/oxc_query/**' - 'crates/oxc_type_synthesis/**' - 'crates/oxc_wasm/**' - 'crates/oxc_prettier/**' @@ -30,7 +29,6 @@ on: - 'crates/oxc/**' - 'crates/oxc_cli/**' - 'crates/oxc_linter/**' - - 'crates/oxc_query/**' - 'crates/oxc_type_synthesis/**' - 'crates/oxc_wasm/**' diff --git a/.typos.toml b/.typos.toml index df8a079b286f3..0850c991ca8aa 100644 --- a/.typos.toml +++ b/.typos.toml @@ -14,7 +14,6 @@ extend-exclude = [ "crates/oxc_syntax/src/xml_entities.rs", "**/*.snap", "pnpm-lock.yaml", - "crates/oxc_linter_plugin/examples/queries/unicorn/prefer-string-slice/rule.yml" ] [default.extend-words] diff --git a/Cargo.lock b/Cargo.lock index d16cd774c00c9..964ee78826034 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,37 +53,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" -[[package]] -name = "anyhow" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" - -[[package]] -name = "async-graphql-parser" -version = "2.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99841c1f890fda6712054e7e37b207738f4aa97870cb1bffcab2f09f2df0957a" -dependencies = [ - "async-graphql-value", - "pest", - "pest_derive", - "serde", - "serde_json", -] - -[[package]] -name = "async-graphql-value" -version = "2.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cecac7ab6737364cff7b16e9273dd51fac7cfbd14ab5d84127df5a56ca9d422" -dependencies = [ - "bytes", - "indexmap 1.9.3", - "serde", - "serde_json", -] - [[package]] name = "async-trait" version = "0.1.77" @@ -128,15 +97,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "backtrace-ext" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" -dependencies = [ - "backtrace", -] - [[package]] name = "base64" version = "0.21.5" @@ -249,9 +209,6 @@ name = "bytes" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" -dependencies = [ - "serde", -] [[package]] name = "cast" @@ -627,18 +584,6 @@ dependencies = [ "encoding_rs", ] -[[package]] -name = "enum-as-inner" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "enum-variants-strings" version = "0.2.3" @@ -998,7 +943,6 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", - "serde", ] [[package]] @@ -1158,9 +1102,6 @@ name = "linked-hash-map" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" -dependencies = [ - "serde", -] [[package]] name = "linux-raw-sys" @@ -1168,17 +1109,6 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" -[[package]] -name = "located_yaml" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bc68ee6f87a1be7fdba1dcfd854528371aa84a8390279b5d7a99d5da82add76" -dependencies = [ - "linked-hash-map", - "serde", - "yaml-rust", -] - [[package]] name = "lock_api" version = "0.4.11" @@ -1217,12 +1147,6 @@ dependencies = [ "serde", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "matchers" version = "0.1.0" @@ -1244,8 +1168,6 @@ version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" dependencies = [ - "backtrace", - "backtrace-ext", "is-terminal", "miette-derive", "once_cell", @@ -1741,26 +1663,6 @@ dependencies = [ "url", ] -[[package]] -name = "oxc_linter_plugin" -version = "0.0.0" -dependencies = [ - "ignore", - "located_yaml", - "miette", - "oxc_allocator", - "oxc_diagnostics", - "oxc_linter", - "oxc_parser", - "oxc_query", - "oxc_semantic", - "oxc_span", - "path-calculate", - "serde", - "serde_yaml", - "trustfall", -] - [[package]] name = "oxc_macros" version = "0.0.0" @@ -1876,22 +1778,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "oxc_query" -version = "0.0.0" -dependencies = [ - "enum-as-inner", - "oxc_allocator", - "oxc_ast", - "oxc_parser", - "oxc_semantic", - "oxc_span", - "rustc-hash", - "serde", - "trustfall", - "url", -] - [[package]] name = "oxc_resolver" version = "1.2.0" @@ -2025,12 +1911,9 @@ dependencies = [ "oxc", "oxc_linter", "oxc_prettier", - "oxc_query", "oxc_type_synthesis", "serde", "serde-wasm-bindgen", - "serde_json", - "trustfall", "wasm-bindgen", "wasm-bindgen-test", ] @@ -2067,15 +1950,6 @@ dependencies = [ "path-dedot", ] -[[package]] -name = "path-calculate" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4690055139867a4a0d11d18ab98d80b325d7aa573b99e0ae8e1bc787a4282da7" -dependencies = [ - "path-absolutize", -] - [[package]] name = "path-dedot" version = "3.1.1" @@ -2661,9 +2535,6 @@ name = "smallvec" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" -dependencies = [ - "serde", -] [[package]] name = "smawk" @@ -3049,46 +2920,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "trustfall" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c5a5b0a24849741733d22afe23b2784dd06a9c1100edd43de0e814a9f391b0b" -dependencies = [ - "anyhow", - "trustfall_core", - "trustfall_derive", -] - -[[package]] -name = "trustfall_core" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "994d4a15d0cf514b856567f85a166ee0e2427f845b194b3744adeb8caf674385" -dependencies = [ - "async-graphql-parser", - "async-graphql-value", - "itertools 0.10.5", - "maplit", - "once_cell", - "regex", - "serde", - "serde_json", - "smallvec", - "thiserror", -] - -[[package]] -name = "trustfall_derive" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb14eb4f23b3b669d232a5c7d2b3d6c89ad9e15be1cbdd2c1e14d87d62569ec" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "typenum" version = "1.17.0" diff --git a/Cargo.toml b/Cargo.toml index e66bec822d4f8..42629a780ccc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,8 +78,6 @@ oxc_transformer = { version = "0.4.0", path = "crates/oxc_transformer" } oxc_macros = { path = "crates/oxc_macros" } oxc_linter = { path = "crates/oxc_linter" } oxc_type_synthesis = { path = "crates/oxc_type_synthesis" } -oxc_query = { path = "crates/oxc_query" } -oxc_linter_plugin = { path = "crates/oxc_linter_plugin" } oxc_prettier = { path = "crates/oxc_prettier" } oxc_tasks_common = { path = "tasks/common" } @@ -130,7 +128,6 @@ static_assertions = { version = "1.1.0" } stacker = { version = "0.1.15" } tracing = { version = "0.1" } tracing-subscriber = { version = "0.3" } -trustfall = { version = "0.6.1" } insta = { version = "1.34.0", features = ["glob"] } codspeed-criterion-compat = { version = "2.3.3", default-features = false } glob = { version = "0.3.1" } diff --git a/README.md b/README.md index c792dce6f413b..6e04a22109212 100644 --- a/README.md +++ b/README.md @@ -155,13 +155,6 @@ this lets you run the linter without a Node.js installation in your CI. -#### Linter Plugin - -We are currently developing a DSL-based plugin system. -The plugin system uses [trustfall] as its query engine and a subset of GraphQL as its query language. - -You will not need to use JavaScript or Rust to write a plugin, this is useful for QAs and security researchers. - ### 🔸 Resolver Module resolution plays a crucial role in JavaScript tooling, especially for tasks like multi-file analysis or bundling. However, it can often become a performance bottleneck. @@ -254,7 +247,6 @@ And also - [@kaleidawave](https://github.com/kaleidawave) for [Ezno Type Checker](#-ezno-type-checker) - [@zackradisic](https://github.com/zackradisic) for [tyvm](https://github.com/zackradisic/tyvm) -- [@u9g](https://github.com/u9g) for the work in progress [oxc_linter_plugin](./crates/oxc_linter_plugin) ## 📖 License @@ -328,6 +320,5 @@ Oxc partially copies code from the following projects, their licenses are listed [swc]: https://swc.rs [tdewolff-minify]: https://github.com/tdewolff/minify [terser]: https://terser.org -[trustfall]: https://github.com/obi1kenobi/trustfall [vscode]: https://github.com/microsoft/vscode [@typescript-eslint]: https://typescript-eslint.io diff --git a/crates/oxc_language_server/Cargo.toml b/crates/oxc_language_server/Cargo.toml index c00e25b3df42a..d2dce2f942c84 100644 --- a/crates/oxc_language_server/Cargo.toml +++ b/crates/oxc_language_server/Cargo.toml @@ -20,21 +20,21 @@ name = "oxc_language_server" test = false [dependencies] -oxc_allocator = { workspace = true } -oxc_diagnostics = { workspace = true } -oxc_linter = { workspace = true } -oxc_parser = { workspace = true } -oxc_semantic = { workspace = true } -oxc_span = { workspace = true } -dashmap = { workspace = true } -env_logger = { workspace = true } -futures = { workspace = true } -ignore = { workspace = true, features = ["simd-accel"] } -miette = { workspace = true, features = ["fancy-no-backtrace"] } -ropey = { workspace = true } -tokio = { workspace = true, features = ["full"] } -tower-lsp = { workspace = true, features = ["proposed"] } -log = "0.4.20" -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -globset = "0.4.14" +oxc_allocator = { workspace = true } +oxc_diagnostics = { workspace = true } +oxc_linter = { workspace = true } +oxc_parser = { workspace = true } +oxc_semantic = { workspace = true } +oxc_span = { workspace = true } +dashmap = { workspace = true } +env_logger = { workspace = true } +futures = { workspace = true } +ignore = { workspace = true, features = ["simd-accel"] } +miette = { workspace = true, features = ["fancy-no-backtrace"] } +ropey = { workspace = true } +tokio = { workspace = true, features = ["full"] } +tower-lsp = { workspace = true, features = ["proposed"] } +log = "0.4.20" +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +globset = "0.4.14" diff --git a/crates/oxc_linter_plugin/Cargo.toml b/crates/oxc_linter_plugin/Cargo.toml deleted file mode 100644 index ad9a9ccc52a48..0000000000000 --- a/crates/oxc_linter_plugin/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -name = "oxc_linter_plugin" -version = "0.0.0" -publish = false -authors.workspace = true -description.workspace = true -edition.workspace = true -homepage.workspace = true -keywords.workspace = true -license.workspace = true -repository.workspace = true -rust-version.workspace = true -categories.workspace = true - -[lints] -workspace = true - -[lib] -doctest = false - -[dependencies] -oxc_span = { workspace = true } -oxc_query = { workspace = true } -oxc_diagnostics = { workspace = true } -oxc_semantic = { workspace = true } -oxc_linter = { workspace = true } -oxc_parser = { workspace = true } -oxc_allocator = { workspace = true } -ignore = { workspace = true } - -serde = { workspace = true, features = ["derive"] } -trustfall = { workspace = true } - -serde_yaml = "0.9.29" -located_yaml = "0.2.1" -path-calculate = "0.1.3" - -[dev_dependencies] -miette = { workspace = true, features = ["fancy"] } diff --git a/crates/oxc_linter_plugin/examples/queries/next/google-font-display/display-has-bad-value.yml b/crates/oxc_linter_plugin/examples/queries/next/google-font-display/display-has-bad-value.yml deleted file mode 100644 index 2704056713e7f..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/google-font-display/display-has-bad-value.yml +++ /dev/null @@ -1,158 +0,0 @@ -name: "next:google-font-display:display-has-bad-value" -query: | - query { - File { - jsx_element { - opening_element { - name @filter(op: "=", value: ["$link"]) - - span_: span { - start @output - end @output - } - - attribute { - name @filter(op: "=", value: ["$href"]) - value_as_constant_string @filter(op: "has_prefix", value: ["$href_value_start"]) - - value_as_url { - search_parameter @fold @transform(op: "count") @filter(op: ">", value:["$zero"]) { - key @filter(op: "=", value: ["$display"]) - value @filter(op: "one_of", value: ["$bad_display_values"]) - } - } - } - } - } - } - } - -args: - zero: 0 - link: "link" - href: "href" - display: "display" - href_value_start: "https://fonts.googleapis.com/css" - bad_display_values: - - "auto" - - "block" - - "fallback" - -summary: "Replace '{display_value}' with 'optional'" -reason: "Specifying display=optional minimizes the risk of invisible text or layout shift. If swapping to the custom font after it has loaded is important to you, then use display=swap instead." - -tests: - pass: - - relative_path: - - "index.tsx" - code: | - import Head from "next/head"; - - export default Test = () => { - return ( - - - - - - - ); - }; - - relative_path: - - "index.tsx" - code: | - import Document, { Html, Head } from "next/document"; - - class MyDocument extends Document { - render() { - return ( - - - - - - ); - } - } - - export default MyDocument; - - relative_path: - - "index.tsx" - code: | - import Document, { Html, Head } from "next/document"; - - class MyDocument extends Document { - render() { - return ( - - - - - - ); - } - } - - export default MyDocument; - fail: - - relative_path: - - "index.tsx" - code: | - import Head from "next/head"; - - export default Test = () => { - return ( - - - - ); - }; - - relative_path: - - "index.tsx" - code: | - import Head from "next/head"; - - export default Test = () => { - return ( - - - - ); - }; - - relative_path: - - "index.tsx" - code: | - import Head from "next/head"; - - export default Test = () => { - return ( - - - - ); - }; diff --git a/crates/oxc_linter_plugin/examples/queries/next/google-font-display/font-display-param-missing.yml b/crates/oxc_linter_plugin/examples/queries/next/google-font-display/font-display-param-missing.yml deleted file mode 100644 index 9c6652c1040b9..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/google-font-display/font-display-param-missing.yml +++ /dev/null @@ -1,124 +0,0 @@ -name: "next:google-font-display:font-display-param-missing" - -query: | - query { - File { - jsx_element { - opening_element { - name @filter(op: "=", value: ["$link"]) - - span_: span { - start @output - end @output - } - - attribute { - name @filter(op: "=", value: ["$href"]) - value_as_constant_string @filter(op: "has_prefix", value: ["$href_value_start"]) - - value_as_url { - search_parameter @fold @transform(op: "count") @filter(op: "=", value:["$zero"]) { - key @filter(op: "=", value: ["$display"]) - } - } - } - } - } - } - } - -args: - zero: 0 - link: "link" - href: "href" - display: "display" - href_value_start: "https://fonts.googleapis.com/css" - -summary: "A font-display parameter is missing (adding `&display=optional` is recommended)" -reason: "Specifying display=optional minimizes the risk of invisible text or layout shift. If swapping to the custom font after it has loaded is important to you, then use display=swap instead." - -tests: - pass: - - relative_path: - - "index.tsx" - code: | - import Head from "next/head"; - - export default Test = () => { - return ( - - - - - - - ); - }; - - relative_path: - - "index.tsx" - code: | - import Document, { Html, Head } from "next/document"; - - class MyDocument extends Document { - render() { - return ( - - - - - - ); - } - } - - export default MyDocument; - - relative_path: - - "index.tsx" - code: | - import Document, { Html, Head } from "next/document"; - - class MyDocument extends Document { - render() { - return ( - - - - - - ); - } - } - - export default MyDocument; - fail: - - relative_path: - - "index.tsx" - code: | - import Head from "next/head"; - - export default Test = () => { - return ( - - - - ); - }; diff --git a/crates/oxc_linter_plugin/examples/queries/next/google-font-preconnect/rule.yml b/crates/oxc_linter_plugin/examples/queries/next/google-font-preconnect/rule.yml deleted file mode 100644 index 8776262cc090e..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/google-font-preconnect/rule.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: "next:google-font-preconnect" - -query: | - query { - File { - jsx_element { - opening_element { - name @filter(op: "=", value: ["$link"]) - - span_: span { - start @output - end @output - } - - attribute @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { - name @filter(op: "=", value: ["$rel"]) - value_as_constant_string @filter(op: "=", value: ["$preconnect"]) - } - - attribute { - name @output @filter(op: "=", value: ["$href"]) - value_as_constant_string @output @filter(op: "has_prefix", value: ["$href_value_start"]) - } - } - } - } - } - -args: - zero: 0 - link: "link" - href: "href" - rel: "rel" - preconnect: "preconnect" - href_value_start: "https://fonts.gstatic.com" - -summary: "Add `rel=\"preconnect\"`" -reason: "Adding preconnect is recommended to initiate an early connection to the origin." - -tests: - pass: - - relative_path: - - "index.tsx" - code: | - export const Test = () => ( -
- - - -
- ) - fail: - - relative_path: - - "index.tsx" - code: | - export const Test = () => ( -
- -
- ) - - relative_path: - - "index.tsx" - code: | - export const Test = () => ( -
- -
- ) diff --git a/crates/oxc_linter_plugin/examples/queries/next/inline-script-id/dangerouslySetInnerHTML.yml b/crates/oxc_linter_plugin/examples/queries/next/inline-script-id/dangerouslySetInnerHTML.yml deleted file mode 100644 index 14709f5c2d804..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/inline-script-id/dangerouslySetInnerHTML.yml +++ /dev/null @@ -1,172 +0,0 @@ -name: "next:inline-script-id:dangerouslySetInnerHTML" - -query: | - query { - File { - import { - from_path @filter(op: "=", value: ["$nextslashscript"]) - - default_import { - local_name @tag - } - } - - jsx_element { - opening_element { - name @filter(op: "=", value: ["%local_name"]) - span_: span { - start @output - end @output - } - spread_attribute @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) - attribute @fold @transform(op: "count") @filter(op: ">", value: ["$zero"]) { - name @filter(op: "=", value: ["$dangerouslySetInnerHTML"]) - } - attribute @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { - name @filter(op: "=", value: ["$id"]) - } - } - } - } - } - -args: - zero: 0 - dangerouslySetInnerHTML: "dangerouslySetInnerHTML" - nextslashscript: "next/script" - id: "id" - -summary: "Add an `id` attribute" -reason: "`next/script` components with inline content require an `id` attribute to be defined to track and optimize the script." - -tests: - pass: - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script'; - - export default function TestPage() { - return ( - - ) - } - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script'; - - export default function TestPage() { - return ( - - ) - } - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script'; - - export default function TestPage() { - return ( - - ) - } - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script'; - const spread = { strategy: "lazyOnload" } - export default function TestPage() { - return ( - - ) - } - fail: - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script'; - - export default function TestPage() { - return ( - - ) - } - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script'; - - export default function TestPage() { - return ( - - ) - } - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script'; - - export default function TestPage() { - return ( - - ) - } - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script'; - const spread = { strategy: "lazyOnload" } - export default function TestPage() { - return ( - - ) - } - fail: - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script'; - - export default function TestPage() { - return ( - - ) - } - - relative_path: - - "index.tsx" - code: | - import MyScript from 'next/script'; - - export default function TestPage() { - return ( - - {`console.log('Hello world');`} - - ) - } diff --git a/crates/oxc_linter_plugin/examples/queries/next/no-assign-module-variable/rule.yml b/crates/oxc_linter_plugin/examples/queries/next/no-assign-module-variable/rule.yml deleted file mode 100644 index e51f46ff43634..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/no-assign-module-variable/rule.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: "next:inline-script-id:no-assign-module-variable" - -query: | - query { - File { - variable_declaration { - left { - assignment_to_variable_name @filter(op: "=", value: ["$module"]) - span_: span { - start @output - end @output - } - } - } - } - } - -args: - module: "module" - -summary: "Do not name this variable `module`" -reason: "The `module` variable is already used and it is highly likely that assigning to this variable will cause errors." - -tests: - pass: - - relative_path: - - "index.tsx" - code: | - let myModule = {}; - - export default function MyComponent() { - return <> - } - fail: - - relative_path: - - "index.tsx" - code: | - let module = {}; - - export default function MyComponent() { - return <> - } diff --git a/crates/oxc_linter_plugin/examples/queries/next/no-before-interactive-script-outside-document/rule.yml b/crates/oxc_linter_plugin/examples/queries/next/no-before-interactive-script-outside-document/rule.yml deleted file mode 100644 index d55abdd942551..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/no-before-interactive-script-outside-document/rule.yml +++ /dev/null @@ -1,298 +0,0 @@ -name: "next:no-before-interactive-script-outside-document" - -query: | - query { - File { - import { - from_path @filter(op: "=", value: ["$nextslashscript"]) - default_import { - local_name @tag(name: "default_imported_class_name") - } - } - - jsx_element { - opening_element { - name @filter(op: "=", value: ["%default_imported_class_name"]) - attribute { - name @filter(op: "=", value: ["$strategy"]) - value_as_constant_string @filter(op: "=", value: ["$beforeInteractive"]) - } - span_: span { - start @output - end @output - } - } - } - - #### #### #### IGNORE ./src/app or ./app #### #### #### - - # ./app - path_part @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { - is_first @filter(op: "=", value: ["$true"]) - name @filter(op: "=", value: ["$app"]) - } - - # ./src/app - path_part @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { - is_first @filter(op: "=", value: ["$true"]) - name @filter(op: "=", value: ["$src"]) - next { - name @filter(op: "=", value: ["$app"]) - } - } - #### #### #### ALLOW ONLY pages/_document.* #### #### #### - - path_part @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { - name @filter(op: "=", value: ["$pages"]) - next { - name @filter(op: "has_prefix", value: ["$underscore_document"]) - } - } - } - } - -args: - nextslashscript: "next/script" - strategy: "strategy" - beforeInteractive: "beforeInteractive" - src: "src" - app: "app" - pages: "pages" - underscore_document: "_document" - zero: 0 - true: true - -summary: "Do not use `next/script`'s `beforeInteractive` strategy should not be used outside of `pages/_document.js`" -reason: | - You cannot use the `next/script` component with the beforeInteractive strategy outside pages/_document.js. - That's because `beforeInteractive` strategy only works inside `pages/_document.js` and is designed to load scripts - that are needed by the entire site (i.e. the script will load when any page in the application has been loaded server-side). - -tests: - pass: - - relative_path: - - "pages" - - "_document.js" - code: | - import Document, { Html, Main, NextScript } from 'next/document' - import Script from 'next/script' - - class MyDocument extends Document { - render() { - return ( - - - - - -
- - - - - ) - } - } - - export default MyDocument - - relative_path: - - "pages" - - "_document.tsx" - - code: | - import Document, { Html, Main, NextScript } from 'next/document' - import ScriptComponent from 'next/script' - - class MyDocument extends Document { - render() { - return ( - - - - - -
- - - - - ) - } - } - - export default MyDocument - - relative_path: - - "pages" - - "_document.tsx" - - code: | - import Document, { Html, Main, NextScript } from 'next/document' - import ScriptComponent from 'next/script' - - class MyDocument extends Document { - render() { - return ( - - - - - -
- - - - - ) - } - } - - export default MyDocument - - relative_path: - - "app" - - "deep" - - "root" - - "layout.tsx" - code: | - import Script from "next/script"; - - export default function Index() { - return ( - - ); - } - - relative_path: - - "app" - - "deep" - - "page.tsx" - code: | - import Script from "next/script"; - - export default function Index() { - return ( - - ); - } - - relative_path: - - "app" - - "deep" - - "randomFile.tsx" - code: | - import Script from "next/script"; - - export default function Index() { - return ( - - ); - } - - relative_path: - - "src" - - "app" - - "deep" - - "randomFile.tsx" - code: | - import Script from "next/script"; - - export default function Index() { - return ( - - ); - } - - relative_path: - - "src" - - "app" - - "layout.tsx" - code: | - import Script from "next/script"; - - export default function Index() { - return ( - - {children} - - ); - } - - - relative_path: - - "components" - - "outside-known-dirs.js" - - code: | - import Head from "next/head"; - import Script from "next/script"; - - export default function Index() { - return ( - - ); - } diff --git a/crates/oxc_linter_plugin/examples/queries/next/no-css-tags/rule.yml b/crates/oxc_linter_plugin/examples/queries/next/no-css-tags/rule.yml deleted file mode 100644 index b751179d3efb8..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/no-css-tags/rule.yml +++ /dev/null @@ -1,119 +0,0 @@ -name: "next:no-css-tags" - -query: | - query { - File { - jsx_element { - opening_element { - name @filter(op: "=", value: ["$link"]) - - span_: span { - start @output - end @output - } - - attribute @fold @transform(op: "count") @filter(op: ">", value: ["$zero"]) { - name @filter(op: "=", value: ["$rel"]) - value_as_constant_string @filter(op: "=", value: ["$stylesheet"]) - } - - attribute @fold @transform(op: "count") @filter(op: ">", value: ["$zero"]) { - name @output @filter(op: "=", value: ["$href"]) - value_as_constant_string @output @filter(op: "not_regex", value: ["$https_regex"]) - } - } - } - } - } - -args: - zero: 0 - link: "link" - href: "href" - rel: "rel" - stylesheet: "stylesheet" - https_regex: "^https?" - -summary: "Do not include stylesheets manually, prefer `@import` in CSS or CSS modules." -reason: "A link element was used to link to an external stylesheet. This can negatively affect CSS resource loading on your webpage." - -tests: - pass: - - relative_path: - - "index.tsx" - code: | - import {Head} from 'next/document'; - - export class Blah extends Head { - render() { - return ( -
-

Hello title

-
- ); - } - } - - relative_path: - - "index.tsx" - code: | - import {Head} from 'next/document'; - export class Blah extends Head { - render() { - return ( -
-

Hello title

- -
- ); - } - } - - relative_path: - - "index.tsx" - code: | - import {Head} from 'next/document'; - export class Blah extends Head { - render(props) { - return ( -
-

Hello title

- -
- ); - } - } - - relative_path: - - "index.tsx" - code: | - import {Head} from 'next/document'; - export class Blah extends Head { - render(props) { - return ( -
-

Hello title

- -
- ); - } - } - fail: - - relative_path: - - "index.tsx" - code: | - import {Head} from 'next/document'; - - export class Blah extends Head { - render() { - return ( -
-

Hello title

- -
- ); - } - } - - relative_path: - - "index.tsx" - code: | -
- -
diff --git a/crates/oxc_linter_plugin/examples/queries/next/no-document-import-in-page/rule.yml b/crates/oxc_linter_plugin/examples/queries/next/no-document-import-in-page/rule.yml deleted file mode 100644 index cbb53ae37501e..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/no-document-import-in-page/rule.yml +++ /dev/null @@ -1,166 +0,0 @@ -name: "next:no-document-import-in-page" - -query: | - query { - File { - import { - from_path @filter(op: "=", value: ["$nextslashdocument"]) - span_: entire_span { - start @output - end @output - } - } - - path_part @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { - name @filter(op: "=", value: ["$pages"]) - next { - name @filter(op: "has_prefix", value: ["$underscore_document"]) - } - } - } - } - -args: - nextslashdocument: "next/document" - pages: "pages" - underscore_document: "_document" - zero: 0 - -summary: "Do not import `` from `next/document` outside of `pages/_document.js`." -reason: "This can cause unexpected issues in your application." - -tests: - pass: - - relative_path: - - "pages" - - "_document.js" - - code: | - import Document from "next/document" - - export default class MyDocument1 extends Document { - render() { - return ( - - - ); - } - } - - - relative_path: - - "pages" - - "_document.js" - - code: | - import Document from "next/document" - - export default class MyDocument2 extends Document { - render() { - return ( - - - ); - } - } - - - relative_path: - - "pages" - - "_document.tsx" - - code: | - import NextDocument from "next/document" - - export default class MyDocument3 extends NextDocument { - render() { - return ( - - - ); - } - } - - - relative_path: - - "pages" - - "_document.page.tsx" - - code: | - import Document from "next/document" - - export default class MyDocument4 extends Document { - render() { - return ( - - - ); - } - } - - - relative_path: - - "pages" - - "_document" - - "index.js" - - code: | - import NDocument1 from "next/document" - - export default class Document extends NDocument1 { - render() { - return ( - - - ); - } - } - - - relative_path: - - "pages" - - "_document" - - "index.tsx" - - code: | - import NDocument2 from "next/document" - - export default class Document extends NDocument2 { - render() { - return ( - - - ); - } - } - - - relative_path: - - "pagesapp" # not a typo - - "src" - - "pages" - - "_document.js" - - code: | - import Document from "next/document" - - export default class MyDocument extends Document { - render() { - return ( - - - ); - } - } - fail: - - relative_path: - - "components" - - "tests.js" - - code: | - import Document from "next/document" - - export const Test = () =>

Test

- - - relative_path: - - "pages" - - "tests.js" - - code: | - import Document from "next/document" - - export const Test = () =>

Test

diff --git a/crates/oxc_linter_plugin/examples/queries/next/no-duplicate-head/rule.DOESNTRUN b/crates/oxc_linter_plugin/examples/queries/next/no-duplicate-head/rule.DOESNTRUN deleted file mode 100644 index 5092104f3e5be..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/no-duplicate-head/rule.DOESNTRUN +++ /dev/null @@ -1,159 +0,0 @@ -name: "next:no-duplicate-head" - -# We don't run this lint now beacuause it outputs a List for span_start and span_end which we don't support yet - -query: | - query { - File { - import { - from_path @filter(op: "=", value: ["$nextslashdocument"]) - - default_import { - local_name @tag(name: "default_imported_class_name") - } - } - - ast_node { - ... on ReturnAST { - ancestor { - ... on ClassAST { - extended_class_name @filter(op: "=", value: ["%default_imported_class_name"]) - } - } - - expression { - strip_parens { - ... on JSXElement { - # Select a `Head` element. - child_element { - opening_element { - name @filter(op: "=", value: ["$Head"]) - span_: span { - start @tag - } - } - } - - # Ensure that selected `Head` element is the earliest one. - # This is so that if there are e.g. 3 such elements, - # we don't report the combo of the second and third as an additional violation. - child_element @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { - opening_element { - name @filter(op: "=", value: ["$Head"]) - span_: span { - start @filter(op: "<", value: ["%start"]) - } - } - } - - # Ensure there are more `Head` elements after the selected one. - child_element @fold @transform(op: "count") @filter(op: ">", value: ["$zero"]) { - opening_element { - name @filter(op: "=", value: ["$Head"]) - span_: span { - start @output @filter(op: ">", value: ["%start"]) - end @output - } - } - } - } - } - } - } - } - } - } - -args: - zero: 0 - nextslashdocument: "next/document" - Head: "Head" - -summary: "Do not include multiple instances of ``." -reason: "This can cause unexpected behavior in your application." - -tests: - pass: - - relative_path: - - "pages" - - "_document.tsx" - - code: | - import Document, { Html, Head, Main, NextScript } from 'next/document' - - class MyDocument extends Document { - render() { - return ( - - - - - - - ) - } - } - - export default MyDocument - - fail: - - relative_path: - - "pages" - - "_document.js" - - code: | - import Document, { Html, Main, NextScript } from 'next/document' - import Head from 'next/head' - - class MyDocument extends Document { - render() { - return ( - - - - - - ) - } - } - - export default MyDocument - - relative_path: - - "pages" - - "_document.page.tsx" - - code: | - import Document, { Html, Main, NextScript } from 'next/document' - import Head from 'next/head' - - class MyDocument extends Document { - render() { - return ( - - - - - - -
- - - - - - ); - } - fail: - - relative_path: - - "index.tsx" - code: | - import Head from "next/head"; - import Script from "next/script"; - - export default function Index() { - return ( - - - - ); - } diff --git a/crates/oxc_linter_plugin/examples/queries/next/no-styled-jsx-in-document/match-_document-then-index.yml b/crates/oxc_linter_plugin/examples/queries/next/no-styled-jsx-in-document/match-_document-then-index.yml deleted file mode 100644 index 08aa4f9c5ec06..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/no-styled-jsx-in-document/match-_document-then-index.yml +++ /dev/null @@ -1,150 +0,0 @@ -name: "next:no-styled-jsx-in-document:match-_document-then-index" - -query: | - query { - File { - jsx_element { - opening_element { - name @filter(op: "=", value: ["$style"]) - - attribute @fold @transform(op: "count") @filter(op: ">", value: ["$zero"]) { - name @filter(op: "=", value: ["$jsx"]) - } - - span_: span { - start @output - end @output - } - } - } - - last_path_part { - name @filter(op: "has_prefix", value: ["$index"]) # TODO: this shouldn't be has_prefix, it really should be = to the file name, and ignore the extension - prev { - name @filter(op: "=", value: ["$underscore_document"]) - prev { - name @filter(op: "=", value: ["$pages"]) - } - } - } - } - } - -args: - zero: 0 - style: "style" - jsx: "jsx" - pages: "pages" - underscore_document: "_document" - index: "index" - -summary: "`styled-jsx` should not be used in `pages/_document.js" -reason: "Custom CSS like `styled-jsx` is not allowed in a Custom Document." - -tests: - pass: - - relative_path: - - "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 ( - - - -
- - - - ) - } - } - - relative_path: - - "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 ( - - - - - -
- - - - ) - } - } - - relative_path: - - "pages" - - "index.js" - - code: | - export default function Page() { - return ( - <> -

Hello world

- - - ) - } - - fail: - - relative_path: - - "pages" - - "_document" - - "index.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 ( - - - - -
- - - - ) - } - } diff --git a/crates/oxc_linter_plugin/examples/queries/next/no-styled-jsx-in-document/match-starts-with-_document.yml b/crates/oxc_linter_plugin/examples/queries/next/no-styled-jsx-in-document/match-starts-with-_document.yml deleted file mode 100644 index 7827030cda792..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/no-styled-jsx-in-document/match-starts-with-_document.yml +++ /dev/null @@ -1,146 +0,0 @@ -name: "next:no-styled-jsx-in-document:match-starts-with-_document" - -query: | - query { - File { - jsx_element { - opening_element { - name @filter(op: "=", value: ["$style"]) - - attribute @fold @transform(op: "count") @filter(op: ">", value: ["$zero"]) { - name @filter(op: "=", value: ["$jsx"]) - } - - span_: span { - start @output - end @output - } - } - } - - last_path_part { - name @filter(op: "has_prefix", value: ["$underscore_document"]) - prev { - name @filter(op: "=", value: ["$pages"]) - } - } - } - } - -args: - zero: 0 - style: "style" - jsx: "jsx" - pages: "pages" - underscore_document: "_document" - -summary: "`styled-jsx` should not be used in `pages/_document.js" -reason: "Custom CSS like `styled-jsx` is not allowed in a Custom Document." - -tests: - pass: - - relative_path: - - "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 ( - - - -
- - - - ) - } - } - - relative_path: - - "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 ( - - - - - -
- - - - ) - } - } - - relative_path: - - "pages" - - "index.js" - - code: | - export default function Page() { - return ( - <> -

Hello world

- - - ) - } - - fail: - - relative_path: - - "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 ( - - - - -
- - - - ) - } - } diff --git a/crates/oxc_linter_plugin/examples/queries/next/no-sync-scripts/rule.yml b/crates/oxc_linter_plugin/examples/queries/next/no-sync-scripts/rule.yml deleted file mode 100644 index 9d118503822fd..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/no-sync-scripts/rule.yml +++ /dev/null @@ -1,100 +0,0 @@ -name: "next:no-sync-scripts" - -query: | - query { - File { - jsx_element { - opening_element { - name @filter(op: "=", value: ["$script"]) - - span_: span { - start @output - end @output - } - - attribute @fold @transform(op: "count") @filter(op: ">", value: ["$zero"]) { - name @filter(op: "=", value: ["$src"]) - } - - attribute @fold @transform(op: "count") @filter(op: "=", value: ["$zero"]) { - name @output @filter(op: "one_of", value: ["$async_or_defer"]) - } - } - } - } - } - -args: - script: "script" - zero: 0 - src: "src" - async_or_defer: - - "async" - - "defer" - -summary: "Synchronous scripts should not be used, prefer to use async or defer." -reason: "A synchronous scripts can impact your webpage performance." - -tests: - pass: - - relative_path: - - "index.tsx" - code: | - import {Head} from 'next/document'; - - export class Blah extends Head { - render() { - return ( -
-

Hello title

- -
- ); - } - } - - relative_path: - - "index.tsx" - code: | - import {Head} from 'next/document'; - - export class Blah extends Head { - render(props) { - return ( -
-

Hello title

- -
- ); - } - } - fail: - - relative_path: - - "index.tsx" - code: | - import {Head} from 'next/document'; - - export class Blah extends Head { - render() { - return ( -
-

Hello title

- -
- ); - } - } - - relative_path: - - "index.tsx" - code: | - import {Head} from 'next/document'; - - export class Blah extends Head { - render(props) { - return ( -
-

Hello title

- -
- ); - } - } diff --git a/crates/oxc_linter_plugin/examples/queries/next/no-title-in-document-head/rule.yml b/crates/oxc_linter_plugin/examples/queries/next/no-title-in-document-head/rule.yml deleted file mode 100644 index 91d7e50c1a4ba..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/no-title-in-document-head/rule.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: "next:no-title-in-document-head" - -query: | - query { - File { - import { - from_path @filter(op: "=", value: ["$nextslashdocument"]) - - specific_import @fold @transform(op: "count") @filter(op: ">", value: ["$zero"]) { - original_name @filter(op: "=", value: ["$Head"]) - } - } - - jsx_element { - opening_element { - name @filter(op: "=", value: ["$title"]) - span_: span { - start @output - end @output - } - } - } - } - } - -args: - Head: "Head" - zero: 0 - title: "title" - nextslashdocument: "next/document" - -summary: "Do not use `` element with `<Head />` component from `next/document`." -reason: "Titles should defined at the page-level using `<Head />` from `next/head` instead." - -tests: - pass: - - relative_path: - - "index.tsx" - code: | - import Head from "next/head"; - - class Test { - render() { - return ( - <Head> - <title>My page title - - ); - } - } - - relative_path: - - "index.tsx" - code: | - import Document, { Html, Head } from "next/document"; - - class MyDocument extends Document { - render() { - return ( - - - - - ); - } - } - - export default MyDocument; - fail: - - relative_path: - - "index.tsx" - code: | - import { Head } from "next/document"; - - class Test { - render() { - return ( - - My page title - - ); - } - } diff --git a/crates/oxc_linter_plugin/examples/queries/next/script-for-ga/src-check.yml b/crates/oxc_linter_plugin/examples/queries/next/script-for-ga/src-check.yml deleted file mode 100644 index e599f50282dbc..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/script-for-ga/src-check.yml +++ /dev/null @@ -1,237 +0,0 @@ -name: "next:script-for-ga:src-check" - -query: | - query { - File { - jsx_element { - opening_element { - name @filter(op: "=", value: ["$script"]) - - span_: span { - start @output - end @output - } - - attribute @fold @transform(op: "count") @filter(op: ">", value: ["$zero"]) { - name @filter(op: "=", value: ["$src"]) - value_as_constant_string @filter(op: "regex", value: ["$g_analytics_urls_regex"]) - } - } - } - } - } - -args: - zero: 0 - script: "script" - src: "src" - g_analytics_urls_regex: "(www\\.google-analytics\\.com\\/analytics\\.js)|(www\\.googletagmanager\\.com\\/gtag\\/js)" - -summary: "Prefer `next/script` component when using the inline script for Google Analytics." -reason: "An inline script was used for Google analytics which might impact your webpage's performance." - -tests: - pass: - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script' - - export class Blah extends Head { - render() { - return ( -
-

Hello title

- -
- ); - } - } - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script' - - export class Blah extends Head { - render() { - return ( -
-

Hello title

- -
- ); - } - } - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script' - - export class Blah extends Head { - render() { - return ( -
-

Hello title

- -
- ); - } - } - - relative_path: - - "index.tsx" - code: | - export class Blah extends Head { - render() { - return ( -
-

Hello title

- -
- ); - } - } - - relative_path: - - "index.tsx" - code: | - export class Blah extends Head { - createGoogleAnalyticsMarkup() { - return { - __html: ` - window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments);} - gtag('js', new Date()); - gtag('config', 'UA-148481588-2');`, - }; - } - - render() { - return ( -
-

Hello title

- -
- ); - } - } diff --git a/crates/oxc_linter_plugin/examples/queries/next/script-for-ga/src-with-dangeroushtml.yml b/crates/oxc_linter_plugin/examples/queries/next/script-for-ga/src-with-dangeroushtml.yml deleted file mode 100644 index 2b2b0a3792a83..0000000000000 --- a/crates/oxc_linter_plugin/examples/queries/next/script-for-ga/src-with-dangeroushtml.yml +++ /dev/null @@ -1,244 +0,0 @@ -name: "next:script-for-ga:src-with-dangeroushtml" - -query: | - query { - File { - jsx_element { - opening_element { - name @filter(op: "=", value: ["$script"]) - - span_: span { - start @output - end @output - } - - attribute @fold @transform(op: "count") @filter(op: ">", value: ["$zero"]) { - name @filter(op: "=", value: ["$dangerouslySetInnerHTML"]) - - value_as_expression @fold @transform(op: "count") @filter(op: ">", value: ["$zero"]) { - ... on ObjectLiteral { - value(key: "__html") { - as_constant_string @filter(op: "regex", value: ["$g_analytics_urls_regex"]) - } - } - } - } - } - } - } - } - -args: - zero: 0 - script: "script" - dangerouslySetInnerHTML: "dangerouslySetInnerHTML" - g_analytics_urls_regex: "(www\\.google-analytics\\.com\\/analytics\\.js)|(www\\.googletagmanager\\.com\\/gtm\\.js)" - -summary: "Prefer `next/script` component when using the inline script for Google Analytics." -reason: "An inline script was used for Google analytics which might impact your webpage's performance." - -tests: - pass: - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script' - - export class Blah extends Head { - render() { - return ( -
-

Hello title

- -
- ); - } - } - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script' - - export class Blah extends Head { - render() { - return ( -
-

Hello title

- -
- ); - } - } - - relative_path: - - "index.tsx" - code: | - import Script from 'next/script' - - export class Blah extends Head { - render() { - return ( -
-

Hello title

- -
- ); - } - } - - relative_path: - - "index.tsx" - code: | - export class Blah extends Head { - render() { - return ( -
-

Hello title

- -
- ); - } - } - - relative_path: - - "index.tsx" - code: | - export class Blah extends Head { - createGoogleAnalyticsMarkup() { - return { - __html: ` - window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments);} - gtag('js', new Date()); - gtag('config', 'UA-148481588-2');`, - }; - } - - render() { - return ( -
-

Hello title

- -
- ); - } - } - fail: - - relative_path: - - "index.tsx" - code: | - export class Blah extends Head { - render() { - return ( -
-

Hello title

qqq - {/* Google Tag Manager - Global base code */} -