mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-04-08 10:44:12 +03:00
Compare commits
No commits in common. "main" and "@redux-devtools/app@1.0.0-5" have entirely different histories.
main
...
@redux-dev
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@1.6.4/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"linked": [],
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": [],
|
||||
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
|
||||
"onlyUpdatePeerDependentsWhenOutOfRange": true
|
||||
}
|
||||
}
|
|
@ -7,5 +7,3 @@ build
|
|||
coverage
|
||||
node_modules
|
||||
__snapshots__
|
||||
storybook-static
|
||||
.vscode/*
|
||||
|
|
28
.eslintrc
Normal file
28
.eslintrc
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"root": true,
|
||||
"parser": "babel-eslint",
|
||||
"extends": ["eslint:recommended", "plugin:react/recommended", "prettier"],
|
||||
"globals": {
|
||||
"chrome": true
|
||||
},
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"jest": true,
|
||||
"node": true
|
||||
},
|
||||
"rules": {
|
||||
"eol-last": ["warn"],
|
||||
"max-len": ["warn", { "code": 120, "ignoreComments": true }],
|
||||
"quotes": ["warn", "single", "avoid-escape"],
|
||||
"jsx-quotes": ["warn", "prefer-double"],
|
||||
"react/prop-types": 0,
|
||||
"prettier/prettier": "error"
|
||||
},
|
||||
"plugins": ["prettier", "react", "babel"],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
}
|
||||
}
|
9
.gitattributes
vendored
9
.gitattributes
vendored
|
@ -1 +1,8 @@
|
|||
* text=auto eol=lf
|
||||
*.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
|
||||
|
|
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
|
@ -1,2 +1 @@
|
|||
github: Methuselah96
|
||||
open_collective: redux-devtools-extension
|
||||
|
|
31
.github/workflows/CI.yml
vendored
31
.github/workflows/CI.yml
vendored
|
@ -1,31 +0,0 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: 'ubuntu-22.04'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
- name: Check formatting
|
||||
run: pnpm run format:check
|
||||
- name: Build
|
||||
run: pnpm run build:all
|
||||
- name: Lint
|
||||
run: pnpm run lint:all
|
||||
- name: Test
|
||||
uses: coactions/setup-xvfb@v1
|
||||
with:
|
||||
run: pnpm run test:all
|
58
.github/workflows/release.yml
vendored
58
.github/workflows/release.yml
vendored
|
@ -1,58 +0,0 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: 'ubuntu-22.04'
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
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@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Create Release Pull Request or Publish to npm
|
||||
id: changesets
|
||||
uses: changesets/action@v1
|
||||
with:
|
||||
version: pnpm changeset version
|
||||
publish: pnpm run release
|
||||
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,4 +9,3 @@ coverage
|
|||
.idea
|
||||
.eslintcache
|
||||
!packages/redux-devtools-slider-monitor/examples/todomvc/dist/index.html
|
||||
.nx
|
||||
|
|
|
@ -8,7 +8,3 @@ coverage
|
|||
node_modules
|
||||
__snapshots__
|
||||
dev
|
||||
**/demo/public/**
|
||||
storybook-static
|
||||
.vscode/*
|
||||
pnpm-lock.yaml
|
||||
|
|
13
.travis.yml
Normal file
13
.travis.yml
Normal file
|
@ -0,0 +1,13 @@
|
|||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- 'stable'
|
||||
cache:
|
||||
yarn: true
|
||||
directories:
|
||||
- 'node_modules'
|
||||
script:
|
||||
- yarn build:all
|
||||
- yarn lint:all
|
||||
- yarn prettier:check
|
||||
- yarn test:all
|
4
CHANGELOG.md
Normal file
4
CHANGELOG.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Change log
|
||||
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
See all notable changes on [Releases](https://github.com/gaearon/redux-devtools/releases) page.
|
21
LICENSE.md
21
LICENSE.md
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-present Dan Abramov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
99
README.md
99
README.md
|
@ -1,100 +1,25 @@
|
|||

|
||||
[](https://github.com/reduxjs/redux-devtools/pulls)
|
||||
[](#backers)
|
||||
[](#sponsors)
|
||||
[](https://travis-ci.org/reduxjs/redux-devtools) [](https://github.com/reduxjs/redux-devtools/pulls)
|
||||
|
||||
# Redux DevTools
|
||||
|
||||
Developer Tools to power-up [Redux](https://redux.js.org/) development workflow or any other architecture which handles the state change (see [integrations](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/Integrations.md)).
|
||||
Developer Tools to power-up [Redux](https://github.com/reactjs/redux) development workflow or any other architecture which handles the state change (see [integrations](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/Integrations.md)).
|
||||
|
||||
It can be used as a browser extension (for [Chrome](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd), [Edge](https://microsoftedge.microsoft.com/addons/detail/redux-devtools/nnkgneoiohoecpdiaponcejilbhhikei) and [Firefox](https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/)), as [a standalone app](https://github.com/reduxjs/redux-devtools/tree/main/packages/redux-devtools-app) or as [a React component](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools) integrated in the client app.
|
||||
It can be used as a browser extension (for [Chrome](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd), [Edge](https://microsoftedge.microsoft.com/addons/detail/redux-devtools/nnkgneoiohoecpdiaponcejilbhhikei) and [Firefox](https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/)), as [a standalone app](https://github.com/zalmoxisus/remotedev-app) or as [a React component](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools) integrated in the client app.
|
||||
|
||||

|
||||
|
||||
## Documentation
|
||||
> Note that this repository is work in progress for [the monorepo](https://github.com/reduxjs/redux-devtools/issues/412), which will merge all the packages. Please refer to [Redux DevTools Extension](https://github.com/zalmoxisus/redux-devtools-extension) and [Redux DevTools package](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools).
|
||||
|
||||
- [Browser Extension Installation and Configuration](https://github.com/reduxjs/redux-devtools/tree/main/extension#installation)
|
||||
### Documentation
|
||||
|
||||
- [Browser Extension Installation and Configuration](https://github.com/zalmoxisus/redux-devtools-extension#installation)
|
||||
- [Manual Integration as a React Component](./docs/Walkthrough.md#manual-integration)
|
||||
- [Extension Options (Arguments)](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md)
|
||||
- [Extension Methods (Advanced API)](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Methods.md)
|
||||
- [Extension Options (Arguments)](https://github.com/zalmoxisus/redux-devtools-extension/tree/master/docs/API/Arguments.md)
|
||||
- [Extension Methods (Advanced API)](https://github.com/zalmoxisus/redux-devtools-extension/tree/master/docs/API/Methods.md)
|
||||
- [Remote monitoring](./docs/Integrations/Remote.md)
|
||||
- [Troubleshooting](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/Troubleshooting.md)
|
||||
- [Recipes](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/Recipes.md)
|
||||
- [FAQ](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/FAQ.md)
|
||||
|
||||
## Development
|
||||
|
||||
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
|
||||
|
||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/redux-devtools-extension#backer)]
|
||||
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/0/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/1/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/2/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/3/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/4/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/5/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/6/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/7/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/8/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/9/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/10/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/11/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/12/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/13/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/14/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/15/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/16/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/17/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/18/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/19/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/20/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/21/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/22/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/23/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/24/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/25/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/26/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/27/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/28/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/backer/29/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/29/avatar.svg"></a>
|
||||
|
||||
## Sponsors
|
||||
|
||||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/redux-devtools-extension#sponsor)]
|
||||
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/0/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/1/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/2/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/3/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/4/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/5/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/6/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/7/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/8/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/9/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/10/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/11/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/12/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/13/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/14/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/15/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/16/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/17/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/18/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/19/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/20/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/21/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/22/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/23/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/24/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/25/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/26/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/27/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/28/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/redux-devtools-extension/sponsor/29/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/29/avatar.svg"></a>
|
||||
- [Troubleshooting](https://github.com/zalmoxisus/redux-devtools-extension/tree/master/docs/Troubleshooting.md)
|
||||
- [Recipes](https://github.com/zalmoxisus/redux-devtools-extension/tree/master/docs/Recipes.md)
|
||||
- [FAQ](https://github.com/zalmoxisus/redux-devtools-extension/tree/master/docs/FAQ.md)
|
||||
|
||||
### License
|
||||
|
||||
|
|
14
babel.config.js
Executable file
14
babel.config.js
Executable file
|
@ -0,0 +1,14 @@
|
|||
module.exports = {
|
||||
presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-flow'],
|
||||
plugins: [
|
||||
[
|
||||
'@babel/plugin-transform-runtime',
|
||||
{
|
||||
regenerator: true,
|
||||
},
|
||||
],
|
||||
['@babel/plugin-proposal-decorators', { legacy: true }],
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
'@babel/plugin-proposal-export-default-from',
|
||||
],
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
## Remote monitoring
|
||||
|
||||
By installing [`@redux-devtools/cli`](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-cli#usage), starting the server server and launching the Redux DevTools app (`redux-devtools --open`), you can connect any remote application, even not javascript. There are some integrations for javascript like [remote-redux-devtools](https://github.com/zalmoxisus/remote-redux-devtools) and [remotedev](https://github.com/zalmoxisus/remotedev), but the plan is to deprecate them and support it out of the box from the extension without a websocket server. It is more useful for non-js apps.
|
||||
By installing [`redux-devtools-cli`](https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-cli#usage), starting the server server and launching the Redux DevTools app (`redux-devtools --open`), you can connect any remote application, even not javascript. There are some integrations for javascript like [remote-redux-devtools](https://github.com/zalmoxisus/remote-redux-devtools) and [remotedev](https://github.com/zalmoxisus/remotedev), but the plan is to deprecate them and support it out of the box from the extension without a websocket server. It is more useful for non-js apps.
|
||||
|
||||
### WebSocket Clients
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Browser Extension
|
||||
|
||||
If you don’t want to bother with installing Redux DevTools and integrating it into your project, consider using [Redux DevTools Extension](https://github.com/reduxjs/redux-devtools/tree/master/extension) for Chrome and Firefox. It provides access to the most popular monitors, is easy to configure to filter actions, and doesn’t require installing any packages.
|
||||
If you don’t want to bother with installing Redux DevTools and integrating it into your project, consider using [Redux DevTools Extension](https://github.com/zalmoxisus/redux-devtools-extension) for Chrome and Firefox. It provides access to the most popular monitors, is easy to configure to filter actions, and doesn’t require installing any packages.
|
||||
|
||||
## Manual Integration
|
||||
|
||||
|
@ -12,14 +12,14 @@ It’s more steps, but you will have full control over monitors and their config
|
|||
### Installation
|
||||
|
||||
```
|
||||
npm install --save-dev @redux-devtools/core
|
||||
npm install --save-dev redux-devtools
|
||||
```
|
||||
|
||||
You’ll also likely want to install some monitors:
|
||||
|
||||
```
|
||||
npm install --save-dev @redux-devtools/log-monitor
|
||||
npm install --save-dev @redux-devtools/dock-monitor
|
||||
npm install --save-dev redux-devtools-log-monitor
|
||||
npm install --save-dev redux-devtools-dock-monitor
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
@ -34,11 +34,11 @@ Somewhere in your project, create a `DevTools` component by passing a `monitor`
|
|||
import React from 'react';
|
||||
|
||||
// Exported from redux-devtools
|
||||
import { createDevTools } from '@redux-devtools/core';
|
||||
import { createDevTools } from 'redux-devtools';
|
||||
|
||||
// Monitors are separate packages, and you can make a custom one
|
||||
import LogMonitor from '@redux-devtools/log-monitor';
|
||||
import DockMonitor from '@redux-devtools/dock-monitor';
|
||||
import LogMonitor from 'redux-devtools-log-monitor';
|
||||
import DockMonitor from 'redux-devtools-dock-monitor';
|
||||
|
||||
// createDevTools takes a monitor and produces a DevTools component
|
||||
const DevTools = createDevTools(
|
||||
|
@ -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+ */
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ If you’d like, you may add another store enhancer called `persistState()`. It
|
|||
|
||||
```js
|
||||
// ...
|
||||
import { persistState } from '@redux-devtools/core';
|
||||
import { persistState } from 'redux-devtools';
|
||||
|
||||
const enhancer = compose(
|
||||
// Middleware you want to use in development:
|
||||
|
@ -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() {
|
||||
|
@ -190,7 +190,7 @@ export default function configureStore(initialState) {
|
|||
|
||||
```js
|
||||
import { createStore, applyMiddleware, compose } from 'redux';
|
||||
import { persistState } from '@redux-devtools/core';
|
||||
import { persistState } from 'redux-devtools';
|
||||
import rootReducer from '../reducers';
|
||||
import DevTools from '../containers/DevTools';
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
import eslint from '@eslint/js';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default [eslint.configs.recommended, eslintConfigPrettier];
|
|
@ -1,43 +0,0 @@
|
|||
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,
|
||||
},
|
||||
];
|
|
@ -1,55 +0,0 @@
|
|||
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',
|
||||
},
|
||||
},
|
||||
];
|
|
@ -1,64 +0,0 @@
|
|||
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',
|
||||
},
|
||||
},
|
||||
];
|
|
@ -1,89 +0,0 @@
|
|||
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',
|
||||
},
|
||||
},
|
||||
];
|
|
@ -1,85 +0,0 @@
|
|||
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',
|
||||
},
|
||||
},
|
||||
];
|
18
eslintrc.ts.base.json
Normal file
18
eslintrc.ts.base.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"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",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
19
eslintrc.ts.jest.base.json
Normal file
19
eslintrc.ts.jest.base.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"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",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier/@typescript-eslint"
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
30
eslintrc.ts.react.base.json
Normal file
30
eslintrc.ts.react.base.json
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"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:prettier/recommended",
|
||||
"prettier/@typescript-eslint",
|
||||
"prettier/react"
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
21
eslintrc.ts.react.jest.base.json
Normal file
21
eslintrc.ts.react.jest.base.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"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:jest/recommended",
|
||||
"plugin:jest/style",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier/@typescript-eslint",
|
||||
"prettier/react"
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
7
extension/.babelrc
Normal file
7
extension/.babelrc
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
||||
"plugins": [
|
||||
["@babel/plugin-proposal-decorators", { "legacy": true }],
|
||||
["@babel/plugin-proposal-class-properties", { "loose": true }]
|
||||
]
|
||||
}
|
8
extension/.bookignore
Normal file
8
extension/.bookignore
Normal file
|
@ -0,0 +1,8 @@
|
|||
src/
|
||||
build/
|
||||
dev/
|
||||
examples/
|
||||
npm-package/
|
||||
test/
|
||||
package.json
|
||||
webpack/
|
7
extension/.eslintignore
Normal file
7
extension/.eslintignore
Normal file
|
@ -0,0 +1,7 @@
|
|||
node_modules
|
||||
build
|
||||
dev
|
||||
webpack/replace
|
||||
examples
|
||||
npm-package
|
||||
_book
|
31
extension/.eslintrc
Normal file
31
extension/.eslintrc
Normal file
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"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,2 +1,9 @@
|
|||
node_modules
|
||||
dist
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
.idea/
|
||||
dist/
|
||||
build/
|
||||
dev/
|
||||
tmp/
|
||||
_book
|
||||
|
|
32
extension/.travis.yml
Normal file
32
extension/.travis.yml
Normal file
|
@ -0,0 +1,32 @@
|
|||
sudo: required
|
||||
dist: trusty
|
||||
language: node_js
|
||||
node_js:
|
||||
- '6'
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.yarn-cache
|
||||
- node_modules
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- google-chrome
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- google-chrome-stable
|
||||
- g++-4.8
|
||||
|
||||
install:
|
||||
- '/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16'
|
||||
- npm install -g yarn
|
||||
- yarn install
|
||||
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start &
|
||||
- sleep 3
|
||||
|
||||
script:
|
||||
- yarn test
|
|
@ -1,238 +1,4 @@
|
|||
# remotedev-redux-devtools-extension
|
||||
# Change Log
|
||||
|
||||
## 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
|
||||
|
||||
- Updated dependencies [8a7eae4]
|
||||
- react-json-tree@0.17.0
|
||||
- @redux-devtools/app@2.2.0
|
||||
- @redux-devtools/slider-monitor@4.0.0
|
||||
- @redux-devtools/ui@1.3.0
|
||||
- @redux-devtools/core@3.13.0
|
||||
- @redux-devtools/utils@2.0.0
|
||||
|
||||
## 3.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4891bf6]
|
||||
- @redux-devtools/core@3.12.0
|
||||
- @redux-devtools/slider-monitor@3.1.2
|
||||
- @redux-devtools/utils@1.2.1
|
||||
- @redux-devtools/app@2.1.4
|
||||
|
||||
## 3.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- ab3c0e2: Avoid persisting the selected action index between sessions
|
||||
- Updated dependencies [ab3c0e2]
|
||||
- Updated dependencies [4c9a890]
|
||||
- @redux-devtools/app@2.1.3
|
||||
- react-json-tree@0.16.2
|
||||
|
||||
## 3.0.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 55cc37e: Fix filter to show state-controlled search value
|
||||
- Updated dependencies [55cc37e]
|
||||
- @redux-devtools/app@2.1.2
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
Every release, along with the migration instructions, is documented on the Github [Releases](https://github.com/zalmoxisus/redux-devtools-extension/releases) page.
|
||||
|
|
|
@ -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/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`.
|
||||
- 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`.
|
||||
|
||||
### 2. For Firefox
|
||||
|
||||
|
@ -57,7 +57,7 @@ const composeEnhancers =
|
|||
compose;
|
||||
```
|
||||
|
||||
> 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).
|
||||
> 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).
|
||||
|
||||
```js
|
||||
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
|
@ -100,12 +100,12 @@ To specify [extension’s options](https://github.com/zalmoxisus/redux-devtools-
|
|||
const composeEnhancers =
|
||||
typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
|
||||
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
|
||||
// Specify extension’s options like name, actionsDenylist, actionsCreators, serialize...
|
||||
// Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
|
||||
})
|
||||
: compose;
|
||||
|
||||
const enhancer = composeEnhancers(
|
||||
applyMiddleware(...middleware),
|
||||
applyMiddleware(...middleware)
|
||||
// other store enhancers if any
|
||||
);
|
||||
const store = createStore(reducer, enhancer);
|
||||
|
@ -113,26 +113,26 @@ const store = createStore(reducer, enhancer);
|
|||
|
||||
> [See the post for more details](https://medium.com/@zalmoxis/improve-your-development-workflow-with-redux-devtools-extension-f0379227ff83).
|
||||
|
||||
### 1.3 Use `@redux-devtools/extension` package from npm
|
||||
### 1.3 Use `redux-devtools-extension` package from npm
|
||||
|
||||
To make things easier, there's an npm package to install:
|
||||
|
||||
```
|
||||
npm install --save @redux-devtools/extension
|
||||
npm install --save redux-devtools-extension
|
||||
```
|
||||
|
||||
and to use like so:
|
||||
|
||||
```js
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
import { composeWithDevTools } from '@redux-devtools/extension';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||
|
||||
const store = createStore(
|
||||
reducer,
|
||||
composeWithDevTools(
|
||||
applyMiddleware(...middleware),
|
||||
applyMiddleware(...middleware)
|
||||
// other store enhancers if any
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -140,32 +140,32 @@ To specify [extension’s options](https://github.com/zalmoxisus/redux-devtools-
|
|||
|
||||
```js
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
import { composeWithDevTools } from '@redux-devtools/extension';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||
|
||||
const composeEnhancers = composeWithDevTools({
|
||||
// Specify name here, actionsDenylist, actionsCreators and other options if needed
|
||||
// Specify name here, actionsBlacklist, actionsCreators and other options if needed
|
||||
});
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ composeEnhancers(
|
||||
applyMiddleware(...middleware),
|
||||
applyMiddleware(...middleware)
|
||||
// other store enhancers if any
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
> There are just a [few lines of code](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/index.js) added to your bundle.
|
||||
> There’re just [few lines of code](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/index.js) added to your bundle.
|
||||
|
||||
In case you don't include other enhancers and middlewares, just use `devToolsEnhancer`:
|
||||
|
||||
```js
|
||||
import { createStore } from 'redux';
|
||||
import { devToolsEnhancer } from '@redux-devtools/extension';
|
||||
import { devToolsEnhancer } from 'redux-devtools-extension';
|
||||
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ devToolsEnhancer(),
|
||||
// Specify name here, actionsDenylist, actionsCreators and other options if needed
|
||||
/* preloadedState, */ devToolsEnhancer()
|
||||
// Specify name here, actionsBlacklist, actionsCreators and other options if needed
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -173,15 +173,15 @@ const store = createStore(
|
|||
|
||||
It's useful to include the extension in production as well. Usually you [can use it for development](https://medium.com/@zalmoxis/using-redux-devtools-in-production-4c5b56c5600f).
|
||||
|
||||
If you want to restrict it there, use `composeWithDevToolsLogOnlyInProduction` or `devToolsEnhancerLogOnlyInProduction`:
|
||||
If you want to restrict it there, use `redux-devtools-extension/logOnlyInProduction`:
|
||||
|
||||
```js
|
||||
import { createStore } from 'redux';
|
||||
import { devToolsEnhancerLogOnlyInProduction } from '@redux-devtools/extension';
|
||||
import { devToolsEnhancer } from 'redux-devtools-extension/logOnlyInProduction';
|
||||
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ devToolsEnhancerLogOnlyInProduction(),
|
||||
/* preloadedState, */ devToolsEnhancer()
|
||||
// options like actionSanitizer, stateSanitizer
|
||||
);
|
||||
```
|
||||
|
@ -190,25 +190,25 @@ or with middlewares and enhancers:
|
|||
|
||||
```js
|
||||
import { createStore, applyMiddleware } from 'redux';
|
||||
import { composeWithDevToolsLogOnlyInProduction } from '@redux-devtools/extension';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction';
|
||||
|
||||
const composeEnhancers = composeWithDevToolsLogOnlyInProduction({
|
||||
const composeEnhancers = composeWithDevTools({
|
||||
// options like actionSanitizer, stateSanitizer
|
||||
});
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ composeEnhancers(
|
||||
applyMiddleware(...middleware),
|
||||
applyMiddleware(...middleware)
|
||||
// other store enhancers if any
|
||||
),
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
> You'll have to add `'process.env.NODE_ENV': JSON.stringify('production')` in your Webpack config for the production bundle ([to envify](https://github.com/gaearon/redux-devtools/blob/master/docs/Walkthrough.md#exclude-devtools-from-production-builds)). If you use `create-react-app`, [it already does it for you.](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/config/webpack.config.prod.js#L253-L257)
|
||||
|
||||
If you're already checking `process.env.NODE_ENV` when creating the store, import `composeWithDevToolsLogOnly` or `devToolsEnhancerLogOnly` for production environment.
|
||||
If you're already checking `process.env.NODE_ENV` when creating the store, include `redux-devtools-extension/logOnly` for production environment.
|
||||
|
||||
If you don’t want to allow the extension in production, just use `composeWithDevToolsDevelopmentOnly` or `devToolsEnhancerDevelopmentOnly`.
|
||||
If you don’t want to allow the extension in production, just use `redux-devtools-extension/developmentOnly`.
|
||||
|
||||
> See [the article](https://medium.com/@zalmoxis/using-redux-devtools-in-production-4c5b56c5600f) for more details.
|
||||
|
||||
|
|
1
extension/SUMMARY.md
Normal file
1
extension/SUMMARY.md
Normal file
|
@ -0,0 +1 @@
|
|||
./docs/README.md
|
18
extension/appveyor.yml
Normal file
18
extension/appveyor.yml
Normal file
|
@ -0,0 +1,18 @@
|
|||
environment:
|
||||
matrix:
|
||||
- nodejs_version: '6'
|
||||
|
||||
cache:
|
||||
- '%LOCALAPPDATA%/Yarn'
|
||||
- node_modules
|
||||
|
||||
install:
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
- yarn install
|
||||
|
||||
test_script:
|
||||
- node --version
|
||||
- yarn --version
|
||||
- yarn test
|
||||
|
||||
build: off
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"presets": [
|
||||
["@babel/preset-env", { "targets": "defaults" }],
|
||||
"@babel/preset-react",
|
||||
"@babel/preset-typescript"
|
||||
]
|
||||
}
|
18
extension/book.json
Normal file
18
extension/book.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"gitbook": "3.2.2",
|
||||
"title": "Redux DevTools Extension",
|
||||
"plugins": ["edit-link", "prism", "-highlight", "github", "anchorjs"],
|
||||
"pluginsConfig": {
|
||||
"edit-link": {
|
||||
"base": "https://github.com/zalmoxisus/redux-devtools-extension/tree/master",
|
||||
"label": "Edit This Page"
|
||||
},
|
||||
"github": {
|
||||
"url": "https://github.com/zalmoxisus/redux-devtools-extension/"
|
||||
},
|
||||
"sharing": {
|
||||
"facebook": true,
|
||||
"twitter": true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
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');
|
|
@ -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,13 +236,13 @@ const store = createStore(
|
|||
actionSanitizer,
|
||||
stateSanitizer: (state) =>
|
||||
state.data ? { ...state, data: '<<LONG_BLOB>>' } : state,
|
||||
}),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
### `actionsDenylist` / `actionsAllowlist`
|
||||
### `actionsBlacklist` / `actionsWhitelist`
|
||||
|
||||
_string or array of strings as regex_ - actions types to be hidden / shown in the monitors (while passed to the reducers). If `actionsAllowlist` specified, `actionsDenylist` is ignored.
|
||||
_string or array of strings as regex_ - actions types to be hidden / shown in the monitors (while passed to the reducers). If `actionsWhitelist` specified, `actionsBlacklist` is ignored.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -251,16 +251,16 @@ createStore(
|
|||
reducer,
|
||||
remotedev({
|
||||
sendTo: 'http://localhost:8000',
|
||||
actionsDenylist: 'SOME_ACTION',
|
||||
// or actionsDenylist: ['SOME_ACTION', 'SOME_OTHER_ACTION']
|
||||
// or just actionsDenylist: 'SOME_' to omit both
|
||||
}),
|
||||
actionsBlacklist: 'SOME_ACTION',
|
||||
// or actionsBlacklist: ['SOME_ACTION', 'SOME_OTHER_ACTION']
|
||||
// or just actionsBlacklist: 'SOME_' to omit both
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
### `predicate`
|
||||
|
||||
_function_ - called for every action before sending, takes `state` and `action` object, and returns `true` in case it allows sending the current data to the monitor. Use it as a more advanced version of `actionsDenylist`/`actionsAllowlist` parameters.
|
||||
_function_ - called for every action before sending, takes `state` and `action` object, and returns `true` in case it allows sending the current data to the monitor. Use it as a more advanced version of `actionsBlacklist`/`actionsWhitelist` parameters.
|
||||
Example of usage:
|
||||
|
||||
```js
|
||||
|
@ -270,7 +270,7 @@ const store = createStore(
|
|||
window.__REDUX_DEVTOOLS_EXTENSION__({
|
||||
predicate: (state, action) =>
|
||||
state.dev.logLevel === VERBOSE && !action.forwarded,
|
||||
}),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ devTools.init({ value: 'initial state' });
|
|||
devTools.send('change state', { value: 'state changed' });
|
||||
```
|
||||
|
||||
See [redux enhancer's example](https://github.com/reduxjs/redux-devtools/blob/main/packages/redux-devtools-extension/src/logOnly.ts), [react example](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/examples/react-counter-messaging/components/Counter.js) and [blog post](https://medium.com/@zalmoxis/redux-devtools-without-redux-or-how-to-have-a-predictable-state-with-any-architecture-61c5f5a7716f) for more details.
|
||||
See [redux enhancer's example](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/logOnly.js), [react example](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/examples/react-counter-messaging/components/Counter.js) and [blog post](https://medium.com/@zalmoxis/redux-devtools-without-redux-or-how-to-have-a-predictable-state-with-any-architecture-61c5f5a7716f) for more details.
|
||||
|
||||
### disconnect()
|
||||
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
# 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 },
|
||||
],
|
||||
}),
|
||||
})
|
||||
),
|
||||
});
|
||||
```
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
### Using in a typescript project
|
||||
|
||||
The recommended way is to use [`@redux-devtools/extension` npm package](/README.md#13-use-redux-devtools-extension-package-from-npm), which contains all typescript definitions. Or you can just use `window as any`:
|
||||
The recommended way is to use [`redux-devtools-extension` npm package](/README.md#13-use-redux-devtools-extension-package-from-npm), which contains all typescript definitions. Or you can just use `window as any`:
|
||||
|
||||
```js
|
||||
const store = createStore(
|
||||
|
@ -15,7 +15,7 @@ const store = createStore(
|
|||
|
||||
Note that you many need to set `no-any` to false in your `tslint.json` file.
|
||||
|
||||
Alternatively you can use type-guard in order to avoid
|
||||
Alternatively you can use typeguard in order to avoid
|
||||
casting to any.
|
||||
|
||||
```typescript
|
||||
|
@ -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
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -54,25 +54,25 @@ The extension is not sharing `store` object, so you should take care of that.
|
|||
|
||||
### Applying multiple times with different sets of options
|
||||
|
||||
We're [not allowing that from instrumentation part](https://github.com/reduxjs/redux-devtools/blob/main/packages/redux-devtools-extension/src/logOnly.ts), which can be used it like so:
|
||||
We're [not allowing that from instrumentation part](https://github.com/zalmoxisus/redux-devtools-instrument/blob/master/src/instrument.js#L676), because that would re-dispatch every app action in case we'd have many liftedStores, but there's [a helper for logging only](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/logOnly.js), which can be used it like so:
|
||||
|
||||
```js
|
||||
import { createStore, compose } from 'redux';
|
||||
import { devToolsEnhancerLogOnly } from '@redux-devtools/extension';
|
||||
import { devToolsEnhancer } from 'redux-devtools-extension/logOnly';
|
||||
|
||||
const store = createStore(
|
||||
reducer,
|
||||
/* preloadedState, */ compose(
|
||||
devToolsEnhancerLogOnly({
|
||||
devToolsEnhancer({
|
||||
instaceID: 1,
|
||||
name: 'Denylisted',
|
||||
actionsDenylist: '...',
|
||||
name: 'Blacklisted',
|
||||
actionsBlacklist: '...',
|
||||
}),
|
||||
devToolsEnhancerLogOnly({
|
||||
devToolsEnhancer({
|
||||
instaceID: 2,
|
||||
name: 'Allowlisted',
|
||||
actionsAllowlist: '...',
|
||||
}),
|
||||
),
|
||||
name: 'Whitelisted',
|
||||
actionsWhitelist: '...',
|
||||
})
|
||||
)
|
||||
);
|
||||
```
|
||||
|
|
|
@ -16,8 +16,6 @@ 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`.
|
||||
|
@ -35,8 +33,8 @@ const store = createStore(
|
|||
window.__REDUX_DEVTOOLS_EXTENSION__
|
||||
? window.__REDUX_DEVTOOLS_EXTENSION__()
|
||||
: (noop) => noop,
|
||||
batchedSubscribe(/* ... */),
|
||||
),
|
||||
batchedSubscribe(/* ... */)
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -60,7 +58,7 @@ const store = createStore(
|
|||
actionSanitizer,
|
||||
stateSanitizer: (state) =>
|
||||
state.data ? { ...state, data: '<<LONG_BLOB>>' } : state,
|
||||
}),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
|
@ -124,7 +122,7 @@ const store = Redux.createStore(
|
|||
window.__REDUX_DEVTOOLS_EXTENSION__ &&
|
||||
window.__REDUX_DEVTOOLS_EXTENSION__({
|
||||
serialize: true,
|
||||
}),
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
{
|
||||
"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:;"
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
|
@ -3,8 +3,13 @@ import PropTypes from 'prop-types';
|
|||
|
||||
class Counter extends Component {
|
||||
render() {
|
||||
const { increment, autoIncrement, incrementAsync, decrement, counter } =
|
||||
this.props;
|
||||
const {
|
||||
increment,
|
||||
autoIncrement,
|
||||
incrementAsync,
|
||||
decrement,
|
||||
counter,
|
||||
} = this.props;
|
||||
return (
|
||||
<p>
|
||||
Clicked: {counter} times <button onClick={increment}>+</button>{' '}
|
||||
|
|
|
@ -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,57 +0,0 @@
|
|||
{
|
||||
"version": "3.2.10",
|
||||
"name": "Redux DevTools",
|
||||
"manifest_version": 3,
|
||||
"description": "Redux Developer Tools for debugging application state changes.",
|
||||
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "extension@redux.devtools"
|
||||
}
|
||||
},
|
||||
"action": {
|
||||
"default_icon": "img/logo/38x38.png",
|
||||
"default_title": "Redux DevTools",
|
||||
"default_popup": "devpanel.html#popup"
|
||||
},
|
||||
"commands": {
|
||||
"devtools-window": {
|
||||
"description": "DevTools window"
|
||||
},
|
||||
"devtools-remote": {
|
||||
"description": "Remote DevTools"
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"16": "img/logo/16x16.png",
|
||||
"48": "img/logo/48x48.png",
|
||||
"128": "img/logo/128x128.png"
|
||||
},
|
||||
"options_ui": {
|
||||
"page": "options.html"
|
||||
},
|
||||
"background": {
|
||||
"scripts": ["background.bundle.js"]
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"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",
|
||||
"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:;"
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
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)',
|
||||
],
|
||||
};
|
3
extension/jest.config.js
Normal file
3
extension/jest.config.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
setupFiles: ['<rootDir>/test/setup.js'],
|
||||
};
|
|
@ -1,81 +1,90 @@
|
|||
{
|
||||
"private": true,
|
||||
"version": "2.17.1",
|
||||
"name": "remotedev-redux-devtools-extension",
|
||||
"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",
|
||||
"author": "Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/reduxjs/redux-devtools.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm run build:extension && pnpm run type-check",
|
||||
"build:extension": "node build.mjs",
|
||||
"start": "webpack --config webpack/dev.config.babel.js",
|
||||
"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:examples": "babel-node examples/buildAll.js",
|
||||
"clean": "rimraf dist && rimraf chrome/dist && rimraf edge/dist && rimraf firefox/dist",
|
||||
"precompress:extension": "npm run lint && npm run test:app && npm run build:extension && npm run test:chrome && npm run test:electron",
|
||||
"precompress:firefox": "npm run lint && npm run build:firefox && npm run test:app",
|
||||
"compress:extension": "cd build/extension && bestzip ../extension.zip",
|
||||
"compress:firefox": "cd build/firefox && bestzip ../firefox.zip",
|
||||
"docs:clean": "rimraf _book",
|
||||
"docs:prepare": "gitbook install",
|
||||
"docs:build": "npm run docs:prepare && gitbook build",
|
||||
"docs:watch": "npm run docs:prepare && gitbook serve",
|
||||
"docs:publish": "npm run docs:clean && npm run docs:build && cd _book && git init && git commit --allow-empty -m 'update book' && git checkout -b gh-pages && touch .nojekyll && git add . && git commit -am 'update book' && git push git@github.com:zalmoxisus/redux-devtools-extension gh-pages --force",
|
||||
"clean": "rimraf build/ && rimraf dev/",
|
||||
"lint": "eslint .",
|
||||
"test:app": "cross-env BABEL_ENV=test jest test/app",
|
||||
"test:chrome": "jest test/chrome",
|
||||
"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"
|
||||
"test:electron": "jest test/electron && rimraf test/electron/tmp",
|
||||
"test": "npm run test:app && npm run build:extension && npm run test:chrome && npm run test:electron"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zalmoxisus/redux-devtools-extension"
|
||||
},
|
||||
"homepage": "https://github.com/zalmoxisus/redux-devtools-extension",
|
||||
"author": "Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.10.5",
|
||||
"@babel/core": "^7.11.1",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.4",
|
||||
"@babel/plugin-proposal-decorators": "^7.12.1",
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"@babel/preset-env": "^7.11.0",
|
||||
"@babel/preset-react": "^7.10.4",
|
||||
"@babel/register": "^7.12.10",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"bestzip": "^2.1.7",
|
||||
"chromedriver": "^2.35.0",
|
||||
"copy-webpack-plugin": "^6.3.1",
|
||||
"cross-env": "^7.0.2",
|
||||
"electron": "^2.0.2",
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-adapter-react-15.4": "^1.4.2",
|
||||
"eslint": "^7.6.0",
|
||||
"eslint-config-airbnb": "^18.2.1",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-react": "^7.21.5",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"file-loader": "^6.0.0",
|
||||
"gitbook-cli": "^2.3.0",
|
||||
"jest": "^26.2.2",
|
||||
"pug-html-loader": "^1.1.5",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react-addons-test-utils": "^15.4.1",
|
||||
"react-transform-catch-errors": "^1.0.0",
|
||||
"react-transform-hmr": "^1.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"selenium-webdriver": "^3.0.1",
|
||||
"sinon-chrome": "^1.1.2",
|
||||
"style-loader": "^1.2.1",
|
||||
"terser-webpack-plugin": "^1.1.0",
|
||||
"webpack": "^4.44.1",
|
||||
"webpack-cli": "^3.3.12"
|
||||
},
|
||||
"dependencies": {
|
||||
"@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-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.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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",
|
||||
"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",
|
||||
"ts-jest": "^29.2.6",
|
||||
"typescript": "~5.8.2",
|
||||
"webpack": "^5.98.0",
|
||||
"webpack-cli": "^6.0.1"
|
||||
"jsan": "^3.1.13",
|
||||
"lodash": "^4.17.19",
|
||||
"react": "^15.4.1",
|
||||
"react-dom": "^15.4.1",
|
||||
"react-icons": "^3.10.0",
|
||||
"react-json-tree": "^0.10.9",
|
||||
"react-redux": "^5.1.2",
|
||||
"redux": "^3.7.2",
|
||||
"redux-devtools": "^3.4.1",
|
||||
"redux-devtools-instrument": "^1.9.6",
|
||||
"remotedev-app": "^0.10.13-beta",
|
||||
"remotedev-monitor-components": "^0.0.5",
|
||||
"remotedev-serialize": "^0.1.8",
|
||||
"remotedev-slider": "^1.1.1",
|
||||
"remotedev-utils": "0.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import { connect, ResolveThunks } from 'react-redux';
|
||||
import { Button, Container, Divider, Toolbar } from '@redux-devtools/ui';
|
||||
import {
|
||||
DevTools,
|
||||
Dispatcher,
|
||||
DispatcherButton,
|
||||
ExportButton,
|
||||
getActiveInstance,
|
||||
getReport,
|
||||
ImportButton,
|
||||
liftedDispatch,
|
||||
MonitorSelector,
|
||||
PrintButton,
|
||||
SliderButton,
|
||||
SliderMonitor,
|
||||
StoreState,
|
||||
TopButtons,
|
||||
} from '@redux-devtools/app';
|
||||
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>;
|
||||
interface OwnProps {
|
||||
readonly position: string;
|
||||
}
|
||||
type Props = StateProps & DispatchProps & OwnProps;
|
||||
|
||||
const isElectron = navigator.userAgent.includes('Electron');
|
||||
|
||||
async function sendMessage(message: SingleMessage) {
|
||||
await chrome.runtime.sendMessage(message);
|
||||
}
|
||||
|
||||
class Actions extends Component<Props> {
|
||||
openWindow = async (position: Position) => {
|
||||
await sendMessage({ type: 'OPEN', position });
|
||||
};
|
||||
openOptionsPage = async () => {
|
||||
if (navigator.userAgent.includes('Firefox')) {
|
||||
await sendMessage({ type: 'OPEN_OPTIONS' });
|
||||
} else {
|
||||
await chrome.runtime.openOptionsPage();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
monitor,
|
||||
dispatcherIsOpen,
|
||||
sliderIsOpen,
|
||||
options,
|
||||
liftedState,
|
||||
liftedDispatch,
|
||||
position,
|
||||
stateTreeSettings,
|
||||
} = this.props;
|
||||
const { features } = options;
|
||||
return (
|
||||
<Container>
|
||||
<TopButtons
|
||||
dispatch={liftedDispatch}
|
||||
liftedState={liftedState}
|
||||
options={options}
|
||||
/>
|
||||
<DevTools
|
||||
monitor={monitor}
|
||||
liftedState={liftedState}
|
||||
monitorState={this.props.monitorState}
|
||||
dispatch={liftedDispatch}
|
||||
features={options.features}
|
||||
stateTreeSettings={stateTreeSettings}
|
||||
/>
|
||||
{sliderIsOpen && options.connectionId && options.features.jump && (
|
||||
<SliderMonitor liftedState={liftedState} dispatch={liftedDispatch} />
|
||||
)}
|
||||
{dispatcherIsOpen &&
|
||||
options.connectionId &&
|
||||
options.features.dispatch && <Dispatcher options={options} />}
|
||||
<Toolbar borderPosition="top">
|
||||
{features.export && <ExportButton />}
|
||||
{features.import && <ImportButton />}
|
||||
{position &&
|
||||
(position !== '#popup' ||
|
||||
navigator.userAgent.includes('Firefox')) && <PrintButton />}
|
||||
<Divider />
|
||||
<MonitorSelector />
|
||||
<Divider />
|
||||
{features.jump && <SliderButton isOpen={this.props.sliderIsOpen} />}
|
||||
{features.dispatch && (
|
||||
<DispatcherButton dispatcherIsOpen={this.props.dispatcherIsOpen} />
|
||||
)}
|
||||
<Divider />
|
||||
{!isElectron && (
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await this.openWindow('window');
|
||||
}}
|
||||
>
|
||||
<MdOutlineWindow />
|
||||
</Button>
|
||||
)}
|
||||
{!isElectron && (
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await this.openWindow('remote');
|
||||
}}
|
||||
>
|
||||
<GoBroadcast />
|
||||
</Button>
|
||||
)}
|
||||
</Toolbar>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: StoreState) => {
|
||||
const instances = state.instances;
|
||||
const id = getActiveInstance(instances);
|
||||
return {
|
||||
liftedState: instances.states[id],
|
||||
monitorState: state.monitor.monitorState,
|
||||
options: instances.options[id],
|
||||
monitor: state.monitor.selected,
|
||||
dispatcherIsOpen: state.monitor.dispatcherIsOpen,
|
||||
sliderIsOpen: state.monitor.sliderIsOpen,
|
||||
reports: state.reports.data,
|
||||
stateTreeSettings: state.stateTreeSettings,
|
||||
};
|
||||
};
|
||||
|
||||
const actionCreators = {
|
||||
liftedDispatch,
|
||||
getReport,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, actionCreators)(Actions);
|
|
@ -1,80 +0,0 @@
|
|||
import React, { Component } from 'react';
|
||||
import { connect, ResolveThunks } from 'react-redux';
|
||||
import { Container, Notification } from '@redux-devtools/ui';
|
||||
import {
|
||||
clearNotification,
|
||||
getActiveInstance,
|
||||
Header,
|
||||
Settings,
|
||||
StoreState,
|
||||
} from '@redux-devtools/app';
|
||||
import Actions from './Actions';
|
||||
|
||||
type StateProps = ReturnType<typeof mapStateToProps>;
|
||||
type DispatchProps = ResolveThunks<typeof actionCreators>;
|
||||
interface OwnProps {
|
||||
readonly position: string;
|
||||
}
|
||||
type Props = StateProps & DispatchProps & OwnProps;
|
||||
|
||||
class App extends Component<Props> {
|
||||
render() {
|
||||
const { position, options, section, theme, notification } = this.props;
|
||||
if (!position && (!options || !options.features)) {
|
||||
return (
|
||||
<div style={{ padding: '20px', width: '100%', textAlign: 'center' }}>
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
let body;
|
||||
switch (section) {
|
||||
case 'Settings':
|
||||
body = <Settings />;
|
||||
break;
|
||||
default:
|
||||
body = <Actions position={position} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Container themeData={theme}>
|
||||
<Header section={section} />
|
||||
{body}
|
||||
{notification && (
|
||||
<Notification
|
||||
type={notification.type}
|
||||
onClose={this.props.clearNotification}
|
||||
>
|
||||
{notification.message}
|
||||
</Notification>
|
||||
)}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state: StoreState) {
|
||||
const instances = state.instances;
|
||||
const id = getActiveInstance(instances);
|
||||
return {
|
||||
options: instances.options[id],
|
||||
section: state.section,
|
||||
theme: state.theme,
|
||||
notification: state.notification,
|
||||
};
|
||||
}
|
||||
|
||||
const actionCreators = {
|
||||
clearNotification,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, actionCreators)(App);
|
|
@ -1,29 +1,33 @@
|
|||
import { Action } from 'redux';
|
||||
import { LiftedState, PerformAction } from '@redux-devtools/instrument';
|
||||
import { LocalFilter } from '@redux-devtools/utils';
|
||||
import mapValues from 'lodash/mapValues';
|
||||
|
||||
export type FilterStateValue =
|
||||
| 'DO_NOT_FILTER'
|
||||
| 'DENYLIST_SPECIFIC'
|
||||
| 'ALLOWLIST_SPECIFIC';
|
||||
|
||||
export const FilterState: { [K in FilterStateValue]: FilterStateValue } = {
|
||||
export const FilterState = {
|
||||
DO_NOT_FILTER: 'DO_NOT_FILTER',
|
||||
DENYLIST_SPECIFIC: 'DENYLIST_SPECIFIC',
|
||||
ALLOWLIST_SPECIFIC: 'ALLOWLIST_SPECIFIC',
|
||||
BLACKLIST_SPECIFIC: 'BLACKLIST_SPECIFIC',
|
||||
WHITELIST_SPECIFIC: 'WHITELIST_SPECIFIC',
|
||||
};
|
||||
|
||||
export const noFiltersApplied = (localFilter: LocalFilter | undefined) =>
|
||||
export function getLocalFilter(config) {
|
||||
if (config.actionsBlacklist || config.actionsWhitelist) {
|
||||
return {
|
||||
whitelist: Array.isArray(config.actionsWhitelist)
|
||||
? config.actionsWhitelist.join('|')
|
||||
: config.actionsWhitelist,
|
||||
blacklist: Array.isArray(config.actionsBlacklist)
|
||||
? config.actionsBlacklist.join('|')
|
||||
: config.actionsBlacklist,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export const noFiltersApplied = (localFilter) =>
|
||||
// !predicate &&
|
||||
!localFilter &&
|
||||
(!window.devToolsOptions ||
|
||||
!window.devToolsOptions.filter ||
|
||||
window.devToolsOptions.filter === FilterState.DO_NOT_FILTER);
|
||||
|
||||
export function isFiltered<A extends Action<string>>(
|
||||
action: A | string,
|
||||
localFilter: LocalFilter | undefined,
|
||||
) {
|
||||
export function isFiltered(action, localFilter) {
|
||||
if (
|
||||
noFiltersApplied(localFilter) ||
|
||||
(typeof action !== 'string' && typeof action.type.match !== 'function')
|
||||
|
@ -31,34 +35,23 @@ export function isFiltered<A extends Action<string>>(
|
|||
return false;
|
||||
}
|
||||
|
||||
const { allowlist, denylist } = localFilter || window.devToolsOptions || {};
|
||||
const actionType = ((action as A).type || action) as string;
|
||||
const { whitelist, blacklist } = localFilter || window.devToolsOptions || {};
|
||||
const actionType = action.type || action;
|
||||
return (
|
||||
(allowlist && !actionType.match(allowlist)) ||
|
||||
(denylist && actionType.match(denylist))
|
||||
(whitelist && !actionType.match(whitelist)) ||
|
||||
(blacklist && actionType.match(blacklist))
|
||||
);
|
||||
}
|
||||
|
||||
function filterActions<A extends Action<string>>(
|
||||
actionsById: { [p: number]: PerformAction<A> },
|
||||
actionSanitizer: ((action: A, id: number) => A) | undefined,
|
||||
): { [p: number]: PerformAction<A> } {
|
||||
function filterActions(actionsById, actionSanitizer) {
|
||||
if (!actionSanitizer) return actionsById;
|
||||
return Object.fromEntries(
|
||||
Object.entries(actionsById).map(([actionId, action]) => [
|
||||
actionId,
|
||||
{
|
||||
...action,
|
||||
action: actionSanitizer(action.action, actionId as unknown as number),
|
||||
},
|
||||
]),
|
||||
);
|
||||
return mapValues(actionsById, (action, id) => ({
|
||||
...action,
|
||||
action: actionSanitizer(action.action, id),
|
||||
}));
|
||||
}
|
||||
|
||||
function filterStates<S>(
|
||||
computedStates: { state: S; error?: string | undefined }[],
|
||||
stateSanitizer: ((state: S, index: number) => S) | undefined,
|
||||
) {
|
||||
function filterStates(computedStates, stateSanitizer) {
|
||||
if (!stateSanitizer) return computedStates;
|
||||
return computedStates.map((state, idx) => ({
|
||||
...state,
|
||||
|
@ -66,19 +59,23 @@ function filterStates<S>(
|
|||
}));
|
||||
}
|
||||
|
||||
export function filterState<S, A extends Action<string>>(
|
||||
state: LiftedState<S, A, unknown>,
|
||||
localFilter: LocalFilter | undefined,
|
||||
stateSanitizer: ((state: S, index: number) => S) | undefined,
|
||||
actionSanitizer: ((action: A, id: number) => A) | undefined,
|
||||
predicate: ((state: S, action: A) => boolean) | undefined,
|
||||
): LiftedState<S, A, unknown> {
|
||||
export function filterState(
|
||||
state,
|
||||
type,
|
||||
localFilter,
|
||||
stateSanitizer,
|
||||
actionSanitizer,
|
||||
nextActionId,
|
||||
predicate
|
||||
) {
|
||||
if (type === 'ACTION') {
|
||||
return !stateSanitizer ? state : stateSanitizer(state, nextActionId - 1);
|
||||
} else if (type !== 'STATE') return state;
|
||||
|
||||
if (predicate || !noFiltersApplied(localFilter)) {
|
||||
const filteredStagedActionIds: number[] = [];
|
||||
const filteredComputedStates: { state: S; error?: string | undefined }[] =
|
||||
[];
|
||||
const sanitizedActionsById: { [p: number]: PerformAction<A> } | undefined =
|
||||
actionSanitizer && {};
|
||||
const filteredStagedActionIds = [];
|
||||
const filteredComputedStates = [];
|
||||
const sanitizedActionsById = actionSanitizer && {};
|
||||
const { actionsById } = state;
|
||||
const { computedStates } = state;
|
||||
|
||||
|
@ -97,10 +94,10 @@ export function filterState<S, A extends Action<string>>(
|
|||
filteredComputedStates.push(
|
||||
stateSanitizer
|
||||
? { ...liftedState, state: stateSanitizer(currState, idx) }
|
||||
: liftedState,
|
||||
: liftedState
|
||||
);
|
||||
if (actionSanitizer) {
|
||||
sanitizedActionsById![id] = {
|
||||
sanitizedActionsById[id] = {
|
||||
...liftedAction,
|
||||
action: actionSanitizer(currAction, id),
|
||||
};
|
||||
|
@ -123,27 +120,14 @@ export function filterState<S, A extends Action<string>>(
|
|||
};
|
||||
}
|
||||
|
||||
export interface PartialLiftedState<S, A extends Action<string>> {
|
||||
readonly actionsById: { [actionId: number]: PerformAction<A> };
|
||||
readonly computedStates: { state: S; error?: string }[];
|
||||
readonly stagedActionIds: readonly number[];
|
||||
readonly currentStateIndex: number;
|
||||
readonly nextActionId: number;
|
||||
readonly committedState?: S;
|
||||
}
|
||||
|
||||
export function startingFrom<S, A extends Action<string>>(
|
||||
sendingActionId: number,
|
||||
state: LiftedState<S, A, unknown>,
|
||||
localFilter: LocalFilter | undefined,
|
||||
stateSanitizer: (<S>(state: S, index: number) => S) | undefined,
|
||||
actionSanitizer:
|
||||
| (<A extends Action<string>>(action: A, id: number) => A)
|
||||
| undefined,
|
||||
predicate:
|
||||
| (<S, A extends Action<string>>(state: S, action: A) => boolean)
|
||||
| undefined,
|
||||
): LiftedState<S, A, unknown> | PartialLiftedState<S, A> | undefined {
|
||||
export function startingFrom(
|
||||
sendingActionId,
|
||||
state,
|
||||
localFilter,
|
||||
stateSanitizer,
|
||||
actionSanitizer,
|
||||
predicate
|
||||
) {
|
||||
const stagedActionIds = state.stagedActionIds;
|
||||
if (sendingActionId <= stagedActionIds[1]) return state;
|
||||
const index = stagedActionIds.indexOf(sendingActionId);
|
||||
|
@ -153,7 +137,7 @@ export function startingFrom<S, A extends Action<string>>(
|
|||
const filteredStagedActionIds = shouldFilter ? [0] : stagedActionIds;
|
||||
const actionsById = state.actionsById;
|
||||
const computedStates = state.computedStates;
|
||||
const newActionsById: { [key: number]: PerformAction<A> } = {};
|
||||
const newActionsById = {};
|
||||
const newComputedStates = [];
|
||||
let key;
|
||||
let currAction;
|
||||
|
@ -181,7 +165,7 @@ export function startingFrom<S, A extends Action<string>>(
|
|||
newComputedStates.push(
|
||||
!stateSanitizer
|
||||
? currState
|
||||
: { ...currState, state: stateSanitizer(currState.state, i) },
|
||||
: { ...currState, state: stateSanitizer(currState.state, i) }
|
||||
);
|
||||
}
|
||||
|
5
extension/src/app/api/generateInstanceId.js
Normal file
5
extension/src/app/api/generateInstanceId.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
let id = 0;
|
||||
|
||||
export default function generateId(instanceId) {
|
||||
return instanceId || ++id;
|
||||
}
|
74
extension/src/app/api/importState.js
Normal file
74
extension/src/app/api/importState.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
import mapValues from 'lodash/mapValues';
|
||||
import jsan from 'jsan';
|
||||
import seralizeImmutable from 'remotedev-serialize/immutable/serialize';
|
||||
|
||||
function deprecate(param) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`\`${param}\` parameter for Redux DevTools Extension is deprecated. Use \`serialize\` parameter instead: https://github.com/zalmoxisus/redux-devtools-extension/releases/tag/v2.12.1`
|
||||
);
|
||||
}
|
||||
|
||||
export default function importState(
|
||||
state,
|
||||
{ deserializeState, deserializeAction, serialize }
|
||||
) {
|
||||
if (!state) return undefined;
|
||||
let parse = jsan.parse;
|
||||
if (serialize) {
|
||||
if (serialize.immutable) {
|
||||
parse = (v) =>
|
||||
jsan.parse(
|
||||
v,
|
||||
seralizeImmutable(
|
||||
serialize.immutable,
|
||||
serialize.refs,
|
||||
serialize.replacer,
|
||||
serialize.reviver
|
||||
).reviver
|
||||
);
|
||||
} else if (serialize.reviver) {
|
||||
parse = (v) => jsan.parse(v, serialize.reviver);
|
||||
}
|
||||
}
|
||||
|
||||
let preloadedState;
|
||||
let nextLiftedState = parse(state);
|
||||
if (nextLiftedState.payload) {
|
||||
if (nextLiftedState.preloadedState) {
|
||||
preloadedState = parse(nextLiftedState.preloadedState);
|
||||
}
|
||||
nextLiftedState = parse(nextLiftedState.payload);
|
||||
}
|
||||
if (deserializeState) {
|
||||
deprecate('deserializeState');
|
||||
if (typeof nextLiftedState.computedStates !== 'undefined') {
|
||||
nextLiftedState.computedStates = nextLiftedState.computedStates.map(
|
||||
(computedState) => ({
|
||||
...computedState,
|
||||
state: deserializeState(computedState.state),
|
||||
})
|
||||
);
|
||||
}
|
||||
if (typeof nextLiftedState.committedState !== 'undefined') {
|
||||
nextLiftedState.committedState = deserializeState(
|
||||
nextLiftedState.committedState
|
||||
);
|
||||
}
|
||||
if (typeof preloadedState !== 'undefined') {
|
||||
preloadedState = deserializeState(preloadedState);
|
||||
}
|
||||
}
|
||||
if (deserializeAction) {
|
||||
deprecate('deserializeAction');
|
||||
nextLiftedState.actionsById = mapValues(
|
||||
nextLiftedState.actionsById,
|
||||
(liftedAction) => ({
|
||||
...liftedAction,
|
||||
action: deserializeAction(liftedAction.action),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return { nextLiftedState, preloadedState };
|
||||
}
|
397
extension/src/app/api/index.js
Normal file
397
extension/src/app/api/index.js
Normal file
|
@ -0,0 +1,397 @@
|
|||
import jsan from 'jsan';
|
||||
import throttle from 'lodash/throttle';
|
||||
import seralizeImmutable from 'remotedev-serialize/immutable/serialize';
|
||||
import { getActionsArray } from 'remotedev-utils';
|
||||
import { getLocalFilter, isFiltered } from './filters';
|
||||
import importState from './importState';
|
||||
import generateId from './generateInstanceId';
|
||||
|
||||
const listeners = {};
|
||||
export const source = '@devtools-page';
|
||||
|
||||
function windowReplacer(key, value) {
|
||||
if (value && value.window === value) {
|
||||
return '[WINDOW]';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function tryCatchStringify(obj) {
|
||||
try {
|
||||
return JSON.stringify(obj);
|
||||
} catch (err) {
|
||||
/* eslint-disable no-console */
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.log('Failed to stringify', err);
|
||||
}
|
||||
/* eslint-enable no-console */
|
||||
return jsan.stringify(obj, windowReplacer, null, {
|
||||
circular: '[CIRCULAR]',
|
||||
date: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let stringifyWarned;
|
||||
function stringify(obj, serialize) {
|
||||
const str =
|
||||
typeof serialize === 'undefined'
|
||||
? tryCatchStringify(obj)
|
||||
: jsan.stringify(obj, serialize.replacer, null, serialize.options);
|
||||
|
||||
if (!stringifyWarned && str && str.length > 16 * 1024 * 1024) {
|
||||
// 16 MB
|
||||
/* eslint-disable no-console */
|
||||
console.warn(
|
||||
'Application state or actions payloads are too large making Redux DevTools serialization slow and consuming a lot of memory. See https://git.io/fpcP5 on how to configure it.'
|
||||
);
|
||||
/* eslint-enable no-console */
|
||||
stringifyWarned = true;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
export function getSeralizeParameter(config, param) {
|
||||
const serialize = config.serialize;
|
||||
if (serialize) {
|
||||
if (serialize === true) return { options: true };
|
||||
if (serialize.immutable) {
|
||||
const immutableSerializer = seralizeImmutable(
|
||||
serialize.immutable,
|
||||
serialize.refs,
|
||||
serialize.replacer,
|
||||
serialize.reviver
|
||||
);
|
||||
return {
|
||||
replacer: immutableSerializer.replacer,
|
||||
reviver: immutableSerializer.reviver,
|
||||
options:
|
||||
typeof serialize.options === 'object'
|
||||
? { ...immutableSerializer.options, ...serialize.options }
|
||||
: immutableSerializer.options,
|
||||
};
|
||||
}
|
||||
if (!serialize.replacer && !serialize.reviver) {
|
||||
return { options: serialize.options };
|
||||
}
|
||||
return {
|
||||
replacer: serialize.replacer,
|
||||
reviver: serialize.reviver,
|
||||
options: serialize.options || true,
|
||||
};
|
||||
}
|
||||
|
||||
const value = config[param];
|
||||
if (typeof value === 'undefined') return undefined;
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`\`${param}\` parameter for Redux DevTools Extension is deprecated. Use \`serialize\` parameter instead: https://github.com/zalmoxisus/redux-devtools-extension/releases/tag/v2.12.1`
|
||||
);
|
||||
|
||||
if (typeof serializeState === 'boolean') return { options: value };
|
||||
if (typeof serializeState === 'function') return { replacer: value };
|
||||
return value;
|
||||
}
|
||||
|
||||
function post(message) {
|
||||
window.postMessage(message, '*');
|
||||
}
|
||||
|
||||
function getStackTrace(config, toExcludeFromTrace) {
|
||||
if (!config.trace) return undefined;
|
||||
if (typeof config.trace === 'function') return config.trace();
|
||||
|
||||
let stack;
|
||||
let extraFrames = 0;
|
||||
let prevStackTraceLimit;
|
||||
const traceLimit = config.traceLimit;
|
||||
const error = Error();
|
||||
if (Error.captureStackTrace) {
|
||||
if (Error.stackTraceLimit < traceLimit) {
|
||||
prevStackTraceLimit = Error.stackTraceLimit;
|
||||
Error.stackTraceLimit = traceLimit;
|
||||
}
|
||||
Error.captureStackTrace(error, toExcludeFromTrace);
|
||||
} else {
|
||||
extraFrames = 3;
|
||||
}
|
||||
stack = error.stack;
|
||||
if (prevStackTraceLimit) Error.stackTraceLimit = prevStackTraceLimit;
|
||||
if (
|
||||
extraFrames ||
|
||||
typeof Error.stackTraceLimit !== 'number' ||
|
||||
Error.stackTraceLimit > traceLimit
|
||||
) {
|
||||
const frames = stack.split('\n');
|
||||
if (frames.length > traceLimit) {
|
||||
stack = frames
|
||||
.slice(0, traceLimit + extraFrames + (frames[0] === 'Error' ? 1 : 0))
|
||||
.join('\n');
|
||||
}
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
function amendActionType(action, config, toExcludeFromTrace) {
|
||||
let timestamp = Date.now();
|
||||
let stack = getStackTrace(config, toExcludeFromTrace);
|
||||
if (typeof action === 'string') {
|
||||
return { action: { type: action }, timestamp, stack };
|
||||
}
|
||||
if (!action.type) return { action: { type: 'update' }, timestamp, stack };
|
||||
if (action.action) return stack ? { stack, ...action } : action;
|
||||
return { action, timestamp, stack };
|
||||
}
|
||||
|
||||
export function toContentScript(message, serializeState, serializeAction) {
|
||||
if (message.type === 'ACTION') {
|
||||
message.action = stringify(message.action, serializeAction);
|
||||
message.payload = stringify(message.payload, serializeState);
|
||||
} else if (message.type === 'STATE' || message.type === 'PARTIAL_STATE') {
|
||||
const {
|
||||
actionsById,
|
||||
computedStates,
|
||||
committedState,
|
||||
...rest
|
||||
} = message.payload;
|
||||
message.payload = rest;
|
||||
message.actionsById = stringify(actionsById, serializeAction);
|
||||
message.computedStates = stringify(computedStates, serializeState);
|
||||
message.committedState = typeof committedState !== 'undefined';
|
||||
} else if (message.type === 'EXPORT') {
|
||||
message.payload = stringify(message.payload, serializeAction);
|
||||
if (typeof message.committedState !== 'undefined') {
|
||||
message.committedState = stringify(
|
||||
message.committedState,
|
||||
serializeState
|
||||
);
|
||||
}
|
||||
}
|
||||
post(message);
|
||||
}
|
||||
|
||||
export function sendMessage(action, state, config, instanceId, name) {
|
||||
let amendedAction = action;
|
||||
if (typeof config !== 'object') {
|
||||
// Legacy: sending actions not from connected part
|
||||
config = {}; // eslint-disable-line no-param-reassign
|
||||
if (action) amendedAction = amendActionType(action, config, sendMessage);
|
||||
}
|
||||
const message = {
|
||||
type: action ? 'ACTION' : 'STATE',
|
||||
action: amendedAction,
|
||||
payload: state,
|
||||
maxAge: config.maxAge,
|
||||
source,
|
||||
name: config.name || name,
|
||||
instanceId: config.instanceId || instanceId || 1,
|
||||
};
|
||||
toContentScript(message, config.serialize, config.serialize);
|
||||
}
|
||||
|
||||
function handleMessages(event) {
|
||||
if (process.env.BABEL_ENV !== 'test' && (!event || event.source !== window)) {
|
||||
return;
|
||||
}
|
||||
const message = event.data;
|
||||
if (!message || message.source !== '@devtools-extension') return;
|
||||
Object.keys(listeners).forEach((id) => {
|
||||
if (message.id && id !== message.id) return;
|
||||
if (typeof listeners[id] === 'function') listeners[id](message);
|
||||
else {
|
||||
listeners[id].forEach((fn) => {
|
||||
fn(message);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function setListener(onMessage, instanceId) {
|
||||
listeners[instanceId] = onMessage;
|
||||
window.addEventListener('message', handleMessages, false);
|
||||
}
|
||||
|
||||
const liftListener = (listener, config) => (message) => {
|
||||
let data = {};
|
||||
if (message.type === 'IMPORT') {
|
||||
data.type = 'DISPATCH';
|
||||
data.payload = {
|
||||
type: 'IMPORT_STATE',
|
||||
...importState(message.state, config),
|
||||
};
|
||||
} else {
|
||||
data = message;
|
||||
}
|
||||
listener(data);
|
||||
};
|
||||
|
||||
export function disconnect() {
|
||||
window.removeEventListener('message', handleMessages);
|
||||
post({ type: 'DISCONNECT', source });
|
||||
}
|
||||
|
||||
export function connect(preConfig) {
|
||||
const config = preConfig || {};
|
||||
const id = generateId(config.instanceId);
|
||||
if (!config.instanceId) config.instanceId = id;
|
||||
if (!config.name) {
|
||||
config.name =
|
||||
document.title && id === 1 ? document.title : `Instance ${id}`;
|
||||
}
|
||||
if (config.serialize) config.serialize = getSeralizeParameter(config);
|
||||
const actionCreators = config.actionCreators || {};
|
||||
const latency = config.latency;
|
||||
const predicate = config.predicate;
|
||||
const localFilter = getLocalFilter(config);
|
||||
const autoPause = config.autoPause;
|
||||
let isPaused = autoPause;
|
||||
let delayedActions = [];
|
||||
let delayedStates = [];
|
||||
|
||||
const rootListiner = (action) => {
|
||||
if (autoPause) {
|
||||
if (action.type === 'START') isPaused = false;
|
||||
else if (action.type === 'STOP') isPaused = true;
|
||||
}
|
||||
if (action.type === 'DISPATCH') {
|
||||
const payload = action.payload;
|
||||
if (payload.type === 'PAUSE_RECORDING') {
|
||||
isPaused = payload.status;
|
||||
toContentScript({
|
||||
type: 'LIFTED',
|
||||
liftedState: { isPaused },
|
||||
instanceId: id,
|
||||
source,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
listeners[id] = [rootListiner];
|
||||
|
||||
const subscribe = (listener) => {
|
||||
if (!listener) return undefined;
|
||||
const liftedListener = liftListener(listener, config);
|
||||
listeners[id].push(liftedListener);
|
||||
|
||||
return function unsubscribe() {
|
||||
const index = listeners[id].indexOf(liftedListener);
|
||||
listeners[id].splice(index, 1);
|
||||
};
|
||||
};
|
||||
|
||||
const unsubscribe = () => {
|
||||
delete listeners[id];
|
||||
};
|
||||
|
||||
const sendDelayed = throttle(() => {
|
||||
sendMessage(delayedActions, delayedStates, config);
|
||||
delayedActions = [];
|
||||
delayedStates = [];
|
||||
}, latency);
|
||||
|
||||
const send = (action, state) => {
|
||||
if (
|
||||
isPaused ||
|
||||
isFiltered(action, localFilter) ||
|
||||
(predicate && !predicate(state, action))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let amendedAction = action;
|
||||
const amendedState = config.stateSanitizer
|
||||
? config.stateSanitizer(state)
|
||||
: state;
|
||||
if (action) {
|
||||
if (config.getActionType) {
|
||||
amendedAction = config.getActionType(action);
|
||||
if (typeof amendedAction !== 'object') {
|
||||
amendedAction = {
|
||||
action: { type: amendedAction },
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
}
|
||||
} else if (config.actionSanitizer) {
|
||||
amendedAction = config.actionSanitizer(action);
|
||||
}
|
||||
amendedAction = amendActionType(amendedAction, config, send);
|
||||
if (latency) {
|
||||
delayedActions.push(amendedAction);
|
||||
delayedStates.push(amendedState);
|
||||
sendDelayed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
sendMessage(amendedAction, amendedState, config);
|
||||
};
|
||||
|
||||
const init = (state, liftedData) => {
|
||||
const message = {
|
||||
type: 'INIT',
|
||||
payload: stringify(state, config.serialize),
|
||||
instanceId: id,
|
||||
source,
|
||||
};
|
||||
if (liftedData && Array.isArray(liftedData)) {
|
||||
// Legacy
|
||||
message.action = stringify(liftedData);
|
||||
message.name = config.name;
|
||||
} else {
|
||||
if (liftedData) {
|
||||
message.liftedState = liftedData;
|
||||
if (liftedData.isPaused) isPaused = true;
|
||||
}
|
||||
message.libConfig = {
|
||||
actionCreators: JSON.stringify(getActionsArray(actionCreators)),
|
||||
name: config.name || document.title,
|
||||
features: config.features,
|
||||
serialize: !!config.serialize,
|
||||
type: config.type,
|
||||
};
|
||||
}
|
||||
post(message);
|
||||
};
|
||||
|
||||
const error = (payload) => {
|
||||
post({ type: 'ERROR', payload, id, source });
|
||||
};
|
||||
|
||||
window.addEventListener('message', handleMessages, false);
|
||||
|
||||
post({ type: 'INIT_INSTANCE', instanceId: id, source });
|
||||
|
||||
return {
|
||||
init,
|
||||
subscribe,
|
||||
unsubscribe,
|
||||
send,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateStore(stores) {
|
||||
return function (newStore, instanceId) {
|
||||
/* eslint-disable no-console */
|
||||
console.warn(
|
||||
'`__REDUX_DEVTOOLS_EXTENSION__.updateStore` is deprecated, remove it and just use ' +
|
||||
"`__REDUX_DEVTOOLS_EXTENSION_COMPOSE__` instead of the extension's store enhancer: " +
|
||||
'https://github.com/zalmoxisus/redux-devtools-extension#12-advanced-store-setup'
|
||||
);
|
||||
/* eslint-enable no-console */
|
||||
const store = stores[instanceId || Object.keys(stores)[0]];
|
||||
// Mutate the store in order to keep the reference
|
||||
store.liftedStore = newStore.liftedStore;
|
||||
store.getState = newStore.getState;
|
||||
store.dispatch = newStore.dispatch;
|
||||
};
|
||||
}
|
||||
|
||||
export function isInIframe() {
|
||||
try {
|
||||
return window.self !== window.top;
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,16 +1,16 @@
|
|||
let handleError: (() => boolean) | undefined;
|
||||
let handleError;
|
||||
let lastTime = 0;
|
||||
|
||||
function createExpBackoffTimer(step: number) {
|
||||
function createExpBackoffTimer(step) {
|
||||
let count = 1;
|
||||
return function (reset?: boolean) {
|
||||
return function (reset) {
|
||||
// Reset call
|
||||
if (reset) {
|
||||
count = 1;
|
||||
return 0;
|
||||
}
|
||||
// Calculate next timeout
|
||||
const timeout = Math.pow(2, count - 1);
|
||||
let timeout = Math.pow(2, count - 1);
|
||||
if (count < 5) count += 1;
|
||||
return timeout * step;
|
||||
};
|
||||
|
@ -18,7 +18,7 @@ function createExpBackoffTimer(step: number) {
|
|||
|
||||
const nextErrorTimeout = createExpBackoffTimer(5000);
|
||||
|
||||
function postError(message: string) {
|
||||
function postError(message) {
|
||||
if (handleError && !handleError()) return;
|
||||
window.postMessage(
|
||||
{
|
||||
|
@ -26,11 +26,11 @@ function postError(message: string) {
|
|||
type: 'ERROR',
|
||||
message: message,
|
||||
},
|
||||
'*',
|
||||
'*'
|
||||
);
|
||||
}
|
||||
|
||||
function catchErrors(e: ErrorEvent) {
|
||||
function catchErrors(e) {
|
||||
if (
|
||||
(window.devToolsOptions && !window.devToolsOptions.shouldCatchErrors) ||
|
||||
e.timeStamp - lastTime < nextErrorTimeout()
|
||||
|
@ -42,7 +42,7 @@ function catchErrors(e: ErrorEvent) {
|
|||
postError(e.message);
|
||||
}
|
||||
|
||||
export default function notifyErrors(onError?: () => boolean) {
|
||||
export default function notifyErrors(onError) {
|
||||
handleError = onError;
|
||||
window.addEventListener('error', catchErrors, false);
|
||||
}
|
10
extension/src/app/api/openWindow.js
Normal file
10
extension/src/app/api/openWindow.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
export default function openWindow(position) {
|
||||
window.postMessage(
|
||||
{
|
||||
source: '@devtools-page',
|
||||
type: 'OPEN',
|
||||
position: position || 'right',
|
||||
},
|
||||
'*'
|
||||
);
|
||||
}
|
202
extension/src/app/containers/App.js
Normal file
202
extension/src/app/containers/App.js
Normal file
|
@ -0,0 +1,202 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import SliderMonitor from 'remotedev-slider/lib/Slider';
|
||||
import { liftedDispatch, getReport } from 'remotedev-app/lib/actions';
|
||||
import { getActiveInstance } from 'remotedev-app/lib/reducers/instances';
|
||||
import styles from 'remotedev-app/lib/styles';
|
||||
import enhance from 'remotedev-app/lib/hoc';
|
||||
import DevTools from 'remotedev-app/lib/containers/DevTools';
|
||||
import Dispatcher from 'remotedev-app/lib/containers/monitors/Dispatcher';
|
||||
import MonitorSelector from 'remotedev-app/lib/components/MonitorSelector';
|
||||
import Notification from 'remotedev-app/lib/components/Notification';
|
||||
import Instances from 'remotedev-app/lib/components/Instances';
|
||||
import Button from 'remotedev-app/lib/components/Button';
|
||||
import RecordButton from 'remotedev-app/lib/components/buttons/RecordButton';
|
||||
import LockButton from 'remotedev-app/lib/components/buttons/LockButton';
|
||||
import DispatcherButton from 'remotedev-app/lib/components/buttons/DispatcherButton';
|
||||
import SliderButton from 'remotedev-app/lib/components/buttons/SliderButton';
|
||||
import ImportButton from 'remotedev-app/lib/components/buttons/ImportButton';
|
||||
import ExportButton from 'remotedev-app/lib/components/buttons/ExportButton';
|
||||
import PrintButton from 'remotedev-app/lib/components/buttons/PrintButton';
|
||||
import {
|
||||
MdSettings,
|
||||
MdBorderLeft,
|
||||
MdBorderRight,
|
||||
MdBorderBottom,
|
||||
} from 'react-icons/md';
|
||||
import { GoRadioTower, GoPin } from 'react-icons/go';
|
||||
|
||||
@enhance
|
||||
class App extends Component {
|
||||
openWindow = (position) => {
|
||||
chrome.runtime.sendMessage({ type: 'OPEN', position });
|
||||
};
|
||||
openOptionsPage = () => {
|
||||
if (navigator.userAgent.indexOf('Firefox') !== -1) {
|
||||
chrome.runtime.sendMessage({ type: 'OPEN_OPTIONS' });
|
||||
} else {
|
||||
chrome.runtime.openOptionsPage();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
monitor,
|
||||
position,
|
||||
togglePersist,
|
||||
dispatcherIsOpen,
|
||||
sliderIsOpen,
|
||||
options,
|
||||
liftedState,
|
||||
} = this.props;
|
||||
if (!position && (!options || !options.features)) {
|
||||
return (
|
||||
<div style={{ padding: '20px', width: '100%', textAlign: 'center' }}>
|
||||
No store found. Make sure to follow{' '}
|
||||
<a
|
||||
href="https://github.com/zalmoxisus/redux-devtools-extension#usage"
|
||||
target="_blank"
|
||||
>
|
||||
the instructions
|
||||
</a>
|
||||
.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const features = options.features || {};
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
<div style={styles.buttonBar}>
|
||||
<MonitorSelector selected={monitor} />
|
||||
<Instances selected={this.props.selected} />
|
||||
</div>
|
||||
<DevTools
|
||||
monitor={monitor}
|
||||
liftedState={liftedState}
|
||||
monitorState={this.props.monitorState}
|
||||
dispatch={this.props.liftedDispatch}
|
||||
lib={options.lib || options.explicitLib}
|
||||
/>
|
||||
<Notification />
|
||||
{sliderIsOpen && options.connectionId && options.features.jump && (
|
||||
<SliderMonitor
|
||||
monitor="SliderMonitor"
|
||||
liftedState={liftedState}
|
||||
dispatch={this.props.liftedDispatch}
|
||||
getReport={this.props.getReport}
|
||||
reports={this.props.reports}
|
||||
showActions={monitor === 'ChartMonitor'}
|
||||
style={{ padding: '15px 5px' }}
|
||||
fillColor="rgb(120, 144, 156)"
|
||||
/>
|
||||
)}
|
||||
{dispatcherIsOpen &&
|
||||
options.connectionId &&
|
||||
options.features.dispatch && <Dispatcher options={options} />}
|
||||
<div style={styles.buttonBar}>
|
||||
{!window.isElectron && position !== '#left' && (
|
||||
<Button
|
||||
Icon={MdBorderLeft}
|
||||
onClick={() => {
|
||||
this.openWindow('left');
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!window.isElectron && position !== '#right' && (
|
||||
<Button
|
||||
Icon={MdBorderRight}
|
||||
onClick={() => {
|
||||
this.openWindow('right');
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!window.isElectron && position !== '#bottom' && (
|
||||
<Button
|
||||
Icon={MdBorderBottom}
|
||||
onClick={() => {
|
||||
this.openWindow('bottom');
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{features.pause && <RecordButton paused={liftedState.isPaused} />}
|
||||
{features.lock && <LockButton locked={liftedState.isLocked} />}
|
||||
{features.persist && (
|
||||
<Button Icon={GoPin} onClick={togglePersist}>
|
||||
Persist
|
||||
</Button>
|
||||
)}
|
||||
{features.dispatch && (
|
||||
<DispatcherButton dispatcherIsOpen={dispatcherIsOpen} />
|
||||
)}
|
||||
{features.jump && <SliderButton isOpen={sliderIsOpen} />}
|
||||
{features.import && <ImportButton />}
|
||||
{features.export && <ExportButton />}
|
||||
{position &&
|
||||
(position !== '#popup' ||
|
||||
navigator.userAgent.indexOf('Firefox') !== -1) && <PrintButton />}
|
||||
{!window.isElectron && (
|
||||
<Button
|
||||
Icon={GoRadioTower}
|
||||
onClick={() => {
|
||||
this.openWindow('remote');
|
||||
}}
|
||||
>
|
||||
Remote
|
||||
</Button>
|
||||
)}
|
||||
{(chrome.runtime.openOptionsPage ||
|
||||
navigator.userAgent.indexOf('Firefox') !== -1) && (
|
||||
<Button Icon={MdSettings} onClick={this.openOptionsPage}>
|
||||
Settings
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
App.propTypes = {
|
||||
bgStore: PropTypes.object,
|
||||
liftedDispatch: PropTypes.func.isRequired,
|
||||
getReport: PropTypes.func.isRequired,
|
||||
togglePersist: PropTypes.func.isRequired,
|
||||
selected: PropTypes.string,
|
||||
liftedState: PropTypes.object.isRequired,
|
||||
monitorState: PropTypes.object,
|
||||
options: PropTypes.object.isRequired,
|
||||
monitor: PropTypes.string,
|
||||
position: PropTypes.string,
|
||||
reports: PropTypes.array.isRequired,
|
||||
dispatcherIsOpen: PropTypes.bool,
|
||||
sliderIsOpen: PropTypes.bool,
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const instances = state.instances;
|
||||
const id = getActiveInstance(instances);
|
||||
return {
|
||||
selected: instances.selected,
|
||||
liftedState: instances.states[id],
|
||||
monitorState: state.monitor.monitorState,
|
||||
options: instances.options[id],
|
||||
monitor: state.monitor.selected,
|
||||
dispatcherIsOpen: state.monitor.dispatcherIsOpen,
|
||||
sliderIsOpen: state.monitor.sliderIsOpen,
|
||||
reports: state.reports.data,
|
||||
shouldSync: state.instances.sync,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
liftedDispatch: bindActionCreators(liftedDispatch, dispatch),
|
||||
getReport: bindActionCreators(getReport, dispatch),
|
||||
togglePersist: () => {
|
||||
dispatch({ type: 'TOGGLE_PERSIST' });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(App);
|
264
extension/src/app/middlewares/api.js
Normal file
264
extension/src/app/middlewares/api.js
Normal file
|
@ -0,0 +1,264 @@
|
|||
import stringifyJSON from 'remotedev-app/lib/utils/stringifyJSON';
|
||||
import {
|
||||
UPDATE_STATE,
|
||||
REMOVE_INSTANCE,
|
||||
LIFTED_ACTION,
|
||||
} from 'remotedev-app/lib/constants/actionTypes';
|
||||
import { nonReduxDispatch } from 'remotedev-app/lib/utils/monitorActions';
|
||||
import syncOptions from '../../browser/extension/options/syncOptions';
|
||||
import openDevToolsWindow from '../../browser/extension/background/openWindow';
|
||||
import { getReport } from '../../browser/extension/background/logging';
|
||||
|
||||
const CONNECTED = 'socket/CONNECTED';
|
||||
const DISCONNECTED = 'socket/DISCONNECTED';
|
||||
const connections = {
|
||||
tab: {},
|
||||
panel: {},
|
||||
monitor: {},
|
||||
};
|
||||
const chunks = {};
|
||||
let monitors = 0;
|
||||
let isMonitored = false;
|
||||
|
||||
const getId = (sender, name) =>
|
||||
sender.tab ? sender.tab.id : name || sender.id;
|
||||
|
||||
function toMonitors(action, tabId, verbose) {
|
||||
Object.keys(connections.monitor).forEach((id) => {
|
||||
connections.monitor[id].postMessage(
|
||||
verbose || action.type === 'ERROR' ? action : { type: UPDATE_STATE }
|
||||
);
|
||||
});
|
||||
Object.keys(connections.panel).forEach((id) => {
|
||||
connections.panel[id].postMessage(action);
|
||||
});
|
||||
}
|
||||
|
||||
function toContentScript({ message, action, id, instanceId, state }) {
|
||||
connections.tab[id].postMessage({
|
||||
type: message,
|
||||
action,
|
||||
state: nonReduxDispatch(window.store, message, instanceId, action, state),
|
||||
id: instanceId.toString().replace(/^[^\/]+\//, ''),
|
||||
});
|
||||
}
|
||||
|
||||
function toAllTabs(msg) {
|
||||
const tabs = connections.tab;
|
||||
Object.keys(tabs).forEach((id) => {
|
||||
tabs[id].postMessage(msg);
|
||||
});
|
||||
}
|
||||
|
||||
function monitorInstances(shouldMonitor, id) {
|
||||
if (!id && isMonitored === shouldMonitor) return;
|
||||
const action = { type: shouldMonitor ? 'START' : 'STOP' };
|
||||
if (id) {
|
||||
if (connections.tab[id]) connections.tab[id].postMessage(action);
|
||||
} else {
|
||||
toAllTabs(action);
|
||||
}
|
||||
isMonitored = shouldMonitor;
|
||||
}
|
||||
|
||||
function getReducerError() {
|
||||
const instancesState = window.store.getState().instances;
|
||||
const payload = instancesState.states[instancesState.current];
|
||||
const computedState = payload.computedStates[payload.currentStateIndex];
|
||||
if (!computedState) return false;
|
||||
return computedState.error;
|
||||
}
|
||||
|
||||
function togglePersist() {
|
||||
const state = window.store.getState();
|
||||
if (state.persistStates) {
|
||||
Object.keys(state.instances.connections).forEach((id) => {
|
||||
if (connections.tab[id]) return;
|
||||
window.store.dispatch({ type: REMOVE_INSTANCE, id });
|
||||
toMonitors({ type: 'NA', id });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Receive messages from content scripts
|
||||
function messaging(request, sender, sendResponse) {
|
||||
let tabId = getId(sender);
|
||||
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 });
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (request.type === 'OPEN_OPTIONS') {
|
||||
chrome.runtime.openOptionsPage();
|
||||
return;
|
||||
}
|
||||
if (request.type === 'GET_OPTIONS') {
|
||||
window.syncOptions.get((options) => {
|
||||
sendResponse({ options });
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (request.type === 'GET_REPORT') {
|
||||
getReport(request.payload, tabId, request.instanceId);
|
||||
return;
|
||||
}
|
||||
if (request.type === 'OPEN') {
|
||||
let position = 'devtools-left';
|
||||
if (
|
||||
['remote', 'panel', 'left', 'right', 'bottom'].indexOf(
|
||||
request.position
|
||||
) !== -1
|
||||
) {
|
||||
position = 'devtools-' + request.position;
|
||||
}
|
||||
openDevToolsWindow(position);
|
||||
return;
|
||||
}
|
||||
if (request.type === 'ERROR') {
|
||||
if (request.payload) {
|
||||
toMonitors(request, tabId);
|
||||
return;
|
||||
}
|
||||
if (!request.message) return;
|
||||
const reducerError = getReducerError();
|
||||
chrome.notifications.create('app-error', {
|
||||
type: 'basic',
|
||||
title: reducerError
|
||||
? 'An error occurred in the reducer'
|
||||
: 'An error occurred in the app',
|
||||
message: reducerError || request.message,
|
||||
iconUrl: 'img/logo/48x48.png',
|
||||
isClickable: !!reducerError,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const action = { type: UPDATE_STATE, request, id: tabId };
|
||||
const instanceId = `${tabId}/${request.instanceId}`;
|
||||
if (request.split) {
|
||||
if (request.split === 'start') {
|
||||
chunks[instanceId] = request;
|
||||
return;
|
||||
}
|
||||
if (request.split === 'chunk') {
|
||||
chunks[instanceId][request.chunk[0]] =
|
||||
(chunks[instanceId][request.chunk[0]] || '') + request.chunk[1];
|
||||
return;
|
||||
}
|
||||
action.request = chunks[instanceId];
|
||||
delete chunks[instanceId];
|
||||
}
|
||||
if (request.instanceId) {
|
||||
action.request.instanceId = instanceId;
|
||||
}
|
||||
window.store.dispatch(action);
|
||||
|
||||
if (request.type === 'EXPORT') {
|
||||
toMonitors(action, tabId, true);
|
||||
} else {
|
||||
toMonitors(action, tabId);
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect(type, id, listener) {
|
||||
return function disconnectListener() {
|
||||
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().persistStates) {
|
||||
window.store.dispatch({ type: REMOVE_INSTANCE, id });
|
||||
toMonitors({ type: 'NA', id });
|
||||
}
|
||||
} else {
|
||||
monitors--;
|
||||
if (!monitors) monitorInstances(false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function onConnect(port) {
|
||||
let id;
|
||||
let listener;
|
||||
|
||||
window.store.dispatch({ type: CONNECTED, port });
|
||||
|
||||
if (port.name === 'tab') {
|
||||
id = getId(port.sender);
|
||||
if (port.sender.frameId) id = `${id}-${port.sender.frameId}`;
|
||||
connections.tab[id] = port;
|
||||
listener = (msg) => {
|
||||
if (msg.name === 'INIT_INSTANCE') {
|
||||
if (typeof id === 'number') {
|
||||
chrome.pageAction.show(id);
|
||||
chrome.pageAction.setIcon({ tabId: id, path: 'img/logo/38x38.png' });
|
||||
}
|
||||
if (isMonitored) port.postMessage({ type: 'START' });
|
||||
|
||||
const state = window.store.getState();
|
||||
if (state.persistStates) {
|
||||
const instanceId = `${id}/${msg.instanceId}`;
|
||||
const persistedState = state.instances.states[instanceId];
|
||||
if (!persistedState) return;
|
||||
toContentScript({
|
||||
message: 'IMPORT',
|
||||
id,
|
||||
instanceId,
|
||||
state: stringifyJSON(
|
||||
persistedState,
|
||||
state.instances.options[instanceId].serialize
|
||||
),
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (msg.name === 'RELAY') {
|
||||
messaging(msg.message, port.sender, id);
|
||||
}
|
||||
};
|
||||
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;
|
||||
connections.panel[id] = port;
|
||||
monitorInstances(true, port.name);
|
||||
monitors++;
|
||||
listener = (msg) => {
|
||||
window.store.dispatch(msg);
|
||||
};
|
||||
port.onMessage.addListener(listener);
|
||||
port.onDisconnect.addListener(disconnect('panel', id, listener));
|
||||
}
|
||||
}
|
||||
|
||||
chrome.runtime.onConnect.addListener(onConnect);
|
||||
chrome.runtime.onConnectExternal.addListener(onConnect);
|
||||
chrome.runtime.onMessage.addListener(messaging);
|
||||
chrome.runtime.onMessageExternal.addListener(messaging);
|
||||
|
||||
chrome.notifications.onClicked.addListener((id) => {
|
||||
chrome.notifications.clear(id);
|
||||
openDevToolsWindow('devtools-right');
|
||||
});
|
||||
|
||||
window.syncOptions = syncOptions(toAllTabs); // Expose to the options page
|
||||
|
||||
export default function api() {
|
||||
return (next) => (action) => {
|
||||
if (action.type === LIFTED_ACTION) toContentScript(action);
|
||||
else if (action.type === 'TOGGLE_PERSIST') togglePersist();
|
||||
return next(action);
|
||||
};
|
||||
}
|
41
extension/src/app/middlewares/instanceSelector.js
Normal file
41
extension/src/app/middlewares/instanceSelector.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
import {
|
||||
SELECT_INSTANCE,
|
||||
UPDATE_STATE,
|
||||
} from 'remotedev-app/lib/constants/actionTypes';
|
||||
|
||||
function selectInstance(tabId, store, next) {
|
||||
const instances = store.getState().instances;
|
||||
if (instances.current === 'default') return;
|
||||
const connections = instances.connections[tabId];
|
||||
if (connections && connections.length === 1) {
|
||||
next({ type: SELECT_INSTANCE, selected: connections[0] });
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentTabId(next) {
|
||||
chrome.tabs.query(
|
||||
{
|
||||
active: true,
|
||||
lastFocusedWindow: true,
|
||||
},
|
||||
(tabs) => {
|
||||
const tab = tabs[0];
|
||||
if (!tab) return;
|
||||
next(tab.id);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default function popupSelector(store) {
|
||||
return (next) => (action) => {
|
||||
const result = next(action);
|
||||
if (action.type === UPDATE_STATE) {
|
||||
if (chrome.devtools && chrome.devtools.inspectedWindow) {
|
||||
selectInstance(chrome.devtools.inspectedWindow.tabId, store, next);
|
||||
} else {
|
||||
getCurrentTabId((tabId) => selectInstance(tabId, store, next));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
31
extension/src/app/middlewares/panelSync.js
Normal file
31
extension/src/app/middlewares/panelSync.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
import {
|
||||
LIFTED_ACTION,
|
||||
UPDATE_STATE,
|
||||
SELECT_INSTANCE,
|
||||
} from 'remotedev-app/lib/constants/actionTypes';
|
||||
import { getActiveInstance } from 'remotedev-app/lib/reducers/instances';
|
||||
|
||||
function panelDispatcher(bgConnection) {
|
||||
let autoselected = false;
|
||||
const tabId = chrome.devtools.inspectedWindow.tabId;
|
||||
|
||||
return (store) => (next) => (action) => {
|
||||
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;
|
23
extension/src/app/middlewares/windowSync.js
Normal file
23
extension/src/app/middlewares/windowSync.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
import {
|
||||
UPDATE_STATE,
|
||||
LIFTED_ACTION,
|
||||
} from 'remotedev-app/lib/constants/actionTypes';
|
||||
import { getActiveInstance } from 'remotedev-app/lib/reducers/instances';
|
||||
|
||||
const syncStores = (baseStore) => (store) => (next) => (action) => {
|
||||
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 });
|
||||
}
|
||||
return next(action);
|
||||
};
|
||||
|
||||
export default syncStores;
|
10
extension/src/app/reducers/background/index.js
Normal file
10
extension/src/app/reducers/background/index.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { combineReducers } from 'redux';
|
||||
import instances from 'remotedev-app/lib/reducers/instances';
|
||||
import persistStates from './persistStates';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
instances,
|
||||
persistStates,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
4
extension/src/app/reducers/background/persistStates.js
Normal file
4
extension/src/app/reducers/background/persistStates.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
export default function persistStates(state = false, action) {
|
||||
if (action.type === 'TOGGLE_PERSIST') return !state;
|
||||
return state;
|
||||
}
|
16
extension/src/app/reducers/panel/index.js
Normal file
16
extension/src/app/reducers/panel/index.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { combineReducers } from 'redux';
|
||||
import instances from 'remotedev-app/lib/reducers/instances';
|
||||
import monitor from 'remotedev-app/lib/reducers/monitor';
|
||||
import notification from 'remotedev-app/lib/reducers/notification';
|
||||
import test from 'remotedev-app/lib/reducers/test';
|
||||
import reports from 'remotedev-app/lib/reducers/reports';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
instances,
|
||||
monitor,
|
||||
test,
|
||||
reports,
|
||||
notification,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
18
extension/src/app/reducers/window/index.js
Normal file
18
extension/src/app/reducers/window/index.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { combineReducers } from 'redux';
|
||||
import instances from './instances';
|
||||
import monitor from 'remotedev-app/lib/reducers/monitor';
|
||||
import notification from 'remotedev-app/lib/reducers/notification';
|
||||
import socket from 'remotedev-app/lib/reducers/socket';
|
||||
import reports from 'remotedev-app/lib/reducers/reports';
|
||||
import test from 'remotedev-app/lib/reducers/test';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
instances,
|
||||
monitor,
|
||||
test,
|
||||
socket,
|
||||
reports,
|
||||
notification,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
23
extension/src/app/reducers/window/instances.js
Normal file
23
extension/src/app/reducers/window/instances.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
import {
|
||||
initialState,
|
||||
dispatchAction,
|
||||
} from 'remotedev-app/lib/reducers/instances';
|
||||
import {
|
||||
UPDATE_STATE,
|
||||
SELECT_INSTANCE,
|
||||
LIFTED_ACTION,
|
||||
} from 'remotedev-app/lib/constants/actionTypes';
|
||||
|
||||
export default function instances(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case UPDATE_STATE:
|
||||
return { ...action.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 };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user