mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-04-25 18:53:44 +03:00
Compare commits
No commits in common. "main" and "remotedev-redux-devtools-extensions@3.0.19" have entirely different histories.
main
...
remotedev-
|
@ -6,8 +6,5 @@
|
||||||
"access": "public",
|
"access": "public",
|
||||||
"baseBranch": "main",
|
"baseBranch": "main",
|
||||||
"updateInternalDependencies": "patch",
|
"updateInternalDependencies": "patch",
|
||||||
"ignore": [],
|
"ignore": []
|
||||||
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
|
|
||||||
"onlyUpdatePeerDependentsWhenOutOfRange": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
15
.gitattributes
vendored
15
.gitattributes
vendored
|
@ -1 +1,14 @@
|
||||||
* 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
|
||||||
|
*.yml text eol=lf
|
||||||
|
*.graphql text eol=lf
|
||||||
|
.eslintrc text eol=lf
|
||||||
|
.prettierrc text eol=lf
|
||||||
|
.babelrc text eol=lf
|
||||||
|
.stylelintrc text eol=lf
|
||||||
|
|
19
.github/workflows/CI.yml
vendored
19
.github/workflows/CI.yml
vendored
|
@ -8,12 +8,15 @@ on:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: 'ubuntu-22.04'
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: pnpm/action-setup@v4
|
with:
|
||||||
- uses: actions/setup-node@v4
|
fetch-depth: 0
|
||||||
|
- uses: nrwl/nx-set-shas@v3
|
||||||
|
- uses: pnpm/action-setup@v2
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
@ -22,10 +25,10 @@ jobs:
|
||||||
- name: Check formatting
|
- name: Check formatting
|
||||||
run: pnpm run format:check
|
run: pnpm run format:check
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm run build:all
|
run: pnpm exec nx affected --target=build --parallel=1
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: pnpm run lint:all
|
run: pnpm exec nx affected --target=lint --parallel=1
|
||||||
- name: Test
|
- name: Test
|
||||||
uses: coactions/setup-xvfb@v1
|
uses: GabrielBB/xvfb-action@v1
|
||||||
with:
|
with:
|
||||||
run: pnpm run test:all
|
run: pnpm exec nx affected --target=test --parallel=1
|
||||||
|
|
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
|
@ -10,18 +10,18 @@ permissions: write-all
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Release
|
name: Release
|
||||||
runs-on: 'ubuntu-22.04'
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repo
|
- name: Checkout Repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
|
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- uses: pnpm/action-setup@v4
|
- uses: pnpm/action-setup@v2
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 'lts/*'
|
node-version: 'lts/*'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
@ -40,19 +40,19 @@ jobs:
|
||||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|
||||||
- name: Archive Chrome Extension
|
- name: Archive Chrome Extension
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: chrome
|
name: chrome
|
||||||
path: extension/chrome/dist
|
path: extension/chrome/dist
|
||||||
|
|
||||||
- name: Archive Edge Extension
|
- name: Archive Edge Extension
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: edge
|
name: edge
|
||||||
path: extension/edge/dist
|
path: extension/edge/dist
|
||||||
|
|
||||||
- name: Archive Firefox Extension
|
- name: Archive Firefox Extension
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: firefox
|
name: firefox
|
||||||
path: extension/firefox/dist
|
path: extension/firefox/dist
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,4 +9,3 @@ coverage
|
||||||
.idea
|
.idea
|
||||||
.eslintcache
|
.eslintcache
|
||||||
!packages/redux-devtools-slider-monitor/examples/todomvc/dist/index.html
|
!packages/redux-devtools-slider-monitor/examples/todomvc/dist/index.html
|
||||||
.nx
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ It can be used as a browser extension (for [Chrome](https://chrome.google.com/we
|
||||||
|
|
||||||
## Development
|
## 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`).
|
This is a monorepo powered by [pnpm](https://pnpm.io/) and [Nx](https://nx.dev/). [Install pnpm](https://pnpm.io/installation) and run `pnpm install` to get started. Each package's dependencies need to be built before the package itself can be built. You can either build all the packages (i.e., `pnpm run build:all`) or use Nx commands to build only the packages necessary for the packages you're working on (i.e., `pnpm nx build remotedev-redux-devtools-extension`).
|
||||||
|
|
||||||
## Backers
|
## Backers
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ const DevTools = createDevTools(
|
||||||
defaultIsVisible={true}
|
defaultIsVisible={true}
|
||||||
>
|
>
|
||||||
<LogMonitor theme="tomorrow" />
|
<LogMonitor theme="tomorrow" />
|
||||||
</DockMonitor>,
|
</DockMonitor>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default DevTools;
|
export default DevTools;
|
||||||
|
@ -88,7 +88,7 @@ const enhancer = compose(
|
||||||
// Middleware you want to use in development:
|
// Middleware you want to use in development:
|
||||||
applyMiddleware(d1, d2, d3),
|
applyMiddleware(d1, d2, d3),
|
||||||
// Required! Enable Redux DevTools with the monitors you chose
|
// Required! Enable Redux DevTools with the monitors you chose
|
||||||
DevTools.instrument(),
|
DevTools.instrument()
|
||||||
);
|
);
|
||||||
|
|
||||||
export default function configureStore(initialState) {
|
export default function configureStore(initialState) {
|
||||||
|
@ -100,8 +100,8 @@ export default function configureStore(initialState) {
|
||||||
if (module.hot) {
|
if (module.hot) {
|
||||||
module.hot.accept('../reducers', () =>
|
module.hot.accept('../reducers', () =>
|
||||||
store.replaceReducer(
|
store.replaceReducer(
|
||||||
require('../reducers') /*.default if you use Babel 6+ */,
|
require('../reducers') /*.default if you use Babel 6+ */
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ const enhancer = compose(
|
||||||
// Required! Enable Redux DevTools with the monitors you chose
|
// Required! Enable Redux DevTools with the monitors you chose
|
||||||
DevTools.instrument(),
|
DevTools.instrument(),
|
||||||
// Optional. Lets you write ?debug_session=<key> in address bar to persist debug sessions
|
// Optional. Lets you write ?debug_session=<key> in address bar to persist debug sessions
|
||||||
persistState(getDebugSessionKey()),
|
persistState(getDebugSessionKey())
|
||||||
);
|
);
|
||||||
|
|
||||||
function getDebugSessionKey() {
|
function getDebugSessionKey() {
|
||||||
|
@ -200,7 +200,7 @@ const enhancer = compose(
|
||||||
// Required! Enable Redux DevTools with the monitors you chose
|
// Required! Enable Redux DevTools with the monitors you chose
|
||||||
DevTools.instrument(),
|
DevTools.instrument(),
|
||||||
// Optional. Lets you write ?debug_session=<key> in address bar to persist debug sessions
|
// Optional. Lets you write ?debug_session=<key> in address bar to persist debug sessions
|
||||||
persistState(getDebugSessionKey()),
|
persistState(getDebugSessionKey())
|
||||||
);
|
);
|
||||||
|
|
||||||
function getDebugSessionKey() {
|
function getDebugSessionKey() {
|
||||||
|
@ -219,8 +219,8 @@ export default function configureStore(initialState) {
|
||||||
if (module.hot) {
|
if (module.hot) {
|
||||||
module.hot.accept('../reducers', () =>
|
module.hot.accept('../reducers', () =>
|
||||||
store.replaceReducer(
|
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}>
|
<Provider store={store}>
|
||||||
<App />
|
<App />
|
||||||
</Provider>,
|
</Provider>,
|
||||||
document.getElementById('root'),
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
@ -353,7 +353,7 @@ export default function showDevTools(store) {
|
||||||
const popup = window.open(
|
const popup = window.open(
|
||||||
null,
|
null,
|
||||||
'Redux DevTools',
|
'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
|
// Reload in case it already exists
|
||||||
popup.location.reload();
|
popup.location.reload();
|
||||||
|
@ -362,7 +362,7 @@ export default function showDevTools(store) {
|
||||||
popup.document.write('<div id="react-devtools-root"></div>');
|
popup.document.write('<div id="react-devtools-root"></div>');
|
||||||
render(
|
render(
|
||||||
<DevTools store={store} />,
|
<DevTools store={store} />,
|
||||||
popup.document.getElementById('react-devtools-root'),
|
popup.document.getElementById('react-devtools-root')
|
||||||
);
|
);
|
||||||
}, 10);
|
}, 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',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
3
eslintrc.js.base.json
Normal file
3
eslintrc.js.base.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"parser": "@babel/eslint-parser"
|
||||||
|
}
|
17
eslintrc.ts.base.json
Normal file
17
eslintrc.ts.base.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"plugins": ["@typescript-eslint"],
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-unsafe-return": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-call": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-member-access": "off"
|
||||||
|
}
|
||||||
|
}
|
18
eslintrc.ts.jest.base.json
Normal file
18
eslintrc.ts.jest.base.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"plugins": ["jest"],
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||||
|
"plugin:jest/recommended",
|
||||||
|
"plugin:jest/style",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-unsafe-return": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-call": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-member-access": "off"
|
||||||
|
}
|
||||||
|
}
|
37
eslintrc.ts.react.base.json
Normal file
37
eslintrc.ts.react.base.json
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugins": ["@typescript-eslint", "react"],
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:react-hooks/recommended",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"react": {
|
||||||
|
"version": "detect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-unsafe-return": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-call": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||||
|
"@typescript-eslint/no-misused-promises": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"checksVoidReturn": {
|
||||||
|
"attributes": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
20
eslintrc.ts.react.jest.base.json
Normal file
20
eslintrc.ts.react.jest.base.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"plugins": ["jest"],
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||||
|
"plugin:react/recommended",
|
||||||
|
"plugin:react-hooks/recommended",
|
||||||
|
"plugin:jest/recommended",
|
||||||
|
"plugin:jest/style",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-unsafe-return": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-call": "off",
|
||||||
|
"@typescript-eslint/no-unsafe-member-access": "off"
|
||||||
|
}
|
||||||
|
}
|
3
extension/.eslintignore
Normal file
3
extension/.eslintignore
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
examples
|
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"]
|
||||||
|
}
|
|
@ -1,170 +1,5 @@
|
||||||
# remotedev-redux-devtools-extension
|
# remotedev-redux-devtools-extension
|
||||||
|
|
||||||
## 3.2.10
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- @redux-devtools/app@6.2.2
|
|
||||||
|
|
||||||
## 3.2.9
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [91f21b2]
|
|
||||||
- @redux-devtools/core@4.1.1
|
|
||||||
- @redux-devtools/slider-monitor@5.1.1
|
|
||||||
- @redux-devtools/utils@3.1.1
|
|
||||||
- @redux-devtools/app@6.2.1
|
|
||||||
|
|
||||||
## 3.2.8
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [6830118]
|
|
||||||
- react-json-tree@0.20.0
|
|
||||||
- @redux-devtools/app@6.2.0
|
|
||||||
- @redux-devtools/slider-monitor@6.0.0
|
|
||||||
- @redux-devtools/ui@1.4.0
|
|
||||||
- @redux-devtools/core@4.1.0
|
|
||||||
- @redux-devtools/utils@4.0.0
|
|
||||||
|
|
||||||
## 3.2.7
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- b25bf13: Send state from background when monitor connects
|
|
||||||
|
|
||||||
## 3.2.6
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 50d7682: Fix DevTools from losing connection
|
|
||||||
|
|
||||||
## 3.2.5
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- eb3ac09: Add logging to background service worker
|
|
||||||
|
|
||||||
## 3.2.4
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- f1d6158: Fix mocking Chrome API for Electron
|
|
||||||
|
|
||||||
## 3.2.3
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- fd9f950: Fix monitoring on opening panel
|
|
||||||
- e49708d: Fix manifest.json for Edge
|
|
||||||
|
|
||||||
## 3.2.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- abd03a7: Fix: only send data to extension if DevTools are open
|
|
||||||
|
|
||||||
## 3.2.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- 83b2c19: Upgrade to Manifest V3
|
|
||||||
|
|
||||||
## 3.1.11
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 73688e1: Fix releasing Firefox extension
|
|
||||||
|
|
||||||
## 3.1.10
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 2163bc3: Split large messages sent from background page to devpanel
|
|
||||||
|
|
||||||
## 3.1.9
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [bbb1a40]
|
|
||||||
- react-json-tree@0.19.0
|
|
||||||
- @redux-devtools/slider-monitor@5.0.1
|
|
||||||
- @redux-devtools/ui@1.3.2
|
|
||||||
|
|
||||||
## 3.1.8
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 191d419: Convert d3 packages to ESM
|
|
||||||
- Updated dependencies [191d419]
|
|
||||||
- @redux-devtools/app@6.0.1
|
|
||||||
|
|
||||||
## 3.1.7
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [5cfe3e5]
|
|
||||||
- Updated dependencies [decc035]
|
|
||||||
- @redux-devtools/app@6.0.0
|
|
||||||
- @redux-devtools/slider-monitor@5.0.0
|
|
||||||
- @redux-devtools/core@4.0.0
|
|
||||||
- @redux-devtools/utils@3.0.0
|
|
||||||
|
|
||||||
## 3.1.6
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [158ba2c]
|
|
||||||
- @redux-devtools/app@5.0.0
|
|
||||||
|
|
||||||
## 3.1.5
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 65205f90: Replace Action<unknown> with Action<string>
|
|
||||||
- Updated dependencies [65205f90]
|
|
||||||
- @redux-devtools/app@4.0.1
|
|
||||||
- @redux-devtools/core@3.13.2
|
|
||||||
|
|
||||||
## 3.1.4
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- Updated dependencies [e57bcb39]
|
|
||||||
- @redux-devtools/app@4.0.0
|
|
||||||
|
|
||||||
## 3.1.3
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- bca76009: Fix missing CSS for code editor
|
|
||||||
|
|
||||||
## 3.1.2
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- 64ed81b0: Fix extension in Firefox and Chrome Incognito
|
|
||||||
|
|
||||||
## 3.1.1
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- d18525b5: Increase min-width of popup
|
|
||||||
- Updated dependencies [57751ff9]
|
|
||||||
- @redux-devtools/app@3.0.0
|
|
||||||
|
|
||||||
## 3.1.0
|
|
||||||
|
|
||||||
### Minor Changes
|
|
||||||
|
|
||||||
- d54adb76: Option to sort State Tree keys alphabetically
|
|
||||||
Option to disable collapsing of object keys
|
|
||||||
|
|
||||||
### Patch Changes
|
|
||||||
|
|
||||||
- @redux-devtools/app@2.2.2
|
|
||||||
|
|
||||||
## 3.0.19
|
## 3.0.19
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
|
|
||||||
- from [Chrome Web Store](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd);
|
- 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 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 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/docs/extensions/get-started/tutorial/hello-world#load-unpacked) `./dev`.
|
- 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
|
### 2. For Firefox
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ const composeEnhancers =
|
||||||
: compose;
|
: compose;
|
||||||
|
|
||||||
const enhancer = composeEnhancers(
|
const enhancer = composeEnhancers(
|
||||||
applyMiddleware(...middleware),
|
applyMiddleware(...middleware)
|
||||||
// other store enhancers if any
|
// other store enhancers if any
|
||||||
);
|
);
|
||||||
const store = createStore(reducer, enhancer);
|
const store = createStore(reducer, enhancer);
|
||||||
|
@ -130,9 +130,9 @@ import { composeWithDevTools } from '@redux-devtools/extension';
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
reducer,
|
reducer,
|
||||||
composeWithDevTools(
|
composeWithDevTools(
|
||||||
applyMiddleware(...middleware),
|
applyMiddleware(...middleware)
|
||||||
// other store enhancers if any
|
// other store enhancers if any
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -148,9 +148,9 @@ const composeEnhancers = composeWithDevTools({
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
reducer,
|
reducer,
|
||||||
/* preloadedState, */ composeEnhancers(
|
/* preloadedState, */ composeEnhancers(
|
||||||
applyMiddleware(...middleware),
|
applyMiddleware(...middleware)
|
||||||
// other store enhancers if any
|
// other store enhancers if any
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ import { devToolsEnhancer } from '@redux-devtools/extension';
|
||||||
|
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
reducer,
|
reducer,
|
||||||
/* preloadedState, */ devToolsEnhancer(),
|
/* preloadedState, */ devToolsEnhancer()
|
||||||
// Specify name here, actionsDenylist, actionsCreators and other options if needed
|
// Specify name here, actionsDenylist, actionsCreators and other options if needed
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
@ -181,7 +181,7 @@ import { devToolsEnhancerLogOnlyInProduction } from '@redux-devtools/extension';
|
||||||
|
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
reducer,
|
reducer,
|
||||||
/* preloadedState, */ devToolsEnhancerLogOnlyInProduction(),
|
/* preloadedState, */ devToolsEnhancerLogOnlyInProduction()
|
||||||
// options like actionSanitizer, stateSanitizer
|
// options like actionSanitizer, stateSanitizer
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
@ -198,9 +198,9 @@ const composeEnhancers = composeWithDevToolsLogOnlyInProduction({
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
reducer,
|
reducer,
|
||||||
/* preloadedState, */ composeEnhancers(
|
/* preloadedState, */ composeEnhancers(
|
||||||
applyMiddleware(...middleware),
|
applyMiddleware(...middleware)
|
||||||
// other store enhancers if any
|
// other store enhancers if any
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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');
|
|
|
@ -1,22 +1,28 @@
|
||||||
{
|
{
|
||||||
"version": "3.2.10",
|
"version": "3.0.19",
|
||||||
"name": "Redux DevTools",
|
"name": "Redux DevTools",
|
||||||
"description": "Redux DevTools for debugging application's state changes.",
|
"description": "Redux DevTools for debugging application's state changes.",
|
||||||
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
||||||
"manifest_version": 3,
|
"manifest_version": 2,
|
||||||
"action": {
|
"page_action": {
|
||||||
"default_icon": "img/logo/gray.png",
|
"default_icon": "img/logo/gray.png",
|
||||||
"default_title": "Redux DevTools",
|
"default_title": "Redux DevTools",
|
||||||
"default_popup": "devpanel.html#popup"
|
"default_popup": "window.html#popup"
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"devtools-window": {
|
"devtools-left": {
|
||||||
"description": "DevTools window"
|
"description": "DevTools window to left"
|
||||||
|
},
|
||||||
|
"devtools-right": {
|
||||||
|
"description": "DevTools window to right"
|
||||||
|
},
|
||||||
|
"devtools-bottom": {
|
||||||
|
"description": "DevTools window to bottom"
|
||||||
},
|
},
|
||||||
"devtools-remote": {
|
"devtools-remote": {
|
||||||
"description": "Remote DevTools"
|
"description": "Remote DevTools"
|
||||||
},
|
},
|
||||||
"_execute_action": {
|
"_execute_page_action": {
|
||||||
"suggested_key": {
|
"suggested_key": {
|
||||||
"default": "Ctrl+Shift+E"
|
"default": "Ctrl+Shift+E"
|
||||||
}
|
}
|
||||||
|
@ -28,37 +34,36 @@
|
||||||
"128": "img/logo/128x128.png"
|
"128": "img/logo/128x128.png"
|
||||||
},
|
},
|
||||||
"options_ui": {
|
"options_ui": {
|
||||||
"page": "options.html"
|
"page": "options.html",
|
||||||
|
"chrome_style": true
|
||||||
},
|
},
|
||||||
"background": {
|
"background": {
|
||||||
"service_worker": "background.bundle.js"
|
"scripts": ["background.bundle.js"],
|
||||||
|
"persistent": false
|
||||||
},
|
},
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
"matches": ["<all_urls>"],
|
"matches": ["<all_urls>"],
|
||||||
"exclude_globs": ["https://www.google*"],
|
"exclude_globs": ["https://www.google*"],
|
||||||
"js": ["content.bundle.js"],
|
"js": ["content.bundle.js", "pagewrap.bundle.js"],
|
||||||
"run_at": "document_start",
|
"run_at": "document_start",
|
||||||
"all_frames": true
|
"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",
|
"devtools_page": "devtools.html",
|
||||||
|
"web_accessible_resources": ["page.bundle.js"],
|
||||||
"externally_connectable": {
|
"externally_connectable": {
|
||||||
"ids": ["*"]
|
"ids": ["*"]
|
||||||
},
|
},
|
||||||
"permissions": ["notifications", "contextMenus", "storage"],
|
"permissions": [
|
||||||
"host_permissions": ["file:///*", "http://*/*", "https://*/*"],
|
"notifications",
|
||||||
"content_security_policy": {
|
"contextMenus",
|
||||||
"extension_pages": "script-src 'self'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;"
|
"storage",
|
||||||
},
|
"file:///*",
|
||||||
|
"http://*/*",
|
||||||
|
"https://*/*"
|
||||||
|
],
|
||||||
|
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;",
|
||||||
"update_url": "https://clients2.google.com/service/update2/crx",
|
"update_url": "https://clients2.google.com/service/update2/crx",
|
||||||
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdJEPwY92xUACA9CcDBDBmbdbp8Ap3cKQ0DJTUuVQvqb4FQAv8RtKY3iUjGvdwuAcSJQIZwHXcP2aNDH3TiFik/NhRK2GRW8X3OZyTdkuDueABGP2KEX8q1WQDgjX/rPIinGYztUrvoICw/UerMPwNW62jwGoVU3YhAGf+15CgX2Y6a4tppnf/+1mPedKPidh0RsM+aJY98rX+r1SPAHPcGzMjocLkqcT75DZBXer8VQN14tOOzRCd6T6oy7qm7eWru8lJwcY66qMQvhk0osqEod2G3nA7aTWpmqPFS66VEiecP9PgZlp8gQdgZ3dFhA62exydlD55JuRhiMIR63yQIDAQAB"
|
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdJEPwY92xUACA9CcDBDBmbdbp8Ap3cKQ0DJTUuVQvqb4FQAv8RtKY3iUjGvdwuAcSJQIZwHXcP2aNDH3TiFik/NhRK2GRW8X3OZyTdkuDueABGP2KEX8q1WQDgjX/rPIinGYztUrvoICw/UerMPwNW62jwGoVU3YhAGf+15CgX2Y6a4tppnf/+1mPedKPidh0RsM+aJY98rX+r1SPAHPcGzMjocLkqcT75DZBXer8VQN14tOOzRCd6T6oy7qm7eWru8lJwcY66qMQvhk0osqEod2G3nA7aTWpmqPFS66VEiecP9PgZlp8gQdgZ3dFhA62exydlD55JuRhiMIR63yQIDAQAB"
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,18 +5,18 @@ Use with
|
||||||
- `window.__REDUX_DEVTOOLS_EXTENSION__([options])`
|
- `window.__REDUX_DEVTOOLS_EXTENSION__([options])`
|
||||||
- `window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__([options])()`
|
- `window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__([options])()`
|
||||||
- `window.__REDUX_DEVTOOLS_EXTENSION__.connect([options])`
|
- `window.__REDUX_DEVTOOLS_EXTENSION__.connect([options])`
|
||||||
- `@redux-devtools/extension` npm package:
|
- `redux-devtools-extension` npm package:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { composeWithDevTools } from '@redux-devtools/extension';
|
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||||
|
|
||||||
const composeEnhancers = composeWithDevTools(options);
|
const composeEnhancers = composeWithDevTools(options);
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
reducer,
|
reducer,
|
||||||
/* preloadedState, */ composeEnhancers(
|
/* preloadedState, */ composeEnhancers(
|
||||||
applyMiddleware(...middleware),
|
applyMiddleware(...middleware)
|
||||||
// other store enhancers if any
|
// 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) =>
|
replacer: (key, value) =>
|
||||||
value && mori.isMap(value) ? mori.toJs(value) : 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,
|
immutable: Immutable,
|
||||||
refs: [ABRecord],
|
refs: [ABRecord],
|
||||||
},
|
},
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ In the example bellow it will always send `{ component: '[React]' }`, regardless
|
||||||
```js
|
```js
|
||||||
function component(
|
function component(
|
||||||
state = { component: null, toJSON: () => ({ component: '[React]' }) },
|
state = { component: null, toJSON: () => ({ component: '[React]' }) },
|
||||||
action,
|
action
|
||||||
) {
|
) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'ADD_COMPONENT':
|
case 'ADD_COMPONENT':
|
||||||
|
@ -206,7 +206,7 @@ function counter(
|
||||||
return { conter: this.count * 10 };
|
return { conter: this.count * 10 };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
action,
|
action
|
||||||
) {
|
) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'INCREMENT':
|
case 'INCREMENT':
|
||||||
|
@ -236,7 +236,7 @@ const store = createStore(
|
||||||
actionSanitizer,
|
actionSanitizer,
|
||||||
stateSanitizer: (state) =>
|
stateSanitizer: (state) =>
|
||||||
state.data ? { ...state, data: '<<LONG_BLOB>>' } : state,
|
state.data ? { ...state, data: '<<LONG_BLOB>>' } : state,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -254,7 +254,7 @@ createStore(
|
||||||
actionsDenylist: 'SOME_ACTION',
|
actionsDenylist: 'SOME_ACTION',
|
||||||
// or actionsDenylist: ['SOME_ACTION', 'SOME_OTHER_ACTION']
|
// or actionsDenylist: ['SOME_ACTION', 'SOME_OTHER_ACTION']
|
||||||
// or just actionsDenylist: 'SOME_' to omit both
|
// or just actionsDenylist: 'SOME_' to omit both
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ const store = createStore(
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION__({
|
window.__REDUX_DEVTOOLS_EXTENSION__({
|
||||||
predicate: (state, action) =>
|
predicate: (state, action) =>
|
||||||
state.dev.logLevel === VERBOSE && !action.forwarded,
|
state.dev.logLevel === VERBOSE && !action.forwarded,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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(
|
compose(
|
||||||
withState('count', 'setCount', 0),
|
withState('count', 'setCount', 0),
|
||||||
inspectProps('Counter inspector'),
|
inspectProps('Counter inspector')
|
||||||
)(Counter);
|
)(Counter);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -167,7 +167,7 @@ run(App, {
|
||||||
{ id: newId(), num: 0 },
|
{ id: newId(), num: 0 },
|
||||||
{ id: newId(), num: 0 },
|
{ id: newId(), num: 0 },
|
||||||
],
|
],
|
||||||
}),
|
})
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
|
@ -28,7 +28,7 @@ type WindowWithDevTools = Window & {
|
||||||
};
|
};
|
||||||
|
|
||||||
const isReduxDevtoolsExtenstionExist = (
|
const isReduxDevtoolsExtenstionExist = (
|
||||||
arg: Window | WindowWithDevTools,
|
arg: Window | WindowWithDevTools
|
||||||
): arg is WindowWithDevTools => {
|
): arg is WindowWithDevTools => {
|
||||||
return '__REDUX_DEVTOOLS_EXTENSION__' in arg;
|
return '__REDUX_DEVTOOLS_EXTENSION__' in arg;
|
||||||
};
|
};
|
||||||
|
@ -40,7 +40,7 @@ const store = createStore(
|
||||||
initialState,
|
initialState,
|
||||||
isReduxDevtoolsExtenstionExist(window)
|
isReduxDevtoolsExtenstionExist(window)
|
||||||
? window.__REDUX_DEVTOOLS_EXTENSION__()
|
? window.__REDUX_DEVTOOLS_EXTENSION__()
|
||||||
: undefined,
|
: undefined
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ const store = createStore(
|
||||||
instaceID: 2,
|
instaceID: 2,
|
||||||
name: 'Allowlisted',
|
name: 'Allowlisted',
|
||||||
actionsAllowlist: '...',
|
actionsAllowlist: '...',
|
||||||
}),
|
})
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
|
@ -35,8 +35,8 @@ const store = createStore(
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION__
|
window.__REDUX_DEVTOOLS_EXTENSION__
|
||||||
? window.__REDUX_DEVTOOLS_EXTENSION__()
|
? window.__REDUX_DEVTOOLS_EXTENSION__()
|
||||||
: (noop) => noop,
|
: (noop) => noop,
|
||||||
batchedSubscribe(/* ... */),
|
batchedSubscribe(/* ... */)
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ const store = createStore(
|
||||||
actionSanitizer,
|
actionSanitizer,
|
||||||
stateSanitizer: (state) =>
|
stateSanitizer: (state) =>
|
||||||
state.data ? { ...state, data: '<<LONG_BLOB>>' } : state,
|
state.data ? { ...state, data: '<<LONG_BLOB>>' } : state,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ const store = Redux.createStore(
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION__ &&
|
window.__REDUX_DEVTOOLS_EXTENSION__ &&
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION__({
|
window.__REDUX_DEVTOOLS_EXTENSION__({
|
||||||
serialize: true,
|
serialize: true,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,28 @@
|
||||||
{
|
{
|
||||||
"version": "3.2.10",
|
"version": "3.0.19",
|
||||||
"name": "Redux DevTools",
|
"name": "Redux DevTools",
|
||||||
"description": "Redux DevTools for debugging application's state changes.",
|
"description": "Redux DevTools for debugging application's state changes.",
|
||||||
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
||||||
"manifest_version": 3,
|
"manifest_version": 2,
|
||||||
"action": {
|
"page_action": {
|
||||||
"default_icon": "img/logo/gray.png",
|
"default_icon": "img/logo/gray.png",
|
||||||
"default_title": "Redux DevTools",
|
"default_title": "Redux DevTools",
|
||||||
"default_popup": "devpanel.html#popup"
|
"default_popup": "window.html#popup"
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"devtools-window": {
|
"devtools-left": {
|
||||||
"description": "DevTools window"
|
"description": "DevTools window to left"
|
||||||
|
},
|
||||||
|
"devtools-right": {
|
||||||
|
"description": "DevTools window to right"
|
||||||
|
},
|
||||||
|
"devtools-bottom": {
|
||||||
|
"description": "DevTools window to bottom"
|
||||||
},
|
},
|
||||||
"devtools-remote": {
|
"devtools-remote": {
|
||||||
"description": "Remote DevTools"
|
"description": "Remote DevTools"
|
||||||
},
|
},
|
||||||
"_execute_action": {
|
"_execute_page_action": {
|
||||||
"suggested_key": {
|
"suggested_key": {
|
||||||
"default": "Ctrl+Shift+E"
|
"default": "Ctrl+Shift+E"
|
||||||
}
|
}
|
||||||
|
@ -28,35 +34,34 @@
|
||||||
"128": "img/logo/128x128.png"
|
"128": "img/logo/128x128.png"
|
||||||
},
|
},
|
||||||
"options_ui": {
|
"options_ui": {
|
||||||
"page": "options.html"
|
"page": "options.html",
|
||||||
|
"chrome_style": true
|
||||||
},
|
},
|
||||||
"background": {
|
"background": {
|
||||||
"service_worker": "background.bundle.js"
|
"scripts": ["background.bundle.js"],
|
||||||
|
"persistent": false
|
||||||
},
|
},
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
"matches": ["<all_urls>"],
|
"matches": ["<all_urls>"],
|
||||||
"exclude_globs": ["https://www.google*"],
|
"exclude_globs": ["https://www.google*"],
|
||||||
"js": ["content.bundle.js"],
|
"js": ["content.bundle.js", "pagewrap.bundle.js"],
|
||||||
"run_at": "document_start",
|
"run_at": "document_start",
|
||||||
"all_frames": true
|
"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",
|
"devtools_page": "devtools.html",
|
||||||
|
"web_accessible_resources": ["page.bundle.js"],
|
||||||
"externally_connectable": {
|
"externally_connectable": {
|
||||||
"ids": ["*"]
|
"ids": ["*"]
|
||||||
},
|
},
|
||||||
"permissions": ["notifications", "contextMenus", "storage"],
|
"permissions": [
|
||||||
"host_permissions": ["file:///*", "http://*/*", "https://*/*"],
|
"notifications",
|
||||||
"content_security_policy": {
|
"contextMenus",
|
||||||
"extension_pages": "script-src 'self'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;"
|
"storage",
|
||||||
}
|
"file:///*",
|
||||||
|
"http://*/*",
|
||||||
|
"https://*/*"
|
||||||
|
],
|
||||||
|
"content_security_policy": "script-src 'self' 'unsafe-eval'; 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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
|
@ -1,4 +1,4 @@
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Redux counter example</title>
|
<title>Redux counter example</title>
|
||||||
|
|
|
@ -10,5 +10,5 @@ render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<App />
|
<App />
|
||||||
</Provider>,
|
</Provider>,
|
||||||
document.getElementById('root'),
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,7 +11,7 @@ app.use(
|
||||||
webpackDevMiddleware(compiler, {
|
webpackDevMiddleware(compiler, {
|
||||||
noInfo: true,
|
noInfo: true,
|
||||||
publicPath: config.output.publicPath,
|
publicPath: config.output.publicPath,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
app.use(webpackHotMiddleware(compiler));
|
app.use(webpackHotMiddleware(compiler));
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ app.listen(port, function (error) {
|
||||||
console.info(
|
console.info(
|
||||||
'==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.',
|
'==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.',
|
||||||
port,
|
port,
|
||||||
port,
|
port
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@ export default function configureStore(preloadedState) {
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
reducer,
|
reducer,
|
||||||
preloadedState,
|
preloadedState,
|
||||||
composeEnhancers(applyMiddleware(invariant(), thunk)),
|
composeEnhancers(applyMiddleware(invariant(), thunk))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (module.hot) {
|
if (module.hot) {
|
||||||
|
|
|
@ -37,7 +37,7 @@ function mockStore(getState, expectedActions, onLastAction) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const mockStoreWithMiddleware = applyMiddleware(...middlewares)(
|
const mockStoreWithMiddleware = applyMiddleware(...middlewares)(
|
||||||
mockStoreWithoutMiddleware,
|
mockStoreWithoutMiddleware
|
||||||
);
|
);
|
||||||
|
|
||||||
return mockStoreWithMiddleware();
|
return mockStoreWithMiddleware();
|
||||||
|
|
|
@ -11,7 +11,7 @@ function setup() {
|
||||||
decrement: expect.createSpy(),
|
decrement: expect.createSpy(),
|
||||||
};
|
};
|
||||||
const component = TestUtils.renderIntoDocument(
|
const component = TestUtils.renderIntoDocument(
|
||||||
<Counter counter={1} {...actions} />,
|
<Counter counter={1} {...actions} />
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
component: component,
|
component: component,
|
||||||
|
|
|
@ -10,7 +10,7 @@ function setup(initialState) {
|
||||||
const app = TestUtils.renderIntoDocument(
|
const app = TestUtils.renderIntoDocument(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<App />
|
<App />
|
||||||
</Provider>,
|
</Provider>
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
app: app,
|
app: app,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>React counter example</title>
|
<title>React counter example</title>
|
||||||
|
|
|
@ -69,7 +69,7 @@ class MainSection extends Component {
|
||||||
const filteredTodos = todos.filter(TODO_FILTERS[filter]);
|
const filteredTodos = todos.filter(TODO_FILTERS[filter]);
|
||||||
const completedCount = todos.reduce(
|
const completedCount = todos.reduce(
|
||||||
(count, todo) => (todo.completed ? count + 1 : count),
|
(count, todo) => (todo.completed ? count + 1 : count),
|
||||||
0,
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Redux TodoMVC example</title>
|
<title>Redux TodoMVC example</title>
|
||||||
|
|
|
@ -12,5 +12,5 @@ render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Root />
|
<Root />
|
||||||
</Provider>,
|
</Provider>,
|
||||||
document.getElementById('root'),
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
|
|
@ -34,14 +34,14 @@ export default function todos(state = initialState, action) {
|
||||||
return state.map((todo) =>
|
return state.map((todo) =>
|
||||||
todo.id === action.id
|
todo.id === action.id
|
||||||
? Object.assign({}, todo, { text: action.text })
|
? Object.assign({}, todo, { text: action.text })
|
||||||
: todo,
|
: todo
|
||||||
);
|
);
|
||||||
|
|
||||||
case COMPLETE_TODO:
|
case COMPLETE_TODO:
|
||||||
return state.map((todo) =>
|
return state.map((todo) =>
|
||||||
todo.id === action.id
|
todo.id === action.id
|
||||||
? Object.assign({}, todo, { completed: !todo.completed })
|
? Object.assign({}, todo, { completed: !todo.completed })
|
||||||
: todo,
|
: todo
|
||||||
);
|
);
|
||||||
|
|
||||||
case COMPLETE_ALL:
|
case COMPLETE_ALL:
|
||||||
|
@ -49,7 +49,7 @@ export default function todos(state = initialState, action) {
|
||||||
return state.map((todo) =>
|
return state.map((todo) =>
|
||||||
Object.assign({}, todo, {
|
Object.assign({}, todo, {
|
||||||
completed: !areAllMarked,
|
completed: !areAllMarked,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
case CLEAR_COMPLETED:
|
case CLEAR_COMPLETED:
|
||||||
|
|
|
@ -11,7 +11,7 @@ app.use(
|
||||||
webpackDevMiddleware(compiler, {
|
webpackDevMiddleware(compiler, {
|
||||||
noInfo: true,
|
noInfo: true,
|
||||||
publicPath: config.output.publicPath,
|
publicPath: config.output.publicPath,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
app.use(webpackHotMiddleware(compiler));
|
app.use(webpackHotMiddleware(compiler));
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ app.listen(port, function (error) {
|
||||||
console.info(
|
console.info(
|
||||||
'==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.',
|
'==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.',
|
||||||
port,
|
port,
|
||||||
port,
|
port
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,7 +11,7 @@ import rootReducer from '../reducers';
|
||||||
export default function configureStore(initialState) {
|
export default function configureStore(initialState) {
|
||||||
let finalCreateStore = compose(
|
let finalCreateStore = compose(
|
||||||
reduxReactRouter({ createHistory }),
|
reduxReactRouter({ createHistory }),
|
||||||
global.devToolsExtension ? global.devToolsExtension() : (f) => f,
|
global.devToolsExtension ? global.devToolsExtension() : (f) => f
|
||||||
)(createStore);
|
)(createStore);
|
||||||
|
|
||||||
const store = finalCreateStore(rootReducer, initialState);
|
const store = finalCreateStore(rootReducer, initialState);
|
||||||
|
|
|
@ -13,7 +13,7 @@ function setup(propOverrides) {
|
||||||
onClearCompleted: expect.createSpy(),
|
onClearCompleted: expect.createSpy(),
|
||||||
onShow: expect.createSpy(),
|
onShow: expect.createSpy(),
|
||||||
},
|
},
|
||||||
propOverrides,
|
propOverrides
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderer = TestUtils.createRenderer();
|
const renderer = TestUtils.createRenderer();
|
||||||
|
@ -72,7 +72,7 @@ describe('components', () => {
|
||||||
0: 'All',
|
0: 'All',
|
||||||
1: 'Active',
|
1: 'Active',
|
||||||
2: 'Completed',
|
2: 'Completed',
|
||||||
}[i],
|
}[i]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -29,7 +29,7 @@ function setup(propOverrides) {
|
||||||
clearCompleted: expect.createSpy(),
|
clearCompleted: expect.createSpy(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
propOverrides,
|
propOverrides
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderer = TestUtils.createRenderer();
|
const renderer = TestUtils.createRenderer();
|
||||||
|
|
|
@ -12,7 +12,7 @@ function setup(propOverrides) {
|
||||||
editing: false,
|
editing: false,
|
||||||
newTodo: false,
|
newTodo: false,
|
||||||
},
|
},
|
||||||
propOverrides,
|
propOverrides
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderer = TestUtils.createRenderer();
|
const renderer = TestUtils.createRenderer();
|
||||||
|
|
|
@ -18,7 +18,7 @@ describe('todos reducer', () => {
|
||||||
todos([], {
|
todos([], {
|
||||||
type: types.ADD_TODO,
|
type: types.ADD_TODO,
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
}),
|
})
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
|
@ -39,8 +39,8 @@ describe('todos reducer', () => {
|
||||||
{
|
{
|
||||||
type: types.ADD_TODO,
|
type: types.ADD_TODO,
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
|
@ -71,8 +71,8 @@ describe('todos reducer', () => {
|
||||||
{
|
{
|
||||||
type: types.ADD_TODO,
|
type: types.ADD_TODO,
|
||||||
text: 'Fix the tests',
|
text: 'Fix the tests',
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Fix the tests',
|
text: 'Fix the tests',
|
||||||
|
@ -110,8 +110,8 @@ describe('todos reducer', () => {
|
||||||
{
|
{
|
||||||
type: types.DELETE_TODO,
|
type: types.DELETE_TODO,
|
||||||
id: 1,
|
id: 1,
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Use Redux',
|
text: 'Use Redux',
|
||||||
|
@ -140,8 +140,8 @@ describe('todos reducer', () => {
|
||||||
type: types.EDIT_TODO,
|
type: types.EDIT_TODO,
|
||||||
text: 'Fix the tests',
|
text: 'Fix the tests',
|
||||||
id: 1,
|
id: 1,
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Fix the tests',
|
text: 'Fix the tests',
|
||||||
|
@ -174,8 +174,8 @@ describe('todos reducer', () => {
|
||||||
{
|
{
|
||||||
type: types.COMPLETE_TODO,
|
type: types.COMPLETE_TODO,
|
||||||
id: 1,
|
id: 1,
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
|
@ -207,8 +207,8 @@ describe('todos reducer', () => {
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
type: types.COMPLETE_ALL,
|
type: types.COMPLETE_ALL,
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
|
@ -239,8 +239,8 @@ describe('todos reducer', () => {
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
type: types.COMPLETE_ALL,
|
type: types.COMPLETE_ALL,
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
|
@ -272,8 +272,8 @@ describe('todos reducer', () => {
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
type: types.CLEAR_COMPLETED,
|
type: types.CLEAR_COMPLETED,
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Use Redux',
|
text: 'Use Redux',
|
||||||
|
@ -308,7 +308,7 @@ describe('todos reducer', () => {
|
||||||
completed: false,
|
completed: false,
|
||||||
text: 'Write tests',
|
text: 'Write tests',
|
||||||
},
|
},
|
||||||
]),
|
])
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Write more tests',
|
text: 'Write more tests',
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
|
|
@ -20,7 +20,7 @@ const composeEnhancers =
|
||||||
compose;
|
compose;
|
||||||
const store = createStore(
|
const store = createStore(
|
||||||
reducer,
|
reducer,
|
||||||
composeEnhancers(applyMiddleware(sagaMiddleware)),
|
composeEnhancers(applyMiddleware(sagaMiddleware))
|
||||||
);
|
);
|
||||||
sagaMiddleware.run(rootSaga);
|
sagaMiddleware.run(rootSaga);
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ function render() {
|
||||||
onIncrementIfOdd={() => action('INCREMENT_IF_ODD')}
|
onIncrementIfOdd={() => action('INCREMENT_IF_ODD')}
|
||||||
onIncrementAsync={() => action('INCREMENT_ASYNC')}
|
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 filteredTodos = todos.filter(TODO_FILTERS[filter]);
|
||||||
const completedCount = todos.reduce(
|
const completedCount = todos.reduce(
|
||||||
(count, todo) => (todo.completed ? count + 1 : count),
|
(count, todo) => (todo.completed ? count + 1 : count),
|
||||||
0,
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<!doctype html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Redux TodoMVC example</title>
|
<title>Redux TodoMVC example</title>
|
||||||
|
|
|
@ -12,5 +12,5 @@ render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<App />
|
<App />
|
||||||
</Provider>,
|
</Provider>,
|
||||||
document.getElementById('root'),
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
|
|
@ -36,7 +36,7 @@ export default function todos(state = initialState, action) {
|
||||||
return state.map((todo) =>
|
return state.map((todo) =>
|
||||||
todo.id === action.id
|
todo.id === action.id
|
||||||
? Object.assign({}, todo, { text: action.text, modified: new Date() })
|
? Object.assign({}, todo, { text: action.text, modified: new Date() })
|
||||||
: todo,
|
: todo
|
||||||
);
|
);
|
||||||
|
|
||||||
case COMPLETE_TODO:
|
case COMPLETE_TODO:
|
||||||
|
@ -46,7 +46,7 @@ export default function todos(state = initialState, action) {
|
||||||
completed: !todo.completed,
|
completed: !todo.completed,
|
||||||
modified: new Date(),
|
modified: new Date(),
|
||||||
})
|
})
|
||||||
: todo,
|
: todo
|
||||||
);
|
);
|
||||||
|
|
||||||
case COMPLETE_ALL:
|
case COMPLETE_ALL:
|
||||||
|
@ -55,7 +55,7 @@ export default function todos(state = initialState, action) {
|
||||||
Object.assign({}, todo, {
|
Object.assign({}, todo, {
|
||||||
completed: !areAllMarked,
|
completed: !areAllMarked,
|
||||||
modified: new Date(),
|
modified: new Date(),
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
case CLEAR_COMPLETED:
|
case CLEAR_COMPLETED:
|
||||||
|
|
|
@ -11,7 +11,7 @@ app.use(
|
||||||
webpackDevMiddleware(compiler, {
|
webpackDevMiddleware(compiler, {
|
||||||
noInfo: true,
|
noInfo: true,
|
||||||
publicPath: config.output.publicPath,
|
publicPath: config.output.publicPath,
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
app.use(webpackHotMiddleware(compiler));
|
app.use(webpackHotMiddleware(compiler));
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ app.listen(port, function (error) {
|
||||||
console.info(
|
console.info(
|
||||||
'==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.',
|
'==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.',
|
||||||
port,
|
port,
|
||||||
port,
|
port
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,7 +13,7 @@ export default function configureStore(preloadedState) {
|
||||||
if (!enhancer) {
|
if (!enhancer) {
|
||||||
console.warn(
|
console.warn(
|
||||||
'Install Redux DevTools Extension to inspect the app state: ' +
|
'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(),
|
onClearCompleted: expect.createSpy(),
|
||||||
onShow: expect.createSpy(),
|
onShow: expect.createSpy(),
|
||||||
},
|
},
|
||||||
propOverrides,
|
propOverrides
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderer = TestUtils.createRenderer();
|
const renderer = TestUtils.createRenderer();
|
||||||
|
@ -72,7 +72,7 @@ describe('components', () => {
|
||||||
0: 'All',
|
0: 'All',
|
||||||
1: 'Active',
|
1: 'Active',
|
||||||
2: 'Completed',
|
2: 'Completed',
|
||||||
}[i],
|
}[i]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -29,7 +29,7 @@ function setup(propOverrides) {
|
||||||
clearCompleted: expect.createSpy(),
|
clearCompleted: expect.createSpy(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
propOverrides,
|
propOverrides
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderer = TestUtils.createRenderer();
|
const renderer = TestUtils.createRenderer();
|
||||||
|
|
|
@ -12,7 +12,7 @@ function setup(propOverrides) {
|
||||||
editing: false,
|
editing: false,
|
||||||
newTodo: false,
|
newTodo: false,
|
||||||
},
|
},
|
||||||
propOverrides,
|
propOverrides
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderer = TestUtils.createRenderer();
|
const renderer = TestUtils.createRenderer();
|
||||||
|
|
|
@ -18,7 +18,7 @@ describe('todos reducer', () => {
|
||||||
todos([], {
|
todos([], {
|
||||||
type: types.ADD_TODO,
|
type: types.ADD_TODO,
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
}),
|
})
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
|
@ -39,8 +39,8 @@ describe('todos reducer', () => {
|
||||||
{
|
{
|
||||||
type: types.ADD_TODO,
|
type: types.ADD_TODO,
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
|
@ -71,8 +71,8 @@ describe('todos reducer', () => {
|
||||||
{
|
{
|
||||||
type: types.ADD_TODO,
|
type: types.ADD_TODO,
|
||||||
text: 'Fix the tests',
|
text: 'Fix the tests',
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Fix the tests',
|
text: 'Fix the tests',
|
||||||
|
@ -110,8 +110,8 @@ describe('todos reducer', () => {
|
||||||
{
|
{
|
||||||
type: types.DELETE_TODO,
|
type: types.DELETE_TODO,
|
||||||
id: 1,
|
id: 1,
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Use Redux',
|
text: 'Use Redux',
|
||||||
|
@ -140,8 +140,8 @@ describe('todos reducer', () => {
|
||||||
type: types.EDIT_TODO,
|
type: types.EDIT_TODO,
|
||||||
text: 'Fix the tests',
|
text: 'Fix the tests',
|
||||||
id: 1,
|
id: 1,
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Fix the tests',
|
text: 'Fix the tests',
|
||||||
|
@ -174,8 +174,8 @@ describe('todos reducer', () => {
|
||||||
{
|
{
|
||||||
type: types.COMPLETE_TODO,
|
type: types.COMPLETE_TODO,
|
||||||
id: 1,
|
id: 1,
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
|
@ -207,8 +207,8 @@ describe('todos reducer', () => {
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
type: types.COMPLETE_ALL,
|
type: types.COMPLETE_ALL,
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
|
@ -239,8 +239,8 @@ describe('todos reducer', () => {
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
type: types.COMPLETE_ALL,
|
type: types.COMPLETE_ALL,
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Run the tests',
|
text: 'Run the tests',
|
||||||
|
@ -272,8 +272,8 @@ describe('todos reducer', () => {
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
type: types.CLEAR_COMPLETED,
|
type: types.CLEAR_COMPLETED,
|
||||||
},
|
}
|
||||||
),
|
)
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Use Redux',
|
text: 'Use Redux',
|
||||||
|
@ -308,7 +308,7 @@ describe('todos reducer', () => {
|
||||||
completed: false,
|
completed: false,
|
||||||
text: 'Write tests',
|
text: 'Write tests',
|
||||||
},
|
},
|
||||||
]),
|
])
|
||||||
).toEqual([
|
).toEqual([
|
||||||
{
|
{
|
||||||
text: 'Write more tests',
|
text: 'Write more tests',
|
||||||
|
|
|
@ -1,22 +1,29 @@
|
||||||
{
|
{
|
||||||
"version": "3.2.10",
|
"version": "3.0.19",
|
||||||
"name": "Redux DevTools",
|
"name": "Redux DevTools",
|
||||||
"manifest_version": 3,
|
"manifest_version": 2,
|
||||||
"description": "Redux Developer Tools for debugging application state changes.",
|
"description": "Redux Developer Tools for debugging application state changes.",
|
||||||
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
"homepage_url": "https://github.com/reduxjs/redux-devtools",
|
||||||
"browser_specific_settings": {
|
"applications": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
"id": "extension@redux.devtools"
|
"id": "extension@redux.devtools",
|
||||||
|
"strict_min_version": "54.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"action": {
|
"page_action": {
|
||||||
"default_icon": "img/logo/38x38.png",
|
"default_icon": "img/logo/38x38.png",
|
||||||
"default_title": "Redux DevTools",
|
"default_title": "Redux DevTools",
|
||||||
"default_popup": "devpanel.html#popup"
|
"default_popup": "window.html#popup"
|
||||||
},
|
},
|
||||||
"commands": {
|
"commands": {
|
||||||
"devtools-window": {
|
"devtools-left": {
|
||||||
"description": "DevTools window"
|
"description": "DevTools window to left"
|
||||||
|
},
|
||||||
|
"devtools-right": {
|
||||||
|
"description": "DevTools window to right"
|
||||||
|
},
|
||||||
|
"devtools-bottom": {
|
||||||
|
"description": "DevTools window to bottom"
|
||||||
},
|
},
|
||||||
"devtools-remote": {
|
"devtools-remote": {
|
||||||
"description": "Remote DevTools"
|
"description": "Remote DevTools"
|
||||||
|
@ -36,22 +43,21 @@
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
"matches": ["<all_urls>"],
|
"matches": ["<all_urls>"],
|
||||||
"js": ["content.bundle.js"],
|
"js": ["content.bundle.js", "pagewrap.bundle.js"],
|
||||||
"run_at": "document_start",
|
"run_at": "document_start",
|
||||||
"all_frames": true
|
"all_frames": true
|
||||||
},
|
|
||||||
{
|
|
||||||
"matches": ["<all_urls>"],
|
|
||||||
"js": ["page.bundle.js"],
|
|
||||||
"run_at": "document_start",
|
|
||||||
"all_frames": true,
|
|
||||||
"world": "MAIN"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"devtools_page": "devtools.html",
|
"devtools_page": "devtools.html",
|
||||||
"permissions": ["notifications", "contextMenus", "tabs", "storage"],
|
"web_accessible_resources": ["page.bundle.js"],
|
||||||
"host_permissions": ["file:///*", "http://*/*", "https://*/*"],
|
"permissions": [
|
||||||
"content_security_policy": {
|
"notifications",
|
||||||
"extension_pages": "script-src 'self'; object-src 'self'; img-src 'self' data:;"
|
"contextMenus",
|
||||||
}
|
"tabs",
|
||||||
|
"storage",
|
||||||
|
"file:///*",
|
||||||
|
"http://*/*",
|
||||||
|
"https://*/*"
|
||||||
|
],
|
||||||
|
"content_security_policy": "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)',
|
|
||||||
],
|
|
||||||
};
|
|
11
extension/jest.config.js
Normal file
11
extension/jest.config.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
module.exports = {
|
||||||
|
setupFilesAfterEnv: ['<rootDir>/test/setup.js'],
|
||||||
|
testPathIgnorePatterns: ['<rootDir>/examples'],
|
||||||
|
testEnvironment: 'jsdom',
|
||||||
|
moduleNameMapper: {
|
||||||
|
'\\.css$': '<rootDir>/test/__mocks__/styleMock.ts',
|
||||||
|
},
|
||||||
|
transformIgnorePatterns: [
|
||||||
|
'node_modules/(?!.pnpm|d3|dateformat|delaunator|internmap|nanoid|robust-predicates|uuid)',
|
||||||
|
],
|
||||||
|
};
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"name": "remotedev-redux-devtools-extension",
|
"name": "remotedev-redux-devtools-extension",
|
||||||
"version": "3.2.10",
|
"version": "3.0.19",
|
||||||
"description": "Redux Developer Tools for debugging application state changes.",
|
"description": "Redux Developer Tools for debugging application state changes.",
|
||||||
"homepage": "https://github.com/reduxjs/redux-devtools/tree/master/extension",
|
"homepage": "https://github.com/reduxjs/redux-devtools/tree/master/extension",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -11,71 +11,86 @@
|
||||||
"url": "https://github.com/reduxjs/redux-devtools.git"
|
"url": "https://github.com/reduxjs/redux-devtools.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm run build:extension && pnpm run type-check",
|
"start": "webpack --env development --watch",
|
||||||
"build:extension": "node build.mjs",
|
"build": "pnpm run build:extension && pnpm run build:chrome && pnpm run build:edge && pnpm run build:firefox",
|
||||||
|
"build:extension": "webpack --env production && webpack --config wrap.webpack.config.js",
|
||||||
|
"build:chrome": "cpy . ../chrome/dist --cwd dist && cpy manifest.json dist --cwd chrome",
|
||||||
|
"build:edge": "cpy . ../edge/dist --cwd dist && cpy manifest.json dist --cwd edge",
|
||||||
|
"build:firefox": "cpy . ../firefox/dist --cwd dist && cpy manifest.json dist --cwd firefox",
|
||||||
"build:examples": "babel-node examples/buildAll.js",
|
"build:examples": "babel-node examples/buildAll.js",
|
||||||
"clean": "rimraf dist && rimraf chrome/dist && rimraf edge/dist && rimraf firefox/dist",
|
"clean": "rimraf dist && rimraf chrome/dist && rimraf edge/dist && rimraf firefox/dist",
|
||||||
"test:app": "cross-env BABEL_ENV=test jest test/app",
|
"test:app": "cross-env BABEL_ENV=test jest test/app",
|
||||||
"test:chrome": "jest test/chrome",
|
"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:electron": "pnpm run build:test:electron:fixture && jest test/electron",
|
||||||
"test": "pnpm run test:app && pnpm run test:chrome && pnpm run test:electron",
|
"test": "pnpm run test:app && pnpm run test:chrome && pnpm run test:electron",
|
||||||
"lint": "eslint .",
|
"build:test:electron:fixture": "webpack --config test/electron/fixture/webpack.config.js",
|
||||||
"type-check": "tsc --noEmit"
|
"type-check": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.14.0",
|
"@babel/polyfill": "^7.12.1",
|
||||||
"@redux-devtools/app": "workspace:^",
|
"@redux-devtools/app": "^2.2.1",
|
||||||
"@redux-devtools/core": "workspace:^",
|
"@redux-devtools/core": "^3.13.0",
|
||||||
"@redux-devtools/instrument": "workspace:^",
|
"@redux-devtools/instrument": "^2.1.0",
|
||||||
"@redux-devtools/serialize": "workspace:^",
|
"@redux-devtools/serialize": "^0.4.1",
|
||||||
"@redux-devtools/slider-monitor": "workspace:^",
|
"@redux-devtools/slider-monitor": "^4.0.0",
|
||||||
"@redux-devtools/ui": "workspace:^",
|
"@redux-devtools/ui": "^1.3.0",
|
||||||
"@redux-devtools/utils": "workspace:^",
|
"@redux-devtools/utils": "^2.0.0",
|
||||||
"@reduxjs/toolkit": "^2.6.0",
|
"@types/jsan": "^3.1.2",
|
||||||
"@types/jsan": "^3.1.5",
|
|
||||||
"jsan": "^3.1.14",
|
"jsan": "^3.1.14",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"react": "^18.3.1",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.2.0",
|
||||||
"react-icons": "^5.5.0",
|
"react-icons": "^4.7.1",
|
||||||
"react-is": "^18.3.1",
|
"react-is": "^18.2.0",
|
||||||
"react-json-tree": "workspace:^",
|
"react-json-tree": "^0.18.0",
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^8.0.5",
|
||||||
"redux": "^5.0.1",
|
"redux": "^4.2.1",
|
||||||
"redux-persist": "^6.0.0",
|
"redux-persist": "^6.0.0",
|
||||||
"styled-components": "^5.3.11"
|
"styled-components": "^5.3.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.26.9",
|
"@babel/core": "^7.21.0",
|
||||||
"@babel/preset-env": "^7.26.9",
|
"@babel/preset-env": "^7.20.2",
|
||||||
"@babel/preset-react": "^7.26.3",
|
"@babel/preset-react": "^7.18.6",
|
||||||
"@babel/preset-typescript": "^7.26.0",
|
"@babel/preset-typescript": "^7.21.0",
|
||||||
"@babel/register": "^7.25.9",
|
"@babel/register": "^7.21.0",
|
||||||
"@testing-library/dom": "^10.4.0",
|
"@testing-library/jest-dom": "^5.16.5",
|
||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@testing-library/react": "^14.0.0",
|
||||||
"@testing-library/react": "^16.2.0",
|
"@types/chrome": "^0.0.218",
|
||||||
"@types/chrome": "^0.0.308",
|
"@types/lodash": "^4.14.191",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/react": "^18.0.28",
|
||||||
"@types/react": "^18.3.18",
|
"@types/react-dom": "^18.0.11",
|
||||||
"@types/react-dom": "^18.3.5",
|
"@types/styled-components": "^5.1.26",
|
||||||
"@types/styled-components": "^5.1.34",
|
"babel-loader": "^9.1.2",
|
||||||
"chromedriver": "^126.0.5",
|
"chromedriver": "^110.0.0",
|
||||||
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
|
"cpy-cli": "^4.2.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"electron": "^31.7.7",
|
"css-loader": "^6.7.3",
|
||||||
"esbuild": "^0.25.0",
|
"electron": "^23.1.1",
|
||||||
"globals": "^15.15.0",
|
"eslint": "^8.35.0",
|
||||||
"immutable": "^5.0.3",
|
"eslint-config-airbnb": "^19.0.4",
|
||||||
"jest": "^29.7.0",
|
"eslint-plugin-import": "^2.27.5",
|
||||||
"jest-environment-jsdom": "^29.7.0",
|
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||||
"pug": "^3.0.3",
|
"eslint-plugin-react": "^7.32.2",
|
||||||
"rimraf": "^6.0.1",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"selenium-webdriver": "^4.29.0",
|
"file-loader": "^6.2.0",
|
||||||
|
"fork-ts-checker-webpack-plugin": "^8.0.0",
|
||||||
|
"immutable": "^4.2.4",
|
||||||
|
"jest": "^29.4.3",
|
||||||
|
"jest-environment-jsdom": "^29.4.3",
|
||||||
|
"pug-html-loader": "^1.1.5",
|
||||||
|
"raw-loader": "^4.0.2",
|
||||||
|
"react-transform-catch-errors": "^1.0.2",
|
||||||
|
"react-transform-hmr": "^1.0.4",
|
||||||
|
"rimraf": "^4.1.3",
|
||||||
|
"selenium-webdriver": "^4.8.1",
|
||||||
"sinon-chrome": "^3.0.1",
|
"sinon-chrome": "^3.0.1",
|
||||||
"ts-jest": "^29.2.6",
|
"style-loader": "^3.3.1",
|
||||||
"typescript": "~5.8.2",
|
"ts-jest": "^29.0.5",
|
||||||
"webpack": "^5.98.0",
|
"typescript": "~4.9.5",
|
||||||
"webpack-cli": "^6.0.1"
|
"webpack": "^5.75.0",
|
||||||
|
"webpack-cli": "^5.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@ import {
|
||||||
StoreState,
|
StoreState,
|
||||||
TopButtons,
|
TopButtons,
|
||||||
} from '@redux-devtools/app';
|
} from '@redux-devtools/app';
|
||||||
import { GoBroadcast } from 'react-icons/go';
|
import { GoRadioTower } from 'react-icons/go';
|
||||||
import { MdOutlineWindow } from 'react-icons/md';
|
import { MdBorderBottom, MdBorderLeft, MdBorderRight } from 'react-icons/md';
|
||||||
import type { Position } from '../pageScript/api/openWindow';
|
import type { Position } from '../pageScript/api/openWindow';
|
||||||
import type { SingleMessage } from '../background/store/apiMiddleware';
|
import type { SingleMessage } from '../background/store/apiMiddleware';
|
||||||
|
|
||||||
|
@ -29,21 +29,25 @@ interface OwnProps {
|
||||||
}
|
}
|
||||||
type Props = StateProps & DispatchProps & OwnProps;
|
type Props = StateProps & DispatchProps & OwnProps;
|
||||||
|
|
||||||
const isElectron = navigator.userAgent.includes('Electron');
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
isElectron?: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function sendMessage(message: SingleMessage) {
|
function sendMessage(message: SingleMessage) {
|
||||||
await chrome.runtime.sendMessage(message);
|
chrome.runtime.sendMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
class Actions extends Component<Props> {
|
class Actions extends Component<Props> {
|
||||||
openWindow = async (position: Position) => {
|
openWindow = (position: Position) => {
|
||||||
await sendMessage({ type: 'OPEN', position });
|
sendMessage({ type: 'OPEN', position });
|
||||||
};
|
};
|
||||||
openOptionsPage = async () => {
|
openOptionsPage = () => {
|
||||||
if (navigator.userAgent.includes('Firefox')) {
|
if (navigator.userAgent.indexOf('Firefox') !== -1) {
|
||||||
await sendMessage({ type: 'OPEN_OPTIONS' });
|
sendMessage({ type: 'OPEN_OPTIONS' });
|
||||||
} else {
|
} else {
|
||||||
await chrome.runtime.openOptionsPage();
|
chrome.runtime.openOptionsPage();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,7 +60,6 @@ class Actions extends Component<Props> {
|
||||||
liftedState,
|
liftedState,
|
||||||
liftedDispatch,
|
liftedDispatch,
|
||||||
position,
|
position,
|
||||||
stateTreeSettings,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { features } = options;
|
const { features } = options;
|
||||||
return (
|
return (
|
||||||
|
@ -72,7 +75,6 @@ class Actions extends Component<Props> {
|
||||||
monitorState={this.props.monitorState}
|
monitorState={this.props.monitorState}
|
||||||
dispatch={liftedDispatch}
|
dispatch={liftedDispatch}
|
||||||
features={options.features}
|
features={options.features}
|
||||||
stateTreeSettings={stateTreeSettings}
|
|
||||||
/>
|
/>
|
||||||
{sliderIsOpen && options.connectionId && options.features.jump && (
|
{sliderIsOpen && options.connectionId && options.features.jump && (
|
||||||
<SliderMonitor liftedState={liftedState} dispatch={liftedDispatch} />
|
<SliderMonitor liftedState={liftedState} dispatch={liftedDispatch} />
|
||||||
|
@ -85,7 +87,7 @@ class Actions extends Component<Props> {
|
||||||
{features.import && <ImportButton />}
|
{features.import && <ImportButton />}
|
||||||
{position &&
|
{position &&
|
||||||
(position !== '#popup' ||
|
(position !== '#popup' ||
|
||||||
navigator.userAgent.includes('Firefox')) && <PrintButton />}
|
navigator.userAgent.indexOf('Firefox') !== -1) && <PrintButton />}
|
||||||
<Divider />
|
<Divider />
|
||||||
<MonitorSelector />
|
<MonitorSelector />
|
||||||
<Divider />
|
<Divider />
|
||||||
|
@ -94,22 +96,40 @@ class Actions extends Component<Props> {
|
||||||
<DispatcherButton dispatcherIsOpen={this.props.dispatcherIsOpen} />
|
<DispatcherButton dispatcherIsOpen={this.props.dispatcherIsOpen} />
|
||||||
)}
|
)}
|
||||||
<Divider />
|
<Divider />
|
||||||
{!isElectron && (
|
{!window.isElectron && position !== '#left' && (
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => {
|
onClick={() => {
|
||||||
await this.openWindow('window');
|
this.openWindow('left');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MdOutlineWindow />
|
<MdBorderLeft />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{!isElectron && (
|
{!window.isElectron && position !== '#right' && (
|
||||||
<Button
|
<Button
|
||||||
onClick={async () => {
|
onClick={() => {
|
||||||
await this.openWindow('remote');
|
this.openWindow('right');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<GoBroadcast />
|
<MdBorderRight />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{!window.isElectron && position !== '#bottom' && (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
this.openWindow('bottom');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MdBorderBottom />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{!window.isElectron && (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
this.openWindow('remote');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<GoRadioTower />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
@ -129,7 +149,6 @@ const mapStateToProps = (state: StoreState) => {
|
||||||
dispatcherIsOpen: state.monitor.dispatcherIsOpen,
|
dispatcherIsOpen: state.monitor.dispatcherIsOpen,
|
||||||
sliderIsOpen: state.monitor.sliderIsOpen,
|
sliderIsOpen: state.monitor.sliderIsOpen,
|
||||||
reports: state.reports.data,
|
reports: state.reports.data,
|
||||||
stateTreeSettings: state.stateTreeSettings,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ class App extends Component<Props> {
|
||||||
<a
|
<a
|
||||||
href="https://github.com/zalmoxisus/redux-devtools-extension#usage"
|
href="https://github.com/zalmoxisus/redux-devtools-extension#usage"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
|
||||||
>
|
>
|
||||||
the instructions
|
the instructions
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -2,28 +2,34 @@ import openDevToolsWindow, { DevToolsPosition } from './openWindow';
|
||||||
|
|
||||||
export function createMenu() {
|
export function createMenu() {
|
||||||
const menus = [
|
const menus = [
|
||||||
{ id: 'devtools-window', title: 'Open in a window' },
|
{ id: 'devtools-left', title: 'To left' },
|
||||||
|
{ id: 'devtools-right', title: 'To right' },
|
||||||
|
{ id: 'devtools-bottom', title: 'To bottom' },
|
||||||
|
{
|
||||||
|
id: 'devtools-panel',
|
||||||
|
title: 'Open in a panel (enable in browser settings)',
|
||||||
|
},
|
||||||
{ id: 'devtools-remote', title: 'Open Remote DevTools' },
|
{ id: 'devtools-remote', title: 'Open Remote DevTools' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const shortcuts: { [commandName: string]: string | undefined } = {};
|
let shortcuts: { [commandName: string]: string | undefined } = {};
|
||||||
chrome.commands.getAll((commands) => {
|
chrome.commands.getAll((commands) => {
|
||||||
for (const { name, shortcut } of commands) {
|
commands.forEach(({ name, shortcut }) => {
|
||||||
shortcuts[name!] = shortcut;
|
shortcuts[name!] = shortcut;
|
||||||
}
|
});
|
||||||
|
|
||||||
for (const { id, title } of menus) {
|
menus.forEach(({ id, title }) => {
|
||||||
chrome.contextMenus.create({
|
chrome.contextMenus.create({
|
||||||
id: id,
|
id: id,
|
||||||
title: title + (shortcuts[id] ? ' (' + shortcuts[id] + ')' : ''),
|
title: title + (shortcuts[id] ? ' (' + shortcuts[id] + ')' : ''),
|
||||||
contexts: ['all'],
|
contexts: ['all'],
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removeMenu() {
|
export function removeMenu() {
|
||||||
await chrome.contextMenus.removeAll();
|
chrome.contextMenus.removeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
chrome.contextMenus.onClicked.addListener(({ menuItemId }) => {
|
chrome.contextMenus.onClicked.addListener(({ menuItemId }) => {
|
||||||
|
|
|
@ -1,23 +1,28 @@
|
||||||
import '../chromeApiMock';
|
import { Store } from 'redux';
|
||||||
import configureStore from './store/backgroundStore';
|
import configureStore, { BackgroundAction } from './store/backgroundStore';
|
||||||
import openDevToolsWindow, { DevToolsPosition } from './openWindow';
|
import openDevToolsWindow, { DevToolsPosition } from './openWindow';
|
||||||
import { createMenu, removeMenu } from './contextMenus';
|
import { createMenu, removeMenu } from './contextMenus';
|
||||||
import { getOptions } from '../options/syncOptions';
|
import syncOptions from '../options/syncOptions';
|
||||||
|
import { BackgroundState } from './store/backgroundReducer';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
store: Store<BackgroundState, BackgroundAction>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Expose the extension's store globally to access it from the windows
|
// Expose the extension's store globally to access it from the windows
|
||||||
// via chrome.runtime.getBackgroundPage
|
// via chrome.runtime.getBackgroundPage
|
||||||
export const store = configureStore();
|
window.store = configureStore();
|
||||||
|
|
||||||
// Listen for keyboard shortcuts
|
// Listen for keyboard shortcuts
|
||||||
chrome.commands.onCommand.addListener((shortcut) => {
|
chrome.commands.onCommand.addListener((shortcut) => {
|
||||||
openDevToolsWindow(shortcut as DevToolsPosition);
|
openDevToolsWindow(shortcut as DevToolsPosition);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Disable the action by default and create the context menu when installed
|
// Create the context menu when installed
|
||||||
chrome.runtime.onInstalled.addListener(() => {
|
chrome.runtime.onInstalled.addListener(() => {
|
||||||
void chrome.action.disable();
|
syncOptions().get((option) => {
|
||||||
|
|
||||||
getOptions((option) => {
|
|
||||||
if (option.showContextMenus) createMenu();
|
if (option.showContextMenus) createMenu();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -26,13 +31,6 @@ chrome.runtime.onInstalled.addListener(() => {
|
||||||
chrome.storage.onChanged.addListener((changes) => {
|
chrome.storage.onChanged.addListener((changes) => {
|
||||||
if (changes.showContextMenus) {
|
if (changes.showContextMenus) {
|
||||||
if (changes.showContextMenus.newValue) createMenu();
|
if (changes.showContextMenus.newValue) createMenu();
|
||||||
else void removeMenu();
|
else removeMenu();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// https://developer.chrome.com/docs/extensions/develop/migrate/to-service-workers#keep_a_service_worker_alive_continuously
|
|
||||||
setInterval(
|
|
||||||
() =>
|
|
||||||
void chrome.storage.local.set({ 'last-heartbeat': new Date().getTime() }),
|
|
||||||
20000,
|
|
||||||
);
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { LIFTED_ACTION } from '@redux-devtools/app';
|
import { LIFTED_ACTION } from '@redux-devtools/app';
|
||||||
import { store } from './index';
|
|
||||||
|
|
||||||
export function getReport(
|
export function getReport(
|
||||||
reportId: string,
|
reportId: string,
|
||||||
tabId: string | number,
|
tabId: string | number,
|
||||||
instanceId: number,
|
instanceId: number
|
||||||
) {
|
) {
|
||||||
chrome.storage.local.get(['s:hostname', 's:port', 's:secure'], (options) => {
|
chrome.storage.local.get(['s:hostname', 's:port', 's:secure'], (options) => {
|
||||||
if (!options['s:hostname'] || !options['s:port']) return;
|
if (!options['s:hostname'] || !options['s:port']) return;
|
||||||
|
@ -25,7 +24,7 @@ export function getReport(
|
||||||
.then((json) => {
|
.then((json) => {
|
||||||
const { payload, preloadedState } = json;
|
const { payload, preloadedState } = json;
|
||||||
if (!payload) return;
|
if (!payload) return;
|
||||||
store.dispatch({
|
window.store.dispatch({
|
||||||
type: LIFTED_ACTION,
|
type: LIFTED_ACTION,
|
||||||
message: 'IMPORT',
|
message: 'IMPORT',
|
||||||
state: JSON.stringify({ payload, preloadedState }),
|
state: JSON.stringify({ payload, preloadedState }),
|
||||||
|
|
|
@ -1,34 +1,83 @@
|
||||||
export type DevToolsPosition = 'devtools-window' | 'devtools-remote';
|
export type DevToolsPosition =
|
||||||
|
| 'devtools-left'
|
||||||
|
| 'devtools-right'
|
||||||
|
| 'devtools-bottom'
|
||||||
|
| 'devtools-panel'
|
||||||
|
| 'devtools-remote';
|
||||||
|
|
||||||
const windows: { [K in DevToolsPosition]?: number } = {};
|
let windows: { [K in DevToolsPosition]?: number } = {};
|
||||||
|
let lastPosition: DevToolsPosition | null = null;
|
||||||
|
|
||||||
export default function openDevToolsWindow(position: DevToolsPosition) {
|
export default function openDevToolsWindow(position: DevToolsPosition) {
|
||||||
|
function popWindow(
|
||||||
|
action: string,
|
||||||
|
url: string,
|
||||||
|
customOptions: chrome.windows.CreateData & chrome.windows.UpdateInfo
|
||||||
|
) {
|
||||||
|
function focusIfExist(callback: () => void) {
|
||||||
if (!windows[position]) {
|
if (!windows[position]) {
|
||||||
createWindow(position);
|
callback();
|
||||||
|
lastPosition = position;
|
||||||
} else {
|
} else {
|
||||||
chrome.windows.update(windows[position], { focused: true }, () => {
|
let params = { focused: true };
|
||||||
if (chrome.runtime.lastError) createWindow(position);
|
if (lastPosition !== position && position !== 'devtools-panel') {
|
||||||
|
params = { ...params, ...customOptions };
|
||||||
|
}
|
||||||
|
chrome.windows.update(windows[position]!, params, () => {
|
||||||
|
lastPosition = null;
|
||||||
|
if (chrome.runtime.lastError) callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createWindow(position: DevToolsPosition) {
|
focusIfExist(() => {
|
||||||
const url = chrome.runtime.getURL(getPath(position));
|
let options: chrome.windows.CreateData = {
|
||||||
chrome.windows.create({ type: 'popup', url }, (win) => {
|
type: 'popup',
|
||||||
|
...customOptions,
|
||||||
|
};
|
||||||
|
if (action === 'open') {
|
||||||
|
options.url = chrome.extension.getURL(
|
||||||
|
url + '#' + position.substr(position.indexOf('-') + 1)
|
||||||
|
);
|
||||||
|
chrome.windows.create(options, (win) => {
|
||||||
windows[position] = win!.id;
|
windows[position] = win!.id;
|
||||||
if (navigator.userAgent.includes('Firefox')) {
|
if (navigator.userAgent.indexOf('Firefox') !== -1) {
|
||||||
void chrome.windows.update(win!.id!, { focused: true });
|
chrome.windows.update(win!.id!, {
|
||||||
|
focused: true,
|
||||||
|
...customOptions,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
function getPath(position: DevToolsPosition) {
|
|
||||||
switch (position) {
|
|
||||||
case 'devtools-window':
|
|
||||||
return 'devpanel.html';
|
|
||||||
case 'devtools-remote':
|
|
||||||
return 'remote.html';
|
|
||||||
default:
|
|
||||||
throw new Error('Unrecognized position');
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let params: chrome.windows.CreateData & chrome.windows.UpdateInfo = {
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
width: 380,
|
||||||
|
height: window.screen.availHeight,
|
||||||
|
};
|
||||||
|
let url = 'window.html';
|
||||||
|
switch (position) {
|
||||||
|
case 'devtools-right':
|
||||||
|
params.left =
|
||||||
|
(window.screen as unknown as { availLeft: number }).availLeft +
|
||||||
|
window.screen.availWidth -
|
||||||
|
params.width!;
|
||||||
|
break;
|
||||||
|
case 'devtools-bottom':
|
||||||
|
params.height = 420;
|
||||||
|
params.top = window.screen.height - params.height;
|
||||||
|
params.width = window.screen.availWidth;
|
||||||
|
break;
|
||||||
|
case 'devtools-panel':
|
||||||
|
params.type = 'panel';
|
||||||
|
break;
|
||||||
|
case 'devtools-remote':
|
||||||
|
params = { width: 850, height: 600 };
|
||||||
|
url = 'remote.html';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
popWindow('open', url, params);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,14 @@ import {
|
||||||
TOGGLE_PERSIST,
|
TOGGLE_PERSIST,
|
||||||
UPDATE_STATE,
|
UPDATE_STATE,
|
||||||
} from '@redux-devtools/app';
|
} from '@redux-devtools/app';
|
||||||
import type { Options, OptionsMessage } from '../../options/syncOptions';
|
import syncOptions, {
|
||||||
|
Options,
|
||||||
|
OptionsMessage,
|
||||||
|
SyncOptions,
|
||||||
|
} from '../../options/syncOptions';
|
||||||
import openDevToolsWindow, { DevToolsPosition } from '../openWindow';
|
import openDevToolsWindow, { DevToolsPosition } from '../openWindow';
|
||||||
import { getReport } from '../logging';
|
import { getReport } from '../logging';
|
||||||
import { Action, Dispatch, Middleware } from 'redux';
|
import { Action, Dispatch, MiddlewareAPI } from 'redux';
|
||||||
import type {
|
import type {
|
||||||
ContentScriptToBackgroundMessage,
|
ContentScriptToBackgroundMessage,
|
||||||
SplitMessage,
|
SplitMessage,
|
||||||
|
@ -28,7 +32,6 @@ import { LiftedState } from '@redux-devtools/instrument';
|
||||||
import type { BackgroundAction, LiftedActionAction } from './backgroundStore';
|
import type { BackgroundAction, LiftedActionAction } from './backgroundStore';
|
||||||
import type { Position } from '../../pageScript/api/openWindow';
|
import type { Position } from '../../pageScript/api/openWindow';
|
||||||
import type { BackgroundState } from './backgroundReducer';
|
import type { BackgroundState } from './backgroundReducer';
|
||||||
import { store } from '../index';
|
|
||||||
|
|
||||||
interface TabMessageBase {
|
interface TabMessageBase {
|
||||||
readonly type: string;
|
readonly type: string;
|
||||||
|
@ -48,11 +51,6 @@ interface StopAction extends TabMessageBase {
|
||||||
readonly id?: never;
|
readonly id?: never;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OptionsAction {
|
|
||||||
readonly type: 'OPTIONS';
|
|
||||||
readonly options: Options;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DispatchAction extends TabMessageBase {
|
interface DispatchAction extends TabMessageBase {
|
||||||
readonly type: 'DISPATCH';
|
readonly type: 'DISPATCH';
|
||||||
readonly action: AppDispatchAction;
|
readonly action: AppDispatchAction;
|
||||||
|
@ -86,7 +84,7 @@ export interface NAAction {
|
||||||
readonly id: string | number;
|
readonly id: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InitMessage<S, A extends Action<string>> {
|
interface InitMessage<S, A extends Action<unknown>> {
|
||||||
readonly type: 'INIT';
|
readonly type: 'INIT';
|
||||||
readonly payload: string;
|
readonly payload: string;
|
||||||
instanceId: string;
|
instanceId: string;
|
||||||
|
@ -139,7 +137,7 @@ interface SerializedActionMessage {
|
||||||
readonly nextActionId: number;
|
readonly nextActionId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SerializedStateMessage<S, A extends Action<string>> {
|
interface SerializedStateMessage<S, A extends Action<unknown>> {
|
||||||
readonly type: 'STATE';
|
readonly type: 'STATE';
|
||||||
readonly payload: Omit<
|
readonly payload: Omit<
|
||||||
LiftedState<S, A, unknown>,
|
LiftedState<S, A, unknown>,
|
||||||
|
@ -153,7 +151,7 @@ interface SerializedStateMessage<S, A extends Action<string>> {
|
||||||
readonly committedState: boolean;
|
readonly committedState: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UpdateStateRequest<S, A extends Action<string>> =
|
type UpdateStateRequest<S, A extends Action<unknown>> =
|
||||||
| InitMessage<S, A>
|
| InitMessage<S, A>
|
||||||
| LiftedMessage
|
| LiftedMessage
|
||||||
| SerializedPartialStateMessage
|
| SerializedPartialStateMessage
|
||||||
|
@ -161,149 +159,91 @@ export type UpdateStateRequest<S, A extends Action<string>> =
|
||||||
| SerializedActionMessage
|
| SerializedActionMessage
|
||||||
| SerializedStateMessage<S, A>;
|
| SerializedStateMessage<S, A>;
|
||||||
|
|
||||||
interface UpdateStateAction<S, A extends Action<string>> {
|
export interface EmptyUpdateStateAction {
|
||||||
|
readonly type: typeof UPDATE_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateStateAction<S, A extends Action<unknown>> {
|
||||||
readonly type: typeof UPDATE_STATE;
|
readonly type: typeof UPDATE_STATE;
|
||||||
request: UpdateStateRequest<S, A>;
|
request: UpdateStateRequest<S, A>;
|
||||||
readonly id: string | number;
|
readonly id: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SplitUpdateStateRequestStart<S, A extends Action<string>> = {
|
|
||||||
split: 'start';
|
|
||||||
} & Partial<UpdateStateRequest<S, A>>;
|
|
||||||
|
|
||||||
interface SplitUpdateStateRequestChunk {
|
|
||||||
readonly split: 'chunk';
|
|
||||||
readonly chunk: [string, string];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SplitUpdateStateRequestEnd {
|
|
||||||
readonly split: 'end';
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SplitUpdateStateRequest<S, A extends Action<string>> =
|
|
||||||
| SplitUpdateStateRequestStart<S, A>
|
|
||||||
| SplitUpdateStateRequestChunk
|
|
||||||
| SplitUpdateStateRequestEnd;
|
|
||||||
|
|
||||||
interface SplitUpdateStateAction<S, A extends Action<string>> {
|
|
||||||
readonly type: typeof UPDATE_STATE;
|
|
||||||
request: SplitUpdateStateRequest<S, A>;
|
|
||||||
readonly id: string | number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TabMessage =
|
export type TabMessage =
|
||||||
| StartAction
|
| StartAction
|
||||||
| StopAction
|
| StopAction
|
||||||
| OptionsAction
|
| OptionsMessage
|
||||||
| DispatchAction
|
| DispatchAction
|
||||||
| ImportAction
|
| ImportAction
|
||||||
| ActionAction
|
| ActionAction
|
||||||
| ExportAction;
|
| ExportAction;
|
||||||
export type PanelMessageWithoutNA<S, A extends Action<string>> =
|
export type PanelMessage<S, A extends Action<unknown>> =
|
||||||
|
| NAAction
|
||||||
| ErrorMessage
|
| ErrorMessage
|
||||||
| UpdateStateAction<S, A>
|
| UpdateStateAction<S, A>
|
||||||
| SetPersistAction;
|
| SetPersistAction;
|
||||||
export type PanelMessage<S, A extends Action<string>> =
|
export type MonitorMessage =
|
||||||
| PanelMessageWithoutNA<S, A>
|
| NAAction
|
||||||
| NAAction;
|
| ErrorMessage
|
||||||
export type PanelMessageWithSplitAction<S, A extends Action<string>> =
|
| EmptyUpdateStateAction
|
||||||
| PanelMessage<S, A>
|
| SetPersistAction;
|
||||||
| SplitUpdateStateAction<S, A>;
|
|
||||||
|
|
||||||
type TabPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
type TabPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
||||||
postMessage: (message: TabMessage) => void;
|
postMessage: (message: TabMessage) => void;
|
||||||
};
|
};
|
||||||
type PanelPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
type PanelPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
||||||
postMessage: <S, A extends Action<string>>(
|
postMessage: <S, A extends Action<unknown>>(
|
||||||
message: PanelMessageWithSplitAction<S, A>,
|
message: PanelMessage<S, A>
|
||||||
) => void;
|
) => void;
|
||||||
};
|
};
|
||||||
|
type MonitorPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
||||||
|
postMessage: (message: MonitorMessage) => void;
|
||||||
|
};
|
||||||
|
|
||||||
export const CONNECTED = 'socket/CONNECTED';
|
export const CONNECTED = 'socket/CONNECTED';
|
||||||
export const DISCONNECTED = 'socket/DISCONNECTED';
|
export const DISCONNECTED = 'socket/DISCONNECTED';
|
||||||
const connections: {
|
const connections: {
|
||||||
readonly tab: { [K in number | string]: TabPort };
|
readonly tab: { [K in number | string]: TabPort };
|
||||||
readonly panel: { [K in number | string]: PanelPort };
|
readonly panel: { [K in number | string]: PanelPort };
|
||||||
|
readonly monitor: { [K in number | string]: MonitorPort };
|
||||||
} = {
|
} = {
|
||||||
tab: {},
|
tab: {},
|
||||||
panel: {},
|
panel: {},
|
||||||
|
monitor: {},
|
||||||
};
|
};
|
||||||
const chunks: {
|
const chunks: {
|
||||||
[instanceId: string]: PageScriptToContentScriptMessageForwardedToMonitors<
|
[instanceId: string]: PageScriptToContentScriptMessageForwardedToMonitors<
|
||||||
unknown,
|
unknown,
|
||||||
Action<string>
|
Action<unknown>
|
||||||
>;
|
>;
|
||||||
} = {};
|
} = {};
|
||||||
let monitors = 0;
|
let monitors = 0;
|
||||||
|
let isMonitored = false;
|
||||||
|
|
||||||
const getId = (sender: chrome.runtime.MessageSender, name?: string) =>
|
const getId = (sender: chrome.runtime.MessageSender, name?: string) =>
|
||||||
sender.tab ? sender.tab.id! : name || sender.id!;
|
sender.tab ? sender.tab.id! : name || sender.id!;
|
||||||
|
|
||||||
type MonitorAction<S, A extends Action<string>> =
|
type MonitorAction<S, A extends Action<unknown>> =
|
||||||
| NAAction
|
| NAAction
|
||||||
| ErrorMessage
|
| ErrorMessage
|
||||||
| UpdateStateAction<S, A>
|
| UpdateStateAction<S, A>
|
||||||
| SetPersistAction;
|
| SetPersistAction;
|
||||||
|
|
||||||
// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts
|
function toMonitors<S, A extends Action<unknown>>(
|
||||||
const maxChromeMsgSize = 32 * 1024 * 1024;
|
action: MonitorAction<S, A>,
|
||||||
|
tabId?: string | number,
|
||||||
function toMonitors<S, A extends Action<string>>(action: MonitorAction<S, A>) {
|
verbose?: boolean
|
||||||
console.log(`Message to monitors: ${action.type}`);
|
) {
|
||||||
|
Object.keys(connections.monitor).forEach((id) => {
|
||||||
for (const port of Object.values(connections.panel)) {
|
connections.monitor[id].postMessage(
|
||||||
try {
|
verbose || action.type === 'ERROR' || action.type === SET_PERSIST
|
||||||
port.postMessage(action);
|
? action
|
||||||
} catch (err) {
|
: { type: UPDATE_STATE }
|
||||||
if (
|
);
|
||||||
action.type !== UPDATE_STATE ||
|
});
|
||||||
err == null ||
|
Object.keys(connections.panel).forEach((id) => {
|
||||||
(err as Error).message !==
|
connections.panel[id].postMessage(action);
|
||||||
'Message length exceeded maximum allowed length.'
|
|
||||||
) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
const splitMessageStart: SplitUpdateStateRequestStart<S, A> = {
|
|
||||||
split: 'start',
|
|
||||||
};
|
|
||||||
const toSplit: [string, string][] = [];
|
|
||||||
let size = 0;
|
|
||||||
for (const [key, value] of Object.entries(
|
|
||||||
action.request as unknown as Record<string, unknown>,
|
|
||||||
)) {
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
size += value.length;
|
|
||||||
if (size > maxChromeMsgSize) {
|
|
||||||
toSplit.push([key, value]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(splitMessageStart as any)[key as keyof typeof splitMessageStart] =
|
|
||||||
value;
|
|
||||||
}
|
|
||||||
|
|
||||||
port.postMessage({ ...action, request: splitMessageStart });
|
|
||||||
|
|
||||||
for (let i = 0; i < toSplit.length; i++) {
|
|
||||||
for (let j = 0; j < toSplit[i][1].length; j += maxChromeMsgSize) {
|
|
||||||
port.postMessage({
|
|
||||||
...action,
|
|
||||||
request: {
|
|
||||||
split: 'chunk',
|
|
||||||
chunk: [
|
|
||||||
toSplit[i][0],
|
|
||||||
toSplit[i][1].substring(j, j + maxChromeMsgSize),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
port.postMessage({ ...action, request: { split: 'end' } });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportMessage {
|
interface ImportMessage {
|
||||||
|
@ -317,15 +257,19 @@ interface ImportMessage {
|
||||||
type ToContentScriptMessage = ImportMessage | LiftedActionAction;
|
type ToContentScriptMessage = ImportMessage | LiftedActionAction;
|
||||||
|
|
||||||
function toContentScript(messageBody: ToContentScriptMessage) {
|
function toContentScript(messageBody: ToContentScriptMessage) {
|
||||||
console.log(`Message to tab ${messageBody.id}: ${messageBody.message}`);
|
|
||||||
|
|
||||||
if (messageBody.message === 'DISPATCH') {
|
if (messageBody.message === 'DISPATCH') {
|
||||||
const { message, action, id, instanceId, state } = messageBody;
|
const { message, action, id, instanceId, state } = messageBody;
|
||||||
connections.tab[id!].postMessage({
|
connections.tab[id!].postMessage({
|
||||||
type: message,
|
type: message,
|
||||||
action,
|
action,
|
||||||
state: nonReduxDispatch(store, message, instanceId, action, state),
|
state: nonReduxDispatch(
|
||||||
id: instanceId.toString().replace(/^[^/]+\//, ''),
|
window.store,
|
||||||
|
message,
|
||||||
|
instanceId,
|
||||||
|
action as AppDispatchAction,
|
||||||
|
state
|
||||||
|
),
|
||||||
|
id: instanceId.toString().replace(/^[^\/]+\//, ''),
|
||||||
});
|
});
|
||||||
} else if (messageBody.message === 'IMPORT') {
|
} else if (messageBody.message === 'IMPORT') {
|
||||||
const { message, action, id, instanceId, state } = messageBody;
|
const { message, action, id, instanceId, state } = messageBody;
|
||||||
|
@ -333,13 +277,13 @@ function toContentScript(messageBody: ToContentScriptMessage) {
|
||||||
type: message,
|
type: message,
|
||||||
action,
|
action,
|
||||||
state: nonReduxDispatch(
|
state: nonReduxDispatch(
|
||||||
store,
|
window.store,
|
||||||
message,
|
message,
|
||||||
instanceId,
|
instanceId,
|
||||||
action as unknown as AppDispatchAction,
|
action as unknown as AppDispatchAction,
|
||||||
state,
|
state
|
||||||
),
|
),
|
||||||
id: instanceId.toString().replace(/^[^/]+\//, ''),
|
id: instanceId.toString().replace(/^[^\/]+\//, ''),
|
||||||
});
|
});
|
||||||
} else if (messageBody.message === 'ACTION') {
|
} else if (messageBody.message === 'ACTION') {
|
||||||
const { message, action, id, instanceId, state } = messageBody;
|
const { message, action, id, instanceId, state } = messageBody;
|
||||||
|
@ -347,13 +291,13 @@ function toContentScript(messageBody: ToContentScriptMessage) {
|
||||||
type: message,
|
type: message,
|
||||||
action,
|
action,
|
||||||
state: nonReduxDispatch(
|
state: nonReduxDispatch(
|
||||||
store,
|
window.store,
|
||||||
message,
|
message,
|
||||||
instanceId,
|
instanceId,
|
||||||
action as unknown as AppDispatchAction,
|
action as unknown as AppDispatchAction,
|
||||||
state,
|
state
|
||||||
),
|
),
|
||||||
id: instanceId.toString().replace(/^[^/]+\//, ''),
|
id: instanceId.toString().replace(/^[^\/]+\//, ''),
|
||||||
});
|
});
|
||||||
} else if (messageBody.message === 'EXPORT') {
|
} else if (messageBody.message === 'EXPORT') {
|
||||||
const { message, action, id, instanceId, state } = messageBody;
|
const { message, action, id, instanceId, state } = messageBody;
|
||||||
|
@ -361,41 +305,53 @@ function toContentScript(messageBody: ToContentScriptMessage) {
|
||||||
type: message,
|
type: message,
|
||||||
action,
|
action,
|
||||||
state: nonReduxDispatch(
|
state: nonReduxDispatch(
|
||||||
store,
|
window.store,
|
||||||
message,
|
message,
|
||||||
instanceId,
|
instanceId,
|
||||||
action as unknown as AppDispatchAction,
|
action as unknown as AppDispatchAction,
|
||||||
state,
|
state
|
||||||
),
|
),
|
||||||
id: instanceId.toString().replace(/^[^/]+\//, ''),
|
id: instanceId.toString().replace(/^[^\/]+\//, ''),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const { message, action, id, instanceId, state } = messageBody;
|
const { message, action, id, instanceId, state } = messageBody;
|
||||||
connections.tab[id].postMessage({
|
connections.tab[id!].postMessage({
|
||||||
type: message,
|
type: message,
|
||||||
action,
|
action,
|
||||||
state: nonReduxDispatch(
|
state: nonReduxDispatch(
|
||||||
store,
|
window.store,
|
||||||
message,
|
message,
|
||||||
instanceId,
|
instanceId,
|
||||||
action as AppDispatchAction,
|
action as AppDispatchAction,
|
||||||
state,
|
state
|
||||||
),
|
),
|
||||||
id: (instanceId as number).toString().replace(/^[^/]+\//, ''),
|
id: (instanceId as number).toString().replace(/^[^\/]+\//, ''),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toAllTabs(msg: TabMessage) {
|
function toAllTabs(msg: TabMessage) {
|
||||||
console.log(`Message to all tabs: ${msg.type}`);
|
const tabs = connections.tab;
|
||||||
|
Object.keys(tabs).forEach((id) => {
|
||||||
|
tabs[id].postMessage(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for (const tabPort of Object.values(connections.tab)) {
|
function monitorInstances(shouldMonitor: boolean, id?: string) {
|
||||||
tabPort.postMessage(msg);
|
if (!id && isMonitored === shouldMonitor) return;
|
||||||
|
const action = {
|
||||||
|
type: shouldMonitor ? ('START' as const) : ('STOP' as const),
|
||||||
|
};
|
||||||
|
if (id) {
|
||||||
|
if (connections.tab[id]) connections.tab[id].postMessage(action);
|
||||||
|
} else {
|
||||||
|
toAllTabs(action);
|
||||||
}
|
}
|
||||||
|
isMonitored = shouldMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReducerError() {
|
function getReducerError() {
|
||||||
const instancesState = store.getState().instances;
|
const instancesState = window.store.getState().instances;
|
||||||
const payload = instancesState.states[instancesState.current];
|
const payload = instancesState.states[instancesState.current];
|
||||||
const computedState = payload.computedStates[payload.currentStateIndex];
|
const computedState = payload.computedStates[payload.currentStateIndex];
|
||||||
if (!computedState) return false;
|
if (!computedState) return false;
|
||||||
|
@ -403,13 +359,13 @@ function getReducerError() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePersist() {
|
function togglePersist() {
|
||||||
const state = store.getState();
|
const state = window.store.getState();
|
||||||
if (state.instances.persisted) {
|
if (state.instances.persisted) {
|
||||||
for (const id of Object.keys(state.instances.connections)) {
|
Object.keys(state.instances.connections).forEach((id) => {
|
||||||
if (connections.tab[id]) return;
|
if (connections.tab[id]) return;
|
||||||
store.dispatch({ type: REMOVE_INSTANCE, id });
|
window.store.dispatch({ type: REMOVE_INSTANCE, id });
|
||||||
toMonitors({ type: 'NA', id });
|
toMonitors({ type: 'NA', id });
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,35 +378,45 @@ interface OpenOptionsMessage {
|
||||||
readonly type: 'OPEN_OPTIONS';
|
readonly type: 'OPEN_OPTIONS';
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SingleMessage = OpenMessage | OpenOptionsMessage | OptionsMessage;
|
interface GetOptionsMessage {
|
||||||
|
readonly type: 'GET_OPTIONS';
|
||||||
|
}
|
||||||
|
|
||||||
type BackgroundStoreMessage<S, A extends Action<string>> =
|
export type SingleMessage =
|
||||||
|
| OpenMessage
|
||||||
|
| OpenOptionsMessage
|
||||||
|
| GetOptionsMessage;
|
||||||
|
|
||||||
|
type BackgroundStoreMessage<S, A extends Action<unknown>> =
|
||||||
| PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<S, A>
|
| PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<S, A>
|
||||||
| SplitMessage
|
| SplitMessage
|
||||||
| SingleMessage;
|
| SingleMessage;
|
||||||
|
type BackgroundStoreResponse = { readonly options: Options };
|
||||||
|
|
||||||
// Receive messages from content scripts
|
// Receive messages from content scripts
|
||||||
function messaging<S, A extends Action<string>>(
|
function messaging<S, A extends Action<unknown>>(
|
||||||
request: BackgroundStoreMessage<S, A>,
|
request: BackgroundStoreMessage<S, A>,
|
||||||
sender: chrome.runtime.MessageSender,
|
sender: chrome.runtime.MessageSender,
|
||||||
|
sendResponse?: (response?: BackgroundStoreResponse) => void
|
||||||
) {
|
) {
|
||||||
let tabId = getId(sender);
|
let tabId = getId(sender);
|
||||||
console.log(`Message from tab ${tabId}: ${request.type ?? request.split}`);
|
|
||||||
if (!tabId) return;
|
if (!tabId) return;
|
||||||
if (sender.frameId) tabId = `${tabId}-${sender.frameId}`;
|
if (sender.frameId) tabId = `${tabId}-${sender.frameId}`;
|
||||||
|
|
||||||
if (request.type === 'STOP') {
|
if (request.type === 'STOP') {
|
||||||
if (!Object.keys(store.getState().instances.connections).length) {
|
if (!Object.keys(window.store.getState().instances.connections).length) {
|
||||||
store.dispatch({ type: DISCONNECTED });
|
window.store.dispatch({ type: DISCONNECTED });
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (request.type === 'OPEN_OPTIONS') {
|
if (request.type === 'OPEN_OPTIONS') {
|
||||||
void chrome.runtime.openOptionsPage();
|
chrome.runtime.openOptionsPage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (request.type === 'OPTIONS') {
|
if (request.type === 'GET_OPTIONS') {
|
||||||
toAllTabs({ type: 'OPTIONS', options: request.options });
|
window.syncOptions.get((options) => {
|
||||||
|
sendResponse!({ options });
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (request.type === 'GET_REPORT') {
|
if (request.type === 'GET_REPORT') {
|
||||||
|
@ -458,8 +424,12 @@ function messaging<S, A extends Action<string>>(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (request.type === 'OPEN') {
|
if (request.type === 'OPEN') {
|
||||||
let position: DevToolsPosition = 'devtools-window';
|
let position: DevToolsPosition = 'devtools-left';
|
||||||
if (['remote', 'window'].includes(request.position)) {
|
if (
|
||||||
|
['remote', 'panel', 'left', 'right', 'bottom'].indexOf(
|
||||||
|
request.position
|
||||||
|
) !== -1
|
||||||
|
) {
|
||||||
position = ('devtools-' + request.position) as DevToolsPosition;
|
position = ('devtools-' + request.position) as DevToolsPosition;
|
||||||
}
|
}
|
||||||
openDevToolsWindow(position);
|
openDevToolsWindow(position);
|
||||||
|
@ -467,7 +437,7 @@ function messaging<S, A extends Action<string>>(
|
||||||
}
|
}
|
||||||
if (request.type === 'ERROR') {
|
if (request.type === 'ERROR') {
|
||||||
if (request.payload) {
|
if (request.payload) {
|
||||||
toMonitors(request);
|
toMonitors(request, tabId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!request.message) return;
|
if (!request.message) return;
|
||||||
|
@ -507,56 +477,56 @@ function messaging<S, A extends Action<string>>(
|
||||||
if (request.instanceId) {
|
if (request.instanceId) {
|
||||||
action.request.instanceId = instanceId;
|
action.request.instanceId = instanceId;
|
||||||
}
|
}
|
||||||
store.dispatch(action);
|
window.store.dispatch(action);
|
||||||
|
|
||||||
toMonitors(action);
|
if (request.type === 'EXPORT') {
|
||||||
|
toMonitors(action, tabId, true);
|
||||||
|
} else {
|
||||||
|
toMonitors(action, tabId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function disconnect(
|
function disconnect(
|
||||||
type: 'tab' | 'panel',
|
type: 'tab' | 'monitor' | 'panel',
|
||||||
id: number | string,
|
id: number | string,
|
||||||
listener: (message: any, port: chrome.runtime.Port) => void,
|
listener?: (message: any, port: chrome.runtime.Port) => void
|
||||||
) {
|
) {
|
||||||
return function disconnectListener() {
|
return function disconnectListener() {
|
||||||
console.log(`Disconnected from ${type} ${id}`);
|
|
||||||
|
|
||||||
const p = connections[type][id];
|
const p = connections[type][id];
|
||||||
if (listener && p) p.onMessage.removeListener(listener);
|
if (listener && p) p.onMessage.removeListener(listener);
|
||||||
if (p) p.onDisconnect.removeListener(disconnectListener);
|
if (p) p.onDisconnect.removeListener(disconnectListener);
|
||||||
delete connections[type][id];
|
delete connections[type][id];
|
||||||
if (type === 'tab') {
|
if (type === 'tab') {
|
||||||
if (!store.getState().instances.persisted) {
|
if (!window.store.getState().instances.persisted) {
|
||||||
store.dispatch({ type: REMOVE_INSTANCE, id });
|
window.store.dispatch({ type: REMOVE_INSTANCE, id });
|
||||||
toMonitors({ type: 'NA', id });
|
toMonitors({ type: 'NA', id });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
monitors--;
|
monitors--;
|
||||||
if (monitors === 0) toAllTabs({ type: 'STOP' });
|
if (!monitors) monitorInstances(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function onConnect<S, A extends Action<string>>(port: chrome.runtime.Port) {
|
function onConnect<S, A extends Action<unknown>>(port: chrome.runtime.Port) {
|
||||||
let id: number | string;
|
let id: number | string;
|
||||||
let listener;
|
let listener;
|
||||||
|
|
||||||
store.dispatch({ type: CONNECTED, port });
|
window.store.dispatch({ type: CONNECTED, port });
|
||||||
|
|
||||||
if (port.name === 'tab') {
|
if (port.name === 'tab') {
|
||||||
id = getId(port.sender!);
|
id = getId(port.sender!);
|
||||||
console.log(`Connected to tab ${id}`);
|
|
||||||
if (port.sender!.frameId) id = `${id}-${port.sender!.frameId}`;
|
if (port.sender!.frameId) id = `${id}-${port.sender!.frameId}`;
|
||||||
connections.tab[id] = port;
|
connections.tab[id] = port;
|
||||||
listener = (msg: ContentScriptToBackgroundMessage<S, A>) => {
|
listener = (msg: ContentScriptToBackgroundMessage<S, A>) => {
|
||||||
console.log(`Message from tab ${id}: ${msg.name}`);
|
|
||||||
if (msg.name === 'INIT_INSTANCE') {
|
if (msg.name === 'INIT_INSTANCE') {
|
||||||
if (typeof id === 'number') {
|
if (typeof id === 'number') {
|
||||||
void chrome.action.enable(id);
|
chrome.pageAction.show(id);
|
||||||
void chrome.action.setIcon({ tabId: id, path: 'img/logo/38x38.png' });
|
chrome.pageAction.setIcon({ tabId: id, path: 'img/logo/38x38.png' });
|
||||||
}
|
}
|
||||||
if (monitors > 0) port.postMessage({ type: 'START' });
|
if (isMonitored) port.postMessage({ type: 'START' });
|
||||||
|
|
||||||
const state = store.getState();
|
const state = window.store.getState();
|
||||||
if (state.instances.persisted) {
|
if (state.instances.persisted) {
|
||||||
const instanceId = `${id}/${msg.instanceId}`;
|
const instanceId = `${id}/${msg.instanceId}`;
|
||||||
const persistedState = state.instances.states[instanceId];
|
const persistedState = state.instances.states[instanceId];
|
||||||
|
@ -567,7 +537,7 @@ function onConnect<S, A extends Action<string>>(port: chrome.runtime.Port) {
|
||||||
instanceId,
|
instanceId,
|
||||||
state: stringifyJSON(
|
state: stringifyJSON(
|
||||||
persistedState,
|
persistedState,
|
||||||
state.instances.options[instanceId].serialize,
|
state.instances.options[instanceId].serialize
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -580,45 +550,22 @@ function onConnect<S, A extends Action<string>>(port: chrome.runtime.Port) {
|
||||||
port.onMessage.addListener(listener);
|
port.onMessage.addListener(listener);
|
||||||
port.onDisconnect.addListener(disconnect('tab', id, listener));
|
port.onDisconnect.addListener(disconnect('tab', id, listener));
|
||||||
} else if (port.name && port.name.indexOf('monitor') === 0) {
|
} else if (port.name && port.name.indexOf('monitor') === 0) {
|
||||||
// devpanel
|
|
||||||
id = getId(port.sender!, port.name);
|
id = getId(port.sender!, port.name);
|
||||||
console.log(`Connected to monitor ${id}`);
|
connections.monitor[id] = port;
|
||||||
connections.panel[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++;
|
monitors++;
|
||||||
toAllTabs({ type: 'START' });
|
|
||||||
listener = (msg: BackgroundAction) => {
|
listener = (msg: BackgroundAction) => {
|
||||||
console.log(`Message from monitor ${id}: ${msg.type}`);
|
window.store.dispatch(msg);
|
||||||
store.dispatch(msg);
|
|
||||||
};
|
};
|
||||||
port.onMessage.addListener(listener);
|
port.onMessage.addListener(listener);
|
||||||
port.onDisconnect.addListener(disconnect('panel', id, listener));
|
port.onDisconnect.addListener(disconnect('panel', id, listener));
|
||||||
|
|
||||||
const { current } = store.getState().instances;
|
|
||||||
if (current !== 'default') {
|
|
||||||
const connectionId = Object.entries(
|
|
||||||
store.getState().instances.connections,
|
|
||||||
).find(([, instanceIds]) => instanceIds.includes(current))?.[0];
|
|
||||||
const options = store.getState().instances.options[current];
|
|
||||||
const state = store.getState().instances.states[current];
|
|
||||||
const { actionsById, computedStates, committedState, ...rest } = state;
|
|
||||||
toMonitors({
|
|
||||||
type: UPDATE_STATE,
|
|
||||||
request: {
|
|
||||||
type: 'STATE',
|
|
||||||
payload: rest as Omit<
|
|
||||||
LiftedState<S, A, unknown>,
|
|
||||||
'actionsById' | 'computedStates' | 'committedState'
|
|
||||||
>,
|
|
||||||
source: '@devtools-page',
|
|
||||||
instanceId:
|
|
||||||
typeof current === 'number' ? current.toString() : current,
|
|
||||||
actionsById: stringifyJSON(actionsById, options.serialize),
|
|
||||||
computedStates: stringifyJSON(computedStates, options.serialize),
|
|
||||||
committedState: typeof committedState !== 'undefined',
|
|
||||||
},
|
|
||||||
id: connectionId ?? current,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -629,14 +576,21 @@ chrome.runtime.onMessageExternal.addListener(messaging);
|
||||||
|
|
||||||
chrome.notifications.onClicked.addListener((id) => {
|
chrome.notifications.onClicked.addListener((id) => {
|
||||||
chrome.notifications.clear(id);
|
chrome.notifications.clear(id);
|
||||||
openDevToolsWindow('devtools-window');
|
openDevToolsWindow('devtools-right');
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
declare global {
|
||||||
const api: Middleware<{}, BackgroundState, Dispatch<BackgroundAction>> =
|
interface Window {
|
||||||
(store) => (next) => (untypedAction) => {
|
syncOptions: SyncOptions;
|
||||||
const action = untypedAction as BackgroundAction;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.syncOptions = syncOptions(toAllTabs); // Expose to the options page
|
||||||
|
|
||||||
|
export default function api(
|
||||||
|
store: MiddlewareAPI<Dispatch<BackgroundAction>, BackgroundState>
|
||||||
|
) {
|
||||||
|
return (next: Dispatch<BackgroundAction>) => (action: BackgroundAction) => {
|
||||||
if (action.type === LIFTED_ACTION) toContentScript(action);
|
if (action.type === LIFTED_ACTION) toContentScript(action);
|
||||||
else if (action.type === TOGGLE_PERSIST) {
|
else if (action.type === TOGGLE_PERSIST) {
|
||||||
togglePersist();
|
togglePersist();
|
||||||
|
@ -647,5 +601,4 @@ const api: Middleware<{}, BackgroundState, Dispatch<BackgroundAction>> =
|
||||||
}
|
}
|
||||||
return next(action);
|
return next(action);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
export default api;
|
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
import { combineReducers, Reducer } from 'redux';
|
import { combineReducers, Reducer } from 'redux';
|
||||||
import { instances, InstancesState } from '@redux-devtools/app';
|
import { instances, InstancesState } from '@redux-devtools/app';
|
||||||
import { BackgroundAction } from './backgroundStore';
|
import type { BackgroundAction } from './backgroundStore';
|
||||||
|
|
||||||
export interface BackgroundState {
|
export interface BackgroundState {
|
||||||
readonly instances: InstancesState;
|
readonly instances: InstancesState;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootReducer: Reducer<
|
const rootReducer: Reducer<BackgroundState, BackgroundAction> =
|
||||||
BackgroundState,
|
combineReducers<BackgroundState>({
|
||||||
BackgroundAction,
|
|
||||||
Partial<BackgroundState>
|
|
||||||
> = combineReducers({
|
|
||||||
instances,
|
instances,
|
||||||
}) as any;
|
});
|
||||||
|
|
||||||
export default rootReducer;
|
export default rootReducer;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { createStore, applyMiddleware } from 'redux';
|
import { createStore, applyMiddleware, PreloadedState } from 'redux';
|
||||||
import {
|
import {
|
||||||
CustomAction,
|
CustomAction,
|
||||||
DispatchAction,
|
DispatchAction,
|
||||||
|
@ -60,7 +60,7 @@ export type BackgroundAction =
|
||||||
| DisconnectedAction;
|
| DisconnectedAction;
|
||||||
|
|
||||||
export default function configureStore(
|
export default function configureStore(
|
||||||
preloadedState?: Partial<BackgroundState>,
|
preloadedState?: PreloadedState<BackgroundState>
|
||||||
) {
|
) {
|
||||||
return createStore(rootReducer, preloadedState, applyMiddleware(api));
|
return createStore(rootReducer, preloadedState, applyMiddleware(api));
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,50 +1,34 @@
|
||||||
// Mock not supported chrome.* API for Firefox and Electron
|
// Mock not supported chrome.* API for Firefox and Electron
|
||||||
|
|
||||||
const isElectron = navigator.userAgent.includes('Electron');
|
window.isElectron =
|
||||||
const isFirefox = navigator.userAgent.includes('Firefox');
|
window.navigator && window.navigator.userAgent.indexOf('Electron') !== -1;
|
||||||
|
|
||||||
|
const isFirefox = navigator.userAgent.indexOf('Firefox') !== -1;
|
||||||
|
|
||||||
// Background page only
|
// Background page only
|
||||||
if (
|
if (
|
||||||
(isElectron && location.pathname === '/background.bundle.js') ||
|
(window.isElectron &&
|
||||||
|
location.pathname === '/_generated_background_page.html') ||
|
||||||
isFirefox
|
isFirefox
|
||||||
) {
|
) {
|
||||||
(chrome.runtime as any).onConnectExternal = {
|
(chrome.runtime as any).onConnectExternal = {
|
||||||
addListener() {
|
addListener() {},
|
||||||
// do nothing.
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
(chrome.runtime as any).onMessageExternal = {
|
(chrome.runtime as any).onMessageExternal = {
|
||||||
addListener() {
|
addListener() {},
|
||||||
// do nothing.
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isElectron) {
|
if (window.isElectron) {
|
||||||
(chrome.notifications as any) = {
|
(chrome.notifications as any) = {
|
||||||
onClicked: {
|
onClicked: {
|
||||||
addListener() {
|
addListener() {},
|
||||||
// do nothing.
|
|
||||||
},
|
|
||||||
},
|
|
||||||
create() {
|
|
||||||
// do nothing.
|
|
||||||
},
|
|
||||||
clear() {
|
|
||||||
// do nothing.
|
|
||||||
},
|
},
|
||||||
|
create() {},
|
||||||
|
clear() {},
|
||||||
};
|
};
|
||||||
(chrome.contextMenus as any) = {
|
(chrome.contextMenus as any) = {
|
||||||
onClicked: {
|
onClicked: {
|
||||||
addListener() {
|
addListener() {},
|
||||||
// do nothing.
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
(chrome.commands as any) = {
|
|
||||||
onCommand: {
|
|
||||||
addListener() {
|
|
||||||
// do nothing.
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
@ -55,39 +39,34 @@ if (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isElectron) {
|
if (window.isElectron) {
|
||||||
if (!chrome.storage.local || !chrome.storage.local.remove) {
|
if (!chrome.storage.local || !chrome.storage.local.remove) {
|
||||||
(chrome.storage as any).local = {
|
(chrome.storage as any).local = {
|
||||||
set(items: { [key: string]: string }, callback: () => void) {
|
set(obj: any, callback: any) {
|
||||||
for (const [key, value] of Object.entries(items)) {
|
Object.keys(obj).forEach((key) => {
|
||||||
localStorage.setItem(key, value);
|
localStorage.setItem(key, obj[key]);
|
||||||
}
|
});
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
get(
|
get(obj: any, callback: any) {
|
||||||
keys: { [key: string]: any },
|
const result: any = {};
|
||||||
callback: (items: { [key: string]: any }) => void,
|
Object.keys(obj).forEach((key) => {
|
||||||
) {
|
result[key] = localStorage.getItem(key) || obj[key];
|
||||||
const result = Object.fromEntries(
|
});
|
||||||
Object.entries(keys).map(([key, value]) => [
|
|
||||||
key,
|
|
||||||
localStorage.getItem(key) ?? value,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(result);
|
callback(result);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Electron ~ 1.4.6
|
// Electron ~ 1.4.6
|
||||||
remove(keys: string | string[], callback: () => void) {
|
remove(items: any, callback: any) {
|
||||||
if (Array.isArray(keys)) {
|
if (Array.isArray(items)) {
|
||||||
for (const key of keys) {
|
items.forEach((name) => {
|
||||||
localStorage.removeItem(key);
|
localStorage.removeItem(name);
|
||||||
}
|
});
|
||||||
} else {
|
} else {
|
||||||
localStorage.removeItem(keys);
|
localStorage.removeItem(items);
|
||||||
}
|
}
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
|
@ -97,17 +76,17 @@ if (isElectron) {
|
||||||
}
|
}
|
||||||
// Avoid error: chrome.runtime.sendMessage is not supported responseCallback
|
// Avoid error: chrome.runtime.sendMessage is not supported responseCallback
|
||||||
const originSendMessage = (chrome.runtime as any).sendMessage;
|
const originSendMessage = (chrome.runtime as any).sendMessage;
|
||||||
(chrome.runtime as any).sendMessage = function (...args: unknown[]) {
|
chrome.runtime.sendMessage = function () {
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (process.env.NODE_ENV === 'development') {
|
||||||
return originSendMessage(...args);
|
return originSendMessage(...arguments);
|
||||||
}
|
}
|
||||||
if (typeof args[arguments.length - 1] === 'function') {
|
if (typeof arguments[arguments.length - 1] === 'function') {
|
||||||
Array.prototype.pop.call(args);
|
Array.prototype.pop.call(arguments);
|
||||||
}
|
}
|
||||||
return originSendMessage(...args);
|
return originSendMessage(...arguments);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFirefox || isElectron) {
|
if (isFirefox || window.isElectron) {
|
||||||
(chrome.storage as any).sync = chrome.storage.local;
|
(chrome.storage as any).sync = chrome.storage.local;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import '../chromeApiMock';
|
|
||||||
import {
|
import {
|
||||||
getOptions,
|
injectOptions,
|
||||||
|
getOptionsFromBg,
|
||||||
isAllowed,
|
isAllowed,
|
||||||
Options,
|
|
||||||
prefetchOptions,
|
|
||||||
prepareOptionsForPage,
|
|
||||||
} from '../options/syncOptions';
|
} from '../options/syncOptions';
|
||||||
import type { TabMessage } from '../background/store/apiMiddleware';
|
import type { TabMessage } from '../background/store/apiMiddleware';
|
||||||
import type {
|
import type {
|
||||||
|
@ -18,7 +15,6 @@ import {
|
||||||
DispatchAction as AppDispatchAction,
|
DispatchAction as AppDispatchAction,
|
||||||
} from '@redux-devtools/app';
|
} from '@redux-devtools/app';
|
||||||
import { LiftedState } from '@redux-devtools/instrument';
|
import { LiftedState } from '@redux-devtools/instrument';
|
||||||
|
|
||||||
const source = '@devtools-extension';
|
const source = '@devtools-extension';
|
||||||
const pageSource = '@devtools-page';
|
const pageSource = '@devtools-page';
|
||||||
// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts
|
// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts
|
||||||
|
@ -86,13 +82,6 @@ interface UpdateAction {
|
||||||
readonly source: typeof source;
|
readonly source: typeof source;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OptionsAction {
|
|
||||||
readonly type: 'OPTIONS';
|
|
||||||
readonly options: Options;
|
|
||||||
readonly id: undefined;
|
|
||||||
readonly source: typeof source;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ContentScriptToPageScriptMessage =
|
export type ContentScriptToPageScriptMessage =
|
||||||
| StartAction
|
| StartAction
|
||||||
| StopAction
|
| StopAction
|
||||||
|
@ -100,21 +89,20 @@ export type ContentScriptToPageScriptMessage =
|
||||||
| ImportAction
|
| ImportAction
|
||||||
| ActionAction
|
| ActionAction
|
||||||
| ExportAction
|
| ExportAction
|
||||||
| UpdateAction
|
| UpdateAction;
|
||||||
| OptionsAction;
|
|
||||||
|
|
||||||
interface ImportStatePayload<S, A extends Action<string>> {
|
interface ImportStatePayload<S, A extends Action<unknown>> {
|
||||||
readonly type: 'IMPORT_STATE';
|
readonly type: 'IMPORT_STATE';
|
||||||
readonly nextLiftedState: LiftedState<S, A, unknown> | readonly A[];
|
readonly nextLiftedState: LiftedState<S, A, unknown> | readonly A[];
|
||||||
readonly preloadedState?: S;
|
readonly preloadedState?: S;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportStateDispatchAction<S, A extends Action<string>> {
|
interface ImportStateDispatchAction<S, A extends Action<unknown>> {
|
||||||
readonly type: 'DISPATCH';
|
readonly type: 'DISPATCH';
|
||||||
readonly payload: ImportStatePayload<S, A>;
|
readonly payload: ImportStatePayload<S, A>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ListenerMessage<S, A extends Action<string>> =
|
export type ListenerMessage<S, A extends Action<unknown>> =
|
||||||
| StartAction
|
| StartAction
|
||||||
| StopAction
|
| StopAction
|
||||||
| DispatchAction
|
| DispatchAction
|
||||||
|
@ -122,7 +110,6 @@ export type ListenerMessage<S, A extends Action<string>> =
|
||||||
| ActionAction
|
| ActionAction
|
||||||
| ExportAction
|
| ExportAction
|
||||||
| UpdateAction
|
| UpdateAction
|
||||||
| OptionsAction
|
|
||||||
| ImportStateDispatchAction<S, A>;
|
| ImportStateDispatchAction<S, A>;
|
||||||
|
|
||||||
function postToPageScript(message: ContentScriptToPageScriptMessage) {
|
function postToPageScript(message: ContentScriptToPageScriptMessage) {
|
||||||
|
@ -167,13 +154,8 @@ function connect() {
|
||||||
source,
|
source,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (message.type === 'OPTIONS') {
|
} else if ('options' in message) {
|
||||||
postToPageScript({
|
injectOptions(message.options);
|
||||||
type: message.type,
|
|
||||||
options: prepareOptionsForPage(message.options),
|
|
||||||
id: undefined,
|
|
||||||
source,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
postToPageScript({
|
postToPageScript({
|
||||||
type: message.type,
|
type: message.type,
|
||||||
|
@ -221,13 +203,11 @@ export type SplitMessage =
|
||||||
| SplitMessageChunk
|
| SplitMessageChunk
|
||||||
| SplitMessageEnd;
|
| SplitMessageEnd;
|
||||||
|
|
||||||
function tryCatch<S, A extends Action<string>>(
|
function tryCatch<S, A extends Action<unknown>>(
|
||||||
fn: (
|
fn: (
|
||||||
args:
|
args: PageScriptToContentScriptMessageWithoutDisconnect<S, A> | SplitMessage
|
||||||
| PageScriptToContentScriptMessageWithoutDisconnect<S, A>
|
|
||||||
| SplitMessage,
|
|
||||||
) => void,
|
) => void,
|
||||||
args: PageScriptToContentScriptMessageWithoutDisconnect<S, A>,
|
args: PageScriptToContentScriptMessageWithoutDisconnect<S, A>
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
return fn(args);
|
return fn(args);
|
||||||
|
@ -254,7 +234,7 @@ function tryCatch<S, A extends Action<string>>(
|
||||||
}
|
}
|
||||||
newArgs[key as keyof typeof newArgs] = arg;
|
newArgs[key as keyof typeof newArgs] = arg;
|
||||||
});
|
});
|
||||||
fn(newArgs as SplitMessage);
|
fn(newArgs as any);
|
||||||
for (let i = 0; i < toSplit.length; i++) {
|
for (let i = 0; i < toSplit.length; i++) {
|
||||||
for (let j = 0; j < toSplit[i][1].length; j += maxChromeMsgSize) {
|
for (let j = 0; j < toSplit[i][1].length; j += maxChromeMsgSize) {
|
||||||
fn({
|
fn({
|
||||||
|
@ -281,38 +261,31 @@ interface InitInstanceContentScriptToBackgroundMessage {
|
||||||
readonly instanceId: number;
|
readonly instanceId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RelayMessage<S, A extends Action<string>> {
|
interface RelayMessage<S, A extends Action<unknown>> {
|
||||||
readonly name: 'RELAY';
|
readonly name: 'RELAY';
|
||||||
readonly message:
|
readonly message:
|
||||||
| PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<S, A>
|
| PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<S, A>
|
||||||
| SplitMessage;
|
| SplitMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ContentScriptToBackgroundMessage<S, A extends Action<string>> =
|
export type ContentScriptToBackgroundMessage<S, A extends Action<unknown>> =
|
||||||
| InitInstanceContentScriptToBackgroundMessage
|
| InitInstanceContentScriptToBackgroundMessage
|
||||||
| RelayMessage<S, A>;
|
| RelayMessage<S, A>;
|
||||||
|
|
||||||
function postToBackground<S, A extends Action<string>>(
|
function postToBackground<S, A extends Action<unknown>>(
|
||||||
message: ContentScriptToBackgroundMessage<S, A>,
|
message: ContentScriptToBackgroundMessage<S, A>
|
||||||
) {
|
) {
|
||||||
bg!.postMessage(message);
|
bg!.postMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
function send<S, A extends Action<string>>(
|
function send<S, A extends Action<unknown>>(
|
||||||
message:
|
message:
|
||||||
| PageScriptToContentScriptMessageWithoutDisconnect<S, A>
|
| PageScriptToContentScriptMessageWithoutDisconnect<S, A>
|
||||||
| SplitMessage,
|
| SplitMessage
|
||||||
) {
|
) {
|
||||||
if (!connected) connect();
|
if (!connected) connect();
|
||||||
if (message.type === 'INIT_INSTANCE') {
|
if (message.type === 'INIT_INSTANCE') {
|
||||||
getOptions((options) => {
|
getOptionsFromBg();
|
||||||
postToPageScript({
|
|
||||||
type: 'OPTIONS',
|
|
||||||
options: prepareOptionsForPage(options),
|
|
||||||
id: undefined,
|
|
||||||
source,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
postToBackground({ name: 'INIT_INSTANCE', instanceId: message.instanceId });
|
postToBackground({ name: 'INIT_INSTANCE', instanceId: message.instanceId });
|
||||||
} else {
|
} else {
|
||||||
postToBackground({ name: 'RELAY', message });
|
postToBackground({ name: 'RELAY', message });
|
||||||
|
@ -320,8 +293,8 @@ function send<S, A extends Action<string>>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resend messages from the page to the background script
|
// Resend messages from the page to the background script
|
||||||
function handleMessages<S, A extends Action<string>>(
|
function handleMessages<S, A extends Action<unknown>>(
|
||||||
event: MessageEvent<PageScriptToContentScriptMessage<S, A>>,
|
event: MessageEvent<PageScriptToContentScriptMessage<S, A>>
|
||||||
) {
|
) {
|
||||||
if (!isAllowed()) return;
|
if (!isAllowed()) return;
|
||||||
if (!event || event.source !== window || typeof event.data !== 'object') {
|
if (!event || event.source !== window || typeof event.data !== 'object') {
|
||||||
|
@ -340,6 +313,4 @@ function handleMessages<S, A extends Action<string>>(
|
||||||
tryCatch(send, message);
|
tryCatch(send, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
prefetchOptions();
|
|
||||||
|
|
||||||
window.addEventListener('message', handleMessages, false);
|
window.addEventListener('message', handleMessages, false);
|
||||||
|
|
|
@ -5,13 +5,11 @@ html
|
||||||
meta(charset='UTF-8')
|
meta(charset='UTF-8')
|
||||||
title Redux DevTools
|
title Redux DevTools
|
||||||
include ../style.pug
|
include ../style.pug
|
||||||
|
style.
|
||||||
|
body {
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
body
|
body
|
||||||
#root
|
#root
|
||||||
div(style='display: flex; justify-content: center; align-items: center')
|
|
||||||
img(
|
|
||||||
src='/img/loading.svg',
|
|
||||||
height=300, width=350,
|
|
||||||
)
|
|
||||||
link(href='/devpanel.bundle.css', rel='stylesheet')
|
|
||||||
script(src='/devpanel.bundle.js')
|
script(src='/devpanel.bundle.js')
|
||||||
|
|
|
@ -1,42 +1,32 @@
|
||||||
import '../chromeApiMock';
|
|
||||||
import React, { CSSProperties, ReactNode } from 'react';
|
import React, { CSSProperties, ReactNode } from 'react';
|
||||||
import { createRoot, Root } from 'react-dom/client';
|
import { createRoot, Root } from 'react-dom/client';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { Persistor } from 'redux-persist';
|
import { Persistor } from 'redux-persist';
|
||||||
import {
|
import { REMOVE_INSTANCE, StoreAction } from '@redux-devtools/app';
|
||||||
REMOVE_INSTANCE,
|
|
||||||
StoreAction,
|
|
||||||
StoreState,
|
|
||||||
UPDATE_STATE,
|
|
||||||
} from '@redux-devtools/app';
|
|
||||||
import App from '../app/App';
|
import App from '../app/App';
|
||||||
import configureStore from './store/panelStore';
|
import configureStore from './store/panelStore';
|
||||||
|
|
||||||
|
import './devpanel.pug';
|
||||||
import { Action, Store } from 'redux';
|
import { Action, Store } from 'redux';
|
||||||
import {
|
import type { PanelMessage } from '../background/store/apiMiddleware';
|
||||||
PanelMessageWithoutNA,
|
import type { StoreStateWithoutSocket } from './store/panelReducer';
|
||||||
PanelMessageWithSplitAction,
|
|
||||||
SplitUpdateStateRequest,
|
|
||||||
UpdateStateRequest,
|
|
||||||
} from '../background/store/apiMiddleware';
|
|
||||||
import { PersistGate } from 'redux-persist/integration/react';
|
import { PersistGate } from 'redux-persist/integration/react';
|
||||||
|
|
||||||
const position = location.hash;
|
const position = location.hash;
|
||||||
const messageStyle: CSSProperties = {
|
const messageStyle: CSSProperties = {
|
||||||
paddingTop: '20px',
|
padding: '20px',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
boxSizing: 'border-box',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let rendered: boolean | undefined;
|
let rendered: boolean | undefined;
|
||||||
let currentRoot: Root | undefined;
|
let currentRoot: Root | undefined;
|
||||||
let store: Store<StoreState, StoreAction> | undefined;
|
let store: Store<StoreStateWithoutSocket, StoreAction> | undefined;
|
||||||
let persistor: Persistor | undefined;
|
let persistor: Persistor | undefined;
|
||||||
let bgConnection: chrome.runtime.Port;
|
let bgConnection: chrome.runtime.Port;
|
||||||
let naTimeout: NodeJS.Timeout;
|
let naTimeout: NodeJS.Timeout;
|
||||||
|
|
||||||
const isChrome = !navigator.userAgent.includes('Firefox');
|
const isChrome = navigator.userAgent.indexOf('Firefox') === -1;
|
||||||
|
|
||||||
function renderNodeAtRoot(node: ReactNode) {
|
function renderNodeAtRoot(node: ReactNode) {
|
||||||
if (currentRoot) currentRoot.unmount();
|
if (currentRoot) currentRoot.unmount();
|
||||||
|
@ -52,7 +42,7 @@ function renderDevTools() {
|
||||||
<PersistGate loading={null} persistor={persistor}>
|
<PersistGate loading={null} persistor={persistor}>
|
||||||
<App position={position} />
|
<App position={position} />
|
||||||
</PersistGate>
|
</PersistGate>
|
||||||
</Provider>,
|
</Provider>
|
||||||
);
|
);
|
||||||
rendered = true;
|
rendered = true;
|
||||||
}
|
}
|
||||||
|
@ -67,19 +57,13 @@ function renderNA() {
|
||||||
<a
|
<a
|
||||||
href="https://github.com/zalmoxisus/redux-devtools-extension#usage"
|
href="https://github.com/zalmoxisus/redux-devtools-extension#usage"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
|
||||||
>
|
>
|
||||||
the instructions
|
the instructions
|
||||||
</a>
|
</a>
|
||||||
.
|
.
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
if (
|
if (isChrome) {
|
||||||
isChrome &&
|
|
||||||
chrome &&
|
|
||||||
chrome.devtools &&
|
|
||||||
chrome.devtools.inspectedWindow
|
|
||||||
) {
|
|
||||||
chrome.devtools.inspectedWindow.getResources((resources) => {
|
chrome.devtools.inspectedWindow.getResources((resources) => {
|
||||||
if (resources[0].url.substr(0, 4) === 'file') {
|
if (resources[0].url.substr(0, 4) === 'file') {
|
||||||
message = (
|
message = (
|
||||||
|
@ -88,7 +72,6 @@ function renderNA() {
|
||||||
<a
|
<a
|
||||||
href="https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/Troubleshooting.md#access-file-url-file"
|
href="https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/Troubleshooting.md#access-file-url-file"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
|
||||||
>
|
>
|
||||||
See details
|
See details
|
||||||
</a>
|
</a>
|
||||||
|
@ -107,76 +90,22 @@ function renderNA() {
|
||||||
}, 3500);
|
}, 3500);
|
||||||
}
|
}
|
||||||
|
|
||||||
let splitMessage: SplitUpdateStateRequest<unknown, Action<string>>;
|
function init(id: number) {
|
||||||
|
|
||||||
function init() {
|
|
||||||
renderNA();
|
renderNA();
|
||||||
|
bgConnection = chrome.runtime.connect({
|
||||||
let name = 'monitor';
|
name: id ? id.toString() : undefined,
|
||||||
if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) {
|
});
|
||||||
name += chrome.devtools.inspectedWindow.tabId;
|
|
||||||
}
|
|
||||||
bgConnection = chrome.runtime.connect({ name });
|
|
||||||
|
|
||||||
bgConnection.onMessage.addListener(
|
bgConnection.onMessage.addListener(
|
||||||
<S, A extends Action<string>>(
|
<S, A extends Action<unknown>>(message: PanelMessage<S, A>) => {
|
||||||
message: PanelMessageWithSplitAction<S, A>,
|
|
||||||
) => {
|
|
||||||
if (message.type === 'NA') {
|
if (message.type === 'NA') {
|
||||||
// TODO Double-check this now that the name is different
|
if (message.id === id) renderNA();
|
||||||
if (message.id === name) renderNA();
|
|
||||||
else store!.dispatch({ type: REMOVE_INSTANCE, id: message.id });
|
else store!.dispatch({ type: REMOVE_INSTANCE, id: message.id });
|
||||||
} else {
|
} else {
|
||||||
if (!rendered) renderDevTools();
|
if (!rendered) renderDevTools();
|
||||||
|
store!.dispatch(message);
|
||||||
if (
|
|
||||||
message.type === UPDATE_STATE &&
|
|
||||||
(message.request as SplitUpdateStateRequest<S, A>).split
|
|
||||||
) {
|
|
||||||
const request = message.request as SplitUpdateStateRequest<S, A>;
|
|
||||||
|
|
||||||
if (request.split === 'start') {
|
|
||||||
splitMessage = request;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.split === 'chunk') {
|
|
||||||
if (
|
|
||||||
(splitMessage as unknown as Record<string, string>)[
|
|
||||||
request.chunk[0]
|
|
||||||
]
|
|
||||||
) {
|
|
||||||
(splitMessage as unknown as Record<string, string>)[
|
|
||||||
request.chunk[0]
|
|
||||||
] += request.chunk[1];
|
|
||||||
} else {
|
|
||||||
(splitMessage as unknown as Record<string, string>)[
|
|
||||||
request.chunk[0]
|
|
||||||
] = request.chunk[1];
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.split === 'end') {
|
|
||||||
store!.dispatch({
|
|
||||||
...message,
|
|
||||||
request: splitMessage as UpdateStateRequest<S, A>,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
`Unable to process split message with type: ${(request as any).split}`,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
store!.dispatch(message as PanelMessageWithoutNA<S, A>);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (position === '#popup') document.body.style.minWidth = '760px';
|
init(chrome.devtools.inspectedWindow.tabId);
|
||||||
if (position !== '#popup') document.body.style.minHeight = '100%';
|
|
||||||
|
|
||||||
init();
|
|
||||||
|
|
|
@ -1,32 +1,41 @@
|
||||||
import { combineReducers, Reducer } from 'redux';
|
import { combineReducers, Reducer } from 'redux';
|
||||||
import {
|
import {
|
||||||
connection,
|
connection,
|
||||||
|
ConnectionState,
|
||||||
instances,
|
instances,
|
||||||
|
InstancesState,
|
||||||
monitor,
|
monitor,
|
||||||
|
MonitorState,
|
||||||
notification,
|
notification,
|
||||||
|
NotificationState,
|
||||||
reports,
|
reports,
|
||||||
|
ReportsState,
|
||||||
section,
|
section,
|
||||||
socket,
|
SectionState,
|
||||||
stateTreeSettings,
|
|
||||||
StoreAction,
|
StoreAction,
|
||||||
StoreState,
|
|
||||||
theme,
|
theme,
|
||||||
|
ThemeState,
|
||||||
} from '@redux-devtools/app';
|
} from '@redux-devtools/app';
|
||||||
|
|
||||||
const rootReducer: Reducer<
|
export interface StoreStateWithoutSocket {
|
||||||
StoreState,
|
readonly section: SectionState;
|
||||||
StoreAction,
|
readonly theme: ThemeState;
|
||||||
Partial<StoreState>
|
readonly connection: ConnectionState;
|
||||||
> = combineReducers({
|
readonly monitor: MonitorState;
|
||||||
|
readonly instances: InstancesState;
|
||||||
|
readonly reports: ReportsState;
|
||||||
|
readonly notification: NotificationState;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootReducer: Reducer<StoreStateWithoutSocket, StoreAction> =
|
||||||
|
combineReducers<StoreStateWithoutSocket>({
|
||||||
instances,
|
instances,
|
||||||
monitor,
|
monitor,
|
||||||
reports,
|
reports,
|
||||||
notification,
|
notification,
|
||||||
section,
|
section,
|
||||||
socket,
|
|
||||||
theme,
|
theme,
|
||||||
connection,
|
connection,
|
||||||
stateTreeSettings,
|
});
|
||||||
}) as any;
|
|
||||||
|
|
||||||
export default rootReducer;
|
export default rootReducer;
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
import { createStore, applyMiddleware, Reducer, Store } from 'redux';
|
import { createStore, applyMiddleware, Reducer } from 'redux';
|
||||||
import localForage from 'localforage';
|
import localForage from 'localforage';
|
||||||
import { persistReducer, persistStore } from 'redux-persist';
|
import { persistReducer, persistStore } from 'redux-persist';
|
||||||
import {
|
import { exportStateMiddleware, StoreAction } from '@redux-devtools/app';
|
||||||
exportStateMiddleware,
|
|
||||||
StoreAction,
|
|
||||||
StoreState,
|
|
||||||
} from '@redux-devtools/app';
|
|
||||||
import panelDispatcher from './panelSyncMiddleware';
|
import panelDispatcher from './panelSyncMiddleware';
|
||||||
import rootReducer from './panelReducer';
|
import rootReducer, { StoreStateWithoutSocket } from './panelReducer';
|
||||||
|
|
||||||
const persistConfig = {
|
const persistConfig = {
|
||||||
key: 'redux-devtools',
|
key: 'redux-devtools',
|
||||||
|
@ -15,20 +11,18 @@ const persistConfig = {
|
||||||
storage: localForage,
|
storage: localForage,
|
||||||
};
|
};
|
||||||
|
|
||||||
const persistedReducer: Reducer<StoreState, StoreAction> = persistReducer(
|
const persistedReducer: Reducer<StoreStateWithoutSocket, StoreAction> =
|
||||||
persistConfig,
|
persistReducer(persistConfig, rootReducer) as any;
|
||||||
rootReducer,
|
|
||||||
) as any;
|
|
||||||
|
|
||||||
export default function configureStore(
|
export default function configureStore(
|
||||||
position: string,
|
position: string,
|
||||||
bgConnection: chrome.runtime.Port,
|
bgConnection: chrome.runtime.Port
|
||||||
) {
|
) {
|
||||||
const enhancer = applyMiddleware(
|
const enhancer = applyMiddleware(
|
||||||
exportStateMiddleware,
|
exportStateMiddleware,
|
||||||
panelDispatcher(bgConnection),
|
panelDispatcher(bgConnection)
|
||||||
);
|
);
|
||||||
const store = createStore(persistedReducer, enhancer);
|
const store = createStore(persistedReducer, enhancer);
|
||||||
const persistor = persistStore(store as Store);
|
const persistor = persistStore(store);
|
||||||
return { store, persistor };
|
return { store, persistor };
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,53 +7,22 @@ import {
|
||||||
TOGGLE_PERSIST,
|
TOGGLE_PERSIST,
|
||||||
UPDATE_STATE,
|
UPDATE_STATE,
|
||||||
} from '@redux-devtools/app';
|
} from '@redux-devtools/app';
|
||||||
import { Dispatch, Middleware, MiddlewareAPI } from 'redux';
|
import { Dispatch, MiddlewareAPI } from 'redux';
|
||||||
|
|
||||||
function selectInstance(
|
function panelDispatcher(bgConnection: chrome.runtime.Port) {
|
||||||
tabId: number,
|
let autoselected = false;
|
||||||
store: MiddlewareAPI<Dispatch<StoreAction>, StoreState>,
|
const tabId = chrome.devtools.inspectedWindow.tabId;
|
||||||
next: (action: unknown) => unknown,
|
|
||||||
) {
|
return (store: MiddlewareAPI<Dispatch<StoreAction>, StoreState>) =>
|
||||||
const instances = store.getState().instances;
|
(next: Dispatch<StoreAction>) =>
|
||||||
if (instances.current === 'default') return;
|
(action: StoreAction) => {
|
||||||
const connections = instances.connections[tabId];
|
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) {
|
if (connections && connections.length === 1) {
|
||||||
next({ type: SELECT_INSTANCE, selected: connections[0] });
|
next({ type: SELECT_INSTANCE, selected: connections[0] });
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentTabId(next: (tabId: number) => void) {
|
|
||||||
chrome.tabs.query(
|
|
||||||
{
|
|
||||||
active: true,
|
|
||||||
lastFocusedWindow: true,
|
|
||||||
},
|
|
||||||
(tabs) => {
|
|
||||||
const tab = tabs[0];
|
|
||||||
if (!tab) return;
|
|
||||||
next(tab.id!);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function panelDispatcher(
|
|
||||||
bgConnection: chrome.runtime.Port,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
||||||
): Middleware<{}, StoreState, Dispatch<StoreAction>> {
|
|
||||||
let autoselected = false;
|
|
||||||
|
|
||||||
return (store) => (next) => (untypedAction) => {
|
|
||||||
const action = untypedAction as StoreAction;
|
|
||||||
|
|
||||||
const result = next(action);
|
|
||||||
if (!autoselected && action.type === UPDATE_STATE) {
|
|
||||||
autoselected = true;
|
|
||||||
|
|
||||||
if (chrome.devtools && chrome.devtools.inspectedWindow) {
|
|
||||||
selectInstance(chrome.devtools.inspectedWindow.tabId, store, next);
|
|
||||||
} else {
|
|
||||||
getCurrentTabId((tabId) => selectInstance(tabId, store, next));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (action.type === LIFTED_ACTION || action.type === TOGGLE_PERSIST) {
|
if (action.type === LIFTED_ACTION || action.type === TOGGLE_PERSIST) {
|
||||||
const instances = store.getState().instances;
|
const instances = store.getState().instances;
|
||||||
|
|
|
@ -1,8 +1,19 @@
|
||||||
chrome.devtools.panels.create(
|
import './devtools.pug';
|
||||||
|
|
||||||
|
function createPanel(url: string) {
|
||||||
|
chrome.devtools.panels.create(
|
||||||
'Redux',
|
'Redux',
|
||||||
'img/logo/scalable.png',
|
'img/logo/scalable.png',
|
||||||
'devpanel.html',
|
url,
|
||||||
() => {
|
function () {}
|
||||||
// do nothing.
|
);
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
if (chrome.runtime.getBackgroundPage) {
|
||||||
|
// Check if the background page's object is accessible (not in incognito)
|
||||||
|
chrome.runtime.getBackgroundPage((background) => {
|
||||||
|
createPanel(background ? 'window.html' : 'devpanel.html');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
createPanel('devpanel.html');
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { OptionsProps } from './Options';
|
import { OptionsProps } from './Options';
|
||||||
|
|
||||||
export default function AllowToRunGroup({ options, saveOption }: OptionsProps) {
|
export default ({ options, saveOption }: OptionsProps) => {
|
||||||
const AllowToRunState = {
|
const AllowToRunState = {
|
||||||
EVERYWHERE: true,
|
EVERYWHERE: true,
|
||||||
ON_SPECIFIC_URLS: false,
|
ON_SPECIFIC_URLS: false,
|
||||||
|
@ -50,4 +50,4 @@ export default function AllowToRunGroup({ options, saveOption }: OptionsProps) {
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { OptionsProps } from './Options';
|
import { OptionsProps } from './Options';
|
||||||
|
|
||||||
export default function ContextMenuGroup({
|
export default ({ options, saveOption }: OptionsProps) => {
|
||||||
options,
|
|
||||||
saveOption,
|
|
||||||
}: OptionsProps) {
|
|
||||||
return (
|
return (
|
||||||
<fieldset className="option-group">
|
<fieldset className="option-group">
|
||||||
<legend className="option-group__title">Context Menu</legend>
|
<legend className="option-group__title">Context Menu</legend>
|
||||||
|
@ -26,4 +23,4 @@ export default function ContextMenuGroup({
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { OptionsProps } from './Options';
|
import { OptionsProps } from './Options';
|
||||||
|
|
||||||
export default function EditorGroup({ options, saveOption }: OptionsProps) {
|
export default ({ options, saveOption }: OptionsProps) => {
|
||||||
const EditorState = {
|
const EditorState = {
|
||||||
BROWSER: 0,
|
BROWSER: 0,
|
||||||
EXTERNAL: 1,
|
EXTERNAL: 1,
|
||||||
|
@ -21,7 +21,7 @@ export default function EditorGroup({ options, saveOption }: OptionsProps) {
|
||||||
onChange={() => saveOption('useEditor', EditorState.BROWSER)}
|
onChange={() => saveOption('useEditor', EditorState.BROWSER)}
|
||||||
/>
|
/>
|
||||||
<label className="option__label" htmlFor="editor-browser">
|
<label className="option__label" htmlFor="editor-browser">
|
||||||
{navigator.userAgent.includes('Firefox')
|
{navigator.userAgent.indexOf('Firefox') !== -1
|
||||||
? "Don't open in external editor"
|
? "Don't open in external editor"
|
||||||
: "Use browser's debugger (from browser devpanel only)"}
|
: "Use browser's debugger (from browser devpanel only)"}
|
||||||
</label>
|
</label>
|
||||||
|
@ -80,4 +80,4 @@ export default function EditorGroup({ options, saveOption }: OptionsProps) {
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { FilterState } from '../pageScript/api/filters';
|
import { FilterState } from '../pageScript/api/filters';
|
||||||
import { OptionsProps } from './Options';
|
import { OptionsProps } from './Options';
|
||||||
|
|
||||||
export default function FilterGroup({ options, saveOption }: OptionsProps) {
|
export default ({ options, saveOption }: OptionsProps) => {
|
||||||
return (
|
return (
|
||||||
<fieldset className="option-group">
|
<fieldset className="option-group">
|
||||||
<legend className="option-group__title">
|
<legend className="option-group__title">
|
||||||
|
@ -68,4 +68,4 @@ export default function FilterGroup({ options, saveOption }: OptionsProps) {
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,10 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { OptionsProps } from './Options';
|
import { OptionsProps } from './Options';
|
||||||
|
|
||||||
export default function MiscellaneousGroup({
|
export default ({ options, saveOption }: OptionsProps) => {
|
||||||
options,
|
|
||||||
saveOption,
|
|
||||||
}: OptionsProps) {
|
|
||||||
return (
|
return (
|
||||||
<fieldset className="option-group">
|
<fieldset className="option-group">
|
||||||
<legend className="option-group__title">Miscellaneous</legend>
|
<legend className="option-group__title">Miscellaneous</legend>
|
||||||
|
@ -50,4 +47,4 @@ export default function MiscellaneousGroup({
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
|
@ -10,12 +10,11 @@ export interface OptionsProps {
|
||||||
readonly options: Options;
|
readonly options: Options;
|
||||||
readonly saveOption: <K extends keyof Options>(
|
readonly saveOption: <K extends keyof Options>(
|
||||||
name: K,
|
name: K,
|
||||||
value: Options[K],
|
value: Options[K]
|
||||||
) => void;
|
) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function OptionsComponent(props: OptionsProps) {
|
export default (props: OptionsProps) => (
|
||||||
return (
|
|
||||||
<div>
|
<div>
|
||||||
<EditorGroup {...props} />
|
<EditorGroup {...props} />
|
||||||
<FilterGroup {...props} />
|
<FilterGroup {...props} />
|
||||||
|
@ -25,12 +24,11 @@ export default function OptionsComponent(props: OptionsProps) {
|
||||||
<div style={{ color: 'red' }}>
|
<div style={{ color: 'red' }}>
|
||||||
<br />
|
<br />
|
||||||
<hr />
|
<hr />
|
||||||
Setting options here is discouraged, and will not be possible in the
|
Setting options here is discouraged, and will not be possible in the next
|
||||||
next major release. Please{' '}
|
major release. Please{' '}
|
||||||
<a
|
<a
|
||||||
href="https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md"
|
href="https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
|
||||||
style={{ color: 'red' }}
|
style={{ color: 'red' }}
|
||||||
>
|
>
|
||||||
specify them as parameters
|
specify them as parameters
|
||||||
|
@ -39,7 +37,6 @@ export default function OptionsComponent(props: OptionsProps) {
|
||||||
<a
|
<a
|
||||||
href="https://github.com/zalmoxisus/redux-devtools-extension/issues/296"
|
href="https://github.com/zalmoxisus/redux-devtools-extension/issues/296"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
|
||||||
style={{ color: 'red' }}
|
style={{ color: 'red' }}
|
||||||
>
|
>
|
||||||
the issue
|
the issue
|
||||||
|
@ -47,5 +44,4 @@ export default function OptionsComponent(props: OptionsProps) {
|
||||||
for more details.
|
for more details.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
|
@ -1,26 +1,24 @@
|
||||||
import '../chromeApiMock';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import OptionsComponent from './Options';
|
import OptionsComponent from './Options';
|
||||||
import {
|
import { Options } from './syncOptions';
|
||||||
getOptions,
|
|
||||||
Options,
|
|
||||||
OptionsMessage,
|
|
||||||
saveOption,
|
|
||||||
subscribeToOptions,
|
|
||||||
} from './syncOptions';
|
|
||||||
|
|
||||||
subscribeToOptions((options) => {
|
import './options.pug';
|
||||||
const message: OptionsMessage = { type: 'OPTIONS', options };
|
|
||||||
void chrome.runtime.sendMessage(message);
|
|
||||||
});
|
|
||||||
|
|
||||||
const renderOptions = (options: Options) => {
|
chrome.runtime.getBackgroundPage((background) => {
|
||||||
|
const syncOptions = background!.syncOptions;
|
||||||
|
|
||||||
|
const saveOption = <K extends keyof Options>(name: K, value: Options[K]) => {
|
||||||
|
syncOptions.save(name, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderOptions = (options: Options) => {
|
||||||
const root = createRoot(document.getElementById('root')!);
|
const root = createRoot(document.getElementById('root')!);
|
||||||
root.render(<OptionsComponent options={options} saveOption={saveOption} />);
|
root.render(<OptionsComponent options={options} saveOption={saveOption} />);
|
||||||
};
|
};
|
||||||
|
|
||||||
subscribeToOptions(renderOptions);
|
syncOptions.subscribe(renderOptions);
|
||||||
getOptions((options) => {
|
syncOptions.get((options) => {
|
||||||
renderOptions(options);
|
renderOptions(options);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,22 +38,21 @@ let options: Options | undefined;
|
||||||
let subscribers: ((options: Options) => void)[] = [];
|
let subscribers: ((options: Options) => void)[] = [];
|
||||||
|
|
||||||
export interface OptionsMessage {
|
export interface OptionsMessage {
|
||||||
readonly type: 'OPTIONS';
|
|
||||||
readonly options: Options;
|
readonly options: Options;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const saveOption = <K extends keyof Options>(
|
type ToAllTabs = (msg: OptionsMessage) => void;
|
||||||
key: K,
|
|
||||||
value: Options[K],
|
const save =
|
||||||
) => {
|
(toAllTabs: ToAllTabs | undefined) =>
|
||||||
const obj: { [K1 in keyof Options]?: Options[K1] } = {};
|
<K extends keyof Options>(key: K, value: Options[K]) => {
|
||||||
|
let obj: { [K1 in keyof Options]?: Options[K1] } = {};
|
||||||
obj[key] = value;
|
obj[key] = value;
|
||||||
void chrome.storage.sync.set(obj);
|
chrome.storage.sync.set(obj);
|
||||||
options![key] = value;
|
options![key] = value;
|
||||||
for (const subscriber of subscribers) {
|
toAllTabs!({ options: options! });
|
||||||
subscriber(options!);
|
subscribers.forEach((s) => s(options!));
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
const migrateOldOptions = (oldOptions: OldOrNewOptions): Options => ({
|
const migrateOldOptions = (oldOptions: OldOrNewOptions): Options => ({
|
||||||
...oldOptions,
|
...oldOptions,
|
||||||
|
@ -72,7 +71,7 @@ const migrateOldOptions = (oldOptions: OldOrNewOptions): Options => ({
|
||||||
: oldOptions.filter,
|
: oldOptions.filter,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getOptions = (callback: (options: Options) => void) => {
|
const get = (callback: (options: Options) => void) => {
|
||||||
if (options) callback(options);
|
if (options) callback(options);
|
||||||
else {
|
else {
|
||||||
chrome.storage.sync.get(
|
chrome.storage.sync.get(
|
||||||
|
@ -94,37 +93,72 @@ export const getOptions = (callback: (options: Options) => void) => {
|
||||||
function (items) {
|
function (items) {
|
||||||
options = migrateOldOptions(items as OldOrNewOptions);
|
options = migrateOldOptions(items as OldOrNewOptions);
|
||||||
callback(options);
|
callback(options);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const prefetchOptions = () =>
|
const subscribe = (callback: (options: Options) => void) => {
|
||||||
getOptions(() => {
|
|
||||||
// do nothing.
|
|
||||||
});
|
|
||||||
|
|
||||||
export const subscribeToOptions = (callback: (options: Options) => void) => {
|
|
||||||
subscribers = subscribers.concat(callback);
|
subscribers = subscribers.concat(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toReg = (str: string) =>
|
const toReg = (str: string) =>
|
||||||
str !== '' ? str.split('\n').filter(Boolean).join('|') : null;
|
str !== '' ? str.split('\n').filter(Boolean).join('|') : null;
|
||||||
|
|
||||||
export const prepareOptionsForPage = (options: Options): Options => ({
|
export const injectOptions = (newOptions: Options) => {
|
||||||
...options,
|
if (!newOptions) return;
|
||||||
|
|
||||||
|
options = {
|
||||||
|
...newOptions,
|
||||||
allowlist:
|
allowlist:
|
||||||
options.filter !== FilterState.DO_NOT_FILTER
|
newOptions.filter !== FilterState.DO_NOT_FILTER
|
||||||
? toReg(options.allowlist)!
|
? toReg(newOptions.allowlist)!
|
||||||
: options.allowlist,
|
: newOptions.allowlist,
|
||||||
denylist:
|
denylist:
|
||||||
options.filter !== FilterState.DO_NOT_FILTER
|
newOptions.filter !== FilterState.DO_NOT_FILTER
|
||||||
? toReg(options.denylist)!
|
? toReg(newOptions.denylist)!
|
||||||
: options.denylist,
|
: newOptions.denylist,
|
||||||
});
|
};
|
||||||
|
let s = document.createElement('script');
|
||||||
|
s.type = 'text/javascript';
|
||||||
|
s.appendChild(
|
||||||
|
document.createTextNode(
|
||||||
|
'window.devToolsOptions = Object.assign(window.devToolsOptions||{},' +
|
||||||
|
JSON.stringify(options) +
|
||||||
|
');'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
(document.head || document.documentElement).appendChild(s);
|
||||||
|
s.parentNode!.removeChild(s);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getOptionsFromBg = () => {
|
||||||
|
/* chrome.runtime.sendMessage({ type: 'GET_OPTIONS' }, response => {
|
||||||
|
if (response && response.options) injectOptions(response.options);
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
get((newOptions) => {
|
||||||
|
injectOptions(newOptions);
|
||||||
|
}); // Legacy
|
||||||
|
};
|
||||||
|
|
||||||
export const isAllowed = (localOptions = options) =>
|
export const isAllowed = (localOptions = options) =>
|
||||||
!localOptions ||
|
!localOptions ||
|
||||||
localOptions.inject ||
|
localOptions.inject ||
|
||||||
!localOptions.urls ||
|
!localOptions.urls ||
|
||||||
location.href.match(toReg(localOptions.urls)!);
|
location.href.match(toReg(localOptions.urls)!);
|
||||||
|
|
||||||
|
export interface SyncOptions {
|
||||||
|
readonly save: <K extends keyof Options>(key: K, value: Options[K]) => void;
|
||||||
|
readonly get: (callback: (options: Options) => void) => void;
|
||||||
|
readonly subscribe: (callback: (options: Options) => void) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function syncOptions(toAllTabs?: ToAllTabs): SyncOptions {
|
||||||
|
if (toAllTabs && !options) get(() => {}); // Initialize
|
||||||
|
return {
|
||||||
|
save: save(toAllTabs),
|
||||||
|
get: get,
|
||||||
|
subscribe: subscribe,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -8,10 +8,10 @@ declare global {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Monitor<S, A extends Action<string>> {
|
export default class Monitor<S, A extends Action<unknown>> {
|
||||||
update: (
|
update: (
|
||||||
liftedState?: LiftedState<S, A, unknown> | undefined,
|
liftedState?: LiftedState<S, A, unknown> | undefined,
|
||||||
libConfig?: LibConfig,
|
libConfig?: LibConfig
|
||||||
) => void;
|
) => void;
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
paused?: boolean;
|
paused?: boolean;
|
||||||
|
@ -21,8 +21,8 @@ export default class Monitor<S, A extends Action<string>> {
|
||||||
constructor(
|
constructor(
|
||||||
update: (
|
update: (
|
||||||
liftedState?: LiftedState<S, A, unknown> | undefined,
|
liftedState?: LiftedState<S, A, unknown> | undefined,
|
||||||
libConfig?: LibConfig,
|
libConfig?: LibConfig
|
||||||
) => void,
|
) => void
|
||||||
) {
|
) {
|
||||||
this.update = update;
|
this.update = update;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import mapValues from 'lodash/mapValues';
|
||||||
import { Action } from 'redux';
|
import { Action } from 'redux';
|
||||||
import { LiftedState, PerformAction } from '@redux-devtools/instrument';
|
import { LiftedState, PerformAction } from '@redux-devtools/instrument';
|
||||||
import { LocalFilter } from '@redux-devtools/utils';
|
import { LocalFilter } from '@redux-devtools/utils';
|
||||||
|
@ -20,13 +21,14 @@ export const noFiltersApplied = (localFilter: LocalFilter | undefined) =>
|
||||||
!window.devToolsOptions.filter ||
|
!window.devToolsOptions.filter ||
|
||||||
window.devToolsOptions.filter === FilterState.DO_NOT_FILTER);
|
window.devToolsOptions.filter === FilterState.DO_NOT_FILTER);
|
||||||
|
|
||||||
export function isFiltered<A extends Action<string>>(
|
export function isFiltered<A extends Action<unknown>>(
|
||||||
action: A | string,
|
action: A | string,
|
||||||
localFilter: LocalFilter | undefined,
|
localFilter: LocalFilter | undefined
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
noFiltersApplied(localFilter) ||
|
noFiltersApplied(localFilter) ||
|
||||||
(typeof action !== 'string' && typeof action.type.match !== 'function')
|
(typeof action !== 'string' &&
|
||||||
|
typeof (action.type as string).match !== 'function')
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -39,25 +41,20 @@ export function isFiltered<A extends Action<string>>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterActions<A extends Action<string>>(
|
function filterActions<A extends Action<unknown>>(
|
||||||
actionsById: { [p: number]: PerformAction<A> },
|
actionsById: { [p: number]: PerformAction<A> },
|
||||||
actionSanitizer: ((action: A, id: number) => A) | undefined,
|
actionSanitizer: ((action: A, id: number) => A) | undefined
|
||||||
): { [p: number]: PerformAction<A> } {
|
): { [p: number]: PerformAction<A> } {
|
||||||
if (!actionSanitizer) return actionsById;
|
if (!actionSanitizer) return actionsById;
|
||||||
return Object.fromEntries(
|
return mapValues(actionsById, (action, id) => ({
|
||||||
Object.entries(actionsById).map(([actionId, action]) => [
|
|
||||||
actionId,
|
|
||||||
{
|
|
||||||
...action,
|
...action,
|
||||||
action: actionSanitizer(action.action, actionId as unknown as number),
|
action: actionSanitizer(action.action, id as unknown as number),
|
||||||
},
|
}));
|
||||||
]),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterStates<S>(
|
function filterStates<S>(
|
||||||
computedStates: { state: S; error?: string | undefined }[],
|
computedStates: { state: S; error?: string | undefined }[],
|
||||||
stateSanitizer: ((state: S, index: number) => S) | undefined,
|
stateSanitizer: ((state: S, index: number) => S) | undefined
|
||||||
) {
|
) {
|
||||||
if (!stateSanitizer) return computedStates;
|
if (!stateSanitizer) return computedStates;
|
||||||
return computedStates.map((state, idx) => ({
|
return computedStates.map((state, idx) => ({
|
||||||
|
@ -66,12 +63,12 @@ function filterStates<S>(
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterState<S, A extends Action<string>>(
|
export function filterState<S, A extends Action<unknown>>(
|
||||||
state: LiftedState<S, A, unknown>,
|
state: LiftedState<S, A, unknown>,
|
||||||
localFilter: LocalFilter | undefined,
|
localFilter: LocalFilter | undefined,
|
||||||
stateSanitizer: ((state: S, index: number) => S) | undefined,
|
stateSanitizer: ((state: S, index: number) => S) | undefined,
|
||||||
actionSanitizer: ((action: A, id: number) => A) | undefined,
|
actionSanitizer: ((action: A, id: number) => A) | undefined,
|
||||||
predicate: ((state: S, action: A) => boolean) | undefined,
|
predicate: ((state: S, action: A) => boolean) | undefined
|
||||||
): LiftedState<S, A, unknown> {
|
): LiftedState<S, A, unknown> {
|
||||||
if (predicate || !noFiltersApplied(localFilter)) {
|
if (predicate || !noFiltersApplied(localFilter)) {
|
||||||
const filteredStagedActionIds: number[] = [];
|
const filteredStagedActionIds: number[] = [];
|
||||||
|
@ -97,7 +94,7 @@ export function filterState<S, A extends Action<string>>(
|
||||||
filteredComputedStates.push(
|
filteredComputedStates.push(
|
||||||
stateSanitizer
|
stateSanitizer
|
||||||
? { ...liftedState, state: stateSanitizer(currState, idx) }
|
? { ...liftedState, state: stateSanitizer(currState, idx) }
|
||||||
: liftedState,
|
: liftedState
|
||||||
);
|
);
|
||||||
if (actionSanitizer) {
|
if (actionSanitizer) {
|
||||||
sanitizedActionsById![id] = {
|
sanitizedActionsById![id] = {
|
||||||
|
@ -123,7 +120,7 @@ export function filterState<S, A extends Action<string>>(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PartialLiftedState<S, A extends Action<string>> {
|
export interface PartialLiftedState<S, A extends Action<unknown>> {
|
||||||
readonly actionsById: { [actionId: number]: PerformAction<A> };
|
readonly actionsById: { [actionId: number]: PerformAction<A> };
|
||||||
readonly computedStates: { state: S; error?: string }[];
|
readonly computedStates: { state: S; error?: string }[];
|
||||||
readonly stagedActionIds: readonly number[];
|
readonly stagedActionIds: readonly number[];
|
||||||
|
@ -132,17 +129,17 @@ export interface PartialLiftedState<S, A extends Action<string>> {
|
||||||
readonly committedState?: S;
|
readonly committedState?: S;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function startingFrom<S, A extends Action<string>>(
|
export function startingFrom<S, A extends Action<unknown>>(
|
||||||
sendingActionId: number,
|
sendingActionId: number,
|
||||||
state: LiftedState<S, A, unknown>,
|
state: LiftedState<S, A, unknown>,
|
||||||
localFilter: LocalFilter | undefined,
|
localFilter: LocalFilter | undefined,
|
||||||
stateSanitizer: (<S>(state: S, index: number) => S) | undefined,
|
stateSanitizer: (<S>(state: S, index: number) => S) | undefined,
|
||||||
actionSanitizer:
|
actionSanitizer:
|
||||||
| (<A extends Action<string>>(action: A, id: number) => A)
|
| (<A extends Action<unknown>>(action: A, id: number) => A)
|
||||||
| undefined,
|
| undefined,
|
||||||
predicate:
|
predicate:
|
||||||
| (<S, A extends Action<string>>(state: S, action: A) => boolean)
|
| (<S, A extends Action<unknown>>(state: S, action: A) => boolean)
|
||||||
| undefined,
|
| undefined
|
||||||
): LiftedState<S, A, unknown> | PartialLiftedState<S, A> | undefined {
|
): LiftedState<S, A, unknown> | PartialLiftedState<S, A> | undefined {
|
||||||
const stagedActionIds = state.stagedActionIds;
|
const stagedActionIds = state.stagedActionIds;
|
||||||
if (sendingActionId <= stagedActionIds[1]) return state;
|
if (sendingActionId <= stagedActionIds[1]) return state;
|
||||||
|
@ -181,7 +178,7 @@ export function startingFrom<S, A extends Action<string>>(
|
||||||
newComputedStates.push(
|
newComputedStates.push(
|
||||||
!stateSanitizer
|
!stateSanitizer
|
||||||
? currState
|
? currState
|
||||||
: { ...currState, state: stateSanitizer(currState.state, i) },
|
: { ...currState, state: stateSanitizer(currState.state, i) }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ interface SerializeWithRequiredImmutable extends SerializeWithImmutable {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSerializeWithImmutable(
|
function isSerializeWithImmutable(
|
||||||
serialize: boolean | SerializeWithImmutable,
|
serialize: boolean | SerializeWithImmutable
|
||||||
): serialize is SerializeWithRequiredImmutable {
|
): serialize is SerializeWithRequiredImmutable {
|
||||||
return !!(serialize as SerializeWithImmutable).immutable;
|
return !!(serialize as SerializeWithImmutable).immutable;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ interface SerializeWithRequiredReviver extends SerializeWithImmutable {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isSerializeWithReviver(
|
function isSerializeWithReviver(
|
||||||
serialize: boolean | SerializeWithImmutable,
|
serialize: boolean | SerializeWithImmutable
|
||||||
): serialize is SerializeWithRequiredReviver {
|
): serialize is SerializeWithRequiredReviver {
|
||||||
return !!(serialize as SerializeWithImmutable).immutable;
|
return !!(serialize as SerializeWithImmutable).immutable;
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,9 @@ interface ParsedSerializedLiftedState {
|
||||||
readonly preloadedState?: string;
|
readonly preloadedState?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function importState<S, A extends Action<string>>(
|
export default function importState<S, A extends Action<unknown>>(
|
||||||
state: string | undefined,
|
state: string | undefined,
|
||||||
{ serialize }: Config,
|
{ serialize }: Config
|
||||||
) {
|
) {
|
||||||
if (!state) return undefined;
|
if (!state) return undefined;
|
||||||
let parse = jsan.parse;
|
let parse = jsan.parse;
|
||||||
|
@ -45,8 +45,8 @@ export default function importState<S, A extends Action<string>>(
|
||||||
serialize.immutable,
|
serialize.immutable,
|
||||||
serialize.refs,
|
serialize.refs,
|
||||||
serialize.replacer,
|
serialize.replacer,
|
||||||
serialize.reviver,
|
serialize.reviver
|
||||||
).reviver,
|
).reviver
|
||||||
);
|
);
|
||||||
} else if (isSerializeWithReviver(serialize)) {
|
} else if (isSerializeWithReviver(serialize)) {
|
||||||
parse = (v) => jsan.parse(v, serialize.reviver);
|
parse = (v) => jsan.parse(v, serialize.reviver);
|
||||||
|
@ -58,7 +58,7 @@ export default function importState<S, A extends Action<string>>(
|
||||||
| LiftedState<S, A, unknown> = parse(state) as
|
| LiftedState<S, A, unknown> = parse(state) as
|
||||||
| ParsedSerializedLiftedState
|
| ParsedSerializedLiftedState
|
||||||
| LiftedState<S, A, unknown>;
|
| LiftedState<S, A, unknown>;
|
||||||
const preloadedState =
|
let preloadedState =
|
||||||
'payload' in parsedSerializedLiftedState &&
|
'payload' in parsedSerializedLiftedState &&
|
||||||
parsedSerializedLiftedState.preloadedState
|
parsedSerializedLiftedState.preloadedState
|
||||||
? (parse(parsedSerializedLiftedState.preloadedState) as S)
|
? (parse(parsedSerializedLiftedState.preloadedState) as S)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import jsan, { Options } from 'jsan';
|
import jsan, { Options } from 'jsan';
|
||||||
import { throttle } from 'lodash-es';
|
import throttle from 'lodash/throttle';
|
||||||
import { immutableSerialize } from '@redux-devtools/serialize';
|
import { immutableSerialize } from '@redux-devtools/serialize';
|
||||||
import { getActionsArray, getLocalFilter } from '@redux-devtools/utils';
|
import { getActionsArray, getLocalFilter } from '@redux-devtools/utils';
|
||||||
import { isFiltered, PartialLiftedState } from './filters';
|
import { isFiltered, PartialLiftedState } from './filters';
|
||||||
|
@ -56,7 +56,7 @@ function stringify(obj: unknown, serialize?: Serialize | undefined) {
|
||||||
// 16 MB
|
// 16 MB
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
console.warn(
|
console.warn(
|
||||||
'Application state or actions payloads are too large making Redux DevTools serialization slow and consuming a lot of memory. See https://github.com/reduxjs/redux-devtools-extension/blob/master/docs/Troubleshooting.md#excessive-use-of-memory-and-cpu on how to configure it.',
|
'Application state or actions payloads are too large making Redux DevTools serialization slow and consuming a lot of memory. See https://github.com/reduxjs/redux-devtools-extension/blob/master/docs/Troubleshooting.md#excessive-use-of-memory-and-cpu on how to configure it.'
|
||||||
);
|
);
|
||||||
/* eslint-enable no-console */
|
/* eslint-enable no-console */
|
||||||
stringifyWarned = true;
|
stringifyWarned = true;
|
||||||
|
@ -80,7 +80,7 @@ export function getSerializeParameter(config: Config) {
|
||||||
serialize.immutable,
|
serialize.immutable,
|
||||||
serialize.refs,
|
serialize.refs,
|
||||||
serialize.replacer,
|
serialize.replacer,
|
||||||
serialize.reviver,
|
serialize.reviver
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
replacer: immutableSerializer.replacer,
|
replacer: immutableSerializer.replacer,
|
||||||
|
@ -115,7 +115,7 @@ interface DisconnectMessage {
|
||||||
readonly source: typeof source;
|
readonly source: typeof source;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InitMessage<S, A extends Action<string>> {
|
interface InitMessage<S, A extends Action<unknown>> {
|
||||||
readonly type: 'INIT';
|
readonly type: 'INIT';
|
||||||
readonly payload: string;
|
readonly payload: string;
|
||||||
readonly instanceId: number;
|
readonly instanceId: number;
|
||||||
|
@ -161,7 +161,7 @@ interface SerializedActionMessage {
|
||||||
readonly nextActionId?: number;
|
readonly nextActionId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SerializedStateMessage<S, A extends Action<string>> {
|
interface SerializedStateMessage<S, A extends Action<unknown>> {
|
||||||
readonly type: 'STATE';
|
readonly type: 'STATE';
|
||||||
readonly payload: Omit<
|
readonly payload: Omit<
|
||||||
LiftedState<S, A, unknown>,
|
LiftedState<S, A, unknown>,
|
||||||
|
@ -183,7 +183,7 @@ interface OpenMessage {
|
||||||
|
|
||||||
export type PageScriptToContentScriptMessageForwardedToMonitors<
|
export type PageScriptToContentScriptMessageForwardedToMonitors<
|
||||||
S,
|
S,
|
||||||
A extends Action<string>,
|
A extends Action<unknown>
|
||||||
> =
|
> =
|
||||||
| InitMessage<S, A>
|
| InitMessage<S, A>
|
||||||
| LiftedMessage
|
| LiftedMessage
|
||||||
|
@ -194,7 +194,7 @@ export type PageScriptToContentScriptMessageForwardedToMonitors<
|
||||||
|
|
||||||
export type PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<
|
export type PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<
|
||||||
S,
|
S,
|
||||||
A extends Action<string>,
|
A extends Action<unknown>
|
||||||
> =
|
> =
|
||||||
| PageScriptToContentScriptMessageForwardedToMonitors<S, A>
|
| PageScriptToContentScriptMessageForwardedToMonitors<S, A>
|
||||||
| ErrorMessage
|
| ErrorMessage
|
||||||
|
@ -204,26 +204,25 @@ export type PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<
|
||||||
|
|
||||||
export type PageScriptToContentScriptMessageWithoutDisconnect<
|
export type PageScriptToContentScriptMessageWithoutDisconnect<
|
||||||
S,
|
S,
|
||||||
A extends Action<string>,
|
A extends Action<unknown>
|
||||||
> =
|
> =
|
||||||
| PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<S, A>
|
| PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<S, A>
|
||||||
| InitInstancePageScriptToContentScriptMessage
|
| InitInstancePageScriptToContentScriptMessage
|
||||||
| InitInstanceMessage;
|
| InitInstanceMessage;
|
||||||
|
|
||||||
export type PageScriptToContentScriptMessage<S, A extends Action<string>> =
|
export type PageScriptToContentScriptMessage<S, A extends Action<unknown>> =
|
||||||
| PageScriptToContentScriptMessageWithoutDisconnect<S, A>
|
| PageScriptToContentScriptMessageWithoutDisconnect<S, A>
|
||||||
| DisconnectMessage;
|
| DisconnectMessage;
|
||||||
|
|
||||||
function post<S, A extends Action<string>>(
|
function post<S, A extends Action<unknown>>(
|
||||||
message: PageScriptToContentScriptMessage<S, A>,
|
message: PageScriptToContentScriptMessage<S, A>
|
||||||
) {
|
) {
|
||||||
window.postMessage(message, '*');
|
window.postMessage(message, '*');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStackTrace(
|
function getStackTrace(
|
||||||
config: Config,
|
config: Config,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
toExcludeFromTrace: Function | undefined
|
||||||
toExcludeFromTrace: Function | undefined,
|
|
||||||
) {
|
) {
|
||||||
if (!config.trace) return undefined;
|
if (!config.trace) return undefined;
|
||||||
if (typeof config.trace === 'function') return config.trace();
|
if (typeof config.trace === 'function') return config.trace();
|
||||||
|
@ -249,7 +248,6 @@ function getStackTrace(
|
||||||
typeof Error.stackTraceLimit !== 'number' ||
|
typeof Error.stackTraceLimit !== 'number' ||
|
||||||
Error.stackTraceLimit > traceLimit!
|
Error.stackTraceLimit > traceLimit!
|
||||||
) {
|
) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
||||||
const frames = stack!.split('\n');
|
const frames = stack!.split('\n');
|
||||||
if (frames.length > traceLimit!) {
|
if (frames.length > traceLimit!) {
|
||||||
stack = frames
|
stack = frames
|
||||||
|
@ -260,18 +258,17 @@ function getStackTrace(
|
||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
function amendActionType<A extends Action<string>>(
|
function amendActionType<A extends Action<unknown>>(
|
||||||
action:
|
action:
|
||||||
| A
|
| A
|
||||||
| StructuralPerformAction<A>
|
| StructuralPerformAction<A>
|
||||||
| StructuralPerformAction<A>[]
|
| StructuralPerformAction<A>[]
|
||||||
| string,
|
| string,
|
||||||
config: Config,
|
config: Config,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
toExcludeFromTrace: Function | undefined
|
||||||
toExcludeFromTrace: Function | undefined,
|
|
||||||
): StructuralPerformAction<A> {
|
): StructuralPerformAction<A> {
|
||||||
const timestamp = Date.now();
|
let timestamp = Date.now();
|
||||||
const stack = getStackTrace(config, toExcludeFromTrace);
|
let stack = getStackTrace(config, toExcludeFromTrace);
|
||||||
if (typeof action === 'string') {
|
if (typeof action === 'string') {
|
||||||
return { action: { type: action } as A, timestamp, stack };
|
return { action: { type: action } as A, timestamp, stack };
|
||||||
}
|
}
|
||||||
|
@ -291,7 +288,7 @@ interface LiftedMessage {
|
||||||
readonly source: typeof source;
|
readonly source: typeof source;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PartialStateMessage<S, A extends Action<string>> {
|
interface PartialStateMessage<S, A extends Action<unknown>> {
|
||||||
readonly type: 'PARTIAL_STATE';
|
readonly type: 'PARTIAL_STATE';
|
||||||
readonly payload: PartialLiftedState<S, A>;
|
readonly payload: PartialLiftedState<S, A>;
|
||||||
readonly source: typeof source;
|
readonly source: typeof source;
|
||||||
|
@ -299,7 +296,7 @@ interface PartialStateMessage<S, A extends Action<string>> {
|
||||||
readonly maxAge: number;
|
readonly maxAge: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExportMessage<S, A extends Action<string>> {
|
interface ExportMessage<S, A extends Action<unknown>> {
|
||||||
readonly type: 'EXPORT';
|
readonly type: 'EXPORT';
|
||||||
readonly payload: readonly A[];
|
readonly payload: readonly A[];
|
||||||
readonly committedState: S;
|
readonly committedState: S;
|
||||||
|
@ -307,21 +304,21 @@ interface ExportMessage<S, A extends Action<string>> {
|
||||||
readonly instanceId: number;
|
readonly instanceId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StructuralPerformAction<A extends Action<string>> {
|
export interface StructuralPerformAction<A extends Action<unknown>> {
|
||||||
readonly action: A;
|
readonly action: A;
|
||||||
readonly timestamp?: number;
|
readonly timestamp?: number;
|
||||||
readonly stack?: string;
|
readonly stack?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SingleUserAction<A extends Action<string>> =
|
type SingleUserAction<A extends Action<unknown>> =
|
||||||
| PerformAction<A>
|
| PerformAction<A>
|
||||||
| StructuralPerformAction<A>
|
| StructuralPerformAction<A>
|
||||||
| A;
|
| A;
|
||||||
type UserAction<A extends Action<string>> =
|
type UserAction<A extends Action<unknown>> =
|
||||||
| SingleUserAction<A>
|
| SingleUserAction<A>
|
||||||
| readonly SingleUserAction<A>[];
|
| readonly SingleUserAction<A>[];
|
||||||
|
|
||||||
interface ActionMessage<S, A extends Action<string>> {
|
interface ActionMessage<S, A extends Action<unknown>> {
|
||||||
readonly type: 'ACTION';
|
readonly type: 'ACTION';
|
||||||
readonly payload: S;
|
readonly payload: S;
|
||||||
readonly source: typeof source;
|
readonly source: typeof source;
|
||||||
|
@ -332,7 +329,7 @@ interface ActionMessage<S, A extends Action<string>> {
|
||||||
readonly name?: string;
|
readonly name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface StateMessage<S, A extends Action<string>> {
|
interface StateMessage<S, A extends Action<unknown>> {
|
||||||
readonly type: 'STATE';
|
readonly type: 'STATE';
|
||||||
readonly payload: LiftedState<S, A, unknown>;
|
readonly payload: LiftedState<S, A, unknown>;
|
||||||
readonly source: typeof source;
|
readonly source: typeof source;
|
||||||
|
@ -372,7 +369,7 @@ interface StopMessage {
|
||||||
readonly instanceId: number;
|
readonly instanceId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ToContentScriptMessage<S, A extends Action<string>> =
|
type ToContentScriptMessage<S, A extends Action<unknown>> =
|
||||||
| LiftedMessage
|
| LiftedMessage
|
||||||
| PartialStateMessage<S, A>
|
| PartialStateMessage<S, A>
|
||||||
| ExportMessage<S, A>
|
| ExportMessage<S, A>
|
||||||
|
@ -383,10 +380,10 @@ type ToContentScriptMessage<S, A extends Action<string>> =
|
||||||
| GetReportMessage
|
| GetReportMessage
|
||||||
| StopMessage;
|
| StopMessage;
|
||||||
|
|
||||||
export function toContentScript<S, A extends Action<string>>(
|
export function toContentScript<S, A extends Action<unknown>>(
|
||||||
message: ToContentScriptMessage<S, A>,
|
message: ToContentScriptMessage<S, A>,
|
||||||
serializeState?: Serialize | undefined,
|
serializeState?: Serialize | undefined,
|
||||||
serializeAction?: Serialize | undefined,
|
serializeAction?: Serialize | undefined
|
||||||
) {
|
) {
|
||||||
if (message.type === 'ACTION') {
|
if (message.type === 'ACTION') {
|
||||||
post({
|
post({
|
||||||
|
@ -428,12 +425,12 @@ export function toContentScript<S, A extends Action<string>>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sendMessage<S, A extends Action<string>>(
|
export function sendMessage<S, A extends Action<unknown>>(
|
||||||
action: StructuralPerformAction<A> | StructuralPerformAction<A>[],
|
action: StructuralPerformAction<A> | StructuralPerformAction<A>[],
|
||||||
state: LiftedState<S, A, unknown>,
|
state: LiftedState<S, A, unknown>,
|
||||||
config: Config,
|
config: Config,
|
||||||
instanceId?: number,
|
instanceId?: number,
|
||||||
name?: string,
|
name?: string
|
||||||
) {
|
) {
|
||||||
let amendedAction = action;
|
let amendedAction = action;
|
||||||
if (typeof config !== 'object') {
|
if (typeof config !== 'object') {
|
||||||
|
@ -453,7 +450,7 @@ export function sendMessage<S, A extends Action<string>>(
|
||||||
instanceId: config.instanceId || instanceId || 1,
|
instanceId: config.instanceId || instanceId || 1,
|
||||||
},
|
},
|
||||||
config.serialize as Serialize | undefined,
|
config.serialize as Serialize | undefined,
|
||||||
config.serialize as Serialize | undefined,
|
config.serialize as Serialize | undefined
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
toContentScript<S, A>(
|
toContentScript<S, A>(
|
||||||
|
@ -467,7 +464,7 @@ export function sendMessage<S, A extends Action<string>>(
|
||||||
instanceId: config.instanceId || instanceId || 1,
|
instanceId: config.instanceId || instanceId || 1,
|
||||||
},
|
},
|
||||||
config.serialize as Serialize | undefined,
|
config.serialize as Serialize | undefined,
|
||||||
config.serialize as Serialize | undefined,
|
config.serialize as Serialize | undefined
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -492,16 +489,16 @@ function handleMessages(event: MessageEvent<ContentScriptToPageScriptMessage>) {
|
||||||
|
|
||||||
export function setListener(
|
export function setListener(
|
||||||
onMessage: (message: ContentScriptToPageScriptMessage) => void,
|
onMessage: (message: ContentScriptToPageScriptMessage) => void,
|
||||||
instanceId: number,
|
instanceId: number
|
||||||
) {
|
) {
|
||||||
listeners[instanceId] = onMessage;
|
listeners[instanceId] = onMessage;
|
||||||
window.addEventListener('message', handleMessages, false);
|
window.addEventListener('message', handleMessages, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const liftListener =
|
const liftListener =
|
||||||
<S, A extends Action<string>>(
|
<S, A extends Action<unknown>>(
|
||||||
listener: (message: ListenerMessage<S, A>) => void,
|
listener: (message: ListenerMessage<S, A>) => void,
|
||||||
config: Config,
|
config: Config
|
||||||
) =>
|
) =>
|
||||||
(message: ContentScriptToPageScriptMessage) => {
|
(message: ContentScriptToPageScriptMessage) => {
|
||||||
if (message.type === 'IMPORT') {
|
if (message.type === 'IMPORT') {
|
||||||
|
@ -523,17 +520,17 @@ export function disconnect() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConnectResponse {
|
export interface ConnectResponse {
|
||||||
init: <S, A extends Action<string>>(
|
init: <S, A extends Action<unknown>>(
|
||||||
state: S,
|
state: S,
|
||||||
liftedData?: LiftedState<S, A, unknown>,
|
liftedData?: LiftedState<S, A, unknown>
|
||||||
) => void;
|
) => void;
|
||||||
subscribe: <S, A extends Action<string>>(
|
subscribe: <S, A extends Action<unknown>>(
|
||||||
listener: (message: ListenerMessage<S, A>) => void,
|
listener: (message: ListenerMessage<S, A>) => void
|
||||||
) => (() => void) | undefined;
|
) => (() => void) | undefined;
|
||||||
unsubscribe: () => void;
|
unsubscribe: () => void;
|
||||||
send: <S, A extends Action<string>>(
|
send: <S, A extends Action<unknown>>(
|
||||||
action: A,
|
action: A,
|
||||||
state: LiftedState<S, A, unknown>,
|
state: LiftedState<S, A, unknown>
|
||||||
) => void;
|
) => void;
|
||||||
error: (payload: string) => void;
|
error: (payload: string) => void;
|
||||||
}
|
}
|
||||||
|
@ -553,8 +550,8 @@ export function connect(preConfig: Config): ConnectResponse {
|
||||||
const localFilter = getLocalFilter(config);
|
const localFilter = getLocalFilter(config);
|
||||||
const autoPause = config.autoPause;
|
const autoPause = config.autoPause;
|
||||||
let isPaused = autoPause;
|
let isPaused = autoPause;
|
||||||
let delayedActions: StructuralPerformAction<Action<string>>[] = [];
|
let delayedActions: StructuralPerformAction<Action<unknown>>[] = [];
|
||||||
let delayedStates: LiftedState<unknown, Action<string>, unknown>[] = [];
|
let delayedStates: LiftedState<unknown, Action<unknown>, unknown>[] = [];
|
||||||
|
|
||||||
const rootListener = (action: ContentScriptToPageScriptMessage) => {
|
const rootListener = (action: ContentScriptToPageScriptMessage) => {
|
||||||
if (autoPause) {
|
if (autoPause) {
|
||||||
|
@ -577,13 +574,13 @@ export function connect(preConfig: Config): ConnectResponse {
|
||||||
|
|
||||||
listeners[id] = [rootListener];
|
listeners[id] = [rootListener];
|
||||||
|
|
||||||
const subscribe = <S, A extends Action<string>>(
|
const subscribe = <S, A extends Action<unknown>>(
|
||||||
listener: (message: ListenerMessage<S, A>) => void,
|
listener: (message: ListenerMessage<S, A>) => void
|
||||||
) => {
|
) => {
|
||||||
if (!listener) return undefined;
|
if (!listener) return undefined;
|
||||||
const liftedListener = liftListener(listener, config);
|
const liftedListener = liftListener(listener, config);
|
||||||
const listenersForId = listeners[id] as ((
|
const listenersForId = listeners[id] as ((
|
||||||
message: ContentScriptToPageScriptMessage,
|
message: ContentScriptToPageScriptMessage
|
||||||
) => void)[];
|
) => void)[];
|
||||||
listenersForId.push(liftedListener);
|
listenersForId.push(liftedListener);
|
||||||
|
|
||||||
|
@ -598,18 +595,14 @@ export function connect(preConfig: Config): ConnectResponse {
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendDelayed = throttle(() => {
|
const sendDelayed = throttle(() => {
|
||||||
sendMessage(
|
sendMessage(delayedActions, delayedStates as any, config);
|
||||||
delayedActions,
|
|
||||||
delayedStates as unknown as LiftedState<unknown, Action<string>, unknown>,
|
|
||||||
config,
|
|
||||||
);
|
|
||||||
delayedActions = [];
|
delayedActions = [];
|
||||||
delayedStates = [];
|
delayedStates = [];
|
||||||
}, latency);
|
}, latency);
|
||||||
|
|
||||||
const send = <S, A extends Action<string>>(
|
const send = <S, A extends Action<unknown>>(
|
||||||
action: A,
|
action: A,
|
||||||
state: LiftedState<S, A, unknown>,
|
state: LiftedState<S, A, unknown>
|
||||||
) => {
|
) => {
|
||||||
if (
|
if (
|
||||||
isPaused ||
|
isPaused ||
|
||||||
|
@ -646,13 +639,13 @@ export function connect(preConfig: Config): ConnectResponse {
|
||||||
sendMessage(
|
sendMessage(
|
||||||
amendedAction as StructuralPerformAction<A>,
|
amendedAction as StructuralPerformAction<A>,
|
||||||
amendedState,
|
amendedState,
|
||||||
config,
|
config
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const init = <S, A extends Action<string>>(
|
const init = <S, A extends Action<unknown>>(
|
||||||
state: S,
|
state: S,
|
||||||
liftedData?: LiftedState<S, A, unknown>,
|
liftedData?: LiftedState<S, A, unknown>
|
||||||
) => {
|
) => {
|
||||||
const message: InitMessage<S, A> = {
|
const message: InitMessage<S, A> = {
|
||||||
type: 'INIT',
|
type: 'INIT',
|
||||||
|
|
|
@ -10,7 +10,7 @@ function createExpBackoffTimer(step: number) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Calculate next timeout
|
// Calculate next timeout
|
||||||
const timeout = Math.pow(2, count - 1);
|
let timeout = Math.pow(2, count - 1);
|
||||||
if (count < 5) count += 1;
|
if (count < 5) count += 1;
|
||||||
return timeout * step;
|
return timeout * step;
|
||||||
};
|
};
|
||||||
|
@ -26,7 +26,7 @@ function postError(message: string) {
|
||||||
type: 'ERROR',
|
type: 'ERROR',
|
||||||
message: message,
|
message: message,
|
||||||
},
|
},
|
||||||
'*',
|
'*'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Action } from 'redux';
|
import { Action } from 'redux';
|
||||||
import type { PageScriptToContentScriptMessage } from './index';
|
import type { PageScriptToContentScriptMessage } from './index';
|
||||||
|
|
||||||
export type Position = 'window' | 'remote';
|
export type Position = 'left' | 'right' | 'bottom' | 'panel' | 'remote';
|
||||||
|
|
||||||
function post<S, A extends Action<string>>(
|
function post<S, A extends Action<unknown>>(
|
||||||
message: PageScriptToContentScriptMessage<S, A>,
|
message: PageScriptToContentScriptMessage<S, A>
|
||||||
) {
|
) {
|
||||||
window.postMessage(message, '*');
|
window.postMessage(message, '*');
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,6 @@ export default function openWindow(position?: Position) {
|
||||||
post({
|
post({
|
||||||
source: '@devtools-page',
|
source: '@devtools-page',
|
||||||
type: 'OPEN',
|
type: 'OPEN',
|
||||||
position: position ?? 'window',
|
position: position || 'right',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@ import { persistState } from '@redux-devtools/core';
|
||||||
import type { ConfigWithExpandedMaxAge } from './index';
|
import type { ConfigWithExpandedMaxAge } from './index';
|
||||||
|
|
||||||
export function getUrlParam(key: string) {
|
export function getUrlParam(key: string) {
|
||||||
const matches = new RegExp(`[?&]${key}=([^&#]+)\\b`).exec(
|
const matches = window.location.href.match(
|
||||||
window.location.href,
|
new RegExp(`[?&]${key}=([^&#]+)\\b`)
|
||||||
);
|
);
|
||||||
return matches && matches.length > 0 ? matches[1] : null;
|
return matches && matches.length > 0 ? matches[1] : null;
|
||||||
}
|
}
|
||||||
|
@ -18,13 +18,13 @@ declare global {
|
||||||
|
|
||||||
export default function configureStore<
|
export default function configureStore<
|
||||||
S,
|
S,
|
||||||
A extends Action<string>,
|
A extends Action<unknown>,
|
||||||
MonitorState,
|
MonitorState,
|
||||||
MonitorAction extends Action<string>,
|
MonitorAction extends Action<unknown>
|
||||||
>(
|
>(
|
||||||
next: StoreEnhancerStoreCreator,
|
next: StoreEnhancerStoreCreator,
|
||||||
monitorReducer: Reducer<MonitorState, MonitorAction>,
|
monitorReducer: Reducer<MonitorState, MonitorAction>,
|
||||||
config: ConfigWithExpandedMaxAge,
|
config: ConfigWithExpandedMaxAge
|
||||||
) {
|
) {
|
||||||
return compose(
|
return compose(
|
||||||
instrument(monitorReducer, {
|
instrument(monitorReducer, {
|
||||||
|
@ -37,6 +37,6 @@ export default function configureStore<
|
||||||
shouldStartLocked: config.shouldStartLocked,
|
shouldStartLocked: config.shouldStartLocked,
|
||||||
pauseActionType: config.pauseActionType || '@@PAUSED',
|
pauseActionType: config.pauseActionType || '@@PAUSED',
|
||||||
}),
|
}),
|
||||||
persistState(getUrlParam('debug_session')),
|
persistState(getUrlParam('debug_session'))
|
||||||
)(next);
|
)(next);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,16 @@ import {
|
||||||
getActionsArray,
|
getActionsArray,
|
||||||
getLocalFilter,
|
getLocalFilter,
|
||||||
} from '@redux-devtools/utils';
|
} from '@redux-devtools/utils';
|
||||||
import { throttle } from 'lodash-es';
|
import throttle from 'lodash/throttle';
|
||||||
import { Action, ActionCreator, Dispatch, Reducer, StoreEnhancer } from 'redux';
|
import {
|
||||||
|
Action,
|
||||||
|
ActionCreator,
|
||||||
|
Dispatch,
|
||||||
|
PreloadedState,
|
||||||
|
Reducer,
|
||||||
|
StoreEnhancer,
|
||||||
|
StoreEnhancerStoreCreator,
|
||||||
|
} from 'redux';
|
||||||
import Immutable from 'immutable';
|
import Immutable from 'immutable';
|
||||||
import {
|
import {
|
||||||
EnhancedStore,
|
EnhancedStore,
|
||||||
|
@ -48,15 +56,15 @@ import type { ContentScriptToPageScriptMessage } from '../contentScript';
|
||||||
|
|
||||||
type EnhancedStoreWithInitialDispatch<
|
type EnhancedStoreWithInitialDispatch<
|
||||||
S,
|
S,
|
||||||
A extends Action<string>,
|
A extends Action<unknown>,
|
||||||
MonitorState,
|
MonitorState
|
||||||
> = EnhancedStore<S, A, MonitorState> & { initialDispatch: Dispatch<A> };
|
> = EnhancedStore<S, A, MonitorState> & { initialDispatch: Dispatch<A> };
|
||||||
|
|
||||||
const source = '@devtools-page';
|
const source = '@devtools-page';
|
||||||
const stores: {
|
let stores: {
|
||||||
[K in string | number]: EnhancedStoreWithInitialDispatch<
|
[K in string | number]: EnhancedStoreWithInitialDispatch<
|
||||||
unknown,
|
unknown,
|
||||||
Action<string>,
|
Action<unknown>,
|
||||||
unknown
|
unknown
|
||||||
>;
|
>;
|
||||||
} = {};
|
} = {};
|
||||||
|
@ -65,7 +73,7 @@ let reportId: string | null | undefined;
|
||||||
function deprecateParam(oldParam: string, newParam: string) {
|
function deprecateParam(oldParam: string, newParam: string) {
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
console.warn(
|
console.warn(
|
||||||
`${oldParam} parameter is deprecated, use ${newParam} instead: https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md`,
|
`${oldParam} parameter is deprecated, use ${newParam} instead: https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md`
|
||||||
);
|
);
|
||||||
/* eslint-enable no-console */
|
/* eslint-enable no-console */
|
||||||
}
|
}
|
||||||
|
@ -89,20 +97,20 @@ export interface ConfigWithExpandedMaxAge {
|
||||||
readonly actionsAllowlist?: string | readonly string[];
|
readonly actionsAllowlist?: string | readonly string[];
|
||||||
serialize?: boolean | SerializeWithImmutable;
|
serialize?: boolean | SerializeWithImmutable;
|
||||||
readonly stateSanitizer?: <S>(state: S, index?: number) => S;
|
readonly stateSanitizer?: <S>(state: S, index?: number) => S;
|
||||||
readonly actionSanitizer?: <A extends Action<string>>(
|
readonly actionSanitizer?: <A extends Action<unknown>>(
|
||||||
action: A,
|
action: A,
|
||||||
id?: number,
|
id?: number
|
||||||
) => A;
|
) => A;
|
||||||
readonly predicate?: <S, A extends Action<string>>(
|
readonly predicate?: <S, A extends Action<unknown>>(
|
||||||
state: S,
|
state: S,
|
||||||
action: A,
|
action: A
|
||||||
) => boolean;
|
) => boolean;
|
||||||
readonly latency?: number;
|
readonly latency?: number;
|
||||||
readonly maxAge?:
|
readonly maxAge?:
|
||||||
| number
|
| number
|
||||||
| (<S, A extends Action<string>>(
|
| (<S, A extends Action<unknown>>(
|
||||||
currentLiftedAction: LiftedAction<S, A, unknown>,
|
currentLiftedAction: LiftedAction<S, A, unknown>,
|
||||||
previousLiftedState: LiftedState<S, A, unknown> | undefined,
|
previousLiftedState: LiftedState<S, A, unknown> | undefined
|
||||||
) => number);
|
) => number);
|
||||||
readonly trace?: boolean | (() => string | undefined);
|
readonly trace?: boolean | (() => string | undefined);
|
||||||
readonly traceLimit?: number;
|
readonly traceLimit?: number;
|
||||||
|
@ -115,9 +123,9 @@ export interface ConfigWithExpandedMaxAge {
|
||||||
readonly autoPause?: boolean;
|
readonly autoPause?: boolean;
|
||||||
readonly features?: Features;
|
readonly features?: Features;
|
||||||
readonly type?: string;
|
readonly type?: string;
|
||||||
readonly getActionType?: <A extends Action<string>>(action: A) => A;
|
readonly getActionType?: <A extends Action<unknown>>(action: A) => A;
|
||||||
readonly actionCreators?: {
|
readonly actionCreators?: {
|
||||||
readonly [key: string]: ActionCreator<Action<string>>;
|
readonly [key: string]: ActionCreator<Action<unknown>>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,16 +137,16 @@ interface ReduxDevtoolsExtension {
|
||||||
(config?: Config): StoreEnhancer;
|
(config?: Config): StoreEnhancer;
|
||||||
open: (position?: Position) => void;
|
open: (position?: Position) => void;
|
||||||
notifyErrors: (onError?: () => boolean) => void;
|
notifyErrors: (onError?: () => boolean) => void;
|
||||||
send: <S, A extends Action<string>>(
|
send: <S, A extends Action<unknown>>(
|
||||||
action: StructuralPerformAction<A> | StructuralPerformAction<A>[],
|
action: StructuralPerformAction<A> | StructuralPerformAction<A>[],
|
||||||
state: LiftedState<S, A, unknown>,
|
state: LiftedState<S, A, unknown>,
|
||||||
config: Config,
|
config: Config,
|
||||||
instanceId?: number,
|
instanceId?: number,
|
||||||
name?: string,
|
name?: string
|
||||||
) => void;
|
) => void;
|
||||||
listen: (
|
listen: (
|
||||||
onMessage: (message: ContentScriptToPageScriptMessage) => void,
|
onMessage: (message: ContentScriptToPageScriptMessage) => void,
|
||||||
instanceId: number,
|
instanceId: number
|
||||||
) => void;
|
) => void;
|
||||||
connect: (preConfig: Config) => ConnectResponse;
|
connect: (preConfig: Config) => ConnectResponse;
|
||||||
disconnect: () => void;
|
disconnect: () => void;
|
||||||
|
@ -150,8 +158,8 @@ declare global {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<unknown>>(
|
||||||
config?: Config,
|
config?: Config
|
||||||
): StoreEnhancer {
|
): StoreEnhancer {
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
if (typeof config !== 'object') config = {};
|
if (typeof config !== 'object') config = {};
|
||||||
|
@ -167,7 +175,7 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
const localFilter = getLocalFilter(config);
|
const localFilter = getLocalFilter(config);
|
||||||
const serializeState = getSerializeParameter(config);
|
const serializeState = getSerializeParameter(config);
|
||||||
const serializeAction = getSerializeParameter(config);
|
const serializeAction = getSerializeParameter(config);
|
||||||
const { stateSanitizer, actionSanitizer, predicate, latency = 500 } = config;
|
let { stateSanitizer, actionSanitizer, predicate, latency = 500 } = config;
|
||||||
|
|
||||||
// Deprecate actionsWhitelist and actionsBlacklist
|
// Deprecate actionsWhitelist and actionsBlacklist
|
||||||
if (config.actionsWhitelist) {
|
if (config.actionsWhitelist) {
|
||||||
|
@ -180,7 +188,7 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
const relayState = throttle(
|
const relayState = throttle(
|
||||||
(
|
(
|
||||||
liftedState?: LiftedState<S, A, unknown> | undefined,
|
liftedState?: LiftedState<S, A, unknown> | undefined,
|
||||||
libConfig?: LibConfig,
|
libConfig?: LibConfig
|
||||||
) => {
|
) => {
|
||||||
relayAction.cancel();
|
relayAction.cancel();
|
||||||
const state = liftedState || store.liftedStore.getState();
|
const state = liftedState || store.liftedStore.getState();
|
||||||
|
@ -193,17 +201,17 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
localFilter,
|
localFilter,
|
||||||
stateSanitizer,
|
stateSanitizer,
|
||||||
actionSanitizer,
|
actionSanitizer,
|
||||||
predicate,
|
predicate
|
||||||
),
|
),
|
||||||
source,
|
source,
|
||||||
instanceId,
|
instanceId,
|
||||||
libConfig,
|
libConfig,
|
||||||
},
|
},
|
||||||
serializeState,
|
serializeState,
|
||||||
serializeAction,
|
serializeAction
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
latency,
|
latency
|
||||||
);
|
);
|
||||||
|
|
||||||
const monitor = new Monitor(relayState);
|
const monitor = new Monitor(relayState);
|
||||||
|
@ -225,7 +233,7 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
instanceId,
|
instanceId,
|
||||||
},
|
},
|
||||||
serializeState,
|
serializeState,
|
||||||
serializeAction,
|
serializeAction
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,13 +269,13 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
? liftedState.actionsById[nextActionId - 1]
|
? liftedState.actionsById[nextActionId - 1]
|
||||||
: actionSanitizer(
|
: actionSanitizer(
|
||||||
liftedState.actionsById[nextActionId - 1].action,
|
liftedState.actionsById[nextActionId - 1].action,
|
||||||
nextActionId - 1,
|
nextActionId - 1
|
||||||
),
|
),
|
||||||
maxAge: getMaxAge(),
|
maxAge: getMaxAge(),
|
||||||
nextActionId,
|
nextActionId,
|
||||||
},
|
},
|
||||||
serializeState,
|
serializeState,
|
||||||
serializeAction,
|
serializeAction
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -279,7 +287,7 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
localFilter,
|
localFilter,
|
||||||
stateSanitizer,
|
stateSanitizer,
|
||||||
actionSanitizer,
|
actionSanitizer,
|
||||||
predicate,
|
predicate
|
||||||
);
|
);
|
||||||
sendingActionId = nextActionId;
|
sendingActionId = nextActionId;
|
||||||
if (typeof payload === 'undefined') return;
|
if (typeof payload === 'undefined') return;
|
||||||
|
@ -292,13 +300,13 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
localFilter,
|
localFilter,
|
||||||
stateSanitizer,
|
stateSanitizer,
|
||||||
actionSanitizer,
|
actionSanitizer,
|
||||||
predicate,
|
predicate
|
||||||
),
|
),
|
||||||
source,
|
source,
|
||||||
instanceId,
|
instanceId,
|
||||||
},
|
},
|
||||||
serializeState,
|
serializeState,
|
||||||
serializeAction,
|
serializeAction
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -311,7 +319,7 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
maxAge: getMaxAge(),
|
maxAge: getMaxAge(),
|
||||||
},
|
},
|
||||||
serializeState,
|
serializeState,
|
||||||
serializeAction,
|
serializeAction
|
||||||
);
|
);
|
||||||
}, latency);
|
}, latency);
|
||||||
|
|
||||||
|
@ -329,7 +337,7 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
instanceId,
|
instanceId,
|
||||||
},
|
},
|
||||||
serializeState,
|
serializeState,
|
||||||
serializeAction,
|
serializeAction
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,7 +357,7 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
instanceId,
|
instanceId,
|
||||||
},
|
},
|
||||||
serializeState,
|
serializeState,
|
||||||
serializeAction,
|
serializeAction
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -411,7 +419,7 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
instanceId,
|
instanceId,
|
||||||
},
|
},
|
||||||
serializeState,
|
serializeState,
|
||||||
serializeAction,
|
serializeAction
|
||||||
);
|
);
|
||||||
reportId = null;
|
reportId = null;
|
||||||
}
|
}
|
||||||
|
@ -429,25 +437,18 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
instanceId,
|
instanceId,
|
||||||
},
|
},
|
||||||
serializeState,
|
serializeState,
|
||||||
serializeAction,
|
serializeAction
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
case 'OPTIONS':
|
|
||||||
window.devToolsOptions = Object.assign(
|
|
||||||
window.devToolsOptions || {},
|
|
||||||
message.options,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredActionIds: number[] = []; // simple circular buffer of non-excluded actions with fixed maxAge-1 length
|
const filteredActionIds: number[] = []; // simple circular buffer of non-excluded actions with fixed maxAge-1 length
|
||||||
const getMaxAge = (
|
const getMaxAge = (
|
||||||
liftedAction?: LiftedAction<S, A, unknown>,
|
liftedAction?: LiftedAction<S, A, unknown>,
|
||||||
liftedState?: LiftedState<S, A, unknown> | undefined,
|
liftedState?: LiftedState<S, A, unknown> | undefined
|
||||||
) => {
|
) => {
|
||||||
const m = (config && config.maxAge) || window.devToolsOptions.maxAge || 50;
|
let m = (config && config.maxAge) || window.devToolsOptions.maxAge || 50;
|
||||||
if (
|
if (
|
||||||
!liftedAction ||
|
!liftedAction ||
|
||||||
noFiltersApplied(localFilter) ||
|
noFiltersApplied(localFilter) ||
|
||||||
|
@ -464,7 +465,10 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
if (filteredActionIds.length >= m) {
|
if (filteredActionIds.length >= m) {
|
||||||
const stagedActionIds = liftedState!.stagedActionIds;
|
const stagedActionIds = liftedState!.stagedActionIds;
|
||||||
let i = 1;
|
let i = 1;
|
||||||
while (maxAge > m && !filteredActionIds.includes(stagedActionIds[i])) {
|
while (
|
||||||
|
maxAge > m &&
|
||||||
|
filteredActionIds.indexOf(stagedActionIds[i]) === -1
|
||||||
|
) {
|
||||||
maxAge--;
|
maxAge--;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -493,7 +497,7 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
instanceId,
|
instanceId,
|
||||||
},
|
},
|
||||||
serializeState,
|
serializeState,
|
||||||
serializeAction,
|
serializeAction
|
||||||
);
|
);
|
||||||
store.subscribe(handleChange);
|
store.subscribe(handleChange);
|
||||||
|
|
||||||
|
@ -522,26 +526,32 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
relayState(liftedState);
|
relayState(liftedState);
|
||||||
}
|
}
|
||||||
|
|
||||||
const enhance = (): StoreEnhancer => (next) => {
|
const enhance =
|
||||||
return <S2, A2 extends Action<string>, PreloadedState>(
|
(): StoreEnhancer =>
|
||||||
reducer_: Reducer<S2, A2, PreloadedState>,
|
<NextExt, NextStateExt>(
|
||||||
initialState_?: PreloadedState | undefined,
|
next: StoreEnhancerStoreCreator<NextExt, NextStateExt>
|
||||||
|
): any => {
|
||||||
|
return <S2 extends S, A2 extends A>(
|
||||||
|
reducer_: Reducer<S2, A2>,
|
||||||
|
initialState_?: PreloadedState<S2>
|
||||||
) => {
|
) => {
|
||||||
if (!isAllowed(window.devToolsOptions)) {
|
if (!isAllowed(window.devToolsOptions)) {
|
||||||
return next(reducer_, initialState_);
|
return next(reducer_, initialState_);
|
||||||
}
|
}
|
||||||
|
|
||||||
store = stores[instanceId] = (
|
store = stores[instanceId] = configureStore(
|
||||||
configureStore(next, monitor.reducer, {
|
next as StoreEnhancerStoreCreator,
|
||||||
|
monitor.reducer,
|
||||||
|
{
|
||||||
...config,
|
...config,
|
||||||
maxAge: getMaxAge as any,
|
maxAge: getMaxAge as any,
|
||||||
}) as any
|
}
|
||||||
)(reducer_, initialState_);
|
)(reducer_, initialState_) as any;
|
||||||
|
|
||||||
if (isInIframe()) setTimeout(init, 3000);
|
if (isInIframe()) setTimeout(init, 3000);
|
||||||
else init();
|
else init();
|
||||||
|
|
||||||
return store as any;
|
return store;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -583,36 +593,34 @@ const preEnhancer =
|
||||||
|
|
||||||
export type InferComposedStoreExt<StoreEnhancers> = StoreEnhancers extends [
|
export type InferComposedStoreExt<StoreEnhancers> = StoreEnhancers extends [
|
||||||
infer HeadStoreEnhancer,
|
infer HeadStoreEnhancer,
|
||||||
...infer RestStoreEnhancers,
|
...infer RestStoreEnhancers
|
||||||
]
|
]
|
||||||
? HeadStoreEnhancer extends StoreEnhancer<infer StoreExt>
|
? HeadStoreEnhancer extends StoreEnhancer<infer StoreExt>
|
||||||
? StoreExt & InferComposedStoreExt<RestStoreEnhancers>
|
? StoreExt & InferComposedStoreExt<RestStoreEnhancers>
|
||||||
: never
|
: never
|
||||||
: // eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
: unknown;
|
||||||
{};
|
|
||||||
|
|
||||||
const extensionCompose =
|
const extensionCompose =
|
||||||
(config: Config) =>
|
(config: Config) =>
|
||||||
<StoreEnhancers extends readonly StoreEnhancer[]>(
|
<StoreEnhancers extends readonly StoreEnhancer<unknown>[]>(
|
||||||
...funcs: StoreEnhancers
|
...funcs: StoreEnhancers
|
||||||
): StoreEnhancer<InferComposedStoreExt<StoreEnhancers>> => {
|
): StoreEnhancer<InferComposedStoreExt<StoreEnhancers>> => {
|
||||||
// @ts-expect-error FIXME
|
// @ts-ignore FIXME
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
const instanceId = generateId(config.instanceId);
|
const instanceId = generateId(config.instanceId);
|
||||||
return [preEnhancer(instanceId), ...funcs].reduceRight(
|
return [preEnhancer(instanceId), ...funcs].reduceRight(
|
||||||
|
// @ts-ignore FIXME
|
||||||
(composed, f) => f(composed),
|
(composed, f) => f(composed),
|
||||||
__REDUX_DEVTOOLS_EXTENSION__({ ...config, instanceId })(...args),
|
__REDUX_DEVTOOLS_EXTENSION__({ ...config, instanceId })(...args)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ReduxDevtoolsExtensionCompose {
|
interface ReduxDevtoolsExtensionCompose {
|
||||||
(
|
(config: Config): <StoreEnhancers extends readonly StoreEnhancer<unknown>[]>(
|
||||||
config: Config,
|
|
||||||
): <StoreEnhancers extends readonly StoreEnhancer[]>(
|
|
||||||
...funcs: StoreEnhancers
|
...funcs: StoreEnhancers
|
||||||
) => StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
|
) => StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
|
||||||
<StoreEnhancers extends readonly StoreEnhancer[]>(
|
<StoreEnhancers extends readonly StoreEnhancer<unknown>[]>(
|
||||||
...funcs: StoreEnhancers
|
...funcs: StoreEnhancers
|
||||||
): StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
|
): StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
|
||||||
}
|
}
|
||||||
|
@ -624,23 +632,25 @@ declare global {
|
||||||
}
|
}
|
||||||
|
|
||||||
function reduxDevtoolsExtensionCompose(
|
function reduxDevtoolsExtensionCompose(
|
||||||
config: Config,
|
config: Config
|
||||||
): <StoreEnhancers extends readonly StoreEnhancer[]>(
|
): <StoreEnhancers extends readonly StoreEnhancer<unknown>[]>(
|
||||||
...funcs: StoreEnhancers
|
...funcs: StoreEnhancers
|
||||||
) => StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
|
) => StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
|
||||||
function reduxDevtoolsExtensionCompose<
|
function reduxDevtoolsExtensionCompose<
|
||||||
StoreEnhancers extends readonly StoreEnhancer[],
|
StoreEnhancers extends readonly StoreEnhancer<unknown>[]
|
||||||
>(
|
>(
|
||||||
...funcs: StoreEnhancers
|
...funcs: StoreEnhancers
|
||||||
): StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
|
): StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
|
||||||
function reduxDevtoolsExtensionCompose(...funcs: [Config] | StoreEnhancer[]) {
|
function reduxDevtoolsExtensionCompose(
|
||||||
|
...funcs: [Config] | StoreEnhancer<unknown>[]
|
||||||
|
) {
|
||||||
if (funcs.length === 0) {
|
if (funcs.length === 0) {
|
||||||
return __REDUX_DEVTOOLS_EXTENSION__();
|
return __REDUX_DEVTOOLS_EXTENSION__();
|
||||||
}
|
}
|
||||||
if (funcs.length === 1 && typeof funcs[0] === 'object') {
|
if (funcs.length === 1 && typeof funcs[0] === 'object') {
|
||||||
return extensionCompose(funcs[0]);
|
return extensionCompose(funcs[0]);
|
||||||
}
|
}
|
||||||
return extensionCompose({})(...(funcs as StoreEnhancer[]));
|
return extensionCompose({})(...(funcs as StoreEnhancer<unknown>[]));
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ = reduxDevtoolsExtensionCompose;
|
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ = reduxDevtoolsExtensionCompose;
|
||||||
|
|
17
extension/src/pageScriptWrap.ts
Normal file
17
extension/src/pageScriptWrap.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
let s = document.createElement('script');
|
||||||
|
s.type = 'text/javascript';
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
const { default: script } = require('raw-loader!../dist/page.bundle.js');
|
||||||
|
s.appendChild(document.createTextNode(script));
|
||||||
|
(document.head || document.documentElement).appendChild(s);
|
||||||
|
s.parentNode!.removeChild(s);
|
||||||
|
} else {
|
||||||
|
s.src = chrome.extension.getURL('page.bundle.js');
|
||||||
|
s.onload = function () {
|
||||||
|
(this as HTMLScriptElement).parentNode!.removeChild(
|
||||||
|
this as HTMLScriptElement
|
||||||
|
);
|
||||||
|
};
|
||||||
|
(document.head || document.documentElement).appendChild(s);
|
||||||
|
}
|
|
@ -2,6 +2,8 @@ import React from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import { Root } from '@redux-devtools/app';
|
import { Root } from '@redux-devtools/app';
|
||||||
|
|
||||||
|
import './remote.pug';
|
||||||
|
|
||||||
chrome.storage.local.get(
|
chrome.storage.local.get(
|
||||||
{
|
{
|
||||||
'select-monitor': 'InspectorMonitor',
|
'select-monitor': 'InspectorMonitor',
|
||||||
|
@ -29,7 +31,7 @@ chrome.storage.local.get(
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
/>,
|
/>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user