Skip to content

Commit 3e028fc

Browse files
authored
feat: extension v1.0.8 (#322)
1 parent 380c387 commit 3e028fc

25 files changed

+260
-199
lines changed

BROWSER_EXTENSION_GUIDE.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,23 @@
55
66
## Chrome
77

8-
1. Download the [`chrome-extension-v1.0.7.zip`](https://github.com/aidenybai/react-scan/tree/main/packages/extension/build) file.
8+
1. Download the [`chrome-extension-v1.0.8.zip`](https://github.com/aidenybai/react-scan/tree/main/packages/extension/build) file.
99
2. Unzip the file.
1010
3. Open Chrome and navigate to `chrome://extensions/`.
1111
4. Enable "Developer mode" if it is not already enabled.
1212
5. Click "Load unpacked" and select the unzipped folder (or drag the folder into the page).
1313

1414
## Firefox
1515

16-
1. Download the [`firefox-extension-v1.0.7.zip`](https://github.com/aidenybai/react-scan/tree/main/packages/extension/build) file.
16+
1. Download the [`firefox-extension-v1.0.8.zip`](https://github.com/aidenybai/react-scan/tree/main/packages/extension/build) file.
1717
2. Unzip the file.
1818
3. Open Firefox and navigate to `about:debugging#/runtime/this-firefox`.
1919
4. Click "Load Temporary Add-on..."
2020
5. Select `manifest.json` from the unzipped folder
2121

2222
## Brave
2323

24-
1. Download the [`brave-extension-v1.0.7.zip`](https://github.com/aidenybai/react-scan/tree/main/packages/extension/build) file.
24+
1. Download the [`brave-extension-v1.0.8.zip`](https://github.com/aidenybai/react-scan/tree/main/packages/extension/build) file.
2525
2. Unzip the file.
2626
3. Open Brave and navigate to `brave://extensions`.
2727
4. Click "Load unpacked" and select the unzipped folder (or drag the folder into the page).

packages/extension/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ pnpm pack:all
6464
```
6565

6666
This will create:
67-
- `chrome-extension-v1.0.7.zip`
68-
- `firefox-extension-v1.0.7.zip`
69-
- `brave-extension-v1.0.7.zip`
67+
- `chrome-extension-v1.0.8.zip`
68+
- `firefox-extension-v1.0.8.zip`
69+
- `brave-extension-v1.0.8.zip`
7070

7171
in the `build` directory.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

packages/extension/package.json

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@react-scan/extension",
3-
"version": "1.0.7",
3+
"version": "1.0.8",
44
"private": true,
55
"type": "module",
66
"scripts": {
@@ -23,7 +23,7 @@
2323
},
2424
"dependencies": {
2525
"@pivanov/utils": "0.0.2",
26-
"bippy": "0.2.7",
26+
"bippy": "0.3.8",
2727
"react": "^18.2.0",
2828
"react-dom": "^18.2.0",
2929
"react-scan": "workspace:*",
@@ -33,13 +33,15 @@
3333
"@types/chrome": "^0.0.281",
3434
"@types/react": "^18.0.26",
3535
"@types/react-dom": "^18.0.9",
36-
"@types/webextension-polyfill": "^0.10.0",
36+
"@types/semver": "^7.5.8",
37+
"@types/webextension-polyfill": "^0.12.0",
3738
"@vitejs/plugin-react": "^4.2.1",
3839
"bestzip": "^2.2.1",
3940
"cross-env": "^7.0.3",
41+
"semver": "^7.7.1",
4042
"vite": "^6.0.7",
4143
"vite-plugin-web-extension": "^4.4.3",
4244
"vite-tsconfig-paths": "^5.1.4",
43-
"webextension-polyfill": "^0.10.0"
45+
"webextension-polyfill": "^0.12.0"
4446
}
4547
}

packages/extension/src/assets/css/no-react.css

+14
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ html.freeze #react-scan-toast {
3232
top: 50%;
3333
left: 50%;
3434
display: flex;
35+
flex-direction: column;
3536
padding: 12px 40px 12px 16px;
3637
min-width: 320px;
3738
max-width: 480px;
@@ -48,6 +49,19 @@ html.freeze #react-scan-toast {
4849
z-index: 2147483651;
4950
}
5051

52+
#react-scan-toast-title {
53+
margin: 0 0 8px 0;
54+
}
55+
56+
#react-scan-toast-message {
57+
display: flex;
58+
align-items: flex-start;
59+
}
60+
61+
#react-scan-toast-message span {
62+
white-space: pre-line;
63+
}
64+
5165
#react-scan-toast .icon {
5266
font-size: .75rem;
5367
margin-right: 8px;
+36-40
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,30 @@
11
import browser from 'webextension-polyfill';
22
import { isInternalUrl } from '~utils/helpers';
33
import { IconState, updateIconForTab } from './icon';
4+
import { BroadcastMessage } from '~types/messages';
45

56
const browserAction = browser.action || browser.browserAction;
67

7-
browser.runtime.onInstalled.addListener(async () => {
8-
const tabs = await browser.tabs.query({ url: ['http://*/*', 'https://*/*'] });
9-
10-
for (const tab of tabs) {
11-
if (tab.id && !isInternalUrl(tab.url || '')) {
12-
try {
13-
await browser.scripting.executeScript({
14-
target: { tabId: tab.id },
15-
func: () => {
16-
localStorage.setItem('react-scan-needs-refresh', 'true');
17-
},
18-
});
19-
20-
await browser.scripting.executeScript({
21-
target: { tabId: tab.id },
22-
files: ['src/inject/index.js', 'src/content/index.js'],
23-
});
24-
} catch {}
25-
}
8+
const injectScripts = async (tabId: number) => {
9+
try {
10+
await browser.scripting.executeScript({
11+
target: { tabId },
12+
files: ['src/content/index.js', 'src/inject/index.js'],
13+
});
14+
15+
await browser.tabs.sendMessage(tabId, {
16+
type: 'react-scan:page-reload',
17+
});
18+
} catch (e) {
19+
// biome-ignore lint/suspicious/noConsole: log error
20+
console.error('Script injection error:', e);
2621
}
27-
});
22+
};
2823

2924
const isScriptsLoaded = async (tabId: number): Promise<boolean> => {
3025
try {
31-
const response = await browser.tabs.sendMessage(tabId, { type: 'react-scan:ping' });
32-
return response?.pong === true;
26+
await browser.tabs.sendMessage(tabId, { type: 'react-scan:ping' });
27+
return true;
3328
} catch {
3429
return false;
3530
}
@@ -44,6 +39,11 @@ const init = async (tab: browser.Tabs.Tab) => {
4439
}
4540

4641
const isLoaded = await isScriptsLoaded(tab.id);
42+
43+
if (!isLoaded) {
44+
await injectScripts(tab.id);
45+
}
46+
4747
if (!isLoaded) {
4848
await updateIconForTab(tab, IconState.DISABLED);
4949
}
@@ -84,31 +84,27 @@ browserAction.onClicked.addListener(async (tab) => {
8484
}
8585

8686
try {
87-
const response = await browser.tabs.sendMessage(tab.id, {
87+
await browser.tabs.sendMessage(tab.id, {
8888
type: 'react-scan:toggle-state',
8989
});
9090

91-
if (response && typeof response.hasReact === 'boolean') {
92-
await updateIconForTab(
93-
tab,
94-
response.hasReact ? IconState.ENABLED : IconState.DISABLED,
95-
);
96-
} else {
97-
await updateIconForTab(tab, IconState.DISABLED);
98-
}
91+
await updateIconForTab(tab, IconState.DISABLED);
9992
} catch {
10093
if (tab.id) {
10194
await updateIconForTab(tab, IconState.DISABLED);
10295
}
10396
}
10497
});
10598

106-
browser.runtime.onMessage.addListener((message, sender) => {
107-
if (!sender.tab?.id) return;
108-
if (message.type === 'react-scan:is-enabled') {
109-
void updateIconForTab(
110-
sender.tab,
111-
message.data.state ? IconState.ENABLED : IconState.DISABLED,
112-
);
113-
}
114-
});
99+
browser.runtime.onMessage.addListener(
100+
(message: unknown, sender: browser.Runtime.MessageSender) => {
101+
const msg = message as BroadcastMessage;
102+
if (!sender.tab?.id) return;
103+
if (msg.type === 'react-scan:is-enabled') {
104+
void updateIconForTab(
105+
sender.tab,
106+
msg.data?.state ? IconState.ENABLED : IconState.DISABLED,
107+
);
108+
}
109+
},
110+
);

packages/extension/src/content/index.ts

+6-24
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
import { busDispatch, busSubscribe, storageSetItem } from '@pivanov/utils';
1+
import { busDispatch, busSubscribe } from '@pivanov/utils';
22
import browser from 'webextension-polyfill';
33
import {
44
type BroadcastMessage,
55
BroadcastSchema,
66
type IEvents,
77
} from '~types/messages';
8-
import { saveLocalStorage } from '../utils/helpers';
9-
import { EXTENSION_STORAGE_KEY, STORAGE_KEY } from '../utils/constants';
10-
118

129
chrome.runtime.onMessage.addListener(
1310
async (message: unknown, _sender, sendResponse) => {
@@ -23,27 +20,12 @@ chrome.runtime.onMessage.addListener(
2320
return false;
2421
}
2522

26-
if (data.type === 'react-scan:toggle-state') {
27-
// Adter extension installation
28-
const needsRefresh = localStorage.getItem('react-scan-needs-refresh');
29-
if (needsRefresh === 'true') {
30-
localStorage.removeItem('react-scan-needs-refresh');
31-
try {
32-
await storageSetItem(EXTENSION_STORAGE_KEY, 'isEnabled', true);
33-
} catch {}
34-
35-
const updatedOptions = {
36-
enabled: true,
37-
showToolbar: true,
38-
dangerouslyForceRunInProduction: true,
39-
};
40-
41-
saveLocalStorage(STORAGE_KEY, updatedOptions);
42-
43-
window.location.reload();
44-
return;
45-
}
23+
if (data.type === 'react-scan:page-reload') {
24+
window.location.reload();
25+
return false;
26+
}
4627

28+
if (data.type === 'react-scan:toggle-state') {
4729
busDispatch<IEvents['react-scan:toggle-state']>(
4830
'react-scan:toggle-state',
4931
undefined,

packages/extension/src/inject/index.ts

+40-16
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
storageSetItem,
77
} from '@pivanov/utils';
88
import * as reactScan from 'react-scan';
9+
import { gt } from 'semver';
910
import type { IEvents } from '~types/messages';
1011
import { EXTENSION_STORAGE_KEY, STORAGE_KEY } from '~utils/constants';
1112
import {
@@ -16,19 +17,22 @@ import {
1617
} from '~utils/helpers';
1718
import { createNotificationUI, toggleNotification } from './notification';
1819

19-
export const isTargetPageAlreadyUsedReactScan = () => {
20-
const reactScanExtensionVersion = reactScan.ReactScanInternals.version;
21-
const currentReactScanVersion =
22-
window.__REACT_SCAN__?.ReactScanInternals.version;
20+
const reactScanExtensionVersion = reactScan.ReactScanInternals.version;
21+
const isTargetPageAlreadyUsedReactScan = () => {
22+
const currentReactScanVersion = window.__REACT_SCAN_VERSION__;
2323

24-
if (window.__REACT_SCAN__?.ReactScanInternals.Store.monitor.value) {
24+
if (
25+
window.__REACT_SCAN__?.ReactScanInternals?.Store?.monitor?.value &&
26+
!currentReactScanVersion
27+
) {
28+
return true;
29+
}
30+
31+
if (!reactScanExtensionVersion || !currentReactScanVersion) {
2532
return false;
2633
}
2734

28-
return (
29-
!!window.__REACT_SCAN__ &&
30-
reactScanExtensionVersion !== currentReactScanVersion
31-
);
35+
return gt(currentReactScanVersion, reactScanExtensionVersion);
3236
};
3337

3438
const getInitialOptions = async (): Promise<reactScan.Options> => {
@@ -54,9 +58,12 @@ const getInitialOptions = async (): Promise<reactScan.Options> => {
5458
const initializeReactScan = async () => {
5559
const options = await getInitialOptions();
5660

57-
window.hideIntro = true;
58-
reactScan.scan(options);
59-
window.reactScan = undefined;
61+
window.__REACT_SCAN_EXTENSION__ = true;
62+
if (options.enabled) {
63+
window.hideIntro = true;
64+
reactScan.scan(options);
65+
window.reactScan = undefined;
66+
}
6067
};
6168

6269
let timer: number | undefined;
@@ -94,7 +101,11 @@ window.addEventListener('DOMContentLoaded', async () => {
94101
isReactAvailable = await hasReactFiber();
95102

96103
if (!isReactAvailable) {
97-
createNotificationUI();
104+
createNotificationUI({
105+
title: 'React Not Detected',
106+
content:
107+
"React is not detected on this page.\nPlease ensure you're visiting a React application.",
108+
});
98109

99110
busDispatch<IEvents['react-scan:send-to-background']>(
100111
'react-scan:send-to-background',
@@ -117,7 +128,18 @@ window.addEventListener('DOMContentLoaded', async () => {
117128
}
118129

119130
if (isTargetPageAlreadyUsedReactScan()) {
120-
createNotificationUI('React Scan is already initialized!');
131+
if (window.__REACT_SCAN__?.ReactScanInternals?.Store?.monitor?.value) {
132+
createNotificationUI({
133+
title: 'Outdated React Scan Monitoring',
134+
content:
135+
'If you are a developer of this website, please upgrade to the latest version of React Scan.',
136+
});
137+
} else {
138+
createNotificationUI({
139+
title: 'Already Initialized',
140+
content: 'React Scan is already initialized on this page.',
141+
});
142+
}
121143

122144
busDispatch<IEvents['react-scan:send-to-background']>(
123145
'react-scan:send-to-background',
@@ -152,7 +174,9 @@ window.addEventListener('DOMContentLoaded', async () => {
152174
);
153175
}
154176

155-
window.reactScan = reactScan.setOptions;
177+
if (!isTargetPageAlreadyUsedReactScan()) {
178+
window.reactScan = reactScan.setOptions;
179+
}
156180

157181
busSubscribe<IEvents['react-scan:toggle-state']>(
158182
'react-scan:toggle-state',
@@ -167,7 +191,7 @@ window.addEventListener('DOMContentLoaded', async () => {
167191
EXTENSION_STORAGE_KEY,
168192
'isEnabled',
169193
);
170-
await updateReactScanState(isEnabled);
194+
await updateReactScanState(!!isEnabled);
171195
} catch {
172196
await updateReactScanState(null);
173197
}

0 commit comments

Comments
 (0)