Showing preview only (477K chars total). Download the full file or copy to clipboard to get everything.
Repository: zalmoxisus/redux-devtools-extension
Branch: master
Commit: 955cfc4f9c2e
Files: 199
Total size: 431.6 KB
Directory structure:
gitextract_tx0nzry9/
├── .babelrc
├── .bookignore
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── appveyor.yml
├── book.json
├── docs/
│ ├── API/
│ │ ├── Arguments.md
│ │ ├── Methods.md
│ │ └── README.md
│ ├── Articles.md
│ ├── Credits.md
│ ├── FAQ.md
│ ├── Features/
│ │ └── Trace.md
│ ├── Feedback.md
│ ├── Integrations.md
│ ├── README.md
│ ├── Recipes.md
│ ├── Troubleshooting.md
│ └── Videos.md
├── examples/
│ ├── buildAll.js
│ ├── counter/
│ │ ├── .babelrc
│ │ ├── actions/
│ │ │ └── counter.js
│ │ ├── components/
│ │ │ └── Counter.js
│ │ ├── containers/
│ │ │ └── App.js
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── reducers/
│ │ │ ├── counter.js
│ │ │ └── index.js
│ │ ├── server.js
│ │ ├── store/
│ │ │ └── configureStore.js
│ │ ├── test/
│ │ │ ├── actions/
│ │ │ │ └── counter.spec.js
│ │ │ ├── components/
│ │ │ │ └── Counter.spec.js
│ │ │ ├── containers/
│ │ │ │ └── App.spec.js
│ │ │ ├── reducers/
│ │ │ │ └── counter.spec.js
│ │ │ └── setup.js
│ │ └── webpack.config.js
│ ├── react-counter-messaging/
│ │ ├── .babelrc
│ │ ├── components/
│ │ │ └── Counter.js
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── package.json
│ │ └── webpack.config.js
│ ├── router/
│ │ ├── .babelrc
│ │ ├── actions/
│ │ │ └── todos.js
│ │ ├── components/
│ │ │ ├── Footer.js
│ │ │ ├── Header.js
│ │ │ ├── MainSection.js
│ │ │ ├── TodoItem.js
│ │ │ └── TodoTextInput.js
│ │ ├── constants/
│ │ │ ├── ActionTypes.js
│ │ │ └── TodoFilters.js
│ │ ├── containers/
│ │ │ ├── App.js
│ │ │ ├── Root.js
│ │ │ └── Wrapper.js
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── reducers/
│ │ │ ├── index.js
│ │ │ └── todos.js
│ │ ├── server.js
│ │ ├── store/
│ │ │ └── configureStore.js
│ │ ├── test/
│ │ │ ├── actions/
│ │ │ │ └── todos.spec.js
│ │ │ ├── components/
│ │ │ │ ├── Footer.spec.js
│ │ │ │ ├── Header.spec.js
│ │ │ │ ├── MainSection.spec.js
│ │ │ │ ├── TodoItem.spec.js
│ │ │ │ └── TodoTextInput.spec.js
│ │ │ ├── reducers/
│ │ │ │ └── todos.spec.js
│ │ │ └── setup.js
│ │ └── webpack.config.js
│ ├── saga-counter/
│ │ ├── .babelrc
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ └── Counter.js
│ │ │ ├── main.js
│ │ │ ├── reducers/
│ │ │ │ └── index.js
│ │ │ └── sagas/
│ │ │ └── index.js
│ │ └── webpack.config.js
│ └── todomvc/
│ ├── .babelrc
│ ├── actions/
│ │ ├── index.js
│ │ └── todos.js
│ ├── components/
│ │ ├── Footer.js
│ │ ├── Header.js
│ │ ├── MainSection.js
│ │ ├── TodoItem.js
│ │ └── TodoTextInput.js
│ ├── constants/
│ │ ├── ActionTypes.js
│ │ └── TodoFilters.js
│ ├── containers/
│ │ └── App.js
│ ├── index.html
│ ├── index.js
│ ├── package.json
│ ├── reducers/
│ │ ├── index.js
│ │ └── todos.js
│ ├── server.js
│ ├── store/
│ │ └── configureStore.js
│ ├── test/
│ │ ├── actions/
│ │ │ └── todos.spec.js
│ │ ├── components/
│ │ │ ├── Footer.spec.js
│ │ │ ├── Header.spec.js
│ │ │ ├── MainSection.spec.js
│ │ │ ├── TodoItem.spec.js
│ │ │ └── TodoTextInput.spec.js
│ │ ├── reducers/
│ │ │ └── todos.spec.js
│ │ └── setup.js
│ └── webpack.config.js
├── gulpfile.babel.js
├── npm-package/
│ ├── README.md
│ ├── developmentOnly.d.ts
│ ├── developmentOnly.js
│ ├── index.d.ts
│ ├── index.js
│ ├── logOnly.d.ts
│ ├── logOnly.js
│ ├── logOnlyInProduction.d.ts
│ ├── logOnlyInProduction.js
│ ├── package.json
│ └── utils/
│ └── assign.js
├── package.json
├── src/
│ ├── app/
│ │ ├── api/
│ │ │ ├── filters.js
│ │ │ ├── generateInstanceId.js
│ │ │ ├── importState.js
│ │ │ ├── index.js
│ │ │ ├── notifyErrors.js
│ │ │ └── openWindow.js
│ │ ├── containers/
│ │ │ └── App.js
│ │ ├── middlewares/
│ │ │ ├── api.js
│ │ │ ├── instanceSelector.js
│ │ │ ├── panelSync.js
│ │ │ └── windowSync.js
│ │ ├── reducers/
│ │ │ ├── background/
│ │ │ │ ├── index.js
│ │ │ │ └── persistStates.js
│ │ │ ├── panel/
│ │ │ │ └── index.js
│ │ │ └── window/
│ │ │ ├── index.js
│ │ │ └── instances.js
│ │ ├── service/
│ │ │ └── Monitor.js
│ │ └── stores/
│ │ ├── backgroundStore.js
│ │ ├── createStore.js
│ │ ├── enhancerStore.js
│ │ ├── panelStore.js
│ │ └── windowStore.js
│ └── browser/
│ ├── extension/
│ │ ├── background/
│ │ │ ├── contextMenus.js
│ │ │ ├── getPreloadedState.js
│ │ │ ├── index.js
│ │ │ ├── logging.js
│ │ │ └── openWindow.js
│ │ ├── chromeAPIMock.js
│ │ ├── devpanel/
│ │ │ └── index.js
│ │ ├── devtools/
│ │ │ └── index.js
│ │ ├── inject/
│ │ │ ├── contentScript.js
│ │ │ ├── deprecatedWarn.js
│ │ │ ├── index.js
│ │ │ ├── pageScript.js
│ │ │ └── pageScriptWrap.js
│ │ ├── manifest.json
│ │ ├── options/
│ │ │ ├── AllowToRunGroup.js
│ │ │ ├── ContextMenuGroup.js
│ │ │ ├── EditorGroup.js
│ │ │ ├── FilterGroup.js
│ │ │ ├── MiscellaneousGroup.js
│ │ │ ├── Options.js
│ │ │ ├── index.js
│ │ │ └── syncOptions.js
│ │ └── window/
│ │ ├── index.js
│ │ └── remote.js
│ ├── firefox/
│ │ └── manifest.json
│ └── views/
│ ├── devpanel.pug
│ ├── devtools.pug
│ ├── includes/
│ │ └── style.pug
│ ├── options.pug
│ ├── remote.pug
│ └── window.pug
├── test/
│ ├── .eslintrc
│ ├── app/
│ │ ├── containers/
│ │ │ └── App.spec.js
│ │ ├── inject/
│ │ │ ├── api.spec.js
│ │ │ └── enhancer.spec.js
│ │ └── setup.js
│ ├── chrome/
│ │ └── extension.spec.js
│ ├── electron/
│ │ ├── devpanel.spec.js
│ │ └── fixture/
│ │ ├── index.html
│ │ ├── main.js
│ │ ├── package.json
│ │ └── renderer.js
│ ├── perf/
│ │ ├── data.js
│ │ └── send.spec.js
│ └── utils/
│ ├── e2e.js
│ └── inject.js
└── webpack/
├── base.config.js
├── dev.config.js
├── prod.config.js
├── replace/
│ ├── JsonpMainTemplate.runtime.js
│ └── log-apply-result.js
└── wrap.config.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .babelrc
================================================
{
"presets": [ "es2015", "stage-0", "react" ],
"plugins": [ "add-module-exports", "transform-decorators-legacy" ]
}
================================================
FILE: .bookignore
================================================
src/
build/
dev/
examples/
npm-package/
test/
package.json
webpack/
================================================
FILE: .eslintignore
================================================
node_modules
build
dev
webpack/replace
examples
test/app/setup.js
npm-package
_book
================================================
FILE: .eslintrc
================================================
{
"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"
]
}
================================================
FILE: .gitignore
================================================
node_modules
npm-debug.log
.DS_Store
.idea/
dist/
build/
dev/
tmp/
_book
================================================
FILE: .travis.yml
================================================
sudo: required
dist: trusty
language: node_js
node_js:
- "6"
cache:
directories:
- $HOME/.yarn-cache
- node_modules
env:
- CXX=g++-4.8
addons:
apt:
sources:
- google-chrome
- ubuntu-toolchain-r-test
packages:
- google-chrome-stable
- g++-4.8
install:
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16"
- npm install -g yarn
- yarn install
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start &
- sleep 3
script:
- yarn test
================================================
FILE: CHANGELOG.md
================================================
# Change Log
This project adheres to [Semantic Versioning](http://semver.org/).
Every release, along with the migration instructions, is documented on the Github [Releases](https://github.com/zalmoxisus/redux-devtools-extension/releases) page.
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015-present Mihail Diordiev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
⚠️⚠️⚠️🚨🚨🚨⚠️⚠️⚠️
## This repo is no longer the home of the redux-devtools-extension. The new home is https://github.com/reduxjs/redux-devtools. Please file your issues and PRs there.
⚠️⚠️⚠️🚨🚨🚨⚠️⚠️⚠️
# Redux DevTools Extension
[](https://gitter.im/zalmoxisus/redux-devtools-extension?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[](http://makeapullrequest.com)
[](#backers)
[](#sponsors)

## Installation
### 1. For Chrome
- from [Chrome Web Store](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd);
- or download `extension.zip` from [last releases](https://github.com/zalmoxisus/redux-devtools-extension/releases), unzip, open `chrome://extensions` url and turn on developer mode from top left and then click; on `Load Unpacked` and select the extracted folder for use
- or build it with `npm i && npm run build:extension` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./build/extension`;
- or run it in dev mode with `npm i && npm start` and [load the extension's folder](https://developer.chrome.com/extensions/getstarted#unpacked) `./dev`.
### 2. For Firefox
- from [Mozilla Add-ons](https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/);
- or build it with `npm i && npm run build:firefox` and [load the extension's folder](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Temporary_Installation_in_Firefox) `./build/firefox` (just select a file from inside the dir).
### 3. For Electron
- just specify `REDUX_DEVTOOLS` in [`electron-devtools-installer`](https://github.com/GPMDP/electron-devtools-installer).
### 4. For other browsers and non-browser environment
- use [`remote-redux-devtools`](https://github.com/zalmoxisus/remote-redux-devtools).
## Usage
> Note that starting from v2.7, `window.devToolsExtension` was renamed to `window.__REDUX_DEVTOOLS_EXTENSION__` / `window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__`.
## 1. With Redux
### 1.1 Basic store
For a basic [Redux store](https://redux.js.org/api/createstore#createstorereducer-preloadedstate-enhancer) simply add:
```diff
const store = createStore(
reducer, /* preloadedState, */
+ window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
```
Note that [`preloadedState`](https://redux.js.org/api/createstore#createstorereducer-preloadedstate-enhancer) argument is optional in Redux's [`createStore`](https://redux.js.org/api/createstore#createstorereducer-preloadedstate-enhancer).
> For universal ("isomorphic") apps, prefix it with `typeof window !== 'undefined' &&`.
```js
const composeEnhancers = (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose;
```
> For TypeScript use [`redux-devtools-extension` npm package](#13-use-redux-devtools-extension-package-from-npm), which contains all the definitions, or just use `(window as any)` (see [Recipes](/docs/Recipes.md#using-in-a-typescript-project) for an example).
```js
const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
```
In case ESLint is configured to not allow using the underscore dangle, wrap it like so:
```diff
+ /* eslint-disable no-underscore-dangle */
const store = createStore(
reducer, /* preloadedState, */
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
+ /* eslint-enable */
```
> **Note**: Passing enhancer as last argument requires **redux@>=3.1.0**. For older versions apply it like [here](https://github.com/zalmoxisus/redux-devtools-extension/blob/v0.4.2/examples/todomvc/store/configureStore.js) or [here](https://github.com/zalmoxisus/redux-devtools-extension/blob/v0.4.2/examples/counter/store/configureStore.js#L7-L12). Don't mix the old Redux API with the new one.
> You don't need to npm install [`redux-devtools`](https://github.com/gaearon/redux-devtools) when using the extension (that's a different lib).
### 1.2 Advanced store setup
If you setup your store with [middleware and enhancers](http://redux.js.org/docs/api/applyMiddleware.html), change:
```diff
import { createStore, applyMiddleware, compose } from 'redux';
+ const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
+ const store = createStore(reducer, /* preloadedState, */ composeEnhancers(
- const store = createStore(reducer, /* preloadedState, */ compose(
applyMiddleware(...middleware)
));
```
> Note that when the extension is not installed, we’re using Redux compose here.
To specify [extension’s options](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md), use it like so:
```js
const composeEnhancers =
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(...middleware),
// other store enhancers if any
);
const store = createStore(reducer, enhancer);
```
> [See the post for more details](https://medium.com/@zalmoxis/improve-your-development-workflow-with-redux-devtools-extension-f0379227ff83).
### 1.3 Use `redux-devtools-extension` package from npm
To make things easier, there's an npm package to install:
```
npm install --save redux-devtools-extension
```
and to use like so:
```js
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
const store = createStore(reducer, composeWithDevTools(
applyMiddleware(...middleware),
// other store enhancers if any
));
```
To specify [extension’s options](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md#windowdevtoolsextensionconfig):
```js
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
const composeEnhancers = composeWithDevTools({
// Specify name here, actionsBlacklist, actionsCreators and other options if needed
});
const store = createStore(reducer, /* preloadedState, */ composeEnhancers(
applyMiddleware(...middleware),
// other store enhancers if any
));
```
> There’re just [few lines of code](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/index.js) added to your bundle.
In case you don't include other enhancers and middlewares, just use `devToolsEnhancer`:
```js
import { createStore } from 'redux';
import { devToolsEnhancer } from 'redux-devtools-extension';
const store = createStore(reducer, /* preloadedState, */ devToolsEnhancer(
// Specify name here, actionsBlacklist, actionsCreators and other options if needed
));
```
### 1.4 Using in production
It's useful to include the extension in production as well. Usually you [can use it for development](https://medium.com/@zalmoxis/using-redux-devtools-in-production-4c5b56c5600f).
If you want to restrict it there, use `redux-devtools-extension/logOnlyInProduction`:
```js
import { createStore } from 'redux';
import { devToolsEnhancer } from 'redux-devtools-extension/logOnlyInProduction';
const store = createStore(reducer, /* preloadedState, */ devToolsEnhancer(
// options like actionSanitizer, stateSanitizer
));
```
or with middlewares and enhancers:
```js
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension/logOnlyInProduction';
const composeEnhancers = composeWithDevTools({
// options like actionSanitizer, stateSanitizer
});
const store = createStore(reducer, /* preloadedState, */ composeEnhancers(
applyMiddleware(...middleware),
// other store enhancers if any
));
```
> You'll have to add `'process.env.NODE_ENV': JSON.stringify('production')` in your Webpack config for the production bundle ([to envify](https://github.com/gaearon/redux-devtools/blob/master/docs/Walkthrough.md#exclude-devtools-from-production-builds)). If you use `create-react-app`, [it already does it for you.](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/config/webpack.config.prod.js#L253-L257)
If you're already checking `process.env.NODE_ENV` when creating the store, include `redux-devtools-extension/logOnly` for production environment.
If you don’t want to allow the extension in production, just use `redux-devtools-extension/developmentOnly`.
> See [the article](https://medium.com/@zalmoxis/using-redux-devtools-in-production-4c5b56c5600f) for more details.
### 1.5 For React Native, hybrid, desktop and server side Redux apps
For React Native we can use [`react-native-debugger`](https://github.com/jhen0409/react-native-debugger), which already included [the same API](https://github.com/jhen0409/react-native-debugger/blob/master/docs/redux-devtools-integration.md) with Redux DevTools Extension.
For most platforms, include [`Remote Redux DevTools`](https://github.com/zalmoxisus/remote-redux-devtools)'s store enhancer, and from the extension's context menu choose 'Open Remote DevTools' for remote monitoring.
## 2. Without Redux
See [integrations](docs/Integrations.md) and [the blog post](https://medium.com/@zalmoxis/redux-devtools-without-redux-or-how-to-have-a-predictable-state-with-any-architecture-61c5f5a7716f) for more details on how to use the extension with any architecture.
## Docs
- [Options (arguments)](docs/API/Arguments.md)
- [Methods (advanced API)](docs/API/Methods.md)
- [FAQ](docs/FAQ.md)
- Features
- [Trace actions calls](/docs/Features/Trace.md)
- [Troubleshooting](docs/Troubleshooting.md)
- [Articles](docs/Articles.md)
- [Videos](docs/Videos.md)
- [Feedback](docs/Feedback.md)
## Demo
Live demos to use the extension with:
- [Counter](http://zalmoxisus.github.io/examples/counter/)
- [TodoMVC](http://zalmoxisus.github.io/examples/todomvc/)
- [Redux Form](http://redux-form.com/6.5.0/examples/simple/)
- [React Tetris](https://chvin.github.io/react-tetris/?lan=en)
- [Book Collection (Angular ngrx store)](https://ngrx.github.io/platform/example-app/)
Also see [`./examples` folder](https://github.com/zalmoxisus/redux-devtools-extension/tree/master/examples).
## Backers
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/redux-devtools-extension#backer)]
<a href="https://opencollective.com/redux-devtools-extension/backer/0/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/1/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/2/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/3/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/4/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/5/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/6/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/7/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/8/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/9/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/10/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/11/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/12/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/13/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/14/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/15/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/16/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/17/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/18/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/19/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/20/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/21/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/22/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/23/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/24/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/25/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/26/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/27/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/28/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/backer/29/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/backer/29/avatar.svg"></a>
## Sponsors
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/redux-devtools-extension#sponsor)]
<a href="https://opencollective.com/redux-devtools-extension/sponsor/0/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/1/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/2/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/3/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/4/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/5/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/6/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/7/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/8/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/9/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/10/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/11/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/12/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/13/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/14/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/15/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/15/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/16/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/17/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/18/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/19/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/20/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/21/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/22/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/23/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/24/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/25/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/26/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/27/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/28/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/redux-devtools-extension/sponsor/29/website" target="_blank"><img src="https://opencollective.com/redux-devtools-extension/sponsor/29/avatar.svg"></a>
## License
MIT
## Created By
If you like this, follow [@mdiordiev](https://twitter.com/mdiordiev) on twitter.
================================================
FILE: appveyor.yml
================================================
environment:
matrix:
- nodejs_version: '6'
cache:
- "%LOCALAPPDATA%/Yarn"
- node_modules
install:
- ps: Install-Product node $env:nodejs_version
- yarn install
test_script:
- node --version
- yarn --version
- yarn test
build: off
================================================
FILE: book.json
================================================
{
"gitbook": "3.2.2",
"title": "Redux DevTools Extension",
"plugins": ["edit-link", "prism", "-highlight", "github", "anchorjs"],
"pluginsConfig": {
"edit-link": {
"base": "https://github.com/zalmoxisus/redux-devtools-extension/tree/master",
"label": "Edit This Page"
},
"github": {
"url": "https://github.com/zalmoxisus/redux-devtools-extension/"
},
"sharing": {
"facebook": true,
"twitter": true
}
}
}
================================================
FILE: docs/API/Arguments.md
================================================
# Options
Use with
- `window.__REDUX_DEVTOOLS_EXTENSION__([options])`
- `window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__([options])()`
- `window.__REDUX_DEVTOOLS_EXTENSION__.connect([options])`
- `redux-devtools-extension` npm package:
```js
import { composeWithDevTools } from 'redux-devtools-extension';
const composeEnhancers = composeWithDevTools(options);
const store = createStore(reducer, /* preloadedState, */ composeEnhancers(
applyMiddleware(...middleware),
// other store enhancers if any
));
```
The `options` object is optional, and can include any of the following.
### `name`
*string* - the instance name to be shown on the monitor page. Default value is `document.title`. If not specified and there's no document title, it will consist of `tabId` and `instanceId`.
### `actionCreators`
*array* or *object* - action creators functions to be available in the Dispatcher. See [the example](https://github.com/zalmoxisus/redux-devtools-extension/commit/477e69d8649dfcdc9bf84dd45605dab7d9775c03).
### `latency`
*number (in ms)* - if more than one action is dispatched in the indicated interval, all new actions will be collected and sent at once. It is the joint between performance and speed. When set to `0`, all actions will be sent instantly. Set it to a higher value when experiencing perf issues (also `maxAge` to a lower value). Default is `500 ms`.
### `maxAge`
*number* (>1) - maximum allowed actions to be stored in the history tree. The oldest actions are removed once maxAge is reached. It's critical for performance. Default is `50`.
### `trace`
*boolean* or *function* - if set to `true`, will include stack trace for every dispatched action, so you can see it in trace tab jumping directly to that part of code ([more details](../Features/Trace.md)). You can use a function (with action object as argument) which should return `new Error().stack` string, getting the stack outside of reducers. Default to `false`.
### `traceLimit`
*number* - maximum stack trace frames to be stored (in case `trace` option was provided as `true`). By default it's `10`. Note that, because extension's calls are excluded, the resulted frames could be 1 less. If `trace` option is a function, `traceLimit` will have no effect, as it's supposed to be handled there.
### `serialize`
*boolean* or *object* which contains:
- **options** `object or boolean`:
- `undefined` - will use regular `JSON.stringify` to send data (it's the fast mode).
- `false` - will handle also circular references.
- `true` - will handle also date, regex, undefined, primitives, error objects, symbols, maps, sets and functions.
- object, which contains `date`, `regex`, `undefined`, `nan`, `infinity`, `error`, `symbol`, `map`, `set` and `function` keys. For each of them you can indicate if to include (by setting as `true`). For `function` key you can also specify a custom function which handles serialization. See [`jsan`](https://github.com/kolodny/jsan) for more details. Example:
```js
const store = Redux.createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
serialize: {
options: {
undefined: true,
function: function(fn) { return fn.toString() }
}
}
}));
```
- **replacer** `function(key, value)` - [JSON `replacer` function](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter) used for both actions and states stringify.
Example of usage with [mori data structures](https://github.com/swannodette/mori):
```js
const store = Redux.createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
serialize: {
replacer: (key, value) => value && mori.isMap(value) ? mori.toJs(value) : value
}
}));
```
In addition, you can specify a data type by adding a [`__serializedType__`](https://github.com/zalmoxisus/remotedev-serialize/blob/master/helpers/index.js#L4) key. So you can deserialize it back while importing or persisting data. Moreover, it will also [show a nice preview showing the provided custom type](https://cloud.githubusercontent.com/assets/7957859/21814330/a17d556a-d761-11e6-85ef-159dd12f36c5.png):
```js
const store = Redux.createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
serialize: {
replacer: (key, value) => {
if (Immutable.List.isList(value)) { // use your custom data type checker
return {
data: value.toArray(), // ImmutableJS custom method to get JS data as array
__serializedType__: 'ImmutableList' // mark you custom data type to show and retrieve back
}
}
}
}
}));
```
- **reviver** `function(key, value)` - [JSON `reviver` function](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Using_the_reviver_parameter) used for parsing the imported actions and states. See [`remotedev-serialize`](https://github.com/zalmoxisus/remotedev-serialize/blob/master/immutable/serialize.js#L8-L41) as an example on how to serialize special data types and get them back:
```js
const store = Redux.createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
serialize: {
reviver: (key, value) => {
if (typeof value === 'object' && value !== null && '__serializedType__' in value) {
switch (value.__serializedType__) {
case 'ImmutableList': return Immutable.List(value.data);
}
}
}
}
}));
```
- **immutable** `object` - automatically serialize/deserialize immutablejs via [remotedev-serialize](https://github.com/zalmoxisus/remotedev-serialize). Just pass the Immutable library like so:
```js
import Immutable from 'immutable'; // https://facebook.github.io/immutable-js/
// ...
// Like above, only showing off compose this time. Reminder you might not want this in prod.
const composeEnhancers = typeof window === 'object' && typeof window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ !== 'undefined' ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
serialize: {
immutable: Immutable
}
}) : compose;
```
It will support all ImmutableJS structures. You can even export them into a file and get them back. The only exception is `Record` class, for which you should pass in addition the references to your classes in `refs`.
- **refs** `array` - ImmutableJS `Record` classes used to make possible restore its instances back when importing, persisting... Example of usage:
``` js
import Immutable from 'immutable';
// ...
const ABRecord = Immutable.Record({ a:1, b:2 });
const myRecord = new ABRecord({ b:3 }); // used in the reducers
const store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
serialize: {
immutable: Immutable,
refs: [ABRecord]
}
}));
```
Also you can specify alternative values right in the state object (in the initial state of the reducer) by adding `toJSON` function:
In the example bellow it will always send `{ component: '[React]' }`, regardless of the state's `component` value (useful when you don't want to send lots of unnecessary data):
```js
function component(
state = { component: null, toJSON: () => ({ component: '[React]' }) },
action
) {
switch (action.type) {
case 'ADD_COMPONENT': return { component: action.component };
default: return state;
}
}
```
You could also alter the value. For example when state is `{ count: 1 }`, we'll send `{ counter: 10 }` (notice we don't have an arrow function this time to use the object's `this`):
```js
function counter(
state = { count: 0, toJSON: function (){ return { conter: this.count * 10 }; } },
action
) {
switch (action.type) {
case 'INCREMENT': return { count: state.count + 1 };
default: return state;
}
}
```
### `actionSanitizer` / `stateSanitizer`
- **actionSanitizer** (*function*) - function which takes `action` object and id number as arguments, and should return `action` object back. See the example bellow.
- **stateSanitizer** (*function*) - function which takes `state` object and index as arguments, and should return `state` object back.
Example of usage:
```js
const actionSanitizer = (action) => (
action.type === 'FILE_DOWNLOAD_SUCCESS' && action.data ?
{ ...action, data: '<<LONG_BLOB>>' } : action
);
const store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
actionSanitizer,
stateSanitizer: (state) => state.data ? { ...state, data: '<<LONG_BLOB>>' } : state
}));
```
### `actionsBlacklist` / `actionsWhitelist`
*string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers). If `actionsWhitelist` specified, `actionsBlacklist` is ignored.
Example:
```js
createStore(reducer, remotedev({
sendTo: 'http://localhost:8000',
actionsBlacklist: 'SOME_ACTION'
// or actionsBlacklist: ['SOME_ACTION', 'SOME_OTHER_ACTION']
// or just actionsBlacklist: 'SOME_' to omit both
}))
```
### `predicate`
*function* - called for every action before sending, takes `state` and `action` object, and returns `true` in case it allows sending the current data to the monitor. Use it as a more advanced version of `actionsBlacklist`/`actionsWhitelist` parameters.
Example of usage:
```js
const store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
predicate: (state, action) => state.dev.logLevel === VERBOSE && !action.forwarded
}));
```
### `shouldRecordChanges`
*boolean* - if specified as `false`, it will not record the changes till clicking on `Start recording` button. Default is `true`. Available only for Redux enhancer, for others use `autoPause`.
### `pauseActionType`
*string* - if specified, whenever clicking on `Pause recording` button and there are actions in the history log, will add this action type. If not specified, will commit when paused. Available only for Redux enhancer. Default is `@@PAUSED`.
### `autoPause`
*boolean* - auto pauses when the extension’s window is not opened, and so has zero impact on your app when not in use. Not available for Redux enhancer (as it already does it but storing the data to be sent). Default is `false`.
### `shouldStartLocked`
*boolean* - if specified as `true`, it will not allow any non-monitor actions to be dispatched till clicking on `Unlock changes` button. Available only for Redux enhancer. Default is `false`.
### `shouldHotReload`
*boolean* - if set to `false`, will not recompute the states on hot reloading (or on replacing the reducers). Available only for Redux enhancer. Default to `true`.
### `shouldCatchErrors`
*boolean* - if specified as `true`, whenever there's an exception in reducers, the monitors will show the error message, and next actions will not be dispatched.
### `features`
If you want to restrict the extension, just specify the features you allow:
```js
const composeEnhancers = composeWithDevTools({
features: {
pause: true, // start/pause recording of dispatched actions
lock: true, // lock/unlock dispatching actions and side effects
persist: true, // persist states on page reloading
export: true, // export history of actions in a file
import: 'custom', // import history of actions from a file
jump: true, // jump back and forth (time travelling)
skip: true, // skip (cancel) actions
reorder: true, // drag and drop actions in the history list
dispatch: true, // dispatch custom actions or action creators
test: true // generate tests for the selected actions
},
// other options like actionSanitizer, stateSanitizer
});
```
If not specified, all of the features are enabled. When set as an object, only those included as `true` will be allowed.
Note that except `true`/`false`, `import` and `export` can be set as `custom` (which is by default for Redux enhancer), meaning that the importing/exporting occurs on the client side. Otherwise, you'll get/set the data right from the monitor part.
================================================
FILE: docs/API/Methods.md
================================================
## Communicate with the extension directly
> Note this is advanced API, which you usually don't need to use with Redux enhancer.
Use the following methods of `window.__REDUX_DEVTOOLS_EXTENSION__`:
- [connect](#connect)
- [disconnect](#disconnect)
- [send](#send)
- [listen](#listen)
- [open](#open)
- [notifyErrors](#notifyerrors)
<a id="connect"></a>
### connect([options])
##### Arguments
- [`options`] *Object* - [see the available options](Arguments.md).
##### Returns
*Object* containing the following methods:
- `subscribe(listener)` - adds a change listener. It will be called any time an action is dispatched from the monitor. Returns a function to unsubscribe the current listener.
- `unsubscribe()` - unsubscribes all listeners.
- `send(action, state)` - sends a new action and state manually to be shown on the monitor. If action is `null` then we suppose we send `liftedState`.
- `init(state)` - sends the initial state to the monitor.
- `error(message)` - sends the error message to be shown in the extension's monitor.
Example of usage:
```js
const devTools = window.__REDUX_DEVTOOLS_EXTENSION__.connect(config);
devTools.subscribe((message) => {
if (message.type === 'DISPATCH' && message.state) {
console.log('DevTools requested to change the state to', message.state);
}
});
devTools.init({ value: 'initial state' });
devTools.send('change state', { value: 'state changed' })
```
See [redux enhancer's example](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/logOnly.js), [react example](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/examples/react-counter-messaging/components/Counter.js) and [blog post](https://medium.com/@zalmoxis/redux-devtools-without-redux-or-how-to-have-a-predictable-state-with-any-architecture-61c5f5a7716f) for more details.
### disconnect()
Remove extensions listener and disconnect extensions background script connection. Usually just unsubscribing the listener inside the `connect` is enough.
<a id="send"></a>
### send(action, state, [options, instanceId])
Send a new action and state manually to be shown on the monitor. It's recommended to use [`connect`](connect), unless you want to hook into an already created instance.
##### Arguments
- `action` *String* (action type) or *Object* with required `type` key.
- `state` *any* - usually object to expand.
- [`options`] *Object* - [see the available options](Arguments.md).
- [`instanceId`] *String* - instance id for which to include the log. If not specified and not present in the `options` object, will be the first available instance.
<a id="listen"></a>
### listen(onMessage, instanceId)
Listen for messages dispatched for specific `instanceId`. For most cases it's better to use `subcribe` inside the [`connect`](connect).
##### Arguments
- `onMessage` *Function* to call when there's an action from the monitor.
- `instanceId` *String* - instance id for which to handle actions.
<a id="open"></a>
### open([position])
Open the extension's window. This should be conditional (usually you don't need to open extension's window automatically).
##### Arguments
- [`position`] *String* - window position: `left`, `right`, `bottom`. Also can be `panel` to [open it in a Chrome panel](../FAQ.md#how-to-keep-devtools-window-focused-all-the-time-in-a-chrome-panel). Or `remote` to [open remote monitor](../FAQ.md#how-to-get-it-work-with-webworkers-react-native-hybrid-desktop-and-server-side-apps). By default is `left`.
<a id="notifyErrors"></a>
### notifyErrors([onError])
When called, the extension will listen for uncaught exceptions on the page, and, if any, will show native notifications. Optionally, you can provide a function to be called when an exception occurs.
##### Arguments
- [`onError`] *Function* to call when there's an exceptions.
================================================
FILE: docs/API/README.md
================================================
# API Reference
- [Parameters](Arguments.md)
- [Methods](Methods.md)
================================================
FILE: docs/Articles.md
================================================
# Articles
- [Improve your development workflow with Redux DevTools Extension](https://medium.com/@zalmoxis/improve-your-development-workflow-with-redux-devtools-extension-f0379227ff83)
- [Using Redux DevTools in production](https://medium.com/@zalmoxis/using-redux-devtools-in-production-4c5b56c5600f)
- [Redux DevTools without Redux](https://medium.com/@zalmoxis/redux-devtools-without-redux-or-how-to-have-a-predictable-state-with-any-architecture-61c5f5a7716f)
================================================
FILE: docs/Credits.md
================================================
# Credits
- Built using [Crossbuilder](https://github.com/zalmoxisus/crossbuilder) boilerplate.
- Includes [Dan Abramov](https://github.com/gaearon)'s [redux-devtools](https://github.com/gaearon/redux-devtools) and the following monitors:
- [Log Monitor](https://github.com/gaearon/redux-devtools-log-monitor)
- [Inspector](https://github.com/alexkuz/redux-devtools-inspector)
- [Dispatch](https://github.com/YoruNoHikage/redux-devtools-dispatch)
- [Slider](https://github.com/calesce/redux-slider-monitor)
- [Chart](https://github.com/romseguy/redux-devtools-chart-monitor)
- [The logo icon](https://github.com/reactjs/redux/issues/151) made by [Keith Yong](https://github.com/keithyong) .
- Examples from [Redux](https://github.com/rackt/redux/tree/master/examples).
================================================
FILE: docs/FAQ.md
================================================
# Redux DevTools Extension FAQ
## Table of Contents
- [How to get it work](#how-to-get-it-work)
- [How to disable/enable it in production](#how-to-disable-it-in-production)
- [How to persist debug sessions across page reloads](#how-to-persist-debug-sessions-across-page-reloads)
- [How to open DevTools programmatically](#how-to-open-devtools-programmatically)
- [How to enable/disable errors notifying](#how-to-enabledisable-errors-notifying)
- [How to get it work with WebWorkers, React Native, hybrid, desktop and server side apps](#how-to-get-it-work-with-webworkers-react-native-hybrid-desktop-and-server-side-apps)
- [Keyboard shortcuts](#keyboard-shortcuts)
#### How to get it work
- Check the extension with [Counter](http://zalmoxisus.github.io/examples/counter/) or [TodoMVC](http://zalmoxisus.github.io/examples/todomvc/) demo.
- Reload the extension on the extensions page (`chrome://extensions/`).
- If something goes wrong, [open an issue](https://github.com/zalmoxisus/redux-devtools-extension/issues) or tweet me: [@mdiordiev](https://twitter.com/mdiordiev).
#### How to disable it in production
Usually you don't have to. See [the article for details on how to include it in production](https://medium.com/@zalmoxis/using-redux-devtools-in-production-4c5b56c5600f).
#### How to persist debug sessions across page reloads
Just click the `Persist` button or add `?debug_session=<session_name>` to the url.
#### How to open DevTools programmatically
```js
window.__REDUX_DEVTOOLS_EXTENSION__.open();
```
Make sure to have it conditionally. Auto opening windows is a bad DX. See the [API](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Methods.md#open) for details.
#### How to enable/disable errors notifying
Just find `Redux DevTools` on the extensions page (`chrome://extensions/`) and click the `Options` link to customize everything. The errors notifying is disabled by default. If enabled, it works only when the store enhancer is called (in order not to show notifications for any sites you visit). In case you want notifications for a non-redux app, init it explicitly by calling `window.__REDUX_DEVTOOLS_EXTENSION__.notifyErrors()` (probably you'll check if `window.__REDUX_DEVTOOLS_EXTENSION__` exists before calling it).
#### How to get it work with WebWorkers, React Native, hybrid, desktop and server side apps
It is not possible to inject extension's script there and to communicate directly. To solve this, use [Remote Redux DevTools](https://github.com/zalmoxisus/remote-redux-devtools). After including it inside the app, click `Remote` button for remote monitoring.
#### Keyboard shortcuts
To set/change the keyboard shortcuts, click "Keyboard shortcuts" button on the bottom of the extensions page (`chrome://extensions/`). By default only `Cmd` (`Ctrl`) + `Shift` + `E` is available, which will open the extension popup (only when the Redux store is available in the current page).
================================================
FILE: docs/Features/Trace.md
================================================
## Trace actions calls

One of the features of Redux DevTools is to select an action in the history and see the callstack that triggered it. It aims to solve the problem of finding the source of events in the event list.
By default it's disabled as, depending of the use case, generating and serializing stack traces for every action can impact the performance. To enable it, set `trace` option to `true` as in [examples](https://github.com/zalmoxisus/redux-devtools-extension/commit/64717bb9b3534ff616d9db56c2be680627c7b09d). See [the API](../API/Arguments.md#trace) for more details.
For some edge cases where stack trace cannot be obtained with just `Error().stack`, you can pass a function as `trace` with your implementation. It's useful for cases where the stack is broken, like, for example, [when calling `setTimeout`](https://github.com/zalmoxisus/redux-devtools-instrument/blob/e7c05c98e7e9654cb7db92a2f56c6b5f3ff2452b/test/instrument.spec.js#L735-L737). It takes `action` object as argument and should return `stack` string. This way it can be also used to provide stack conditionally only for certain actions.
There's also an optional `traceLimit` parameter, which is `10` by default, to prevent consuming too much memory and serializing large stacks and also allows you to get larger stacks than limited by the browser (it will overpass default limit of `10` imposed by Chrome in `Error.stackTraceLimit`). If `trace` option is a function, `traceLimit` will have no effect, that should be handled there like so: `trace: () => new Error().stack.split('\n').slice(0, limit+1).join('\n')` (`+1` is needed for Chrome where's an extra 1st frame for `Error\n`).
Apart from opening resources in Chrome DevTools, as seen in the demo above, it can open the file (and jump to the line-column) right in your editor. Pretty useful for debugging, and also as an alternative when it's not possible to use openResource (for Firefox or when using the extension from window or for remote debugging). You can click Settings button and enable that, also adding the path to your project root directory to use. It works out of the box for VSCode, Atom, Webstorm/Phpstorm/IntelliJ, Sublime, Emacs, MacVim, Textmate on Mac and Windows. For Linux you can use [`atom-url-handler`](https://github.com/eclemens/atom-url-handler).
================================================
FILE: docs/Feedback.md
================================================
# Feedback wanted
[File an issue](https://github.com/zalmoxisus/redux-devtools-extension/issues) or [submit a PR](https://github.com/zalmoxisus/redux-devtools-extension/pulls) if you have suggestions, rate us and leave a review on [Chrome Store](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd/reviews), post feature requests and bug reports on [Product Pains](https://productpains.com/product/redux-devtools-extension), or ping me on Twitter as [@mdiordiev](https://twitter.com/mdiordiev).
<iframe width="100%" height="400px" scrolling="no" style="border:0" src="https://productpains.com/widget.html?token=fc7887ce-f3f9-105a-e5cb-43b9eeb668d5"></iframe><script type="text/javascript" src="https://productpains.com/js/lib/iframeResizer.min.js"></script>
================================================
FILE: docs/Integrations.md
================================================
# Integrations for js and non-js frameworks
Mostly functional:
- [React](#react)
- [Angular](#angular)
- [Cycle](#cycle)
- [Ember](#ember)
- [Fable](#fable)
- [Freezer](#freezer)
- [Mobx](#mobx)
- [PureScript](#purescript)
- [Reductive](#reductive)
- [Aurelia](#aurelia)
In progress:
- [ClojureScript](#clojurescript)
- [Horizon](#horizon)
- [Python](#python)
- [Swift](#swift)
### [React](https://github.com/facebook/react)
#### Inspect React props
##### [`react-inspect-props`](https://github.com/lucasconstantino/react-inspect-props)
```js
import { compose, withState } from 'recompose'
import { inspectProps } from 'react-inspect-props'
compose(
withState('count', 'setCount', 0),
inspectProps('Counter inspector')
)(Counter)
```
#### Inspect React states
##### [`remotedev-react-state`](https://github.com/jhen0409/remotedev-react-state)
```js
import connectToDevTools from 'remotedev-react-state'
componentWillMount() {
// Connect to devtools after setup initial state
connectToDevTools(this/*, options */)
}
```
#### Inspect React hooks (useState and useReducer)
##### [`reinspect`](https://github.com/troch/reinspect)
```js
import { useState } from 'reinspect'
export function CounterWithUseState({ id }) {
const [count, setCount] = useState(0, id)
// ...
}
```
### [Mobx](https://github.com/mobxjs/mobx)
#### [`mobx-remotedev`](https://github.com/zalmoxisus/mobx-remotedev)
```js
import remotedev from 'mobx-remotedev';
// or import remotedev from 'mobx-remotedev/lib/dev'
// in case you want to use it in production or don't have process.env.NODE_ENV === 'development'
const appStore = observable({
// ...
});
// Or
class appStore {
// ...
}
export default remotedev(appStore);
````
### [Angular](https://github.com/angular/angular)
#### [ng2-redux](https://github.com/angular-redux/ng2-redux)
```js
import { NgReduxModule, NgRedux, DevToolsExtension } from 'ng2-redux';
@NgModule({
/* ... */
imports: [ /* ... */, NgReduxModule ]
})export class AppModule {
constructor(
private ngRedux: NgRedux,
private devTools: DevToolsExtension) {
let enhancers = [];
// ... add whatever other enhancers you want.
// You probably only want to expose this tool in devMode.
if (__DEVMODE__ && devTools.isEnabled()) {
enhancers = [ ...enhancers, devTools.enhancer() ];
}
this.ngRedux.configureStore(
rootReducer,
initialState,
[],
enhancers);
}
}
```
For Angular 1 see [ng-redux](https://github.com/angular-redux/ng-redux).
#### [Angular @ngrx/store](https://ngrx.io/) + [`@ngrx/store-devtools`](https://ngrx.io/guide/store-devtools)
```js
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
@NgModule({
imports: [
StoreModule.forRoot(rootReducer),
// Instrumentation must be imported after importing StoreModule (config is optional)
StoreDevtoolsModule.instrument({
maxAge: 5
})
]
})
export class AppModule { }
```
[`Example of integration`](https://github.com/ngrx/platform/tree/master/projects/example-app/) ([live demo](https://ngrx.github.io/platform/example-app/)).
### [Ember](http://emberjs.com/)
#### [`ember-redux`](https://github.com/ember-redux/ember-redux)
```js
//app/enhancers/index.js
import { compose } from 'redux';
var devtools = window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : f => f;
export default compose(devtools);
```
### [Cycle](https://github.com/cyclejs/cyclejs)
#### [`@culli/store`](https://github.com/milankinen/culli/tree/master/packages/store)
```js
import {run} from "@cycle/most-run"
import {makeDOMDriver as DOM} from "@cycle/dom"
import Store, {ReduxDevtools} from "@culli/store"
import App, {newId} from "./App"
run(App, {
DOM: DOM("#app"),
Store: Store(ReduxDevtools({items: [{id: newId(), num: 0}, {id: newId(), num: 0}]}))
})
```
### [Freezer](https://github.com/arqex/freezer)
#### [`freezer-redux-devtools`](https://github.com/arqex/freezer-redux-devtools)
```js
import React, { Component } from 'react';
import { supportChromeExtension } from 'freezer-redux-devtools/freezer-redux-middleware';
import Freezer from 'freezer-js';
// Our state is a freezer object
var State = new Freezer({hello: 'world'});
// Enable the extension
supportChromeExtension( State );
```
### [Horizon](https://github.com/rethinkdb/horizon)
#### [`horizon-remotedev`](https://github.com/zalmoxisus/horizon-remotedev)
```js
// import hzRemotedev from 'horizon-remotedev';
// or import hzRemotedev from 'horizon-remotedev/lib/dev'
// in case you want to use it in production or don't have process.env.NODE_ENV === 'development'
//Setup Horizon connection
const horizon = Horizon();
// ...
// Specify the horizon instance to monitor
hzRemotedev(horizon("react_messages"))
```
### [Fable](https://github.com/fable-compiler/Fable)
#### [`fable-elmish/debugger`](https://github.com/fable-elmish/debugger)
```fsharp
open Elmish.Debug
Program.mkProgram init update view
|> Program.withDebugger // connect to a devtools monitor via Chrome extension if available
|> Program.run
```
or
```fsharp
open Elmish.Debug
Program.mkProgram init update view
|> Program.withDebuggerAt (Remote("localhost",8000)) // connect to a server running on localhost:8000
|> Program.run
```
### [PureScript](https://github.com/purescript/purescript)
#### [`purescript-react-redux`](https://github.com/ethul/purescript-react-redux)
[`Example of integration`](https://github.com/ethul/purescript-react-redux-example).
### [ClojureScript](https://github.com/clojure/clojurescript)
[`Example of integration`](http://gitlab.xet.ru:9999/publicpr/clojurescript-redux/tree/master#dev-setup)
### [Python](https://www.python.org/)
#### [`pyredux`](https://github.com/peterpeter5/pyredux)
[WIP](https://github.com/zalmoxisus/remotedev-server/issues/34)
### [Swift](https://github.com/apple/swift)
#### [`katanaMonitor`](https://github.com/bolismauro/katanaMonitor-lib-swift) for [`katana-swift`](https://github.com/BendingSpoons/katana-swift)
```swift
import KatanaMonitor
var middleware: [StoreMiddleware] = [
// other middleware
]
#if DEBUG
middleware.append(MonitorMiddleware.create(using: .defaultConfiguration))
#endif
```
### [Reductive](https://github.com/reasonml-community/reductive)
#### [`reductive-dev-tools`](https://github.com/ambientlight/reductive-dev-tools)
```reason
let storeEnhancer =
ReductiveDevTools.(
Connectors.reductiveEnhancer(
Extension.enhancerOptions(~name="MyApp", ()),
)
);
let storeCreator = storeEnhancer @@ Reductive.Store.create;
```
### [Aurelia](http://aurelia.io)
#### [`aurelia-store`](https://aurelia.io/docs/plugins/store)
```ts
import {Aurelia} from 'aurelia-framework';
import {initialState} from './state';
export function configure(aurelia: Aurelia) {
aurelia.use
.standardConfiguration()
.feature('resources');
...
aurelia.use.plugin('aurelia-store', {
initialState,
devToolsOptions: { // optional
... // see https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md
},
});
aurelia.start().then(() => aurelia.setRoot());
}
```
================================================
FILE: docs/README.md
================================================
# Documentation
* [Extension](/README.md)
* [Installation](/README.md#installation)
* [Usage](/README.md#usage)
* [Demo](/README.md#demo)
* [API Reference](/docs/API/README.md)
* [Options (arguments)](/docs/API/Arguments.md)
* [Methods (advanced API)](/docs/API/Methods.md)
* Features
* [Trace actions calls](/docs/Features/Trace.md)
* [Integrations](/docs/Integrations.md)
* [FAQ](/docs/FAQ.md)
* [Troubleshooting](/docs/Troubleshooting.md)
* [Recipes](/docs/Recipes.md)
* [Articles](/docs/Articles.md)
* [Videos](/docs/Videos.md)
* [Credits](/docs/Credits.md)
* [Support us](/README.md#backers)
* [Feedback](/docs/Feedback.md)
* [Change Log](https://github.com/zalmoxisus/redux-devtools-extension/releases)
================================================
FILE: docs/Recipes.md
================================================
# Recipes
### Using in a typescript project
The recommended way is to use [`redux-devtools-extension` npm package](/README.md#13-use-redux-devtools-extension-package-from-npm), which contains all typescript definitions. Or you can just use `window as any`:
```js
const store = createStore(
rootReducer,
initialState,
(window as any).__REDUX_DEVTOOLS_EXTENSION__ &&
(window as any).__REDUX_DEVTOOLS_EXTENSION__()
);
```
Note that you many need to set `no-any` to false in your `tslint.json` file.
Alternatively you can use typeguard in order to avoid
casting to any.
```typescript
import { createStore, StoreEnhancer } from "redux";
// ...
type WindowWithDevTools = Window & {
__REDUX_DEVTOOLS_EXTENSION__: () => StoreEnhancer<unknown, {}>
}
const isReduxDevtoolsExtenstionExist =
(arg: Window | WindowWithDevTools):
arg is WindowWithDevTools => {
return '__REDUX_DEVTOOLS_EXTENSION__' in arg;
}
// ...
const store = createStore(rootReducer, initialState,
isReduxDevtoolsExtenstionExist(window) ?
window.__REDUX_DEVTOOLS_EXTENSION__() : undefined)
```
### Export from browser console or from application
```js
store.liftedStore.getState()
```
The extension is not sharing `store` object, so you should take care of that.
### Applying multiple times with different sets of options
We're [not allowing that from instrumentation part](https://github.com/zalmoxisus/redux-devtools-instrument/blob/master/src/instrument.js#L676), because that would re-dispatch every app action in case we'd have many liftedStores, but there's [a helper for logging only](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/logOnly.js), which can be used it like so:
```js
import { createStore, compose } from 'redux';
import { devToolsEnhancer } from 'redux-devtools-extension/logOnly';
const store = createStore(reducer, /* preloadedState, */ compose(
devToolsEnhancer({
instaceID: 1,
name: 'Blacklisted',
actionsBlacklist: '...'
}),
devToolsEnhancer({
instaceID: 2,
name: 'Whitelisted',
actionsWhitelist: '...'
})
));
```
================================================
FILE: docs/Troubleshooting.md
================================================
# Troubleshooting
### I just see empty log or "No store found"
Make sure you [applied the enhancer](https://github.com/zalmoxisus/redux-devtools-extension#2-use-with-redux). Note that passing enhancer as last argument requires redux@>=3.1.0. For older versions apply it like [here](https://github.com/zalmoxisus/redux-devtools-extension/blob/v0.4.2/examples/todomvc/store/configureStore.js) or [here](https://github.com/zalmoxisus/redux-devtools-extension/blob/v0.4.2/examples/counter/store/configureStore.js#L7-L12).
Don't mix the old Redux API with the new one. Pass enhancers and applyMiddleware as last createStore argument.
### Access file url (`file:///`)
If you develop on your local filesystem, make sure to allow Redux DevTools access to `file:///` URLs in the settings of this extension:
<img width="746" alt="extensions" src="https://cloud.githubusercontent.com/assets/7957859/19075220/a0fad99e-8a4c-11e6-8b87-757f2dc179cb.png">
### It shows only the `@@INIT` action or moving back and forth doesn't update the state
Most likely you mutate the state. Check it by [adding `redux-immutable-state-invariant` middleware](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/examples/counter/store/configureStore.js#L3).
### @@INIT or REPLACE action resets the state of the app or last actions RE-APPLIED
`@@redux/REPLACE` (or `@@INIT`) is used internally when the application is hot reloaded. When you use `store.replaceReducer` the effect will be the same as for hot-reloading, where the extension is recomputing all the history again. To avoid that set [`shouldHotReload`](/docs/API/Arguments.md#shouldhotreload) parameter to `false`.
### It doesn't work with other store enhancers
Usually the extension's store enhancer should be last in the compose. When you're using [`window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__`](/README.md#12-advanced-store-setup) or [`composeWithDevTools`](/README.md#13-use-redux-devtools-extension-package-from-npm) helper you don't have to worry about the enhancers order. However some enhancers ([like `redux-batched-subscribe`](https://github.com/zalmoxisus/redux-devtools-extension/issues/261)) also have this requirement to be the last in the compose. In this case you can use it like so:
```js
const store = createStore(reducer, preloadedState, compose(
// applyMiddleware(thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : noop => noop,
batchedSubscribe(/* ... */)
));
```
Where `batchedSubscribe` is `redux-batched-subscribe` store enhancer.
### Excessive use of memory and CPU
That is happening due to serialization of some huge objects included in the state or action. The solution is to [sanitize them](/docs/API/Arguments.md#actionsanitizer--statesanitizer).
You can do that by including/omitting data containing specific values, having specific types... In the example below we're omitting parts of action and state objects with the key `data` (in case of action only when was dispatched action `FILE_DOWNLOAD_SUCCESS`):
```js
const actionSanitizer = (action) => (
action.type === 'FILE_DOWNLOAD_SUCCESS' && action.data ?
{ ...action, data: '<<LONG_BLOB>>' } : action
);
const store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
actionSanitizer,
stateSanitizer: (state) => state.data ? { ...state, data: '<<LONG_BLOB>>' } : state
}));
```
There's a more advanced [example on how to implement that for `ui-router`](https://github.com/zalmoxisus/redux-devtools-extension/issues/455#issuecomment-404538385).
The extension is in different process and cannot access the store object directly, unlike vanilla [`redux-devtools`](https://github.com/reduxjs/redux-devtools) which doesn't have this issue. In case sanitizing doesn't fit your use case, you might consider including it directly as a react component, so there will be no need to serialize the data, but it would add some complexity.
### It fails to serialize data when [passing synthetic events](https://github.com/zalmoxisus/redux-devtools-extension/issues/275) or [calling an action directly with `redux-actions`](https://github.com/zalmoxisus/redux-devtools-extension/issues/287)
React synthetic event cannot be reused for performance reason. So, it's not possible to serialize event objects you pass to action payloads.
1. The best solution is **not to pass the whole event object to reducers, but the data you need**:
```diff
function click(event) {
return {
type: ELEMENT_CLICKED,
- event: event
+ value: event.target.value
};
}
```
2. If you cannot pick data from the event object or, for some reason, you need the whole object, use `event.persist()` as suggested in [React Docs](https://facebook.github.io/react/docs/events.html#event-pooling), but it will consume RAM while not needed.
```diff
function increment(event) {
+ event.persist();
return {
type: ELEMENT_CLICKED,
event: event,
};
}
```
3. A workaround, to pass the whole object and at the same time not to persist it, is to override this key of the stringified payload in your action creator. Add a custom `toJSON` function right in the action object (which will be called by the extension before accessing the object):
```diff
function increment(event) {
return {
type: ELEMENT_CLICKED,
event: event,
+ toJSON: function (){
+ return { ...this, event: '[Event]' };
+ }
};
}
```
Note that it shouldn't be arrow function as we want to have access to the function's `this`.
As we don't have access to the original object, skipping and recomputing actions during hot reloading will not work in this case. We recommend to use the first solution whenever possible.
### Symbols or other unserializable data not shown
To get data which cannot be serialized by `JSON.stringify`, set [`serialize` parameter](/docs/API/Arguments.md#serialize):
```js
const store = Redux.createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
serialize: true
}));
```
It will handle also date, regex, undefined, error objects, symbols, maps, sets and functions.
================================================
FILE: docs/Videos.md
================================================
# Videos
- [Debugging flux applications in production at React Europe 2016](https://youtu.be/YU8jQ2HtqH4)
- [Hot Reloading with Time Travel at React Europe 2015](https://youtu.be/xsSnOQynTHs)
- [Getting Started with Redux DevTools Extension](https://egghead.io/lessons/javascript-getting-started-with-redux-dev-tools)
- [React & Redux With ExpressJS](https://www.youtube.com/watch?v=6ygcbRpZFR4)
================================================
FILE: examples/buildAll.js
================================================
/**
* Runs an ordered set of commands within each of the build directories.
*/
import fs from 'fs';
import path from 'path';
import { spawnSync } from 'child_process';
var exampleDirs = fs.readdirSync(__dirname).filter((file) => {
return fs.statSync(path.join(__dirname, file)).isDirectory();
});
// Ordering is important here. `npm install` must come first.
var cmdArgs = [
{ cmd: 'npm', args: ['install'] },
{ cmd: 'webpack', args: ['index.js'] }
];
for (const dir of exampleDirs) {
for (const cmdArg of cmdArgs) {
// declare opts in this scope to avoid https://github.com/joyent/node/issues/9158
const opts = {
cwd: path.join(__dirname, dir),
stdio: 'inherit'
};
let result = {};
if (process.platform === 'win32') {
result = spawnSync(cmdArg.cmd + '.cmd', cmdArg.args, opts);
} else {
result = spawnSync(cmdArg.cmd, cmdArg.args, opts);
}
if (result.status !== 0) {
throw new Error('Building examples exited with non-zero');
}
}
}
================================================
FILE: examples/counter/.babelrc
================================================
{
"presets": [ "es2015", "stage-0", "react" ]
}
================================================
FILE: examples/counter/actions/counter.js
================================================
export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';
let t;
export function increment() {
return {
type: INCREMENT_COUNTER
};
}
export function decrement() {
return {
type: DECREMENT_COUNTER
};
}
export function autoIncrement(delay = 10) {
return dispatch => {
if (t) {
clearInterval(t);
t = undefined;
return;
}
t = setInterval(() => {
dispatch(increment());
}, delay);
};
}
export function incrementAsync(delay = 1000) {
return dispatch => {
setTimeout(() => {
dispatch(increment());
}, delay);
};
}
================================================
FILE: examples/counter/components/Counter.js
================================================
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Counter extends Component {
render() {
const { increment, autoIncrement, incrementAsync, decrement, counter } = this.props;
return (
<p>
Clicked: {counter} times
{' '}
<button onClick={increment}>+</button>
{' '}
<button onClick={decrement}>-</button>
{' '}
<button onClick={incrementAsync}>Increment async</button>
{' '}
<button onClick={autoIncrement}>Auto increment</button>
</p>
);
}
}
Counter.propTypes = {
increment: PropTypes.func.isRequired,
autoIncrement: PropTypes.func.isRequired,
incrementAsync: PropTypes.func.isRequired,
decrement: PropTypes.func.isRequired,
counter: PropTypes.number.isRequired
};
export default Counter;
================================================
FILE: examples/counter/containers/App.js
================================================
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Counter from '../components/Counter';
import * as CounterActions from '../actions/counter';
function mapStateToProps(state) {
return {
counter: state.counter
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(CounterActions, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
================================================
FILE: examples/counter/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>Redux counter example</title>
</head>
<body>
<div id="root">
</div>
<script src="/static/bundle.js"></script>
</body>
</html>
================================================
FILE: examples/counter/index.js
================================================
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import App from './containers/App';
import configureStore from './store/configureStore';
const store = configureStore();
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
================================================
FILE: examples/counter/package.json
================================================
{
"name": "redux-counter-example",
"version": "0.0.0",
"description": "Redux counter example",
"scripts": {
"start": "node server.js",
"test": "NODE_ENV=test mocha --recursive --compilers js:babel-core/register --require ./test/setup.js",
"test:watch": "npm test -- --watch"
},
"repository": {
"type": "git",
"url": "https://github.com/rackt/redux.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/rackt/redux/issues"
},
"homepage": "http://rackt.github.io/redux",
"dependencies": {
"prop-types": "^15.6.2",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-redux": "^6.0.0",
"redux": "^4.0.1",
"redux-devtools-extension": "^2.13.7",
"redux-thunk": "^2.3.0"
},
"devDependencies": {
"babel-cli": "^6.3.17",
"babel-core": "^6.3.17",
"babel-loader": "^7.0.0",
"babel-preset-es2015": "^6.0.0",
"babel-preset-react": "6.3.13",
"babel-preset-stage-0": "^6.3.13",
"express": "^4.13.3",
"redux-immutable-state-invariant": "^2.1.0",
"webpack": "^4.0.0",
"webpack-dev-server": "^3.0.0",
"webpack-hot-middleware": "^2.2.0"
}
}
================================================
FILE: examples/counter/reducers/counter.js
================================================
import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../actions/counter';
export default function counter(state = 0, action) {
switch (action.type) {
case INCREMENT_COUNTER:
return state + 1;
case DECREMENT_COUNTER:
return state - 1;
default:
return state;
}
}
================================================
FILE: examples/counter/reducers/index.js
================================================
import { combineReducers } from 'redux';
import counter from './counter';
const rootReducer = combineReducers({
counter
});
export default rootReducer;
================================================
FILE: examples/counter/server.js
================================================
var webpack = require('webpack');
var webpackDevMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');
var config = require('./webpack.config');
var app = new require('express')();
var port = 4001;
var compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }));
app.use(webpackHotMiddleware(compiler));
app.get("/", function(req, res) {
res.sendFile(__dirname + '/index.html');
});
app.listen(port, function(error) {
if (error) {
console.error(error);
} else {
console.info("==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.", port, port);
}
});
================================================
FILE: examples/counter/store/configureStore.js
================================================
import { createStore, applyMiddleware, compose } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import invariant from 'redux-immutable-state-invariant';
import reducer from '../reducers';
import * as actionCreators from '../actions/counter';
export default function configureStore(preloadedState) {
const composeEnhancers = composeWithDevTools({ actionCreators, trace: true, traceLimit: 25 });
const store = createStore(reducer, preloadedState, composeEnhancers(
applyMiddleware(invariant(), thunk)
));
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
store.replaceReducer(require('../reducers').default)
});
}
return store;
}
================================================
FILE: examples/counter/test/actions/counter.spec.js
================================================
import expect from 'expect';
import { applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import * as actions from '../../actions/counter';
const middlewares = [thunk];
/*
* Creates a mock of Redux store with middleware.
*/
function mockStore(getState, expectedActions, onLastAction) {
if (!Array.isArray(expectedActions)) {
throw new Error('expectedActions should be an array of expected actions.');
}
if (typeof onLastAction !== 'undefined' && typeof onLastAction !== 'function') {
throw new Error('onLastAction should either be undefined or function.');
}
function mockStoreWithoutMiddleware() {
return {
getState() {
return typeof getState === 'function' ?
getState() :
getState;
},
dispatch(action) {
const expectedAction = expectedActions.shift();
expect(action).toEqual(expectedAction);
if (onLastAction && !expectedActions.length) {
onLastAction();
}
return action;
}
};
}
const mockStoreWithMiddleware = applyMiddleware(
...middlewares
)(mockStoreWithoutMiddleware);
return mockStoreWithMiddleware();
}
describe('actions', () => {
it('increment should create increment action', () => {
expect(actions.increment()).toEqual({ type: actions.INCREMENT_COUNTER });
});
it('decrement should create decrement action', () => {
expect(actions.decrement()).toEqual({ type: actions.DECREMENT_COUNTER });
});
it('incrementIfOdd should create increment action', (done) => {
const expectedActions = [
{ type: actions.INCREMENT_COUNTER }
];
const store = mockStore({ counter: 1 }, expectedActions, done);
store.dispatch(actions.incrementIfOdd());
});
it('incrementIfOdd shouldnt create increment action if counter is even', (done) => {
const expectedActions = [];
const store = mockStore({ counter: 2 }, expectedActions);
store.dispatch(actions.incrementIfOdd());
done();
});
it('incrementAsync should create increment action', (done) => {
const expectedActions = [
{ type: actions.INCREMENT_COUNTER }
];
const store = mockStore({ counter: 0 }, expectedActions, done);
store.dispatch(actions.incrementAsync(100));
});
});
================================================
FILE: examples/counter/test/components/Counter.spec.js
================================================
import expect from 'expect';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import Counter from '../../components/Counter';
function setup() {
const actions = {
increment: expect.createSpy(),
incrementIfOdd: expect.createSpy(),
incrementAsync: expect.createSpy(),
decrement: expect.createSpy()
};
const component = TestUtils.renderIntoDocument(<Counter counter={1} {...actions} />);
return {
component: component,
actions: actions,
buttons: TestUtils.scryRenderedDOMComponentsWithTag(component, 'button'),
p: TestUtils.findRenderedDOMComponentWithTag(component, 'p')
};
}
describe('Counter component', () => {
it('should display count', () => {
const { p } = setup();
expect(p.textContent).toMatch(/^Clicked: 1 times/);
});
it('first button should call increment', () => {
const { buttons, actions } = setup();
TestUtils.Simulate.click(buttons[0]);
expect(actions.increment).toHaveBeenCalled();
});
it('second button should call decrement', () => {
const { buttons, actions } = setup();
TestUtils.Simulate.click(buttons[1]);
expect(actions.decrement).toHaveBeenCalled();
});
it('third button should call incrementIfOdd', () => {
const { buttons, actions } = setup();
TestUtils.Simulate.click(buttons[2]);
expect(actions.incrementIfOdd).toHaveBeenCalled();
});
it('fourth button should call incrementAsync', () => {
const { buttons, actions } = setup();
TestUtils.Simulate.click(buttons[3]);
expect(actions.incrementAsync).toHaveBeenCalled();
});
});
================================================
FILE: examples/counter/test/containers/App.spec.js
================================================
import expect from 'expect';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import { Provider } from 'react-redux';
import App from '../../containers/App';
import configureStore from '../../store/configureStore';
function setup(initialState) {
const store = configureStore(initialState);
const app = TestUtils.renderIntoDocument(
<Provider store={store}>
<App />
</Provider>
);
return {
app: app,
buttons: TestUtils.scryRenderedDOMComponentsWithTag(app, 'button'),
p: TestUtils.findRenderedDOMComponentWithTag(app, 'p')
};
}
describe('containers', () => {
describe('App', () => {
it('should display initial count', () => {
const { p } = setup();
expect(p.textContent).toMatch(/^Clicked: 0 times/);
});
it('should display updated count after increment button click', () => {
const { buttons, p } = setup();
TestUtils.Simulate.click(buttons[0]);
expect(p.textContent).toMatch(/^Clicked: 1 times/);
});
it('should display updated count after decrement button click', () => {
const { buttons, p } = setup();
TestUtils.Simulate.click(buttons[1]);
expect(p.textContent).toMatch(/^Clicked: -1 times/);
});
it('shouldnt change if even and if odd button clicked', () => {
const { buttons, p } = setup();
TestUtils.Simulate.click(buttons[2]);
expect(p.textContent).toMatch(/^Clicked: 0 times/);
});
it('should change if odd and if odd button clicked', () => {
const { buttons, p } = setup({ counter: 1 });
TestUtils.Simulate.click(buttons[2]);
expect(p.textContent).toMatch(/^Clicked: 2 times/);
});
});
});
================================================
FILE: examples/counter/test/reducers/counter.spec.js
================================================
import expect from 'expect';
import counter from '../../reducers/counter';
import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../../actions/counter';
describe('reducers', () => {
describe('counter', () => {
it('should handle initial state', () => {
expect(counter(undefined, {})).toBe(0);
});
it('should handle INCREMENT_COUNTER', () => {
expect(counter(1, { type: INCREMENT_COUNTER })).toBe(2);
});
it('should handle DECREMENT_COUNTER', () => {
expect(counter(1, { type: DECREMENT_COUNTER })).toBe(0);
});
it('should handle unknown action type', () => {
expect(counter(1, { type: 'unknown' })).toBe(1);
});
});
});
================================================
FILE: examples/counter/test/setup.js
================================================
import { jsdom } from 'jsdom';
global.document = jsdom('<!doctype html><html><body></body></html>');
global.window = document.defaultView;
global.navigator = global.window.navigator;
================================================
FILE: examples/counter/webpack.config.js
================================================
var path = require('path');
var webpack = require('webpack');
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: [
'webpack-hot-middleware/client',
'./index'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
new webpack.HotModuleReplacementPlugin()
],
module: {
rules: [{
test: /\.js$/,
loaders: ['babel-loader'],
exclude: /node_modules/
}]
}
};
================================================
FILE: examples/react-counter-messaging/.babelrc
================================================
{
"presets": [ "es2015", "stage-0", "react" ]
}
================================================
FILE: examples/react-counter-messaging/components/Counter.js
================================================
import React, { Component } from 'react';
const withDevTools = (
// process.env.NODE_ENV === 'development' &&
typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__
);
class Counter extends Component {
constructor() {
super();
this.state = { counter: 0 };
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
}
componentWillMount() {
if (withDevTools) {
this.devTools = window.__REDUX_DEVTOOLS_EXTENSION__.connect();
this.unsubscribe = this.devTools.subscribe((message) => {
// Implement monitors actions.
// For example time traveling:
if (message.type === 'DISPATCH' && message.payload.type === 'JUMP_TO_STATE') {
this.setState(message.state);
}
});
}
}
componentWillUnmount() {
if (withDevTools) {
this.unsubscribe(); // Use if you have other subscribers from other components.
window.__REDUX_DEVTOOLS_EXTENSION__.disconnect(); // If there aren't other subscribers.
}
}
increment() {
const state = { counter: this.state.counter + 1 };
if (withDevTools) this.devTools.send('increment', state);
this.setState(state);
}
decrement() {
const state = { counter: this.state.counter - 1 };
if (withDevTools) this.devTools.send('decrement', state);
this.setState(state);
}
render() {
const { counter } = this.state;
return (
<p>
Clicked: {counter} times
{' '}
<button onClick={this.increment}>+</button>
{' '}
<button onClick={this.decrement}>-</button>
</p>
);
}
}
export default Counter;
================================================
FILE: examples/react-counter-messaging/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>React counter example</title>
</head>
<body>
<div id="root">
</div>
<script src="/static/bundle.js"></script>
</body>
</html>
================================================
FILE: examples/react-counter-messaging/index.js
================================================
import React from 'react';
import { render } from 'react-dom';
import Counter from './components/Counter';
render(
<Counter />,
document.getElementById('root')
);
================================================
FILE: examples/react-counter-messaging/package.json
================================================
{
"name": "react-counter-example",
"version": "0.0.0",
"description": "React counter example",
"scripts": {
"start": "webpack-dev-server --progress"
},
"repository": {
"type": "git",
"url": "https://github.com/zalmoxisus/redux-devtools-extension.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/zalmoxisus/redux-devtools-extension/issues"
},
"homepage": "https://github.com/zalmoxisus/redux-devtools-extension",
"dependencies": {
"react": "^16.0.0",
"react-dom": "^16.0.0"
},
"devDependencies": {
"babel-cli": "^6.3.17",
"babel-core": "^6.3.17",
"babel-loader": "^7.0.0",
"babel-preset-es2015": "^6.0.0",
"babel-preset-react": "6.3.13",
"babel-preset-stage-0": "^6.3.13",
"webpack": "^4.0.0",
"webpack-cli": "^3.2.0",
"webpack-dev-server": "^3.0.0",
"webpack-hot-middleware": "^2.2.0"
}
}
================================================
FILE: examples/react-counter-messaging/webpack.config.js
================================================
var path = require('path');
var webpack = require('webpack');
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: [
'./index'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
module: {
rules: [{
test: /\.js$/,
loaders: ['babel-loader'],
exclude: /node_modules/
}]
},
devServer: {
port: 4004
}
};
================================================
FILE: examples/router/.babelrc
================================================
{
"presets": [ "es2015", "stage-0", "react" ],
"plugins": [ "add-module-exports", "transform-decorators-legacy" ]
}
================================================
FILE: examples/router/actions/todos.js
================================================
import * as types from '../constants/ActionTypes';
export function addTodo(text) {
return { type: types.ADD_TODO, text };
}
export function deleteTodo(id) {
return { type: types.DELETE_TODO, id };
}
export function editTodo(id, text) {
return { type: types.EDIT_TODO, id, text };
}
export function completeTodo(id) {
return { type: types.COMPLETE_TODO, id };
}
export function completeAll() {
return { type: types.COMPLETE_ALL };
}
export function clearCompleted() {
return { type: types.CLEAR_COMPLETED };
}
================================================
FILE: examples/router/components/Footer.js
================================================
import React, { PropTypes, Component } from 'react';
import classnames from 'classnames';
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters';
const FILTER_TITLES = {
[SHOW_ALL]: 'All',
[SHOW_ACTIVE]: 'Active',
[SHOW_COMPLETED]: 'Completed'
};
class Footer extends Component {
renderTodoCount() {
const { activeCount } = this.props;
const itemWord = activeCount === 1 ? 'item' : 'items';
return (
<span className="todo-count">
<strong>{activeCount || 'No'}</strong> {itemWord} left
</span>
);
}
renderFilterLink(filter) {
const title = FILTER_TITLES[filter];
const { filter: selectedFilter, onShow } = this.props;
return (
<a className={classnames({ selected: filter === selectedFilter })}
style={{ cursor: 'pointer' }}
onClick={() => onShow(filter)}>
{title}
</a>
);
}
renderClearButton() {
const { completedCount, onClearCompleted } = this.props;
if (completedCount > 0) {
return (
<button className="clear-completed"
onClick={onClearCompleted} >
Clear completed
</button>
);
}
}
render() {
return (
<footer className="footer">
{this.renderTodoCount()}
<ul className="filters">
{[SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED].map(filter =>
<li key={filter}>
{this.renderFilterLink(filter)}
</li>
)}
</ul>
{this.renderClearButton()}
</footer>
);
}
}
Footer.propTypes = {
completedCount: PropTypes.number.isRequired,
activeCount: PropTypes.number.isRequired,
filter: PropTypes.string.isRequired,
onClearCompleted: PropTypes.func.isRequired,
onShow: PropTypes.func.isRequired
};
export default Footer;
================================================
FILE: examples/router/components/Header.js
================================================
import React, { PropTypes, Component } from 'react';
import TodoTextInput from './TodoTextInput';
class Header extends Component {
handleSave(text) {
if (text.length !== 0) {
this.props.addTodo(text);
}
}
render() {
const { path } = this.props;
return (
<header className="header">
<h1 style={{ fontSize: 80 }}>{path}</h1>
<TodoTextInput newTodo
onSave={this.handleSave.bind(this)}
placeholder="What needs to be done?" />
</header>
);
}
}
Header.propTypes = {
addTodo: PropTypes.func.isRequired
};
export default Header;
================================================
FILE: examples/router/components/MainSection.js
================================================
import React, { Component, PropTypes } from 'react';
import TodoItem from './TodoItem';
import Footer from './Footer';
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters';
const TODO_FILTERS = {
[SHOW_ALL]: () => true,
[SHOW_ACTIVE]: todo => !todo.completed,
[SHOW_COMPLETED]: todo => todo.completed
};
class MainSection extends Component {
constructor(props, context) {
super(props, context);
this.state = { filter: SHOW_ALL };
}
handleClearCompleted() {
const atLeastOneCompleted = this.props.todos.some(todo => todo.completed);
if (atLeastOneCompleted) {
this.props.actions.clearCompleted();
}
}
handleShow(filter) {
this.setState({ filter });
}
renderToggleAll(completedCount) {
const { todos, actions } = this.props;
if (todos.length > 0) {
return (
<input className="toggle-all"
type="checkbox"
checked={completedCount === todos.length}
onChange={actions.completeAll} />
);
}
}
renderFooter(completedCount) {
const { todos } = this.props;
const { filter } = this.state;
const activeCount = todos.length - completedCount;
if (todos.length) {
return (
<Footer completedCount={completedCount}
activeCount={activeCount}
filter={filter}
onClearCompleted={this.handleClearCompleted.bind(this)}
onShow={this.handleShow.bind(this)} />
);
}
}
render() {
const { todos, actions } = this.props;
const { filter } = this.state;
const filteredTodos = todos.filter(TODO_FILTERS[filter]);
const completedCount = todos.reduce((count, todo) =>
todo.completed ? count + 1 : count,
0
);
return (
<section className="main">
{this.renderToggleAll(completedCount)}
<ul className="todo-list">
{filteredTodos.map(todo =>
<TodoItem key={todo.id} todo={todo} {...actions} />
)}
</ul>
{this.renderFooter(completedCount)}
</section>
);
}
}
MainSection.propTypes = {
todos: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
};
export default MainSection;
================================================
FILE: examples/router/components/TodoItem.js
================================================
import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import TodoTextInput from './TodoTextInput';
class TodoItem extends Component {
constructor(props, context) {
super(props, context);
this.state = {
editing: false
};
}
handleDoubleClick() {
this.setState({ editing: true });
}
handleSave(id, text) {
if (text.length === 0) {
this.props.deleteTodo(id);
} else {
this.props.editTodo(id, text);
}
this.setState({ editing: false });
}
render() {
const {todo, completeTodo, deleteTodo} = this.props;
let element;
if (this.state.editing) {
element = (
<TodoTextInput text={todo.text}
editing={this.state.editing}
onSave={(text) => this.handleSave(todo.id, text)} />
);
} else {
element = (
<div className="view">
<input className="toggle"
type="checkbox"
checked={todo.completed}
onChange={() => completeTodo(todo.id)} />
<label onDoubleClick={this.handleDoubleClick.bind(this)}>
{todo.text}
</label>
<button className="destroy"
onClick={() => deleteTodo(todo.id)} />
</div>
);
}
return (
<li className={classnames({
completed: todo.completed,
editing: this.state.editing
})}>
{element}
</li>
);
}
}
TodoItem.propTypes = {
todo: PropTypes.object.isRequired,
editTodo: PropTypes.func.isRequired,
deleteTodo: PropTypes.func.isRequired,
completeTodo: PropTypes.func.isRequired
};
export default TodoItem;
================================================
FILE: examples/router/components/TodoTextInput.js
================================================
import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
class TodoTextInput extends Component {
constructor(props, context) {
super(props, context);
this.state = {
text: this.props.text || ''
};
}
handleSubmit(e) {
const text = e.target.value.trim();
if (e.which === 13) {
this.props.onSave(text);
if (this.props.newTodo) {
this.setState({ text: '' });
}
}
}
handleChange(e) {
this.setState({ text: e.target.value });
}
handleBlur(e) {
if (!this.props.newTodo) {
this.props.onSave(e.target.value);
}
}
render() {
return (
<input className={
classnames({
edit: this.props.editing,
'new-todo': this.props.newTodo
})}
type="text"
placeholder={this.props.placeholder}
autoFocus="true"
value={this.state.text}
onBlur={this.handleBlur.bind(this)}
onChange={this.handleChange.bind(this)}
onKeyDown={this.handleSubmit.bind(this)} />
);
}
}
TodoTextInput.propTypes = {
onSave: PropTypes.func.isRequired,
text: PropTypes.string,
placeholder: PropTypes.string,
editing: PropTypes.bool,
newTodo: PropTypes.bool
};
export default TodoTextInput;
================================================
FILE: examples/router/constants/ActionTypes.js
================================================
export const ADD_TODO = 'ADD_TODO';
export const DELETE_TODO = 'DELETE_TODO';
export const EDIT_TODO = 'EDIT_TODO';
export const COMPLETE_TODO = 'COMPLETE_TODO';
export const COMPLETE_ALL = 'COMPLETE_ALL';
export const CLEAR_COMPLETED = 'CLEAR_COMPLETED';
================================================
FILE: examples/router/constants/TodoFilters.js
================================================
export const SHOW_ALL = 'show_all';
export const SHOW_COMPLETED = 'show_completed';
export const SHOW_ACTIVE = 'show_active';
================================================
FILE: examples/router/containers/App.js
================================================
import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Header from '../components/Header';
import MainSection from '../components/MainSection';
import * as TodoActions from '../actions/todos';
class App extends Component {
render() {
const { todos, path, actions } = this.props;
return (
<div>
<Header addTodo={actions.addTodo} path={path} />
<MainSection todos={todos} actions={actions} />
</div>
);
}
}
App.propTypes = {
todos: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
};
function mapStateToProps(state) {
return {
todos: state.todos,
path: state.router.location.pathname
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(TodoActions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
================================================
FILE: examples/router/containers/Root.js
================================================
import React, { Component, PropTypes } from 'react';
import { Provider } from 'react-redux';
import { Route, Redirect } from 'react-router';
import { ReduxRouter } from 'redux-router';
import Wrapper from './Wrapper';
import App from './App';
class Root extends Component {
render() {
return (
<ReduxRouter>
<Redirect from="/" to="Standard Todo"/>
<Route path="/" component={Wrapper}>
<Route path="/:id" component={App}/>
</Route>
</ReduxRouter>
);
}
}
export default Root;
================================================
FILE: examples/router/containers/Wrapper.js
================================================
import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { pushState } from 'redux-router';
import { Route, Link } from 'react-router';
import * as TodoActions from '../actions/todos';
function mapDispatchToProps(dispatch) {
return {
pushState: bindActionCreators(pushState, dispatch),
actions: bindActionCreators(TodoActions, dispatch)
};
}
@connect((state) => ({}), mapDispatchToProps)
class Wrapper extends Component {
static propTypes = {
children: PropTypes.node
};
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
event.preventDefault();
const { actions, pushState } = this.props;
const path = event.target.innerText;
pushState(null, path);
console.log('Navigate to', path);
if (this.timeout) clearInterval(this.timeout);
if (path === 'AutoTodo') {
console.log('!');
this.timeout = setInterval(() => {
actions.addTodo('Auto generated task');
}, 100);
}
}
render() {
return (
<div>
<div style={{ padding: 20, backgroundColor: '#eee', fontWeight: 'bold', textAlign: 'center' }}>
<a href="#" onClick={this.handleClick}>Standard Todo</a> | <a href="#" onClick={this.handleClick}>AutoTodo</a>
</div>
{this.props.children}
</div>
);
}
}
export default Wrapper;
================================================
FILE: examples/router/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>Redux TodoMVC example</title>
</head>
<body>
<div class="todoapp" id="root">
</div>
<script src="/static/bundle.js"></script>
</body>
</html>
================================================
FILE: examples/router/index.js
================================================
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import Root from './containers/Root';
import configureStore from './store/configureStore';
import 'todomvc-app-css/index.css';
const store = configureStore();
render(
<Provider store={store}>
<Root />
</Provider>,
document.getElementById('root')
);
================================================
FILE: examples/router/package.json
================================================
{
"name": "redux-todomvc-example",
"version": "0.0.0",
"description": "Redux TodoMVC example",
"scripts": {
"start": "node server.js",
"test": "NODE_ENV=test mocha --recursive --compilers js:babel-core/register --require ./test/setup.js",
"test:watch": "npm test -- --watch"
},
"repository": {
"type": "git",
"url": "https://github.com/rackt/redux.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/rackt/redux/issues"
},
"homepage": "http://rackt.github.io/redux",
"dependencies": {
"classnames": "^2.1.2",
"history": "^1.13.1",
"react": "^0.14.0",
"react-dom": "^0.14.0",
"react-redux": "^4.0.0",
"react-router": "^1.0.2",
"redux": "^3.0.0",
"redux-router": "^1.0.0-beta5"
},
"devDependencies": {
"babel-core": "^6.3.15",
"babel-loader": "^6.2.0",
"babel-plugin-add-module-exports": "^0.1.1",
"babel-plugin-react-transform": "^2.0.0-beta1",
"babel-plugin-transform-decorators-legacy": "^1.2.0",
"babel-polyfill": "^6.3.14",
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-preset-stage-0": "^6.3.13",
"expect": "^1.8.0",
"express": "^4.13.3",
"jsdom": "^5.6.1",
"mocha": "^2.2.5",
"node-libs-browser": "^0.5.2",
"raw-loader": "^0.5.1",
"react-addons-test-utils": "^0.14.0",
"react-transform-hmr": "^1.0.0",
"style-loader": "^0.12.3",
"todomvc-app-css": "^2.0.1",
"webpack": "^1.9.11",
"webpack-dev-middleware": "^1.2.0",
"webpack-hot-middleware": "^2.2.0"
}
}
================================================
FILE: examples/router/reducers/index.js
================================================
import { combineReducers } from 'redux';
import { routerStateReducer } from 'redux-router';
import todos from './todos';
const rootReducer = combineReducers({
todos,
router: routerStateReducer
});
export default rootReducer;
================================================
FILE: examples/router/reducers/todos.js
================================================
import { ADD_TODO, DELETE_TODO, EDIT_TODO, COMPLETE_TODO, COMPLETE_ALL, CLEAR_COMPLETED } from '../constants/ActionTypes';
const initialState = [{
text: 'Use Redux',
completed: false,
id: 0
}];
export default function todos(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return [{
id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,
completed: false,
text: action.text
}, ...state];
case DELETE_TODO:
return state.filter(todo =>
todo.id !== action.id
);
case EDIT_TODO:
return state.map(todo =>
todo.id === action.id ?
Object.assign({}, todo, { text: action.text }) :
todo
);
case COMPLETE_TODO:
return state.map(todo =>
todo.id === action.id ?
Object.assign({}, todo, { completed: !todo.completed }) :
todo
);
case COMPLETE_ALL:
const areAllMarked = state.every(todo => todo.completed);
return state.map(todo => Object.assign({}, todo, {
completed: !areAllMarked
}));
case CLEAR_COMPLETED:
return state.filter(todo => todo.completed === false);
default:
return state;
}
}
================================================
FILE: examples/router/server.js
================================================
var webpack = require('webpack');
var webpackDevMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');
var config = require('./webpack.config');
var app = new require('express')();
var port = 4002;
var compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }));
app.use(webpackHotMiddleware(compiler));
app.get("/", function(req, res) {
res.sendFile(__dirname + '/index.html');
});
app.listen(port, function(error) {
if (error) {
console.error(error);
} else {
console.info("==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.", port, port);
}
});
================================================
FILE: examples/router/store/configureStore.js
================================================
import { createStore, compose } from 'redux';
import { reduxReactRouter, routerStateReducer, ReduxRouter } from 'redux-router';
//import createHistory from 'history/lib/createBrowserHistory';
import createHistory from 'history/lib/createHashHistory';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
let finalCreateStore = compose(
reduxReactRouter({ createHistory }),
global.devToolsExtension ? global.devToolsExtension() : f => f
)(createStore);
const store = finalCreateStore(rootReducer, initialState);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextReducer = require('../reducers');
store.replaceReducer(nextReducer);
});
}
return store;
}
================================================
FILE: examples/router/test/actions/todos.spec.js
================================================
import expect from 'expect';
import * as types from '../../constants/ActionTypes';
import * as actions from '../../actions/todos';
describe('todo actions', () => {
it('addTodo should create ADD_TODO action', () => {
expect(actions.addTodo('Use Redux')).toEqual({
type: types.ADD_TODO,
text: 'Use Redux'
});
});
it('deleteTodo should create DELETE_TODO action', () => {
expect(actions.deleteTodo(1)).toEqual({
type: types.DELETE_TODO,
id: 1
});
});
it('editTodo should create EDIT_TODO action', () => {
expect(actions.editTodo(1, 'Use Redux everywhere')).toEqual({
type: types.EDIT_TODO,
id: 1,
text: 'Use Redux everywhere'
});
});
it('completeTodo should create COMPLETE_TODO action', () => {
expect(actions.completeTodo(1)).toEqual({
type: types.COMPLETE_TODO,
id: 1
});
});
it('completeAll should create COMPLETE_ALL action', () => {
expect(actions.completeAll()).toEqual({
type: types.COMPLETE_ALL
});
});
it('clearCompleted should create CLEAR_COMPLETED action', () => {
expect(actions.clearCompleted('Use Redux')).toEqual({
type: types.CLEAR_COMPLETED
});
});
});
================================================
FILE: examples/router/test/components/Footer.spec.js
================================================
import expect from 'expect';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import Footer from '../../components/Footer';
import { SHOW_ALL, SHOW_ACTIVE } from '../../constants/TodoFilters';
function setup(propOverrides) {
const props = Object.assign({
completedCount: 0,
activeCount: 0,
filter: SHOW_ALL,
onClearCompleted: expect.createSpy(),
onShow: expect.createSpy()
}, propOverrides);
const renderer = TestUtils.createRenderer();
renderer.render(<Footer {...props} />);
const output = renderer.getRenderOutput();
return {
props: props,
output: output
};
}
function getTextContent(elem) {
const children = Array.isArray(elem.props.children) ?
elem.props.children : [elem.props.children];
return children.reduce(function concatText(out, child) {
// Children are either elements or text strings
return out + (child.props ? getTextContent(child) : child);
}, '');
}
describe('components', () => {
describe('Footer', () => {
it('should render container', () => {
const { output } = setup();
expect(output.type).toBe('footer');
expect(output.props.className).toBe('footer');
});
it('should display active count when 0', () => {
const { output } = setup({ activeCount: 0 });
const [count] = output.props.children;
expect(getTextContent(count)).toBe('No items left');
});
it('should display active count when above 0', () => {
const { output } = setup({ activeCount: 1 });
const [count] = output.props.children;
expect(getTextContent(count)).toBe('1 item left');
});
it('should render filters', () => {
const { output } = setup();
const [, filters] = output.props.children;
expect(filters.type).toBe('ul');
expect(filters.props.className).toBe('filters');
expect(filters.props.children.length).toBe(3);
filters.props.children.forEach(function checkFilter(filter, i) {
expect(filter.type).toBe('li');
const a = filter.props.children;
expect(a.props.className).toBe(i === 0 ? 'selected' : '');
expect(a.props.children).toBe({
0: 'All',
1: 'Active',
2: 'Completed'
}[i]);
});
});
it('should call onShow when a filter is clicked', () => {
const { output, props } = setup();
const [, filters] = output.props.children;
const filterLink = filters.props.children[1].props.children;
filterLink.props.onClick({});
expect(props.onShow).toHaveBeenCalledWith(SHOW_ACTIVE);
});
it('shouldnt show clear button when no completed todos', () => {
const { output } = setup({ completedCount: 0 });
const [,, clear] = output.props.children;
expect(clear).toBe(undefined);
});
it('should render clear button when completed todos', () => {
const { output } = setup({ completedCount: 1 });
const [,, clear] = output.props.children;
expect(clear.type).toBe('button');
expect(clear.props.children).toBe('Clear completed');
});
it('should call onClearCompleted on clear button click', () => {
const { output, props } = setup({ completedCount: 1 });
const [,, clear] = output.props.children;
clear.props.onClick({});
expect(props.onClearCompleted).toHaveBeenCalled();
});
});
});
================================================
FILE: examples/router/test/components/Header.spec.js
================================================
import expect from 'expect';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import Header from '../../components/Header';
import TodoTextInput from '../../components/TodoTextInput';
function setup() {
const props = {
addTodo: expect.createSpy()
};
const renderer = TestUtils.createRenderer();
renderer.render(<Header {...props} />);
const output = renderer.getRenderOutput();
return {
props: props,
output: output,
renderer: renderer
};
}
describe('components', () => {
describe('Header', () => {
it('should render correctly', () => {
const { output } = setup();
expect(output.type).toBe('header');
expect(output.props.className).toBe('header');
const [h1, input] = output.props.children;
expect(h1.type).toBe('h1');
expect(h1.props.children).toBe('todos');
expect(input.type).toBe(TodoTextInput);
expect(input.props.newTodo).toBe(true);
expect(input.props.placeholder).toBe('What needs to be done?');
});
it('should call call addTodo if length of text is greater than 0', () => {
const { output, props } = setup();
const input = output.props.children[1];
input.props.onSave('');
expect(props.addTodo.calls.length).toBe(0);
input.props.onSave('Use Redux');
expect(props.addTodo.calls.length).toBe(1);
});
});
});
================================================
FILE: examples/router/test/components/MainSection.spec.js
================================================
import expect from 'expect';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import MainSection from '../../components/MainSection';
import TodoItem from '../../components/TodoItem';
import Footer from '../../components/Footer';
import { SHOW_ALL, SHOW_COMPLETED } from '../../constants/TodoFilters';
function setup(propOverrides) {
const props = Object.assign({
todos: [{
text: 'Use Redux',
completed: false,
id: 0
}, {
text: 'Run the tests',
completed: true,
id: 1
}],
actions: {
editTodo: expect.createSpy(),
deleteTodo: expect.createSpy(),
completeTodo: expect.createSpy(),
completeAll: expect.createSpy(),
clearCompleted: expect.createSpy()
}
}, propOverrides);
const renderer = TestUtils.createRenderer();
renderer.render(<MainSection {...props} />);
const output = renderer.getRenderOutput();
return {
props: props,
output: output,
renderer: renderer
};
}
describe('components', () => {
describe('MainSection', () => {
it('should render container', () => {
const { output } = setup();
expect(output.type).toBe('section');
expect(output.props.className).toBe('main');
});
describe('toggle all input', () => {
it('should render', () => {
const { output } = setup();
const [toggle] = output.props.children;
expect(toggle.type).toBe('input');
expect(toggle.props.type).toBe('checkbox');
expect(toggle.props.checked).toBe(false);
});
it('should be checked if all todos completed', () => {
const { output } = setup({ todos: [{
text: 'Use Redux',
completed: true,
id: 0
}]});
const [toggle] = output.props.children;
expect(toggle.props.checked).toBe(true);
});
it('should call completeAll on change', () => {
const { output, props } = setup();
const [toggle] = output.props.children;
toggle.props.onChange({});
expect(props.actions.completeAll).toHaveBeenCalled();
});
});
describe('footer', () => {
it('should render', () => {
const { output } = setup();
const [,, footer] = output.props.children;
expect(footer.type).toBe(Footer);
expect(footer.props.completedCount).toBe(1);
expect(footer.props.activeCount).toBe(1);
expect(footer.props.filter).toBe(SHOW_ALL);
});
it('onShow should set the filter', () => {
const { output, renderer } = setup();
const [,, footer] = output.props.children;
footer.props.onShow(SHOW_COMPLETED);
const updated = renderer.getRenderOutput();
const [,, updatedFooter] = updated.props.children;
expect(updatedFooter.props.filter).toBe(SHOW_COMPLETED);
});
it('onClearCompleted should call clearCompleted', () => {
const { output, props } = setup();
const [,, footer] = output.props.children;
footer.props.onClearCompleted();
expect(props.actions.clearCompleted).toHaveBeenCalled();
});
it('onClearCompleted shouldnt call clearCompleted if no todos completed', () => {
const { output, props } = setup({ todos: [{
text: 'Use Redux',
completed: false,
id: 0
}]});
const [,, footer] = output.props.children;
footer.props.onClearCompleted();
expect(props.actions.clearCompleted.calls.length).toBe(0);
});
});
describe('todo list', () => {
it('should render', () => {
const { output, props } = setup();
const [, list] = output.props.children;
expect(list.type).toBe('ul');
expect(list.props.children.length).toBe(2);
list.props.children.forEach((item, i) => {
expect(item.type).toBe(TodoItem);
expect(item.props.todo).toBe(props.todos[i]);
});
});
it('should filter items', () => {
const { output, renderer, props } = setup();
const [,, footer] = output.props.children;
footer.props.onShow(SHOW_COMPLETED);
const updated = renderer.getRenderOutput();
const [, updatedList] = updated.props.children;
expect(updatedList.props.children.length).toBe(1);
expect(updatedList.props.children[0].props.todo).toBe(props.todos[1]);
});
});
});
});
================================================
FILE: examples/router/test/components/TodoItem.spec.js
================================================
import expect from 'expect';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import TodoItem from '../../components/TodoItem';
import TodoTextInput from '../../components/TodoTextInput';
function setup( editing = false ) {
const props = {
todo: {
id: 0,
text: 'Use Redux',
completed: false
},
editTodo: expect.createSpy(),
deleteTodo: expect.createSpy(),
completeTodo: expect.createSpy()
};
const renderer = TestUtils.createRenderer();
renderer.render(
<TodoItem {...props} />
);
let output = renderer.getRenderOutput();
if (editing) {
const label = output.props.children.props.children[1];
label.props.onDoubleClick({});
output = renderer.getRenderOutput();
}
return {
props: props,
output: output,
renderer: renderer
};
}
describe('components', () => {
describe('TodoItem', () => {
it('initial render', () => {
const { output } = setup();
expect(output.type).toBe('li');
expect(output.props.className).toBe('');
const div = output.props.children;
expect(div.type).toBe('div');
expect(div.props.className).toBe('view');
const [input, label, button] = div.props.children;
expect(input.type).toBe('input');
expect(input.props.checked).toBe(false);
expect(label.type).toBe('label');
expect(label.props.children).toBe('Use Redux');
expect(button.type).toBe('button');
expect(button.props.className).toBe('destroy');
});
it('input onChange should call completeTodo', () => {
const { output, props } = setup();
const input = output.props.children.props.children[0];
input.props.onChange({});
expect(props.completeTodo).toHaveBeenCalledWith(0);
});
it('button onClick should call deleteTodo', () => {
const { output, props } = setup();
const button = output.props.children.props.children[2];
button.props.onClick({});
expect(props.deleteTodo).toHaveBeenCalledWith(0);
});
it('label onDoubleClick should put component in edit state', () => {
const { output, renderer } = setup();
const label = output.props.children.props.children[1];
label.props.onDoubleClick({});
const updated = renderer.getRenderOutput();
expect(updated.type).toBe('li');
expect(updated.props.className).toBe('editing');
});
it('edit state render', () => {
const { output } = setup(true);
expect(output.type).toBe('li');
expect(output.props.className).toBe('editing');
const input = output.props.children;
expect(input.type).toBe(TodoTextInput);
expect(input.props.text).toBe('Use Redux');
expect(input.props.editing).toBe(true);
});
it('TodoTextInput onSave should call editTodo', () => {
const { output, props } = setup(true);
output.props.children.props.onSave('Use Redux');
expect(props.editTodo).toHaveBeenCalledWith(0, 'Use Redux');
});
it('TodoTextInput onSave should call deleteTodo if text is empty', () => {
const { output, props } = setup(true);
output.props.children.props.onSave('');
expect(props.deleteTodo).toHaveBeenCalledWith(0);
});
it('TodoTextInput onSave should exit component from edit state', () => {
const { output, renderer } = setup(true);
output.props.children.props.onSave('Use Redux');
const updated = renderer.getRenderOutput();
expect(updated.type).toBe('li');
expect(updated.props.className).toBe('');
});
});
});
================================================
FILE: examples/router/test/components/TodoTextInput.spec.js
================================================
import expect from 'expect';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import TodoTextInput from '../../components/TodoTextInput';
function setup(propOverrides) {
const props = Object.assign({
onSave: expect.createSpy(),
text: 'Use Redux',
placeholder: 'What needs to be done?',
editing: false,
newTodo: false
}, propOverrides);
const renderer = TestUtils.createRenderer();
renderer.render(
<TodoTextInput {...props} />
);
let output = renderer.getRenderOutput();
output = renderer.getRenderOutput();
return {
props: props,
output: output,
renderer: renderer
};
}
describe('components', () => {
describe('TodoTextInput', () => {
it('should render correctly', () => {
const { output } = setup();
expect(output.props.placeholder).toEqual('What needs to be done?');
expect(output.props.value).toEqual('Use Redux');
expect(output.props.className).toEqual('');
});
it('should render correctly when editing=true', () => {
const { output } = setup({ editing: true });
expect(output.props.className).toEqual('edit');
});
it('should render correctly when newTodo=true', () => {
const { output } = setup({ newTodo: true });
expect(output.props.className).toEqual('new-todo');
});
it('should update value on change', () => {
const { output, renderer } = setup();
output.props.onChange({ target: { value: 'Use Radox' }});
const updated = renderer.getRenderOutput();
expect(updated.props.value).toEqual('Use Radox');
});
it('should call onSave on return key press', () => {
const { output, props } = setup();
output.props.onKeyDown({ which: 13, target: { value: 'Use Redux' }});
expect(props.onSave).toHaveBeenCalledWith('Use Redux');
});
it('should reset state on return key press if newTodo', () => {
const { output, renderer } = setup({ newTodo: true });
output.props.onKeyDown({ which: 13, target: { value: 'Use Redux' }});
const updated = renderer.getRenderOutput();
expect(updated.props.value).toEqual('');
});
it('should call onSave on blur', () => {
const { output, props } = setup();
output.props.onBlur({ target: { value: 'Use Redux' }});
expect(props.onSave).toHaveBeenCalledWith('Use Redux');
});
it('shouldnt call onSave on blur if newTodo', () => {
const { output, props } = setup({ newTodo: true });
output.props.onBlur({ target: { value: 'Use Redux' }});
expect(props.onSave.calls.length).toBe(0);
});
});
});
================================================
FILE: examples/router/test/reducers/todos.spec.js
================================================
import expect from 'expect';
import todos from '../../reducers/todos';
import * as types from '../../constants/ActionTypes';
describe('todos reducer', () => {
it('should handle initial state', () => {
expect(
todos(undefined, {})
).toEqual([{
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should handle ADD_TODO', () => {
expect(
todos([], {
type: types.ADD_TODO,
text: 'Run the tests'
})
).toEqual([{
text: 'Run the tests',
completed: false,
id: 0
}]);
expect(
todos([{
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.ADD_TODO,
text: 'Run the tests'
})
).toEqual([{
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}]);
expect(
todos([{
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.ADD_TODO,
text: 'Fix the tests'
})
).toEqual([{
text: 'Fix the tests',
completed: false,
id: 2
}, {
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should handle DELETE_TODO', () => {
expect(
todos([{
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.DELETE_TODO,
id: 1
})
).toEqual([{
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should handle EDIT_TODO', () => {
expect(
todos([{
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.EDIT_TODO,
text: 'Fix the tests',
id: 1
})
).toEqual([{
text: 'Fix the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should handle COMPLETE_TODO', () => {
expect(
todos([{
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.COMPLETE_TODO,
id: 1
})
).toEqual([{
text: 'Run the tests',
completed: true,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should handle COMPLETE_ALL', () => {
expect(
todos([{
text: 'Run the tests',
completed: true,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.COMPLETE_ALL
})
).toEqual([{
text: 'Run the tests',
completed: true,
id: 1
}, {
text: 'Use Redux',
completed: true,
id: 0
}]);
// Unmark if all todos are currently completed
expect(
todos([{
text: 'Run the tests',
completed: true,
id: 1
}, {
text: 'Use Redux',
completed: true,
id: 0
}], {
type: types.COMPLETE_ALL
})
).toEqual([{
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should handle CLEAR_COMPLETED', () => {
expect(
todos([{
text: 'Run the tests',
completed: true,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.CLEAR_COMPLETED
})
).toEqual([{
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should not generate duplicate ids after CLEAR_COMPLETED', () => {
expect(
[{
type: types.COMPLETE_TODO,
id: 0
}, {
type: types.CLEAR_COMPLETED
}, {
type: types.ADD_TODO,
text: 'Write more tests'
}].reduce(todos, [{
id: 0,
completed: false,
text: 'Use Redux'
}, {
id: 1,
completed: false,
text: 'Write tests'
}])
).toEqual([{
text: 'Write more tests',
completed: false,
id: 2
}, {
text: 'Write tests',
completed: false,
id: 1
}]);
});
});
================================================
FILE: examples/router/test/setup.js
================================================
import { jsdom } from 'jsdom';
global.document = jsdom('<!doctype html><html><body></body></html>');
global.window = document.defaultView;
global.navigator = global.window.navigator;
================================================
FILE: examples/router/webpack.config.js
================================================
var path = require('path');
var webpack = require('webpack');
module.exports = {
devtool: 'cheap-module-eval-source-map',
entry: [
'webpack-hot-middleware/client',
'./index'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
],
module: {
loaders: [{
test: /\.js$/,
loaders: ['babel'],
exclude: /node_modules/,
include: __dirname
}, {
test: /\.css?$/,
loaders: ['style', 'raw'],
include: __dirname
}]
}
};
================================================
FILE: examples/saga-counter/.babelrc
================================================
{
"presets": [ "es2015", "stage-0", "react" ]
}
================================================
FILE: examples/saga-counter/index.html
================================================
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Redux Saga Counter example</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="/static/bundle.js"></script>
</body>
</html>
================================================
FILE: examples/saga-counter/package.json
================================================
{
"name": "redux-counter-example",
"version": "0.0.0",
"description": "Redux counter example",
"scripts": {
"start": "webpack-dev-server --progress"
},
"repository": {
"type": "git",
"url": "https://github.com/rackt/redux.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/rackt/redux/issues"
},
"homepage": "http://rackt.github.io/redux",
"dependencies": {
"prop-types": "^15.6.2",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-redux": "^6.0.0",
"redux": "^4.0.0",
"redux-saga": "^0.10.5"
},
"devDependencies": {
"babel-cli": "^6.3.17",
"babel-core": "^6.3.17",
"babel-loader": "^7.0.0",
"babel-preset-es2015": "^6.0.0",
"babel-preset-react": "6.3.13",
"babel-preset-stage-0": "^6.3.13",
"webpack": "^4.0.0",
"webpack-cli": "^3.2.0",
"webpack-dev-server": "^3.1.0",
"webpack-hot-middleware": "^2.24.0"
}
}
================================================
FILE: examples/saga-counter/src/components/Counter.js
================================================
import React from 'react'
import PropTypes from 'prop-types'
const Counter = ({ value, onIncrement, onIncrementAsync, onDecrement, onIncrementIfOdd }) =>
<p>
Clicked: {value} times
{' '}
<button onClick={onIncrement}>
+
</button>
{' '}
<button onClick={onDecrement}>
-
</button>
{' '}
<button onClick={onIncrementIfOdd}>
Increment if odd
</button>
{' '}
<button onClick={onIncrementAsync}>
Increment async
</button>
</p>
Counter.propTypes = {
value: PropTypes.number.isRequired,
onIncrement: PropTypes.func.isRequired,
onDecrement: PropTypes.func.isRequired,
onIncrementAsync: PropTypes.func.isRequired,
onIncrementIfOdd: PropTypes.func.isRequired
}
export default Counter
================================================
FILE: examples/saga-counter/src/main.js
================================================
import "babel-polyfill"
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware, compose } from 'redux'
import createSagaMiddleware from 'redux-saga'
// import sagaMonitor from './sagaMonitor'
import Counter from './components/Counter'
import reducer from './reducers'
import rootSaga from './sagas'
const sagaMiddleware = createSagaMiddleware(/* {sagaMonitor} */)
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true, traceLimit: 25 }) || compose;
const store = createStore(
reducer,
composeEnhancers(applyMiddleware(sagaMiddleware))
)
sagaMiddleware.run(rootSaga)
const action = type => store.dispatch({type})
function render() {
ReactDOM.render(
<Counter
value={store.getState()}
onIncrement={() => action('INCREMENT')}
onDecrement={() => action('DECREMENT')}
onIncrementIfOdd={() => action('INCREMENT_IF_ODD')}
onIncrementAsync={() => action('INCREMENT_ASYNC')} />,
document.getElementById('root')
)
}
render()
store.subscribe(render)
================================================
FILE: examples/saga-counter/src/reducers/index.js
================================================
export default function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'INCREMENT_IF_ODD':
return (state % 2 !== 0) ? state + 1 : state
case 'DECREMENT':
return state - 1
default:
return state
}
}
================================================
FILE: examples/saga-counter/src/sagas/index.js
================================================
/* eslint-disable no-constant-condition */
import { takeEvery } from 'redux-saga'
import { put, call } from 'redux-saga/effects'
import { delay } from 'redux-saga'
export function* incrementAsync() {
yield call(delay, 1000)
yield put({type: 'INCREMENT'})
}
export default function* rootSaga() {
yield* takeEvery('INCREMENT_ASYNC', incrementAsync)
}
================================================
FILE: examples/saga-counter/webpack.config.js
================================================
var path = require('path');
var webpack = require('webpack');
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: [
path.join(__dirname, 'src', 'main')
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
module: {
rules: [{
test: /\.js$/,
loaders: ['babel-loader'],
exclude: /node_modules/
}]
},
devServer: {
port: 4003
}
};
================================================
FILE: examples/todomvc/.babelrc
================================================
{
"presets": [ "es2015", "stage-0", "react" ]
}
================================================
FILE: examples/todomvc/actions/index.js
================================================
export * from './todos';
================================================
FILE: examples/todomvc/actions/todos.js
================================================
import * as types from '../constants/ActionTypes';
export function addTodo(text) {
return { type: types.ADD_TODO, text };
}
export function deleteTodo(id) {
return { type: types.DELETE_TODO, id };
}
export function editTodo(id, text) {
return { type: types.EDIT_TODO, id, text };
}
export function completeTodo(id) {
return { type: types.COMPLETE_TODO, id };
}
export function completeAll() {
return { type: types.COMPLETE_ALL };
}
export function clearCompleted() {
return { type: types.CLEAR_COMPLETED };
}
================================================
FILE: examples/todomvc/components/Footer.js
================================================
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters';
const FILTER_TITLES = {
[SHOW_ALL]: 'All',
[SHOW_ACTIVE]: 'Active',
[SHOW_COMPLETED]: 'Completed'
};
class Footer extends Component {
renderTodoCount() {
const { activeCount } = this.props;
const itemWord = activeCount === 1 ? 'item' : 'items';
return (
<span className="todo-count">
<strong>{activeCount || 'No'}</strong> {itemWord} left
</span>
);
}
renderFilterLink(filter) {
const title = FILTER_TITLES[filter];
const { filter: selectedFilter, onShow } = this.props;
return (
<a className={classnames({ selected: filter === selectedFilter })}
style={{ cursor: 'pointer' }}
onClick={() => onShow(filter)}>
{title}
</a>
);
}
renderClearButton() {
const { completedCount, onClearCompleted } = this.props;
if (completedCount > 0) {
return (
<button className="clear-completed"
onClick={onClearCompleted} >
Clear completed
</button>
);
}
}
render() {
return (
<footer className="footer">
{this.renderTodoCount()}
<ul className="filters">
{[SHOW_ALL, SHOW_ACTIVE, SHOW_COMPLETED].map(filter =>
<li key={filter}>
{this.renderFilterLink(filter)}
</li>
)}
</ul>
{this.renderClearButton()}
</footer>
);
}
}
Footer.propTypes = {
completedCount: PropTypes.number.isRequired,
activeCount: PropTypes.number.isRequired,
filter: PropTypes.string.isRequired,
onClearCompleted: PropTypes.func.isRequired,
onShow: PropTypes.func.isRequired
};
export default Footer;
================================================
FILE: examples/todomvc/components/Header.js
================================================
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import TodoTextInput from './TodoTextInput';
class Header extends Component {
handleSave(text) {
if (text.length !== 0) {
this.props.addTodo(text);
}
}
render() {
return (
<header className="header">
<h1>todos</h1>
<TodoTextInput newTodo
onSave={this.handleSave.bind(this)}
placeholder="What needs to be done?" />
</header>
);
}
}
Header.propTypes = {
addTodo: PropTypes.func.isRequired
};
export default Header;
================================================
FILE: examples/todomvc/components/MainSection.js
================================================
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import TodoItem from './TodoItem';
import Footer from './Footer';
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/TodoFilters';
const TODO_FILTERS = {
[SHOW_ALL]: () => true,
[SHOW_ACTIVE]: todo => !todo.completed,
[SHOW_COMPLETED]: todo => todo.completed
};
class MainSection extends Component {
constructor(props, context) {
super(props, context);
this.state = { filter: SHOW_ALL };
}
handleClearCompleted() {
const atLeastOneCompleted = this.props.todos.some(todo => todo.completed);
if (atLeastOneCompleted) {
this.props.actions.clearCompleted();
}
}
handleShow(filter) {
this.setState({ filter });
}
renderToggleAll(completedCount) {
const { todos, actions } = this.props;
if (todos.length > 0) {
return (
<input className="toggle-all"
type="checkbox"
checked={completedCount === todos.length}
onChange={actions.completeAll} />
);
}
}
renderFooter(completedCount) {
const { todos } = this.props;
const { filter } = this.state;
const activeCount = todos.length - completedCount;
if (todos.length) {
return (
<Footer completedCount={completedCount}
activeCount={activeCount}
filter={filter}
onClearCompleted={this.handleClearCompleted.bind(this)}
onShow={this.handleShow.bind(this)} />
);
}
}
render() {
const { todos, actions } = this.props;
const { filter } = this.state;
const filteredTodos = todos.filter(TODO_FILTERS[filter]);
const completedCount = todos.reduce((count, todo) =>
todo.completed ? count + 1 : count,
0
);
return (
<section className="main">
{this.renderToggleAll(completedCount)}
<ul className="todo-list">
{filteredTodos.map(todo =>
<TodoItem key={todo.id} todo={todo} {...actions} />
)}
</ul>
{this.renderFooter(completedCount)}
</section>
);
}
}
MainSection.propTypes = {
todos: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
};
export default MainSection;
================================================
FILE: examples/todomvc/components/TodoItem.js
================================================
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import TodoTextInput from './TodoTextInput';
class TodoItem extends Component {
constructor(props, context) {
super(props, context);
this.state = {
editing: false
};
}
handleDoubleClick() {
this.setState({ editing: true });
}
handleSave(id, text) {
if (text.length === 0) {
this.props.deleteTodo(id);
} else {
this.props.editTodo(id, text);
}
this.setState({ editing: false });
}
render() {
const {todo, completeTodo, deleteTodo} = this.props;
let element;
if (this.state.editing) {
element = (
<TodoTextInput text={todo.text}
editing={this.state.editing}
onSave={(text) => this.handleSave(todo.id, text)} />
);
} else {
element = (
<div className="view">
<input className="toggle"
type="checkbox"
checked={todo.completed}
onChange={() => completeTodo(todo.id)} />
<label onDoubleClick={this.handleDoubleClick.bind(this)}>
{todo.text}
</label>
<button className="destroy"
onClick={() => deleteTodo(todo.id)} />
</div>
);
}
return (
<li className={classnames({
completed: todo.completed,
editing: this.state.editing
})}>
{element}
</li>
);
}
}
TodoItem.propTypes = {
todo: PropTypes.object.isRequired,
editTodo: PropTypes.func.isRequired,
deleteTodo: PropTypes.func.isRequired,
completeTodo: PropTypes.func.isRequired
};
export default TodoItem;
================================================
FILE: examples/todomvc/components/TodoTextInput.js
================================================
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
class TodoTextInput extends Component {
constructor(props, context) {
super(props, context);
this.state = {
text: this.props.text || ''
};
}
handleSubmit(e) {
const text = e.target.value.trim();
if (e.which === 13) {
this.props.onSave(text);
if (this.props.newTodo) {
this.setState({ text: '' });
}
}
}
handleChange(e) {
this.setState({ text: e.target.value });
}
handleBlur(e) {
if (!this.props.newTodo) {
this.props.onSave(e.target.value);
}
}
render() {
return (
<input className={
classnames({
edit: this.props.editing,
'new-todo': this.props.newTodo
})}
type="text"
placeholder={this.props.placeholder}
autoFocus={true}
value={this.state.text}
onBlur={this.handleBlur.bind(this)}
onChange={this.handleChange.bind(this)}
onKeyDown={this.handleSubmit.bind(this)} />
);
}
}
TodoTextInput.propTypes = {
onSave: PropTypes.func.isRequired,
text: PropTypes.string,
placeholder: PropTypes.string,
editing: PropTypes.bool,
newTodo: PropTypes.bool
};
export default TodoTextInput;
================================================
FILE: examples/todomvc/constants/ActionTypes.js
================================================
export const ADD_TODO = 'ADD_TODO';
export const DELETE_TODO = 'DELETE_TODO';
export const EDIT_TODO = 'EDIT_TODO';
export const COMPLETE_TODO = 'COMPLETE_TODO';
export const COMPLETE_ALL = 'COMPLETE_ALL';
export const CLEAR_COMPLETED = 'CLEAR_COMPLETED';
================================================
FILE: examples/todomvc/constants/TodoFilters.js
================================================
export const SHOW_ALL = 'show_all';
export const SHOW_COMPLETED = 'show_completed';
export const SHOW_ACTIVE = 'show_active';
================================================
FILE: examples/todomvc/containers/App.js
================================================
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Header from '../components/Header';
import MainSection from '../components/MainSection';
import * as TodoActions from '../actions/todos';
class App extends Component {
render() {
const { todos, actions } = this.props;
return (
<div>
<Header addTodo={actions.addTodo} />
<MainSection todos={todos} actions={actions} />
</div>
);
}
}
App.propTypes = {
todos: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
};
function mapStateToProps(state) {
return {
todos: state.todos
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(TodoActions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
================================================
FILE: examples/todomvc/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>Redux TodoMVC example</title>
</head>
<body>
<div class="todoapp" id="root">
</div>
<script src="/static/bundle.js"></script>
</body>
</html>
================================================
FILE: examples/todomvc/index.js
================================================
import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import App from './containers/App';
import configureStore from './store/configureStore';
import 'todomvc-app-css/index.css';
const store = configureStore();
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
================================================
FILE: examples/todomvc/package.json
================================================
{
"name": "redux-todomvc-example",
"version": "0.0.0",
"description": "Redux TodoMVC example",
"scripts": {
"start": "node server.js",
"test": "NODE_ENV=test mocha --recursive --compilers js:babel-core/register --require ./test/setup.js",
"test:watch": "npm test -- --watch"
},
"repository": {
"type": "git",
"url": "https://github.com/rackt/redux.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/rackt/redux/issues"
},
"homepage": "http://rackt.github.io/redux",
"dependencies": {
"classnames": "^2.1.2",
"prop-types": "^15.6.2",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-redux": "^6.0.0",
"redux": "^4.0.0"
},
"devDependencies": {
"babel-cli": "^6.3.17",
"babel-core": "^6.3.17",
"babel-loader": "^7.0.0",
"babel-preset-es2015": "^6.0.0",
"babel-preset-react": "6.3.13",
"babel-preset-stage-0": "^6.3.13",
"express": "^4.13.3",
"raw-loader": "^1.0.0",
"style-loader": "^0.23.0",
"todomvc-app-css": "^2.0.1",
"webpack": "^4.0.0",
"webpack-dev-server": "^3.0.0",
"webpack-hot-middleware": "^2.2.0"
}
}
================================================
FILE: examples/todomvc/reducers/index.js
================================================
import { combineReducers } from 'redux';
import todos from './todos';
const rootReducer = combineReducers({
todos
});
export default rootReducer;
================================================
FILE: examples/todomvc/reducers/todos.js
================================================
import { ADD_TODO, DELETE_TODO, EDIT_TODO, COMPLETE_TODO, COMPLETE_ALL, CLEAR_COMPLETED } from '../constants/ActionTypes';
const initialState = [{
text: 'Use Redux',
completed: false,
modified: new Date(),
id: 0
}];
export default function todos(state = initialState, action) {
switch (action.type) {
case ADD_TODO:
return [{
id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1,
completed: false,
modified: new Date(),
text: action.text
}, ...state];
case DELETE_TODO:
return state.filter(todo =>
todo.id !== action.id
);
case EDIT_TODO:
return state.map(todo =>
todo.id === action.id ?
Object.assign({}, todo, { text: action.text, modified: new Date() }) :
todo
);
case COMPLETE_TODO:
return state.map(todo =>
todo.id === action.id ?
Object.assign({}, todo, { completed: !todo.completed, modified: new Date() }) :
todo
);
case COMPLETE_ALL:
const areAllMarked = state.every(todo => todo.completed);
return state.map(todo => Object.assign({}, todo, {
completed: !areAllMarked, modified: new Date()
}));
case CLEAR_COMPLETED:
return state.filter(todo => todo.completed === false);
default:
return state;
}
}
================================================
FILE: examples/todomvc/server.js
================================================
var webpack = require('webpack');
var webpackDevMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');
var config = require('./webpack.config');
var app = new require('express')();
var port = 4002;
var compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }));
app.use(webpackHotMiddleware(compiler));
app.get("/", function(req, res) {
res.sendFile(__dirname + '/index.html');
});
app.listen(port, function(error) {
if (error) {
console.error(error);
} else {
console.info("==> 🌎 Listening on port %s. Open up http://localhost:%s/ in your browser.", port, port);
}
});
================================================
FILE: examples/todomvc/store/configureStore.js
================================================
import { createStore } from 'redux';
import rootReducer from '../reducers';
import * as actionCreators from '../actions';
export default function configureStore(preloadedState) {
const enhancer = window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__({ actionCreators, serialize: true, trace: true });
if (!enhancer) {
console.warn('Install Redux DevTools Extension to inspect the app state: ' +
'https://github.com/zalmoxisus/redux-devtools-extension#installation')
}
const store = createStore(rootReducer, preloadedState, enhancer);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
store.replaceReducer(require('../reducers').default)
});
}
return store;
}
================================================
FILE: examples/todomvc/test/actions/todos.spec.js
================================================
import expect from 'expect';
import * as types from '../../constants/ActionTypes';
import * as actions from '../../actions/todos';
describe('todo actions', () => {
it('addTodo should create ADD_TODO action', () => {
expect(actions.addTodo('Use Redux')).toEqual({
type: types.ADD_TODO,
text: 'Use Redux'
});
});
it('deleteTodo should create DELETE_TODO action', () => {
expect(actions.deleteTodo(1)).toEqual({
type: types.DELETE_TODO,
id: 1
});
});
it('editTodo should create EDIT_TODO action', () => {
expect(actions.editTodo(1, 'Use Redux everywhere')).toEqual({
type: types.EDIT_TODO,
id: 1,
text: 'Use Redux everywhere'
});
});
it('completeTodo should create COMPLETE_TODO action', () => {
expect(actions.completeTodo(1)).toEqual({
type: types.COMPLETE_TODO,
id: 1
});
});
it('completeAll should create COMPLETE_ALL action', () => {
expect(actions.completeAll()).toEqual({
type: types.COMPLETE_ALL
});
});
it('clearCompleted should create CLEAR_COMPLETED action', () => {
expect(actions.clearCompleted('Use Redux')).toEqual({
type: types.CLEAR_COMPLETED
});
});
});
================================================
FILE: examples/todomvc/test/components/Footer.spec.js
================================================
import expect from 'expect';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import Footer from '../../components/Footer';
import { SHOW_ALL, SHOW_ACTIVE } from '../../constants/TodoFilters';
function setup(propOverrides) {
const props = Object.assign({
completedCount: 0,
activeCount: 0,
filter: SHOW_ALL,
onClearCompleted: expect.createSpy(),
onShow: expect.createSpy()
}, propOverrides);
const renderer = TestUtils.createRenderer();
renderer.render(<Footer {...props} />);
const output = renderer.getRenderOutput();
return {
props: props,
output: output
};
}
function getTextContent(elem) {
const children = Array.isArray(elem.props.children) ?
elem.props.children : [elem.props.children];
return children.reduce(function concatText(out, child) {
// Children are either elements or text strings
return out + (child.props ? getTextContent(child) : child);
}, '');
}
describe('components', () => {
describe('Footer', () => {
it('should render container', () => {
const { output } = setup();
expect(output.type).toBe('footer');
expect(output.props.className).toBe('footer');
});
it('should display active count when 0', () => {
const { output } = setup({ activeCount: 0 });
const [count] = output.props.children;
expect(getTextContent(count)).toBe('No items left');
});
it('should display active count when above 0', () => {
const { output } = setup({ activeCount: 1 });
const [count] = output.props.children;
expect(getTextContent(count)).toBe('1 item left');
});
it('should render filters', () => {
const { output } = setup();
const [, filters] = output.props.children;
expect(filters.type).toBe('ul');
expect(filters.props.className).toBe('filters');
expect(filters.props.children.length).toBe(3);
filters.props.children.forEach(function checkFilter(filter, i) {
expect(filter.type).toBe('li');
const a = filter.props.children;
expect(a.props.className).toBe(i === 0 ? 'selected' : '');
expect(a.props.children).toBe({
0: 'All',
1: 'Active',
2: 'Completed'
}[i]);
});
});
it('should call onShow when a filter is clicked', () => {
const { output, props } = setup();
const [, filters] = output.props.children;
const filterLink = filters.props.children[1].props.children;
filterLink.props.onClick({});
expect(props.onShow).toHaveBeenCalledWith(SHOW_ACTIVE);
});
it('shouldnt show clear button when no completed todos', () => {
const { output } = setup({ completedCount: 0 });
const [,, clear] = output.props.children;
expect(clear).toBe(undefined);
});
it('should render clear button when completed todos', () => {
const { output } = setup({ completedCount: 1 });
const [,, clear] = output.props.children;
expect(clear.type).toBe('button');
expect(clear.props.children).toBe('Clear completed');
});
it('should call onClearCompleted on clear button click', () => {
const { output, props } = setup({ completedCount: 1 });
const [,, clear] = output.props.children;
clear.props.onClick({});
expect(props.onClearCompleted).toHaveBeenCalled();
});
});
});
================================================
FILE: examples/todomvc/test/components/Header.spec.js
================================================
import expect from 'expect';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import Header from '../../components/Header';
import TodoTextInput from '../../components/TodoTextInput';
function setup() {
const props = {
addTodo: expect.createSpy()
};
const renderer = TestUtils.createRenderer();
renderer.render(<Header {...props} />);
const output = renderer.getRenderOutput();
return {
props: props,
output: output,
renderer: renderer
};
}
describe('components', () => {
describe('Header', () => {
it('should render correctly', () => {
const { output } = setup();
expect(output.type).toBe('header');
expect(output.props.className).toBe('header');
const [h1, input] = output.props.children;
expect(h1.type).toBe('h1');
expect(h1.props.children).toBe('todos');
expect(input.type).toBe(TodoTextInput);
expect(input.props.newTodo).toBe(true);
expect(input.props.placeholder).toBe('What needs to be done?');
});
it('should call call addTodo if length of text is greater than 0', () => {
const { output, props } = setup();
const input = output.props.children[1];
input.props.onSave('');
expect(props.addTodo.calls.length).toBe(0);
input.props.onSave('Use Redux');
expect(props.addTodo.calls.length).toBe(1);
});
});
});
================================================
FILE: examples/todomvc/test/components/MainSection.spec.js
================================================
import expect from 'expect';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import MainSection from '../../components/MainSection';
import TodoItem from '../../components/TodoItem';
import Footer from '../../components/Footer';
import { SHOW_ALL, SHOW_COMPLETED } from '../../constants/TodoFilters';
function setup(propOverrides) {
const props = Object.assign({
todos: [{
text: 'Use Redux',
completed: false,
id: 0
}, {
text: 'Run the tests',
completed: true,
id: 1
}],
actions: {
editTodo: expect.createSpy(),
deleteTodo: expect.createSpy(),
completeTodo: expect.createSpy(),
completeAll: expect.createSpy(),
clearCompleted: expect.createSpy()
}
}, propOverrides);
const renderer = TestUtils.createRenderer();
renderer.render(<MainSection {...props} />);
const output = renderer.getRenderOutput();
return {
props: props,
output: output,
renderer: renderer
};
}
describe('components', () => {
describe('MainSection', () => {
it('should render container', () => {
const { output } = setup();
expect(output.type).toBe('section');
expect(output.props.className).toBe('main');
});
describe('toggle all input', () => {
it('should render', () => {
const { output } = setup();
const [toggle] = output.props.children;
expect(toggle.type).toBe('input');
expect(toggle.props.type).toBe('checkbox');
expect(toggle.props.checked).toBe(false);
});
it('should be checked if all todos completed', () => {
const { output } = setup({ todos: [{
text: 'Use Redux',
completed: true,
id: 0
}]});
const [toggle] = output.props.children;
expect(toggle.props.checked).toBe(true);
});
it('should call completeAll on change', () => {
const { output, props } = setup();
const [toggle] = output.props.children;
toggle.props.onChange({});
expect(props.actions.completeAll).toHaveBeenCalled();
});
});
describe('footer', () => {
it('should render', () => {
const { output } = setup();
const [,, footer] = output.props.children;
expect(footer.type).toBe(Footer);
expect(footer.props.completedCount).toBe(1);
expect(footer.props.activeCount).toBe(1);
expect(footer.props.filter).toBe(SHOW_ALL);
});
it('onShow should set the filter', () => {
const { output, renderer } = setup();
const [,, footer] = output.props.children;
footer.props.onShow(SHOW_COMPLETED);
const updated = renderer.getRenderOutput();
const [,, updatedFooter] = updated.props.children;
expect(updatedFooter.props.filter).toBe(SHOW_COMPLETED);
});
it('onClearCompleted should call clearCompleted', () => {
const { output, props } = setup();
const [,, footer] = output.props.children;
footer.props.onClearCompleted();
expect(props.actions.clearCompleted).toHaveBeenCalled();
});
it('onClearCompleted shouldnt call clearCompleted if no todos completed', () => {
const { output, props } = setup({ todos: [{
text: 'Use Redux',
completed: false,
id: 0
}]});
const [,, footer] = output.props.children;
footer.props.onClearCompleted();
expect(props.actions.clearCompleted.calls.length).toBe(0);
});
});
describe('todo list', () => {
it('should render', () => {
const { output, props } = setup();
const [, list] = output.props.children;
expect(list.type).toBe('ul');
expect(list.props.children.length).toBe(2);
list.props.children.forEach((item, i) => {
expect(item.type).toBe(TodoItem);
expect(item.props.todo).toBe(props.todos[i]);
});
});
it('should filter items', () => {
const { output, renderer, props } = setup();
const [,, footer] = output.props.children;
footer.props.onShow(SHOW_COMPLETED);
const updated = renderer.getRenderOutput();
const [, updatedList] = updated.props.children;
expect(updatedList.props.children.length).toBe(1);
expect(updatedList.props.children[0].props.todo).toBe(props.todos[1]);
});
});
});
});
================================================
FILE: examples/todomvc/test/components/TodoItem.spec.js
================================================
import expect from 'expect';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import TodoItem from '../../components/TodoItem';
import TodoTextInput from '../../components/TodoTextInput';
function setup( editing = false ) {
const props = {
todo: {
id: 0,
text: 'Use Redux',
completed: false
},
editTodo: expect.createSpy(),
deleteTodo: expect.createSpy(),
completeTodo: expect.createSpy()
};
const renderer = TestUtils.createRenderer();
renderer.render(
<TodoItem {...props} />
);
let output = renderer.getRenderOutput();
if (editing) {
const label = output.props.children.props.children[1];
label.props.onDoubleClick({});
output = renderer.getRenderOutput();
}
return {
props: props,
output: output,
renderer: renderer
};
}
describe('components', () => {
describe('TodoItem', () => {
it('initial render', () => {
const { output } = setup();
expect(output.type).toBe('li');
expect(output.props.className).toBe('');
const div = output.props.children;
expect(div.type).toBe('div');
expect(div.props.className).toBe('view');
const [input, label, button] = div.props.children;
expect(input.type).toBe('input');
expect(input.props.checked).toBe(false);
expect(label.type).toBe('label');
expect(label.props.children).toBe('Use Redux');
expect(button.type).toBe('button');
expect(button.props.className).toBe('destroy');
});
it('input onChange should call completeTodo', () => {
const { output, props } = setup();
const input = output.props.children.props.children[0];
input.props.onChange({});
expect(props.completeTodo).toHaveBeenCalledWith(0);
});
it('button onClick should call deleteTodo', () => {
const { output, props } = setup();
const button = output.props.children.props.children[2];
button.props.onClick({});
expect(props.deleteTodo).toHaveBeenCalledWith(0);
});
it('label onDoubleClick should put component in edit state', () => {
const { output, renderer } = setup();
const label = output.props.children.props.children[1];
label.props.onDoubleClick({});
const updated = renderer.getRenderOutput();
expect(updated.type).toBe('li');
expect(updated.props.className).toBe('editing');
});
it('edit state render', () => {
const { output } = setup(true);
expect(output.type).toBe('li');
expect(output.props.className).toBe('editing');
const input = output.props.children;
expect(input.type).toBe(TodoTextInput);
expect(input.props.text).toBe('Use Redux');
expect(input.props.editing).toBe(true);
});
it('TodoTextInput onSave should call editTodo', () => {
const { output, props } = setup(true);
output.props.children.props.onSave('Use Redux');
expect(props.editTodo).toHaveBeenCalledWith(0, 'Use Redux');
});
it('TodoTextInput onSave should call deleteTodo if text is empty', () => {
const { output, props } = setup(true);
output.props.children.props.onSave('');
expect(props.deleteTodo).toHaveBeenCalledWith(0);
});
it('TodoTextInput onSave should exit component from edit state', () => {
const { output, renderer } = setup(true);
output.props.children.props.onSave('Use Redux');
const updated = renderer.getRenderOutput();
expect(updated.type).toBe('li');
expect(updated.props.className).toBe('');
});
});
});
================================================
FILE: examples/todomvc/test/components/TodoTextInput.spec.js
================================================
import expect from 'expect';
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import TodoTextInput from '../../components/TodoTextInput';
function setup(propOverrides) {
const props = Object.assign({
onSave: expect.createSpy(),
text: 'Use Redux',
placeholder: 'What needs to be done?',
editing: false,
newTodo: false
}, propOverrides);
const renderer = TestUtils.createRenderer();
renderer.render(
<TodoTextInput {...props} />
);
let output = renderer.getRenderOutput();
output = renderer.getRenderOutput();
return {
props: props,
output: output,
renderer: renderer
};
}
describe('components', () => {
describe('TodoTextInput', () => {
it('should render correctly', () => {
const { output } = setup();
expect(output.props.placeholder).toEqual('What needs to be done?');
expect(output.props.value).toEqual('Use Redux');
expect(output.props.className).toEqual('');
});
it('should render correctly when editing=true', () => {
const { output } = setup({ editing: true });
expect(output.props.className).toEqual('edit');
});
it('should render correctly when newTodo=true', () => {
const { output } = setup({ newTodo: true });
expect(output.props.className).toEqual('new-todo');
});
it('should update value on change', () => {
const { output, renderer } = setup();
output.props.onChange({ target: { value: 'Use Radox' }});
const updated = renderer.getRenderOutput();
expect(updated.props.value).toEqual('Use Radox');
});
it('should call onSave on return key press', () => {
const { output, props } = setup();
output.props.onKeyDown({ which: 13, target: { value: 'Use Redux' }});
expect(props.onSave).toHaveBeenCalledWith('Use Redux');
});
it('should reset state on return key press if newTodo', () => {
const { output, renderer } = setup({ newTodo: true });
output.props.onKeyDown({ which: 13, target: { value: 'Use Redux' }});
const updated = renderer.getRenderOutput();
expect(updated.props.value).toEqual('');
});
it('should call onSave on blur', () => {
const { output, props } = setup();
output.props.onBlur({ target: { value: 'Use Redux' }});
expect(props.onSave).toHaveBeenCalledWith('Use Redux');
});
it('shouldnt call onSave on blur if newTodo', () => {
const { output, props } = setup({ newTodo: true });
output.props.onBlur({ target: { value: 'Use Redux' }});
expect(props.onSave.calls.length).toBe(0);
});
});
});
================================================
FILE: examples/todomvc/test/reducers/todos.spec.js
================================================
import expect from 'expect';
import todos from '../../reducers/todos';
import * as types from '../../constants/ActionTypes';
describe('todos reducer', () => {
it('should handle initial state', () => {
expect(
todos(undefined, {})
).toEqual([{
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should handle ADD_TODO', () => {
expect(
todos([], {
type: types.ADD_TODO,
text: 'Run the tests'
})
).toEqual([{
text: 'Run the tests',
completed: false,
id: 0
}]);
expect(
todos([{
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.ADD_TODO,
text: 'Run the tests'
})
).toEqual([{
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}]);
expect(
todos([{
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.ADD_TODO,
text: 'Fix the tests'
})
).toEqual([{
text: 'Fix the tests',
completed: false,
id: 2
}, {
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should handle DELETE_TODO', () => {
expect(
todos([{
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.DELETE_TODO,
id: 1
})
).toEqual([{
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should handle EDIT_TODO', () => {
expect(
todos([{
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.EDIT_TODO,
text: 'Fix the tests',
id: 1
})
).toEqual([{
text: 'Fix the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should handle COMPLETE_TODO', () => {
expect(
todos([{
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.COMPLETE_TODO,
id: 1
})
).toEqual([{
text: 'Run the tests',
completed: true,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should handle COMPLETE_ALL', () => {
expect(
todos([{
text: 'Run the tests',
completed: true,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.COMPLETE_ALL
})
).toEqual([{
text: 'Run the tests',
completed: true,
id: 1
}, {
text: 'Use Redux',
completed: true,
id: 0
}]);
// Unmark if all todos are currently completed
expect(
todos([{
text: 'Run the tests',
completed: true,
id: 1
}, {
text: 'Use Redux',
completed: true,
id: 0
}], {
type: types.COMPLETE_ALL
})
).toEqual([{
text: 'Run the tests',
completed: false,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should handle CLEAR_COMPLETED', () => {
expect(
todos([{
text: 'Run the tests',
completed: true,
id: 1
}, {
text: 'Use Redux',
completed: false,
id: 0
}], {
type: types.CLEAR_COMPLETED
})
).toEqual([{
text: 'Use Redux',
completed: false,
id: 0
}]);
});
it('should not generate duplicate ids after CLEAR_COMPLETED', () => {
expect(
[{
type: types.COMPLETE_TODO,
id: 0
}, {
type: types.CLEAR_COMPLETED
}, {
type: types.ADD_TODO,
text: 'Write more tests'
}].reduce(todos, [{
id: 0,
completed: false,
text: 'Use Redux'
}, {
id: 1,
completed: false,
text: 'Write tests'
}])
).toEqual([{
text: 'Write more tests',
completed: false,
id: 2
}, {
text: 'Write tests',
completed: false,
id: 1
}]);
});
});
================================================
FILE: examples/todomvc/test/setup.js
================================================
import { jsdom } from 'jsdom';
global.document = jsdom('<!doctype html><html><body></body></html>');
global.window = document.defaultView;
global.navigator = global.window.navigator;
================================================
FILE: examples/todomvc/webpack.config.js
================================================
var path = require('path');
var webpack = require('webpack');
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: [
'webpack-hot-middleware/client',
'./index'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/static/'
},
plugins: [
new webpack.HotModuleReplacementPlugin()
],
module: {
rules: [{
test: /\.js$/,
loaders: ['babel-loader'],
exclude: /node_modules/
}, {
test: /\.css?$/,
loaders: ['style-loader', 'raw-loader'],
include: __dirname
}]
}
};
================================================
FILE: gulpfile.babel.js
================================================
import fs from 'fs';
import gulp from 'gulp';
import gutil from 'gulp-util';
import jade from 'gulp-pug';
import rename from 'gulp-rename';
import zip from 'gulp-zip';
import webpack from 'webpack';
import mocha from 'gulp-mocha';
import crdv from 'chromedriver';
import devConfig from './webpack/dev.config';
import prodConfig from './webpack/prod.config';
import wrapConfig from './webpack/wrap.config';
function copy(dest) {
gulp.src('./src/assets/**/*').pipe(gulp.dest(dest));
}
/*
* common tasks
*/
gulp.task('replace-webpack-code', () => {
const replaceTasks = [{
from: './webpack/replace/JsonpMainTemplate.runtime.js',
to: './node_modules/webpack/lib/JsonpMainTemplate.runtime.js'
}, {
from: './webpack/replace/log-apply-result.js',
to: './node_modules/webpack/hot/log-apply-result.js'
}];
replaceTasks.forEach(task => fs.writeFileSync(task.to, fs.readFileSync(task.from)));
});
/*
* dev tasks
*/
gulp.task('webpack:dev', (callback) => {
webpack(devConfig, (err, stats) => {
if (err) {
throw new gutil.PluginError('webpack:dev', err);
}
gutil.log('[webpack:dev]', stats.toString({ colors: true }));
});
callback();
});
gulp.task('views:dev', () => {
gulp.src('./src/browser/views/*.pug')
.pipe(jade({
locals: { env: 'dev' }
}))
.pipe(gulp.dest('./dev'));
});
gulp.task('copy:dev', () => {
gulp.src('./src/browser/extension/manifest.json')
.pipe(rename('manifest.json'))
.pipe(gulp.dest('./dev'));
copy('./dev');
});
/*
* build tasks
*/
gulp.task('webpack:build:extension', (callback) => {
function webpackProcess(config) {
return new Promise((resolve, reject) =>
webpack(config, (err, stats) => {
if (err) {
reject(new gutil.PluginError('webpack:build', err));
}
gutil.log('[webpack:build]', stats.toString({ colors: true }));
resolve();
})
);
}
webpackProcess(wrapConfig)
.then(() => webpackProcess(prodConfig))
.then(() => {
const dest = './build/extension';
fs.rename(
`${dest}/js/redux-devtools-extension.bundle.js`,
`${dest}/js/redux-devtools-extension.js`,
callback
);
});
});
gulp.task('views:build:extension', () => {
gulp.src([
'./src/browser/views/*.pug'
])
.pipe(jade({
locals: { env: 'prod' }
}))
.pipe(gulp.dest('./build/extension'));
});
gulp.task('copy:build:extension', () => {
gulp.src('./src/browser/extension/manifest.json')
.pipe(rename('manifest.json'))
.pipe(gulp.dest('./build/extension'));
copy('./build/extension');
});
gulp.task('copy:build:firefox', ['build:extension'], () => {
gulp.src([
'./build/extension/**', '!./build/extension/js/redux-devtools-extension.js'
])
.pipe(gulp.dest('./build/firefox'))
.on('finish', function() {
gulp.src('./src/browser/firefox/manifest.json')
.pipe(gulp.dest('./build/firefox'));
});
copy('./build/firefox');
});
/*
* compress task
*/
gulp.task('compress:extension', () => {
gulp.src('build/extension/**')
.pipe(zip('extension.zip'))
.pipe(gulp.dest('./build'));
});
gulp.task('compress:firefox', () => {
gulp.src('build/firefox/**')
.pipe(zip('firefox.zip'))
.pipe(gulp.dest('./build'));
});
/*
* watch tasks
*/
gulp.task('views:watch', () => {
gulp.watch('./src/browser/views/*.pug', ['views:dev']);
});
gulp.task('copy:watch', () => {
gulp.watch(['./src/browser/extension/manifest.json', './src/assets/**/*'], ['copy:dev']);
});
gulp.task('test:chrome', () => {
crdv.start();
return gulp.src('./test/chrome/*.spec.js')
.pipe(mocha({ require: ['babel-polyfill', 'co-mocha'] }))
.on('end', () => crdv.stop());
});
gulp.task('test:electron', () => {
crdv.start();
return gulp.src('./test/electron/*.spec.js')
.pipe(mocha({ require: ['babel-polyfill', 'co-mocha'] }))
.on('end', () => crdv.stop());
});
gulp.task('default', ['replace-webpack-code', 'webpack:dev', 'views:dev', 'copy:dev', 'views:watch', 'copy:watch']);
gulp.task('build:extension', ['replace-webpack-code', 'webpack:build:extension', 'views:build:extension', 'copy:build:extension']);
gulp.task('build:firefox', ['copy:build:firefox']);
================================================
FILE: npm-package/README.md
================================================
# Redux DevTools Extension's helper
[](https://gitter.im/zalmoxisus/redux-devtools-extension?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Usage
Install:
```
npm install --save redux-devtools-extension
```
and use like that:
```js
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
const store = createStore(reducer, composeWithDevTools(
applyMiddleware(...middleware),
// other store enhancers if any
));
```
or if needed to apply [extension’s options](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md#windowdevtoolsextensionconfig):
```js
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
const composeEnhancers = composeWithDevTools({
// Specify here name, actionsBlacklist, actionsCreators and other options
});
const store = createStore(reducer, composeEnhancers(
applyMiddleware(...middleware),
// other store enhancers if any
));
```
There’re just [few lines of code](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/npm-package/index.js). If you don’t want to allow the extension in production, just use ‘redux-devtools-extension/developmentOnly’ instead of ‘redux-devtools-extension’.
## License
MIT
================================================
FILE: npm-package/developmentOnly.d.ts
================================================
export * from "redux-devtools-extension";
================================================
FILE: npm-package/developmentOnly.js
================================================
"use strict";
var compose = require('redux').compose;
exports.__esModule = true;
exports.composeWithDevTools = (
process.env.NODE_ENV !== 'production' && typeof window !== 'undefined' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ :
function() {
if (arguments.length === 0) return undefined;
if (typeof arguments[0] === 'object') return compose;
return compose.apply(null, arguments);
}
);
exports.devToolsEnhancer = (
process.env.NODE_ENV !== 'production' && typeof window !== 'undefined' &&
window.__REDUX_DEVTOOLS_EXTENSION__ ?
window.__REDUX_DEVTOOLS_EXTENSION__ :
function() { return function(noop) { return noop; } }
);
================================================
FILE: npm-package/index.d.ts
================================================
import {Action, ActionCreator, StoreEnhancer, compose} from "redux";
export interface EnhancerOptions {
/**
* the instance name to be showed on the monitor page. Default value is `document.title`.
* If not specified and there's no document title, it will consist of `tabId` and `instanceId`.
*/
name?: string;
/**
* action creators functions to be available in the Dispatcher.
*/
actionCreators?: ActionCreator<any>[] | {[key: string]: ActionCreator<any>};
/**
* if more than one action is dispatched in the indicated interval, all new actions will be collected and sent at once.
* It is the joint between performance and speed. When set to `0`, all actions will be sent instantly.
* Set it to a higher value when experiencing perf issues (also `maxAge` to a lower value).
*
* @default 500 ms.
*/
latency?: number;
/**
* (> 1) - maximum allowed actions to be stored in the history tree. The oldest actions are removed once maxAge is reached. It's critical for performance.
*
* @default 50
*/
maxAge?: number;
/**
* - `undefined` - will use regular `JSON.stringify` to send data (it's the fast mode).
* - `false` - will handle also circular references.
* - `true` - will handle also date, regex, undefined, error objects, symbols, maps, sets and functions.
* - object, which contains `date`, `regex`, `undefined`, `error`, `symbol`, `map`, `set` and `function` keys.
* For each of them you can indicate if to include (by setting as `true`).
* For `function` key you can also specify a custom function which handles serialization.
* See [`jsan`](https://github.com/kolodny/jsan) for more details.
*/
serialize?: boolean | {
date?: boolean;
regex?: boolean;
undefined?: boolean;
error?: boolean;
symbol?: boolean;
map?: boolean;
set?: boolean;
function?: boolean | Function;
};
/**
* function which takes `action` object and id number as arguments, and should return `action` object back.
*/
actionSanitizer?: <A extends Action>(action: A, id: number) => A;
/**
* function which takes `state` object and index as arguments, and should return `state` object back.
*/
stateSanitizer?: <S>(state: S, index: number) => S;
/**
* *string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers).
* If `actionsWhitelist` specified, `actionsBlacklist` is ignored.
*/
actionsBlacklist?: string | string[];
/**
* *string or array of strings as regex* - actions types to be hidden / shown in the monitors (while passed to the reducers).
* If `actionsWhitelist` specified, `actionsBlacklist` is ignored.
*/
actionsWhitelist?: string | string[];
/**
* called for every action before sending, takes `state` and `action` object, and returns `true` in case it allows sending the current data to the monitor.
* Use it as a more advanced version of `actionsBlacklist`/`actionsWhitelist` parameters.
*/
predicate?: <S, A extends Action>(state: S, action: A) => boolean;
/**
* if specified as `false`, it will not record the changes till clicking on `Start recording` button.
* Available only for Redux enhancer, for others use `autoPause`.
*
* @default true
*/
shouldRecordChanges?: boolean;
/**
* if specified, whenever clicking on `Pause recording` button and there are actions in the history log, will add this action type.
* If not specified, will commit when paused. Available only for Redux enhancer.
*
* @default "@@PAUSED""
*/
pauseActionType?: string;
/**
* auto pauses when the extension’s window is not opened, and so has zero impact on your app when not in use.
* Not available for Redux enhancer (as it already does it but storing the data to be sent).
*
* @default false
*/
autoPause?: boolean;
/**
* if specified as `true`, it will not allow any non-monitor actions to be dispatched till clicking on `Unlock changes` button.
* Available only for Redux enhancer.
*
* @default false
*/
shouldStartLocked?: boolean;
/**
* if set to `false`, will not recompute the states on hot reloading (or on replacing the reducers). Available only for Redux enhancer.
*
* @default true
*/
shouldHotReload?: boolean;
/**
* if specified as `true`, whenever there's an exception in reducers, the monitors will show the error message, and next actions will not be dispatched.
*
* @default false
*/
shouldCatchErrors?: boolean;
/**
* If you want to restrict the extension, specify the features you allow.
* If not specified, all of the features are enabled. When set as an object, only those included as `true` will be allowed.
* Note that except `true`/`false`, `import` and `export` can be set as `custom` (which is by default for Redux enhancer), meaning that the importing/exporting occurs on the client side.
* Otherwise, you'll get/set the data right from the monitor part.
*/
features?: {
/**
* start/pause recording of dispatched actions
*/
pause?: boolean;
/**
* lock/unlock dispatching actions and side effects
*/
lock?: boolean;
/**
* persist states on page reloading
*/
persist?: boolean;
/**
* export history of actions in a file
*/
export?: boolean | "custom";
/**
* import history of actions from a file
*/
import?: boolean | "custom";
/**
* jump back and forth (time travelling)
*/
jump?: boolean;
/**
* skip (cancel) actions
*/
skip?: boolean;
/**
* drag and drop actions in the history list
*/
reorder?: boolean;
/**
* dispatch custom actions or action creators
*/
dispatch?: boolean;
/**
* generate tests for the selected actions
*/
test?: boolean;
};
/**
* Set to true or a stacktrace-returning function to record call stack traces for dispatched actions.
* Defaults to false.
*/
trace?: boolean | (<A extends Action>(action: A) => string);
/**
* The maximum number of stack trace entries to record per action. Defaults to 10.
*/
traceLimit?: number;
}
export function composeWithDevTools<StoreExt, StateExt>(...funcs: Array<StoreEnhancer<StoreExt>>): StoreEnhancer<StoreExt>;
export function composeWithDevTools(options: EnhancerOptions): typeof compose;
export function devToolsEnhancer(options: EnhancerOptions): StoreEnhancer<any>;
================================================
FILE: npm-package/index.js
================================================
"use strict";
var compose = require('redux').compose;
exports.__esModule = true;
exports.composeWithDevTools = (
typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ :
function() {
if (arguments.length === 0) return undefined;
if (typeof arguments[0] === 'object') return compose;
return compose.apply(null, arguments);
}
);
exports.devToolsEnhancer = (
typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__ ?
window.__REDUX_DEVTOOLS_EXTENSION__ :
function() { return function(noop) { return noop; } }
);
================================================
FILE: npm-package/logOnly.d.ts
================================================
export * from "redux-devtools-extension";
================================================
FILE: npm-package/logOnly.js
================================================
"use strict";
var assign = require('./utils/assign');
var compose = require('redux').compose;
function enhancer() {
var config = arguments[0] || {};
config.features = { pause: true, export: true, test: true };
config.type = 'redux';
if (config.autoPause === undefined) config.autoPause = true;
if (config.latency === undefined) config.latency = 500;
return function(createStore) {
return function(reducer, preloadedState, enhancer) {
var store = createStore(reducer, preloadedState, enhancer);
var origDispatch = store.dispatch;
var devTools = window.__REDUX_DEVTOOLS_EXTENSION__.connect(config);
devTools.init(store.getState());
var dispatch = function(action) {
var r = origDispatch(action);
devTools.send(action, store.getState());
return r;
};
if (Object.assign) return Object.assign(store, { dispatch: dispatch });
return assign(store, 'dispatch', dispatch);
}
}
}
function composeWithEnhancer(config) {
return function () {
return compose(compose.apply(null, arguments), enhancer(config));
}
}
exports.__esModule = true;
exports.composeWithDevTools = function() {
if (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__) {
if (arguments.length === 0) return enhancer();
if (typeof arguments[0] === 'object') return composeWithEnhancer(arguments[0]);
return composeWithEnhancer().apply(null, arguments);
}
if (arguments.length === 0) return undefined;
if (typeof arguments[0] === 'object') return compose;
return compose.apply(null, arguments);
};
exports.devToolsEnhancer = (
typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__ ?
enhancer :
function() { return function(noop) { return noop; } }
);
================================================
FILE: npm-package/logOnlyInProduction.d.ts
================================================
export * from "redux-devtools-extension";
================================================
FILE: npm-package/logOnlyInProduction.js
================================================
"use strict";
var compose = require('redux').compose;
var logOnly = require('./logOnly');
exports.__esModule = true;
exports.composeWithDevTools = (
process.env.NODE_ENV === 'production' ? logOnly.composeWithDevTools :
typeof window !== 'undefined' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ :
function() {
if (arguments.length === 0) return undefined;
if (typeof arguments[0] === 'object') return compose;
return compose.apply(null, arguments);
}
);
exports.devToolsEnhancer = (
process.env.NODE_ENV === 'production' ? logOnly.devToolsEnhancer :
typeof window !== 'undefined' &&
window.__REDUX_DEVTOOLS_EXTENSION__ ?
window.__REDUX_DEVTOOLS_EXTENSION__ :
function() { return function(noop) { return noop; } }
);
================================================
FILE: npm-package/package.json
================================================
{
"name": "redux-devtools-extension",
"version": "2.13.8",
"description": "Wrappers for Redux DevTools Extension.",
"main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/zalmoxisus/redux-devtools-extension"
},
"homepage": "https://github.com/zalmoxisus/redux-devtools-extension",
"author": "Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)",
"license": "MIT",
"peerDependencies": {
"redux": "^3.1.0 || ^4.0.0"
}
}
================================================
FILE: npm-package/utils/assign.js
================================================
var objectKeys = Object.keys || function (obj) {
var keys = [];
for (var key in obj) {
if ({}.hasOwnProperty.call(obj, key)) keys.push(key);
}
return keys;
};
function assign(obj, newKey, newValue) {
var keys = objectKeys(obj);
var copy = {};
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
copy[key] = obj[key];
}
copy[newKey] = newValue;
return copy;
}
module.expor
gitextract_tx0nzry9/
├── .babelrc
├── .bookignore
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── appveyor.yml
├── book.json
├── docs/
│ ├── API/
│ │ ├── Arguments.md
│ │ ├── Methods.md
│ │ └── README.md
│ ├── Articles.md
│ ├── Credits.md
│ ├── FAQ.md
│ ├── Features/
│ │ └── Trace.md
│ ├── Feedback.md
│ ├── Integrations.md
│ ├── README.md
│ ├── Recipes.md
│ ├── Troubleshooting.md
│ └── Videos.md
├── examples/
│ ├── buildAll.js
│ ├── counter/
│ │ ├── .babelrc
│ │ ├── actions/
│ │ │ └── counter.js
│ │ ├── components/
│ │ │ └── Counter.js
│ │ ├── containers/
│ │ │ └── App.js
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── reducers/
│ │ │ ├── counter.js
│ │ │ └── index.js
│ │ ├── server.js
│ │ ├── store/
│ │ │ └── configureStore.js
│ │ ├── test/
│ │ │ ├── actions/
│ │ │ │ └── counter.spec.js
│ │ │ ├── components/
│ │ │ │ └── Counter.spec.js
│ │ │ ├── containers/
│ │ │ │ └── App.spec.js
│ │ │ ├── reducers/
│ │ │ │ └── counter.spec.js
│ │ │ └── setup.js
│ │ └── webpack.config.js
│ ├── react-counter-messaging/
│ │ ├── .babelrc
│ │ ├── components/
│ │ │ └── Counter.js
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── package.json
│ │ └── webpack.config.js
│ ├── router/
│ │ ├── .babelrc
│ │ ├── actions/
│ │ │ └── todos.js
│ │ ├── components/
│ │ │ ├── Footer.js
│ │ │ ├── Header.js
│ │ │ ├── MainSection.js
│ │ │ ├── TodoItem.js
│ │ │ └── TodoTextInput.js
│ │ ├── constants/
│ │ │ ├── ActionTypes.js
│ │ │ └── TodoFilters.js
│ │ ├── containers/
│ │ │ ├── App.js
│ │ │ ├── Root.js
│ │ │ └── Wrapper.js
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── package.json
│ │ ├── reducers/
│ │ │ ├── index.js
│ │ │ └── todos.js
│ │ ├── server.js
│ │ ├── store/
│ │ │ └── configureStore.js
│ │ ├── test/
│ │ │ ├── actions/
│ │ │ │ └── todos.spec.js
│ │ │ ├── components/
│ │ │ │ ├── Footer.spec.js
│ │ │ │ ├── Header.spec.js
│ │ │ │ ├── MainSection.spec.js
│ │ │ │ ├── TodoItem.spec.js
│ │ │ │ └── TodoTextInput.spec.js
│ │ │ ├── reducers/
│ │ │ │ └── todos.spec.js
│ │ │ └── setup.js
│ │ └── webpack.config.js
│ ├── saga-counter/
│ │ ├── .babelrc
│ │ ├── index.html
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── components/
│ │ │ │ └── Counter.js
│ │ │ ├── main.js
│ │ │ ├── reducers/
│ │ │ │ └── index.js
│ │ │ └── sagas/
│ │ │ └── index.js
│ │ └── webpack.config.js
│ └── todomvc/
│ ├── .babelrc
│ ├── actions/
│ │ ├── index.js
│ │ └── todos.js
│ ├── components/
│ │ ├── Footer.js
│ │ ├── Header.js
│ │ ├── MainSection.js
│ │ ├── TodoItem.js
│ │ └── TodoTextInput.js
│ ├── constants/
│ │ ├── ActionTypes.js
│ │ └── TodoFilters.js
│ ├── containers/
│ │ └── App.js
│ ├── index.html
│ ├── index.js
│ ├── package.json
│ ├── reducers/
│ │ ├── index.js
│ │ └── todos.js
│ ├── server.js
│ ├── store/
│ │ └── configureStore.js
│ ├── test/
│ │ ├── actions/
│ │ │ └── todos.spec.js
│ │ ├── components/
│ │ │ ├── Footer.spec.js
│ │ │ ├── Header.spec.js
│ │ │ ├── MainSection.spec.js
│ │ │ ├── TodoItem.spec.js
│ │ │ └── TodoTextInput.spec.js
│ │ ├── reducers/
│ │ │ └── todos.spec.js
│ │ └── setup.js
│ └── webpack.config.js
├── gulpfile.babel.js
├── npm-package/
│ ├── README.md
│ ├── developmentOnly.d.ts
│ ├── developmentOnly.js
│ ├── index.d.ts
│ ├── index.js
│ ├── logOnly.d.ts
│ ├── logOnly.js
│ ├── logOnlyInProduction.d.ts
│ ├── logOnlyInProduction.js
│ ├── package.json
│ └── utils/
│ └── assign.js
├── package.json
├── src/
│ ├── app/
│ │ ├── api/
│ │ │ ├── filters.js
│ │ │ ├── generateInstanceId.js
│ │ │ ├── importState.js
│ │ │ ├── index.js
│ │ │ ├── notifyErrors.js
│ │ │ └── openWindow.js
│ │ ├── containers/
│ │ │ └── App.js
│ │ ├── middlewares/
│ │ │ ├── api.js
│ │ │ ├── instanceSelector.js
│ │ │ ├── panelSync.js
│ │ │ └── windowSync.js
│ │ ├── reducers/
│ │ │ ├── background/
│ │ │ │ ├── index.js
│ │ │ │ └── persistStates.js
│ │ │ ├── panel/
│ │ │ │ └── index.js
│ │ │ └── window/
│ │ │ ├── index.js
│ │ │ └── instances.js
│ │ ├── service/
│ │ │ └── Monitor.js
│ │ └── stores/
│ │ ├── backgroundStore.js
│ │ ├── createStore.js
│ │ ├── enhancerStore.js
│ │ ├── panelStore.js
│ │ └── windowStore.js
│ └── browser/
│ ├── extension/
│ │ ├── background/
│ │ │ ├── contextMenus.js
│ │ │ ├── getPreloadedState.js
│ │ │ ├── index.js
│ │ │ ├── logging.js
│ │ │ └── openWindow.js
│ │ ├── chromeAPIMock.js
│ │ ├── devpanel/
│ │ │ └── index.js
│ │ ├── devtools/
│ │ │ └── index.js
│ │ ├── inject/
│ │ │ ├── contentScript.js
│ │ │ ├── deprecatedWarn.js
│ │ │ ├── index.js
│ │ │ ├── pageScript.js
│ │ │ └── pageScriptWrap.js
│ │ ├── manifest.json
│ │ ├── options/
│ │ │ ├── AllowToRunGroup.js
│ │ │ ├── ContextMenuGroup.js
│ │ │ ├── EditorGroup.js
│ │ │ ├── FilterGroup.js
│ │ │ ├── MiscellaneousGroup.js
│ │ │ ├── Options.js
│ │ │ ├── index.js
│ │ │ └── syncOptions.js
│ │ └── window/
│ │ ├── index.js
│ │ └── remote.js
│ ├── firefox/
│ │ └── manifest.json
│ └── views/
│ ├── devpanel.pug
│ ├── devtools.pug
│ ├── includes/
│ │ └── style.pug
│ ├── options.pug
│ ├── remote.pug
│ └── window.pug
├── test/
│ ├── .eslintrc
│ ├── app/
│ │ ├── containers/
│ │ │ └── App.spec.js
│ │ ├── inject/
│ │ │ ├── api.spec.js
│ │ │ └── enhancer.spec.js
│ │ └── setup.js
│ ├── chrome/
│ │ └── extension.spec.js
│ ├── electron/
│ │ ├── devpanel.spec.js
│ │ └── fixture/
│ │ ├── index.html
│ │ ├── main.js
│ │ ├── package.json
│ │ └── renderer.js
│ ├── perf/
│ │ ├── data.js
│ │ └── send.spec.js
│ └── utils/
│ ├── e2e.js
│ └── inject.js
└── webpack/
├── base.config.js
├── dev.config.js
├── prod.config.js
├── replace/
│ ├── JsonpMainTemplate.runtime.js
│ └── log-apply-result.js
└── wrap.config.js
SYMBOL INDEX (249 symbols across 83 files)
FILE: examples/counter/actions/counter.js
constant INCREMENT_COUNTER (line 1) | const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
constant DECREMENT_COUNTER (line 2) | const DECREMENT_COUNTER = 'DECREMENT_COUNTER';
function increment (line 6) | function increment() {
function decrement (line 12) | function decrement() {
function autoIncrement (line 18) | function autoIncrement(delay = 10) {
function incrementAsync (line 31) | function incrementAsync(delay = 1000) {
FILE: examples/counter/components/Counter.js
class Counter (line 4) | class Counter extends Component {
method render (line 5) | render() {
FILE: examples/counter/containers/App.js
function mapStateToProps (line 6) | function mapStateToProps(state) {
function mapDispatchToProps (line 12) | function mapDispatchToProps(dispatch) {
FILE: examples/counter/reducers/counter.js
function counter (line 3) | function counter(state = 0, action) {
FILE: examples/counter/store/configureStore.js
function configureStore (line 8) | function configureStore(preloadedState) {
FILE: examples/counter/test/actions/counter.spec.js
function mockStore (line 11) | function mockStore(getState, expectedActions, onLastAction) {
FILE: examples/counter/test/components/Counter.spec.js
function setup (line 6) | function setup() {
FILE: examples/counter/test/containers/App.spec.js
function setup (line 8) | function setup(initialState) {
FILE: examples/react-counter-messaging/components/Counter.js
class Counter (line 8) | class Counter extends Component {
method constructor (line 9) | constructor() {
method componentWillMount (line 17) | componentWillMount() {
method componentWillUnmount (line 30) | componentWillUnmount() {
method increment (line 37) | increment() {
method decrement (line 43) | decrement() {
method render (line 49) | render() {
FILE: examples/router/actions/todos.js
function addTodo (line 3) | function addTodo(text) {
function deleteTodo (line 7) | function deleteTodo(id) {
function editTodo (line 11) | function editTodo(id, text) {
function completeTodo (line 15) | function completeTodo(id) {
function completeAll (line 19) | function completeAll() {
function clearCompleted (line 23) | function clearCompleted() {
FILE: examples/router/components/Footer.js
constant FILTER_TITLES (line 5) | const FILTER_TITLES = {
class Footer (line 11) | class Footer extends Component {
method renderTodoCount (line 12) | renderTodoCount() {
method renderFilterLink (line 23) | renderFilterLink(filter) {
method renderClearButton (line 36) | renderClearButton() {
method render (line 48) | render() {
FILE: examples/router/components/Header.js
class Header (line 4) | class Header extends Component {
method handleSave (line 5) | handleSave(text) {
method render (line 11) | render() {
FILE: examples/router/components/MainSection.js
constant TODO_FILTERS (line 6) | const TODO_FILTERS = {
class MainSection (line 12) | class MainSection extends Component {
method constructor (line 13) | constructor(props, context) {
method handleClearCompleted (line 18) | handleClearCompleted() {
method handleShow (line 25) | handleShow(filter) {
method renderToggleAll (line 29) | renderToggleAll(completedCount) {
method renderFooter (line 41) | renderFooter(completedCount) {
method render (line 57) | render() {
FILE: examples/router/components/TodoItem.js
class TodoItem (line 5) | class TodoItem extends Component {
method constructor (line 6) | constructor(props, context) {
method handleDoubleClick (line 13) | handleDoubleClick() {
method handleSave (line 17) | handleSave(id, text) {
method render (line 26) | render() {
FILE: examples/router/components/TodoTextInput.js
class TodoTextInput (line 4) | class TodoTextInput extends Component {
method constructor (line 5) | constructor(props, context) {
method handleSubmit (line 12) | handleSubmit(e) {
method handleChange (line 22) | handleChange(e) {
method handleBlur (line 26) | handleBlur(e) {
method render (line 32) | render() {
FILE: examples/router/constants/ActionTypes.js
constant ADD_TODO (line 1) | const ADD_TODO = 'ADD_TODO';
constant DELETE_TODO (line 2) | const DELETE_TODO = 'DELETE_TODO';
constant EDIT_TODO (line 3) | const EDIT_TODO = 'EDIT_TODO';
constant COMPLETE_TODO (line 4) | const COMPLETE_TODO = 'COMPLETE_TODO';
constant COMPLETE_ALL (line 5) | const COMPLETE_ALL = 'COMPLETE_ALL';
constant CLEAR_COMPLETED (line 6) | const CLEAR_COMPLETED = 'CLEAR_COMPLETED';
FILE: examples/router/constants/TodoFilters.js
constant SHOW_ALL (line 1) | const SHOW_ALL = 'show_all';
constant SHOW_COMPLETED (line 2) | const SHOW_COMPLETED = 'show_completed';
constant SHOW_ACTIVE (line 3) | const SHOW_ACTIVE = 'show_active';
FILE: examples/router/containers/App.js
class App (line 8) | class App extends Component {
method render (line 9) | render() {
function mapStateToProps (line 25) | function mapStateToProps(state) {
function mapDispatchToProps (line 32) | function mapDispatchToProps(dispatch) {
FILE: examples/router/containers/Root.js
class Root (line 8) | class Root extends Component {
method render (line 9) | render() {
FILE: examples/router/containers/Wrapper.js
function mapDispatchToProps (line 8) | function mapDispatchToProps(dispatch) {
class Wrapper (line 15) | @connect((state) => ({}), mapDispatchToProps)
method constructor (line 21) | constructor(props) {
method handleClick (line 26) | handleClick(event) {
method render (line 43) | render() {
FILE: examples/router/reducers/todos.js
function todos (line 9) | function todos(state = initialState, action) {
FILE: examples/router/store/configureStore.js
function configureStore (line 7) | function configureStore(initialState) {
FILE: examples/router/test/components/Footer.spec.js
function setup (line 7) | function setup(propOverrides) {
function getTextContent (line 26) | function getTextContent(elem) {
FILE: examples/router/test/components/Header.spec.js
function setup (line 7) | function setup() {
FILE: examples/router/test/components/MainSection.spec.js
function setup (line 9) | function setup(propOverrides) {
FILE: examples/router/test/components/TodoItem.spec.js
function setup (line 7) | function setup( editing = false ) {
FILE: examples/router/test/components/TodoTextInput.spec.js
function setup (line 6) | function setup(propOverrides) {
FILE: examples/saga-counter/src/main.js
function render (line 25) | function render() {
FILE: examples/saga-counter/src/reducers/index.js
function counter (line 1) | function counter(state = 0, action) {
FILE: examples/todomvc/actions/todos.js
function addTodo (line 3) | function addTodo(text) {
function deleteTodo (line 7) | function deleteTodo(id) {
function editTodo (line 11) | function editTodo(id, text) {
function completeTodo (line 15) | function completeTodo(id) {
function completeAll (line 19) | function completeAll() {
function clearCompleted (line 23) | function clearCompleted() {
FILE: examples/todomvc/components/Footer.js
constant FILTER_TITLES (line 6) | const FILTER_TITLES = {
class Footer (line 12) | class Footer extends Component {
method renderTodoCount (line 13) | renderTodoCount() {
method renderFilterLink (line 24) | renderFilterLink(filter) {
method renderClearButton (line 37) | renderClearButton() {
method render (line 49) | render() {
FILE: examples/todomvc/components/Header.js
class Header (line 5) | class Header extends Component {
method handleSave (line 6) | handleSave(text) {
method render (line 12) | render() {
FILE: examples/todomvc/components/MainSection.js
constant TODO_FILTERS (line 7) | const TODO_FILTERS = {
class MainSection (line 13) | class MainSection extends Component {
method constructor (line 14) | constructor(props, context) {
method handleClearCompleted (line 19) | handleClearCompleted() {
method handleShow (line 26) | handleShow(filter) {
method renderToggleAll (line 30) | renderToggleAll(completedCount) {
method renderFooter (line 42) | renderFooter(completedCount) {
method render (line 58) | render() {
FILE: examples/todomvc/components/TodoItem.js
class TodoItem (line 6) | class TodoItem extends Component {
method constructor (line 7) | constructor(props, context) {
method handleDoubleClick (line 14) | handleDoubleClick() {
method handleSave (line 18) | handleSave(id, text) {
method render (line 27) | render() {
FILE: examples/todomvc/components/TodoTextInput.js
class TodoTextInput (line 5) | class TodoTextInput extends Component {
method constructor (line 6) | constructor(props, context) {
method handleSubmit (line 13) | handleSubmit(e) {
method handleChange (line 23) | handleChange(e) {
method handleBlur (line 27) | handleBlur(e) {
method render (line 33) | render() {
FILE: examples/todomvc/constants/ActionTypes.js
constant ADD_TODO (line 1) | const ADD_TODO = 'ADD_TODO';
constant DELETE_TODO (line 2) | const DELETE_TODO = 'DELETE_TODO';
constant EDIT_TODO (line 3) | const EDIT_TODO = 'EDIT_TODO';
constant COMPLETE_TODO (line 4) | const COMPLETE_TODO = 'COMPLETE_TODO';
constant COMPLETE_ALL (line 5) | const COMPLETE_ALL = 'COMPLETE_ALL';
constant CLEAR_COMPLETED (line 6) | const CLEAR_COMPLETED = 'CLEAR_COMPLETED';
FILE: examples/todomvc/constants/TodoFilters.js
constant SHOW_ALL (line 1) | const SHOW_ALL = 'show_all';
constant SHOW_COMPLETED (line 2) | const SHOW_COMPLETED = 'show_completed';
constant SHOW_ACTIVE (line 3) | const SHOW_ACTIVE = 'show_active';
FILE: examples/todomvc/containers/App.js
class App (line 9) | class App extends Component {
method render (line 10) | render() {
function mapStateToProps (line 26) | function mapStateToProps(state) {
function mapDispatchToProps (line 32) | function mapDispatchToProps(dispatch) {
FILE: examples/todomvc/reducers/todos.js
function todos (line 10) | function todos(state = initialState, action) {
FILE: examples/todomvc/store/configureStore.js
function configureStore (line 5) | function configureStore(preloadedState) {
FILE: examples/todomvc/test/components/Footer.spec.js
function setup (line 7) | function setup(propOverrides) {
function getTextContent (line 26) | function getTextContent(elem) {
FILE: examples/todomvc/test/components/Header.spec.js
function setup (line 7) | function setup() {
FILE: examples/todomvc/test/components/MainSection.spec.js
function setup (line 9) | function setup(propOverrides) {
FILE: examples/todomvc/test/components/TodoItem.spec.js
function setup (line 7) | function setup( editing = false ) {
FILE: examples/todomvc/test/components/TodoTextInput.spec.js
function setup (line 6) | function setup(propOverrides) {
FILE: gulpfile.babel.js
function copy (line 14) | function copy(dest) {
function webpackProcess (line 66) | function webpackProcess(config) {
FILE: npm-package/index.d.ts
type EnhancerOptions (line 3) | interface EnhancerOptions {
FILE: npm-package/logOnly.js
function enhancer (line 6) | function enhancer() {
function composeWithEnhancer (line 33) | function composeWithEnhancer(config) {
FILE: npm-package/utils/assign.js
function assign (line 9) | function assign(obj, newKey, newValue) {
FILE: src/app/api/filters.js
function getLocalFilter (line 9) | function getLocalFilter(config) {
function isFiltered (line 25) | function isFiltered(action, localFilter) {
function filterActions (line 39) | function filterActions(actionsById, actionSanitizer) {
function filterStates (line 46) | function filterStates(computedStates, stateSanitizer) {
function filterState (line 53) | function filterState(state, type, localFilter, stateSanitizer, actionSan...
function startingFrom (line 102) | function startingFrom(
FILE: src/app/api/generateInstanceId.js
function generateId (line 3) | function generateId(instanceId) {
FILE: src/app/api/importState.js
function deprecate (line 5) | function deprecate(param) {
function importState (line 9) | function importState(state, { deserializeState, deserializeAction, seria...
FILE: src/app/api/index.js
function windowReplacer (line 12) | function windowReplacer(key, value) {
function tryCatchStringify (line 19) | function tryCatchStringify(obj) {
function stringify (line 31) | function stringify(obj, serialize) {
function getSeralizeParameter (line 45) | function getSeralizeParameter(config, param) {
function post (line 73) | function post(message) {
function getStackTrace (line 77) | function getStackTrace(config, toExcludeFromTrace) {
function amendActionType (line 106) | function amendActionType(action, config, toExcludeFromTrace) {
function toContentScript (line 115) | function toContentScript(message, serializeState, serializeAction) {
function sendMessage (line 134) | function sendMessage(action, state, config, instanceId, name) {
function handleMessages (line 153) | function handleMessages(event) {
function setListener (line 164) | function setListener(onMessage, instanceId) {
function disconnect (line 183) | function disconnect() {
function connect (line 188) | function connect(preConfig) {
function updateStore (line 314) | function updateStore(stores) {
function isInIframe (line 329) | function isInIframe() {
FILE: src/app/api/notifyErrors.js
function createExpBackoffTimer (line 4) | function createExpBackoffTimer(step) {
function postError (line 21) | function postError(message) {
function catchErrors (line 30) | function catchErrors(e) {
function notifyErrors (line 39) | function notifyErrors(onError) {
FILE: src/app/api/openWindow.js
function openWindow (line 1) | function openWindow(position) {
FILE: src/app/containers/App.js
class App (line 29) | @enhance
method render (line 42) | render() {
function mapStateToProps (line 164) | function mapStateToProps(state) {
function mapDispatchToProps (line 180) | function mapDispatchToProps(dispatch) {
FILE: src/app/middlewares/api.js
constant CONNECTED (line 8) | const CONNECTED = 'socket/CONNECTED';
constant DISCONNECTED (line 9) | const DISCONNECTED = 'socket/DISCONNECTED';
function toMonitors (line 21) | function toMonitors(action, tabId, verbose) {
function toContentScript (line 32) | function toContentScript({ message, action, id, instanceId, state }) {
function toAllTabs (line 41) | function toAllTabs(msg) {
function monitorInstances (line 48) | function monitorInstances(shouldMonitor, id) {
function getReducerError (line 59) | function getReducerError() {
function togglePersist (line 67) | function togglePersist() {
function messaging (line 79) | function messaging(request, sender, sendResponse) {
function disconnect (line 155) | function disconnect(type, id, listener) {
function onConnect (line 173) | function onConnect(port) {
function api (line 241) | function api() {
FILE: src/app/middlewares/instanceSelector.js
function selectInstance (line 3) | function selectInstance(tabId, store, next) {
function getCurrentTabId (line 12) | function getCurrentTabId(next) {
function popupSelector (line 23) | function popupSelector(store) {
FILE: src/app/middlewares/panelSync.js
function panelDispatcher (line 4) | function panelDispatcher(bgConnection) {
FILE: src/app/reducers/background/persistStates.js
function persistStates (line 1) | function persistStates(state = false, action) {
FILE: src/app/reducers/window/instances.js
function instances (line 4) | function instances(state = initialState, action) {
FILE: src/app/service/Monitor.js
class Monitor (line 1) | class Monitor {
method constructor (line 2) | constructor(update) {
FILE: src/app/stores/backgroundStore.js
function configureStore (line 5) | function configureStore(preloadedState) {
FILE: src/app/stores/createStore.js
function configureStore (line 3) | function configureStore(reducer, initialState, enhance) {
FILE: src/app/stores/enhancerStore.js
function getUrlParam (line 5) | function getUrlParam(key) {
function configureStore (line 10) | function configureStore(next, monitorReducer, config) {
FILE: src/app/stores/panelStore.js
function configureStore (line 7) | function configureStore(position, bgConnection, preloadedState) {
FILE: src/app/stores/windowStore.js
function configureStore (line 10) | function configureStore(baseStore, position, preloadedState) {
FILE: src/browser/extension/background/contextMenus.js
function createMenu (line 3) | function createMenu() {
function removeMenu (line 28) | function removeMenu() {
FILE: src/browser/extension/background/getPreloadedState.js
function getPreloadedState (line 8) | function getPreloadedState(position, cb) {
FILE: src/browser/extension/background/logging.js
function getReport (line 3) | function getReport(reportId, tabId, instanceId) {
FILE: src/browser/extension/background/openWindow.js
function openDevToolsWindow (line 4) | function openDevToolsWindow(position) {
FILE: src/browser/extension/chromeAPIMock.js
method addListener (line 15) | addListener() {}
method addListener (line 18) | addListener() {}
method addListener (line 24) | addListener() {}
method create (line 26) | create() {}
method clear (line 27) | clear() {}
method addListener (line 31) | addListener() {}
method set (line 45) | set(obj, callback) {
method get (line 53) | get(obj, callback) {
method remove (line 63) | remove(items, callback) {
FILE: src/browser/extension/devpanel/index.js
function renderDevTools (line 23) | function renderDevTools() {
function renderNA (line 37) | function renderNA() {
function init (line 70) | function init(id) {
FILE: src/browser/extension/devtools/index.js
function createPanel (line 1) | function createPanel(url) {
FILE: src/browser/extension/inject/contentScript.js
function connect (line 9) | function connect() {
function handleDisconnect (line 44) | function handleDisconnect() {
function tryCatch (line 50) | function tryCatch(fn, args) {
function send (line 91) | function send(message) {
function handleMessages (line 102) | function handleMessages(event) {
FILE: src/browser/extension/inject/pageScript.js
function deprecateParam (line 23) | function deprecateParam(oldParam, newParam) {
function exportState (line 71) | function exportState() {
function relay (line 84) | function relay(type, state, action, nextActionId, libConfig) {
function dispatchRemotely (line 153) | function dispatchRemotely(action) {
function importPayloadFrom (line 163) | function importPayloadFrom(state) {
function dispatchMonitorAction (line 174) | function dispatchMonitorAction(action) {
function onMessage (line 195) | function onMessage(message) {
function init (line 260) | function init() {
function handleChange (line 280) | function handleChange() {
FILE: src/browser/extension/options/syncOptions.js
function syncOptions (line 92) | function syncOptions(toAllTabs) {
FILE: test/app/inject/enhancer.spec.js
function counter (line 7) | function counter(state = 0, action) {
FILE: test/electron/devpanel.spec.js
function showReduxPanel (line 40) | function showReduxPanel() {
FILE: test/electron/fixture/renderer.js
constant INCREMENT_COUNTER (line 3) | const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
constant DECREMENT_COUNTER (line 4) | const DECREMENT_COUNTER = 'DECREMENT_COUNTER';
FILE: test/perf/send.spec.js
function test (line 7) | function test(title, data, maxTime = 100) {
FILE: test/utils/inject.js
function insertScript (line 1) | function insertScript(str) {
function listenMessage (line 7) | function listenMessage(f) {
FILE: webpack/replace/JsonpMainTemplate.runtime.js
function webpackHotUpdateCallback (line 7) | function webpackHotUpdateCallback(chunkId, moreModules) { // eslint-disa...
function evalCode (line 13) | function evalCode(code, context) {
function hotDownloadManifest (line 28) | function hotDownloadManifest(callback) { // eslint-disable-line no-unuse...
Condensed preview — 199 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (481K chars).
[
{
"path": ".babelrc",
"chars": 120,
"preview": "{\n \"presets\": [ \"es2015\", \"stage-0\", \"react\" ],\n \"plugins\": [ \"add-module-exports\", \"transform-decorators-legacy\" ]\n}\n"
},
{
"path": ".bookignore",
"chars": 67,
"preview": "src/\nbuild/\ndev/\nexamples/\nnpm-package/\ntest/\npackage.json\nwebpack/"
},
{
"path": ".eslintignore",
"chars": 84,
"preview": "node_modules\nbuild\ndev\nwebpack/replace\nexamples\ntest/app/setup.js\nnpm-package\n_book\n"
},
{
"path": ".eslintrc",
"chars": 699,
"preview": "{\n \"extends\": \"eslint-config-airbnb\",\n \"globals\": {\n \"chrome\": true,\n \"__DEVELOPMENT__\": true\n },\n \"env\": {\n "
},
{
"path": ".gitignore",
"chars": 73,
"preview": "node_modules\nnpm-debug.log\n.DS_Store\n.idea/\ndist/\nbuild/\ndev/\ntmp/\n_book\n"
},
{
"path": ".travis.yml",
"chars": 615,
"preview": "sudo: required\ndist: trusty\nlanguage: node_js\nnode_js:\n - \"6\"\ncache:\n directories:\n - $HOME/.yarn-cache\n - node_"
},
{
"path": "CHANGELOG.md",
"chars": 247,
"preview": "# Change Log\n\nThis project adheres to [Semantic Versioning](http://semver.org/). \nEvery release, along with the migrati"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 2686,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "LICENSE",
"chars": 1089,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2015-present Mihail Diordiev\n\nPermission is hereby granted, free of charge, to any "
},
{
"path": "README.md",
"chars": 22394,
"preview": "⚠️⚠️⚠️🚨🚨🚨⚠️⚠️⚠️\n## This repo is no longer the home of the redux-devtools-extension. The new home is https://github.com/r"
},
{
"path": "appveyor.yml",
"chars": 254,
"preview": "environment:\n matrix:\n - nodejs_version: '6'\n\ncache:\n - \"%LOCALAPPDATA%/Yarn\"\n - node_modules\n\ninstall:\n - ps: In"
},
{
"path": "book.json",
"chars": 467,
"preview": "{\n \"gitbook\": \"3.2.2\",\n \"title\": \"Redux DevTools Extension\",\n \"plugins\": [\"edit-link\", \"prism\", \"-highlight\", \"github"
},
{
"path": "docs/API/Arguments.md",
"chars": 12284,
"preview": "# Options\n\nUse with\n - `window.__REDUX_DEVTOOLS_EXTENSION__([options])`\n - `window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__("
},
{
"path": "docs/API/Methods.md",
"chars": 3849,
"preview": "## Communicate with the extension directly\n\n> Note this is advanced API, which you usually don't need to use with Redux "
},
{
"path": "docs/API/README.md",
"chars": 70,
"preview": "# API Reference\n\n- [Parameters](Arguments.md)\n- [Methods](Methods.md)\n"
},
{
"path": "docs/Articles.md",
"chars": 466,
"preview": "# Articles\n\n- [Improve your development workflow with Redux DevTools Extension](https://medium.com/@zalmoxis/improve-you"
},
{
"path": "docs/Credits.md",
"chars": 794,
"preview": "# Credits\n\n - Built using [Crossbuilder](https://github.com/zalmoxisus/crossbuilder) boilerplate.\n - Includes [Dan Abram"
},
{
"path": "docs/FAQ.md",
"chars": 2949,
"preview": "# Redux DevTools Extension FAQ\n\n## Table of Contents\n- [How to get it work](#how-to-get-it-work)\n- [How to disable/enabl"
},
{
"path": "docs/Features/Trace.md",
"chars": 2435,
"preview": "## Trace actions calls\n\n or [submit a PR](https"
},
{
"path": "docs/Integrations.md",
"chars": 7158,
"preview": "# Integrations for js and non-js frameworks\n\nMostly functional:\n- [React](#react)\n- [Angular](#angular)\n- [Cycle](#cycle"
},
{
"path": "docs/README.md",
"chars": 722,
"preview": "# Documentation\n\n* [Extension](/README.md)\n * [Installation](/README.md#installation)\n * [Usage](/README.md#usage)\n *"
},
{
"path": "docs/Recipes.md",
"chars": 2086,
"preview": "# Recipes\n\n### Using in a typescript project\n\nThe recommended way is to use [`redux-devtools-extension` npm package](/RE"
},
{
"path": "docs/Troubleshooting.md",
"chars": 6246,
"preview": "# Troubleshooting\n\n### I just see empty log or \"No store found\"\n\nMake sure you [applied the enhancer](https://github.com"
},
{
"path": "docs/Videos.md",
"chars": 397,
"preview": "# Videos\n\n- [Debugging flux applications in production at React Europe 2016](https://youtu.be/YU8jQ2HtqH4)\n- [Hot Reload"
},
{
"path": "examples/buildAll.js",
"chars": 1015,
"preview": "/**\n * Runs an ordered set of commands within each of the build directories.\n */\n\nimport fs from 'fs';\nimport path from "
},
{
"path": "examples/counter/.babelrc",
"chars": 50,
"preview": "{\n \"presets\": [ \"es2015\", \"stage-0\", \"react\" ]\n}\n"
},
{
"path": "examples/counter/actions/counter.js",
"chars": 640,
"preview": "export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';\nexport const DECREMENT_COUNTER = 'DECREMENT_COUNTER';\n\nlet t;\n\nexp"
},
{
"path": "examples/counter/components/Counter.js",
"chars": 834,
"preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\n\nclass Counter extends Component {\n rende"
},
{
"path": "examples/counter/containers/App.js",
"chars": 431,
"preview": "import { bindActionCreators } from 'redux';\nimport { connect } from 'react-redux';\nimport Counter from '../components/Co"
},
{
"path": "examples/counter/index.html",
"chars": 187,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>Redux counter example</title>\n </head>\n <body>\n <div id=\"root\">\n </di"
},
{
"path": "examples/counter/index.js",
"chars": 325,
"preview": "import React from 'react';\nimport { render } from 'react-dom';\nimport { Provider } from 'react-redux';\nimport App from '"
},
{
"path": "examples/counter/package.json",
"chars": 1156,
"preview": "{\n \"name\": \"redux-counter-example\",\n \"version\": \"0.0.0\",\n \"description\": \"Redux counter example\",\n \"scripts\": {\n "
},
{
"path": "examples/counter/reducers/counter.js",
"chars": 285,
"preview": "import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../actions/counter';\n\nexport default function counter(state = 0, a"
},
{
"path": "examples/counter/reducers/index.js",
"chars": 156,
"preview": "import { combineReducers } from 'redux';\nimport counter from './counter';\n\nconst rootReducer = combineReducers({\n count"
},
{
"path": "examples/counter/server.js",
"chars": 711,
"preview": "var webpack = require('webpack');\nvar webpackDevMiddleware = require('webpack-dev-middleware');\nvar webpackHotMiddleware"
},
{
"path": "examples/counter/store/configureStore.js",
"chars": 793,
"preview": "import { createStore, applyMiddleware, compose } from 'redux';\nimport { composeWithDevTools } from 'redux-devtools-exten"
},
{
"path": "examples/counter/test/actions/counter.spec.js",
"chars": 2270,
"preview": "import expect from 'expect';\nimport { applyMiddleware } from 'redux';\nimport thunk from 'redux-thunk';\nimport * as actio"
},
{
"path": "examples/counter/test/components/Counter.spec.js",
"chars": 1603,
"preview": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport Counter "
},
{
"path": "examples/counter/test/containers/App.spec.js",
"chars": 1697,
"preview": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport { Provid"
},
{
"path": "examples/counter/test/reducers/counter.spec.js",
"chars": 683,
"preview": "import expect from 'expect';\nimport counter from '../../reducers/counter';\nimport { INCREMENT_COUNTER, DECREMENT_COUNTER"
},
{
"path": "examples/counter/test/setup.js",
"chars": 184,
"preview": "import { jsdom } from 'jsdom';\n\nglobal.document = jsdom('<!doctype html><html><body></body></html>');\nglobal.window = do"
},
{
"path": "examples/counter/webpack.config.js",
"chars": 495,
"preview": "var path = require('path');\nvar webpack = require('webpack');\n\nmodule.exports = {\n mode: 'development',\n devtool: 'sou"
},
{
"path": "examples/react-counter-messaging/.babelrc",
"chars": 50,
"preview": "{\n \"presets\": [ \"es2015\", \"stage-0\", \"react\" ]\n}\n"
},
{
"path": "examples/react-counter-messaging/components/Counter.js",
"chars": 1666,
"preview": "import React, { Component } from 'react';\n\nconst withDevTools = (\n // process.env.NODE_ENV === 'development' &&\n typeo"
},
{
"path": "examples/react-counter-messaging/index.html",
"chars": 187,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>React counter example</title>\n </head>\n <body>\n <div id=\"root\">\n </di"
},
{
"path": "examples/react-counter-messaging/index.js",
"chars": 168,
"preview": "import React from 'react';\nimport { render } from 'react-dom';\nimport Counter from './components/Counter';\n\nrender(\n <C"
},
{
"path": "examples/react-counter-messaging/package.json",
"chars": 897,
"preview": "{\n \"name\": \"react-counter-example\",\n \"version\": \"0.0.0\",\n \"description\": \"React counter example\",\n \"scripts\": {\n "
},
{
"path": "examples/react-counter-messaging/webpack.config.js",
"chars": 430,
"preview": "var path = require('path');\nvar webpack = require('webpack');\n\nmodule.exports = {\n mode: 'development',\n devtool: 'sou"
},
{
"path": "examples/router/.babelrc",
"chars": 119,
"preview": "{\n \"presets\": [ \"es2015\", \"stage-0\", \"react\" ],\n \"plugins\": [ \"add-module-exports\", \"transform-decorators-legacy\" ]\n}"
},
{
"path": "examples/router/actions/todos.js",
"chars": 527,
"preview": "import * as types from '../constants/ActionTypes';\n\nexport function addTodo(text) {\n return { type: types.ADD_TODO, tex"
},
{
"path": "examples/router/components/Footer.js",
"chars": 1830,
"preview": "import React, { PropTypes, Component } from 'react';\nimport classnames from 'classnames';\nimport { SHOW_ALL, SHOW_COMPLE"
},
{
"path": "examples/router/components/Header.js",
"chars": 643,
"preview": "import React, { PropTypes, Component } from 'react';\nimport TodoTextInput from './TodoTextInput';\n\nclass Header extends "
},
{
"path": "examples/router/components/MainSection.js",
"chars": 2248,
"preview": "import React, { Component, PropTypes } from 'react';\nimport TodoItem from './TodoItem';\nimport Footer from './Footer';\ni"
},
{
"path": "examples/router/components/TodoItem.js",
"chars": 1702,
"preview": "import React, { Component, PropTypes } from 'react';\nimport classnames from 'classnames';\nimport TodoTextInput from './T"
},
{
"path": "examples/router/components/TodoTextInput.js",
"chars": 1283,
"preview": "import React, { Component, PropTypes } from 'react';\nimport classnames from 'classnames';\n\nclass TodoTextInput extends C"
},
{
"path": "examples/router/constants/ActionTypes.js",
"chars": 256,
"preview": "export const ADD_TODO = 'ADD_TODO';\nexport const DELETE_TODO = 'DELETE_TODO';\nexport const EDIT_TODO = 'EDIT_TODO';\nexpo"
},
{
"path": "examples/router/constants/TodoFilters.js",
"chars": 126,
"preview": "export const SHOW_ALL = 'show_all';\nexport const SHOW_COMPLETED = 'show_completed';\nexport const SHOW_ACTIVE = 'show_act"
},
{
"path": "examples/router/containers/App.js",
"chars": 941,
"preview": "import React, { Component, PropTypes } from 'react';\nimport { bindActionCreators } from 'redux';\nimport { connect } from"
},
{
"path": "examples/router/containers/Root.js",
"chars": 534,
"preview": "import React, { Component, PropTypes } from 'react';\nimport { Provider } from 'react-redux';\nimport { Route, Redirect } "
},
{
"path": "examples/router/containers/Wrapper.js",
"chars": 1465,
"preview": "import React, { Component, PropTypes } from 'react';\nimport { bindActionCreators } from 'redux';\nimport { connect } from"
},
{
"path": "examples/router/index.html",
"chars": 203,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>Redux TodoMVC example</title>\n </head>\n <body>\n <div class=\"todoapp\" id="
},
{
"path": "examples/router/index.js",
"chars": 389,
"preview": "import 'babel-polyfill';\nimport React from 'react';\nimport { render } from 'react-dom';\nimport { Provider } from 'react-"
},
{
"path": "examples/router/package.json",
"chars": 1573,
"preview": "{\n \"name\": \"redux-todomvc-example\",\n \"version\": \"0.0.0\",\n \"description\": \"Redux TodoMVC example\",\n \"scripts\": {\n "
},
{
"path": "examples/router/reducers/index.js",
"chars": 231,
"preview": "import { combineReducers } from 'redux';\nimport { routerStateReducer } from 'redux-router';\nimport todos from './todos';"
},
{
"path": "examples/router/reducers/todos.js",
"chars": 1171,
"preview": "import { ADD_TODO, DELETE_TODO, EDIT_TODO, COMPLETE_TODO, COMPLETE_ALL, CLEAR_COMPLETED } from '../constants/ActionTypes"
},
{
"path": "examples/router/server.js",
"chars": 711,
"preview": "var webpack = require('webpack');\nvar webpackDevMiddleware = require('webpack-dev-middleware');\nvar webpackHotMiddleware"
},
{
"path": "examples/router/store/configureStore.js",
"chars": 814,
"preview": "import { createStore, compose } from 'redux';\nimport { reduxReactRouter, routerStateReducer, ReduxRouter } from 'redux-r"
},
{
"path": "examples/router/test/actions/todos.spec.js",
"chars": 1213,
"preview": "import expect from 'expect';\nimport * as types from '../../constants/ActionTypes';\nimport * as actions from '../../actio"
},
{
"path": "examples/router/test/components/Footer.spec.js",
"chars": 3381,
"preview": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport Footer f"
},
{
"path": "examples/router/test/components/Header.spec.js",
"chars": 1391,
"preview": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport Header f"
},
{
"path": "examples/router/test/components/MainSection.spec.js",
"chars": 4431,
"preview": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport MainSect"
},
{
"path": "examples/router/test/components/TodoItem.spec.js",
"chars": 3585,
"preview": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport TodoItem"
},
{
"path": "examples/router/test/components/TodoTextInput.spec.js",
"chars": 2633,
"preview": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport TodoText"
},
{
"path": "examples/router/test/reducers/todos.spec.js",
"chars": 4594,
"preview": "import expect from 'expect';\nimport todos from '../../reducers/todos';\nimport * as types from '../../constants/ActionTyp"
},
{
"path": "examples/router/test/setup.js",
"chars": 184,
"preview": "import { jsdom } from 'jsdom';\n\nglobal.document = jsdom('<!doctype html><html><body></body></html>');\nglobal.window = do"
},
{
"path": "examples/router/webpack.config.js",
"chars": 684,
"preview": "var path = require('path');\nvar webpack = require('webpack');\n\nmodule.exports = {\n devtool: 'cheap-module-eval-source-m"
},
{
"path": "examples/saga-counter/.babelrc",
"chars": 50,
"preview": "{\n \"presets\": [ \"es2015\", \"stage-0\", \"react\" ]\n}\n"
},
{
"path": "examples/saga-counter/index.html",
"chars": 313,
"preview": "<!doctype html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initia"
},
{
"path": "examples/saga-counter/package.json",
"chars": 934,
"preview": "{\n \"name\": \"redux-counter-example\",\n \"version\": \"0.0.0\",\n \"description\": \"Redux counter example\",\n \"scripts\": {\n "
},
{
"path": "examples/saga-counter/src/components/Counter.js",
"chars": 842,
"preview": "import React from 'react'\nimport PropTypes from 'prop-types'\n\nconst Counter = ({ value, onIncrement, onIncrementAsync, o"
},
{
"path": "examples/saga-counter/src/main.js",
"chars": 1112,
"preview": "import \"babel-polyfill\"\n\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport { createStore, applyMiddlewar"
},
{
"path": "examples/saga-counter/src/reducers/index.js",
"chars": 286,
"preview": "export default function counter(state = 0, action) {\n switch (action.type) {\n case 'INCREMENT':\n return state +"
},
{
"path": "examples/saga-counter/src/sagas/index.js",
"chars": 358,
"preview": "/* eslint-disable no-constant-condition */\n\nimport { takeEvery } from 'redux-saga'\nimport { put, call } from 'redux-saga"
},
{
"path": "examples/saga-counter/webpack.config.js",
"chars": 456,
"preview": "var path = require('path');\nvar webpack = require('webpack');\n\nmodule.exports = {\n mode: 'development',\n devtool: 'sou"
},
{
"path": "examples/todomvc/.babelrc",
"chars": 49,
"preview": "{\n \"presets\": [ \"es2015\", \"stage-0\", \"react\" ]\n}"
},
{
"path": "examples/todomvc/actions/index.js",
"chars": 25,
"preview": "export * from './todos';\n"
},
{
"path": "examples/todomvc/actions/todos.js",
"chars": 527,
"preview": "import * as types from '../constants/ActionTypes';\n\nexport function addTodo(text) {\n return { type: types.ADD_TODO, tex"
},
{
"path": "examples/todomvc/components/Footer.js",
"chars": 1855,
"preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport classnames from 'classnames';\nimpor"
},
{
"path": "examples/todomvc/components/Header.js",
"chars": 609,
"preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport TodoTextInput from './TodoTextInput"
},
{
"path": "examples/todomvc/components/MainSection.js",
"chars": 2273,
"preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport TodoItem from './TodoItem';\nimport "
},
{
"path": "examples/todomvc/components/TodoItem.js",
"chars": 1727,
"preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport classnames from 'classnames';\nimpor"
},
{
"path": "examples/todomvc/components/TodoTextInput.js",
"chars": 1308,
"preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport classnames from 'classnames';\n\nclas"
},
{
"path": "examples/todomvc/constants/ActionTypes.js",
"chars": 256,
"preview": "export const ADD_TODO = 'ADD_TODO';\nexport const DELETE_TODO = 'DELETE_TODO';\nexport const EDIT_TODO = 'EDIT_TODO';\nexpo"
},
{
"path": "examples/todomvc/constants/TodoFilters.js",
"chars": 126,
"preview": "export const SHOW_ALL = 'show_all';\nexport const SHOW_COMPLETED = 'show_completed';\nexport const SHOW_ACTIVE = 'show_act"
},
{
"path": "examples/todomvc/containers/App.js",
"chars": 906,
"preview": "import React, { Component } from 'react';\nimport PropTypes from 'prop-types';\nimport { bindActionCreators } from 'redux'"
},
{
"path": "examples/todomvc/index.html",
"chars": 203,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>Redux TodoMVC example</title>\n </head>\n <body>\n <div class=\"todoapp\" id="
},
{
"path": "examples/todomvc/index.js",
"chars": 386,
"preview": "import 'babel-polyfill';\nimport React from 'react';\nimport { render } from 'react-dom';\nimport { Provider } from 'react-"
},
{
"path": "examples/todomvc/package.json",
"chars": 1155,
"preview": "{\n \"name\": \"redux-todomvc-example\",\n \"version\": \"0.0.0\",\n \"description\": \"Redux TodoMVC example\",\n \"scripts\": {\n "
},
{
"path": "examples/todomvc/reducers/index.js",
"chars": 150,
"preview": "import { combineReducers } from 'redux';\nimport todos from './todos';\n\nconst rootReducer = combineReducers({\n todos\n});"
},
{
"path": "examples/todomvc/reducers/todos.js",
"chars": 1289,
"preview": "import { ADD_TODO, DELETE_TODO, EDIT_TODO, COMPLETE_TODO, COMPLETE_ALL, CLEAR_COMPLETED } from '../constants/ActionTypes"
},
{
"path": "examples/todomvc/server.js",
"chars": 711,
"preview": "var webpack = require('webpack');\nvar webpackDevMiddleware = require('webpack-dev-middleware');\nvar webpackHotMiddleware"
},
{
"path": "examples/todomvc/store/configureStore.js",
"chars": 793,
"preview": "import { createStore } from 'redux';\nimport rootReducer from '../reducers';\nimport * as actionCreators from '../actions'"
},
{
"path": "examples/todomvc/test/actions/todos.spec.js",
"chars": 1213,
"preview": "import expect from 'expect';\nimport * as types from '../../constants/ActionTypes';\nimport * as actions from '../../actio"
},
{
"path": "examples/todomvc/test/components/Footer.spec.js",
"chars": 3381,
"preview": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport Footer f"
},
{
"path": "examples/todomvc/test/components/Header.spec.js",
"chars": 1391,
"preview": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport Header f"
},
{
"path": "examples/todomvc/test/components/MainSection.spec.js",
"chars": 4431,
"preview": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport MainSect"
},
{
"path": "examples/todomvc/test/components/TodoItem.spec.js",
"chars": 3585,
"preview": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport TodoItem"
},
{
"path": "examples/todomvc/test/components/TodoTextInput.spec.js",
"chars": 2633,
"preview": "import expect from 'expect';\nimport React from 'react';\nimport TestUtils from 'react-addons-test-utils';\nimport TodoText"
},
{
"path": "examples/todomvc/test/reducers/todos.spec.js",
"chars": 4594,
"preview": "import expect from 'expect';\nimport todos from '../../reducers/todos';\nimport * as types from '../../constants/ActionTyp"
},
{
"path": "examples/todomvc/test/setup.js",
"chars": 184,
"preview": "import { jsdom } from 'jsdom';\n\nglobal.document = jsdom('<!doctype html><html><body></body></html>');\nglobal.window = do"
},
{
"path": "examples/todomvc/webpack.config.js",
"chars": 599,
"preview": "var path = require('path');\nvar webpack = require('webpack');\n\nmodule.exports = {\n mode: 'development',\n devtool: 'sou"
},
{
"path": "gulpfile.babel.js",
"chars": 4230,
"preview": "import fs from 'fs';\nimport gulp from 'gulp';\nimport gutil from 'gulp-util';\nimport jade from 'gulp-pug';\nimport rename "
},
{
"path": "npm-package/README.md",
"chars": 1544,
"preview": "# Redux DevTools Extension's helper\n\n[.compose;\n\nexports.__esModule = true;\nexports.composeWithDevTools = (\n pro"
},
{
"path": "npm-package/index.d.ts",
"chars": 6491,
"preview": "import {Action, ActionCreator, StoreEnhancer, compose} from \"redux\";\n\nexport interface EnhancerOptions {\n /**\n * the "
},
{
"path": "npm-package/index.js",
"chars": 635,
"preview": "\"use strict\";\n\nvar compose = require('redux').compose;\n\nexports.__esModule = true;\nexports.composeWithDevTools = (\n typ"
},
{
"path": "npm-package/logOnly.d.ts",
"chars": 42,
"preview": "export * from \"redux-devtools-extension\";\n"
},
{
"path": "npm-package/logOnly.js",
"chars": 1782,
"preview": "\"use strict\";\n\nvar assign = require('./utils/assign');\nvar compose = require('redux').compose;\n\nfunction enhancer() {\n "
},
{
"path": "npm-package/logOnlyInProduction.d.ts",
"chars": 42,
"preview": "export * from \"redux-devtools-extension\";\n"
},
{
"path": "npm-package/logOnlyInProduction.js",
"chars": 840,
"preview": "\"use strict\";\n\nvar compose = require('redux').compose;\nvar logOnly = require('./logOnly');\n\nexports.__esModule = true;\ne"
},
{
"path": "npm-package/package.json",
"chars": 494,
"preview": "{\n \"name\": \"redux-devtools-extension\",\n \"version\": \"2.13.8\",\n \"description\": \"Wrappers for Redux DevTools Extension.\""
},
{
"path": "npm-package/utils/assign.js",
"chars": 446,
"preview": "var objectKeys = Object.keys || function (obj) {\n var keys = [];\n for (var key in obj) {\n if ({}.hasOwnProper"
},
{
"path": "package.json",
"chars": 3739,
"preview": "{\n \"version\": \"2.17.1\",\n \"name\": \"remotedev-redux-devtools-extension\",\n \"description\": \"Redux Developer Tools for deb"
},
{
"path": "src/app/api/filters.js",
"chars": 4981,
"preview": "import mapValues from 'lodash/mapValues';\n\nexport const FilterState = {\n DO_NOT_FILTER: 'DO_NOT_FILTER',\n BLACKLIST_SP"
},
{
"path": "src/app/api/generateInstanceId.js",
"chars": 93,
"preview": "let id = 0;\n\nexport default function generateId(instanceId) {\n return instanceId || ++id;\n}\n"
},
{
"path": "src/app/api/importState.js",
"chars": 1984,
"preview": "import mapValues from 'lodash/mapValues';\nimport jsan from 'jsan';\nimport seralizeImmutable from 'remotedev-serialize/im"
},
{
"path": "src/app/api/index.js",
"chars": 11222,
"preview": "import jsan from 'jsan';\nimport throttle from 'lodash/throttle';\nimport seralizeImmutable from 'remotedev-serialize/immu"
},
{
"path": "src/app/api/notifyErrors.js",
"chars": 944,
"preview": "let handleError;\nlet lastTime = 0;\n\nfunction createExpBackoffTimer(step) {\n let count = 1;\n return function(reset) {\n "
},
{
"path": "src/app/api/openWindow.js",
"chars": 165,
"preview": "export default function openWindow(position) {\n window.postMessage({\n source: '@devtools-page',\n type: 'OPEN',\n "
},
{
"path": "src/app/containers/App.js",
"chars": 6864,
"preview": "import React, { Component, PropTypes } from 'react';\nimport { bindActionCreators } from 'redux';\nimport { connect } from"
},
{
"path": "src/app/middlewares/api.js",
"chars": 7659,
"preview": "import stringifyJSON from 'remotedev-app/lib/utils/stringifyJSON';\nimport { UPDATE_STATE, REMOVE_INSTANCE, LIFTED_ACTION"
},
{
"path": "src/app/middlewares/instanceSelector.js",
"chars": 1006,
"preview": "import { SELECT_INSTANCE, UPDATE_STATE } from 'remotedev-app/lib/constants/actionTypes';\n\nfunction selectInstance(tabId,"
},
{
"path": "src/app/middlewares/panelSync.js",
"chars": 1057,
"preview": "import { LIFTED_ACTION, UPDATE_STATE, SELECT_INSTANCE } from 'remotedev-app/lib/constants/actionTypes';\nimport { getActi"
},
{
"path": "src/app/middlewares/windowSync.js",
"chars": 706,
"preview": "import { UPDATE_STATE, LIFTED_ACTION } from 'remotedev-app/lib/constants/actionTypes';\nimport { getActiveInstance } from"
},
{
"path": "src/app/reducers/background/index.js",
"chars": 249,
"preview": "import { combineReducers } from 'redux';\nimport instances from 'remotedev-app/lib/reducers/instances';\nimport persistSta"
},
{
"path": "src/app/reducers/background/persistStates.js",
"chars": 136,
"preview": "export default function persistStates(state = false, action) {\n if (action.type === 'TOGGLE_PERSIST') return !state;\n "
},
{
"path": "src/app/reducers/panel/index.js",
"chars": 469,
"preview": "import { combineReducers } from 'redux';\nimport instances from 'remotedev-app/lib/reducers/instances';\nimport monitor fr"
},
{
"path": "src/app/reducers/window/index.js",
"chars": 510,
"preview": "import { combineReducers } from 'redux';\nimport instances from './instances';\nimport monitor from 'remotedev-app/lib/red"
},
{
"path": "src/app/reducers/window/instances.js",
"chars": 610,
"preview": "import { initialState, dispatchAction } from 'remotedev-app/lib/reducers/instances';\nimport { UPDATE_STATE, SELECT_INSTA"
},
{
"path": "src/app/service/Monitor.js",
"chars": 1443,
"preview": "export default class Monitor {\n constructor(update) {\n this.update = update;\n }\n reducer = (state = {}, action) =>"
},
{
"path": "src/app/stores/backgroundStore.js",
"chars": 549,
"preview": "import { createStore, applyMiddleware } from 'redux';\nimport rootReducer from '../reducers/background';\nimport api from "
},
{
"path": "src/app/stores/createStore.js",
"chars": 169,
"preview": "import { createStore } from 'redux';\n\nexport default function configureStore(reducer, initialState, enhance) {\n return "
},
{
"path": "src/app/stores/enhancerStore.js",
"chars": 1016,
"preview": "import { compose } from 'redux';\nimport instrument from 'redux-devtools-instrument';\nimport persistState from 'redux-dev"
},
{
"path": "src/app/stores/panelStore.js",
"chars": 529,
"preview": "import { createStore, applyMiddleware } from 'redux';\nimport persist from 'remotedev-app/lib/middlewares/persist';\nimpor"
},
{
"path": "src/app/stores/windowStore.js",
"chars": 1489,
"preview": "import { createStore, compose, applyMiddleware } from 'redux';\nimport persist from 'remotedev-app/lib/middlewares/persis"
},
{
"path": "src/browser/extension/background/contextMenus.js",
"chars": 935,
"preview": "import openDevToolsWindow from './openWindow';\n\nexport function createMenu() {\n const menus = [\n { id: 'devtools-lef"
},
{
"path": "src/browser/extension/background/getPreloadedState.js",
"chars": 773,
"preview": "const getIfExists = (sel, template) => (\n typeof sel === 'undefined' ||\n typeof template === 'undefined' ||\n typeof t"
},
{
"path": "src/browser/extension/background/index.js",
"chars": 892,
"preview": "import configureStore from '../../../app/stores/backgroundStore';\nimport openDevToolsWindow from './openWindow';\nimport "
},
{
"path": "src/browser/extension/background/logging.js",
"chars": 1064,
"preview": "import { LIFTED_ACTION } from 'remotedev-app/lib/constants/actionTypes';\n\nexport function getReport(reportId, tabId, ins"
},
{
"path": "src/browser/extension/background/openWindow.js",
"chars": 1779,
"preview": "let windows = {};\nlet lastPosition = null;\n\nexport default function openDevToolsWindow(position) {\n function popWindow("
},
{
"path": "src/browser/extension/chromeAPIMock.js",
"chars": 2256,
"preview": "// Mock not supported chrome.* API for Firefox and Electron\n\nwindow.isElectron = window.navigator &&\n window.navigator."
},
{
"path": "src/browser/extension/devpanel/index.js",
"chars": 2684,
"preview": "import 'remotedev-monitor-components/lib/presets';\nimport React from 'react';\nimport { render, unmountComponentAtNode } "
},
{
"path": "src/browser/extension/devtools/index.js",
"chars": 405,
"preview": "function createPanel(url) {\n chrome.devtools.panels.create(\n 'Redux', 'img/logo/scalable.png', url, function() {}\n "
},
{
"path": "src/browser/extension/inject/contentScript.js",
"chars": 3315,
"preview": "import { injectOptions, getOptionsFromBg, isAllowed } from '../options/syncOptions';\nconst source = '@devtools-extension"
},
{
"path": "src/browser/extension/inject/deprecatedWarn.js",
"chars": 310,
"preview": "// Deprecated warning for inject.bundle.js\n/* eslint-disable no-console */\nconsole.warn(\n 'Using Redux DevTools inside "
},
{
"path": "src/browser/extension/inject/index.js",
"chars": 681,
"preview": "// Include this script in Chrome apps and extensions for remote debugging\n// <script src=\"chrome-extension://lmhkpmbekcp"
},
{
"path": "src/browser/extension/inject/pageScript.js",
"chars": 13859,
"preview": "import { getActionsArray, evalAction } from 'remotedev-utils';\nimport throttle from 'lodash/throttle';\nimport createStor"
},
{
"path": "src/browser/extension/inject/pageScriptWrap.js",
"chars": 517,
"preview": "let s = document.createElement('script');\ns.type = 'text/javascript';\n\nif (process.env.NODE_ENV === 'production') {\n co"
},
{
"path": "src/browser/extension/manifest.json",
"chars": 2280,
"preview": "{\n \"version\": \"2.17.1\",\n \"name\": \"Redux DevTools\",\n \"short_name\": \"Redux DevTools\",\n \"description\": \"Redux DevTools "
},
{
"path": "src/browser/extension/options/AllowToRunGroup.js",
"chars": 1533,
"preview": "import React from 'react';\n\nexport default ({ options, saveOption }) => {\n const AllowToRunState = {\n EVERYWHERE: tr"
},
{
"path": "src/browser/extension/options/ContextMenuGroup.js",
"chars": 733,
"preview": "import React from 'react';\n\nexport default ({ options, saveOption }) => {\n return (\n <fieldset className=\"option-gro"
},
{
"path": "src/browser/extension/options/EditorGroup.js",
"chars": 2471,
"preview": "import React from 'react';\n\nexport default ({ options, saveOption }) => {\n const EditorState = {\n BROWSER: 0,\n EX"
},
{
"path": "src/browser/extension/options/FilterGroup.js",
"chars": 2292,
"preview": "import React from 'react';\nimport { FilterState } from '../../../app/api/filters';\n\nexport default ({ options, saveOptio"
},
{
"path": "src/browser/extension/options/MiscellaneousGroup.js",
"chars": 1635,
"preview": "import React from 'react';\n\nexport default ({ options, saveOption }) => {\n const browserName = navigator.userAgent.incl"
},
{
"path": "src/browser/extension/options/Options.js",
"chars": 957,
"preview": "import React from 'react';\nimport EditorGroup from './EditorGroup';\nimport FilterGroup from './FilterGroup';\nimport Allo"
},
{
"path": "src/browser/extension/options/index.js",
"chars": 544,
"preview": "import React from 'react';\nimport { render } from 'react-dom';\nimport Options from './Options';\n\nchrome.runtime.getBackg"
},
{
"path": "src/browser/extension/options/syncOptions.js",
"chars": 2739,
"preview": "import { FilterState } from '../../../app/api/filters';\n\nlet options;\nlet subscribers = [];\n\nconst save = (toAllTabs) =>"
},
{
"path": "src/browser/extension/window/index.js",
"chars": 1171,
"preview": "import 'remotedev-monitor-components/lib/presets';\nimport React from 'react';\nimport { render } from 'react-dom';\nimport"
},
{
"path": "src/browser/extension/window/remote.js",
"chars": 803,
"preview": "import React from 'react';\nimport { render } from 'react-dom';\nimport App from 'remotedev-app';\n\nchrome.storage.local.ge"
},
{
"path": "src/browser/firefox/manifest.json",
"chars": 1599,
"preview": "{\n \"version\": \"2.17.1\",\n \"name\": \"Redux DevTools\",\n \"manifest_version\": 2,\n \"description\": \"Redux Developer Tools fo"
},
{
"path": "src/browser/views/devpanel.pug",
"chars": 216,
"preview": "doctype html\n\nhtml\n head\n meta(charset='UTF-8')\n title Redux DevTools\n include ./includes/style.pug\n style.\n b"
},
{
"path": "src/browser/views/devtools.pug",
"chars": 151,
"preview": "doctype html\n\nhtml\n head\n meta(charset='UTF-8')\n title Redux DevTools\n\n body\n #root\n scrip"
},
{
"path": "src/browser/views/includes/style.pug",
"chars": 1090,
"preview": "style.\n html {\n height: 100%;\n width: 100%;\n }\n body {\n overflow: hidden;\n height: 100%;\n width: 100%;"
},
{
"path": "src/browser/views/options.pug",
"chars": 1911,
"preview": "doctype html\n\nhtml\n head\n meta(charset='UTF-8')\n title Redux DevTools Options\n style.\n body {\n padding: 2p"
},
{
"path": "src/browser/views/remote.pug",
"chars": 157,
"preview": "doctype html\n\nhtml\n head\n meta(charset='UTF-8')\n title RemoteDev\n include ./includes/style.pug\n\n body\n #root\n "
},
{
"path": "src/browser/views/window.pug",
"chars": 391,
"preview": "doctype html\n\nhtml\n head\n meta(charset='UTF-8')\n title Redux DevTools\n include ./includes/style.pug\n\n body\n #r"
},
{
"path": "test/.eslintrc",
"chars": 121,
"preview": "{\n \"env\": {\n \"mocha\": true\n },\n \"globals\": {\n \"UI\": true\n },\n \"rules\": {\n \"no-unused-expressions\": 0\n }\n}"
},
{
"path": "test/app/containers/App.spec.js",
"chars": 726,
"preview": "import expect from 'expect';\nimport React from 'react';\nimport { mount } from 'enzyme';\nimport { Provider } from 'react-"
},
{
"path": "test/app/inject/api.spec.js",
"chars": 2581,
"preview": "import expect from 'expect';\nimport { insertScript, listenMessage } from '../../utils/inject';\nimport '../../../src/brow"
},
{
"path": "test/app/inject/enhancer.spec.js",
"chars": 6162,
"preview": "import 'babel-polyfill';\nimport expect from 'expect';\nimport { createStore, compose } from 'redux';\nimport { insertScrip"
},
{
"path": "test/app/setup.js",
"chars": 728,
"preview": "require('babel-register')();\nrequire('babel-polyfill');\nglobal.chrome = require('sinon-chrome');\nvar jsdom = require('js"
},
{
"path": "test/chrome/extension.spec.js",
"chars": 2604,
"preview": "import { resolve } from 'path';\nimport webdriver from 'selenium-webdriver';\nimport expect from 'expect';\nimport { switch"
},
{
"path": "test/electron/devpanel.spec.js",
"chars": 3282,
"preview": "import { join } from 'path';\nimport webdriver from 'selenium-webdriver';\nimport electronPath from 'electron';\nimport exp"
},
{
"path": "test/electron/fixture/index.html",
"chars": 277,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n <title>Electron Test</title>\n </head>\n <body>\n <span"
},
{
"path": "test/electron/fixture/main.js",
"chars": 485,
"preview": "const path = require('path');\nconst { app, BrowserWindow } = require('electron');\n\napp.setPath('userData', path.join(__d"
},
{
"path": "test/electron/fixture/package.json",
"chars": 107,
"preview": "{\n \"name\": \"electron-test\",\n \"productName\": \"Electron Test\",\n \"main\": \"main.js\",\n \"version\": \"0.1.0\"\n}\n"
},
{
"path": "test/electron/fixture/renderer.js",
"chars": 930,
"preview": "const { createStore } = require('redux');\n\nconst INCREMENT_COUNTER = 'INCREMENT_COUNTER';\nconst DECREMENT_COUNTER = 'DEC"
},
{
"path": "test/perf/data.js",
"chars": 132567,
"preview": "// Source: http://beta.json-generator.com/V1omRaUJG\n/* eslint-disable */\n\nexport const bigString = Array(10000000).join("
},
{
"path": "test/perf/send.spec.js",
"chars": 726,
"preview": "import 'babel-polyfill';\nimport expect from 'expect';\nimport { bigArray, bigString, circularData } from './data';\nimport"
},
{
"path": "test/utils/e2e.js",
"chars": 1446,
"preview": "import webdriver from 'selenium-webdriver';\n\nexport const delay = time => new Promise(resolve => setTimeout(resolve, tim"
},
{
"path": "test/utils/inject.js",
"chars": 496,
"preview": "export function insertScript(str) {\n const s = window.document.createElement('script');\n s.appendChild(document.create"
},
{
"path": "webpack/base.config.js",
"chars": 2033,
"preview": "import path from 'path';\nimport webpack from 'webpack';\nimport TerserPlugin from 'terser-webpack-plugin';\n\nconst extpath"
},
{
"path": "webpack/dev.config.js",
"chars": 457,
"preview": "import path from 'path';\nimport webpack from 'webpack';\nimport baseConfig from './base.config';\n\nlet config = baseConfig"
},
{
"path": "webpack/prod.config.js",
"chars": 240,
"preview": "import path from 'path';\nimport baseConfig from './base.config';\n\nexport default baseConfig({\n output: { path: path.joi"
},
{
"path": "webpack/replace/JsonpMainTemplate.runtime.js",
"chars": 2030,
"preview": "/*\n\tMIT License http://www.opensource.org/licenses/mit-license.php\n\tAuthor Tobias Koppers @sokra\n*/\n/*globals hotAddUpda"
},
{
"path": "webpack/replace/log-apply-result.js",
"chars": 1016,
"preview": "/*\n\tMIT License http://www.opensource.org/licenses/mit-license.php\n\tAuthor Tobias Koppers @sokra\n*/\nmodule.exports = fun"
},
{
"path": "webpack/wrap.config.js",
"chars": 322,
"preview": "import path from 'path';\nimport baseConfig from './base.config';\n\nexport default baseConfig({\n input: { page: [ path.jo"
}
]
About this extraction
This page contains the full source code of the zalmoxisus/redux-devtools-extension GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 199 files (431.6 KB), approximately 125.0k tokens, and a symbol index with 249 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.