Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditionally enable react-scan #47

Open
jordan-cutler opened this issue Nov 28, 2024 · 11 comments
Open

Conditionally enable react-scan #47

jordan-cutler opened this issue Nov 28, 2024 · 11 comments
Assignees

Comments

@jordan-cutler
Copy link

Question

Hi there 👋, the README suggests the following to use react-scan programmatically:

import { scan } from 'react-scan'; // import this BEFORE react
import React from 'react';

if (typeof window !== 'undefined') {
  scan({
    enabled: true,
    log: true, // logs render info to console (default: false)
  });
}

However, if we want to only load react-scan in local builds, and allow for easy enabling and disabling, is that possible? If so, how?

Ideal API

My ideal API would be something like this:

import React from 'react';

function enableScanning() {
  import('react-scan').then(({ scan }) => {
    scan({
      enabled: true,
      log: true,
    });
  });
}

const AdminPanel = () => {
   return (
       ...
      <button onClick={enableScanning}>Enable React Scan</button>
      <button onClick={disableScanning}>Disable React Scan</button>
       ...
   )
}

But given the comment about react-scan needing to be imported before react, it seems like that won't be possible. Is that accurate?

@RobPruzan
Copy link
Collaborator

This shouldn't be a problem to add (but currently not possible). We can probably add this in the next release

@NisargIO
Copy link
Collaborator

NisargIO commented Dec 2, 2024

@jordan-cutler You can do something like this in your Layout.

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html>
      {process.env.NODE_ENV === "development" && (
        <head>
          <script
            src="https://unpkg.com/react-scan/dist/auto.global.js"
            async
          />
        </head>
      )}
      <body>
        ...
      </body>
    </html>
  );
}

@cameronb23
Copy link

Definitely second this! Would be super useful to be able to toggle it on and off on a developer build.

@Toumash
Copy link

Toumash commented Dec 7, 2024

Im using vite/typescript (not sure) conditional imports.
Here's a complete configuration.

  1. Add the imports key into package.json
  2. Add the devtools.dev.ts (with react-scan) and devtools.prod.ts (empty) files
  3. Import the files before react import (in your app entrypoint)
  4. Switch by --mode production or --mode development like this
vite --mode development
vite build --mode production
From 5816eba0a62c14d386bb590bad4b0426c2a8a4d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20D=C5=82uski?= 
Date: Sun, 20 Aug 2023 03:38:58 +0200
Subject: [PATCH] feat: make react-devtools import conditional (only on dev
 build)

---
 extension/package.json          | 6 ++++++
 extension/src/content/index.tsx | 2 +-
 extension/src/devtools.dev.ts   | 1 +
 extension/src/devtools.prod.ts  | 0
 extension/tsconfig.json         | 3 ++-
 5 files changed, 10 insertions(+), 2 deletions(-)
 create mode 100644 extension/src/devtools.dev.ts
 create mode 100644 extension/src/devtools.prod.ts

diff --git a/extension/package.json b/extension/package.json
index 51ece72d..73a673da 100644
--- a/extension/package.json
+++ b/extension/package.json
@@ -98,5 +98,11 @@
     "vite": "^4.4.6",
     "vite-plugin-eslint": "^1.8.1",
     "vitest": "^0.33.0"
+  },
+  "imports": {
+    "#env-import/*.ts": {
+      "development": "./src/*.dev.ts",
+      "production": "./src/*.prod.ts"
+    }
   }
 }
diff --git a/extension/src/content/index.tsx b/extension/src/content/index.tsx
index 07e630eb..254243dd 100644
--- a/extension/src/content/index.tsx
+++ b/extension/src/content/index.tsx
@@ -1,4 +1,4 @@
-import 'react-devtools';
+import '#env-import/devtools.ts';
 import React from 'react';
 import { createRoot } from 'react-dom/client';
 
diff --git a/extension/src/devtools.dev.ts b/extension/src/devtools.dev.ts
new file mode 100644
index 00000000..b33e2a07
--- /dev/null
+++ b/extension/src/devtools.dev.ts
@@ -0,0 +1 @@
+import 'react-devtools';
\ No newline at end of file
diff --git a/extension/src/devtools.prod.ts b/extension/src/devtools.prod.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/extension/tsconfig.json b/extension/tsconfig.json
index 52defc17..569dd3f9 100644
--- a/extension/tsconfig.json
+++ b/extension/tsconfig.json
@@ -11,7 +11,7 @@
             "ESNext"
         ],
         "module": "ESNext",
-        "moduleResolution": "node",
+        "moduleResolution": "bundler",
         "noImplicitReturns": true,
         "noUnusedLocals": true,
         "strict": true,
@@ -25,6 +25,7 @@
         "isolatedModules": true,
         "noEmit": true,
         "allowJs": true,
+        "allowImportingTsExtensions": true,
         "typeRoots": [
             "./types",
             "./node_modules/@types"
-- 
6.45.2.windows.1

@WickyNilliams
Copy link

I'd like to add react scan locally in dev, but also be able to toggle it on/off.

For example, I have some local-only UI widget that has some common dev utils. I'd like to add a button there toggle react scan on/off. Is this possible?

@Toumash
Copy link

Toumash commented Dec 10, 2024

I'd like to add react scan locally in dev, but also be able to toggle it on/off.

For example, I have some local-only UI widget that has some common dev utils. I'd like to add a button there toggle react scan on/off. Is this possible?

#79

@DAcodedBEAT
Copy link

I hacked together a custom vite plugin for now, but would love to see a vite-plugin as a first-class citizen:

import { Plugin } from 'vite';

export default function reactScanPlugin(options = {}) {
    const defaultScanConfig = {
        enabled: true,
        log: true,
    };
    const scanConfig = { ...defaultScanConfig, ...options };

    return {
        name: 'vite-plugin-react-scan',
        enforce: 'post',
        transform(code, id) {
            if (id.endsWith('main.jsx') || id.endsWith('main.tsx')) {
                const scanScript = `
import { scan } from 'react-scan';

if (typeof window !== 'undefined') {
    scan(${JSON.stringify(scanConfig)});
}
                `;
                return {
                    code: `${scanScript}\n${code}`,
                    map: null,
                };
            }
        },
    };
}

@Toumash
Copy link

Toumash commented Jan 8, 2025

I hacked together a custom vite plugin for now, but would love to see a vite-plugin as a first-class citizen:

import { Plugin } from 'vite';

export default function reactScanPlugin(options = {}) {
    const defaultScanConfig = {
        enabled: true,
        log: true,
    };
    const scanConfig = { ...defaultScanConfig, ...options };

    return {
        name: 'vite-plugin-react-scan',
        enforce: 'post',
        transform(code, id) {
            if (id.endsWith('main.jsx') || id.endsWith('main.tsx')) {
                const scanScript = `
import { scan } from 'react-scan';

if (typeof window !== 'undefined') {
    scan(${JSON.stringify(scanConfig)});
}
                `;
                return {
                    code: `${scanScript}\n${code}`,
                    map: null,
                };
            }
        },
    };
}

How is that different than conditional import apart it wont work that easily in chrome extensions or other targets where the main.tsx is gonna have a different name?

@DAcodedBEAT
Copy link

I hacked together a custom vite plugin for now, but would love to see a vite-plugin as a first-class citizen:

import { Plugin } from 'vite';

export default function reactScanPlugin(options = {}) {
    const defaultScanConfig = {
        enabled: true,
        log: true,
    };
    const scanConfig = { ...defaultScanConfig, ...options };

    return {
        name: 'vite-plugin-react-scan',
        enforce: 'post',
        transform(code, id) {
            if (id.endsWith('main.jsx') || id.endsWith('main.tsx')) {
                const scanScript = `
import { scan } from 'react-scan';

if (typeof window !== 'undefined') {
    scan(${JSON.stringify(scanConfig)});
}
                `;
                return {
                    code: `${scanScript}\n${code}`,
                    map: null,
                };
            }
        },
    };
}

How is that different than conditional import apart it wont work that easily in chrome extensions or other targets where the main.tsx is gonna have a different name?

@Toumash you're free to do whatever works best for your setup. Vite plugins seem to be the standard for layering devtools into a JavaScript bundle with vite, and since that's how my current application is structured, I didn’t want to use any alternative mechanisms.

You’re right that this won't support entrypoints that are not named 'main' - there’s probably a better way to determine the entry point. Worst-case scenario, it could be explicitly provided as a parameter to the plugin.

For now, I just wanted to share my approach in case it inspires others to improve on it or build a more robust and extendable Vite plugin that can handle a variety of workflows.

@Toumash
Copy link

Toumash commented Jan 8, 2025

Ok, thank you for clarification. I'm just being curious.

For now, I just wanted to share my approach in case it inspires others to improve on it or build a more robust and extendable Vite plugin that can handle a variety of workflows.

I'm actually seeing a vite plugin's code for the first time - thank you!

@pivanov
Copy link
Collaborator

pivanov commented Jan 16, 2025

@Toumash @DAcodedBEAT you guys should trye this one: @pivanov/vite-plugin-react-scan :)

please let me know how it's work! then we can close this one, right?

@pivanov pivanov self-assigned this Jan 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants