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 (
              <Html>
                <Head>
                  <meta charSet="utf-8" />
                </Head>
                <body>
                  <Main />
                  <NextScript />
                  <Script
                    id="scriptBeforeInteractive"
                    src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.16/lodash.min.js?a=scriptBeforeInteractive"
                    strategy="beforeInteractive"
                  ></Script>
                </body>
              </Html>
            )
          }
        }

        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 (
              <Html>
                <Head>
                  <meta charSet="utf-8" />
                </Head>
                <body>
                  <Main />
                  <NextScript />
                  <ScriptComponent
                    id="scriptBeforeInteractive"
                    src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.17/lodash.min.js?a=scriptBeforeInteractive"
                    strategy="beforeInteractive"
                  ></ScriptComponent>
                </body>
              </Html>
            )
          }
        }

        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 (
              <Html>
                <Head>
                  <meta charSet="utf-8" />
                </Head>
                <body>
                  <Main />
                  <NextScript />
                  <ScriptComponent
                    id="scriptBeforeInteractive"
                    src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.18/lodash.min.js?a=scriptBeforeInteractive"
                  ></ScriptComponent>
                </body>
              </Html>
            )
          }
        }

        export default MyDocument
    - relative_path:
        - "app"
        - "deep"
        - "root"
        - "layout.tsx"
      code: |
        import Script from "next/script";

        export default function Index() {
          return (
            <Script
              id="scriptBeforeInteractive"
              src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.19/lodash.min.js?a=scriptBeforeInteractive"
              strategy="beforeInteractive"
            ></Script>
          );
        }
    - relative_path:
        - "app"
        - "deep"
        - "page.tsx"
      code: |
        import Script from "next/script";

        export default function Index() {
          return (
            <Script
              id="scriptBeforeInteractive"
              src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js?a=scriptBeforeInteractive"
              strategy="beforeInteractive"
            ></Script>
          );
        }
    - relative_path:
        - "app"
        - "deep"
        - "randomFile.tsx"
      code: |
        import Script from "next/script";

        export default function Index() {
          return (
            <Script
              id="scriptBeforeInteractive"
              src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js?a=scriptBeforeInteractive"
              strategy="beforeInteractive"
            ></Script>
          );
        }
    - relative_path:
        - "src"
        - "app"
        - "deep"
        - "randomFile.tsx"
      code: |
        import Script from "next/script";

        export default function Index() {
          return (
            <Script
              id="scriptBeforeInteractive"
              src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.22/lodash.min.js?a=scriptBeforeInteractive"
              strategy="beforeInteractive"
            ></Script>
          );
        }
    - relative_path:
        - "src"
        - "app"
        - "layout.tsx"
      code: |
        import Script from "next/script";

        export default function Index() {
          return (
            <html lang="en">
              <body className={inter.className}>{children}</body>
              <Script
                src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.23/lodash.min.js?a=scriptBeforeInteractive"
                strategy='beforeInteractive'
              />
            </html>
          );
        }
    - relative_path:
        - "app"
        - "layout.tsx"
      code: |
        import Script from "next/script";

        export default function Index() {
          return (
            <html lang="en">
              <body className={inter.className}>{children}</body>
              <Script
                src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.24/lodash.min.js?a=scriptBeforeInteractive"
                strategy='beforeInteractive'
              />
            </html>
          );
        }

  fail:
    - relative_path:
        - "pages"
        - "index.js"

      code: |
        import Head from "next/head";
        import Script from "next/script";

        export default function Index() {
          return (
            <Script
              id="scriptBeforeInteractive"
              src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.25/lodash.min.js?a=scriptBeforeInteractive"
              strategy="beforeInteractive"
            ></Script>
          );
        }

    - relative_path:
        - "components"
        - "outside-known-dirs.js"

      code: |
        import Head from "next/head";
        import Script from "next/script";

        export default function Index() {
          return (
            <Script
              id="scriptBeforeInteractive"
              src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js?a=scriptBeforeInteractive"
              strategy="beforeInteractive"
            ></Script>
          );
        }
