mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-04-15 14:02:00 +03:00
Compare commits
422 Commits
@redux-dev
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
1c5df1ee32 | ||
|
3fc48a226b | ||
|
17b55ef99f | ||
|
73a01cc5a7 | ||
|
07e8f2c3ad | ||
|
8c563d71bb | ||
|
d730ea185f | ||
|
6df66b7320 | ||
|
3c90662cfa | ||
|
763bf937a4 | ||
|
8ec2b303ee | ||
|
91f21b2ffc | ||
|
ff60266836 | ||
|
6830118951 | ||
|
cc7ec13fb9 | ||
|
e46bbcaef3 | ||
|
4a8fbc675a | ||
|
c9bed44e0e | ||
|
750046c4d9 | ||
|
f42403c579 | ||
|
519ec090c6 | ||
|
4e71048997 | ||
|
5b33056bc5 | ||
|
da0051706e | ||
|
6ea51af67c | ||
|
9a46407254 | ||
|
07bd4476b2 | ||
|
b9993eb5f7 | ||
|
5228d46328 | ||
|
2002a81071 | ||
|
c065b03caa | ||
|
81a9ee33cc | ||
|
3c47910110 | ||
|
1c6f45fb47 | ||
|
054b27a9f8 | ||
|
415257cd41 | ||
|
14201108e7 | ||
|
90737b7e26 | ||
|
54c6f26c81 | ||
|
6e400f68b3 | ||
|
2c65192b4f | ||
|
88a02a8332 | ||
|
04858cd514 | ||
|
b25bf1304b | ||
|
41fae27637 | ||
|
fdce076757 | ||
|
b934e80d23 | ||
|
50d7682776 | ||
|
344387c9c6 | ||
|
80c570d6d0 | ||
|
aca0cd09a1 | ||
|
75dbf74963 | ||
|
997f7b636d | ||
|
eb3ac09b03 | ||
|
3c39eb49f2 | ||
|
61c09e1cc3 | ||
|
f1d61580a8 | ||
|
c5aef77b85 | ||
|
908e1c11bd | ||
|
d591c18fc8 | ||
|
bc4b0755c3 | ||
|
2c8f0a0544 | ||
|
68d4440e38 | ||
|
c52962d532 | ||
|
18b86498e2 | ||
|
01ee4e99be | ||
|
c133d59461 | ||
|
fd9f9504f0 | ||
|
e49708d831 | ||
|
f64cbda982 | ||
|
abd03a70c7 | ||
|
b3e8f209fd | ||
|
238a38fb21 | ||
|
76183cfa43 | ||
|
9fa9a6ff79 | ||
|
2a93de46a1 | ||
|
83b2c19a11 | ||
|
61ec00f505 | ||
|
7f41fcf0fc | ||
|
73688e117a | ||
|
4164b6279e | ||
|
9b2f8720c9 | ||
|
0bd66c7388 | ||
|
8682d05b0b | ||
|
bd463b19ec | ||
|
0462470dca | ||
|
9a78f414ae | ||
|
652bcfa7f8 | ||
|
2163bc3f09 | ||
|
6cf528b4a0 | ||
|
ae3f4e59f5 | ||
|
fce9074175 | ||
|
3184647fa9 | ||
|
4c73661e78 | ||
|
23f1c47224 | ||
|
2c1a74cdff | ||
|
baed7ccd07 | ||
|
443b993f6a | ||
|
65126657cb | ||
|
42e11518f3 | ||
|
5e81525818 | ||
|
1735536bd6 | ||
|
51e46328a5 | ||
|
b55cdf0aea | ||
|
cacf7cf182 | ||
|
a1fe1a4018 | ||
|
08336c06ca | ||
|
76711b7ee1 | ||
|
96ac1f291a | ||
|
a4382ecb9c | ||
|
d9cdc25cb2 | ||
|
819000df9b | ||
|
35fb9b12f4 | ||
|
d5bbd78c52 | ||
|
25aeba0bd8 | ||
|
846ded1c74 | ||
|
04d141932d | ||
|
1fc9f76e50 | ||
|
249b3a030b | ||
|
612a23488d | ||
|
367fce39e6 | ||
|
57755f6f7d | ||
|
9c1b1b84b8 | ||
|
6842c895d5 | ||
|
911b063fba | ||
|
c8cf847681 | ||
|
af486b01e9 | ||
|
bbb1a40395 | ||
|
18cde73771 | ||
|
191d419773 | ||
|
629419bd1b | ||
|
b56a3aa0dc | ||
|
f3878541c2 | ||
|
075e9f6099 | ||
|
138f4f37b6 | ||
|
8f166edc73 | ||
|
9545a46c5d | ||
|
cb4772fa2c | ||
|
ab7df4cdd6 | ||
|
2eddfd60a8 | ||
|
dc6b584ad0 | ||
|
5c6aa8aa0a | ||
|
4c6add7aa2 | ||
|
f282e26a59 | ||
|
d0b89ca577 | ||
|
d76df9985f | ||
|
3b7bca2dbb | ||
|
6b80162ca9 | ||
|
3c44f223f5 | ||
|
fe9a3674e0 | ||
|
eba12e74f0 | ||
|
8257f616f5 | ||
|
9e4e054f8b | ||
|
31aabfa82c | ||
|
7b90e75e03 | ||
|
8f3b306a66 | ||
|
57af776834 | ||
|
c3497c1b17 | ||
|
eff9410828 | ||
|
264f076252 | ||
|
a0db02b31e | ||
|
eff9bee92d | ||
|
baf484adbc | ||
|
c9f2491d7e | ||
|
a3a6eb4cb3 | ||
|
4dd0dd2d19 | ||
|
8aa37659b3 | ||
|
28a08cde92 | ||
|
1f1d188601 | ||
|
81e3efd070 | ||
|
91dd5df25b | ||
|
1b4c36c46a | ||
|
32d1ee0894 | ||
|
e2e65dbeab | ||
|
c42e33437e | ||
|
d4adb601da | ||
|
fa7af18888 | ||
|
00664dcd3e | ||
|
b79b2c3bbc | ||
|
e9afa8fb27 | ||
|
5cfe3e5522 | ||
|
decc035570 | ||
|
6f9ef2e941 | ||
|
3205269f8c | ||
|
d165cc7bd9 | ||
|
c03264c54d | ||
|
6954eb9580 | ||
|
178002de65 | ||
|
ae1bc3aaae | ||
|
158ba2ce12 | ||
|
b54bc75cbb | ||
|
37fee5574a | ||
|
57d97026f3 | ||
|
2e4929b3b7 | ||
|
d95a5ff5ca | ||
|
7f5bddbdc2 | ||
|
6fc18ed74e | ||
|
8e99d35749 | ||
|
a3f86a42df | ||
|
a7d612fbdc | ||
|
8979004b53 | ||
|
3f5719ec1f | ||
|
c723e4a3c8 | ||
|
057b5e6de7 | ||
|
ba56d5b96d | ||
|
e8da9f0945 | ||
|
487775b206 | ||
|
2ab5bd833e | ||
|
262ea85ce1 | ||
|
d57d015de9 | ||
|
42531c503e | ||
|
11be87f31a | ||
|
65205f9078 | ||
|
963f1963e7 | ||
|
e7e43f16b0 | ||
|
27da2edc1a | ||
|
46911b13dd | ||
|
c781ac0624 | ||
|
b649e13f5b | ||
|
c6464ef371 | ||
|
669c87b9f4 | ||
|
8a216f531e | ||
|
986acf4e73 | ||
|
508506fe90 | ||
|
cacd101481 | ||
|
c280ec5988 | ||
|
20024cfb5b | ||
|
9695e499f2 | ||
|
840aa45c19 | ||
|
7e6d04380b | ||
|
e57bcb3933 | ||
|
f523d06499 | ||
|
bca760097b | ||
|
ee64102c1d | ||
|
64ed81b09a | ||
|
50f84218cd | ||
|
801e1a4cba | ||
|
02e211463b | ||
|
2ca07f5c37 | ||
|
92f0e217d4 | ||
|
c50bdc02e8 | ||
|
4e0620c131 | ||
|
546c98d406 | ||
|
391ab8b565 | ||
|
ffbf132174 | ||
|
57751ff933 | ||
|
b9459ec7ae | ||
|
d18525b5c7 | ||
|
135a676949 | ||
|
fe32709c4c | ||
|
850680d2b8 | ||
|
59159ede40 | ||
|
6c44727915 | ||
|
156454d6f1 | ||
|
71b77b9d4d | ||
|
14a795737b | ||
|
176a6cc59e | ||
|
e57dd29be2 | ||
|
c5f247e81c | ||
|
fbd031818d | ||
|
bb9bd907c5 | ||
|
1f591944cd | ||
|
319446868d | ||
|
7009fe2ca9 | ||
|
615c8a906f | ||
|
ded6be7683 | ||
|
915d5115e4 | ||
|
96475e19e2 | ||
|
922985f9ea | ||
|
d73787b172 | ||
|
5f104534b2 | ||
|
f1842af4d3 | ||
|
026694c360 | ||
|
81ba239ea2 | ||
|
0ade007a87 | ||
|
7d722f35b6 | ||
|
4fb9f1a6cd | ||
|
398746ba75 | ||
|
58a8135b08 | ||
|
acee8321a0 | ||
|
d6ca48a6b4 | ||
|
27dad81aa7 | ||
|
aec0fee986 | ||
|
bca3cb5db9 | ||
|
51eef5ce2a | ||
|
07af421555 | ||
|
21bba99afc | ||
|
0f25f33886 | ||
|
1126a80b3a | ||
|
d54adb76f8 | ||
|
c52cfbe469 | ||
|
59be92a8af | ||
|
409046eac2 | ||
|
f86902dfb0 | ||
|
22f234da8d | ||
|
6003063de9 | ||
|
0760d0d0ed | ||
|
b72ef8686d | ||
|
8fd2b51ca6 | ||
|
58825ee363 | ||
|
3abaa33177 | ||
|
0bf0c4e30f | ||
|
450cde6e1d | ||
|
f15145ed13 | ||
|
8c97757bbe | ||
|
0511ea4a80 | ||
|
65594c5cc9 | ||
|
a9469d8558 | ||
|
af23114ed8 | ||
|
5398f6b171 | ||
|
2ed146a519 | ||
|
399c25e215 | ||
|
5ec2361e7d | ||
|
762a0c5a87 | ||
|
bef71f4a0e | ||
|
19297685bb | ||
|
be6f3e0e48 | ||
|
9a0af4257f | ||
|
421ea47603 | ||
|
bb5d670143 | ||
|
ffb6983700 | ||
|
a07167406a | ||
|
9536998256 | ||
|
07456db41e | ||
|
c72c021e2d | ||
|
a7729dae87 | ||
|
69833d35b2 | ||
|
c9293470ec | ||
|
248aa98c0b | ||
|
8254de5437 | ||
|
7e1299886e | ||
|
78eed2dd20 | ||
|
ce26486f83 | ||
|
fa25234d36 | ||
|
5219bf8c68 | ||
|
1f5e5c519b | ||
|
f11383d294 | ||
|
81926f3212 | ||
|
5f33eeb8bf | ||
|
0833fa1622 | ||
|
935460457b | ||
|
aac607c490 | ||
|
9994ea62e8 | ||
|
b323f77d31 | ||
|
40b024a308 | ||
|
1aa6c4f7a0 | ||
|
76c6b2d19a | ||
|
a132e78808 | ||
|
f6e4a3cb68 | ||
|
1ef86dc751 | ||
|
4c1096a369 | ||
|
20ebf725c1 | ||
|
7d814f6aa6 | ||
|
55078635ce | ||
|
92f13ed730 | ||
|
6436116048 | ||
|
590dbc3974 | ||
|
fe140516b7 | ||
|
c5d8b61252 | ||
|
8c86b965bf | ||
|
3920b78f1d | ||
|
623b85dc1d | ||
|
eed9449862 | ||
|
fd6b0b3542 | ||
|
c2dae52c95 | ||
|
cac62d6e8d | ||
|
cd6b0c414f | ||
|
adff9dd9f9 | ||
|
27f13c02e6 | ||
|
cc5d7b7ffd | ||
|
61d0aca1fc | ||
|
48824ad20f | ||
|
2de22c9807 | ||
|
0266df5776 | ||
|
9e01600dde | ||
|
50f1a870ff | ||
|
37b51a1bb5 | ||
|
5cf6886783 | ||
|
9f71b1e31b | ||
|
f3ead32eba | ||
|
9429b1cbc2 | ||
|
fac75b3581 | ||
|
32b87260cb | ||
|
d2de8078b2 | ||
|
2804e36464 | ||
|
00b07715d1 | ||
|
6cc517d97e | ||
|
35fb3343f5 | ||
|
8384f1766a | ||
|
46bd102068 | ||
|
aa7aa94592 | ||
|
bdae246a9d | ||
|
79c0035c72 | ||
|
64aa035538 | ||
|
3c4c8782ee | ||
|
ef973608b9 | ||
|
55f5bac479 | ||
|
94928262b1 | ||
|
4ff825021d | ||
|
84f662259d | ||
|
ea55ba2c11 | ||
|
ccfd89ee9e | ||
|
418a909144 | ||
|
93eaca078f | ||
|
29bac4d535 | ||
|
3b78a19b9f | ||
|
4644c1ce01 | ||
|
8521d3fd27 | ||
|
41ea59b42e | ||
|
24f60a7aa7 | ||
|
6cf1865f55 | ||
|
db1fcd12c0 | ||
|
4e061f4600 | ||
|
c9589c0307 | ||
|
6c9ae2713f | ||
|
1ee787e353 | ||
|
e9da5fc411 | ||
|
e60c2d37dd | ||
|
e21d40d5b8 | ||
|
d28e05f156 | ||
|
ac7c6ab355 | ||
|
a55ba3026a |
|
@ -6,5 +6,8 @@
|
|||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": []
|
||||
"ignore": [],
|
||||
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
|
||||
"onlyUpdatePeerDependentsWhenOutOfRange": true
|
||||
}
|
||||
}
|
||||
|
|
15
.gitattributes
vendored
15
.gitattributes
vendored
|
@ -1,14 +1 @@
|
|||
*.js text eol=lf
|
||||
*.jsx text eol=lf
|
||||
*.ts text eol=lf
|
||||
*.tsx text eol=lf
|
||||
*.json text eol=lf
|
||||
*.css text eol=lf
|
||||
*.html text eol=lf
|
||||
*.md text eol=lf
|
||||
*.yml text eol=lf
|
||||
*.graphql text eol=lf
|
||||
.eslintrc text eol=lf
|
||||
.prettierrc text eol=lf
|
||||
.babelrc text eol=lf
|
||||
.stylelintrc text eol=lf
|
||||
* text=auto eol=lf
|
||||
|
|
19
.github/workflows/CI.yml
vendored
19
.github/workflows/CI.yml
vendored
|
@ -8,15 +8,12 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: 'ubuntu-22.04'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: nrwl/nx-set-shas@v2
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
|
@ -25,10 +22,10 @@ jobs:
|
|||
- name: Check formatting
|
||||
run: pnpm run format:check
|
||||
- name: Build
|
||||
run: pnpm exec nx affected --target=build --parallel=3
|
||||
run: pnpm run build:all
|
||||
- name: Lint
|
||||
run: pnpm exec nx affected --target=lint --parallel=3
|
||||
run: pnpm run lint:all
|
||||
- name: Test
|
||||
uses: GabrielBB/xvfb-action@v1
|
||||
uses: coactions/setup-xvfb@v1
|
||||
with:
|
||||
run: pnpm exec nx affected --target=test --parallel=3
|
||||
run: pnpm run test:all
|
||||
|
|
28
.github/workflows/release.yml
vendored
28
.github/workflows/release.yml
vendored
|
@ -5,21 +5,23 @@ on:
|
|||
branches:
|
||||
- main
|
||||
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: 'ubuntu-22.04'
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
|
@ -36,3 +38,21 @@ jobs:
|
|||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Archive Chrome Extension
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: chrome
|
||||
path: extension/chrome/dist
|
||||
|
||||
- name: Archive Edge Extension
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: edge
|
||||
path: extension/edge/dist
|
||||
|
||||
- name: Archive Firefox Extension
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: firefox
|
||||
path: extension/firefox/dist
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,3 +9,4 @@ coverage
|
|||
.idea
|
||||
.eslintcache
|
||||
!packages/redux-devtools-slider-monitor/examples/todomvc/dist/index.html
|
||||
.nx
|
||||
|
|
|
@ -24,7 +24,7 @@ It can be used as a browser extension (for [Chrome](https://chrome.google.com/we
|
|||
|
||||
## Development
|
||||
|
||||
This is a monorepo powered by [pnpm](https://pnpm.io/) and [Nx](https://nx.dev/). [Install pnpm](https://pnpm.io/installation) and run `pnpm install` to get started. Each package's dependencies need to be built before the package itself can be built. You can either build all the packages (i.e., `pnpm run build:all`) or use Nx commands to build only the packages necessary for the packages you're working on (i.e., `pnpm nx build remotedev-redux-devtools-extension`).
|
||||
This is a monorepo powered by [pnpm](https://pnpm.io/). [Install pnpm](https://pnpm.io/installation) and run `pnpm install` to get started. Each package's dependencies need to be built before the package itself can be built. You can either build all the packages (i.e., `pnpm run build:all`) or use pnpm workspace commands to build only the packages necessary for the packages you're working on (i.e., `pnpm --filter "remotedev-redux-devtools-extension" build`).
|
||||
|
||||
## Backers
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ const DevTools = createDevTools(
|
|||
defaultIsVisible={true}
|
||||
>
|
||||
<LogMonitor theme="tomorrow" />
|
||||
</DockMonitor>
|
||||
</DockMonitor>,
|
||||
);
|
||||
|
||||
export default DevTools;
|
||||
|
@ -88,7 +88,7 @@ const enhancer = compose(
|
|||
// Middleware you want to use in development:
|
||||
applyMiddleware(d1, d2, d3),
|
||||
// Required! Enable Redux DevTools with the monitors you chose
|
||||
DevTools.instrument()
|
||||
DevTools.instrument(),
|
||||
);
|
||||
|
||||
export default function configureStore(initialState) {
|
||||
|
@ -100,8 +100,8 @@ export default function configureStore(initialState) {
|
|||
if (module.hot) {
|
||||
module.hot.accept('../reducers', () =>
|
||||
store.replaceReducer(
|
||||
require('../reducers') /*.default if you use Babel 6+ */
|
||||
)
|
||||
require('../reducers') /*.default if you use Babel 6+ */,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ const enhancer = compose(
|
|||
// Required! Enable Redux DevTools with the monitors you chose
|
||||
DevTools.instrument(),
|
||||
// Optional. Lets you write ?debug_session=<key> in address bar to persist debug sessions
|
||||
persistState(getDebugSessionKey())
|
||||
persistState(getDebugSessionKey()),
|
||||
);
|
||||
|
||||
function getDebugSessionKey() {
|
||||
|
@ -200,7 +200,7 @@ const enhancer = compose(
|
|||
// Required! Enable Redux DevTools with the monitors you chose
|
||||
DevTools.instrument(),
|
||||
// Optional. Lets you write ?debug_session=<key> in address bar to persist debug sessions
|
||||
persistState(getDebugSessionKey())
|
||||
persistState(getDebugSessionKey()),
|
||||
);
|
||||
|
||||
function getDebugSessionKey() {
|
||||
|
@ -219,8 +219,8 @@ export default function configureStore(initialState) {
|
|||
if (module.hot) {
|
||||
module.hot.accept('../reducers', () =>
|
||||
store.replaceReducer(
|
||||
require('../reducers') /*.default if you use Babel 6+ */
|
||||
)
|
||||
require('../reducers') /*.default if you use Babel 6+ */,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -333,7 +333,7 @@ render(
|
|||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
document.getElementById('root')
|
||||
document.getElementById('root'),
|
||||
);
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
|
@ -353,7 +353,7 @@ export default function showDevTools(store) {
|
|||
const popup = window.open(
|
||||
null,
|
||||
'Redux DevTools',
|
||||
'menubar=no,location=no,resizable=yes,scrollbars=no,status=no'
|
||||
'menubar=no,location=no,resizable=yes,scrollbars=no,status=no',
|
||||
);
|
||||
// Reload in case it already exists
|
||||
popup.location.reload();
|
||||
|
@ -362,7 +362,7 @@ export default function showDevTools(store) {
|
|||
popup.document.write('<div id="react-devtools-root"></div>');
|
||||
render(
|
||||
<DevTools store={store} />,
|
||||
popup.document.getElementById('react-devtools-root')
|
||||
popup.document.getElementById('react-devtools-root'),
|
||||
);
|
||||
}, 10);
|
||||
}
|
||||
|
|
4
eslint.js.config.base.mjs
Normal file
4
eslint.js.config.base.mjs
Normal file
|
@ -0,0 +1,4 @@
|
|||
import eslint from '@eslint/js';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default [eslint.configs.recommended, eslintConfigPrettier];
|
43
eslint.js.react.jest.config.base.mjs
Normal file
43
eslint.js.react.jest.config.base.mjs
Normal file
|
@ -0,0 +1,43 @@
|
|||
import eslint from '@eslint/js';
|
||||
import react from 'eslint-plugin-react';
|
||||
import { fixupPluginRules } from '@eslint/compat';
|
||||
import eslintPluginReactHooks from 'eslint-plugin-react-hooks';
|
||||
import jest from 'eslint-plugin-jest';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default [
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
...eslint.configs.recommended,
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
...react.configs.flat.recommended,
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
plugins: {
|
||||
'react-hooks': fixupPluginRules(eslintPluginReactHooks),
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
...jest.configs['flat/recommended'],
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
...jest.configs['jest/style'],
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
...eslintConfigPrettier,
|
||||
},
|
||||
];
|
55
eslint.ts.config.base.mjs
Normal file
55
eslint.ts.config.base.mjs
Normal file
|
@ -0,0 +1,55 @@
|
|||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default (tsconfigRootDir, files = ['**/*.ts'], project = true) => [
|
||||
{
|
||||
files,
|
||||
...eslint.configs.recommended,
|
||||
},
|
||||
...tseslint.configs.recommendedTypeChecked.map((config) => ({
|
||||
files,
|
||||
...config,
|
||||
})),
|
||||
...tseslint.configs.stylisticTypeChecked.map((config) => ({
|
||||
files,
|
||||
...config,
|
||||
})),
|
||||
{
|
||||
files,
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project,
|
||||
tsconfigRootDir,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files,
|
||||
...eslintConfigPrettier,
|
||||
},
|
||||
{
|
||||
files,
|
||||
rules: {
|
||||
'@typescript-eslint/no-unsafe-return': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/prefer-optional-chain': 'off',
|
||||
'@typescript-eslint/no-base-to-string': 'off',
|
||||
'@typescript-eslint/consistent-indexed-object-style': 'off',
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/consistent-type-definitions': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/prefer-for-of': 'off',
|
||||
'@typescript-eslint/non-nullable-type-assertion-style': 'off',
|
||||
'@typescript-eslint/class-literal-property-style': 'off',
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'off',
|
||||
'@typescript-eslint/prefer-string-starts-ends-with': 'off',
|
||||
'@typescript-eslint/no-duplicate-type-constituents': 'off',
|
||||
'@typescript-eslint/array-type': 'off',
|
||||
'@typescript-eslint/prefer-function-type': 'off',
|
||||
},
|
||||
},
|
||||
];
|
64
eslint.ts.jest.config.base.mjs
Normal file
64
eslint.ts.jest.config.base.mjs
Normal file
|
@ -0,0 +1,64 @@
|
|||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import jest from 'eslint-plugin-jest';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default (tsconfigRootDir) => [
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
...eslint.configs.recommended,
|
||||
},
|
||||
...tseslint.configs.recommendedTypeChecked.map((config) => ({
|
||||
files: ['test/**/*.ts'],
|
||||
...config,
|
||||
})),
|
||||
...tseslint.configs.stylisticTypeChecked.map((config) => ({
|
||||
files: ['test/**/*.ts'],
|
||||
...config,
|
||||
})),
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.test.json'],
|
||||
tsconfigRootDir,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
...jest.configs['flat/recommended'],
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
...jest.configs['jest/style'],
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
...eslintConfigPrettier,
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unsafe-return': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/prefer-optional-chain': 'off',
|
||||
'@typescript-eslint/no-base-to-string': 'off',
|
||||
'@typescript-eslint/consistent-indexed-object-style': 'off',
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/consistent-type-definitions': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/prefer-for-of': 'off',
|
||||
'@typescript-eslint/non-nullable-type-assertion-style': 'off',
|
||||
'@typescript-eslint/class-literal-property-style': 'off',
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'off',
|
||||
'@typescript-eslint/prefer-string-starts-ends-with': 'off',
|
||||
'@typescript-eslint/no-duplicate-type-constituents': 'off',
|
||||
'@typescript-eslint/array-type': 'off',
|
||||
'@typescript-eslint/prefer-function-type': 'off',
|
||||
},
|
||||
},
|
||||
];
|
89
eslint.ts.react.config.base.mjs
Normal file
89
eslint.ts.react.config.base.mjs
Normal file
|
@ -0,0 +1,89 @@
|
|||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import react from 'eslint-plugin-react';
|
||||
import { fixupPluginRules } from '@eslint/compat';
|
||||
import eslintPluginReactHooks from 'eslint-plugin-react-hooks';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default (
|
||||
tsconfigRootDir,
|
||||
files = ['**/*.ts', '**/*.tsx'],
|
||||
project = true,
|
||||
) => [
|
||||
{
|
||||
files,
|
||||
...eslint.configs.recommended,
|
||||
},
|
||||
...tseslint.configs.recommendedTypeChecked.map((config) => ({
|
||||
files,
|
||||
...config,
|
||||
})),
|
||||
...tseslint.configs.stylisticTypeChecked.map((config) => ({
|
||||
files,
|
||||
...config,
|
||||
})),
|
||||
{
|
||||
files,
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project,
|
||||
tsconfigRootDir,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files,
|
||||
...react.configs.flat.recommended,
|
||||
},
|
||||
{
|
||||
files,
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files,
|
||||
plugins: {
|
||||
'react-hooks': fixupPluginRules(eslintPluginReactHooks),
|
||||
},
|
||||
},
|
||||
{
|
||||
files,
|
||||
...eslintConfigPrettier,
|
||||
},
|
||||
{
|
||||
files,
|
||||
rules: {
|
||||
'@typescript-eslint/no-unsafe-return': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/no-misused-promises': [
|
||||
'error',
|
||||
{
|
||||
checksVoidReturn: {
|
||||
attributes: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/prefer-optional-chain': 'off',
|
||||
'@typescript-eslint/no-base-to-string': 'off',
|
||||
'@typescript-eslint/consistent-indexed-object-style': 'off',
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/consistent-type-definitions': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/prefer-for-of': 'off',
|
||||
'@typescript-eslint/non-nullable-type-assertion-style': 'off',
|
||||
'@typescript-eslint/class-literal-property-style': 'off',
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'off',
|
||||
'@typescript-eslint/prefer-string-starts-ends-with': 'off',
|
||||
'@typescript-eslint/no-duplicate-type-constituents': 'off',
|
||||
'@typescript-eslint/array-type': 'off',
|
||||
'@typescript-eslint/prefer-function-type': 'off',
|
||||
'react/prop-types': 'off',
|
||||
},
|
||||
},
|
||||
];
|
85
eslint.ts.react.jest.config.base.mjs
Normal file
85
eslint.ts.react.jest.config.base.mjs
Normal file
|
@ -0,0 +1,85 @@
|
|||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import react from 'eslint-plugin-react';
|
||||
import { fixupPluginRules } from '@eslint/compat';
|
||||
import eslintPluginReactHooks from 'eslint-plugin-react-hooks';
|
||||
import jest from 'eslint-plugin-jest';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default (tsconfigRootDir) => [
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...eslint.configs.recommended,
|
||||
},
|
||||
...tseslint.configs.recommendedTypeChecked.map((config) => ({
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...config,
|
||||
})),
|
||||
...tseslint.configs.stylisticTypeChecked.map((config) => ({
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...config,
|
||||
})),
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.test.json'],
|
||||
tsconfigRootDir,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...react.configs.flat.recommended,
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
plugins: {
|
||||
'react-hooks': fixupPluginRules(eslintPluginReactHooks),
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...jest.configs['flat/recommended'],
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...jest.configs['jest/style'],
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
...eslintConfigPrettier,
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts', 'test/**/*.tsx'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unsafe-return': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/prefer-optional-chain': 'off',
|
||||
'@typescript-eslint/no-base-to-string': 'off',
|
||||
'@typescript-eslint/consistent-indexed-object-style': 'off',
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/consistent-type-definitions': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/prefer-for-of': 'off',
|
||||
'@typescript-eslint/non-nullable-type-assertion-style': 'off',
|
||||
'@typescript-eslint/class-literal-property-style': 'off',
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'off',
|
||||
'@typescript-eslint/prefer-string-starts-ends-with': 'off',
|
||||
'@typescript-eslint/no-duplicate-type-constituents': 'off',
|
||||
'@typescript-eslint/array-type': 'off',
|
||||
'@typescript-eslint/prefer-function-type': 'off',
|
||||
},
|
||||
},
|
||||
];
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"parser": "@babel/eslint-parser"
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||
"@typescript-eslint/no-unsafe-call": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off"
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"plugins": ["jest"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"plugin:jest/recommended",
|
||||
"plugin:jest/style",
|
||||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||
"@typescript-eslint/no-unsafe-call": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off"
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"plugins": ["@typescript-eslint", "react"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||
"@typescript-eslint/no-unsafe-call": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||
"@typescript-eslint/no-misused-promises": [
|
||||
"error",
|
||||
{
|
||||
"checksVoidReturn": {
|
||||
"attributes": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"plugins": ["jest"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:jest/recommended",
|
||||
"plugin:jest/style",
|
||||
"prettier"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||
"@typescript-eslint/no-unsafe-call": "off",
|
||||
"@typescript-eslint/no-unsafe-member-access": "off"
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
node_modules
|
||||
build
|
||||
dev
|
||||
webpack/replace
|
||||
examples
|
||||
npm-package
|
||||
_book
|
|
@ -1,31 +0,0 @@
|
|||
{
|
||||
"root": true,
|
||||
"extends": "eslint-config-airbnb",
|
||||
"globals": {
|
||||
"chrome": true,
|
||||
"__DEVELOPMENT__": true
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"rules": {
|
||||
"react/jsx-uses-react": 2,
|
||||
"react/jsx-uses-vars": 2,
|
||||
"react/react-in-jsx-scope": 2,
|
||||
"react/jsx-quotes": 0,
|
||||
"block-scoped-var": 0,
|
||||
"padded-blocks": 0,
|
||||
"quotes": [1, "single"],
|
||||
"comma-style": [2, "last"],
|
||||
"no-use-before-define": [0, "nofunc"],
|
||||
"func-names": 0,
|
||||
"prefer-const": 0,
|
||||
"comma-dangle": 0,
|
||||
"id-length": 0,
|
||||
"indent": [2, 2, { "SwitchCase": 1 }],
|
||||
"new-cap": [2, { "capIsNewExceptions": ["Test"] }],
|
||||
"default-case": 0
|
||||
},
|
||||
"plugins": ["react"]
|
||||
}
|
9
extension/.gitignore
vendored
9
extension/.gitignore
vendored
|
@ -1,9 +1,2 @@
|
|||
node_modules
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
.idea/
|
||||
dist/
|
||||
build/
|
||||
dev/
|
||||
tmp/
|
||||
_book
|
||||
dist
|
||||
|
|
|
@ -1,5 +1,202 @@
|
|||
# remotedev-redux-devtools-extension
|
||||
|
||||
## 3.2.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @redux-devtools/app@6.2.2
|
||||
|
||||
## 3.2.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [91f21b2]
|
||||
- @redux-devtools/core@4.1.1
|
||||
- @redux-devtools/slider-monitor@5.1.1
|
||||
- @redux-devtools/utils@3.1.1
|
||||
- @redux-devtools/app@6.2.1
|
||||
|
||||
## 3.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6830118]
|
||||
- react-json-tree@0.20.0
|
||||
- @redux-devtools/app@6.2.0
|
||||
- @redux-devtools/slider-monitor@6.0.0
|
||||
- @redux-devtools/ui@1.4.0
|
||||
- @redux-devtools/core@4.1.0
|
||||
- @redux-devtools/utils@4.0.0
|
||||
|
||||
## 3.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b25bf13: Send state from background when monitor connects
|
||||
|
||||
## 3.2.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 50d7682: Fix DevTools from losing connection
|
||||
|
||||
## 3.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- eb3ac09: Add logging to background service worker
|
||||
|
||||
## 3.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f1d6158: Fix mocking Chrome API for Electron
|
||||
|
||||
## 3.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- fd9f950: Fix monitoring on opening panel
|
||||
- e49708d: Fix manifest.json for Edge
|
||||
|
||||
## 3.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- abd03a7: Fix: only send data to extension if DevTools are open
|
||||
|
||||
## 3.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 83b2c19: Upgrade to Manifest V3
|
||||
|
||||
## 3.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 73688e1: Fix releasing Firefox extension
|
||||
|
||||
## 3.1.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 2163bc3: Split large messages sent from background page to devpanel
|
||||
|
||||
## 3.1.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [bbb1a40]
|
||||
- react-json-tree@0.19.0
|
||||
- @redux-devtools/slider-monitor@5.0.1
|
||||
- @redux-devtools/ui@1.3.2
|
||||
|
||||
## 3.1.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 191d419: Convert d3 packages to ESM
|
||||
- Updated dependencies [191d419]
|
||||
- @redux-devtools/app@6.0.1
|
||||
|
||||
## 3.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [5cfe3e5]
|
||||
- Updated dependencies [decc035]
|
||||
- @redux-devtools/app@6.0.0
|
||||
- @redux-devtools/slider-monitor@5.0.0
|
||||
- @redux-devtools/core@4.0.0
|
||||
- @redux-devtools/utils@3.0.0
|
||||
|
||||
## 3.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [158ba2c]
|
||||
- @redux-devtools/app@5.0.0
|
||||
|
||||
## 3.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 65205f90: Replace Action<unknown> with Action<string>
|
||||
- Updated dependencies [65205f90]
|
||||
- @redux-devtools/app@4.0.1
|
||||
- @redux-devtools/core@3.13.2
|
||||
|
||||
## 3.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [e57bcb39]
|
||||
- @redux-devtools/app@4.0.0
|
||||
|
||||
## 3.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- bca76009: Fix missing CSS for code editor
|
||||
|
||||
## 3.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 64ed81b0: Fix extension in Firefox and Chrome Incognito
|
||||
|
||||
## 3.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- d18525b5: Increase min-width of popup
|
||||
- Updated dependencies [57751ff9]
|
||||
- @redux-devtools/app@3.0.0
|
||||
|
||||
## 3.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- d54adb76: Option to sort State Tree keys alphabetically
|
||||
Option to disable collapsing of object keys
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @redux-devtools/app@2.2.2
|
||||
|
||||
## 3.0.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 450cde6e: Fix responsive layout
|
||||
|
||||
## 3.0.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [81926f32]
|
||||
- react-json-tree@0.18.0
|
||||
- @redux-devtools/app@2.2.1
|
||||
|
||||
## 3.0.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1aa6c4f7: Fix remounting root for devpanel
|
||||
|
||||
## 3.0.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 20ebf725: Remove source map from page wrap bundle
|
||||
|
||||
## 3.0.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 24f60a7a: bump min popup window width to 760px #1126 #1129
|
||||
|
||||
## 3.0.13
|
||||
|
||||
### Patch Changes
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
|
||||
- from [Chrome Web Store](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd);
|
||||
- or download `extension.zip` from [last releases](https://github.com/zalmoxisus/redux-devtools-extension/releases), unzip, open `chrome://extensions` url and turn on developer mode from top left and then click; on `Load Unpacked` and select the extracted folder for use
|
||||
- or build it with `npm i && npm run build:extension` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./build/extension`;
|
||||
- or run it in dev mode with `npm i && npm start` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./dev`.
|
||||
- or build it with `npm i && npm run build:extension` and [load the extension's folder](https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world#load-unpacked) `./build/extension`;
|
||||
- or run it in dev mode with `npm i && npm start` and [load the extension's folder](https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world#load-unpacked) `./dev`.
|
||||
|
||||
### 2. For Firefox
|
||||
|
||||
|
@ -57,7 +57,7 @@ const composeEnhancers =
|
|||
compose;
|
||||
```
|
||||
|
||||
> For TypeScript use [`redux-devtools-extension` npm package](#13-use-redux-devtools-extension-package-from-npm), which contains all the definitions, or just use `(window as any)` (see [Recipes](/docs/Recipes.md#using-in-a-typescript-project) for an example).
|
||||
> For TypeScript use [`redux-devtools-extension` npm package](#13-use-redux-devtoolsextension-package-from-npm), which contains all the definitions, or just use `(window as any)` (see [Recipes](/docs/Recipes.md#using-in-a-typescript-project) for an example).
|
||||
|
||||
```js
|
||||
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
|
@ -105,7 +105,7 @@ const composeEnhancers =
|
|||
: compose;
|
||||
|
||||
const enhancer = composeEnhancers(
|
||||
applyMiddleware(...middleware)
|
||||
applyMiddleware(...middleware),
|
||||
// other store enhancers if any
|
||||
);
|
||||
const store = createStore(reducer, enhancer);
|
||||
|
@ -130,9 +130,9 @@ import { composeWithDevTools } from '@redux-devtools/extension';
|
|||
const store = createStore(
|
||||
reducer,
|
||||
composeWithDevTools(
|
||||
applyMiddleware(...middleware)
|
||||
applyMiddleware(...middleware),
|
||||
// other store enhancers if any
|
||||
)
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -148,9 +148,9 @@ const composeEnhancers = composeWithDevTools({
|
|||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ composeEnhancers(
|
||||
applyMiddleware(...middleware)
|
||||
applyMiddleware(...middleware),
|
||||
// other store enhancers if any
|
||||
)
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -164,7 +164,7 @@ import { devToolsEnhancer } from '@redux-devtools/extension';
|
|||
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ devToolsEnhancer()
|
||||
/* preloadedState, */ devToolsEnhancer(),
|
||||
// Specify name here, actionsDenylist, actionsCreators and other options if needed
|
||||
);
|
||||
```
|
||||
|
@ -181,7 +181,7 @@ import { devToolsEnhancerLogOnlyInProduction } from '@redux-devtools/extension';
|
|||
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ devToolsEnhancerLogOnlyInProduction()
|
||||
/* preloadedState, */ devToolsEnhancerLogOnlyInProduction(),
|
||||
// options like actionSanitizer, stateSanitizer
|
||||
);
|
||||
```
|
||||
|
@ -198,9 +198,9 @@ const composeEnhancers = composeWithDevToolsLogOnlyInProduction({
|
|||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ composeEnhancers(
|
||||
applyMiddleware(...middleware)
|
||||
applyMiddleware(...middleware),
|
||||
// other store enhancers if any
|
||||
)
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
|
|
55
extension/build.mjs
Normal file
55
extension/build.mjs
Normal file
|
@ -0,0 +1,55 @@
|
|||
import * as fs from 'node:fs';
|
||||
import * as esbuild from 'esbuild';
|
||||
import pug from 'pug';
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const prod = !args.includes('--dev');
|
||||
|
||||
await esbuild.build({
|
||||
bundle: true,
|
||||
logLevel: 'info',
|
||||
outdir: 'dist',
|
||||
minify: prod,
|
||||
sourcemap: !prod,
|
||||
define: {
|
||||
'process.env.NODE_ENV': prod ? '"production"' : '"development"',
|
||||
'process.env.BABEL_ENV': prod ? '"production"' : '"development"',
|
||||
},
|
||||
entryPoints: [
|
||||
{ out: 'background.bundle', in: 'src/background/index.ts' },
|
||||
{ out: 'options.bundle', in: 'src/options/index.tsx' },
|
||||
{ out: 'remote.bundle', in: 'src/remote/index.tsx' },
|
||||
{ out: 'devpanel.bundle', in: 'src/devpanel/index.tsx' },
|
||||
{ out: 'devtools.bundle', in: 'src/devtools/index.ts' },
|
||||
{ out: 'content.bundle', in: 'src/contentScript/index.ts' },
|
||||
{ out: 'page.bundle', in: 'src/pageScript/index.ts' },
|
||||
],
|
||||
loader: {
|
||||
'.woff2': 'file',
|
||||
},
|
||||
});
|
||||
|
||||
console.log();
|
||||
|
||||
console.log('Creating HTML files...');
|
||||
const htmlFiles = ['devpanel', 'devtools', 'options', 'remote'];
|
||||
for (const htmlFile of htmlFiles) {
|
||||
fs.writeFileSync(
|
||||
`dist/${htmlFile}.html`,
|
||||
pug.renderFile(`src/${htmlFile}/${htmlFile}.pug`),
|
||||
);
|
||||
}
|
||||
|
||||
console.log('Copying manifest.json...');
|
||||
fs.copyFileSync('chrome/manifest.json', 'dist/manifest.json');
|
||||
|
||||
console.log('Copying assets...');
|
||||
fs.cpSync('src/assets', 'dist', { recursive: true });
|
||||
|
||||
console.log('Copying dist for each browser...');
|
||||
fs.cpSync('dist', 'chrome/dist', { recursive: true });
|
||||
fs.copyFileSync('chrome/manifest.json', 'chrome/dist/manifest.json');
|
||||
fs.cpSync('dist', 'edge/dist', { recursive: true });
|
||||
fs.copyFileSync('edge/manifest.json', 'edge/dist/manifest.json');
|
||||
fs.cpSync('dist', 'firefox/dist', { recursive: true });
|
||||
fs.copyFileSync('firefox/manifest.json', 'firefox/dist/manifest.json');
|
|
@ -1,29 +1,22 @@
|
|||
{
|
||||
"version": "3.0.11",
|
||||
"version": "3.2.10",
|
||||
"name": "Redux DevTools",
|
||||
"short_name": "Redux DevTools",
|
||||
"description": "Redux DevTools for debugging application's state changes.",
|
||||
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
||||
"manifest_version": 2,
|
||||
"page_action": {
|
||||
"manifest_version": 3,
|
||||
"action": {
|
||||
"default_icon": "img/logo/gray.png",
|
||||
"default_title": "Redux DevTools",
|
||||
"default_popup": "window.html#popup"
|
||||
"default_popup": "devpanel.html#popup"
|
||||
},
|
||||
"commands": {
|
||||
"devtools-left": {
|
||||
"description": "DevTools window to left"
|
||||
},
|
||||
"devtools-right": {
|
||||
"description": "DevTools window to right"
|
||||
},
|
||||
"devtools-bottom": {
|
||||
"description": "DevTools window to bottom"
|
||||
"devtools-window": {
|
||||
"description": "DevTools window"
|
||||
},
|
||||
"devtools-remote": {
|
||||
"description": "Remote DevTools"
|
||||
},
|
||||
"_execute_page_action": {
|
||||
"_execute_action": {
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+Shift+E"
|
||||
}
|
||||
|
@ -35,36 +28,37 @@
|
|||
"128": "img/logo/128x128.png"
|
||||
},
|
||||
"options_ui": {
|
||||
"page": "options.html",
|
||||
"chrome_style": true
|
||||
"page": "options.html"
|
||||
},
|
||||
"background": {
|
||||
"scripts": ["background.bundle.js"],
|
||||
"persistent": false
|
||||
"service_worker": "background.bundle.js"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"exclude_globs": ["https://www.google*"],
|
||||
"js": ["content.bundle.js", "pagewrap.bundle.js"],
|
||||
"js": ["content.bundle.js"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true
|
||||
},
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"exclude_globs": ["https://www.google*"],
|
||||
"js": ["page.bundle.js"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true,
|
||||
"world": "MAIN"
|
||||
}
|
||||
],
|
||||
"devtools_page": "devtools.html",
|
||||
"web_accessible_resources": ["page.bundle.js"],
|
||||
"externally_connectable": {
|
||||
"ids": ["*"]
|
||||
},
|
||||
"permissions": [
|
||||
"notifications",
|
||||
"contextMenus",
|
||||
"storage",
|
||||
"file:///*",
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;",
|
||||
"permissions": ["notifications", "contextMenus", "storage"],
|
||||
"host_permissions": ["file:///*", "http://*/*", "https://*/*"],
|
||||
"content_security_policy": {
|
||||
"extension_pages": "script-src 'self'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;"
|
||||
},
|
||||
"update_url": "https://clients2.google.com/service/update2/crx",
|
||||
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdJEPwY92xUACA9CcDBDBmbdbp8Ap3cKQ0DJTUuVQvqb4FQAv8RtKY3iUjGvdwuAcSJQIZwHXcP2aNDH3TiFik/NhRK2GRW8X3OZyTdkuDueABGP2KEX8q1WQDgjX/rPIinGYztUrvoICw/UerMPwNW62jwGoVU3YhAGf+15CgX2Y6a4tppnf/+1mPedKPidh0RsM+aJY98rX+r1SPAHPcGzMjocLkqcT75DZBXer8VQN14tOOzRCd6T6oy7qm7eWru8lJwcY66qMQvhk0osqEod2G3nA7aTWpmqPFS66VEiecP9PgZlp8gQdgZ3dFhA62exydlD55JuRhiMIR63yQIDAQAB"
|
||||
}
|
|
@ -5,18 +5,18 @@ Use with
|
|||
- `window.__REDUX_DEVTOOLS_EXTENSION__([options])`
|
||||
- `window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__([options])()`
|
||||
- `window.__REDUX_DEVTOOLS_EXTENSION__.connect([options])`
|
||||
- `redux-devtools-extension` npm package:
|
||||
- `@redux-devtools/extension` npm package:
|
||||
|
||||
```js
|
||||
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||
import { composeWithDevTools } from '@redux-devtools/extension';
|
||||
|
||||
const composeEnhancers = composeWithDevTools(options);
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ composeEnhancers(
|
||||
applyMiddleware(...middleware)
|
||||
applyMiddleware(...middleware),
|
||||
// other store enhancers if any
|
||||
)
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -70,7 +70,7 @@ _boolean_ or _object_ which contains:
|
|||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -87,7 +87,7 @@ _boolean_ or _object_ which contains:
|
|||
replacer: (key, value) =>
|
||||
value && mori.isMap(value) ? mori.toJs(value) : value,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -109,7 +109,7 @@ _boolean_ or _object_ which contains:
|
|||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -134,7 +134,7 @@ _boolean_ or _object_ which contains:
|
|||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -174,7 +174,7 @@ _boolean_ or _object_ which contains:
|
|||
immutable: Immutable,
|
||||
refs: [ABRecord],
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -185,7 +185,7 @@ In the example bellow it will always send `{ component: '[React]' }`, regardless
|
|||
```js
|
||||
function component(
|
||||
state = { component: null, toJSON: () => ({ component: '[React]' }) },
|
||||
action
|
||||
action,
|
||||
) {
|
||||
switch (action.type) {
|
||||
case 'ADD_COMPONENT':
|
||||
|
@ -206,7 +206,7 @@ function counter(
|
|||
return { conter: this.count * 10 };
|
||||
},
|
||||
},
|
||||
action
|
||||
action,
|
||||
) {
|
||||
switch (action.type) {
|
||||
case 'INCREMENT':
|
||||
|
@ -236,7 +236,7 @@ const store = createStore(
|
|||
actionSanitizer,
|
||||
stateSanitizer: (state) =>
|
||||
state.data ? { ...state, data: '<<LONG_BLOB>>' } : state,
|
||||
})
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -254,7 +254,7 @@ createStore(
|
|||
actionsDenylist: 'SOME_ACTION',
|
||||
// or actionsDenylist: ['SOME_ACTION', 'SOME_OTHER_ACTION']
|
||||
// or just actionsDenylist: 'SOME_' to omit both
|
||||
})
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -270,7 +270,7 @@ const store = createStore(
|
|||
window.__REDUX_DEVTOOLS_EXTENSION__({
|
||||
predicate: (state, action) =>
|
||||
state.dev.logLevel === VERBOSE && !action.forwarded,
|
||||
})
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
|
|
37
extension/docs/Architecture.md
Normal file
37
extension/docs/Architecture.md
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Architecture Notes
|
||||
|
||||
This document exists to keep track of how the different parts of the Redux DevTools interact, since it's easy to forget how it all works together. This is intended for internal purposes and is just a collection of notes to myself.
|
||||
|
||||
## Entry Points
|
||||
|
||||
### Window
|
||||
|
||||
This is the default view that is shown in the Redux DevTools popup, the Chrome DevTools tab (if direct access to the background page is available), and new popup windows that are created. It has direct access to the background page via `chrome.runtime.getBackgroundPage`.
|
||||
|
||||
### DevPanel
|
||||
|
||||
This is the view that is shown in the Chrome DevTools tab if direct access to the background page is not available.
|
||||
|
||||
Initially this was the view that was always used for the Chrome DevTools tab, but when support to directly access the background page from the DevTools tab was added, [the Window View became the preferred view](https://github.com/zalmoxisus/redux-devtools-extension/pull/580).
|
||||
|
||||
### Remote
|
||||
|
||||
This does not interact with the other parts of the extension at all, it just renders the `App` component from `@redux-devtools/app`.
|
||||
|
||||
It can be triggered by hitting the "Remote" button in any of the other views, which calls `chrome.windows.create` and creates a new window.
|
||||
|
||||
### DevTools
|
||||
|
||||
This is the script that adds the Redux panel in the Chrome DevTools using `chrome.devtools.panels.create`.
|
||||
|
||||
It creates a Window View if it has direct access to the background page, otherwise it creates a DevPanel View.
|
||||
|
||||
Note that this used to always show the DevPanel View, but [started using the Window View by default](https://github.com/zalmoxisus/redux-devtools-extension/pull/580) once direct access to the background page was added to Chrome DevTools tabs.
|
||||
|
||||
### Content Script
|
||||
|
||||
Passes messages between the injected page script and the background page.
|
||||
|
||||
It listens for messages from the injected page script using `window.addEventListener('message', ...)`. It knows the message is from the injected page script if `message.source` is `'@devtools-page'`. See the Chrome DevTools docs where this approach [is documented](https://developer.chrome.com/docs/extensions/how-to/devtools/extend-devtools#evaluated-scripts-to-devtools).
|
||||
|
||||
It creates a connection to the background page using `chrome.runtime.connect` with the name `'tab'` when it receives the first message from the injected page script.
|
|
@ -32,7 +32,7 @@ import { inspectProps } from 'react-inspect-props';
|
|||
|
||||
compose(
|
||||
withState('count', 'setCount', 0),
|
||||
inspectProps('Counter inspector')
|
||||
inspectProps('Counter inspector'),
|
||||
)(Counter);
|
||||
```
|
||||
|
||||
|
@ -167,7 +167,7 @@ run(App, {
|
|||
{ id: newId(), num: 0 },
|
||||
{ id: newId(), num: 0 },
|
||||
],
|
||||
})
|
||||
}),
|
||||
),
|
||||
});
|
||||
```
|
||||
|
|
|
@ -28,7 +28,7 @@ type WindowWithDevTools = Window & {
|
|||
};
|
||||
|
||||
const isReduxDevtoolsExtenstionExist = (
|
||||
arg: Window | WindowWithDevTools
|
||||
arg: Window | WindowWithDevTools,
|
||||
): arg is WindowWithDevTools => {
|
||||
return '__REDUX_DEVTOOLS_EXTENSION__' in arg;
|
||||
};
|
||||
|
@ -40,7 +40,7 @@ const store = createStore(
|
|||
initialState,
|
||||
isReduxDevtoolsExtenstionExist(window)
|
||||
? window.__REDUX_DEVTOOLS_EXTENSION__()
|
||||
: undefined
|
||||
: undefined,
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -72,7 +72,7 @@ const store = createStore(
|
|||
instaceID: 2,
|
||||
name: 'Allowlisted',
|
||||
actionsAllowlist: '...',
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
);
|
||||
```
|
||||
|
|
|
@ -16,6 +16,8 @@ If you develop on your local filesystem, make sure to allow Redux DevTools acces
|
|||
|
||||
Most likely you mutate the state. Check it by [adding `redux-immutable-state-invariant` middleware](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/examples/counter/store/configureStore.js#L3).
|
||||
|
||||
Another cause could be that you are creating multiple stores, which means that the devtools get attached to one but the application uses another. See [https://github.com/reduxjs/redux-toolkit/issues/2753](this issue).
|
||||
|
||||
### @@INIT or REPLACE action resets the state of the app or last actions RE-APPLIED
|
||||
|
||||
`@@redux/REPLACE` (or `@@INIT`) is used internally when the application is hot reloaded. When you use `store.replaceReducer` the effect will be the same as for hot-reloading, where the extension is recomputing all the history again. To avoid that set [`shouldHotReload`](/docs/API/Arguments.md#shouldhotreload) parameter to `false`.
|
||||
|
@ -33,8 +35,8 @@ const store = createStore(
|
|||
window.__REDUX_DEVTOOLS_EXTENSION__
|
||||
? window.__REDUX_DEVTOOLS_EXTENSION__()
|
||||
: (noop) => noop,
|
||||
batchedSubscribe(/* ... */)
|
||||
)
|
||||
batchedSubscribe(/* ... */),
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -58,7 +60,7 @@ const store = createStore(
|
|||
actionSanitizer,
|
||||
stateSanitizer: (state) =>
|
||||
state.data ? { ...state, data: '<<LONG_BLOB>>' } : state,
|
||||
})
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -122,7 +124,7 @@ const store = Redux.createStore(
|
|||
window.__REDUX_DEVTOOLS_EXTENSION__ &&
|
||||
window.__REDUX_DEVTOOLS_EXTENSION__({
|
||||
serialize: true,
|
||||
})
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
|
|
62
extension/edge/manifest.json
Normal file
62
extension/edge/manifest.json
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"version": "3.2.10",
|
||||
"name": "Redux DevTools",
|
||||
"description": "Redux DevTools for debugging application's state changes.",
|
||||
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
||||
"manifest_version": 3,
|
||||
"action": {
|
||||
"default_icon": "img/logo/gray.png",
|
||||
"default_title": "Redux DevTools",
|
||||
"default_popup": "devpanel.html#popup"
|
||||
},
|
||||
"commands": {
|
||||
"devtools-window": {
|
||||
"description": "DevTools window"
|
||||
},
|
||||
"devtools-remote": {
|
||||
"description": "Remote DevTools"
|
||||
},
|
||||
"_execute_action": {
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+Shift+E"
|
||||
}
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"16": "img/logo/16x16.png",
|
||||
"48": "img/logo/48x48.png",
|
||||
"128": "img/logo/128x128.png"
|
||||
},
|
||||
"options_ui": {
|
||||
"page": "options.html"
|
||||
},
|
||||
"background": {
|
||||
"service_worker": "background.bundle.js"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"exclude_globs": ["https://www.google*"],
|
||||
"js": ["content.bundle.js"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true
|
||||
},
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"exclude_globs": ["https://www.google*"],
|
||||
"js": ["page.bundle.js"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true,
|
||||
"world": "MAIN"
|
||||
}
|
||||
],
|
||||
"devtools_page": "devtools.html",
|
||||
"externally_connectable": {
|
||||
"ids": ["*"]
|
||||
},
|
||||
"permissions": ["notifications", "contextMenus", "storage"],
|
||||
"host_permissions": ["file:///*", "http://*/*", "https://*/*"],
|
||||
"content_security_policy": {
|
||||
"extension_pages": "script-src 'self'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;"
|
||||
}
|
||||
}
|
38
extension/eslint.config.mjs
Normal file
38
extension/eslint.config.mjs
Normal file
|
@ -0,0 +1,38 @@
|
|||
import globals from 'globals';
|
||||
import eslintJs from '../eslint.js.config.base.mjs';
|
||||
import eslintTsReact from '../eslint.ts.react.config.base.mjs';
|
||||
import eslintJsReactJest from '../eslint.js.react.jest.config.base.mjs';
|
||||
|
||||
export default [
|
||||
...eslintJs,
|
||||
...eslintTsReact(import.meta.dirname),
|
||||
...eslintJsReactJest,
|
||||
{
|
||||
ignores: [
|
||||
'chrome',
|
||||
'dist',
|
||||
'edge',
|
||||
'examples',
|
||||
'firefox',
|
||||
'test/electron/fixture/dist',
|
||||
],
|
||||
},
|
||||
{
|
||||
files: ['build.mjs'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.nodeBuiltin,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.js', 'test/**/*.jsx'],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
EUI: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
|
@ -1,4 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Redux counter example</title>
|
||||
|
|
|
@ -10,5 +10,5 @@ render(
|
|||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
document.getElementById('root')
|
||||
document.getElementById('root'),
|
||||
);
|
||||
|
|
|
@ -11,7 +11,7 @@ app.use(
|
|||
webpackDevMiddleware(compiler, {
|
||||
noInfo: true,
|
||||
publicPath: config.output.publicPath,
|
||||
})
|
||||
}),
|
||||
);
|
||||
app.use(webpackHotMiddleware(compiler));
|
||||
|
||||
|
@ -26,7 +26,7 @@ app.listen(port, function (error) {
|
|||
console.info(
|
||||
'==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.',
|
||||
port,
|
||||
port
|
||||
port,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -14,7 +14,7 @@ export default function configureStore(preloadedState) {
|
|||
const store = createStore(
|
||||
reducer,
|
||||
preloadedState,
|
||||
composeEnhancers(applyMiddleware(invariant(), thunk))
|
||||
composeEnhancers(applyMiddleware(invariant(), thunk)),
|
||||
);
|
||||
|
||||
if (module.hot) {
|
||||
|
|
|
@ -37,7 +37,7 @@ function mockStore(getState, expectedActions, onLastAction) {
|
|||
}
|
||||
|
||||
const mockStoreWithMiddleware = applyMiddleware(...middlewares)(
|
||||
mockStoreWithoutMiddleware
|
||||
mockStoreWithoutMiddleware,
|
||||
);
|
||||
|
||||
return mockStoreWithMiddleware();
|
||||
|
|
|
@ -11,7 +11,7 @@ function setup() {
|
|||
decrement: expect.createSpy(),
|
||||
};
|
||||
const component = TestUtils.renderIntoDocument(
|
||||
<Counter counter={1} {...actions} />
|
||||
<Counter counter={1} {...actions} />,
|
||||
);
|
||||
return {
|
||||
component: component,
|
||||
|
|
|
@ -10,7 +10,7 @@ function setup(initialState) {
|
|||
const app = TestUtils.renderIntoDocument(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
</Provider>,
|
||||
);
|
||||
return {
|
||||
app: app,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>React counter example</title>
|
||||
|
|
|
@ -69,7 +69,7 @@ class MainSection extends Component {
|
|||
const filteredTodos = todos.filter(TODO_FILTERS[filter]);
|
||||
const completedCount = todos.reduce(
|
||||
(count, todo) => (todo.completed ? count + 1 : count),
|
||||
0
|
||||
0,
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Redux TodoMVC example</title>
|
||||
|
|
|
@ -12,5 +12,5 @@ render(
|
|||
<Provider store={store}>
|
||||
<Root />
|
||||
</Provider>,
|
||||
document.getElementById('root')
|
||||
document.getElementById('root'),
|
||||
);
|
||||
|
|
|
@ -34,14 +34,14 @@ export default function todos(state = initialState, action) {
|
|||
return state.map((todo) =>
|
||||
todo.id === action.id
|
||||
? Object.assign({}, todo, { text: action.text })
|
||||
: todo
|
||||
: todo,
|
||||
);
|
||||
|
||||
case COMPLETE_TODO:
|
||||
return state.map((todo) =>
|
||||
todo.id === action.id
|
||||
? Object.assign({}, todo, { completed: !todo.completed })
|
||||
: todo
|
||||
: todo,
|
||||
);
|
||||
|
||||
case COMPLETE_ALL:
|
||||
|
@ -49,7 +49,7 @@ export default function todos(state = initialState, action) {
|
|||
return state.map((todo) =>
|
||||
Object.assign({}, todo, {
|
||||
completed: !areAllMarked,
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
case CLEAR_COMPLETED:
|
||||
|
|
|
@ -11,7 +11,7 @@ app.use(
|
|||
webpackDevMiddleware(compiler, {
|
||||
noInfo: true,
|
||||
publicPath: config.output.publicPath,
|
||||
})
|
||||
}),
|
||||
);
|
||||
app.use(webpackHotMiddleware(compiler));
|
||||
|
||||
|
@ -26,7 +26,7 @@ app.listen(port, function (error) {
|
|||
console.info(
|
||||
'==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.',
|
||||
port,
|
||||
port
|
||||
port,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -11,7 +11,7 @@ import rootReducer from '../reducers';
|
|||
export default function configureStore(initialState) {
|
||||
let finalCreateStore = compose(
|
||||
reduxReactRouter({ createHistory }),
|
||||
global.devToolsExtension ? global.devToolsExtension() : (f) => f
|
||||
global.devToolsExtension ? global.devToolsExtension() : (f) => f,
|
||||
)(createStore);
|
||||
|
||||
const store = finalCreateStore(rootReducer, initialState);
|
||||
|
|
|
@ -13,7 +13,7 @@ function setup(propOverrides) {
|
|||
onClearCompleted: expect.createSpy(),
|
||||
onShow: expect.createSpy(),
|
||||
},
|
||||
propOverrides
|
||||
propOverrides,
|
||||
);
|
||||
|
||||
const renderer = TestUtils.createRenderer();
|
||||
|
@ -72,7 +72,7 @@ describe('components', () => {
|
|||
0: 'All',
|
||||
1: 'Active',
|
||||
2: 'Completed',
|
||||
}[i]
|
||||
}[i],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -29,7 +29,7 @@ function setup(propOverrides) {
|
|||
clearCompleted: expect.createSpy(),
|
||||
},
|
||||
},
|
||||
propOverrides
|
||||
propOverrides,
|
||||
);
|
||||
|
||||
const renderer = TestUtils.createRenderer();
|
||||
|
|
|
@ -12,7 +12,7 @@ function setup(propOverrides) {
|
|||
editing: false,
|
||||
newTodo: false,
|
||||
},
|
||||
propOverrides
|
||||
propOverrides,
|
||||
);
|
||||
|
||||
const renderer = TestUtils.createRenderer();
|
||||
|
|
|
@ -18,7 +18,7 @@ describe('todos reducer', () => {
|
|||
todos([], {
|
||||
type: types.ADD_TODO,
|
||||
text: 'Run the tests',
|
||||
})
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
|
@ -39,8 +39,8 @@ describe('todos reducer', () => {
|
|||
{
|
||||
type: types.ADD_TODO,
|
||||
text: 'Run the tests',
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
|
@ -71,8 +71,8 @@ describe('todos reducer', () => {
|
|||
{
|
||||
type: types.ADD_TODO,
|
||||
text: 'Fix the tests',
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Fix the tests',
|
||||
|
@ -110,8 +110,8 @@ describe('todos reducer', () => {
|
|||
{
|
||||
type: types.DELETE_TODO,
|
||||
id: 1,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Use Redux',
|
||||
|
@ -140,8 +140,8 @@ describe('todos reducer', () => {
|
|||
type: types.EDIT_TODO,
|
||||
text: 'Fix the tests',
|
||||
id: 1,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Fix the tests',
|
||||
|
@ -174,8 +174,8 @@ describe('todos reducer', () => {
|
|||
{
|
||||
type: types.COMPLETE_TODO,
|
||||
id: 1,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
|
@ -207,8 +207,8 @@ describe('todos reducer', () => {
|
|||
],
|
||||
{
|
||||
type: types.COMPLETE_ALL,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
|
@ -239,8 +239,8 @@ describe('todos reducer', () => {
|
|||
],
|
||||
{
|
||||
type: types.COMPLETE_ALL,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
|
@ -272,8 +272,8 @@ describe('todos reducer', () => {
|
|||
],
|
||||
{
|
||||
type: types.CLEAR_COMPLETED,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Use Redux',
|
||||
|
@ -308,7 +308,7 @@ describe('todos reducer', () => {
|
|||
completed: false,
|
||||
text: 'Write tests',
|
||||
},
|
||||
])
|
||||
]),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Write more tests',
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
|
|
@ -20,7 +20,7 @@ const composeEnhancers =
|
|||
compose;
|
||||
const store = createStore(
|
||||
reducer,
|
||||
composeEnhancers(applyMiddleware(sagaMiddleware))
|
||||
composeEnhancers(applyMiddleware(sagaMiddleware)),
|
||||
);
|
||||
sagaMiddleware.run(rootSaga);
|
||||
|
||||
|
@ -35,7 +35,7 @@ function render() {
|
|||
onIncrementIfOdd={() => action('INCREMENT_IF_ODD')}
|
||||
onIncrementAsync={() => action('INCREMENT_ASYNC')}
|
||||
/>,
|
||||
document.getElementById('root')
|
||||
document.getElementById('root'),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ class MainSection extends Component {
|
|||
const filteredTodos = todos.filter(TODO_FILTERS[filter]);
|
||||
const completedCount = todos.reduce(
|
||||
(count, todo) => (todo.completed ? count + 1 : count),
|
||||
0
|
||||
0,
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Redux TodoMVC example</title>
|
||||
|
|
|
@ -12,5 +12,5 @@ render(
|
|||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
document.getElementById('root')
|
||||
document.getElementById('root'),
|
||||
);
|
||||
|
|
|
@ -36,7 +36,7 @@ export default function todos(state = initialState, action) {
|
|||
return state.map((todo) =>
|
||||
todo.id === action.id
|
||||
? Object.assign({}, todo, { text: action.text, modified: new Date() })
|
||||
: todo
|
||||
: todo,
|
||||
);
|
||||
|
||||
case COMPLETE_TODO:
|
||||
|
@ -46,7 +46,7 @@ export default function todos(state = initialState, action) {
|
|||
completed: !todo.completed,
|
||||
modified: new Date(),
|
||||
})
|
||||
: todo
|
||||
: todo,
|
||||
);
|
||||
|
||||
case COMPLETE_ALL:
|
||||
|
@ -55,7 +55,7 @@ export default function todos(state = initialState, action) {
|
|||
Object.assign({}, todo, {
|
||||
completed: !areAllMarked,
|
||||
modified: new Date(),
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
case CLEAR_COMPLETED:
|
||||
|
|
|
@ -11,7 +11,7 @@ app.use(
|
|||
webpackDevMiddleware(compiler, {
|
||||
noInfo: true,
|
||||
publicPath: config.output.publicPath,
|
||||
})
|
||||
}),
|
||||
);
|
||||
app.use(webpackHotMiddleware(compiler));
|
||||
|
||||
|
@ -26,7 +26,7 @@ app.listen(port, function (error) {
|
|||
console.info(
|
||||
'==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.',
|
||||
port,
|
||||
port
|
||||
port,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -13,7 +13,7 @@ export default function configureStore(preloadedState) {
|
|||
if (!enhancer) {
|
||||
console.warn(
|
||||
'Install Redux DevTools Extension to inspect the app state: ' +
|
||||
'https://github.com/zalmoxisus/redux-devtools-extension#installation'
|
||||
'https://github.com/zalmoxisus/redux-devtools-extension#installation',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ function setup(propOverrides) {
|
|||
onClearCompleted: expect.createSpy(),
|
||||
onShow: expect.createSpy(),
|
||||
},
|
||||
propOverrides
|
||||
propOverrides,
|
||||
);
|
||||
|
||||
const renderer = TestUtils.createRenderer();
|
||||
|
@ -72,7 +72,7 @@ describe('components', () => {
|
|||
0: 'All',
|
||||
1: 'Active',
|
||||
2: 'Completed',
|
||||
}[i]
|
||||
}[i],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -29,7 +29,7 @@ function setup(propOverrides) {
|
|||
clearCompleted: expect.createSpy(),
|
||||
},
|
||||
},
|
||||
propOverrides
|
||||
propOverrides,
|
||||
);
|
||||
|
||||
const renderer = TestUtils.createRenderer();
|
||||
|
|
|
@ -12,7 +12,7 @@ function setup(propOverrides) {
|
|||
editing: false,
|
||||
newTodo: false,
|
||||
},
|
||||
propOverrides
|
||||
propOverrides,
|
||||
);
|
||||
|
||||
const renderer = TestUtils.createRenderer();
|
||||
|
|
|
@ -18,7 +18,7 @@ describe('todos reducer', () => {
|
|||
todos([], {
|
||||
type: types.ADD_TODO,
|
||||
text: 'Run the tests',
|
||||
})
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
|
@ -39,8 +39,8 @@ describe('todos reducer', () => {
|
|||
{
|
||||
type: types.ADD_TODO,
|
||||
text: 'Run the tests',
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
|
@ -71,8 +71,8 @@ describe('todos reducer', () => {
|
|||
{
|
||||
type: types.ADD_TODO,
|
||||
text: 'Fix the tests',
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Fix the tests',
|
||||
|
@ -110,8 +110,8 @@ describe('todos reducer', () => {
|
|||
{
|
||||
type: types.DELETE_TODO,
|
||||
id: 1,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Use Redux',
|
||||
|
@ -140,8 +140,8 @@ describe('todos reducer', () => {
|
|||
type: types.EDIT_TODO,
|
||||
text: 'Fix the tests',
|
||||
id: 1,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Fix the tests',
|
||||
|
@ -174,8 +174,8 @@ describe('todos reducer', () => {
|
|||
{
|
||||
type: types.COMPLETE_TODO,
|
||||
id: 1,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
|
@ -207,8 +207,8 @@ describe('todos reducer', () => {
|
|||
],
|
||||
{
|
||||
type: types.COMPLETE_ALL,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
|
@ -239,8 +239,8 @@ describe('todos reducer', () => {
|
|||
],
|
||||
{
|
||||
type: types.COMPLETE_ALL,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Run the tests',
|
||||
|
@ -272,8 +272,8 @@ describe('todos reducer', () => {
|
|||
],
|
||||
{
|
||||
type: types.CLEAR_COMPLETED,
|
||||
}
|
||||
)
|
||||
},
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Use Redux',
|
||||
|
@ -308,7 +308,7 @@ describe('todos reducer', () => {
|
|||
completed: false,
|
||||
text: 'Write tests',
|
||||
},
|
||||
])
|
||||
]),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'Write more tests',
|
||||
|
|
|
@ -1,29 +1,22 @@
|
|||
{
|
||||
"version": "3.0.11",
|
||||
"version": "3.2.10",
|
||||
"name": "Redux DevTools",
|
||||
"manifest_version": 2,
|
||||
"manifest_version": 3,
|
||||
"description": "Redux Developer Tools for debugging application state changes.",
|
||||
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
||||
"applications": {
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "extension@redux.devtools",
|
||||
"strict_min_version": "54.0"
|
||||
"id": "extension@redux.devtools"
|
||||
}
|
||||
},
|
||||
"page_action": {
|
||||
"action": {
|
||||
"default_icon": "img/logo/38x38.png",
|
||||
"default_title": "Redux DevTools",
|
||||
"default_popup": "window.html#popup"
|
||||
"default_popup": "devpanel.html#popup"
|
||||
},
|
||||
"commands": {
|
||||
"devtools-left": {
|
||||
"description": "DevTools window to left"
|
||||
},
|
||||
"devtools-right": {
|
||||
"description": "DevTools window to right"
|
||||
},
|
||||
"devtools-bottom": {
|
||||
"description": "DevTools window to bottom"
|
||||
"devtools-window": {
|
||||
"description": "DevTools window"
|
||||
},
|
||||
"devtools-remote": {
|
||||
"description": "Remote DevTools"
|
||||
|
@ -43,21 +36,22 @@
|
|||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["content.bundle.js", "pagewrap.bundle.js"],
|
||||
"js": ["content.bundle.js"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true
|
||||
},
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["page.bundle.js"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true,
|
||||
"world": "MAIN"
|
||||
}
|
||||
],
|
||||
"devtools_page": "devtools.html",
|
||||
"web_accessible_resources": ["page.bundle.js"],
|
||||
"permissions": [
|
||||
"notifications",
|
||||
"contextMenus",
|
||||
"tabs",
|
||||
"storage",
|
||||
"file:///*",
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
"content_security_policy": "script-src 'self'; object-src 'self'; img-src 'self' data:;"
|
||||
"permissions": ["notifications", "contextMenus", "tabs", "storage"],
|
||||
"host_permissions": ["file:///*", "http://*/*", "https://*/*"],
|
||||
"content_security_policy": {
|
||||
"extension_pages": "script-src 'self'; object-src 'self'; img-src 'self' data:;"
|
||||
}
|
||||
}
|
11
extension/jest.config.cjs
Normal file
11
extension/jest.config.cjs
Normal file
|
@ -0,0 +1,11 @@
|
|||
module.exports = {
|
||||
setupFilesAfterEnv: ['<rootDir>/test/setup.js'],
|
||||
testPathIgnorePatterns: ['<rootDir>/examples'],
|
||||
testEnvironment: 'jsdom',
|
||||
moduleNameMapper: {
|
||||
'\\.css$': '<rootDir>/test/__mocks__/styleMock.js',
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!.pnpm|@babel/code-frame|@babel/highlight|@babel/helper-validator-identifier|chalk|d3|dateformat|delaunator|internmap|jsondiffpatch|lodash-es|nanoid|robust-predicates|uuid)',
|
||||
],
|
||||
};
|
|
@ -1,8 +0,0 @@
|
|||
module.exports = {
|
||||
setupFilesAfterEnv: ['<rootDir>/test/setup.js'],
|
||||
testPathIgnorePatterns: ['<rootDir>/examples'],
|
||||
testEnvironment: 'jsdom',
|
||||
moduleNameMapper: {
|
||||
'\\.css$': '<rootDir>/test/__mocks__/styleMock.ts',
|
||||
},
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"private": true,
|
||||
"name": "remotedev-redux-devtools-extension",
|
||||
"version": "3.0.13",
|
||||
"version": "3.2.10",
|
||||
"description": "Redux Developer Tools for debugging application state changes.",
|
||||
"homepage": "https://github.com/reduxjs/redux-devtools/tree/master/extension",
|
||||
"license": "MIT",
|
||||
|
@ -11,88 +11,71 @@
|
|||
"url": "https://github.com/reduxjs/redux-devtools.git"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "webpack --config webpack/dev.config.babel.js",
|
||||
"build": "pnpm run build:extension && pnpm run build:firefox",
|
||||
"build:extension": "rimraf build/extension && webpack --config webpack/wrap.config.babel.js && webpack --config webpack/prod.config.babel.js",
|
||||
"build:firefox": "webpack --config webpack/prod.firefox.config.babel.js",
|
||||
"build": "pnpm run build:extension && pnpm run type-check",
|
||||
"build:extension": "node build.mjs",
|
||||
"build:examples": "babel-node examples/buildAll.js",
|
||||
"precompress:extension": "pnpm run lint && pnpm run test:app && pnpm run build:extension && pnpm run test:chrome && pnpm run test:electron",
|
||||
"precompress:firefox": "pnpm run lint && pnpm run build:firefox && pnpm run test:app",
|
||||
"compress:extension": "bestzip build/extension.zip build/extension",
|
||||
"compress:firefox": "bestzip build/extension.zip build/extension",
|
||||
"clean": "rimraf build && rimraf dev",
|
||||
"clean": "rimraf dist && rimraf chrome/dist && rimraf edge/dist && rimraf firefox/dist",
|
||||
"test:app": "cross-env BABEL_ENV=test jest test/app",
|
||||
"test:chrome": "jest test/chrome",
|
||||
"test:electron": "pnpm run build:test:electron:fixture && jest test/electron",
|
||||
"test": "pnpm run test:app && pnpm run build:extension && pnpm run test:chrome && pnpm run test:electron",
|
||||
"build:test:electron:fixture": "webpack --config test/electron/fixture/webpack.config.js",
|
||||
"test:electron": "pnpm run build:test:electron:fixture && jest test/electron",
|
||||
"test": "pnpm run test:app && pnpm run test:chrome && pnpm run test:electron",
|
||||
"lint": "eslint .",
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"@redux-devtools/app": "^2.2.0",
|
||||
"@redux-devtools/core": "^3.13.0",
|
||||
"@redux-devtools/instrument": "^2.1.0",
|
||||
"@redux-devtools/serialize": "^0.4.1",
|
||||
"@redux-devtools/slider-monitor": "^4.0.0",
|
||||
"@redux-devtools/ui": "^1.3.0",
|
||||
"@redux-devtools/utils": "^2.0.0",
|
||||
"@types/jsan": "^3.1.2",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@redux-devtools/app": "workspace:^",
|
||||
"@redux-devtools/core": "workspace:^",
|
||||
"@redux-devtools/instrument": "workspace:^",
|
||||
"@redux-devtools/serialize": "workspace:^",
|
||||
"@redux-devtools/slider-monitor": "workspace:^",
|
||||
"@redux-devtools/ui": "workspace:^",
|
||||
"@redux-devtools/utils": "workspace:^",
|
||||
"@reduxjs/toolkit": "^2.6.0",
|
||||
"@types/jsan": "^3.1.5",
|
||||
"jsan": "^3.1.14",
|
||||
"localforage": "^1.10.0",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-is": "^17.0.2",
|
||||
"react-json-tree": "^0.17.0",
|
||||
"react-redux": "^8.0.2",
|
||||
"redux": "^4.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-is": "^18.3.1",
|
||||
"react-json-tree": "workspace:^",
|
||||
"react-redux": "^9.2.0",
|
||||
"redux": "^5.0.1",
|
||||
"redux-persist": "^6.0.0",
|
||||
"styled-components": "^5.3.5"
|
||||
"styled-components": "^5.3.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.2",
|
||||
"@babel/preset-env": "^7.18.2",
|
||||
"@babel/preset-react": "^7.17.12",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@babel/register": "^7.17.7",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@types/chrome": "^0.0.188",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/react": "^17.0.45",
|
||||
"@types/react-dom": "^17.0.17",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"babel-loader": "^8.2.5",
|
||||
"bestzip": "^2.2.1",
|
||||
"chromedriver": "^101.0.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"@babel/core": "^7.26.9",
|
||||
"@babel/preset-env": "^7.26.9",
|
||||
"@babel/preset-react": "^7.26.3",
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"@babel/register": "^7.25.9",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.2.0",
|
||||
"@types/chrome": "^0.0.308",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/react": "^18.3.18",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"@types/styled-components": "^5.1.34",
|
||||
"chromedriver": "^126.0.5",
|
||||
"cross-env": "^7.0.3",
|
||||
"css-loader": "^6.7.1",
|
||||
"electron": "^19.0.1",
|
||||
"eslint": "^8.16.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-react": "^7.30.0",
|
||||
"eslint-plugin-react-hooks": "^4.5.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"fork-ts-checker-webpack-plugin": "^7.2.11",
|
||||
"immutable": "^4.1.0",
|
||||
"jest": "^27.5.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"pug-html-loader": "^1.1.5",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react-transform-catch-errors": "^1.0.2",
|
||||
"react-transform-hmr": "^1.0.4",
|
||||
"rimraf": "^3.0.2",
|
||||
"selenium-webdriver": "^4.2.0",
|
||||
"electron": "^31.7.7",
|
||||
"esbuild": "^0.25.0",
|
||||
"globals": "^15.15.0",
|
||||
"immutable": "^5.0.3",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"pug": "^3.0.3",
|
||||
"rimraf": "^6.0.1",
|
||||
"selenium-webdriver": "^4.29.0",
|
||||
"sinon-chrome": "^3.0.1",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-jest": "^27.1.5",
|
||||
"typescript": "~4.6.4",
|
||||
"webpack": "^5.72.1",
|
||||
"webpack-cli": "^4.9.2"
|
||||
"ts-jest": "^29.2.6",
|
||||
"typescript": "~5.8.2",
|
||||
"webpack": "^5.98.0",
|
||||
"webpack-cli": "^6.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,10 @@ import {
|
|||
StoreState,
|
||||
TopButtons,
|
||||
} from '@redux-devtools/app';
|
||||
import { GoRadioTower } from 'react-icons/go';
|
||||
import { MdBorderBottom, MdBorderLeft, MdBorderRight } from 'react-icons/md';
|
||||
import { Position } from '../api/openWindow';
|
||||
import { SingleMessage } from '../middlewares/api';
|
||||
import { GoBroadcast } from 'react-icons/go';
|
||||
import { MdOutlineWindow } from 'react-icons/md';
|
||||
import type { Position } from '../pageScript/api/openWindow';
|
||||
import type { SingleMessage } from '../background/store/apiMiddleware';
|
||||
|
||||
type StateProps = ReturnType<typeof mapStateToProps>;
|
||||
type DispatchProps = ResolveThunks<typeof actionCreators>;
|
||||
|
@ -29,25 +29,21 @@ interface OwnProps {
|
|||
}
|
||||
type Props = StateProps & DispatchProps & OwnProps;
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
isElectron?: boolean;
|
||||
}
|
||||
}
|
||||
const isElectron = navigator.userAgent.includes('Electron');
|
||||
|
||||
function sendMessage(message: SingleMessage) {
|
||||
chrome.runtime.sendMessage(message);
|
||||
async function sendMessage(message: SingleMessage) {
|
||||
await chrome.runtime.sendMessage(message);
|
||||
}
|
||||
|
||||
class Actions extends Component<Props> {
|
||||
openWindow = (position: Position) => {
|
||||
sendMessage({ type: 'OPEN', position });
|
||||
openWindow = async (position: Position) => {
|
||||
await sendMessage({ type: 'OPEN', position });
|
||||
};
|
||||
openOptionsPage = () => {
|
||||
if (navigator.userAgent.indexOf('Firefox') !== -1) {
|
||||
sendMessage({ type: 'OPEN_OPTIONS' });
|
||||
openOptionsPage = async () => {
|
||||
if (navigator.userAgent.includes('Firefox')) {
|
||||
await sendMessage({ type: 'OPEN_OPTIONS' });
|
||||
} else {
|
||||
chrome.runtime.openOptionsPage();
|
||||
await chrome.runtime.openOptionsPage();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -60,6 +56,7 @@ class Actions extends Component<Props> {
|
|||
liftedState,
|
||||
liftedDispatch,
|
||||
position,
|
||||
stateTreeSettings,
|
||||
} = this.props;
|
||||
const { features } = options;
|
||||
return (
|
||||
|
@ -75,6 +72,7 @@ class Actions extends Component<Props> {
|
|||
monitorState={this.props.monitorState}
|
||||
dispatch={liftedDispatch}
|
||||
features={options.features}
|
||||
stateTreeSettings={stateTreeSettings}
|
||||
/>
|
||||
{sliderIsOpen && options.connectionId && options.features.jump && (
|
||||
<SliderMonitor liftedState={liftedState} dispatch={liftedDispatch} />
|
||||
|
@ -87,7 +85,7 @@ class Actions extends Component<Props> {
|
|||
{features.import && <ImportButton />}
|
||||
{position &&
|
||||
(position !== '#popup' ||
|
||||
navigator.userAgent.indexOf('Firefox') !== -1) && <PrintButton />}
|
||||
navigator.userAgent.includes('Firefox')) && <PrintButton />}
|
||||
<Divider />
|
||||
<MonitorSelector />
|
||||
<Divider />
|
||||
|
@ -96,40 +94,22 @@ class Actions extends Component<Props> {
|
|||
<DispatcherButton dispatcherIsOpen={this.props.dispatcherIsOpen} />
|
||||
)}
|
||||
<Divider />
|
||||
{!window.isElectron && position !== '#left' && (
|
||||
{!isElectron && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.openWindow('left');
|
||||
onClick={async () => {
|
||||
await this.openWindow('window');
|
||||
}}
|
||||
>
|
||||
<MdBorderLeft />
|
||||
<MdOutlineWindow />
|
||||
</Button>
|
||||
)}
|
||||
{!window.isElectron && position !== '#right' && (
|
||||
{!isElectron && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.openWindow('right');
|
||||
onClick={async () => {
|
||||
await this.openWindow('remote');
|
||||
}}
|
||||
>
|
||||
<MdBorderRight />
|
||||
</Button>
|
||||
)}
|
||||
{!window.isElectron && position !== '#bottom' && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.openWindow('bottom');
|
||||
}}
|
||||
>
|
||||
<MdBorderBottom />
|
||||
</Button>
|
||||
)}
|
||||
{!window.isElectron && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.openWindow('remote');
|
||||
}}
|
||||
>
|
||||
<GoRadioTower />
|
||||
<GoBroadcast />
|
||||
</Button>
|
||||
)}
|
||||
</Toolbar>
|
||||
|
@ -149,6 +129,7 @@ const mapStateToProps = (state: StoreState) => {
|
|||
dispatcherIsOpen: state.monitor.dispatcherIsOpen,
|
||||
sliderIsOpen: state.monitor.sliderIsOpen,
|
||||
reports: state.reports.data,
|
||||
stateTreeSettings: state.stateTreeSettings,
|
||||
};
|
||||
};
|
||||
|
|
@ -27,6 +27,7 @@ class App extends Component<Props> {
|
|||
<a
|
||||
href="https://github.com/zalmoxisus/redux-devtools-extension#usage"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
the instructions
|
||||
</a>
|
|
@ -1,18 +0,0 @@
|
|||
import { Action } from 'redux';
|
||||
import { PageScriptToContentScriptMessage } from './index';
|
||||
|
||||
export type Position = 'left' | 'right' | 'bottom' | 'panel' | 'remote';
|
||||
|
||||
function post<S, A extends Action<unknown>>(
|
||||
message: PageScriptToContentScriptMessage<S, A>
|
||||
) {
|
||||
window.postMessage(message, '*');
|
||||
}
|
||||
|
||||
export default function openWindow(position?: Position) {
|
||||
post({
|
||||
source: '@devtools-page',
|
||||
type: 'OPEN',
|
||||
position: position || 'right',
|
||||
});
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
import {
|
||||
getActiveInstance,
|
||||
LIFTED_ACTION,
|
||||
SELECT_INSTANCE,
|
||||
StoreAction,
|
||||
StoreState,
|
||||
TOGGLE_PERSIST,
|
||||
UPDATE_STATE,
|
||||
} from '@redux-devtools/app';
|
||||
import { Dispatch, MiddlewareAPI } from 'redux';
|
||||
|
||||
function panelDispatcher(bgConnection: chrome.runtime.Port) {
|
||||
let autoselected = false;
|
||||
const tabId = chrome.devtools.inspectedWindow.tabId;
|
||||
|
||||
return (store: MiddlewareAPI<Dispatch<StoreAction>, StoreState>) =>
|
||||
(next: Dispatch<StoreAction>) =>
|
||||
(action: StoreAction) => {
|
||||
const result = next(action);
|
||||
if (!autoselected && action.type === UPDATE_STATE && tabId) {
|
||||
autoselected = true;
|
||||
const connections = store.getState().instances.connections[tabId];
|
||||
if (connections && connections.length === 1) {
|
||||
next({ type: SELECT_INSTANCE, selected: connections[0] });
|
||||
}
|
||||
}
|
||||
if (action.type === LIFTED_ACTION || action.type === TOGGLE_PERSIST) {
|
||||
const instances = store.getState().instances;
|
||||
const instanceId = getActiveInstance(instances);
|
||||
const id = instances.options[instanceId].connectionId;
|
||||
bgConnection.postMessage({ ...action, instanceId, id });
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export default panelDispatcher;
|
|
@ -1,34 +0,0 @@
|
|||
import {
|
||||
getActiveInstance,
|
||||
LIFTED_ACTION,
|
||||
StoreAction,
|
||||
StoreState,
|
||||
TOGGLE_PERSIST,
|
||||
UPDATE_STATE,
|
||||
} from '@redux-devtools/app';
|
||||
import { Dispatch, MiddlewareAPI, Store } from 'redux';
|
||||
import { BackgroundState } from '../reducers/background';
|
||||
import { WindowStoreAction } from '../stores/windowStore';
|
||||
import { BackgroundAction } from '../stores/backgroundStore';
|
||||
|
||||
const syncStores =
|
||||
(baseStore: Store<BackgroundState, BackgroundAction>) =>
|
||||
(store: MiddlewareAPI<Dispatch<StoreAction>, StoreState>) =>
|
||||
(next: Dispatch<WindowStoreAction>) =>
|
||||
(action: StoreAction) => {
|
||||
if (action.type === UPDATE_STATE) {
|
||||
return next({
|
||||
...action,
|
||||
instances: baseStore.getState().instances,
|
||||
});
|
||||
}
|
||||
if (action.type === LIFTED_ACTION || action.type === TOGGLE_PERSIST) {
|
||||
const instances = store.getState().instances;
|
||||
const instanceId = getActiveInstance(instances);
|
||||
const id = instances.options[instanceId].connectionId;
|
||||
baseStore.dispatch({ ...action, instanceId, id } as any);
|
||||
}
|
||||
return next(action);
|
||||
};
|
||||
|
||||
export default syncStores;
|
|
@ -1,41 +0,0 @@
|
|||
import { combineReducers, Reducer } from 'redux';
|
||||
import {
|
||||
connection,
|
||||
ConnectionState,
|
||||
instances,
|
||||
InstancesState,
|
||||
monitor,
|
||||
MonitorState,
|
||||
notification,
|
||||
NotificationState,
|
||||
reports,
|
||||
ReportsState,
|
||||
section,
|
||||
SectionState,
|
||||
StoreAction,
|
||||
theme,
|
||||
ThemeState,
|
||||
} from '@redux-devtools/app';
|
||||
|
||||
export interface StoreStateWithoutSocket {
|
||||
readonly section: SectionState;
|
||||
readonly theme: ThemeState;
|
||||
readonly connection: ConnectionState;
|
||||
readonly monitor: MonitorState;
|
||||
readonly instances: InstancesState;
|
||||
readonly reports: ReportsState;
|
||||
readonly notification: NotificationState;
|
||||
}
|
||||
|
||||
const rootReducer: Reducer<StoreStateWithoutSocket, StoreAction> =
|
||||
combineReducers<StoreStateWithoutSocket>({
|
||||
instances,
|
||||
monitor,
|
||||
reports,
|
||||
notification,
|
||||
section,
|
||||
theme,
|
||||
connection,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
|
@ -1,27 +0,0 @@
|
|||
import { combineReducers, Reducer } from 'redux';
|
||||
import {
|
||||
connection,
|
||||
monitor,
|
||||
notification,
|
||||
reports,
|
||||
section,
|
||||
socket,
|
||||
theme,
|
||||
StoreState,
|
||||
} from '@redux-devtools/app';
|
||||
import instances from './instances';
|
||||
import { WindowStoreAction } from '../../stores/windowStore';
|
||||
|
||||
const rootReducer: Reducer<StoreState, WindowStoreAction> =
|
||||
combineReducers<StoreState>({
|
||||
instances,
|
||||
monitor,
|
||||
socket,
|
||||
reports,
|
||||
notification,
|
||||
section,
|
||||
theme,
|
||||
connection,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
|
@ -1,34 +0,0 @@
|
|||
import {
|
||||
instancesInitialState,
|
||||
dispatchAction,
|
||||
UPDATE_STATE,
|
||||
SELECT_INSTANCE,
|
||||
LIFTED_ACTION,
|
||||
SET_PERSIST,
|
||||
} from '@redux-devtools/app';
|
||||
import {
|
||||
ExpandedUpdateStateAction,
|
||||
WindowStoreAction,
|
||||
} from '../../stores/windowStore';
|
||||
|
||||
export default function instances(
|
||||
state = instancesInitialState,
|
||||
action: WindowStoreAction
|
||||
) {
|
||||
switch (action.type) {
|
||||
case UPDATE_STATE:
|
||||
return {
|
||||
...(action as ExpandedUpdateStateAction).instances,
|
||||
selected: state.selected,
|
||||
};
|
||||
case LIFTED_ACTION:
|
||||
if (action.message === 'DISPATCH') return dispatchAction(state, action);
|
||||
return state;
|
||||
case SELECT_INSTANCE:
|
||||
return { ...state, selected: action.selected };
|
||||
case SET_PERSIST:
|
||||
return { ...state, persisted: action.payload };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import {
|
||||
Action,
|
||||
createStore,
|
||||
PreloadedState,
|
||||
Reducer,
|
||||
StoreEnhancer,
|
||||
} from 'redux';
|
||||
|
||||
export default function configureStore<S, A extends Action<unknown>>(
|
||||
reducer: Reducer<S, A>,
|
||||
initialState: PreloadedState<S> | undefined,
|
||||
enhance: () => StoreEnhancer
|
||||
) {
|
||||
return createStore(reducer, initialState, enhance());
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
import { createStore, applyMiddleware, Reducer } from 'redux';
|
||||
import localForage from 'localforage';
|
||||
import { persistReducer, persistStore } from 'redux-persist';
|
||||
import { exportStateMiddleware, StoreAction } from '@redux-devtools/app';
|
||||
import panelDispatcher from '../middlewares/panelSync';
|
||||
import rootReducer, { StoreStateWithoutSocket } from '../reducers/panel';
|
||||
|
||||
const persistConfig = {
|
||||
key: 'redux-devtools',
|
||||
blacklist: ['instances', 'socket'],
|
||||
storage: localForage,
|
||||
};
|
||||
|
||||
const persistedReducer: Reducer<StoreStateWithoutSocket, StoreAction> =
|
||||
persistReducer(persistConfig, rootReducer) as any;
|
||||
|
||||
export default function configureStore(
|
||||
position: string,
|
||||
bgConnection: chrome.runtime.Port
|
||||
) {
|
||||
const enhancer = applyMiddleware(
|
||||
exportStateMiddleware,
|
||||
panelDispatcher(bgConnection)
|
||||
);
|
||||
const store = createStore(persistedReducer, enhancer);
|
||||
const persistor = persistStore(store);
|
||||
return { store, persistor };
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
import {
|
||||
createStore,
|
||||
compose,
|
||||
applyMiddleware,
|
||||
Store,
|
||||
StoreEnhancer,
|
||||
Reducer,
|
||||
} from 'redux';
|
||||
import localForage from 'localforage';
|
||||
import { persistReducer, persistStore } from 'redux-persist';
|
||||
import {
|
||||
api,
|
||||
CONNECT_REQUEST,
|
||||
exportStateMiddleware,
|
||||
InstancesState,
|
||||
StoreActionWithoutUpdateState,
|
||||
StoreState,
|
||||
UpdateStateAction,
|
||||
} from '@redux-devtools/app';
|
||||
import syncStores from '../middlewares/windowSync';
|
||||
import instanceSelector from '../middlewares/instanceSelector';
|
||||
import rootReducer from '../reducers/window';
|
||||
import { BackgroundState } from '../reducers/background';
|
||||
import { BackgroundAction } from './backgroundStore';
|
||||
import { EmptyUpdateStateAction, NAAction } from '../middlewares/api';
|
||||
|
||||
export interface ExpandedUpdateStateAction extends UpdateStateAction {
|
||||
readonly instances: InstancesState;
|
||||
}
|
||||
|
||||
export type WindowStoreAction =
|
||||
| StoreActionWithoutUpdateState
|
||||
| ExpandedUpdateStateAction
|
||||
| NAAction
|
||||
| EmptyUpdateStateAction;
|
||||
|
||||
const persistConfig = {
|
||||
key: 'redux-devtools',
|
||||
blacklist: ['instances', 'socket'],
|
||||
storage: localForage,
|
||||
};
|
||||
|
||||
const persistedReducer: Reducer<StoreState, WindowStoreAction> = persistReducer(
|
||||
persistConfig,
|
||||
rootReducer
|
||||
) as any;
|
||||
|
||||
export default function configureStore(
|
||||
baseStore: Store<BackgroundState, BackgroundAction>,
|
||||
position: string
|
||||
) {
|
||||
let enhancer: StoreEnhancer;
|
||||
const middlewares = [exportStateMiddleware, api, syncStores(baseStore)];
|
||||
if (!position || position === '#popup') {
|
||||
// select current tab instance for devPanel and pageAction
|
||||
middlewares.push(instanceSelector);
|
||||
}
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
enhancer = applyMiddleware(...middlewares);
|
||||
} else {
|
||||
enhancer = compose(
|
||||
applyMiddleware(...middlewares),
|
||||
window.__REDUX_DEVTOOLS_EXTENSION__
|
||||
? window.__REDUX_DEVTOOLS_EXTENSION__()
|
||||
: (noop: unknown) => noop
|
||||
);
|
||||
}
|
||||
const store = createStore(persistedReducer, enhancer);
|
||||
const persistor = persistStore(store, null, () => {
|
||||
if (store.getState().connection.type !== 'disabled') {
|
||||
store.dispatch({
|
||||
type: CONNECT_REQUEST,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return { store, persistor };
|
||||
}
|
|
@ -2,34 +2,28 @@ import openDevToolsWindow, { DevToolsPosition } from './openWindow';
|
|||
|
||||
export function createMenu() {
|
||||
const menus = [
|
||||
{ id: 'devtools-left', title: 'To left' },
|
||||
{ id: 'devtools-right', title: 'To right' },
|
||||
{ id: 'devtools-bottom', title: 'To bottom' },
|
||||
{
|
||||
id: 'devtools-panel',
|
||||
title: 'Open in a panel (enable in browser settings)',
|
||||
},
|
||||
{ id: 'devtools-window', title: 'Open in a window' },
|
||||
{ id: 'devtools-remote', title: 'Open Remote DevTools' },
|
||||
];
|
||||
|
||||
let shortcuts: { [commandName: string]: string | undefined } = {};
|
||||
const shortcuts: { [commandName: string]: string | undefined } = {};
|
||||
chrome.commands.getAll((commands) => {
|
||||
commands.forEach(({ name, shortcut }) => {
|
||||
for (const { name, shortcut } of commands) {
|
||||
shortcuts[name!] = shortcut;
|
||||
});
|
||||
}
|
||||
|
||||
menus.forEach(({ id, title }) => {
|
||||
for (const { id, title } of menus) {
|
||||
chrome.contextMenus.create({
|
||||
id: id,
|
||||
title: title + (shortcuts[id] ? ' (' + shortcuts[id] + ')' : ''),
|
||||
contexts: ['all'],
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function removeMenu() {
|
||||
chrome.contextMenus.removeAll();
|
||||
export async function removeMenu() {
|
||||
await chrome.contextMenus.removeAll();
|
||||
}
|
||||
|
||||
chrome.contextMenus.onClicked.addListener(({ menuItemId }) => {
|
|
@ -1,30 +1,23 @@
|
|||
import { Store } from 'redux';
|
||||
import configureStore, {
|
||||
BackgroundAction,
|
||||
} from '../../../app/stores/backgroundStore';
|
||||
import '../chromeApiMock';
|
||||
import configureStore from './store/backgroundStore';
|
||||
import openDevToolsWindow, { DevToolsPosition } from './openWindow';
|
||||
import { createMenu, removeMenu } from './contextMenus';
|
||||
import syncOptions from '../options/syncOptions';
|
||||
import { BackgroundState } from '../../../app/reducers/background';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
store: Store<BackgroundState, BackgroundAction>;
|
||||
}
|
||||
}
|
||||
import { getOptions } from '../options/syncOptions';
|
||||
|
||||
// Expose the extension's store globally to access it from the windows
|
||||
// via chrome.runtime.getBackgroundPage
|
||||
window.store = configureStore();
|
||||
export const store = configureStore();
|
||||
|
||||
// Listen for keyboard shortcuts
|
||||
chrome.commands.onCommand.addListener((shortcut) => {
|
||||
openDevToolsWindow(shortcut as DevToolsPosition);
|
||||
});
|
||||
|
||||
// Create the context menu when installed
|
||||
// Disable the action by default and create the context menu when installed
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
syncOptions().get((option) => {
|
||||
void chrome.action.disable();
|
||||
|
||||
getOptions((option) => {
|
||||
if (option.showContextMenus) createMenu();
|
||||
});
|
||||
});
|
||||
|
@ -33,6 +26,13 @@ chrome.runtime.onInstalled.addListener(() => {
|
|||
chrome.storage.onChanged.addListener((changes) => {
|
||||
if (changes.showContextMenus) {
|
||||
if (changes.showContextMenus.newValue) createMenu();
|
||||
else removeMenu();
|
||||
else void removeMenu();
|
||||
}
|
||||
});
|
||||
|
||||
// https://developer.chrome.com/docs/extensions/develop/migrate/to-service-workers#keep_a_service_worker_alive_continuously
|
||||
setInterval(
|
||||
() =>
|
||||
void chrome.storage.local.set({ 'last-heartbeat': new Date().getTime() }),
|
||||
20000,
|
||||
);
|
|
@ -1,9 +1,10 @@
|
|||
import { LIFTED_ACTION } from '@redux-devtools/app';
|
||||
import { store } from './index';
|
||||
|
||||
export function getReport(
|
||||
reportId: string,
|
||||
tabId: string | number,
|
||||
instanceId: number
|
||||
instanceId: number,
|
||||
) {
|
||||
chrome.storage.local.get(['s:hostname', 's:port', 's:secure'], (options) => {
|
||||
if (!options['s:hostname'] || !options['s:port']) return;
|
||||
|
@ -24,7 +25,7 @@ export function getReport(
|
|||
.then((json) => {
|
||||
const { payload, preloadedState } = json;
|
||||
if (!payload) return;
|
||||
window.store.dispatch({
|
||||
store.dispatch({
|
||||
type: LIFTED_ACTION,
|
||||
message: 'IMPORT',
|
||||
state: JSON.stringify({ payload, preloadedState }),
|
34
extension/src/background/openWindow.ts
Normal file
34
extension/src/background/openWindow.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
export type DevToolsPosition = 'devtools-window' | 'devtools-remote';
|
||||
|
||||
const windows: { [K in DevToolsPosition]?: number } = {};
|
||||
|
||||
export default function openDevToolsWindow(position: DevToolsPosition) {
|
||||
if (!windows[position]) {
|
||||
createWindow(position);
|
||||
} else {
|
||||
chrome.windows.update(windows[position], { focused: true }, () => {
|
||||
if (chrome.runtime.lastError) createWindow(position);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createWindow(position: DevToolsPosition) {
|
||||
const url = chrome.runtime.getURL(getPath(position));
|
||||
chrome.windows.create({ type: 'popup', url }, (win) => {
|
||||
windows[position] = win!.id;
|
||||
if (navigator.userAgent.includes('Firefox')) {
|
||||
void chrome.windows.update(win!.id!, { focused: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getPath(position: DevToolsPosition) {
|
||||
switch (position) {
|
||||
case 'devtools-window':
|
||||
return 'devpanel.html';
|
||||
case 'devtools-remote':
|
||||
return 'remote.html';
|
||||
default:
|
||||
throw new Error('Unrecognized position');
|
||||
}
|
||||
}
|
|
@ -11,32 +11,24 @@ import {
|
|||
TOGGLE_PERSIST,
|
||||
UPDATE_STATE,
|
||||
} from '@redux-devtools/app';
|
||||
import syncOptions, {
|
||||
Options,
|
||||
OptionsMessage,
|
||||
SyncOptions,
|
||||
} from '../../browser/extension/options/syncOptions';
|
||||
import openDevToolsWindow, {
|
||||
DevToolsPosition,
|
||||
} from '../../browser/extension/background/openWindow';
|
||||
import { getReport } from '../../browser/extension/background/logging';
|
||||
import { Action, Dispatch, MiddlewareAPI } from 'redux';
|
||||
import {
|
||||
import type { Options, OptionsMessage } from '../../options/syncOptions';
|
||||
import openDevToolsWindow, { DevToolsPosition } from '../openWindow';
|
||||
import { getReport } from '../logging';
|
||||
import { Action, Dispatch, Middleware } from 'redux';
|
||||
import type {
|
||||
ContentScriptToBackgroundMessage,
|
||||
SplitMessage,
|
||||
} from '../../browser/extension/inject/contentScript';
|
||||
import {
|
||||
} from '../../contentScript';
|
||||
import type {
|
||||
ErrorMessage,
|
||||
PageScriptToContentScriptMessageForwardedToMonitors,
|
||||
PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance,
|
||||
} from '../api';
|
||||
} from '../../pageScript/api';
|
||||
import { LiftedState } from '@redux-devtools/instrument';
|
||||
import {
|
||||
BackgroundAction,
|
||||
LiftedActionAction,
|
||||
} from '../stores/backgroundStore';
|
||||
import { Position } from '../api/openWindow';
|
||||
import { BackgroundState } from '../reducers/background';
|
||||
import type { BackgroundAction, LiftedActionAction } from './backgroundStore';
|
||||
import type { Position } from '../../pageScript/api/openWindow';
|
||||
import type { BackgroundState } from './backgroundReducer';
|
||||
import { store } from '../index';
|
||||
|
||||
interface TabMessageBase {
|
||||
readonly type: string;
|
||||
|
@ -56,6 +48,11 @@ interface StopAction extends TabMessageBase {
|
|||
readonly id?: never;
|
||||
}
|
||||
|
||||
interface OptionsAction {
|
||||
readonly type: 'OPTIONS';
|
||||
readonly options: Options;
|
||||
}
|
||||
|
||||
interface DispatchAction extends TabMessageBase {
|
||||
readonly type: 'DISPATCH';
|
||||
readonly action: AppDispatchAction;
|
||||
|
@ -89,7 +86,7 @@ export interface NAAction {
|
|||
readonly id: string | number;
|
||||
}
|
||||
|
||||
interface InitMessage<S, A extends Action<unknown>> {
|
||||
interface InitMessage<S, A extends Action<string>> {
|
||||
readonly type: 'INIT';
|
||||
readonly payload: string;
|
||||
instanceId: string;
|
||||
|
@ -142,7 +139,7 @@ interface SerializedActionMessage {
|
|||
readonly nextActionId: number;
|
||||
}
|
||||
|
||||
interface SerializedStateMessage<S, A extends Action<unknown>> {
|
||||
interface SerializedStateMessage<S, A extends Action<string>> {
|
||||
readonly type: 'STATE';
|
||||
readonly payload: Omit<
|
||||
LiftedState<S, A, unknown>,
|
||||
|
@ -156,7 +153,7 @@ interface SerializedStateMessage<S, A extends Action<unknown>> {
|
|||
readonly committedState: boolean;
|
||||
}
|
||||
|
||||
type UpdateStateRequest<S, A extends Action<unknown>> =
|
||||
export type UpdateStateRequest<S, A extends Action<string>> =
|
||||
| InitMessage<S, A>
|
||||
| LiftedMessage
|
||||
| SerializedPartialStateMessage
|
||||
|
@ -164,91 +161,149 @@ type UpdateStateRequest<S, A extends Action<unknown>> =
|
|||
| SerializedActionMessage
|
||||
| SerializedStateMessage<S, A>;
|
||||
|
||||
export interface EmptyUpdateStateAction {
|
||||
readonly type: typeof UPDATE_STATE;
|
||||
}
|
||||
|
||||
interface UpdateStateAction<S, A extends Action<unknown>> {
|
||||
interface UpdateStateAction<S, A extends Action<string>> {
|
||||
readonly type: typeof UPDATE_STATE;
|
||||
request: UpdateStateRequest<S, A>;
|
||||
readonly id: string | number;
|
||||
}
|
||||
|
||||
type SplitUpdateStateRequestStart<S, A extends Action<string>> = {
|
||||
split: 'start';
|
||||
} & Partial<UpdateStateRequest<S, A>>;
|
||||
|
||||
interface SplitUpdateStateRequestChunk {
|
||||
readonly split: 'chunk';
|
||||
readonly chunk: [string, string];
|
||||
}
|
||||
|
||||
interface SplitUpdateStateRequestEnd {
|
||||
readonly split: 'end';
|
||||
}
|
||||
|
||||
export type SplitUpdateStateRequest<S, A extends Action<string>> =
|
||||
| SplitUpdateStateRequestStart<S, A>
|
||||
| SplitUpdateStateRequestChunk
|
||||
| SplitUpdateStateRequestEnd;
|
||||
|
||||
interface SplitUpdateStateAction<S, A extends Action<string>> {
|
||||
readonly type: typeof UPDATE_STATE;
|
||||
request: SplitUpdateStateRequest<S, A>;
|
||||
readonly id: string | number;
|
||||
}
|
||||
|
||||
export type TabMessage =
|
||||
| StartAction
|
||||
| StopAction
|
||||
| OptionsMessage
|
||||
| OptionsAction
|
||||
| DispatchAction
|
||||
| ImportAction
|
||||
| ActionAction
|
||||
| ExportAction;
|
||||
export type PanelMessage<S, A extends Action<unknown>> =
|
||||
| NAAction
|
||||
export type PanelMessageWithoutNA<S, A extends Action<string>> =
|
||||
| ErrorMessage
|
||||
| UpdateStateAction<S, A>
|
||||
| SetPersistAction;
|
||||
export type MonitorMessage =
|
||||
| NAAction
|
||||
| ErrorMessage
|
||||
| EmptyUpdateStateAction
|
||||
| SetPersistAction;
|
||||
export type PanelMessage<S, A extends Action<string>> =
|
||||
| PanelMessageWithoutNA<S, A>
|
||||
| NAAction;
|
||||
export type PanelMessageWithSplitAction<S, A extends Action<string>> =
|
||||
| PanelMessage<S, A>
|
||||
| SplitUpdateStateAction<S, A>;
|
||||
|
||||
type TabPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
||||
postMessage: (message: TabMessage) => void;
|
||||
};
|
||||
type PanelPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
||||
postMessage: <S, A extends Action<unknown>>(
|
||||
message: PanelMessage<S, A>
|
||||
postMessage: <S, A extends Action<string>>(
|
||||
message: PanelMessageWithSplitAction<S, A>,
|
||||
) => void;
|
||||
};
|
||||
type MonitorPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
||||
postMessage: (message: MonitorMessage) => void;
|
||||
};
|
||||
|
||||
export const CONNECTED = 'socket/CONNECTED';
|
||||
export const DISCONNECTED = 'socket/DISCONNECTED';
|
||||
const connections: {
|
||||
readonly tab: { [K in number | string]: TabPort };
|
||||
readonly panel: { [K in number | string]: PanelPort };
|
||||
readonly monitor: { [K in number | string]: MonitorPort };
|
||||
} = {
|
||||
tab: {},
|
||||
panel: {},
|
||||
monitor: {},
|
||||
};
|
||||
const chunks: {
|
||||
[instanceId: string]: PageScriptToContentScriptMessageForwardedToMonitors<
|
||||
unknown,
|
||||
Action<unknown>
|
||||
Action<string>
|
||||
>;
|
||||
} = {};
|
||||
let monitors = 0;
|
||||
let isMonitored = false;
|
||||
|
||||
const getId = (sender: chrome.runtime.MessageSender, name?: string) =>
|
||||
sender.tab ? sender.tab.id! : name || sender.id!;
|
||||
|
||||
type MonitorAction<S, A extends Action<unknown>> =
|
||||
type MonitorAction<S, A extends Action<string>> =
|
||||
| NAAction
|
||||
| ErrorMessage
|
||||
| UpdateStateAction<S, A>
|
||||
| SetPersistAction;
|
||||
|
||||
function toMonitors<S, A extends Action<unknown>>(
|
||||
action: MonitorAction<S, A>,
|
||||
tabId?: string | number,
|
||||
verbose?: boolean
|
||||
) {
|
||||
Object.keys(connections.monitor).forEach((id) => {
|
||||
connections.monitor[id].postMessage(
|
||||
verbose || action.type === 'ERROR' || action.type === SET_PERSIST
|
||||
? action
|
||||
: { type: UPDATE_STATE }
|
||||
);
|
||||
});
|
||||
Object.keys(connections.panel).forEach((id) => {
|
||||
connections.panel[id].postMessage(action);
|
||||
});
|
||||
// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts
|
||||
const maxChromeMsgSize = 32 * 1024 * 1024;
|
||||
|
||||
function toMonitors<S, A extends Action<string>>(action: MonitorAction<S, A>) {
|
||||
console.log(`Message to monitors: ${action.type}`);
|
||||
|
||||
for (const port of Object.values(connections.panel)) {
|
||||
try {
|
||||
port.postMessage(action);
|
||||
} catch (err) {
|
||||
if (
|
||||
action.type !== UPDATE_STATE ||
|
||||
err == null ||
|
||||
(err as Error).message !==
|
||||
'Message length exceeded maximum allowed length.'
|
||||
) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const splitMessageStart: SplitUpdateStateRequestStart<S, A> = {
|
||||
split: 'start',
|
||||
};
|
||||
const toSplit: [string, string][] = [];
|
||||
let size = 0;
|
||||
for (const [key, value] of Object.entries(
|
||||
action.request as unknown as Record<string, unknown>,
|
||||
)) {
|
||||
if (typeof value === 'string') {
|
||||
size += value.length;
|
||||
if (size > maxChromeMsgSize) {
|
||||
toSplit.push([key, value]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
(splitMessageStart as any)[key as keyof typeof splitMessageStart] =
|
||||
value;
|
||||
}
|
||||
|
||||
port.postMessage({ ...action, request: splitMessageStart });
|
||||
|
||||
for (let i = 0; i < toSplit.length; i++) {
|
||||
for (let j = 0; j < toSplit[i][1].length; j += maxChromeMsgSize) {
|
||||
port.postMessage({
|
||||
...action,
|
||||
request: {
|
||||
split: 'chunk',
|
||||
chunk: [
|
||||
toSplit[i][0],
|
||||
toSplit[i][1].substring(j, j + maxChromeMsgSize),
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
port.postMessage({ ...action, request: { split: 'end' } });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ImportMessage {
|
||||
|
@ -262,19 +317,15 @@ interface ImportMessage {
|
|||
type ToContentScriptMessage = ImportMessage | LiftedActionAction;
|
||||
|
||||
function toContentScript(messageBody: ToContentScriptMessage) {
|
||||
console.log(`Message to tab ${messageBody.id}: ${messageBody.message}`);
|
||||
|
||||
if (messageBody.message === 'DISPATCH') {
|
||||
const { message, action, id, instanceId, state } = messageBody;
|
||||
connections.tab[id!].postMessage({
|
||||
type: message,
|
||||
action,
|
||||
state: nonReduxDispatch(
|
||||
window.store,
|
||||
message,
|
||||
instanceId,
|
||||
action as AppDispatchAction,
|
||||
state
|
||||
),
|
||||
id: instanceId.toString().replace(/^[^\/]+\//, ''),
|
||||
state: nonReduxDispatch(store, message, instanceId, action, state),
|
||||
id: instanceId.toString().replace(/^[^/]+\//, ''),
|
||||
});
|
||||
} else if (messageBody.message === 'IMPORT') {
|
||||
const { message, action, id, instanceId, state } = messageBody;
|
||||
|
@ -282,13 +333,13 @@ function toContentScript(messageBody: ToContentScriptMessage) {
|
|||
type: message,
|
||||
action,
|
||||
state: nonReduxDispatch(
|
||||
window.store,
|
||||
store,
|
||||
message,
|
||||
instanceId,
|
||||
action as unknown as AppDispatchAction,
|
||||
state
|
||||
state,
|
||||
),
|
||||
id: instanceId.toString().replace(/^[^\/]+\//, ''),
|
||||
id: instanceId.toString().replace(/^[^/]+\//, ''),
|
||||
});
|
||||
} else if (messageBody.message === 'ACTION') {
|
||||
const { message, action, id, instanceId, state } = messageBody;
|
||||
|
@ -296,13 +347,13 @@ function toContentScript(messageBody: ToContentScriptMessage) {
|
|||
type: message,
|
||||
action,
|
||||
state: nonReduxDispatch(
|
||||
window.store,
|
||||
store,
|
||||
message,
|
||||
instanceId,
|
||||
action as unknown as AppDispatchAction,
|
||||
state
|
||||
state,
|
||||
),
|
||||
id: instanceId.toString().replace(/^[^\/]+\//, ''),
|
||||
id: instanceId.toString().replace(/^[^/]+\//, ''),
|
||||
});
|
||||
} else if (messageBody.message === 'EXPORT') {
|
||||
const { message, action, id, instanceId, state } = messageBody;
|
||||
|
@ -310,53 +361,41 @@ function toContentScript(messageBody: ToContentScriptMessage) {
|
|||
type: message,
|
||||
action,
|
||||
state: nonReduxDispatch(
|
||||
window.store,
|
||||
store,
|
||||
message,
|
||||
instanceId,
|
||||
action as unknown as AppDispatchAction,
|
||||
state
|
||||
state,
|
||||
),
|
||||
id: instanceId.toString().replace(/^[^\/]+\//, ''),
|
||||
id: instanceId.toString().replace(/^[^/]+\//, ''),
|
||||
});
|
||||
} else {
|
||||
const { message, action, id, instanceId, state } = messageBody;
|
||||
connections.tab[id!].postMessage({
|
||||
connections.tab[id].postMessage({
|
||||
type: message,
|
||||
action,
|
||||
state: nonReduxDispatch(
|
||||
window.store,
|
||||
store,
|
||||
message,
|
||||
instanceId,
|
||||
action as AppDispatchAction,
|
||||
state
|
||||
state,
|
||||
),
|
||||
id: (instanceId as number).toString().replace(/^[^\/]+\//, ''),
|
||||
id: (instanceId as number).toString().replace(/^[^/]+\//, ''),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function toAllTabs(msg: TabMessage) {
|
||||
const tabs = connections.tab;
|
||||
Object.keys(tabs).forEach((id) => {
|
||||
tabs[id].postMessage(msg);
|
||||
});
|
||||
}
|
||||
console.log(`Message to all tabs: ${msg.type}`);
|
||||
|
||||
function monitorInstances(shouldMonitor: boolean, id?: string) {
|
||||
if (!id && isMonitored === shouldMonitor) return;
|
||||
const action = {
|
||||
type: shouldMonitor ? ('START' as const) : ('STOP' as const),
|
||||
};
|
||||
if (id) {
|
||||
if (connections.tab[id]) connections.tab[id].postMessage(action);
|
||||
} else {
|
||||
toAllTabs(action);
|
||||
for (const tabPort of Object.values(connections.tab)) {
|
||||
tabPort.postMessage(msg);
|
||||
}
|
||||
isMonitored = shouldMonitor;
|
||||
}
|
||||
|
||||
function getReducerError() {
|
||||
const instancesState = window.store.getState().instances;
|
||||
const instancesState = store.getState().instances;
|
||||
const payload = instancesState.states[instancesState.current];
|
||||
const computedState = payload.computedStates[payload.currentStateIndex];
|
||||
if (!computedState) return false;
|
||||
|
@ -364,13 +403,13 @@ function getReducerError() {
|
|||
}
|
||||
|
||||
function togglePersist() {
|
||||
const state = window.store.getState();
|
||||
const state = store.getState();
|
||||
if (state.instances.persisted) {
|
||||
Object.keys(state.instances.connections).forEach((id) => {
|
||||
for (const id of Object.keys(state.instances.connections)) {
|
||||
if (connections.tab[id]) return;
|
||||
window.store.dispatch({ type: REMOVE_INSTANCE, id });
|
||||
store.dispatch({ type: REMOVE_INSTANCE, id });
|
||||
toMonitors({ type: 'NA', id });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,45 +422,35 @@ interface OpenOptionsMessage {
|
|||
readonly type: 'OPEN_OPTIONS';
|
||||
}
|
||||
|
||||
interface GetOptionsMessage {
|
||||
readonly type: 'GET_OPTIONS';
|
||||
}
|
||||
export type SingleMessage = OpenMessage | OpenOptionsMessage | OptionsMessage;
|
||||
|
||||
export type SingleMessage =
|
||||
| OpenMessage
|
||||
| OpenOptionsMessage
|
||||
| GetOptionsMessage;
|
||||
|
||||
type BackgroundStoreMessage<S, A extends Action<unknown>> =
|
||||
type BackgroundStoreMessage<S, A extends Action<string>> =
|
||||
| PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<S, A>
|
||||
| SplitMessage
|
||||
| SingleMessage;
|
||||
type BackgroundStoreResponse = { readonly options: Options };
|
||||
|
||||
// Receive messages from content scripts
|
||||
function messaging<S, A extends Action<unknown>>(
|
||||
function messaging<S, A extends Action<string>>(
|
||||
request: BackgroundStoreMessage<S, A>,
|
||||
sender: chrome.runtime.MessageSender,
|
||||
sendResponse?: (response?: BackgroundStoreResponse) => void
|
||||
) {
|
||||
let tabId = getId(sender);
|
||||
console.log(`Message from tab ${tabId}: ${request.type ?? request.split}`);
|
||||
if (!tabId) return;
|
||||
if (sender.frameId) tabId = `${tabId}-${sender.frameId}`;
|
||||
|
||||
if (request.type === 'STOP') {
|
||||
if (!Object.keys(window.store.getState().instances.connections).length) {
|
||||
window.store.dispatch({ type: DISCONNECTED });
|
||||
if (!Object.keys(store.getState().instances.connections).length) {
|
||||
store.dispatch({ type: DISCONNECTED });
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (request.type === 'OPEN_OPTIONS') {
|
||||
chrome.runtime.openOptionsPage();
|
||||
void chrome.runtime.openOptionsPage();
|
||||
return;
|
||||
}
|
||||
if (request.type === 'GET_OPTIONS') {
|
||||
window.syncOptions.get((options) => {
|
||||
sendResponse!({ options });
|
||||
});
|
||||
if (request.type === 'OPTIONS') {
|
||||
toAllTabs({ type: 'OPTIONS', options: request.options });
|
||||
return;
|
||||
}
|
||||
if (request.type === 'GET_REPORT') {
|
||||
|
@ -429,12 +458,8 @@ function messaging<S, A extends Action<unknown>>(
|
|||
return;
|
||||
}
|
||||
if (request.type === 'OPEN') {
|
||||
let position: DevToolsPosition = 'devtools-left';
|
||||
if (
|
||||
['remote', 'panel', 'left', 'right', 'bottom'].indexOf(
|
||||
request.position
|
||||
) !== -1
|
||||
) {
|
||||
let position: DevToolsPosition = 'devtools-window';
|
||||
if (['remote', 'window'].includes(request.position)) {
|
||||
position = ('devtools-' + request.position) as DevToolsPosition;
|
||||
}
|
||||
openDevToolsWindow(position);
|
||||
|
@ -442,7 +467,7 @@ function messaging<S, A extends Action<unknown>>(
|
|||
}
|
||||
if (request.type === 'ERROR') {
|
||||
if (request.payload) {
|
||||
toMonitors(request, tabId);
|
||||
toMonitors(request);
|
||||
return;
|
||||
}
|
||||
if (!request.message) return;
|
||||
|
@ -482,56 +507,56 @@ function messaging<S, A extends Action<unknown>>(
|
|||
if (request.instanceId) {
|
||||
action.request.instanceId = instanceId;
|
||||
}
|
||||
window.store.dispatch(action);
|
||||
store.dispatch(action);
|
||||
|
||||
if (request.type === 'EXPORT') {
|
||||
toMonitors(action, tabId, true);
|
||||
} else {
|
||||
toMonitors(action, tabId);
|
||||
}
|
||||
toMonitors(action);
|
||||
}
|
||||
|
||||
function disconnect(
|
||||
type: 'tab' | 'monitor' | 'panel',
|
||||
type: 'tab' | 'panel',
|
||||
id: number | string,
|
||||
listener?: (message: any, port: chrome.runtime.Port) => void
|
||||
listener: (message: any, port: chrome.runtime.Port) => void,
|
||||
) {
|
||||
return function disconnectListener() {
|
||||
console.log(`Disconnected from ${type} ${id}`);
|
||||
|
||||
const p = connections[type][id];
|
||||
if (listener && p) p.onMessage.removeListener(listener);
|
||||
if (p) p.onDisconnect.removeListener(disconnectListener);
|
||||
delete connections[type][id];
|
||||
if (type === 'tab') {
|
||||
if (!window.store.getState().instances.persisted) {
|
||||
window.store.dispatch({ type: REMOVE_INSTANCE, id });
|
||||
if (!store.getState().instances.persisted) {
|
||||
store.dispatch({ type: REMOVE_INSTANCE, id });
|
||||
toMonitors({ type: 'NA', id });
|
||||
}
|
||||
} else {
|
||||
monitors--;
|
||||
if (!monitors) monitorInstances(false);
|
||||
if (monitors === 0) toAllTabs({ type: 'STOP' });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function onConnect<S, A extends Action<unknown>>(port: chrome.runtime.Port) {
|
||||
function onConnect<S, A extends Action<string>>(port: chrome.runtime.Port) {
|
||||
let id: number | string;
|
||||
let listener;
|
||||
|
||||
window.store.dispatch({ type: CONNECTED, port });
|
||||
store.dispatch({ type: CONNECTED, port });
|
||||
|
||||
if (port.name === 'tab') {
|
||||
id = getId(port.sender!);
|
||||
console.log(`Connected to tab ${id}`);
|
||||
if (port.sender!.frameId) id = `${id}-${port.sender!.frameId}`;
|
||||
connections.tab[id] = port;
|
||||
listener = (msg: ContentScriptToBackgroundMessage<S, A>) => {
|
||||
console.log(`Message from tab ${id}: ${msg.name}`);
|
||||
if (msg.name === 'INIT_INSTANCE') {
|
||||
if (typeof id === 'number') {
|
||||
chrome.pageAction.show(id);
|
||||
chrome.pageAction.setIcon({ tabId: id, path: 'img/logo/38x38.png' });
|
||||
void chrome.action.enable(id);
|
||||
void chrome.action.setIcon({ tabId: id, path: 'img/logo/38x38.png' });
|
||||
}
|
||||
if (isMonitored) port.postMessage({ type: 'START' });
|
||||
if (monitors > 0) port.postMessage({ type: 'START' });
|
||||
|
||||
const state = window.store.getState();
|
||||
const state = store.getState();
|
||||
if (state.instances.persisted) {
|
||||
const instanceId = `${id}/${msg.instanceId}`;
|
||||
const persistedState = state.instances.states[instanceId];
|
||||
|
@ -542,7 +567,7 @@ function onConnect<S, A extends Action<unknown>>(port: chrome.runtime.Port) {
|
|||
instanceId,
|
||||
state: stringifyJSON(
|
||||
persistedState,
|
||||
state.instances.options[instanceId].serialize
|
||||
state.instances.options[instanceId].serialize,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
@ -555,22 +580,45 @@ function onConnect<S, A extends Action<unknown>>(port: chrome.runtime.Port) {
|
|||
port.onMessage.addListener(listener);
|
||||
port.onDisconnect.addListener(disconnect('tab', id, listener));
|
||||
} else if (port.name && port.name.indexOf('monitor') === 0) {
|
||||
id = getId(port.sender!, port.name);
|
||||
connections.monitor[id] = port;
|
||||
monitorInstances(true);
|
||||
monitors++;
|
||||
port.onDisconnect.addListener(disconnect('monitor', id));
|
||||
} else {
|
||||
// devpanel
|
||||
id = port.name || port.sender!.frameId!;
|
||||
id = getId(port.sender!, port.name);
|
||||
console.log(`Connected to monitor ${id}`);
|
||||
connections.panel[id] = port;
|
||||
monitorInstances(true, port.name);
|
||||
monitors++;
|
||||
toAllTabs({ type: 'START' });
|
||||
listener = (msg: BackgroundAction) => {
|
||||
window.store.dispatch(msg);
|
||||
console.log(`Message from monitor ${id}: ${msg.type}`);
|
||||
store.dispatch(msg);
|
||||
};
|
||||
port.onMessage.addListener(listener);
|
||||
port.onDisconnect.addListener(disconnect('panel', id, listener));
|
||||
|
||||
const { current } = store.getState().instances;
|
||||
if (current !== 'default') {
|
||||
const connectionId = Object.entries(
|
||||
store.getState().instances.connections,
|
||||
).find(([, instanceIds]) => instanceIds.includes(current))?.[0];
|
||||
const options = store.getState().instances.options[current];
|
||||
const state = store.getState().instances.states[current];
|
||||
const { actionsById, computedStates, committedState, ...rest } = state;
|
||||
toMonitors({
|
||||
type: UPDATE_STATE,
|
||||
request: {
|
||||
type: 'STATE',
|
||||
payload: rest as Omit<
|
||||
LiftedState<S, A, unknown>,
|
||||
'actionsById' | 'computedStates' | 'committedState'
|
||||
>,
|
||||
source: '@devtools-page',
|
||||
instanceId:
|
||||
typeof current === 'number' ? current.toString() : current,
|
||||
actionsById: stringifyJSON(actionsById, options.serialize),
|
||||
computedStates: stringifyJSON(computedStates, options.serialize),
|
||||
committedState: typeof committedState !== 'undefined',
|
||||
},
|
||||
id: connectionId ?? current,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -581,21 +629,14 @@ chrome.runtime.onMessageExternal.addListener(messaging);
|
|||
|
||||
chrome.notifications.onClicked.addListener((id) => {
|
||||
chrome.notifications.clear(id);
|
||||
openDevToolsWindow('devtools-right');
|
||||
openDevToolsWindow('devtools-window');
|
||||
});
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
syncOptions: SyncOptions;
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
||||
const api: Middleware<{}, BackgroundState, Dispatch<BackgroundAction>> =
|
||||
(store) => (next) => (untypedAction) => {
|
||||
const action = untypedAction as BackgroundAction;
|
||||
|
||||
window.syncOptions = syncOptions(toAllTabs); // Expose to the options page
|
||||
|
||||
export default function api(
|
||||
store: MiddlewareAPI<Dispatch<BackgroundAction>, BackgroundState>
|
||||
) {
|
||||
return (next: Dispatch<BackgroundAction>) => (action: BackgroundAction) => {
|
||||
if (action.type === LIFTED_ACTION) toContentScript(action);
|
||||
else if (action.type === TOGGLE_PERSIST) {
|
||||
togglePersist();
|
||||
|
@ -606,4 +647,5 @@ export default function api(
|
|||
}
|
||||
return next(action);
|
||||
};
|
||||
}
|
||||
|
||||
export default api;
|
|
@ -1,14 +1,17 @@
|
|||
import { combineReducers, Reducer } from 'redux';
|
||||
import { instances, InstancesState } from '@redux-devtools/app';
|
||||
import { BackgroundAction } from '../../stores/backgroundStore';
|
||||
import { BackgroundAction } from './backgroundStore';
|
||||
|
||||
export interface BackgroundState {
|
||||
readonly instances: InstancesState;
|
||||
}
|
||||
|
||||
const rootReducer: Reducer<BackgroundState, BackgroundAction> =
|
||||
combineReducers<BackgroundState>({
|
||||
instances,
|
||||
});
|
||||
const rootReducer: Reducer<
|
||||
BackgroundState,
|
||||
BackgroundAction,
|
||||
Partial<BackgroundState>
|
||||
> = combineReducers({
|
||||
instances,
|
||||
}) as any;
|
||||
|
||||
export default rootReducer;
|
|
@ -1,12 +1,12 @@
|
|||
import { createStore, applyMiddleware, PreloadedState } from 'redux';
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
import {
|
||||
CustomAction,
|
||||
DispatchAction,
|
||||
LIFTED_ACTION,
|
||||
StoreActionWithoutLiftedAction,
|
||||
} from '@redux-devtools/app';
|
||||
import rootReducer, { BackgroundState } from '../reducers/background';
|
||||
import api, { CONNECTED, DISCONNECTED } from '../middlewares/api';
|
||||
import rootReducer, { BackgroundState } from './backgroundReducer';
|
||||
import api, { CONNECTED, DISCONNECTED } from './apiMiddleware';
|
||||
|
||||
interface LiftedActionActionBase {
|
||||
action?: DispatchAction | string | CustomAction;
|
||||
|
@ -60,7 +60,7 @@ export type BackgroundAction =
|
|||
| DisconnectedAction;
|
||||
|
||||
export default function configureStore(
|
||||
preloadedState?: PreloadedState<BackgroundState>
|
||||
preloadedState?: Partial<BackgroundState>,
|
||||
) {
|
||||
return createStore(rootReducer, preloadedState, applyMiddleware(api));
|
||||
/*
|
|
@ -1,83 +0,0 @@
|
|||
export type DevToolsPosition =
|
||||
| 'devtools-left'
|
||||
| 'devtools-right'
|
||||
| 'devtools-bottom'
|
||||
| 'devtools-panel'
|
||||
| 'devtools-remote';
|
||||
|
||||
let windows: { [K in DevToolsPosition]?: number } = {};
|
||||
let lastPosition: DevToolsPosition | null = null;
|
||||
|
||||
export default function openDevToolsWindow(position: DevToolsPosition) {
|
||||
function popWindow(
|
||||
action: string,
|
||||
url: string,
|
||||
customOptions: chrome.windows.CreateData & chrome.windows.UpdateInfo
|
||||
) {
|
||||
function focusIfExist(callback: () => void) {
|
||||
if (!windows[position]) {
|
||||
callback();
|
||||
lastPosition = position;
|
||||
} else {
|
||||
let params = { focused: true };
|
||||
if (lastPosition !== position && position !== 'devtools-panel') {
|
||||
params = { ...params, ...customOptions };
|
||||
}
|
||||
chrome.windows.update(windows[position]!, params, () => {
|
||||
lastPosition = null;
|
||||
if (chrome.runtime.lastError) callback();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
focusIfExist(() => {
|
||||
let options: chrome.windows.CreateData = {
|
||||
type: 'popup',
|
||||
...customOptions,
|
||||
};
|
||||
if (action === 'open') {
|
||||
options.url = chrome.extension.getURL(
|
||||
url + '#' + position.substr(position.indexOf('-') + 1)
|
||||
);
|
||||
chrome.windows.create(options, (win) => {
|
||||
windows[position] = win!.id;
|
||||
if (navigator.userAgent.indexOf('Firefox') !== -1) {
|
||||
chrome.windows.update(win!.id!, {
|
||||
focused: true,
|
||||
...customOptions,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let params: chrome.windows.CreateData & chrome.windows.UpdateInfo = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: 380,
|
||||
height: window.screen.availHeight,
|
||||
};
|
||||
let url = 'window.html';
|
||||
switch (position) {
|
||||
case 'devtools-right':
|
||||
params.left =
|
||||
(window.screen as unknown as { availLeft: number }).availLeft +
|
||||
window.screen.availWidth -
|
||||
params.width!;
|
||||
break;
|
||||
case 'devtools-bottom':
|
||||
params.height = 420;
|
||||
params.top = window.screen.height - params.height;
|
||||
params.width = window.screen.availWidth;
|
||||
break;
|
||||
case 'devtools-panel':
|
||||
params.type = 'panel';
|
||||
break;
|
||||
case 'devtools-remote':
|
||||
params = { width: 850, height: 600 };
|
||||
url = 'remote.html';
|
||||
break;
|
||||
}
|
||||
popWindow('open', url, params);
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
// Mock not supported chrome.* API for Firefox and Electron
|
||||
|
||||
window.isElectron =
|
||||
window.navigator && window.navigator.userAgent.indexOf('Electron') !== -1;
|
||||
|
||||
const isFirefox = navigator.userAgent.indexOf('Firefox') !== -1;
|
||||
|
||||
// Background page only
|
||||
if (
|
||||
(window.isElectron &&
|
||||
location.pathname === '/_generated_background_page.html') ||
|
||||
isFirefox
|
||||
) {
|
||||
(chrome.runtime as any).onConnectExternal = {
|
||||
addListener() {},
|
||||
};
|
||||
(chrome.runtime as any).onMessageExternal = {
|
||||
addListener() {},
|
||||
};
|
||||
|
||||
if (window.isElectron) {
|
||||
(chrome.notifications as any) = {
|
||||
onClicked: {
|
||||
addListener() {},
|
||||
},
|
||||
create() {},
|
||||
clear() {},
|
||||
};
|
||||
(chrome.contextMenus as any) = {
|
||||
onClicked: {
|
||||
addListener() {},
|
||||
},
|
||||
};
|
||||
} else {
|
||||
(chrome.storage as any).sync = chrome.storage.local;
|
||||
(chrome.runtime as any).onInstalled = {
|
||||
addListener: (cb: any) => cb(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (window.isElectron) {
|
||||
if (!chrome.storage.local || !chrome.storage.local.remove) {
|
||||
(chrome.storage as any).local = {
|
||||
set(obj: any, callback: any) {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
localStorage.setItem(key, obj[key]);
|
||||
});
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
get(obj: any, callback: any) {
|
||||
const result: any = {};
|
||||
Object.keys(obj).forEach((key) => {
|
||||
result[key] = localStorage.getItem(key) || obj[key];
|
||||
});
|
||||
if (callback) {
|
||||
callback(result);
|
||||
}
|
||||
},
|
||||
// Electron ~ 1.4.6
|
||||
remove(items: any, callback: any) {
|
||||
if (Array.isArray(items)) {
|
||||
items.forEach((name) => {
|
||||
localStorage.removeItem(name);
|
||||
});
|
||||
} else {
|
||||
localStorage.removeItem(items);
|
||||
}
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
// Avoid error: chrome.runtime.sendMessage is not supported responseCallback
|
||||
const originSendMessage = (chrome.runtime as any).sendMessage;
|
||||
chrome.runtime.sendMessage = function () {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return originSendMessage(...arguments);
|
||||
}
|
||||
if (typeof arguments[arguments.length - 1] === 'function') {
|
||||
Array.prototype.pop.call(arguments);
|
||||
}
|
||||
return originSendMessage(...arguments);
|
||||
};
|
||||
}
|
||||
|
||||
if (isFirefox || window.isElectron) {
|
||||
(chrome.storage as any).sync = chrome.storage.local;
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
import React, { CSSProperties } from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Persistor } from 'redux-persist';
|
||||
import { REMOVE_INSTANCE, StoreAction } from '@redux-devtools/app';
|
||||
import App from '../../../app/containers/App';
|
||||
import configureStore from '../../../app/stores/panelStore';
|
||||
|
||||
import '../../views/devpanel.pug';
|
||||
import { Action, Store } from 'redux';
|
||||
import { PanelMessage } from '../../../app/middlewares/api';
|
||||
import { StoreStateWithoutSocket } from '../../../app/reducers/panel';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
|
||||
const position = location.hash;
|
||||
const messageStyle: CSSProperties = {
|
||||
padding: '20px',
|
||||
width: '100%',
|
||||
textAlign: 'center',
|
||||
};
|
||||
|
||||
let rendered: boolean | undefined;
|
||||
let store: Store<StoreStateWithoutSocket, StoreAction> | undefined;
|
||||
let persistor: Persistor | undefined;
|
||||
let bgConnection: chrome.runtime.Port;
|
||||
let naTimeout: NodeJS.Timeout;
|
||||
|
||||
const isChrome = navigator.userAgent.indexOf('Firefox') === -1;
|
||||
|
||||
function renderDevTools() {
|
||||
const node = document.getElementById('root');
|
||||
unmountComponentAtNode(node!);
|
||||
clearTimeout(naTimeout);
|
||||
({ store, persistor } = configureStore(position, bgConnection));
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<App position={position} />
|
||||
</PersistGate>
|
||||
</Provider>,
|
||||
node
|
||||
);
|
||||
rendered = true;
|
||||
}
|
||||
|
||||
function renderNA() {
|
||||
if (rendered === false) return;
|
||||
rendered = false;
|
||||
naTimeout = setTimeout(() => {
|
||||
let message = (
|
||||
<div style={messageStyle}>
|
||||
No store found. Make sure to follow{' '}
|
||||
<a
|
||||
href="https://github.com/zalmoxisus/redux-devtools-extension#usage"
|
||||
target="_blank"
|
||||
>
|
||||
the instructions
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
);
|
||||
if (isChrome) {
|
||||
chrome.devtools.inspectedWindow.getResources((resources) => {
|
||||
if (resources[0].url.substr(0, 4) === 'file') {
|
||||
message = (
|
||||
<div style={messageStyle}>
|
||||
No store found. Most likely you did not allow access to file URLs.{' '}
|
||||
<a
|
||||
href="https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/Troubleshooting.md#access-file-url-file"
|
||||
target="_blank"
|
||||
>
|
||||
See details
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const node = document.getElementById('root');
|
||||
unmountComponentAtNode(node!);
|
||||
render(message, node);
|
||||
store = undefined;
|
||||
});
|
||||
} else {
|
||||
const node = document.getElementById('root');
|
||||
unmountComponentAtNode(node!);
|
||||
render(message, node);
|
||||
store = undefined;
|
||||
}
|
||||
}, 3500);
|
||||
}
|
||||
|
||||
function init(id: number) {
|
||||
renderNA();
|
||||
bgConnection = chrome.runtime.connect({
|
||||
name: id ? id.toString() : undefined,
|
||||
});
|
||||
bgConnection.onMessage.addListener(
|
||||
<S, A extends Action<unknown>>(message: PanelMessage<S, A>) => {
|
||||
if (message.type === 'NA') {
|
||||
if (message.id === id) renderNA();
|
||||
else store!.dispatch({ type: REMOVE_INSTANCE, id: message.id });
|
||||
} else {
|
||||
if (!rendered) renderDevTools();
|
||||
store!.dispatch(message);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
init(chrome.devtools.inspectedWindow.tabId);
|
|
@ -1,19 +0,0 @@
|
|||
import '../../views/devtools.pug';
|
||||
|
||||
function createPanel(url: string) {
|
||||
chrome.devtools.panels.create(
|
||||
'Redux',
|
||||
'img/logo/scalable.png',
|
||||
url,
|
||||
function () {}
|
||||
);
|
||||
}
|
||||
|
||||
if (chrome.runtime.getBackgroundPage) {
|
||||
// Check if the background page's object is accessible (not in incognito)
|
||||
chrome.runtime.getBackgroundPage((background) => {
|
||||
createPanel(background ? 'window.html' : 'devpanel.html');
|
||||
});
|
||||
} else {
|
||||
createPanel('devpanel.html');
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
let s = document.createElement('script');
|
||||
s.type = 'text/javascript';
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
const { default: script } = require('raw-loader!tmp/page.bundle.js');
|
||||
s.appendChild(document.createTextNode(script));
|
||||
(document.head || document.documentElement).appendChild(s);
|
||||
s.parentNode!.removeChild(s);
|
||||
} else {
|
||||
s.src = chrome.extension.getURL('page.bundle.js');
|
||||
s.onload = function () {
|
||||
(this as HTMLScriptElement).parentNode!.removeChild(
|
||||
this as HTMLScriptElement
|
||||
);
|
||||
};
|
||||
(document.head || document.documentElement).appendChild(s);
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
import React from 'react';
|
||||
import EditorGroup from './EditorGroup';
|
||||
import FilterGroup from './FilterGroup';
|
||||
import AllowToRunGroup from './AllowToRunGroup';
|
||||
import MiscellaneousGroup from './MiscellaneousGroup';
|
||||
import ContextMenuGroup from './ContextMenuGroup';
|
||||
import { Options } from './syncOptions';
|
||||
|
||||
export interface OptionsProps {
|
||||
readonly options: Options;
|
||||
readonly saveOption: <K extends keyof Options>(
|
||||
name: K,
|
||||
value: Options[K]
|
||||
) => void;
|
||||
}
|
||||
|
||||
export default (props: OptionsProps) => (
|
||||
<div>
|
||||
<EditorGroup {...props} />
|
||||
<FilterGroup {...props} />
|
||||
<AllowToRunGroup {...props} />
|
||||
<MiscellaneousGroup {...props} />
|
||||
<ContextMenuGroup {...props} />
|
||||
<div style={{ color: 'red' }}>
|
||||
<br />
|
||||
<hr />
|
||||
Setting options here is discouraged, and will not be possible in the next
|
||||
major release. Please{' '}
|
||||
<a
|
||||
href="https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md"
|
||||
target="_blank"
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
specify them as parameters
|
||||
</a>
|
||||
. See{' '}
|
||||
<a
|
||||
href="https://github.com/zalmoxisus/redux-devtools-extension/issues/296"
|
||||
target="_blank"
|
||||
style={{ color: 'red' }}
|
||||
>
|
||||
the issue
|
||||
</a>{' '}
|
||||
for more details.
|
||||
</div>
|
||||
</div>
|
||||
);
|
|
@ -1,26 +0,0 @@
|
|||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import OptionsComponent from './Options';
|
||||
import { Options } from './syncOptions';
|
||||
|
||||
import '../../views/options.pug';
|
||||
|
||||
chrome.runtime.getBackgroundPage((background) => {
|
||||
const syncOptions = background!.syncOptions;
|
||||
|
||||
const saveOption = <K extends keyof Options>(name: K, value: Options[K]) => {
|
||||
syncOptions.save(name, value);
|
||||
};
|
||||
|
||||
const renderOptions = (options: Options) => {
|
||||
render(
|
||||
<OptionsComponent options={options} saveOption={saveOption} />,
|
||||
document.getElementById('root')
|
||||
);
|
||||
};
|
||||
|
||||
syncOptions.subscribe(renderOptions);
|
||||
syncOptions.get((options) => {
|
||||
renderOptions(options);
|
||||
});
|
||||
});
|
|
@ -1,38 +0,0 @@
|
|||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
import { UPDATE_STATE } from '@redux-devtools/app';
|
||||
import App from '../../../app/containers/App';
|
||||
import configureStore from '../../../app/stores/windowStore';
|
||||
import { MonitorMessage } from '../../../app/middlewares/api';
|
||||
|
||||
import '../../views/window.pug';
|
||||
|
||||
const position = location.hash;
|
||||
|
||||
chrome.runtime.getBackgroundPage((window) => {
|
||||
const { store } = window!;
|
||||
const { store: localStore, persistor } = configureStore(store, position);
|
||||
let name = 'monitor';
|
||||
if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) {
|
||||
name += chrome.devtools.inspectedWindow.tabId;
|
||||
}
|
||||
const bg = chrome.runtime.connect({ name });
|
||||
const update = (action?: MonitorMessage) => {
|
||||
localStore.dispatch(action || { type: UPDATE_STATE });
|
||||
};
|
||||
bg.onMessage.addListener(update);
|
||||
update();
|
||||
|
||||
render(
|
||||
<Provider store={localStore}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<App position={position} />
|
||||
</PersistGate>
|
||||
</Provider>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
});
|
||||
|
||||
if (position !== '#popup') document.body.style.minHeight = '100%';
|
|
@ -1,15 +0,0 @@
|
|||
doctype html
|
||||
|
||||
html
|
||||
head
|
||||
meta(charset='UTF-8')
|
||||
title Redux DevTools
|
||||
include ./includes/style.pug
|
||||
style.
|
||||
body {
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
body
|
||||
#root
|
||||
script(src='/devpanel.bundle.js')
|
|
@ -1,17 +0,0 @@
|
|||
doctype html
|
||||
|
||||
html
|
||||
head
|
||||
meta(charset='UTF-8')
|
||||
title Redux DevTools
|
||||
include ./includes/style.pug
|
||||
|
||||
body
|
||||
#root
|
||||
div(style='position: relative')
|
||||
img(
|
||||
src='/img/loading.svg',
|
||||
height=300, width=350,
|
||||
style='position: absolute; top: 50%; left: 50%; margin-top: -175px; margin-left: -175px;'
|
||||
)
|
||||
script(src='/window.bundle.js')
|
113
extension/src/chromeApiMock.ts
Normal file
113
extension/src/chromeApiMock.ts
Normal file
|
@ -0,0 +1,113 @@
|
|||
// Mock not supported chrome.* API for Firefox and Electron
|
||||
|
||||
const isElectron = navigator.userAgent.includes('Electron');
|
||||
const isFirefox = navigator.userAgent.includes('Firefox');
|
||||
|
||||
// Background page only
|
||||
if (
|
||||
(isElectron && location.pathname === '/background.bundle.js') ||
|
||||
isFirefox
|
||||
) {
|
||||
(chrome.runtime as any).onConnectExternal = {
|
||||
addListener() {
|
||||
// do nothing.
|
||||
},
|
||||
};
|
||||
(chrome.runtime as any).onMessageExternal = {
|
||||
addListener() {
|
||||
// do nothing.
|
||||
},
|
||||
};
|
||||
|
||||
if (isElectron) {
|
||||
(chrome.notifications as any) = {
|
||||
onClicked: {
|
||||
addListener() {
|
||||
// do nothing.
|
||||
},
|
||||
},
|
||||
create() {
|
||||
// do nothing.
|
||||
},
|
||||
clear() {
|
||||
// do nothing.
|
||||
},
|
||||
};
|
||||
(chrome.contextMenus as any) = {
|
||||
onClicked: {
|
||||
addListener() {
|
||||
// do nothing.
|
||||
},
|
||||
},
|
||||
};
|
||||
(chrome.commands as any) = {
|
||||
onCommand: {
|
||||
addListener() {
|
||||
// do nothing.
|
||||
},
|
||||
},
|
||||
};
|
||||
} else {
|
||||
(chrome.storage as any).sync = chrome.storage.local;
|
||||
(chrome.runtime as any).onInstalled = {
|
||||
addListener: (cb: any) => cb(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (isElectron) {
|
||||
if (!chrome.storage.local || !chrome.storage.local.remove) {
|
||||
(chrome.storage as any).local = {
|
||||
set(items: { [key: string]: string }, callback: () => void) {
|
||||
for (const [key, value] of Object.entries(items)) {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
get(
|
||||
keys: { [key: string]: any },
|
||||
callback: (items: { [key: string]: any }) => void,
|
||||
) {
|
||||
const result = Object.fromEntries(
|
||||
Object.entries(keys).map(([key, value]) => [
|
||||
key,
|
||||
localStorage.getItem(key) ?? value,
|
||||
]),
|
||||
);
|
||||
if (callback) {
|
||||
callback(result);
|
||||
}
|
||||
},
|
||||
// Electron ~ 1.4.6
|
||||
remove(keys: string | string[], callback: () => void) {
|
||||
if (Array.isArray(keys)) {
|
||||
for (const key of keys) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
} else {
|
||||
localStorage.removeItem(keys);
|
||||
}
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
// Avoid error: chrome.runtime.sendMessage is not supported responseCallback
|
||||
const originSendMessage = (chrome.runtime as any).sendMessage;
|
||||
(chrome.runtime as any).sendMessage = function (...args: unknown[]) {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
return originSendMessage(...args);
|
||||
}
|
||||
if (typeof args[arguments.length - 1] === 'function') {
|
||||
Array.prototype.pop.call(args);
|
||||
}
|
||||
return originSendMessage(...args);
|
||||
};
|
||||
}
|
||||
|
||||
if (isFirefox || isElectron) {
|
||||
(chrome.storage as any).sync = chrome.storage.local;
|
||||
}
|
|
@ -1,20 +1,24 @@
|
|||
import '../chromeApiMock';
|
||||
import {
|
||||
injectOptions,
|
||||
getOptionsFromBg,
|
||||
getOptions,
|
||||
isAllowed,
|
||||
Options,
|
||||
prefetchOptions,
|
||||
prepareOptionsForPage,
|
||||
} from '../options/syncOptions';
|
||||
import { TabMessage } from '../../../app/middlewares/api';
|
||||
import {
|
||||
import type { TabMessage } from '../background/store/apiMiddleware';
|
||||
import type {
|
||||
PageScriptToContentScriptMessage,
|
||||
PageScriptToContentScriptMessageWithoutDisconnect,
|
||||
PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance,
|
||||
} from '../../../app/api';
|
||||
} from '../pageScript/api';
|
||||
import { Action } from 'redux';
|
||||
import {
|
||||
CustomAction,
|
||||
DispatchAction as AppDispatchAction,
|
||||
} from '@redux-devtools/app';
|
||||
import { LiftedState } from '@redux-devtools/instrument';
|
||||
|
||||
const source = '@devtools-extension';
|
||||
const pageSource = '@devtools-page';
|
||||
// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts
|
||||
|
@ -82,6 +86,13 @@ interface UpdateAction {
|
|||
readonly source: typeof source;
|
||||
}
|
||||
|
||||
interface OptionsAction {
|
||||
readonly type: 'OPTIONS';
|
||||
readonly options: Options;
|
||||
readonly id: undefined;
|
||||
readonly source: typeof source;
|
||||
}
|
||||
|
||||
export type ContentScriptToPageScriptMessage =
|
||||
| StartAction
|
||||
| StopAction
|
||||
|
@ -89,20 +100,21 @@ export type ContentScriptToPageScriptMessage =
|
|||
| ImportAction
|
||||
| ActionAction
|
||||
| ExportAction
|
||||
| UpdateAction;
|
||||
| UpdateAction
|
||||
| OptionsAction;
|
||||
|
||||
interface ImportStatePayload<S, A extends Action<unknown>> {
|
||||
interface ImportStatePayload<S, A extends Action<string>> {
|
||||
readonly type: 'IMPORT_STATE';
|
||||
readonly nextLiftedState: LiftedState<S, A, unknown> | readonly A[];
|
||||
readonly preloadedState?: S;
|
||||
}
|
||||
|
||||
interface ImportStateDispatchAction<S, A extends Action<unknown>> {
|
||||
interface ImportStateDispatchAction<S, A extends Action<string>> {
|
||||
readonly type: 'DISPATCH';
|
||||
readonly payload: ImportStatePayload<S, A>;
|
||||
}
|
||||
|
||||
export type ListenerMessage<S, A extends Action<unknown>> =
|
||||
export type ListenerMessage<S, A extends Action<string>> =
|
||||
| StartAction
|
||||
| StopAction
|
||||
| DispatchAction
|
||||
|
@ -110,6 +122,7 @@ export type ListenerMessage<S, A extends Action<unknown>> =
|
|||
| ActionAction
|
||||
| ExportAction
|
||||
| UpdateAction
|
||||
| OptionsAction
|
||||
| ImportStateDispatchAction<S, A>;
|
||||
|
||||
function postToPageScript(message: ContentScriptToPageScriptMessage) {
|
||||
|
@ -154,8 +167,13 @@ function connect() {
|
|||
source,
|
||||
});
|
||||
}
|
||||
} else if ('options' in message) {
|
||||
injectOptions(message.options);
|
||||
} else if (message.type === 'OPTIONS') {
|
||||
postToPageScript({
|
||||
type: message.type,
|
||||
options: prepareOptionsForPage(message.options),
|
||||
id: undefined,
|
||||
source,
|
||||
});
|
||||
} else {
|
||||
postToPageScript({
|
||||
type: message.type,
|
||||
|
@ -203,11 +221,13 @@ export type SplitMessage =
|
|||
| SplitMessageChunk
|
||||
| SplitMessageEnd;
|
||||
|
||||
function tryCatch<S, A extends Action<unknown>>(
|
||||
function tryCatch<S, A extends Action<string>>(
|
||||
fn: (
|
||||
args: PageScriptToContentScriptMessageWithoutDisconnect<S, A> | SplitMessage
|
||||
args:
|
||||
| PageScriptToContentScriptMessageWithoutDisconnect<S, A>
|
||||
| SplitMessage,
|
||||
) => void,
|
||||
args: PageScriptToContentScriptMessageWithoutDisconnect<S, A>
|
||||
args: PageScriptToContentScriptMessageWithoutDisconnect<S, A>,
|
||||
) {
|
||||
try {
|
||||
return fn(args);
|
||||
|
@ -234,7 +254,7 @@ function tryCatch<S, A extends Action<unknown>>(
|
|||
}
|
||||
newArgs[key as keyof typeof newArgs] = arg;
|
||||
});
|
||||
fn(newArgs as any);
|
||||
fn(newArgs as SplitMessage);
|
||||
for (let i = 0; i < toSplit.length; i++) {
|
||||
for (let j = 0; j < toSplit[i][1].length; j += maxChromeMsgSize) {
|
||||
fn({
|
||||
|
@ -261,31 +281,38 @@ interface InitInstanceContentScriptToBackgroundMessage {
|
|||
readonly instanceId: number;
|
||||
}
|
||||
|
||||
interface RelayMessage<S, A extends Action<unknown>> {
|
||||
interface RelayMessage<S, A extends Action<string>> {
|
||||
readonly name: 'RELAY';
|
||||
readonly message:
|
||||
| PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<S, A>
|
||||
| SplitMessage;
|
||||
}
|
||||
|
||||
export type ContentScriptToBackgroundMessage<S, A extends Action<unknown>> =
|
||||
export type ContentScriptToBackgroundMessage<S, A extends Action<string>> =
|
||||
| InitInstanceContentScriptToBackgroundMessage
|
||||
| RelayMessage<S, A>;
|
||||
|
||||
function postToBackground<S, A extends Action<unknown>>(
|
||||
message: ContentScriptToBackgroundMessage<S, A>
|
||||
function postToBackground<S, A extends Action<string>>(
|
||||
message: ContentScriptToBackgroundMessage<S, A>,
|
||||
) {
|
||||
bg!.postMessage(message);
|
||||
}
|
||||
|
||||
function send<S, A extends Action<unknown>>(
|
||||
function send<S, A extends Action<string>>(
|
||||
message:
|
||||
| PageScriptToContentScriptMessageWithoutDisconnect<S, A>
|
||||
| SplitMessage
|
||||
| SplitMessage,
|
||||
) {
|
||||
if (!connected) connect();
|
||||
if (message.type === 'INIT_INSTANCE') {
|
||||
getOptionsFromBg();
|
||||
getOptions((options) => {
|
||||
postToPageScript({
|
||||
type: 'OPTIONS',
|
||||
options: prepareOptionsForPage(options),
|
||||
id: undefined,
|
||||
source,
|
||||
});
|
||||
});
|
||||
postToBackground({ name: 'INIT_INSTANCE', instanceId: message.instanceId });
|
||||
} else {
|
||||
postToBackground({ name: 'RELAY', message });
|
||||
|
@ -293,8 +320,8 @@ function send<S, A extends Action<unknown>>(
|
|||
}
|
||||
|
||||
// Resend messages from the page to the background script
|
||||
function handleMessages<S, A extends Action<unknown>>(
|
||||
event: MessageEvent<PageScriptToContentScriptMessage<S, A>>
|
||||
function handleMessages<S, A extends Action<string>>(
|
||||
event: MessageEvent<PageScriptToContentScriptMessage<S, A>>,
|
||||
) {
|
||||
if (!isAllowed()) return;
|
||||
if (!event || event.source !== window || typeof event.data !== 'object') {
|
||||
|
@ -313,4 +340,6 @@ function handleMessages<S, A extends Action<unknown>>(
|
|||
tryCatch(send, message);
|
||||
}
|
||||
|
||||
prefetchOptions();
|
||||
|
||||
window.addEventListener('message', handleMessages, false);
|
17
extension/src/devpanel/devpanel.pug
Normal file
17
extension/src/devpanel/devpanel.pug
Normal file
|
@ -0,0 +1,17 @@
|
|||
doctype html
|
||||
|
||||
html
|
||||
head
|
||||
meta(charset='UTF-8')
|
||||
title Redux DevTools
|
||||
include ../style.pug
|
||||
|
||||
body
|
||||
#root
|
||||
div(style='display: flex; justify-content: center; align-items: center')
|
||||
img(
|
||||
src='/img/loading.svg',
|
||||
height=300, width=350,
|
||||
)
|
||||
link(href='/devpanel.bundle.css', rel='stylesheet')
|
||||
script(src='/devpanel.bundle.js')
|
182
extension/src/devpanel/index.tsx
Normal file
182
extension/src/devpanel/index.tsx
Normal file
|
@ -0,0 +1,182 @@
|
|||
import '../chromeApiMock';
|
||||
import React, { CSSProperties, ReactNode } from 'react';
|
||||
import { createRoot, Root } from 'react-dom/client';
|
||||
import { Provider } from 'react-redux';
|
||||
import { Persistor } from 'redux-persist';
|
||||
import {
|
||||
REMOVE_INSTANCE,
|
||||
StoreAction,
|
||||
StoreState,
|
||||
UPDATE_STATE,
|
||||
} from '@redux-devtools/app';
|
||||
import App from '../app/App';
|
||||
import configureStore from './store/panelStore';
|
||||
|
||||
import { Action, Store } from 'redux';
|
||||
import {
|
||||
PanelMessageWithoutNA,
|
||||
PanelMessageWithSplitAction,
|
||||
SplitUpdateStateRequest,
|
||||
UpdateStateRequest,
|
||||
} from '../background/store/apiMiddleware';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
|
||||
const position = location.hash;
|
||||
const messageStyle: CSSProperties = {
|
||||
paddingTop: '20px',
|
||||
width: '100%',
|
||||
textAlign: 'center',
|
||||
boxSizing: 'border-box',
|
||||
};
|
||||
|
||||
let rendered: boolean | undefined;
|
||||
let currentRoot: Root | undefined;
|
||||
let store: Store<StoreState, StoreAction> | undefined;
|
||||
let persistor: Persistor | undefined;
|
||||
let bgConnection: chrome.runtime.Port;
|
||||
let naTimeout: NodeJS.Timeout;
|
||||
|
||||
const isChrome = !navigator.userAgent.includes('Firefox');
|
||||
|
||||
function renderNodeAtRoot(node: ReactNode) {
|
||||
if (currentRoot) currentRoot.unmount();
|
||||
currentRoot = createRoot(document.getElementById('root')!);
|
||||
currentRoot.render(node);
|
||||
}
|
||||
|
||||
function renderDevTools() {
|
||||
clearTimeout(naTimeout);
|
||||
({ store, persistor } = configureStore(position, bgConnection));
|
||||
renderNodeAtRoot(
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<App position={position} />
|
||||
</PersistGate>
|
||||
</Provider>,
|
||||
);
|
||||
rendered = true;
|
||||
}
|
||||
|
||||
function renderNA() {
|
||||
if (rendered === false) return;
|
||||
rendered = false;
|
||||
naTimeout = setTimeout(() => {
|
||||
let message = (
|
||||
<div style={messageStyle}>
|
||||
No store found. Make sure to follow{' '}
|
||||
<a
|
||||
href="https://github.com/zalmoxisus/redux-devtools-extension#usage"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
the instructions
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
);
|
||||
if (
|
||||
isChrome &&
|
||||
chrome &&
|
||||
chrome.devtools &&
|
||||
chrome.devtools.inspectedWindow
|
||||
) {
|
||||
chrome.devtools.inspectedWindow.getResources((resources) => {
|
||||
if (resources[0].url.substr(0, 4) === 'file') {
|
||||
message = (
|
||||
<div style={messageStyle}>
|
||||
No store found. Most likely you did not allow access to file URLs.{' '}
|
||||
<a
|
||||
href="https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/Troubleshooting.md#access-file-url-file"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
See details
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderNodeAtRoot(message);
|
||||
store = undefined;
|
||||
});
|
||||
} else {
|
||||
renderNodeAtRoot(message);
|
||||
store = undefined;
|
||||
}
|
||||
}, 3500);
|
||||
}
|
||||
|
||||
let splitMessage: SplitUpdateStateRequest<unknown, Action<string>>;
|
||||
|
||||
function init() {
|
||||
renderNA();
|
||||
|
||||
let name = 'monitor';
|
||||
if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) {
|
||||
name += chrome.devtools.inspectedWindow.tabId;
|
||||
}
|
||||
bgConnection = chrome.runtime.connect({ name });
|
||||
|
||||
bgConnection.onMessage.addListener(
|
||||
<S, A extends Action<string>>(
|
||||
message: PanelMessageWithSplitAction<S, A>,
|
||||
) => {
|
||||
if (message.type === 'NA') {
|
||||
// TODO Double-check this now that the name is different
|
||||
if (message.id === name) renderNA();
|
||||
else store!.dispatch({ type: REMOVE_INSTANCE, id: message.id });
|
||||
} else {
|
||||
if (!rendered) renderDevTools();
|
||||
|
||||
if (
|
||||
message.type === UPDATE_STATE &&
|
||||
(message.request as SplitUpdateStateRequest<S, A>).split
|
||||
) {
|
||||
const request = message.request as SplitUpdateStateRequest<S, A>;
|
||||
|
||||
if (request.split === 'start') {
|
||||
splitMessage = request;
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.split === 'chunk') {
|
||||
if (
|
||||
(splitMessage as unknown as Record<string, string>)[
|
||||
request.chunk[0]
|
||||
]
|
||||
) {
|
||||
(splitMessage as unknown as Record<string, string>)[
|
||||
request.chunk[0]
|
||||
] += request.chunk[1];
|
||||
} else {
|
||||
(splitMessage as unknown as Record<string, string>)[
|
||||
request.chunk[0]
|
||||
] = request.chunk[1];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.split === 'end') {
|
||||
store!.dispatch({
|
||||
...message,
|
||||
request: splitMessage as UpdateStateRequest<S, A>,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`Unable to process split message with type: ${(request as any).split}`,
|
||||
);
|
||||
} else {
|
||||
store!.dispatch(message as PanelMessageWithoutNA<S, A>);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (position === '#popup') document.body.style.minWidth = '760px';
|
||||
if (position !== '#popup') document.body.style.minHeight = '100%';
|
||||
|
||||
init();
|
32
extension/src/devpanel/store/panelReducer.ts
Normal file
32
extension/src/devpanel/store/panelReducer.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { combineReducers, Reducer } from 'redux';
|
||||
import {
|
||||
connection,
|
||||
instances,
|
||||
monitor,
|
||||
notification,
|
||||
reports,
|
||||
section,
|
||||
socket,
|
||||
stateTreeSettings,
|
||||
StoreAction,
|
||||
StoreState,
|
||||
theme,
|
||||
} from '@redux-devtools/app';
|
||||
|
||||
const rootReducer: Reducer<
|
||||
StoreState,
|
||||
StoreAction,
|
||||
Partial<StoreState>
|
||||
> = combineReducers({
|
||||
instances,
|
||||
monitor,
|
||||
reports,
|
||||
notification,
|
||||
section,
|
||||
socket,
|
||||
theme,
|
||||
connection,
|
||||
stateTreeSettings,
|
||||
}) as any;
|
||||
|
||||
export default rootReducer;
|
34
extension/src/devpanel/store/panelStore.ts
Normal file
34
extension/src/devpanel/store/panelStore.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { createStore, applyMiddleware, Reducer, Store } from 'redux';
|
||||
import localForage from 'localforage';
|
||||
import { persistReducer, persistStore } from 'redux-persist';
|
||||
import {
|
||||
exportStateMiddleware,
|
||||
StoreAction,
|
||||
StoreState,
|
||||
} from '@redux-devtools/app';
|
||||
import panelDispatcher from './panelSyncMiddleware';
|
||||
import rootReducer from './panelReducer';
|
||||
|
||||
const persistConfig = {
|
||||
key: 'redux-devtools',
|
||||
blacklist: ['instances', 'socket'],
|
||||
storage: localForage,
|
||||
};
|
||||
|
||||
const persistedReducer: Reducer<StoreState, StoreAction> = persistReducer(
|
||||
persistConfig,
|
||||
rootReducer,
|
||||
) as any;
|
||||
|
||||
export default function configureStore(
|
||||
position: string,
|
||||
bgConnection: chrome.runtime.Port,
|
||||
) {
|
||||
const enhancer = applyMiddleware(
|
||||
exportStateMiddleware,
|
||||
panelDispatcher(bgConnection),
|
||||
);
|
||||
const store = createStore(persistedReducer, enhancer);
|
||||
const persistor = persistStore(store as Store);
|
||||
return { store, persistor };
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user