diff --git a/.babelrc b/.babelrc deleted file mode 100644 index b65902f..0000000 --- a/.babelrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "presets": ["es2015", "react", "stage-0"], - "env": { - "development": { - "presets": ["react-hmre"] - }, - "test": { - "presets": [] - } - } -} diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 96bb403..0000000 --- a/.eslintrc +++ /dev/null @@ -1,50 +0,0 @@ -{ - "parser": "babel-eslint", - "env": { - "browser": true, - "node": true, - "es6": true - }, - "ecmaFeatures": { - "modules": true - }, - "rules": { - "no-bitwise": 2, - "no-else-return": 2, - "no-eq-null": 2, - "no-extra-parens": 0, - "no-floating-decimal": 2, - "no-inner-declarations": [2, "both"], - "no-lonely-if": 2, - "no-multiple-empty-lines": [2, {"max": 3}], - "no-self-compare": 2, - "no-underscore-dangle": 0, - "no-use-before-define": 0, - "no-unused-expressions": 0, - "no-void": 2, - "brace-style": [2, "1tbs"], - "camelcase": [1, {"properties": "never"}], - "consistent-return": 0, - "comma-style": [2, "last"], - "complexity": [1, 12], - "func-names": 0, - "guard-for-in": 2, - "max-len": [0, 120, 4], - "new-cap": [2, {"newIsCap": true, "capIsNew": false}], - "quotes": [2, "single"], - "keyword-spacing": [2, {"before": true, "after": true}], - "space-before-blocks": [2, "always"], - "array-bracket-spacing": [2, "never"], - "space-in-parens": [2, "never"], - "strict": [0], - "valid-jsdoc": 2, - "wrap-iife": [2, "any"], - "yoda": [1, "never"] - }, - "plugins": [ - "react" - ], - "globals": { - - } -} diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 9f70f4c..0000000 --- a/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -*.swp -*~ -*.iml -.*.haste_cache.* -.DS_Store -.idea -npm-debug.log -node_modules -.env -public/ -dist/ -.DS_Store diff --git a/.npmignore b/.npmignore deleted file mode 100644 index a2cec57..0000000 --- a/.npmignore +++ /dev/null @@ -1,13 +0,0 @@ -*.swp -*~ -*.iml -.*.haste_cache.* -.DS_Store -.idea -.babelrc -.eslintrc -npm-debug.log -src/ -examples/ -public/ -scripts/ diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 6addbc9..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Julian Ćwirko - -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. \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 8a86997..0000000 --- a/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -.PHONY: publish dev test example - -dev: - npm run dev - -build: - npm run prepublish - -publish: - npm version patch - npm publish . - -test: - npm run test - -testwatch: - npm run test-watch - -example: - npm run build - -publish_pages: example - gh-pages -d ./public diff --git a/README.md b/README.md deleted file mode 100644 index 579bb80..0000000 --- a/README.md +++ /dev/null @@ -1,694 +0,0 @@ -## Redux modules - -The `redux-modules` package offers a different method of handling [redux](http://redux.js.org/) module packaging. - -The overall idea behind `redux-modules` is to build all of the corresponding redux functionality, constants, reducers, actions in a common location that makes it easy to understand the entire workflow common to a component of an application. - -Redux modules is essentially a collection of helpers that provide functionality for common tasks related to using Redux. - -## Quick take - -A redux module enables us to build our redux modules in a simple manner. A redux module can be as simple as: - -```javascript -/** - * Creates a `types` object - * with the hash of constants - **/ -const types = createConstants({ - prefix: 'TODO' -})( - 'CREATE', - 'MARK_DONE', - 'FETCH_ALL': {api: true} -); - -/** - * The root reducer that handles only create - **/ -const reducer = createReducer({ - [types.CREATE]: (state, {payload}) => ({ - ...state, - todos: state.todos.concat(payload) - }), - - // decorator form - @apiHandler(types.FETCH_ALL, (apiTypes) => { - // optional overrides - [apiTypes.FETCH_ALL_LOADING]: (state, action) => ({...state, loading: true}) - }) - handleFetchAll: (state, action) => ({...state, todos: action.payload}) - - // or non-decorator form: - handleFetchAll: createApiHandler(types.FETCH_ALL)((state, action) => { - return {...state, todos: action.payload}; - }) -}) - -/** - * Actions - **/ -const actions = createActions({ - createTodo: (text) => (dispatch) => dispatch({ - type: types.CREATE, - payload: {text: text, done: false} - }), - - // decorator form - @api(types.FETCH_ALL) - fetchAll: (client, opts) => client.get({path: '/todos'}) - - // or non-decorator form - fetchAll: createApiAction(types.FETCH_ALL)((client, opts) => { - return () => (dispatch, getState) => client.get('/todos') - }) -}) -``` - -In our app, our entire todo handler, reducer, and actions are all in one place in a single file. Incorporating the handler, reducer, and actions in our redux app is up to you. See [Usage in Redux](#usage-with-react-redux) for information. - -## Installation - -```bash -npm install --save redux-module-builder -``` - -## Example - -For example, let's take the idea of writing a TODO application. We'll need a few actions: - -* Create -* Mark done -* Fetch All - -Using `redux-modules`, we can create an object that carries a unique _type_ string for each of the actions in namespace path on a type object using the `createConstants()` exported method. For instance: - -```javascript -const types = createConstants('TODO')({ - 'CREATE': true, // value is ignored - 'FETCH_ALL': {api: true} -}) -``` - -
- -If you prefer not to use any of the api helpers included with `redux-modules`, the `createConstants()` function accepts a simple list of types instead: - -```javascript -const types = createConstants('TODO')( - 'CREATE', 'MARK_DONE', 'DELETE' -) -``` - -## createReducer - -The `createReducer()` exported function is a simple reduce handler. It accepts a single object and is responsible for passing action handling down through defined functions on a per-action basis. - -The first argument object is the list of handlers, by their type with a function to be called on the dispatch of the action. For instance, from our TODO example, this object might look like: - -```javascript -const reducer = createReducer({ - [types.CREATE]: (state, {payload}) => ({ - ...state, - todos: state.todos.concat(payload) - }) -}); -``` - -The previous object defines a handler for the `types.CREATE` action type, but does not define one for the `types.FETCH_ALL`. When the `types.CREATE` type is dispatched, the function above runs and is considered the reducer for the action. In this example, when the `types.FETCH_ALL` action is dispatched, the default handler: `(state, action) => state` is called (aka the original state). - -To add handlers, we only need to define the key and value function. - -## API handling - -The power of `redux-modules` really comes into play when dealing with async code. The common pattern of handling async API calls, which generates multiple states. - -Notice in our above example we can mark types as `api: true` within a type object and notice that it created 4 action key/values in the object which correspond to different states of an api call (loading, success, error, and the type itself). We can use this pattern to provide simple api handling to any action/reducer. - -### apiMiddleware - -In order to set this up, we need to add a middleware to our redux stack. This middleware helps us define defaults for handling API requests. For instance. In addition, `redux-modules` depends upon [redux-thunk](https://github.com/gaearon/redux-thunk) being available as a middleware _after_ the apiMiddleware. - -For instance: - -```javascript -// ... -let todoApp = combineReducers(reducers) -let apiMiddleware = createApiMiddleware({ - baseUrl: `https://fullstackreact.com`, - headers: {} - }); -let store = createStore(reducers, - applyMiddleware(apiMiddleware, thunk)); -// ... -``` - -The object the `createApiMiddleware()` accepts is the default configuration for all API requests. For instance, this is a good spot to add custom headers, a `baseUrl` (required), etc. Whatever we pass in here is accessible across every api client. - -For more _dynamic_ requests, we can pass a function into any one of these options and it will be called with the state so we can dynamically respond. An instance where we might want to pass a function would be with our headers, which might respond with a token for every request. Another might be a case for A/B testing where we can dynamically assign the `baseUrl` on a per-user basis. - -```javascript -// dynamically adding headers for _every_ request: -let apiMiddleware = createApiMiddleware({ - baseUrl: `https://fullstackreact.com`, - headers: (state) => ({ - 'X-Auth-Token': state.user.token - }) - }); -``` - -The `apiMiddleware` above currently decorates an actions `meta` object. For instance: - -```javascript -{ - type: 'API_FETCH_ALL', - payload: {}, - meta: {isApi: true} -} -// passes to the `thunk` middleware the resulting action: -{ - type: 'API_FETCH_ALL', - payload: {}, - meta: { - isApi: true, - baseUrl: BASE_URL, - headers: {} - } -} -``` - -### apiActions - -The easiest way to take advantage of this api infrastructure is to decorate the actions with the `@api` decorator (or the non-decorator form `createApiAction()`). When calling an api action created with the `@api/createApiAction`, `redux-modules` will dispatch two actions, the loading action and the handler. - -The `loading` action is fired with the type `[NAMESPACE]_loading`. The second action it dispatches is the handler function. The method that it is decorated with is expected to return a promise (although `redux-modules` will convert the response to a promise if it's not already one) which is expected to resolve in the case of success and error otherwise. - -More conveniently, `redux-modules` provides a client instance of [ApiClient](https://github.com/fullstackreact/redux-modules/blob/master/src/lib/apiClient.js) for every request (which is a thin wrapper around `fetch`). - -```javascript -// actions -@api(types.FETCH_ALL) -fetchAll: (client, opts) => { - return client.get({ - path: '/news' - }).then((json) => { - return json; - }); -} -``` - -The `apiClient` includes handling for request transformations, status checking, and response transformations. We'll look at those in a minute. The `apiClient` instance includes the HTTP methods: - -* GET -* POST -* PUT -* PATCH -* DELETE -* HEAD - -These methods can be called on the client itself: - -```javascript -client.get({}) -client.post({}) -client.put({}) -``` - -When they are called, they will be passed a list of options, which includes the base options (passed by the middleware) combined with custom options passed to the client (as the first argument). - -#### api client options - -The `apiClient` methods accept an argument of options for a per-api request customization. The following options are available and each can be either an atomic value or a function which gets called with the api request options within the client itself _or_ in the `baseOpts` of the middleware. - -When defining these options in the middleware, keep in mind that they will be available for every request passed by the `client` instance. - -1. path - -If a `path` option is found, `apiClient` will append the path to the `baseUrl`. If the `client` is called with a single _string_ argument, then it is considered the `path`. For instance: - -```javascript -// the following are equivalent -client.get('/foo') -client.get({path: '/foo'}) -// each results in a GET request to [baseUrl]/foo -``` - -2. url - -To completely ignore the `baseUrl` for a request, we can pass the `url` option which is used for the request. - -```javascript -client.get({url: 'http://google.com/?q=fullstackreact'}) -``` - -3. appendPath - -For dynamic calls, sometimes it's convenient to add a component to the path. For instance, we might want to append the url with a custom session key. - -```javascript -client.get({appendPath: 'abc123'}) -``` - -4. appendExt - -The `appendExt` is primarily useful for padding extensions on a url. For instance, to make all requests to the url with the `.json` extension, we can pass the `appendExt` to json: - -```javascript -client.get({appendExt: 'json'}) -``` - -5. params - -The `params` option is a query-string list of parameters that will be passed as query-string options. - -```javascript -client.get({params: {q: 'fullstackreact'}}) -``` - -6. headers - -Every request can define their own headers (or globally with the middleware) by using the `headers` option: - -```javascript -client.get({headers: {'X-Token': 'bearer someTokenThing'}}) -``` - -#### transforms - -The request and response transforms provide a way to manipulate requests as they go out and and they return. These are functions that are called with the `state` as well as the current request options. - -#### requestTransforms - -Request transforms are functions that can be defined to create a dynamic way to manipulate headers, body, etc. For instance, if we want to create a protected route, we can use a requestTransform to append a custom header. - -```javascript -client.get({ - requestTransforms: [(state, opts) => req => { - req.headers['X-Name'] = 'Ari'; - return req; - }] -}) -``` - -#### responseTransforms - -A response transform handles the resulting request response and gives us an opportunity to transform the data to another format on the way in. The default response transform is to respond with the response body into json. To handle the actual response, we can assign a responseTransform to overwrite the default json parsing and get a handle on the actual fetch response. - -```javascript -let timeTransform = (state, opts) => res => { - res.headers.set('X-Response-Time', time); - return res; -} -let jsonTransform = (state, opts) => (res) => { - let time = res.headers.get('X-Response-Time'); - return res.json().then(json => ({...json, time})) -} -client.get({ - responseTransforms: [timeTransform, jsonTransform] -}) -``` - -For apis that do not respond with json, the `responseTransforms` are a good spot to handle conversion to another format, such as xml. - -### apiHandlers - -To handle api responses in a reducer, `redux-modules` provides the `apiHandler` decorator (and it's non-decorator form: `createApiHandler()`). This decorator provides a common interface for handling the different states of an api request (i.e. `loading`, `success`, and `error` states). - -```javascript -@apiHandler(types.FETCH_ALL) -handleFetchAll: (state, {payload}) => {...state, ...payload} -``` - -The decorated function is considered the _success_ function handler and will be called upon a success status code returned from the request. - -To handle custom loading states, we can "hook" into them with a second argument. The second argument is a function that's called with the dynamic states provided by the argument it's called with. For instance, to handle custom handling of an error state: - -```javascript -@apiHandler(types.FETCH_ALL, (apiTypes) => ({ - [apiTypes.ERROR]: (state, {payload}) => ({ - ...state, - error: payload.body.message, - loading: false - }) -})) -handleFetchAll: (state, {payload}) => {...state, ...payload} -``` - -## Usage with react-redux - -There are multiple methods for combining `redux-modules` with react and this is our opinion about how to use the two together. - -First, our directory structure generally sets all of our modules in their own directory: - -```bash -index.js - /redux - /modules/ - todo.js - users.js - configureStore.js - rootReducer.js - index.js -``` - -Configuring the store for our app is straight-forward. First, we'll apply the `createApiMiddleware()` before we create the final store. In a `configureStore.js` file, we like to handle creating a store in a single spot. We'll export a function to configure the store: - -```javascript -import {rootReducer, actions} from './rootReducer' - -export const configureStore = ({initialState = {}}) => { - let middleware = [ - createApiMiddleware({ - baseUrl: BASE_URL - }), - thunkMiddleware - ] - // ... - const finalCreateStore = - compose(applyMiddleware(...middleware))(createStore); - - const store = finalCreateStore(rootReducer, initialState); - // ... -} -``` - -This creates the middleware for us. Next, we like to combine our actions into a single actions object that we'll pass along down through our components. We'll use the `bindActionCreatorsToStore()` export to build our action creators and bind them to the store. - -We'll need to bind our actions to the store, so that when we call dispatch it will use our store's dispatch (see [react-redux](https://github.com/reactjs/react-redux)). Just after we create the store, we'll: - -```javascript -let actions = bindActionCreatorsToStore(actions, store); -``` - -From here, we just return the store and actions from the function: - -```javascript -export const configureStore = ({initialState = {}}) => { - // ... - const store = finalCreateStore(rootReducer, initialState); - // ... - let actions = bindActionCreatorsToStore(actions, store); - - return {store, actions}; -} -``` - -Now that the heavy-lifting is done, the `rootReducer.js` file is pretty simple. We export all the actions and reducers pretty simply: - -```javascript -const containers = ['users', 'todos']; - -export const reducers = {} -export const actions = {}; - -containers.forEach(k => { - let val = require(`./modules/${v}`); - reducers[k] = val.reducer; - actions[k] = val.actions || {}; -}); - -export const rootReducer = combineReducers(reducers); -``` - -From here, our main container can pass the store and actions as props to our components: - -```javascript -const {store, actions} = configureStore({initialState}); -// ... -ReactDOM.render( - , - node); -``` - -Now, anywhere in our code, we can refer to the actions we export in our modules by their namespace. For instance, to call the `createTodo()` function, we can reference it by the prop namespace: - -```javascript -class Container extends React.Component { - - createTodo() { - const {actions} = this.props; - // form: actions.[namespace].[actionName](); - actions.todos.createTodo("Finish this text"); - } - - render() { - return ( -
- Create todo -
- ) - } -} -``` - -## Combining usage with `ducks-modular-redux` - -`redux-modules` plays nicely with other redux packages as well. For instance, the `ducks-modular-redux` package defines a specific method of handling actions, reducers, and types. - -To create types in the same way, we can use the `separator` and `prefix` options in `createConstants()`. For instance, to create the constants defined by `ducks-modular-redux`'s README': - -```javascript -const LOAD = 'my-app/widgets/LOAD'; -const CREATE = 'my-app/widgets/CREATE'; -const UPDATE = 'my-app/widgets/UPDATE'; -const REMOVE = 'my-app/widgets/REMOVE'; -// In redux-modules: -const types = createConstants({ - prefix: ['my-app', 'widgets'], - separator: '/' -})('LOAD', 'CREATE', 'UPDATE', 'REMOVE') -``` - -Handling the reducer function is similarly easy as well: - -```javascript -export default function reducer(state = {}, action = {}) { - switch (action.type) { - // do reducer stuff - default: return state; - } -} -const reducer = createReducer({ - [types.LOAD]: (state, {payload}) => ({ - ...state, - todos: state.todos.concat(payload) - }), - // ... -}); -``` - -Finally, exporting functions individually from the file is directly supported. The `createActions()` and `createApiAction()` helpers can be used directly on created functions. - -## All exports - -The `redux-modules` comprises the following exports: - -### createConstants - -`createConstants()` creates an object to handle creating an object of type constants. It allows for multiple types to be dynamically created with their own custom prefixing, created on a single object. - -```javascript -const types = createConstants({})('DOG', 'CAT'); -``` - -Options: - -* prefix (string/array, default: '') - -The `prefix` option creates each type with a predefined prefix. - -```javascript -const types = createConstants({ - prefix: 'animals' -})('DOG', 'CAT') -expect(types.DOG).to.eql('ANIMALS_DOG'); -expect(types.CAT).to.eql('ANIMALS_CAT'); - -const types = createConstants({ - prefix: ['test', 'animals'] -})('DOG', 'CAT') -expect(types.DOG).to.eql('TEST_ANIMALS_DOG'); -expect(types.CAT).to.eql('TEST_ANIMALS_CAT'); -``` - -* separator (string, default: `_`) - -The separator option allows us to change the way prefixes are concatenated. To change the separator to use a `/`, add the separator option: - -```javascript -const types = createConstants({ - separator: '/', - prefix: ['test', 'animals'] -})('DOG', 'CAT') -expect(types.DOG).to.eql('TEST/ANIMALS/DOG'); -expect(types.CAT).to.eql('TEST/ANIMALS/CAT'); -``` - -* initialObject (object, default: `{}`) - -For the case where you want to define types on an existing object, `createConstants()` accepts an `initialObject` to add the types. This allows us to create a single global object (for instance) to define all of our types. - -```javascript -const types = createConstants({ - initialObject: types -})('OTHER', 'CONSTANTS'); -``` - -### createReducer - -The `createReducer()` function returns a function that acts similar to the switch-case functionality of redux where we'll define the types and the reducers that handle the types. - -```javascript -const reducer = createReducer({ - [types.CREATE]: (state, {payload}) => ({ - ...state, - todos: state.todos.concat(payload) - }) -}); -``` - -### bindActionCreatorsToStore - -The `bindActionCreatorsToStore()` function accepts two arguments, the action handler object and the store object. It takes each action, binds the function to the `store` object (so `this` refers to the `store`) and then calls the `bindActionCreators()` redux function to the store object. Use the returned value as the reducer object. - -```javascript -let actions = bindActionCreatorsToStore(actions, store); -``` - -### createApiMiddleware - -In order to set global options for every api request, we have to include a middleware in our stack. Creating the middleware for our redux stack uses `createApiMiddleware()` and essentially looks for any api action (with `meta.isApi` set to true) and merges global options into the `meta` key of the action. - -We _must_ set the `baseUrl` in the middleware, which is used as the default url to make a request against. Without the `baseUrl`, all requests will be sent without an http component (unless set otherwise in the `apiClient`): - -```javascript -let apiMiddleware = createApiMiddleware({ - baseUrl: `https://fullstackreact.com`, - headers: { - 'Accept': 'application/json' - } - }); -``` - -### apiClient - -The `apiClient` is a loose wrapper around the native html5 `fetch()` function (built around `isomorphic-fetch`, which makes testing easier). When an action is marked as an API action, it will be called with an instance of the `apiClient` as well as the options `fetch()` will be called with. This gives us an easy, flexible way to make API requests. - -The `apiClient` instance creates methods for each HTTP method, which accepts custom parameters to make the requests. It handles building the request, the options, putting together the url, packaging the body, request and response transformations, and more. - -Using the `apiClient` instance inside of an api action request looks like: - -```javascript -@api(types.FETCH_ALL) -fetchAll: (client, opts) => client.get({path: '/todos'}) -// or non-decorator version -let decoratedFetchall = createApiAction(types.FETCH_ALL)(function(client, opts) { - return client.get({path: '/todos'}) -}); -``` - -By default, the request is assumed to be in json format, but this is flexible and can be manipulated on a global/per-request level. - -Every option that the `apiMiddleware` and `apiClient.[method]` accepts can be either an atomic value or it can be a function. If a function is passed, it will be called at runtime with the current options and state to allow for dynamic responses based upon the state. - -The available options for _both_ apiMiddleware and client method requests are [here](#api-client-options). - -### createApiAction/@api - -To create an api action creator, we can decorate it with the `@api` decorator (when defined inline in an object) or using the `createApiAction()` function. Using this decorator, the function itself will be used to fetch an api. - -The decorated function is expected to use the `client`, although it is not required. It is expected that the decorated function returns a value, either a promise or an atomic value. - -```javascript -{ - actions: { - @api(types.FETCH_ALL) - fetchAll: (client, opts) => client.get({path: '/todos'}) - } -} -// OR non-decorator version -const fetchAll = createApiAction(types.FETCH_ALL)( - (client, opts) => client.get('/todos')); -``` - -Using the decorator will dispatch actions according to their response status, first dispatching the `loading` type (i.e. `{type: 'API_FETCH_ALL_LOADING', opts}`), then it calls the handler. Once the handler returns, the corresponding action `_SUCCESS` or `_ERROR` action types are dispatched. - -### createApiHandler/@apiHandler - -In order to handle the response from an api request, we need to create a reducer. The `api` decorator fires the status values for the state of the api request. Using the `createApiHandler()/@apiHandler` decorator sets up default handlers for dealing with these responses. - -```javascript -{ - reducers: { - @apiHandler(types.FETCH_ALL) - handleFetchAll: (state, action) => ({...state, ...action.payload}); - } -} -// or non-decorator version -const handlers = createApiHandler(types.FETCH_ALL) => {})((state, action) => { - return { - ...state, - ...action.payload - } -}); -``` - -The default actions will set the `loading` flag to true when the `_LOADING` action type is called, while the `loading` flag (in the state) will be set to false for the `_ERROR` and `_SUCCESS` types. - -For the case where we want to handle the states in a custom way, we can pass a second argument as a function which is called with the api states object where we can set custom handlers. For instance, to handle loading and errors in our previous example: - -```javascript -{ - reducers: { - @apiHandler(types.FETCH_ALL, (apiStates) => { - [apiStates.loading]: (state, action) => ({...state, loading: true}), - [apiStates.error]: (state, action) => { - return ({ - ...state, - error: action.payload - }) - } - }) - handleFetchAll: (state, action) => ({...state, ...action.payload}); - } -} -``` - -The decorated function is considered the success handler. - -## TODOS: - -* [x] Use the custom type creator in `createConstants()` for defining apis -* [] Add docs for `createRootReducer()` - -## Contributing - -```shell -git clone https://github.com/fullstackreact/redux-modules.git -cd redux-modules -npm install -make dev -``` - -To run the tests (please ensure the tests pass when creating a pull request): - -```shell -make test # or npm run test -``` - -___ - -# Fullstack React Book - - -Fullstack React Book - - -This repo was written and is maintained by the [Fullstack React](https://fullstackreact.com) team. In the book we cover many more projects like this. We walk through each line of code, explain why it's there and how it works. - -This app is only one of several apps we have in the book. If you're looking to learn React, there's no faster way than by spending a few hours with the Fullstack React book. - -
- -## License - [MIT](/LICENSE) diff --git a/api.js b/api.js deleted file mode 100644 index 4bba306..0000000 --- a/api.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./dist/api'); diff --git a/index.html b/index.html new file mode 100644 index 0000000..facab5f --- /dev/null +++ b/index.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/index.js b/index.js deleted file mode 100644 index b91eb38..0000000 --- a/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./dist/index'); \ No newline at end of file diff --git a/package.json b/package.json deleted file mode 100644 index 26194ce..0000000 --- a/package.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "name": "redux-module-builder", - "version": "0.3.2", - "description": "Redux modules", - "author": "Fullstack.io ", - "license": "MIT", - "options": { - "mocha": "--require scripts/mocha_runner -t rewireify src/**/__tests__/**/*.js" - }, - "repository": { - "type": "git", - "url": "https://github.com/fullstackreact/redux-modules.git" - }, - "main": "dist/index.js", - "scripts": { - "prepublish": ". ./scripts/prepublish.sh", - "dev": "NODE_ENV=development ./node_modules/hjs-webpack/bin/hjs-dev-server.js", - "build": "NODE_ENV=production webpack", - "publish_pages": "gh-pages -d public/", - "lint": "eslint ./src", - "lintfix": "eslint ./src --fix", - "testonly": "NODE_ENV=test mocha $npm_package_options_mocha", - "test": "npm run lint && npm run testonly", - "test:watch": "npm run testonly -- --watch --watch-extensions js" - }, - "devDependencies": { - "autoprefixer": "^6.3.7", - "babel-cli": "^6.11.4", - "babel-core": "^6.11.4", - "babel-eslint": "^6.1.2", - "babel-loader": "^6.2.4", - "babel-plugin-transform-es2015-modules-umd": "^6.8.0", - "babel-polyfill": "^6.9.1", - "babel-preset-es2015": "^6.9.0", - "babel-preset-react": "^6.11.1", - "babel-preset-react-hmre": "^1.1.1", - "babel-preset-stage-0": "^6.5.0", - "babel-preset-stage-2": "^6.11.0", - "babel-runtime": "^6.9.2", - "chai": "^3.5.0", - "chai-spies": "^0.7.1", - "css-loader": "^0.23.1", - "cssnano": "^3.7.3", - "dotenv": "^2.0.0", - "enzyme": "^2.4.1", - "eslint": "^3.1.1", - "eslint-plugin-babel": "^3.3.0", - "eslint-plugin-react": "^5.2.2", - "fetch-mock": "^5.0.3", - "file-loader": "^0.9.0", - "gh-pages": "^0.11.0", - "highlight.js": "^9.5.0", - "hjs-webpack": "^8.3.0", - "html-loader": "^0.4.3", - "install": "^0.8.1", - "invariant": "^2.2.1", - "jsdom": "^9.4.1", - "markdown-loader": "^0.1.7", - "markdown-with-front-matter-loader": "^0.1.0", - "marked": "^0.3.5", - "mocha": "^2.5.3", - "mockery": "^1.7.0", - "nodemon": "^1.9.2", - "npm": "^3.10.5", - "npm-font-open-sans": "0.0.3", - "postcss-loader": "^0.9.1", - "precss": "^1.4.0", - "raw-loader": "^0.5.1", - "react": "^15.2.1", - "react-addons-test-utils": "^15.2.1", - "react-dom": "^15.2.1", - "react-github-fork-ribbon": "^0.4.4", - "react-router": "^2.6.0", - "redux-mock-store": "^1.1.2", - "redux-thunk": "^2.1.0", - "sinon": "^1.17.4", - "sinon-chai": "^2.8.0", - "style-loader": "^0.13.1", - "url-loader": "^0.5.7", - "webpack": "^1.13.1" - }, - "peerDependencies": { - "react": "~0.14.8 || ^15.0.0" - }, - "dependencies": { - "invariant": "^2.2.1", - "query-string": "^4.2.2", - "react-github-fork-ribbon": "^0.4.4", - "react-inspector": "^1.1.0", - "redux": "^3.5.2", - "redux-actions": "^0.10.1", - "whatwg-fetch": "^1.0.0" - } -} diff --git a/redux-module-builder.0.1.1.css b/redux-module-builder.0.1.1.css new file mode 100644 index 0000000..c10fee2 --- /dev/null +++ b/redux-module-builder.0.1.1.css @@ -0,0 +1 @@ +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,300);body,html{height:100%;margin:0;padding:0}._3w7Bz{font-family:Open Sans,sans-serif;font-weight:lighter}.fCj7q{font-size:.5em;background:rgba(27,202,255,.8);padding:10px;margin:0;color:#333;text-align:center;border-bottom:2px solid #1c7c99}._2J5R4 ._35yex{position:relative;min-height:100%}._2J5R4 ._31CNX{border:1px solid #efefef;padding:10px}._2J5R4 ._3w7Bz{padding:15px;margin:10px 0} \ No newline at end of file diff --git a/redux-module-builder.0.1.1.js b/redux-module-builder.0.1.1.js new file mode 100644 index 0000000..7650071 --- /dev/null +++ b/redux-module-builder.0.1.1.js @@ -0,0 +1,23 @@ +!function(e){function t(r){if(n[r])return n[r].exports;var a=n[r]={exports:{},id:r,loaded:!1};return e[r].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n={};return t.m=e,t.c=n,t.p="/",t(0)}([function(e,t,n){e.exports=n(192)},function(e,t,n){"use strict";function r(e,t,n,r,a,o,i,s){if(!e){var l;if(void 0===t)l=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[n,r,a,o,i,s],u=0;l=new Error(t.replace(/%s/g,function(){return c[u++]})),l.name="Invariant Violation"}throw l.framesToPop=1,l}}e.exports=r},function(e,t){"use strict";function n(e){for(var t=arguments.length-1,n="Minified React error #"+e+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+e,r=0;r2?n-2:0),a=2;a1){for(var _=Array(g),h=0;h1){for(var v=Array(b),y=0;y should not have a "'+t+'" prop')}t.__esModule=!0,t.routes=t.route=t.components=t.component=t.history=void 0,t.falsy=r;var a=n(4),o=a.PropTypes.func,i=a.PropTypes.object,s=a.PropTypes.arrayOf,l=a.PropTypes.oneOfType,c=a.PropTypes.element,u=a.PropTypes.shape,d=a.PropTypes.string,p=(t.history=u({listen:o.isRequired,push:o.isRequired,replace:o.isRequired,go:o.isRequired,goBack:o.isRequired,goForward:o.isRequired}),t.component=l([o,d])),m=(t.components=l([p,i]),t.route=l([i,c]));t.routes=l([m,s(m)])},function(e,t,n){"use strict";function r(e,t){return(e&t)===t}var a=n(2),o=(n(1),{MUST_USE_PROPERTY:1,HAS_SIDE_EFFECTS:2,HAS_BOOLEAN_VALUE:4,HAS_NUMERIC_VALUE:8,HAS_POSITIVE_NUMERIC_VALUE:24,HAS_OVERLOADED_BOOLEAN_VALUE:32,injectDOMPropertyConfig:function(e){var t=o,n=e.Properties||{},i=e.DOMAttributeNamespaces||{},l=e.DOMAttributeNames||{},c=e.DOMPropertyNames||{},u=e.DOMMutationMethods||{};e.isCustomAttribute&&s._isCustomAttributeFunctions.push(e.isCustomAttribute);for(var d in n){s.properties.hasOwnProperty(d)?a("48",d):void 0;var p=d.toLowerCase(),m=n[d],f={attributeName:p,attributeNamespace:null,propertyName:d,mutationMethod:null,mustUseProperty:r(m,t.MUST_USE_PROPERTY),hasSideEffects:r(m,t.HAS_SIDE_EFFECTS),hasBooleanValue:r(m,t.HAS_BOOLEAN_VALUE),hasNumericValue:r(m,t.HAS_NUMERIC_VALUE),hasPositiveNumericValue:r(m,t.HAS_POSITIVE_NUMERIC_VALUE),hasOverloadedBooleanValue:r(m,t.HAS_OVERLOADED_BOOLEAN_VALUE)};if(!f.mustUseProperty&&f.hasSideEffects?a("49",d):void 0,f.hasBooleanValue+f.hasNumericValue+f.hasOverloadedBooleanValue<=1?void 0:a("50",d),l.hasOwnProperty(d)){var g=l[d];f.attributeName=g}i.hasOwnProperty(d)&&(f.attributeNamespace=i[d]),c.hasOwnProperty(d)&&(f.propertyName=c[d]),u.hasOwnProperty(d)&&(f.mutationMethod=u[d]),s.properties[d]=f}}}),i=":A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD",s={ID_ATTRIBUTE_NAME:"data-reactid",ROOT_ATTRIBUTE_NAME:"data-reactroot",ATTRIBUTE_NAME_START_CHAR:i,ATTRIBUTE_NAME_CHAR:i+"\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040",properties:{},getPossibleStandardName:null,_isCustomAttributeFunctions:[],isCustomAttribute:function(e){for(var t=0;t0?void 0:(0,p["default"])(!1),null!=u&&(o+=encodeURI(u))):"("===l?a+=1:")"===l?a-=1:":"===l.charAt(0)?(c=l.substring(1),u=t[c],null!=u||a>0?void 0:(0,p["default"])(!1),null!=u&&(o+=encodeURIComponent(u))):o+=l;return o.replace(/\/+/g,"/")}t.__esModule=!0,t.compilePattern=i,t.matchPattern=s,t.getParamNames=l,t.getParams=c,t.formatPattern=u;var d=n(8),p=r(d),m={}},function(e,t,n){"use strict";function r(e){if(_){var t=e.node,n=e.children;if(n.length)for(var r=0;r=t.length?{value:void 0,done:!0}:(e=r(t,n),this._i+=e.length,{value:e,done:!1})})},function(e,t,n){"use strict";var r={};e.exports=r},function(e,t,n){"use strict";var r=n(1),a=function(e){var t,n={};e instanceof Object&&!Array.isArray(e)?void 0:r(!1);for(t in e)e.hasOwnProperty(t)&&(n[t]=t);return n};e.exports=a},function(e,t){"use strict";t.__esModule=!0;var n=!("undefined"==typeof window||!window.document||!window.document.createElement);t.canUseDOM=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function a(e){return l.stringify(e).replace(/%20/g,"+")}function o(e){return function(){function t(e){if(null==e.query){var t=e.search;e.query=C(t.substring(1)),e[f]={search:t,searchBase:""}}return e}function n(e,t){var n,r=e[f],a=t?E(t):"";if(!r&&!a)return e;"string"==typeof e&&(e=d.parsePath(e));var o=void 0;o=r&&e.search===r.search?r.searchBase:e.search||"";var s=o;return a&&(s+=(s?"&":"?")+a),i({},e,(n={search:s},n[f]={search:s,searchBase:o},n))}function r(e){return y.listenBefore(function(n,r){u["default"](e,t(n),r)})}function o(e){return y.listen(function(n){e(t(n))})}function s(e){y.push(n(e,e.query))}function l(e){y.replace(n(e,e.query))}function c(e,t){return y.createPath(n(e,t||e.query))}function p(e,t){return y.createHref(n(e,t||e.query))}function _(e){for(var r=arguments.length,a=Array(r>1?r-1:0),o=1;o-1?void 0:i("96",e),!c.plugins[n]){t.extractEvents?void 0:i("97",e),c.plugins[n]=t;var r=t.eventTypes;for(var o in r)a(r[o],t,o)?void 0:i("98",o,e)}}}function a(e,t,n){c.eventNameDispatchConfigs.hasOwnProperty(n)?i("99",n):void 0,c.eventNameDispatchConfigs[n]=e;var r=e.phasedRegistrationNames;if(r){for(var a in r)if(r.hasOwnProperty(a)){var s=r[a];o(s,t,n)}return!0}return!!e.registrationName&&(o(e.registrationName,t,n),!0)}function o(e,t,n){c.registrationNameModules[e]?i("100",e):void 0,c.registrationNameModules[e]=t,c.registrationNameDependencies[e]=t.eventTypes[n].dependencies}var i=n(2),s=(n(1),null),l={},c={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},possibleRegistrationNames:null,injectEventPluginOrder:function(e){s?i("101"):void 0,s=Array.prototype.slice.call(e),r()},injectEventPluginsByName:function(e){var t=!1;for(var n in e)if(e.hasOwnProperty(n)){var a=e[n];l.hasOwnProperty(n)&&l[n]===a||(l[n]?i("102",n):void 0,l[n]=a,t=!0)}t&&r()},getPluginModuleForEvent:function(e){var t=e.dispatchConfig;if(t.registrationName)return c.registrationNameModules[t.registrationName]||null;for(var n in t.phasedRegistrationNames)if(t.phasedRegistrationNames.hasOwnProperty(n)){var r=c.registrationNameModules[t.phasedRegistrationNames[n]];if(r)return r}return null},_resetEventPlugins:function(){s=null;for(var e in l)l.hasOwnProperty(e)&&delete l[e];c.plugins.length=0;var t=c.eventNameDispatchConfigs;for(var n in t)t.hasOwnProperty(n)&&delete t[n];var r=c.registrationNameModules;for(var a in r)r.hasOwnProperty(a)&&delete r[a]}};e.exports=c},function(e,t,n){"use strict";function r(e){return Object.prototype.hasOwnProperty.call(e,_)||(e[_]=f++,p[e[_]]={}),p[e[_]]}var a,o=n(5),i=n(17),s=n(62),l=n(507),c=n(171),u=n(536),d=n(107),p={},m=!1,f=0,g={topAbort:"abort",topAnimationEnd:u("animationend")||"animationend",topAnimationIteration:u("animationiteration")||"animationiteration",topAnimationStart:u("animationstart")||"animationstart",topBlur:"blur",topCanPlay:"canplay",topCanPlayThrough:"canplaythrough",topChange:"change",topClick:"click",topCompositionEnd:"compositionend",topCompositionStart:"compositionstart",topCompositionUpdate:"compositionupdate",topContextMenu:"contextmenu",topCopy:"copy",topCut:"cut",topDoubleClick:"dblclick",topDrag:"drag",topDragEnd:"dragend",topDragEnter:"dragenter",topDragExit:"dragexit",topDragLeave:"dragleave",topDragOver:"dragover",topDragStart:"dragstart",topDrop:"drop",topDurationChange:"durationchange",topEmptied:"emptied",topEncrypted:"encrypted",topEnded:"ended",topError:"error",topFocus:"focus",topInput:"input",topKeyDown:"keydown",topKeyPress:"keypress",topKeyUp:"keyup",topLoadedData:"loadeddata",topLoadedMetadata:"loadedmetadata",topLoadStart:"loadstart",topMouseDown:"mousedown",topMouseMove:"mousemove",topMouseOut:"mouseout",topMouseOver:"mouseover",topMouseUp:"mouseup",topPaste:"paste",topPause:"pause",topPlay:"play",topPlaying:"playing",topProgress:"progress",topRateChange:"ratechange",topScroll:"scroll",topSeeked:"seeked",topSeeking:"seeking",topSelectionChange:"selectionchange",topStalled:"stalled",topSuspend:"suspend",topTextInput:"textInput",topTimeUpdate:"timeupdate",topTouchCancel:"touchcancel",topTouchEnd:"touchend",topTouchMove:"touchmove",topTouchStart:"touchstart",topTransitionEnd:u("transitionend")||"transitionend",topVolumeChange:"volumechange",topWaiting:"waiting",topWheel:"wheel"},_="_reactListenersID"+String(Math.random()).slice(2),h=o({},l,{ReactEventListener:null,injection:{injectReactEventListener:function(e){e.setHandleTopLevel(h.handleTopLevel),h.ReactEventListener=e}},setEnabled:function(e){h.ReactEventListener&&h.ReactEventListener.setEnabled(e)},isEnabled:function(){return!(!h.ReactEventListener||!h.ReactEventListener.isEnabled())},listenTo:function(e,t){for(var n=t,a=r(n),o=s.registrationNameDependencies[e],l=i.topLevelTypes,c=0;c]/;e.exports=r},function(e,t,n){"use strict";var r,a=n(7),o=n(95),i=/^[ \r\n\t\f]/,s=/<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/,l=n(103),c=l(function(e,t){if(e.namespaceURI!==o.svg||"innerHTML"in e)e.innerHTML=t;else{r=r||document.createElement("div"),r.innerHTML=""+t+"";for(var n=r.firstChild.childNodes,a=0;a";for(t.style.display="none",n(219).appendChild(t),t.src="javascript:",e=t.contentWindow.document,e.open(),e.write("",returnEnd:!0,subLanguage:["actionscript","javascript","handlebars","xml"]}},{className:"meta",variants:[{begin:/<\?xml/,end:/\?>/,relevance:10},{begin:/<\?\w+/,end:/\?>/}]},{className:"tag",begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},n]}]}}},function(e,t){e.exports=function(e){var t="for let if while then else return where group by xquery encoding versionmodule namespace boundary-space preserve strip default collation base-uri orderingcopy-namespaces order declare import schema namespace function option in allowing emptyat tumbling window sliding window start when only end when previous next stable ascendingdescending empty greatest least some every satisfies switch case typeswitch try catch andor to union intersect instance of treat as castable cast map array delete insert intoreplace value rename copy modify update",n="false true xs:string xs:integer element item xs:date xs:datetime xs:float xs:double xs:decimal QName xs:anyURI xs:long xs:int xs:short xs:byte attribute",r={begin:/\$[a-zA-Z0-9\-]+/},a={className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},o={className:"string",variants:[{begin:/"/,end:/"/,contains:[{begin:/""/,relevance:0}]},{begin:/'/,end:/'/,contains:[{begin:/''/,relevance:0}]}]},i={className:"meta",begin:"%\\w+"},s={className:"comment",begin:"\\(:",end:":\\)",relevance:10,contains:[{className:"doctag",begin:"@\\w+"}]},l={begin:"{",end:"}"},c=[r,o,a,s,i,l];return l.contains=c,{aliases:["xpath","xq"],case_insensitive:!1,lexemes:/[a-zA-Z\$][a-zA-Z0-9_:\-]*/,illegal:/(proc)|(abstract)|(extends)|(until)|(#)/,keywords:{keyword:t,literal:n},contains:c}}},function(e,t){e.exports=function(e){var t={literal:"{ } true false yes no Yes No True False null"},n="^[ \\-]*",r="[a-zA-Z_][\\w\\-]*",a={className:"attr",variants:[{begin:n+r+":"},{begin:n+'"'+r+'":'},{begin:n+"'"+r+"':"}]},o={className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]},i={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/}],contains:[e.BACKSLASH_ESCAPE,o]};return{case_insensitive:!0,aliases:["yml","YAML","yaml"],contains:[a,{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>] *$",returnEnd:!0,contains:i.contains,end:a.variants[0].begin},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!!"+e.UNDERSCORE_IDENT_RE},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"^ *-",relevance:0},i,e.HASH_COMMENT_MODE,e.C_NUMBER_MODE],keywords:t}}},function(e,t){e.exports=function(e){var t={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:'b"',end:'"'},{begin:"b'",end:"'"},e.inherit(e.APOS_STRING_MODE,{illegal:null}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null})]},n={variants:[e.BINARY_NUMBER_MODE,e.C_NUMBER_MODE]};return{aliases:["zep"],case_insensitive:!0,keywords:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var let while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally int uint long ulong char uchar double float bool boolean stringlikely unlikely",contains:[e.C_LINE_COMMENT_MODE,e.HASH_COMMENT_MODE,e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0,keywords:"__halt_compiler",lexemes:e.UNDERSCORE_IDENT_RE}),{className:"string",begin:"<<<['\"]?\\w+['\"]?$",end:"^\\w+;",contains:[e.BACKSLASH_ESCAPE]},{begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function",beginKeywords:"function",end:/[;{]/,excludeEnd:!0,illegal:"\\$|\\[|%",contains:[e.UNDERSCORE_TITLE_MODE,{className:"params",begin:"\\(",end:"\\)",contains:["self",e.C_BLOCK_COMMENT_MODE,t,n]}]},{className:"class",beginKeywords:"class interface",end:"{",excludeEnd:!0,illegal:/[:\(\$"]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",end:";",illegal:/[\.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use",end:";",contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"=>"},t,n]}}},function(e,t){"use strict";function n(e,t,n){function a(){return s=!0,l?void(u=[].concat(r.call(arguments))):void n.apply(this,arguments)}function o(){if(!s&&(c=!0,!l)){for(l=!0;!s&&i=e&&c&&(s=!0,n()))}}var i=0,s=!1,l=!1,c=!1,u=void 0;o()}t.__esModule=!0;var r=Array.prototype.slice;t.loopAsync=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function a(){function e(e){try{e=e||window.history.state||{}}catch(t){e={}}var n=d.getWindowPath(),r=e,a=r.key,i=void 0;a?i=p.readState(a):(i=null,a=v.createKey(),h&&window.history.replaceState(o({},e,{key:a}),null));var s=c.parsePath(n);return v.createLocation(o({},s,{state:i}),void 0,a)}function t(t){function n(t){void 0!==t.state&&r(e(t.state))}var r=t.transitionTo;return d.addEventListener(window,"popstate",n),function(){d.removeEventListener(window,"popstate",n)}}function n(e){var t=e.basename,n=e.pathname,r=e.search,a=e.hash,o=e.state,i=e.action,s=e.key;if(i!==l.POP){p.saveState(s,o);var c=(t||"")+n+r+a,u={key:s};if(i===l.PUSH){if(b)return window.location.href=c,!1;window.history.pushState(u,null,c)}else{if(b)return window.location.replace(c),!1;window.history.replaceState(u,null,c)}}}function r(e){1===++y&&(E=t(v));var n=v.listenBefore(e);return function(){n(),0===--y&&E()}}function a(e){1===++y&&(E=t(v));var n=v.listen(e);return function(){n(),0===--y&&E()}}function i(e){1===++y&&(E=t(v)),v.registerTransitionHook(e)}function m(e){v.unregisterTransitionHook(e),0===--y&&E()}var g=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];u.canUseDOM?void 0:s["default"](!1);var _=g.forceRefresh,h=d.supportsHistory(),b=!h||_,v=f["default"](o({},g,{getCurrentLocation:e,finishTransition:n,saveState:p.saveState})),y=0,E=void 0;return o({},v,{listenBefore:r,listen:a,registerTransitionHook:i,unregisterTransitionHook:m})}t.__esModule=!0;var o=Object.assign||function(e){for(var t=1;t=0&&t=0&&_Redux modules\n

The redux-modules package offers a different method of handling redux module packaging.

\n

The overall idea behind redux-modules is to build all of the corresponding redux functionality, constants, reducers, actions in a common location that makes it easy to understand the entire workflow common to a component of an application.

\n

Redux modules is essentially a collection of helpers that provide functionality for common tasks related to using Redux.

\n

Quick take

\n

A redux module enables us to build our redux modules in a simple manner. A redux module can be as simple as:

\n
/**\n * Creates a `types` object\n * with the hash of constants\n **/\nconst types = createConstants({\n  prefix: 'TODO'\n})(\n  'CREATE',\n  'MARK_DONE',\n  'FETCH_ALL': {api: true}\n);\n\n/**\n * The root reducer that handles only create\n **/\nconst reducer = createReducer({\n  [types.CREATE]: (state, {payload}) => ({\n    ...state,\n    todos: state.todos.concat(payload)\n  }),\n\n  // decorator form\n  @apiHandler(types.FETCH_ALL, (apiTypes) => {\n    // optional overrides\n    [apiTypes.FETCH_ALL_LOADING]: (state, action) => ({...state, loading: true})\n  })\n  handleFetchAll: (state, action) => ({...state, todos: action.payload})\n\n  // or non-decorator form:\n  handleFetchAll: createApiHandler(types.FETCH_ALL)((state, action) => {\n    return {...state, todos: action.payload};\n  })\n})\n\n/**\n * Actions\n **/\nconst actions = createActions({\n  createTodo: (text) => (dispatch) => dispatch({\n    type: types.CREATE,\n    payload: {text: text, done: false}\n  }),\n\n  // decorator form\n  @api(types.FETCH_ALL)\n  fetchAll: (client, opts) => client.get({path: '/todos'})\n\n  // or non-decorator form\n  fetchAll: createApiAction(types.FETCH_ALL)((client, opts) => {\n    return () => (dispatch, getState) => client.get('/todos')\n  })\n})\n
\n

In our app, our entire todo handler, reducer, and actions are all in one place in a single file. Incorporating the handler, reducer, and actions in our redux app is up to you. See Usage in Redux for information.

\n

Installation

\n
npm install --save redux-module-builder\n
\n

Example

\n

For example, let's take the idea of writing a TODO application. We'll need a few actions:

\n
    \n
  • Create
  • \n
  • Mark done
  • \n
  • Fetch All
  • \n
\n

Using redux-modules, we can create an object that carries a unique type string for each of the actions in namespace path on a type object using the createConstants() exported method. For instance:

\n
const types = createConstants('TODO')({\n  'CREATE': true, // value is ignored\n  'FETCH_ALL': {api: true}\n})\n
\n
\n\n

If you prefer not to use any of the api helpers included with redux-modules, the createConstants() function accepts a simple list of types instead:

\n
const types = createConstants('TODO')(\n  'CREATE', 'MARK_DONE', 'DELETE'\n)\n
\n

createReducer

\n

The createReducer() exported function is a simple reduce handler. It accepts a single object and is responsible for passing action handling down through defined functions on a per-action basis.

\n

The first argument object is the list of handlers, by their type with a function to be called on the dispatch of the action. For instance, from our TODO example, this object might look like:

\n
const reducer = createReducer({\n  [types.CREATE]: (state, {payload}) => ({\n    ...state,\n    todos: state.todos.concat(payload)\n  })\n});\n
\n

The previous object defines a handler for the types.CREATE action type, but does not define one for the types.FETCH_ALL. When the types.CREATE type is dispatched, the function above runs and is considered the reducer for the action. In this example, when the types.FETCH_ALL action is dispatched, the default handler: (state, action) => state is called (aka the original state).

\n

To add handlers, we only need to define the key and value function.

\n

API handling

\n

The power of redux-modules really comes into play when dealing with async code. The common pattern of handling async API calls, which generates multiple states.

\n

Notice in our above example we can mark types as api: true within a type object and notice that it created 4 action key/values in the object which correspond to different states of an api call (loading, success, error, and the type itself). We can use this pattern to provide simple api handling to any action/reducer.

\n

apiMiddleware

\n

In order to set this up, we need to add a middleware to our redux stack. This middleware helps us define defaults for handling API requests. For instance. In addition, redux-modules depends upon redux-thunk being available as a middleware after the apiMiddleware.

\n

For instance:

\n
// ...\nlet todoApp = combineReducers(reducers)\nlet apiMiddleware = createApiMiddleware({\n                      baseUrl: `https://fullstackreact.com`,\n                      headers: {}\n                    });\nlet store = createStore(reducers,\n            applyMiddleware(apiMiddleware, thunk));\n// ...\n
\n

The object the createApiMiddleware() accepts is the default configuration for all API requests. For instance, this is a good spot to add custom headers, a baseUrl (required), etc. Whatever we pass in here is accessible across every api client.

\n

For more dynamic requests, we can pass a function into any one of these options and it will be called with the state so we can dynamically respond. An instance where we might want to pass a function would be with our headers, which might respond with a token for every request. Another might be a case for A/B testing where we can dynamically assign the baseUrl on a per-user basis.

\n
// dynamically adding headers for _every_ request:\nlet apiMiddleware = createApiMiddleware({\n                      baseUrl: `https://fullstackreact.com`,\n                      headers: (state) => ({\n                        'X-Auth-Token': state.user.token\n                      })\n                    });\n
\n

The apiMiddleware above currently decorates an actions meta object. For instance:

\n
{\n  type: 'API_FETCH_ALL',\n  payload: {},\n  meta: {isApi: true}\n}\n// passes to the `thunk` middleware the resulting action:\n{\n  type: 'API_FETCH_ALL',\n  payload: {},\n  meta: {\n    isApi: true,\n    baseUrl: BASE_URL,\n    headers: {}\n  }\n}\n
\n

apiActions

\n

The easiest way to take advantage of this api infrastructure is to decorate the actions with the @api decorator (or the non-decorator form createApiAction()). When calling an api action created with the @api/createApiAction, redux-modules will dispatch two actions, the loading action and the handler.

\n

The loading action is fired with the type [NAMESPACE]_loading. The second action it dispatches is the handler function. The method that it is decorated with is expected to return a promise (although redux-modules will convert the response to a promise if it's not already one) which is expected to resolve in the case of success and error otherwise.

\n

More conveniently, redux-modules provides a client instance of ApiClient for every request (which is a thin wrapper around fetch).

\n
// actions\n@api(types.FETCH_ALL)\nfetchAll: (client, opts) => {\n  return client.get({\n    path: '/news'\n  }).then((json) => {\n    return json;\n  });\n}\n
\n

The apiClient includes handling for request transformations, status checking, and response transformations. We'll look at those in a minute. The apiClient instance includes the HTTP methods:

\n
    \n
  • GET
  • \n
  • POST
  • \n
  • PUT
  • \n
  • PATCH
  • \n
  • DELETE
  • \n
  • HEAD
  • \n
\n

These methods can be called on the client itself:

\n
client.get({})\nclient.post({})\nclient.put({})\n
\n

When they are called, they will be passed a list of options, which includes the base options (passed by the middleware) combined with custom options passed to the client (as the first argument).

\n

api client options

\n

The apiClient methods accept an argument of options for a per-api request customization. The following options are available and each can be either an atomic value or a function which gets called with the api request options within the client itself or in the baseOpts of the middleware.

\n

When defining these options in the middleware, keep in mind that they will be available for every request passed by the client instance.

\n
    \n
  1. path
  2. \n
\n

If a path option is found, apiClient will append the path to the baseUrl. If the client is called with a single string argument, then it is considered the path. For instance:

\n
// the following are equivalent\nclient.get('/foo')\nclient.get({path: '/foo'})\n// each results in a GET request to [baseUrl]/foo\n
\n
    \n
  1. url
  2. \n
\n

To completely ignore the baseUrl for a request, we can pass the url option which is used for the request.

\n
client.get({url: 'http://google.com/?q=fullstackreact'})\n
\n
    \n
  1. appendPath
  2. \n
\n

For dynamic calls, sometimes it's convenient to add a component to the path. For instance, we might want to append the url with a custom session key.

\n
client.get({appendPath: 'abc123'})\n
\n
    \n
  1. appendExt
  2. \n
\n

The appendExt is primarily useful for padding extensions on a url. For instance, to make all requests to the url with the .json extension, we can pass the appendExt to json:

\n
client.get({appendExt: 'json'})\n
\n
    \n
  1. params
  2. \n
\n

The params option is a query-string list of parameters that will be passed as query-string options.

\n
client.get({params: {q: 'fullstackreact'}})\n
\n
    \n
  1. headers
  2. \n
\n

Every request can define their own headers (or globally with the middleware) by using the headers option:

\n
client.get({headers: {'X-Token': 'bearer someTokenThing'}})\n
\n

transforms

\n

The request and response transforms provide a way to manipulate requests as they go out and and they return. These are functions that are called with the state as well as the current request options.

\n

requestTransforms

\n

Request transforms are functions that can be defined to create a dynamic way to manipulate headers, body, etc. For instance, if we want to create a protected route, we can use a requestTransform to append a custom header.

\n
client.get({\n  requestTransforms: [(state, opts) => req => {\n    req.headers['X-Name'] = 'Ari';\n    return req;\n  }]\n})\n
\n

responseTransforms

\n

A response transform handles the resulting request response and gives us an opportunity to transform the data to another format on the way in. The default response transform is to respond with the response body into json. To handle the actual response, we can assign a responseTransform to overwrite the default json parsing and get a handle on the actual fetch response.

\n
let timeTransform = (state, opts) => res => {\n  res.headers.set('X-Response-Time', time);\n  return res;\n}\nlet jsonTransform = (state, opts) => (res) => {\n  let time = res.headers.get('X-Response-Time');\n  return res.json().then(json => ({...json, time}))\n}\nclient.get({\n  responseTransforms: [timeTransform, jsonTransform]\n})\n
\n

For apis that do not respond with json, the responseTransforms are a good spot to handle conversion to another format, such as xml.

\n

apiHandlers

\n

To handle api responses in a reducer, redux-modules provides the apiHandler decorator (and it's non-decorator form: createApiHandler()). This decorator provides a common interface for handling the different states of an api request (i.e. loading, success, and error states).

\n
@apiHandler(types.FETCH_ALL)\nhandleFetchAll: (state, {payload}) => {...state, ...payload}\n
\n

The decorated function is considered the success function handler and will be called upon a success status code returned from the request.

\n

To handle custom loading states, we can "hook" into them with a second argument. The second argument is a function that's called with the dynamic states provided by the argument it's called with. For instance, to handle custom handling of an error state:

\n
@apiHandler(types.FETCH_ALL, (apiTypes) => ({\n  [apiTypes.ERROR]: (state, {payload}) => ({\n    ...state,\n    error: payload.body.message,\n    loading: false\n  })\n}))\nhandleFetchAll: (state, {payload}) => {...state, ...payload}\n
\n

Usage with react-redux

\n

There are multiple methods for combining redux-modules with react and this is our opinion about how to use the two together.

\n

First, our directory structure generally sets all of our modules in their own directory:

\n
index.js\n  /redux\n    /modules/\n      todo.js\n      users.js\n    configureStore.js\n    rootReducer.js\n    index.js\n
\n

Configuring the store for our app is straight-forward. First, we'll apply the createApiMiddleware() before we create the final store. In a configureStore.js file, we like to handle creating a store in a single spot. We'll export a function to configure the store:

\n
import {rootReducer, actions} from './rootReducer'\n\nexport const configureStore = ({initialState = {}}) => {\n  let middleware = [\n    createApiMiddleware({\n      baseUrl: BASE_URL\n    }),\n    thunkMiddleware\n  ]\n  // ...\n  const finalCreateStore =\n        compose(applyMiddleware(...middleware))(createStore);\n\n  const store = finalCreateStore(rootReducer, initialState);\n  // ...\n}\n
\n

This creates the middleware for us. Next, we like to combine our actions into a single actions object that we'll pass along down through our components. We'll use the bindActionCreatorsToStore() export to build our action creators and bind them to the store.

\n

We'll need to bind our actions to the store, so that when we call dispatch it will use our store's dispatch (see react-redux). Just after we create the store, we'll:

\n
let actions = bindActionCreatorsToStore(actions, store);\n
\n

From here, we just return the store and actions from the function:

\n
export const configureStore = ({initialState = {}}) => {\n  // ...\n  const store = finalCreateStore(rootReducer, initialState);\n  // ...\n  let actions = bindActionCreatorsToStore(actions, store);\n\n  return {store, actions};\n}\n
\n

Now that the heavy-lifting is done, the rootReducer.js file is pretty simple. We export all the actions and reducers pretty simply:

\n
const containers = ['users', 'todos'];\n\nexport const reducers = {}\nexport const actions = {};\n\ncontainers.forEach(k => {\n  let val = require(`./modules/${v}`);\n  reducers[k] = val.reducer;\n  actions[k] = val.actions || {};\n});\n\nexport const rootReducer = combineReducers(reducers);\n
\n

From here, our main container can pass the store and actions as props to our components:

\n
const {store, actions} = configureStore({initialState});\n// ...\nReactDOM.render(\n  <Container store={store}, actions={actions} />,\n  node);\n
\n

Now, anywhere in our code, we can refer to the actions we export in our modules by their namespace. For instance, to call the createTodo() function, we can reference it by the prop namespace:

\n
class Container extends React.Component {\n\n  createTodo() {\n    const {actions} = this.props;\n    // form: actions.[namespace].[actionName]();\n    actions.todos.createTodo("Finish this text");\n  }\n\n  render() {\n    return (\n      <div onClick={this.createTodo.bind(this)}>\n        Create todo\n      </div>\n    )\n  }\n}\n
\n

Combining usage with ducks-modular-redux

\n

redux-modules plays nicely with other redux packages as well. For instance, the ducks-modular-redux package defines a specific method of handling actions, reducers, and types.

\n

To create types in the same way, we can use the separator and prefix options in createConstants(). For instance, to create the constants defined by ducks-modular-redux's README':

\n
const LOAD   = 'my-app/widgets/LOAD';\nconst CREATE = 'my-app/widgets/CREATE';\nconst UPDATE = 'my-app/widgets/UPDATE';\nconst REMOVE = 'my-app/widgets/REMOVE';\n// In redux-modules:\nconst types = createConstants({\n  prefix: ['my-app', 'widgets'],\n  separator: '/'\n})('LOAD', 'CREATE', 'UPDATE', 'REMOVE')\n
\n

Handling the reducer function is similarly easy as well:

\n
export default function reducer(state = {}, action = {}) {\n  switch (action.type) {\n    // do reducer stuff\n    default: return state;\n  }\n}\nconst reducer = createReducer({\n  [types.LOAD]: (state, {payload}) => ({\n    ...state,\n    todos: state.todos.concat(payload)\n  }),\n  // ...\n});\n
\n

Finally, exporting functions individually from the file is directly supported. The createActions() and createApiAction() helpers can be used directly on created functions.

\n

All exports

\n

The redux-modules comprises the following exports:

\n

createConstants

\n

createConstants() creates an object to handle creating an object of type constants. It allows for multiple types to be dynamically created with their own custom prefixing, created on a single object.

\n
const types = createConstants({})('DOG', 'CAT');\n
\n

Options:

\n
    \n
  • prefix (string/array, default: '')
  • \n
\n

The prefix option creates each type with a predefined prefix.

\n
const types = createConstants({\n  prefix: 'animals'\n})('DOG', 'CAT')\nexpect(types.DOG).to.eql('ANIMALS_DOG');\nexpect(types.CAT).to.eql('ANIMALS_CAT');\n\nconst types = createConstants({\n  prefix: ['test', 'animals']\n})('DOG', 'CAT')\nexpect(types.DOG).to.eql('TEST_ANIMALS_DOG');\nexpect(types.CAT).to.eql('TEST_ANIMALS_CAT');\n
\n
    \n
  • separator (string, default: _)
  • \n
\n

The separator option allows us to change the way prefixes are concatenated. To change the separator to use a /, add the separator option:

\n
const types = createConstants({\n  separator: '/',\n  prefix: ['test', 'animals']\n})('DOG', 'CAT')\nexpect(types.DOG).to.eql('TEST/ANIMALS/DOG');\nexpect(types.CAT).to.eql('TEST/ANIMALS/CAT');\n
\n
    \n
  • initialObject (object, default: {})
  • \n
\n

For the case where you want to define types on an existing object, createConstants() accepts an initialObject to add the types. This allows us to create a single global object (for instance) to define all of our types.

\n
const types = createConstants({\n  initialObject: types\n})('OTHER', 'CONSTANTS');\n
\n

createReducer

\n

The createReducer() function returns a function that acts similar to the switch-case functionality of redux where we'll define the types and the reducers that handle the types.

\n
const reducer = createReducer({\n  [types.CREATE]: (state, {payload}) => ({\n    ...state,\n    todos: state.todos.concat(payload)\n  })\n});\n
\n

bindActionCreatorsToStore

\n

The bindActionCreatorsToStore() function accepts two arguments, the action handler object and the store object. It takes each action, binds the function to the store object (so this refers to the store) and then calls the bindActionCreators() redux function to the store object. Use the returned value as the reducer object.

\n
let actions = bindActionCreatorsToStore(actions, store);\n
\n

createApiMiddleware

\n

In order to set global options for every api request, we have to include a middleware in our stack. Creating the middleware for our redux stack uses createApiMiddleware() and essentially looks for any api action (with meta.isApi set to true) and merges global options into the meta key of the action.

\n

We must set the baseUrl in the middleware, which is used as the default url to make a request against. Without the baseUrl, all requests will be sent without an http component (unless set otherwise in the apiClient):

\n
let apiMiddleware = createApiMiddleware({\n                      baseUrl: `https://fullstackreact.com`,\n                      headers: {\n                        'Accept': 'application/json'\n                      }\n                    });\n
\n

apiClient

\n

The apiClient is a loose wrapper around the native html5 fetch() function (built around isomorphic-fetch, which makes testing easier). When an action is marked as an API action, it will be called with an instance of the apiClient as well as the options fetch() will be called with. This gives us an easy, flexible way to make API requests.

\n

The apiClient instance creates methods for each HTTP method, which accepts custom parameters to make the requests. It handles building the request, the options, putting together the url, packaging the body, request and response transformations, and more.

\n

Using the apiClient instance inside of an api action request looks like:

\n
@api(types.FETCH_ALL)\nfetchAll: (client, opts) => client.get({path: '/todos'})\n// or non-decorator version\nlet decoratedFetchall = createApiAction(types.FETCH_ALL)(function(client, opts) {\n  return client.get({path: '/todos'})\n});\n
\n

By default, the request is assumed to be in json format, but this is flexible and can be manipulated on a global/per-request level.

\n

Every option that the apiMiddleware and apiClient.[method] accepts can be either an atomic value or it can be a function. If a function is passed, it will be called at runtime with the current options and state to allow for dynamic responses based upon the state.

\n

The available options for both apiMiddleware and client method requests are here.

\n

createApiAction/@api

\n

To create an api action creator, we can decorate it with the @api decorator (when defined inline in an object) or using the createApiAction() function. Using this decorator, the function itself will be used to fetch an api.

\n

The decorated function is expected to use the client, although it is not required. It is expected that the decorated function returns a value, either a promise or an atomic value.

\n
{\n  actions: {\n    @api(types.FETCH_ALL)\n    fetchAll: (client, opts) => client.get({path: '/todos'})\n  }\n}\n// OR non-decorator version\nconst fetchAll = createApiAction(types.FETCH_ALL)(\n  (client, opts) => client.get('/todos'));\n
\n

Using the decorator will dispatch actions according to their response status, first dispatching the loading type (i.e. {type: 'API_FETCH_ALL_LOADING', opts}), then it calls the handler. Once the handler returns, the corresponding action _SUCCESS or _ERROR action types are dispatched.

\n

createApiHandler/@apiHandler

\n

In order to handle the response from an api request, we need to create a reducer. The api decorator fires the status values for the state of the api request. Using the createApiHandler()/@apiHandler decorator sets up default handlers for dealing with these responses.

\n
{\n  reducers: {\n    @apiHandler(types.FETCH_ALL)\n    handleFetchAll: (state, action) => ({...state, ...action.payload});\n  }\n}\n// or non-decorator version\nconst handlers = createApiHandler(types.FETCH_ALL) => {})((state, action) => {\n  return {\n    ...state,\n    ...action.payload\n  }  \n});\n
\n

The default actions will set the loading flag to true when the _LOADING action type is called, while the loading flag (in the state) will be set to false for the _ERROR and _SUCCESS types.

\n

For the case where we want to handle the states in a custom way, we can pass a second argument as a function which is called with the api states object where we can set custom handlers. For instance, to handle loading and errors in our previous example:

\n
{\n  reducers: {\n    @apiHandler(types.FETCH_ALL, (apiStates) => {\n      [apiStates.loading]: (state, action) => ({...state, loading: true}),\n      [apiStates.error]: (state, action) => {\n        return ({\n          ...state,\n          error: action.payload\n        })\n      }\n    })\n    handleFetchAll: (state, action) => ({...state, ...action.payload});\n  }\n}\n
\n

The decorated function is considered the success handler.

\n

TODOS:

\n

[] Use the custom type creator in createConstants() for defining apis\n[] Add docs for createRootReducer()

\n

Contributing

\n
git clone https://github.com/fullstackreact/redux-modules.git\ncd redux-modules\nnpm install\nmake dev\n
\n

To run the tests (please ensure the tests pass when creating a pull request):

\n
make test # or npm run test\n
\n
\n

Fullstack React Book

\n

\nFullstack React Book\n

\n

This repo was written and is maintained by the Fullstack React team. In the book we cover many more projects like this. We walk through each line of code, explain why it's there and how it works.

\n

This app is only one of several apps we have in the book. If you're looking to learn React, there's no faster way than by spending a few hours with the Fullstack React book.

\n
\n\n

License

\n

MIT

\n' +}},function(e,t){function n(){d&&c&&(d=!1,c.length?u=c.concat(u):p=-1,u.length&&r())}function r(){if(!d){var e=i(n);d=!0;for(var t=u.length;t;){for(c=u,u=[];++p1)for(var n=1;n=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}Object.defineProperty(t,"__esModule",{value:!0});var o=Object.assign||function(e){for(var t=1;t")},m=function(e){var t=e.tagName,n=e.style;return s["default"].createElement("span",{style:(0,o["default"])({},d["default"].htmlCloseTag,n)},"")},f={1:"ELEMENT_NODE",3:"TEXT_NODE",7:"PROCESSING_INSTRUCTION_NODE",8:"COMMENT_NODE",9:"DOCUMENT_NODE",10:"DOCUMENT_TYPE_NODE",11:"DOCUMENT_FRAGMENT_NODE"},g=function(e){var t=e.isCloseTag,n=(e.name,e.data),r=e.expanded;if(t)return s["default"].createElement(m,{style:{marginLeft:-12},tagName:n.tagName});switch(n.nodeType){case Node.ELEMENT_NODE:return s["default"].createElement("span",null,s["default"].createElement(p,{tagName:n.tagName,attributes:n.attributes}),(0,c["default"])(n)?n.textContent:!r&&"…",!r&&s["default"].createElement(m,{tagName:n.tagName}));case Node.TEXT_NODE:return s["default"].createElement("span",null,n.textContent);case Node.COMMENT_NODE:return s["default"].createElement("span",{style:d["default"].htmlComment},"");case Node.PROCESSING_INSTRUCTION_NODE:return s["default"].createElement("span",null,n.nodeName);case Node.DOCUMENT_TYPE_NODE:return s["default"].createElement("span",{style:d["default"].htmlDoctype},"");case Node.DOCUMENT_NODE:return s["default"].createElement("span",null,n.nodeName);case Node.DOCUMENT_FRAGMENT_NODE:return s["default"].createElement("span",null,n.nodeName);default:return s["default"].createElement("span",null,f[n.nodeType])}};g.propTypes={isCloseTag:i.PropTypes.bool,name:i.PropTypes.string,data:i.PropTypes.object.isRequired,expanded:i.PropTypes.bool.isRequired},t["default"]=g},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0}),t.Inspector=t.DOMInspector=t.TableInspector=t.ObjectInspector=void 0;var a=n(25),o=r(a),i=n(112),s=r(i),l=n(4),c=r(l),u=n(447),d=r(u),p=n(451),m=r(p),f=n(444),g=r(f),_=n(436),h=r(_);t.ObjectInspector=d["default"],t.TableInspector=m["default"],t.DOMInspector=g["default"];var b=function(e){var t=e.table,n=void 0!==t&&t,r=e.data,a=(0,s["default"])(e,["table","data"]);return n?c["default"].createElement(m["default"],(0,o["default"])({data:r},a)):(0,h["default"])(r)?c["default"].createElement(g["default"],(0,o["default"])({data:r},a)):c["default"].createElement(d["default"],(0,o["default"])({data:r},a))};b.propTypes={data:c["default"].PropTypes.any.isRequired,name:c["default"].PropTypes.string,path:c["default"].PropTypes.oneOfType([c["default"].PropTypes.string,c["default"].PropTypes.array]),table:c["default"].PropTypes.bool},t.Inspector=b,t["default"]=b},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(25),o=r(a),i=n(41),s=r(i),l=n(42),c=r(l),u=n(44),d=r(u),p=n(43),m=r(p),f=n(113),g=r(f),_=n(203),h=r(_),b=n(26),v=r(b),y=n(4),E=r(y),C=n(141),S=r(C),T=n(449),x=r(T),N=n(448),w=r(N),M=function(e){var t=g["default"].mark(function n(t){var r,a,o,i,s,l,c,u,d,p,m,f,_,b,y,E,C,S,T;return g["default"].wrap(function(n){for(;;)switch(n.prev=n.next){case 0:if(r="object"===("undefined"==typeof t?"undefined":(0,v["default"])(t))&&null!==t||"function"==typeof t){n.next=3;break}return n.abrupt("return");case 3:if(Array.isArray(t)||!t[Symbol.iterator]){n.next=42;break}a=0,o=!0,i=!1,s=void 0,n.prev=8,l=t[Symbol.iterator]();case 10:if(o=(c=l.next()).done){n.next=26;break}if(u=c.value,!Array.isArray(u)||2!==u.length){n.next=20;break}return d=(0,h["default"])(u,2),p=d[0],m=d[1],n.next=18,{name:p,data:m};case 18:n.next=22;break;case 20:return n.next=22,{name:a.toString(),data:u};case 22:a++;case 23:o=!0,n.next=10;break;case 26:n.next=32;break;case 28:n.prev=28,n.t0=n["catch"](8),i=!0,s=n.t0;case 32:n.prev=32,n.prev=33,!o&&l["return"]&&l["return"]();case 35:if(n.prev=35,!i){n.next=38;break}throw s;case 38:return n.finish(35);case 39:return n.finish(32);case 40:n.next=81;break;case 42:f=!0,_=!1,b=void 0,n.prev=45,y=Object.getOwnPropertyNames(t)[Symbol.iterator]();case 47:if(f=(E=y.next()).done){n.next=64;break}if(C=E.value,!t.propertyIsEnumerable(C)){n.next=55;break}return S=t[C],n.next=53,{name:C,data:S};case 53:n.next=61;break;case 55:if(!e){n.next=61;break}T=void 0;try{T=t[C]}catch(g){}if(void 0===T){n.next=61;break}return n.next=61,{name:C,data:T,isNonenumerable:!0};case 61:f=!0,n.next=47;break;case 64:n.next=70;break;case 66:n.prev=66,n.t1=n["catch"](45),_=!0,b=n.t1;case 70:n.prev=70,n.prev=71,!f&&y["return"]&&y["return"]();case 73:if(n.prev=73,!_){n.next=76;break}throw b;case 76:return n.finish(73);case 77:return n.finish(70);case 78:if(!e||t===Object.prototype){n.next=81;break}return n.next=81,{name:"__proto__",data:Object.getPrototypeOf(t),isNonenumerable:!0};case 81:case"end":return n.stop()}},n,this,[[8,28,32,40],[33,,35,39],[45,66,70,78],[71,,73,77]])});return t},O=function(e){var t=e.depth,n=e.name,r=e.data,a=e.isNonenumerable;return 0===t?E["default"].createElement(x["default"],{name:n,data:r}):E["default"].createElement(w["default"],{name:n,data:r,isNonenumerable:a})},D=function(e){function t(){return(0,s["default"])(this,t),(0,d["default"])(this,Object.getPrototypeOf(t).apply(this,arguments))}return(0,m["default"])(t,e),(0,c["default"])(t,[{key:"render",value:function(){var e=this.props.showNonenumerable,t=M(e);return E["default"].createElement(S["default"],(0,o["default"])({nodeRenderer:O,dataIterator:t},this.props))}}]),t}(y.Component);D.defaultProps={showNonenumerable:!1},D.propTypes={expandLevel:y.PropTypes.number,expandPaths:y.PropTypes.oneOfType([y.PropTypes.string,y.PropTypes.array]),name:y.PropTypes.string,data:y.PropTypes.any,showNonenumerable:y.PropTypes.bool},t["default"]=D},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(4),o=r(a),i=n(138),s=r(i),l=n(90),c=r(l),u=function(){return o["default"].createElement("span",null,": ")},d=function(e){var t=e.name,n=e.data,r=e.isNonenumerable,a=n;return o["default"].createElement("span",null,o["default"].createElement(s["default"],{name:t,dimmed:r}),o["default"].createElement(u,null),o["default"].createElement(c["default"],{object:a}))};d.propTypes={isNonenumerable:a.PropTypes.bool},d.defaultProps={isNonenumerable:!1},t["default"]=d},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function a(e,t){return 0===e.length?[]:e.slice(1).reduce(function(e,n){return e.concat([t,n])},[e[0]])}Object.defineProperty(t,"__esModule",{value:!0});var o=n(26),i=r(o),s=n(4),l=r(s),c=n(90),u=r(c),d=n(138),p=r(d),m={preview:{fontStyle:"italic"}},f=function(e){var t=(e.name,e.data),n=e.maxProperties,r=t;if("object"!==("undefined"==typeof r?"undefined":(0,i["default"])(r))||null===r||r instanceof Date||r instanceof RegExp)return l["default"].createElement(u["default"],{object:r});if(Array.isArray(r))return l["default"].createElement("span",{style:m.preview},"[",a(r.map(function(e,t){return l["default"].createElement(u["default"],{key:t,object:e})}),", "),"]");var o=[];for(var s in r){var c=r[s];if(r.hasOwnProperty(s)){var d=void 0;if(o.length===n-1&&Object.keys(r).length>n&&(d=l["default"].createElement("span",{key:"ellipsis"},"…")),o.push(l["default"].createElement("span",{key:s},l["default"].createElement(p["default"],{name:s}),": ",l["default"].createElement(u["default"],{object:c}),d)),d)break}}return l["default"].createElement("span",{style:m.preview},r.constructor.name+" {",a(o,", "),"}")};f.propTypes={maxProperties:s.PropTypes.number},f.defaultProps={maxProperties:5},t["default"]=f},function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={htmlTag:{color:"rgb(168, 148, 166)"},htmlTagName:{color:"rgb(136, 18, 128)",textTransform:"lowercase"},htmlCloseTag:{color:"rgb(168, 148, 166)"},htmlCloseTagName:{color:"rgb(136, 18, 128)",textTransform:"lowercase"},htmlAttributeName:{color:"rgb(153, 69, 0)"},htmlAttributeValue:{color:"rgb(26, 26, 166)"},htmlComment:{color:"rgb(35, 110, 37)"},htmlDoctype:{color:"rgb(192, 192, 192)"}};t["default"]=n},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(26),o=r(a),i=n(25),s=r(i),l=n(41),c=r(l),u=n(42),d=r(u),p=n(44),m=r(p),f=n(43),g=r(f),_=n(4),h=r(_),b=n(90),v=r(b),y=n(452),E=r(y),C=n(140),S=r(C),T={base:{position:"relative",border:"1px solid #aaa",fontFamily:"Menlo, monospace",fontSize:"11px",lineHeight:"120%",boxSizing:"border-box",cursor:"default"},th:{position:"relative",height:"auto",textAlign:"left",backgroundColor:"#eee",borderBottom:"1px solid #aaa",fontWeight:"normal",verticalAlign:"middle",padding:"0 4px",whiteSpace:"nowrap",textOverflow:"ellipsis",overflow:"hidden",lineHeight:"14px"},"th:hover":{backgroundColor:"hsla(0, 0%, 90%, 1)"},th_div:{whiteSpace:"nowrap",textOverflow:"ellipsis",overflow:"hidden",fontSize:"11px",lineHeight:"120%"},tr:{display:"table-row"},td:{boxSizing:"border-box",border:"none",height:"16px",verticalAlign:"top",padding:"1px 4px",WebkitUserSelect:"text",whiteSpace:"nowrap",textOverflow:"ellipsis",overflow:"hidden",lineHeight:"14px"},leftBorder:{none:{borderLeft:"none"},solid:{borderLeft:"1px solid #aaa"}}},x=function(e){return h["default"].createElement("div",{style:{position:"absolute",top:1,right:0,bottom:1,display:"flex",alignItems:"center"}},e.children)},N=function(e){var t=e.sortAscending,n=t?"▲":"▼";return h["default"].createElement("div",{style:Object.assign({display:"block",marginRight:3,width:8,height:7,marginTop:-7,color:"#6e6e6e",fontSize:12},S["default"])},n)},w=function(e){function t(e){(0,c["default"])(this,t);var n=(0,m["default"])(this,Object.getPrototypeOf(t).call(this,e));return n.state={hovered:!1},n}return(0,g["default"])(t,e),(0,d["default"])(t,[{key:"toggleHovered",value:function(e){this.setState({hovered:!this.state.hovered})}},{key:"render",value:function(){var e=this.props.sortAscending,t=this.props.sorted;return h["default"].createElement("th",(0,s["default"])({},this.props,{style:(0,s["default"])({},T.th,this.props.borderStyle,this.state.hovered?this.props.hoveredStyle:{}),onMouseEnter:this.toggleHovered.bind(this),onMouseLeave:this.toggleHovered.bind(this),onClick:this.props.onClick}),h["default"].createElement("div",{style:T.th_div},this.props.children),function(){if(t)return h["default"].createElement(x,null,h["default"].createElement(N,{sortAscending:e}))}())}}]),t}(_.Component);w.defaultProps={sortAscending:!1,sorted:!1,hoveredStyle:T["th:hover"],borderStyle:T.leftBorder.solid,onClick:void 0};var M=function(e){var t=e.indexColumnText,n=e.columns,r=e.sorted,a=e.sortIndexColumn,o=e.sortColumn,i=e.sortAscending,s=e.onTHClick,l=e.onIndexTHClick;return h["default"].createElement("div",{style:{top:0,height:"17px",left:0,right:0,overflowX:"hidden"}},h["default"].createElement("table",{style:{tableLayout:"fixed",borderSpacing:0,borderCollapse:"separate",height:"100%",width:"100%",margin:0}},h["default"].createElement("tbody",null,h["default"].createElement("tr",null,h["default"].createElement(w,{borderStyle:T.leftBorder.none,sorted:r&&a,sortAscending:i,onClick:l},t),n.map(function(e){return h["default"].createElement(w,{key:e,sorted:r&&o===e,sortAscending:i,onClick:s.bind(void 0,e)},e)})))))};M.defaultProps={indexColumnText:"(index)",columns:[]};var O=function(e){var t=e.rows,n=e.columns,r=e.rowsData;return h["default"].createElement("div",{style:{position:"static",top:"17px",bottom:0,overflowY:"overlay",transform:"translateZ(0)",left:0,right:0,overflowX:"hidden"}},h["default"].createElement("table",{style:{positon:"static",left:0,top:0,right:0,bottom:0,borderTop:"0 none transparent",margin:0,backgroundImage:"linear-gradient(to bottom, white, white 50%, rgb(234, 243, 255) 50%, rgb(234, 243, 255))",backgroundSize:"128px 32px",tableLayout:"fixed",borderSpacing:0,borderCollapse:"separate",width:"100%",fontSize:"11px",lineHeight:"120%"}},h["default"].createElement("colgroup",null),h["default"].createElement("tbody",null,t.map(function(e,t){return h["default"].createElement("tr",{key:e,style:T.tr},h["default"].createElement("td",{style:(0,s["default"])({},T.td,T.leftBorder.none)},e),n.map(function(e){var n=r[t];return"object"===("undefined"==typeof n?"undefined":(0,o["default"])(n))&&null!==n&&n.hasOwnProperty(e)?h["default"].createElement("td",{key:e,style:(0,s["default"])({},T.td,T.leftBorder.solid)},h["default"].createElement(v["default"],{object:n[e]})):h["default"].createElement("td",{key:e,style:(0,s["default"])({},T.td,T.leftBorder.solid)})}))}))))},D=function(e){function t(e){(0,c["default"])(this,t);var n=(0,m["default"])(this,Object.getPrototypeOf(t).call(this,e));return n.state={sorted:!1,sortIndexColumn:!1,sortColumn:void 0,sortAscending:!1},n}return(0,g["default"])(t,e),(0,d["default"])(t,[{key:"handleIndexTHClick",value:function(){this.setState({sorted:!0,sortIndexColumn:!0,sortColumn:void 0,sortAscending:!this.state.sortIndexColumn||!this.state.sortAscending})}},{key:"handleTHClick",value:function(e){this.setState({sorted:!0,sortIndexColumn:!1,sortColumn:e,sortAscending:e!==this.state.sortColumn||!this.state.sortAscending})}},{key:"render",value:function(){var e=this.props.data,t=this.props.columns;if("object"!==("undefined"==typeof e?"undefined":(0,o["default"])(e))||null===e)return h["default"].createElement("div",null);var n=(0,E["default"])(e),r=n.rowHeaders,a=n.colHeaders;void 0!==t&&(a=t);var i=r.map(function(t){return e[t]}),s=(this.state.sorted,this.state.sortIndexColumn),l=this.state.sortColumn,c=this.state.sortAscending,u=void 0;if(void 0!==l?u=i.map(function(e,t){if("object"===("undefined"==typeof e?"undefined":(0,o["default"])(e))&&null!==e){var n=e[l];return[n,t]}return[void 0,t]}):s&&(u=r.map(function(e,t){var n=r[t];return[n,t]})),void 0!==u){var d=function(e,t){return function(n,r){var a=e(n),i=e(r),s="undefined"==typeof a?"undefined":(0,o["default"])(a),l="undefined"==typeof i?"undefined":(0,o["default"])(i),c=function(e,t){return et?1:0},u=void 0;if(s===l)u=c(a,i);else{var d={string:0,number:1,object:2,symbol:3,"boolean":4,undefined:5,"function":6};u=c(d[s],d[l])}return t||(u=-u),u}},p=u.sort(d(function(e){return e[0]},c)).map(function(e){return e[1]});r=p.map(function(e){return r[e]}),i=p.map(function(e){return i[e]})}return h["default"].createElement("div",{style:T.base},h["default"].createElement(M,{columns:a,sorted:this.state.sorted,sortIndexColumn:this.state.sortIndexColumn,sortColumn:this.state.sortColumn,sortAscending:this.state.sortAscending,onTHClick:this.handleTHClick.bind(this),onIndexTHClick:this.handleIndexTHClick.bind(this)}),h["default"].createElement(O,{rows:r,columns:a,rowsData:i}))}}]),t}(_.Component);t["default"]=D,D.propTypes={data:h["default"].PropTypes.oneOfType([h["default"].PropTypes.array,h["default"].PropTypes.object]),columns:h["default"].PropTypes.array},D.defaultProps={data:void 0,columns:void 0}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function a(e){if("object"===("undefined"==typeof e?"undefined":(0,l["default"])(e))){var t=void 0;if(Array.isArray(e)){var n=e.length;t=[].concat((0,i["default"])(Array(n).keys()))}else null!==e&&(t=Object.keys(e));var r=t.reduce(function(t,n){var r=e[n];if("object"===("undefined"==typeof r?"undefined":(0,l["default"])(r))&&null!==r){var a=Object.keys(r);a.reduce(function(e,t){return e.includes(t)||e.push(t),e},t)}return t},[]);return{rowHeaders:t,colHeaders:r}}}Object.defineProperty(t,"__esModule",{value:!0});var o=n(204),i=r(o),s=n(26),l=r(s);t["default"]=a,Array.prototype.includes||(Array.prototype.includes=function(e){var t=Object(this),n=parseInt(t.length)||0;if(0===n)return!1;var r,a=parseInt(arguments[1])||0;a>=0?r=a:(r=n+a,r<0&&(r=0));for(var o;r0?g["default"].createElement(v,{expanded:t}):l&&g["default"].createElement("span",{style:b.placeholder}," "),c),g["default"].createElement("ol",{role:"group",style:b.childNodesContainer},u))}}]),t}(f.Component);y.propTypes={name:f.PropTypes.string,data:f.PropTypes.any,expanded:f.PropTypes.bool,shouldShowArrow:f.PropTypes.bool,shouldShowPlaceholder:f.PropTypes.bool,nodeRenderer:f.PropTypes.func,onClick:f.PropTypes.func},y.defaultProps={name:"",data:void 0,expanded:!0,nodeRenderer:function(e){var t=e.name;e.data,e.expanded;return g["default"].createElement("span",null,t)},onClick:function(){},shouldShowArrow:!1,shouldShowPlaceholder:!0},t["default"]=y},function(e,t){"use strict";function n(e,t){return!t(e).next().done}Object.defineProperty(t,"__esModule",{value:!0}),t.hasChildNodes=n;var r=t.DEFAULT_ROOT_PATH="$",a="*",o=t.wildcardPathsFromLevel=function(e){return Array.from({length:e},function(e,t){return[r].concat(Array.from({length:t},function(e,t){return"*"})).join(".")})};t.getExpandedPaths=function(e,t,i,s){var l=arguments.length<=4||void 0===arguments[4]?{}:arguments[4],c=[].concat(o(s)).concat(i).filter(function(e){return"string"==typeof e}),u=[];return c.forEach(function(o){var i=o.split("."),s=function l(e,o,s){if(s===i.length)return void u.push(o);var c=i[s];if(0===s)!n(e,t)||c!==r&&c!==a||l(e,r,s+1);else if(c===a){var d=!0,p=!1,m=void 0;try{for(var f,g=t(e)[Symbol.iterator]();!(d=(f=g.next()).done);d=!0){var _=f.value,h=_.name,b=_.data;n(b,t)&&l(b,o+"."+h,s+1)}}catch(v){p=!0,m=v}finally{try{!d&&g["return"]&&g["return"]()}finally{if(p)throw m}}}else{var y=e[c];n(y,t)&&l(y,o+"."+c,s+1)}};s(e,"",0)}),u.reduce(function(e,t){return e[t]=!0,e},l)}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}t.__esModule=!0;var a=n(9),o=(r(a),n(33)),i={contextTypes:{history:o.history},componentWillMount:function(){this.history=this.context.history}};t["default"]=i,e.exports=t["default"]},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}t.__esModule=!0;var a=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function o(e){return!e||!e.__v2_compatible__}function i(e){return e&&e.getCurrentLocation}t.__esModule=!0;var s=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function o(e,t){var n=e.history,r=e.routes,o=e.location,s=a(e,["history","routes","location"]);n||o?void 0:(0,l["default"])(!1),n=n?n:(0,u["default"])(s);var c=(0,p["default"])(n,(0,m.createRoutes)(r)),d=void 0;o?o=n.createLocation(o):d=n.listen(function(e){o=e});var g=(0,f.createRouterObject)(n,c);n=(0,f.createRoutingHistory)(n,c),c.match(o,function(e,r,a){t(e,r,a&&i({},a,{history:n,router:g,matchContext:{history:n,transitionManager:c,router:g}})),d&&d()})}t.__esModule=!0;var i=Object.assign||function(e){for(var t=1;t=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function o(e){return function(){var t=arguments.length<=0||void 0===arguments[0]?{}:arguments[0],n=t.routes,r=a(t,["routes"]),o=(0,l["default"])(e)(r),s=(0,u["default"])(o,n);return i({},o,s)}}t.__esModule=!0;var i=Object.assign||function(e){for(var t=1;t8&&S<=11),N=32,w=String.fromCharCode(N),M=m.topLevelTypes,O={beforeInput:{phasedRegistrationNames:{bubbled:v({onBeforeInput:null}),captured:v({onBeforeInputCapture:null})},dependencies:[M.topCompositionEnd,M.topKeyPress,M.topTextInput,M.topPaste]},compositionEnd:{phasedRegistrationNames:{bubbled:v({onCompositionEnd:null}),captured:v({onCompositionEndCapture:null})},dependencies:[M.topBlur,M.topCompositionEnd,M.topKeyDown,M.topKeyPress,M.topKeyUp,M.topMouseDown]},compositionStart:{phasedRegistrationNames:{bubbled:v({onCompositionStart:null}),captured:v({onCompositionStartCapture:null})},dependencies:[M.topBlur,M.topCompositionStart,M.topKeyDown,M.topKeyPress,M.topKeyUp,M.topMouseDown]},compositionUpdate:{phasedRegistrationNames:{bubbled:v({onCompositionUpdate:null}),captured:v({onCompositionUpdateCapture:null})},dependencies:[M.topBlur,M.topCompositionUpdate,M.topKeyDown,M.topKeyPress,M.topKeyUp,M.topMouseDown]}},D=!1,A=null,R={eventTypes:O,extractEvents:function(e,t,n,r){return[c(e,t,n,r),p(e,t,n,r)]}};e.exports=R},function(e,t,n){"use strict";var r=n(150),a=n(7),o=(n(10),n(253),n(531)),i=n(259),s=n(263),l=(n(3),s(function(e){return i(e)})),c=!1,u="cssFloat";if(a.canUseDOM){var d=document.createElement("div").style;try{d.font=""}catch(p){c=!0}void 0===document.documentElement.style.cssFloat&&(u="styleFloat")}var m={createMarkupForStyles:function(e,t){var n="";for(var r in e)if(e.hasOwnProperty(r)){var a=e[r];null!=a&&(n+=l(r)+":",n+=o(r,a,t)+";")}return n||null},setValueForStyles:function(e,t,n){var a=e.style;for(var i in t)if(t.hasOwnProperty(i)){var s=o(i,t[i],n);if("float"!==i&&"cssFloat"!==i||(i=u),s)a[i]=s;else{var l=c&&r.shorthandPropertyExpansions[i];if(l)for(var d in l)a[d]="";else a[i]=""}}}};e.exports=m},function(e,t,n){"use strict";function r(e){var t=e.nodeName&&e.nodeName.toLowerCase();return"select"===t||"input"===t&&"file"===e.type}function a(e){var t=T.getPooled(D.change,R,e,x(e));y.accumulateTwoPhaseDispatches(t),S.batchedUpdates(o,t)}function o(e){v.enqueueEvents(e),v.processEventQueue(!1)}function i(e,t){A=e,R=t,A.attachEvent("onchange",a)}function s(){A&&(A.detachEvent("onchange",a),A=null,R=null)}function l(e,t){if(e===O.topChange)return t}function c(e,t,n){e===O.topFocus?(s(),i(t,n)):e===O.topBlur&&s()}function u(e,t){A=e,R=t,I=e.value,P=Object.getOwnPropertyDescriptor(e.constructor.prototype,"value"),Object.defineProperty(A,"value",F),A.attachEvent?A.attachEvent("onpropertychange",p):A.addEventListener("propertychange",p,!1)}function d(){A&&(delete A.value,A.detachEvent?A.detachEvent("onpropertychange",p):A.removeEventListener("propertychange",p,!1),A=null,R=null,I=null,P=null)}function p(e){if("value"===e.propertyName){var t=e.srcElement.value;t!==I&&(I=t,a(e))}}function m(e,t){if(e===O.topInput)return t}function f(e,t,n){e===O.topFocus?(d(),u(t,n)):e===O.topBlur&&d()}function g(e,t){if((e===O.topSelectionChange||e===O.topKeyUp||e===O.topKeyDown)&&A&&A.value!==I)return I=A.value,R}function _(e){return e.nodeName&&"input"===e.nodeName.toLowerCase()&&("checkbox"===e.type||"radio"===e.type)}function h(e,t){if(e===O.topClick)return t}var b=n(17),v=n(49),y=n(50),E=n(7),C=n(6),S=n(16),T=n(18),x=n(106),N=n(107),w=n(179),M=n(21),O=b.topLevelTypes,D={change:{phasedRegistrationNames:{bubbled:M({onChange:null}),captured:M({onChangeCapture:null})},dependencies:[O.topBlur,O.topChange,O.topClick,O.topFocus,O.topInput,O.topKeyDown,O.topKeyUp,O.topSelectionChange]}},A=null,R=null,I=null,P=null,k=!1;E.canUseDOM&&(k=N("change")&&(!("documentMode"in document)||document.documentMode>8));var L=!1;E.canUseDOM&&(L=N("input")&&(!("documentMode"in document)||document.documentMode>11));var F={get:function(){return P.get.call(this)},set:function(e){I=""+e,P.set.call(this,e)}},B={eventTypes:D,extractEvents:function(e,t,n,a){var o,i,s=t?C.getNodeFromInstance(t):window;if(r(s)?k?o=l:i=c:w(s)?L?o=m:(o=g,i=f):_(s)&&(o=h),o){var u=o(e,t);if(u){var d=T.getPooled(D.change,u,n,a);return d.type="change",y.accumulateTwoPhaseDispatches(d),d}}i&&i(e,s,t)}};e.exports=B},function(e,t,n){"use strict";function r(e){return e.substring(1,e.indexOf(" "))}var a=n(2),o=n(39),i=n(7),s=n(256),l=n(13),c=n(129),u=(n(1),/^(<[^ \/>]+)/),d="data-danger-index",p={dangerouslyRenderMarkup:function(e){i.canUseDOM?void 0:a("51");for(var t,n={},o=0;o1?1-t:void 0;return this._fallbackText=a.slice(e,s),this._fallbackText}}),o.addPoolingTo(r),e.exports=r},function(e,t,n){"use strict";var r=n(34),a=r.injection.MUST_USE_PROPERTY,o=r.injection.HAS_BOOLEAN_VALUE,i=r.injection.HAS_NUMERIC_VALUE,s=r.injection.HAS_POSITIVE_NUMERIC_VALUE,l=r.injection.HAS_OVERLOADED_BOOLEAN_VALUE,c={isCustomAttribute:RegExp.prototype.test.bind(new RegExp("^(data|aria)-["+r.ATTRIBUTE_NAME_CHAR+"]*$")),Properties:{accept:0,acceptCharset:0,accessKey:0,action:0,allowFullScreen:o,allowTransparency:0,alt:0,async:o,autoComplete:0,autoPlay:o,capture:o,cellPadding:0,cellSpacing:0,charSet:0,challenge:0,checked:a|o,cite:0,classID:0,className:0,cols:s,colSpan:0,content:0,contentEditable:0,contextMenu:0,controls:o,coords:0,crossOrigin:0,data:0,dateTime:0,"default":o,defer:o,dir:0,disabled:o,download:l,draggable:0,encType:0,form:0,formAction:0,formEncType:0,formMethod:0,formNoValidate:o,formTarget:0,frameBorder:0,headers:0,height:0,hidden:o,high:0,href:0,hrefLang:0,htmlFor:0,httpEquiv:0,icon:0,id:0,inputMode:0,integrity:0,is:0,keyParams:0,keyType:0,kind:0,label:0,lang:0,list:0,loop:o,low:0,manifest:0,marginHeight:0,marginWidth:0,max:0,maxLength:0,media:0,mediaGroup:0,method:0,min:0,minLength:0,multiple:a|o,muted:a|o,name:0,nonce:0,noValidate:o,open:o,optimum:0,pattern:0,placeholder:0,poster:0,preload:0,profile:0,radioGroup:0,readOnly:o,rel:0,required:o,reversed:o,role:0,rows:s,rowSpan:i,sandbox:0,scope:0,scoped:o,scrolling:0,seamless:o,selected:a|o,shape:0,size:s,sizes:0,span:s,spellCheck:0,src:0,srcDoc:0,srcLang:0,srcSet:0,start:i,step:0,style:0,summary:0,tabIndex:0,target:0,title:0,type:0,useMap:0,value:0,width:0,wmode:0,wrap:0,about:0,datatype:0,inlist:0,prefix:0,property:0,resource:0,"typeof":0,vocab:0,autoCapitalize:0,autoCorrect:0,autoSave:0,color:0,itemProp:0,itemScope:o,itemType:0,itemID:0,itemRef:0,results:0,security:0,unselectable:0},DOMAttributeNames:{acceptCharset:"accept-charset",className:"class",htmlFor:"for",httpEquiv:"http-equiv"},DOMPropertyNames:{}};e.exports=c},function(e,t,n){"use strict";var r=n(5),a=n(153),o=n(155),i=n(154),s=n(494),l=n(15),c=n(168),u=n(170),d=n(537),p=(n(3),l.createElement),m=l.createFactory,f=l.cloneElement,g=r,_={Children:{map:a.map,forEach:a.forEach,count:a.count,toArray:a.toArray,only:d},Component:o,createElement:p,cloneElement:f,isValidElement:l.isValidElement,PropTypes:c,createClass:i.createClass,createFactory:m,createMixin:function(e){return e},DOM:s,version:u,__spread:g};e.exports=_},function(e,t,n){"use strict";function r(e,t,n,r){var a=void 0===e[n];null!=t&&a&&(e[n]=o(t))}var a=n(40),o=(n(64),n(178)),i=(n(97),n(108)),s=n(109),l=(n(3),{instantiateChildren:function(e,t,n,a){if(null==e)return null;var o={};return s(e,r,o),o},updateChildren:function(e,t,n,r,s){if(t||e){var l,c;for(l in t)if(t.hasOwnProperty(l)){c=e&&e[l];var u=c&&c._currentElement,d=t[l];if(null!=c&&i(u,d))a.receiveComponent(c,d,r,s),t[l]=c;else{c&&(n[l]=a.getHostNode(c),a.unmountComponent(c,!1));var p=o(d);t[l]=p}}for(l in e)!e.hasOwnProperty(l)||t&&t.hasOwnProperty(l)||(c=e[l],n[l]=a.getHostNode(c),a.unmountComponent(c,!1))}},unmountChildren:function(e,t){for(var n in e)if(e.hasOwnProperty(n)){var r=e[n];a.unmountComponent(r,t)}}});e.exports=l},function(e,t,n){"use strict";function r(e){}function a(e,t){}function o(e){return e.prototype&&e.prototype.isReactComponent}var i=n(2),s=n(5),l=n(99),c=n(24),u=n(15),d=n(100),p=n(65),m=(n(10),n(166)),f=(n(102),n(40)),g=n(169),_=n(530),h=n(55),b=(n(1),n(108));n(3);r.prototype.render=function(){var e=p.get(this)._currentElement.type,t=e(this.props,this.context,this.updater);return a(e,t),t};var v=1,y={construct:function(e){this._currentElement=e,this._rootNodeID=null,this._instance=null,this._hostParent=null,this._hostContainerInfo=null,this._updateBatchNumber=null,this._pendingElement=null,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._renderedNodeType=null,this._renderedComponent=null,this._context=null,this._mountOrder=0,this._topLevelWrapper=null,this._pendingCallbacks=null,this._calledComponentWillUnmount=!1},mountComponent:function(e,t,n,s){this._context=s,this._mountOrder=v++,this._hostParent=t,this._hostContainerInfo=n;var l,c=this._currentElement.props,d=this._processContext(s),m=this._currentElement.type,f=this._constructComponent(c,d);o(m)||null!=f&&null!=f.render||(l=f,a(m,l),null===f||f===!1||u.isValidElement(f)?void 0:i("105",m.displayName||m.name||"Component"),f=new r(m));f.props=c,f.context=d,f.refs=h,f.updater=g,this._instance=f,p.set(f,this);var _=f.state;void 0===_&&(f.state=_=null),"object"!=typeof _||Array.isArray(_)?i("106",this.getName()||"ReactCompositeComponent"):void 0,this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1;var b;return b=f.unstable_handleError?this.performInitialMountWithErrorHandling(l,t,n,e,s):this.performInitialMount(l,t,n,e,s),f.componentDidMount&&e.getReactMountReady().enqueue(f.componentDidMount,f),b},_constructComponent:function(e,t){return this._constructComponentWithoutOwner(e,t)},_constructComponentWithoutOwner:function(e,t){var n,r=this._currentElement.type;return n=o(r)?new r(e,t,g):r(e,t,g)},performInitialMountWithErrorHandling:function(e,t,n,r,a){var o,i=r.checkpoint();try{o=this.performInitialMount(e,t,n,r,a)}catch(s){r.rollback(i),this._instance.unstable_handleError(s),this._pendingStateQueue&&(this._instance.state=this._processPendingState(this._instance.props,this._instance.context)),i=r.checkpoint(),this._renderedComponent.unmountComponent(!0),r.rollback(i),o=this.performInitialMount(e,t,n,r,a)}return o},performInitialMount:function(e,t,n,r,a){var o=this._instance;o.componentWillMount&&(o.componentWillMount(),this._pendingStateQueue&&(o.state=this._processPendingState(o.props,o.context))),void 0===e&&(e=this._renderValidatedComponent()),this._renderedNodeType=m.getType(e);var i=this._instantiateReactComponent(e);this._renderedComponent=i;var s=f.mountComponent(i,r,t,n,this._processChildContext(a));return s},getHostNode:function(){return f.getHostNode(this._renderedComponent)},unmountComponent:function(e){if(this._renderedComponent){var t=this._instance;if(t.componentWillUnmount&&!t._calledComponentWillUnmount)if(t._calledComponentWillUnmount=!0,e){var n=this.getName()+".componentWillUnmount()";d.invokeGuardedCallback(n,t.componentWillUnmount.bind(t))}else t.componentWillUnmount();this._renderedComponent&&(f.unmountComponent(this._renderedComponent,e),this._renderedNodeType=null,this._renderedComponent=null,this._instance=null),this._pendingStateQueue=null,this._pendingReplaceState=!1,this._pendingForceUpdate=!1,this._pendingCallbacks=null,this._pendingElement=null,this._context=null,this._rootNodeID=null,this._topLevelWrapper=null,p.remove(t)}},_maskContext:function(e){var t=this._currentElement.type,n=t.contextTypes;if(!n)return h;var r={};for(var a in n)r[a]=e[a];return r},_processContext:function(e){var t=this._maskContext(e);return t},_processChildContext:function(e){var t=this._currentElement.type,n=this._instance,r=n.getChildContext&&n.getChildContext();if(r){"object"!=typeof t.childContextTypes?i("107",this.getName()||"ReactCompositeComponent"):void 0;for(var a in r)a in t.childContextTypes?void 0:i("108",this.getName()||"ReactCompositeComponent",a);return s({},e,r)}return e},_checkContextTypes:function(e,t,n){_(e,t,n,this.getName(),null,this._debugID)},receiveComponent:function(e,t,n){var r=this._currentElement,a=this._context;this._pendingElement=null,this.updateComponent(t,r,e,a,n)},performUpdateIfNecessary:function(e){null!=this._pendingElement?f.receiveComponent(this,this._pendingElement,e,this._context):null!==this._pendingStateQueue||this._pendingForceUpdate?this.updateComponent(e,this._currentElement,this._currentElement,this._context,this._context):this._updateBatchNumber=null},updateComponent:function(e,t,n,r,a){var o=this._instance;null==o?i("136",this.getName()||"ReactCompositeComponent"):void 0;var s,l,c=!1;this._context===a?s=o.context:(s=this._processContext(a),c=!0),l=n.props,t!==n&&(c=!0),c&&o.componentWillReceiveProps&&o.componentWillReceiveProps(l,s);var u=this._processPendingState(l,s),d=!0;!this._pendingForceUpdate&&o.shouldComponentUpdate&&(d=o.shouldComponentUpdate(l,u,s)),this._updateBatchNumber=null,d?(this._pendingForceUpdate=!1,this._performComponentUpdate(n,l,u,s,e,a)):(this._currentElement=n,this._context=a,o.props=l,o.state=u,o.context=s)},_processPendingState:function(e,t){var n=this._instance,r=this._pendingStateQueue,a=this._pendingReplaceState;if(this._pendingReplaceState=!1,this._pendingStateQueue=null,!r)return n.state;if(a&&1===r.length)return r[0];for(var o=s({},a?r[0]:n.state),i=a?1:0;i=0||null!=t.is}function f(e){var t=e.type;p(t),this._currentElement=e,this._tag=t.toLowerCase(),this._namespaceURI=null,this._renderedChildren=null,this._previousStyle=null,this._previousStyleCopy=null,this._hostNode=null,this._hostParent=null,this._rootNodeID=null,this._domID=null,this._hostContainerInfo=null,this._wrapperState=null,this._topLevelWrapper=null,this._flags=0}var g=n(2),_=n(5),h=n(476),b=n(478),v=n(39),y=n(95),E=n(34),C=n(152),S=n(17),T=n(49),x=n(62),N=n(63),w=n(156),M=n(489),O=n(157),D=n(6),A=n(497),R=n(499),I=n(158),P=n(502),k=(n(10),n(511)),L=n(515),F=(n(13),n(68)),B=(n(1),n(107),n(21)),U=(n(130),n(110),n(3),O),G=T.deleteListener,q=D.getNodeFromInstance,H=N.listenTo,j=x.registrationNameModules,z={string:!0,number:!0},W=B({style:null}),V=B({__html:null}),$={children:null,dangerouslySetInnerHTML:null,suppressContentEditableWarning:null},K=11,Q={topAbort:"abort",topCanPlay:"canplay",topCanPlayThrough:"canplaythrough",topDurationChange:"durationchange",topEmptied:"emptied",topEncrypted:"encrypted",topEnded:"ended",topError:"error",topLoadedData:"loadeddata",topLoadedMetadata:"loadedmetadata",topLoadStart:"loadstart",topPause:"pause",topPlay:"play",topPlaying:"playing",topProgress:"progress",topRateChange:"ratechange",topSeeked:"seeked",topSeeking:"seeking",topStalled:"stalled",topSuspend:"suspend",topTimeUpdate:"timeupdate",topVolumeChange:"volumechange",topWaiting:"waiting"},Z={area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0},X={listing:!0,pre:!0,textarea:!0},Y=_({menuitem:!0},Z),J=/^[a-zA-Z][a-zA-Z:_\.\-\d]*$/,ee={},te={}.hasOwnProperty,ne=1;f.displayName="ReactDOMComponent",f.Mixin={mountComponent:function(e,t,n,r){this._rootNodeID=ne++,this._domID=n._idCounter++,this._hostParent=t,this._hostContainerInfo=n;var o=this._currentElement.props;switch(this._tag){case"audio":case"form":case"iframe":case"img":case"link":case"object":case"source":case"video":this._wrapperState={listeners:null},e.getReactMountReady().enqueue(u,this);break;case"button":o=M.getHostProps(this,o,t);break;case"input":A.mountWrapper(this,o,t),o=A.getHostProps(this,o),e.getReactMountReady().enqueue(u,this);break;case"option":R.mountWrapper(this,o,t),o=R.getHostProps(this,o);break;case"select":I.mountWrapper(this,o,t),o=I.getHostProps(this,o),e.getReactMountReady().enqueue(u,this);break;case"textarea":P.mountWrapper(this,o,t),o=P.getHostProps(this,o),e.getReactMountReady().enqueue(u,this)}a(this,o);var i,d;null!=t?(i=t._namespaceURI,d=t._tag):n._tag&&(i=n._namespaceURI,d=n._tag),(null==i||i===y.svg&&"foreignobject"===d)&&(i=y.html),i===y.html&&("svg"===this._tag?i=y.svg:"math"===this._tag&&(i=y.mathml)),this._namespaceURI=i;var p;if(e.useCreateElement){var m,f=n._ownerDocument;if(i===y.html)if("script"===this._tag){var g=f.createElement("div"),_=this._currentElement.type;g.innerHTML="<"+_+">",m=g.removeChild(g.firstChild)}else m=o.is?f.createElement(this._currentElement.type,o.is):f.createElement(this._currentElement.type);else m=f.createElementNS(i,this._currentElement.type);D.precacheNode(this,m),this._flags|=U.hasCachedChildNodes,this._hostParent||C.setAttributeForRoot(m),this._updateDOMProperties(null,o,e);var b=v(m);this._createInitialChildren(e,o,r,b),p=b}else{var E=this._createOpenTagMarkupAndPutListeners(e,o),S=this._createContentMarkup(e,o,r);p=!S&&Z[this._tag]?E+"/>":E+">"+S+""}switch(this._tag){case"input":e.getReactMountReady().enqueue(s,this),o.autoFocus&&e.getReactMountReady().enqueue(h.focusDOMComponent,this);break;case"textarea":e.getReactMountReady().enqueue(l,this),o.autoFocus&&e.getReactMountReady().enqueue(h.focusDOMComponent,this);break;case"select":o.autoFocus&&e.getReactMountReady().enqueue(h.focusDOMComponent,this);break;case"button":o.autoFocus&&e.getReactMountReady().enqueue(h.focusDOMComponent,this);break;case"option":e.getReactMountReady().enqueue(c,this)}return p},_createOpenTagMarkupAndPutListeners:function(e,t){var n="<"+this._currentElement.type;for(var r in t)if(t.hasOwnProperty(r)){var a=t[r];if(null!=a)if(j.hasOwnProperty(r))a&&o(this,r,a,e);else{r===W&&(a&&(a=this._previousStyleCopy=_({},t.style)),a=b.createMarkupForStyles(a,this));var i=null;null!=this._tag&&m(this._tag,t)?$.hasOwnProperty(r)||(i=C.createMarkupForCustomAttribute(r,a)):i=C.createMarkupForProperty(r,a),i&&(n+=" "+i)}}return e.renderToStaticMarkup?n:(this._hostParent||(n+=" "+C.createMarkupForRoot()),n+=" "+C.createMarkupForID(this._domID))},_createContentMarkup:function(e,t,n){var r="",a=t.dangerouslySetInnerHTML;if(null!=a)null!=a.__html&&(r=a.__html);else{var o=z[typeof t.children]?t.children:null,i=null!=o?null:t.children;if(null!=o)r=F(o);else if(null!=i){var s=this.mountChildren(i,e,n);r=s.join("")}}return X[this._tag]&&"\n"===r.charAt(0)?"\n"+r:r},_createInitialChildren:function(e,t,n,r){var a=t.dangerouslySetInnerHTML;if(null!=a)null!=a.__html&&v.queueHTML(r,a.__html);else{var o=z[typeof t.children]?t.children:null,i=null!=o?null:t.children;if(null!=o)v.queueText(r,o);else if(null!=i)for(var s=this.mountChildren(i,e,n),l=0;l"},receiveComponent:function(){},getHostNode:function(){return o.getNodeFromInstance(this)},unmountComponent:function(){o.uncacheNode(this)}}),e.exports=i},function(e,t,n){"use strict";function r(e){return a.createFactory(e)}var a=n(15),o=n(262),i=o({a:"a",abbr:"abbr",address:"address",area:"area",article:"article",aside:"aside",audio:"audio",b:"b",base:"base",bdi:"bdi",bdo:"bdo",big:"big",blockquote:"blockquote",body:"body",br:"br",button:"button",canvas:"canvas",caption:"caption",cite:"cite",code:"code",col:"col",colgroup:"colgroup",data:"data",datalist:"datalist",dd:"dd",del:"del",details:"details",dfn:"dfn",dialog:"dialog",div:"div",dl:"dl",dt:"dt",em:"em",embed:"embed",fieldset:"fieldset",figcaption:"figcaption",figure:"figure",footer:"footer",form:"form",h1:"h1",h2:"h2",h3:"h3",h4:"h4",h5:"h5",h6:"h6",head:"head",header:"header",hgroup:"hgroup",hr:"hr",html:"html",i:"i",iframe:"iframe",img:"img",input:"input",ins:"ins",kbd:"kbd",keygen:"keygen",label:"label",legend:"legend",li:"li",link:"link",main:"main",map:"map",mark:"mark",menu:"menu",menuitem:"menuitem",meta:"meta",meter:"meter",nav:"nav",noscript:"noscript",object:"object",ol:"ol",optgroup:"optgroup",option:"option",output:"output",p:"p",param:"param",picture:"picture",pre:"pre",progress:"progress",q:"q",rp:"rp",rt:"rt",ruby:"ruby",s:"s",samp:"samp",script:"script",section:"section",select:"select",small:"small",source:"source",span:"span",strong:"strong",style:"style",sub:"sub",summary:"summary",sup:"sup",table:"table",tbody:"tbody",td:"td",textarea:"textarea",tfoot:"tfoot",th:"th",thead:"thead",time:"time",title:"title",tr:"tr",track:"track",u:"u",ul:"ul","var":"var",video:"video",wbr:"wbr",circle:"circle",clipPath:"clipPath",defs:"defs",ellipse:"ellipse",g:"g",image:"image",line:"line",linearGradient:"linearGradient",mask:"mask",path:"path",pattern:"pattern",polygon:"polygon",polyline:"polyline",radialGradient:"radialGradient",rect:"rect",stop:"stop",svg:"svg",text:"text",tspan:"tspan"},r);e.exports=i},function(e,t){"use strict";var n={useCreateElement:!0};e.exports=n},function(e,t,n){"use strict";var r=n(94),a=n(6),o={dangerouslyProcessChildrenUpdates:function(e,t){var n=a.getNodeFromInstance(e);r.processUpdates(n,t)}};e.exports=o},function(e,t,n){"use strict";function r(){this._rootNodeID&&p.updateWrapper(this)}function a(e){var t=this._currentElement.props,n=c.executeOnChange(t,e);d.asap(r,this);var a=t.name;if("radio"===t.type&&null!=a){for(var i=u.getNodeFromInstance(this),s=i;s.parentNode;)s=s.parentNode;for(var l=s.querySelectorAll("input[name="+JSON.stringify(""+a)+'][type="radio"]'),p=0;pt.end?(n=t.end,r=t.start):(n=t.start,r=t.end),a.moveToElementText(e),a.moveStart("character",n),a.setEndPoint("EndToStart",a),a.moveEnd("character",r-n),a.select()}function s(e,t){if(window.getSelection){var n=window.getSelection(),r=e[u()].length,a=Math.min(t.start,r),o=void 0===t.end?a:Math.min(t.end,r);if(!n.extend&&a>o){var i=o;o=a,a=i}var s=c(e,a),l=c(e,o);if(s&&l){var d=document.createRange();d.setStart(s.node,s.offset),n.removeAllRanges(),a>o?(n.addRange(d),n.extend(l.node,l.offset)):(d.setEnd(l.node,l.offset),n.addRange(d))}}}var l=n(7),c=n(535),u=n(177),d=l.canUseDOM&&"selection"in document&&!("getSelection"in window),p={getOffsets:d?a:o,setOffsets:d?i:s};e.exports=p},function(e,t,n){"use strict";var r=n(2),a=n(5),o=n(94),i=n(39),s=n(6),l=(n(10),n(68)),c=(n(1),n(110),function(e){this._currentElement=e,this._stringText=""+e,this._hostNode=null,this._hostParent=null,this._domID=null,this._mountIndex=0,this._closingComment=null,this._commentNodes=null});a(c.prototype,{mountComponent:function(e,t,n,r){var a=n._idCounter++,o=" react-text: "+a+" ",c=" /react-text ";if(this._domID=a,this._hostParent=t,e.useCreateElement){var u=n._ownerDocument,d=u.createComment(o),p=u.createComment(c),m=i(u.createDocumentFragment());return i.queueChild(m,i(d)),this._stringText&&i.queueChild(m,i(u.createTextNode(this._stringText))),i.queueChild(m,i(p)),s.precacheNode(this,d),this._closingComment=p,m}var f=l(this._stringText);return e.renderToStaticMarkup?f:""+f+""},receiveComponent:function(e,t){if(e!==this._currentElement){this._currentElement=e;var n=""+e;if(n!==this._stringText){this._stringText=n;var r=this.getHostNode();o.replaceDelimitedText(r[0],r[1],n)}}},getHostNode:function(){var e=this._commentNodes;if(e)return e;if(!this._closingComment)for(var t=s.getNodeFromInstance(this),n=t.nextSibling;;){if(null==n?r("67",this._domID):void 0,8===n.nodeType&&" /react-text "===n.nodeValue){this._closingComment=n;break}n=n.nextSibling}return e=[this._hostNode,this._closingComment],this._commentNodes=e,e},unmountComponent:function(){this._closingComment=null,this._commentNodes=null,s.uncacheNode(this)}}),e.exports=c},function(e,t,n){"use strict";function r(){this._rootNodeID&&d.updateWrapper(this)}function a(e){var t=this._currentElement.props,n=l.executeOnChange(t,e);return u.asap(r,this),n}var o=n(2),i=n(5),s=n(61),l=n(98),c=n(6),u=n(16),d=(n(1),n(3),{getHostProps:function(e,t){null!=t.dangerouslySetInnerHTML?o("91"):void 0;var n=i({},s.getHostProps(e,t),{value:void 0,defaultValue:void 0,children:""+e._wrapperState.initialValue,onChange:e._wrapperState.onChange});return n},mountWrapper:function(e,t){var n=l.getValue(t),r=n;if(null==n){var i=t.defaultValue,s=t.children;null!=s&&(null!=i?o("92"):void 0,Array.isArray(s)&&(s.length<=1?void 0:o("93"),s=s[0]),i=""+s),null==i&&(i=""),r=i}e._wrapperState={initialValue:""+r,listeners:null,onChange:a.bind(e)}},updateWrapper:function(e){var t=e._currentElement.props,n=c.getNodeFromInstance(e),r=l.getValue(t);if(null!=r){var a=""+r;a!==n.value&&(n.value=a),null==t.defaultValue&&(n.defaultValue=a)}null!=t.defaultValue&&(n.defaultValue=t.defaultValue)},postMountWrapper:function(e){var t=c.getNodeFromInstance(e);t.value=t.textContent}});e.exports=d},function(e,t,n){"use strict";function r(e,t){"_hostNode"in e?void 0:l("33"),"_hostNode"in t?void 0:l("33");for(var n=0,r=e;r;r=r._hostParent)n++;for(var a=0,o=t;o;o=o._hostParent)a++;for(;n-a>0;)e=e._hostParent,n--;for(;a-n>0;)t=t._hostParent,a--;for(var i=n;i--;){if(e===t)return e;e=e._hostParent,t=t._hostParent}return null}function a(e,t){"_hostNode"in e?void 0:l("35"),"_hostNode"in t?void 0:l("35");for(;t;){if(t===e)return!0;t=t._hostParent}return!1}function o(e){return"_hostNode"in e?void 0:l("36"),e._hostParent}function i(e,t,n){for(var r=[];e;)r.push(e),e=e._hostParent;var a;for(a=r.length;a-- >0;)t(r[a],!1,n);for(a=0;a0;)n(l[c],!1,o)}var l=n(2);n(1);e.exports={isAncestor:a,getLowestCommonAncestor:r,getParentInstance:o,traverseTwoPhase:i,traverseEnterLeave:s}},function(e,t,n){"use strict";function r(e,t){null!=t&&"string"==typeof t.type&&(t.type.indexOf("-")>=0||t.props.is||o(e,t))}var a,o=(n(34),n(62),n(64),n(3),function(e,t){var n=[];for(var r in t.props){var o=a(t.type,r,e);o||n.push(r)}n.map(function(e){return"`"+e+"`"}).join(", ");1===n.length||n.length>1}),i={onBeforeMountComponent:function(e,t){r(e,t)},onBeforeUpdateComponent:function(e,t){r(e,t)}};e.exports=i},function(e,t,n){"use strict";function r(){this.reinitializeTransaction()}var a=n(5),o=n(16),i=n(67),s=n(13),l={initialize:s,close:function(){p.isBatchingUpdates=!1}},c={initialize:s,close:o.flushBatchedUpdates.bind(o)},u=[c,l];a(r.prototype,i.Mixin,{getTransactionWrappers:function(){return u}});var d=new r,p={isBatchingUpdates:!1,batchedUpdates:function(e,t,n,r,a,o){var i=p.isBatchingUpdates;p.isBatchingUpdates=!0,i?e(t,n,r,a,o):d.perform(e,null,t,n,r,a,o)}};e.exports=p},function(e,t,n){"use strict";function r(){C||(C=!0,h.EventEmitter.injectReactEventListener(_),h.EventPluginHub.injectEventPluginOrder(i),h.EventPluginUtils.injectComponentTree(d),h.EventPluginUtils.injectTreeTraversal(m),h.EventPluginHub.injectEventPluginsByName({SimpleEventPlugin:E,EnterLeaveEventPlugin:s,ChangeEventPlugin:o,SelectEventPlugin:y,BeforeInputEventPlugin:a}),h.HostComponent.injectGenericComponentClass(u),h.HostComponent.injectTextComponentClass(f),h.DOMProperty.injectDOMPropertyConfig(l),h.DOMProperty.injectDOMPropertyConfig(v),h.EmptyComponent.injectEmptyComponentFactory(function(e){return new p(e)}),h.Updates.injectReconcileTransaction(b),h.Updates.injectBatchingStrategy(g),h.Component.injectEnvironment(c))}var a=n(477),o=n(479),i=n(481),s=n(482),l=n(484),c=n(156),u=n(490),d=n(6),p=n(493),m=n(503),f=n(501),g=n(505),_=n(508),h=n(509),b=n(513),v=n(516),y=n(517),E=n(518),C=!1;e.exports={inject:r}},function(e,t,n){"use strict";function r(e){a.enqueueEvents(e),a.processEventQueue(!1)}var a=n(49),o={handleTopLevel:function(e,t,n,o){var i=a.extractEvents(e,t,n,o);r(i)}};e.exports=o},function(e,t,n){"use strict";function r(e){for(;e._hostParent;)e=e._hostParent;var t=d.getNodeFromInstance(e),n=t.parentNode;return d.getClosestInstanceFromNode(n)}function a(e,t){this.topLevelType=e,this.nativeEvent=t,this.ancestors=[]}function o(e){var t=m(e.nativeEvent),n=d.getClosestInstanceFromNode(t),a=n;do e.ancestors.push(a),a=a&&r(a);while(a);for(var o=0;o/,o=/^<\!\-\-/,i={CHECKSUM_ATTR_NAME:"data-react-checksum",addChecksumToMarkup:function(e){var t=r(e);return o.test(e)?e:e.replace(a," "+i.CHECKSUM_ATTR_NAME+'="'+t+'"$&')},canReuseMarkup:function(e,t){var n=t.getAttribute(i.CHECKSUM_ATTR_NAME);n=n&&parseInt(n,10);var a=r(e);return a===n}};e.exports=i},function(e,t,n){"use strict";function r(e,t,n){return{type:p.INSERT_MARKUP,content:e,fromIndex:null,fromNode:null,toIndex:n,afterNode:t}}function a(e,t,n){return{type:p.MOVE_EXISTING,content:null,fromIndex:e._mountIndex,fromNode:m.getHostNode(e),toIndex:n,afterNode:t}}function o(e,t){return{type:p.REMOVE_NODE,content:null,fromIndex:e._mountIndex,fromNode:t,toIndex:null,afterNode:null}}function i(e){return{type:p.SET_MARKUP,content:e,fromIndex:null,fromNode:null,toIndex:null,afterNode:null}}function s(e){return{type:p.TEXT_CONTENT,content:e,fromIndex:null,fromNode:null,toIndex:null,afterNode:null}}function l(e,t){return t&&(e=e||[],e.push(t)),e}function c(e,t){d.processChildrenUpdates(e,t)}var u=n(2),d=n(99),p=(n(65),n(10),n(165)),m=(n(24),n(40)),f=n(486),g=(n(13),n(533)),_=(n(1),{Mixin:{_reconcilerInstantiateChildren:function(e,t,n){return f.instantiateChildren(e,t,n)},_reconcilerUpdateChildren:function(e,t,n,r,a){var o;return o=g(t),f.updateChildren(e,o,n,r,a),o},mountChildren:function(e,t,n){var r=this._reconcilerInstantiateChildren(e,t,n);this._renderedChildren=r;var a=[],o=0;for(var i in r)if(r.hasOwnProperty(i)){var s=r[i],l=m.mountComponent(s,t,this,this._hostContainerInfo,n);s._mountIndex=o++,a.push(l)}return a},updateTextContent:function(e){var t=this._renderedChildren;f.unmountChildren(t,!1);for(var n in t)t.hasOwnProperty(n)&&u("118");var r=[s(e)];c(this,r)},updateMarkup:function(e){var t=this._renderedChildren;f.unmountChildren(t,!1);for(var n in t)t.hasOwnProperty(n)&&u("118");var r=[i(e)];c(this,r)},updateChildren:function(e,t,n){this._updateChildren(e,t,n)},_updateChildren:function(e,t,n){var r=this._renderedChildren,a={},o=this._reconcilerUpdateChildren(r,e,a,t,n);if(o||r){var i,s=null,u=0,d=0,p=null;for(i in o)if(o.hasOwnProperty(i)){var f=r&&r[i],g=o[i];f===g?(s=l(s,this.moveChild(f,p,d,u)),u=Math.max(f._mountIndex,u),f._mountIndex=d):(f&&(u=Math.max(f._mountIndex,u)),s=l(s,this._mountChildAtIndex(g,p,d,t,n))),d++,p=m.getHostNode(g)}for(i in a)a.hasOwnProperty(i)&&(s=l(s,this._unmountChild(r[i],a[i])));s&&c(this,s),this._renderedChildren=o}},unmountChildren:function(e){var t=this._renderedChildren;f.unmountChildren(t,e),this._renderedChildren=null},moveChild:function(e,t,n,r){if(e._mountIndex=t)return{node:a,offset:t-o};o=i}a=n(r(a))}}e.exports=a},function(e,t,n){"use strict";function r(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n["Webkit"+e]="webkit"+t,n["Moz"+e]="moz"+t,n["ms"+e]="MS"+t,n["O"+e]="o"+t.toLowerCase(),n}function a(e){if(s[e])return s[e];if(!i[e])return e;var t=i[e];for(var n in t)if(t.hasOwnProperty(n)&&n in l)return s[e]=t[n];return""}var o=n(7),i={animationend:r("Animation","AnimationEnd"),animationiteration:r("Animation","AnimationIteration"),animationstart:r("Animation","AnimationStart"),transitionend:r("Transition","TransitionEnd")},s={},l={};o.canUseDOM&&(l=document.createElement("div").style,"AnimationEvent"in window||(delete i.animationend.animation,delete i.animationiteration.animation,delete i.animationstart.animation),"TransitionEvent"in window||delete i.transitionend.transition),e.exports=a},function(e,t,n){"use strict";function r(e){return o.isValidElement(e)?void 0:a("23"),e}var a=n(2),o=n(15);n(1);e.exports=r},function(e,t,n){"use strict";function r(e){return'"'+a(e)+'"'}var a=n(68);e.exports=r},function(e,t,n){"use strict";var r=n(164);e.exports=r.renderSubtreeIntoContainer},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{"default":e}}function a(){for(var e=arguments.length,t=Array(e),n=0;n=0,o=a&&r.regeneratorRuntime;if(r.regeneratorRuntime=void 0,e.exports=n(544),a)r.regeneratorRuntime=o;else try{delete r.regeneratorRuntime}catch(i){r.regeneratorRuntime=void 0}}).call(t,function(){return this}())},function(e,t,n){(function(t,n){!function(t){"use strict";function r(e,t,n,r){var a=Object.create((t||o).prototype),i=new f(r||[]);return a._invoke=d(e,n,i),a}function a(e,t,n){try{return{type:"normal",arg:e.call(t,n)}}catch(r){return{type:"throw",arg:r}}}function o(){}function i(){}function s(){}function l(e){["next","throw","return"].forEach(function(t){e[t]=function(e){return this._invoke(t,e)}})}function c(e){this.arg=e}function u(e){function t(n,r,o,i){var s=a(e[n],e,r);if("throw"!==s.type){var l=s.arg,u=l.value;return u instanceof c?Promise.resolve(u.arg).then(function(e){t("next",e,o,i)},function(e){t("throw",e,o,i)}):Promise.resolve(u).then(function(e){l.value=e,o(l)},i)}i(s.arg)}function r(e,n){function r(){return new Promise(function(r,a){t(e,n,r,a)})}return o=o?o.then(r,r):r()}"object"==typeof n&&n.domain&&(t=n.domain.bind(t));var o;this._invoke=r}function d(e,t,n){var r=T;return function(o,i){if(r===N)throw new Error("Generator is already running");if(r===w){if("throw"===o)throw i;return _()}for(;;){var s=n.delegate;if(s){if("return"===o||"throw"===o&&s.iterator[o]===h){n.delegate=null;var l=s.iterator["return"];if(l){var c=a(l,s.iterator,i);if("throw"===c.type){o="throw",i=c.arg;continue}}if("return"===o)continue}var c=a(s.iterator[o],s.iterator,i);if("throw"===c.type){n.delegate=null,o="throw",i=c.arg;continue}o="next",i=h;var u=c.arg;if(!u.done)return r=x,u;n[s.resultName]=u.value,n.next=s.nextLoc,n.delegate=null}if("next"===o)n.sent=n._sent=i;else if("throw"===o){if(r===T)throw r=w,i;n.dispatchException(i)&&(o="next",i=h)}else"return"===o&&n.abrupt("return",i);r=N;var c=a(e,t,n);if("normal"===c.type){r=n.done?w:x;var u={value:c.arg,done:n.done};if(c.arg!==M)return u;n.delegate&&"next"===o&&(i=h)}else"throw"===c.type&&(r=w,o="throw",i=c.arg)}}}function p(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function m(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function f(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(p,this),this.reset(!0)}function g(e){if(e){var t=e[y];if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var n=-1,r=function a(){for(;++n=0;--r){var a=this.tryEntries[r],o=a.completion;if("root"===a.tryLoc)return t("end");if(a.tryLoc<=this.prev){var i=b.call(a,"catchLoc"),s=b.call(a,"finallyLoc");if(i&&s){if(this.prev=0;--n){var r=this.tryEntries[n];if(r.tryLoc<=this.prev&&b.call(r,"finallyLoc")&&this.prev=0;--t){var n=this.tryEntries[t];if(n.finallyLoc===e)return this.complete(n.completion,n.afterLoc),m(n),M}},"catch":function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var n=this.tryEntries[t];if(n.tryLoc===e){var r=n.completion;if("throw"===r.type){var a=r.arg;m(n)}return a}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,n){return this.delegate={iterator:g(e),resultName:t,nextLoc:n},M}}}("object"==typeof t?t:"object"==typeof window?window:"object"==typeof self?self:this)}).call(t,function(){return this}(),n(441))},function(e,t){"use strict";e.exports=function(e){return encodeURIComponent(e).replace(/[!'()*]/g,function(e){return"%"+e.charCodeAt(0).toString(16).toUpperCase()})}},function(e,t,n){function r(e,t){for(var n=0;n=0&&v.splice(t,1)}function s(e){var t=document.createElement("style");return t.type="text/css",o(e,t),t}function l(e){var t=document.createElement("link");return t.rel="stylesheet",o(e,t),t}function c(e,t){var n,r,a;if(t.singleton){var o=b++;n=h||(h=s(t)),r=u.bind(null,n,o,!1),a=u.bind(null,n,o,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=l(t),r=p.bind(null,n),a=function(){i(n),n.href&&URL.revokeObjectURL(n.href)}):(n=s(t),r=d.bind(null,n),a=function(){i(n)});return r(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;r(e=t)}else a()}}function u(e,t,n,r){var a=n?"":r.css;if(e.styleSheet)e.styleSheet.cssText=y(t,a);else{var o=document.createTextNode(a),i=e.childNodes;i[t]&&e.removeChild(i[t]),i.length?e.insertBefore(o,i[t]):e.appendChild(o)}}function d(e,t){var n=t.css,r=t.media;if(r&&e.setAttribute("media",r),e.styleSheet)e.styleSheet.cssText=n;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}function p(e,t){var n=t.css,r=t.sourceMap;r&&(n+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(r))))+" */");var a=new Blob([n],{type:"text/css"}),o=e.href;e.href=URL.createObjectURL(a),o&&URL.revokeObjectURL(o)}var m={},f=function(e){var t;return function(){return"undefined"==typeof t&&(t=e.apply(this,arguments)),t}},g=f(function(){return/msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase())}),_=f(function(){return document.head||document.getElementsByTagName("head")[0]}),h=null,b=0,v=[];e.exports=function(e,t){t=t||{},"undefined"==typeof t.singleton&&(t.singleton=g()),"undefined"==typeof t.insertAt&&(t.insertAt="bottom");var n=a(e);return r(n,t),function(e){for(var o=[],i=0;i { - if (typeof global[property] === 'undefined') { - exposedProperties.push(property); - global[property] = document.defaultView[property]; - } -}); - -global.navigator = { - userAgent: 'node.js' -}; - -chai.use(spies); - -const google = { - maps: { - LatLng: function(lat, lng) { - return { - latitude: parseFloat(lat), - longitude: parseFloat(lng), - - lat: function() { - return this.latitude; - }, - lng: function() { - return this.longitude; - } - }; - }, - LatLngBounds: function(ne, sw) { - return { - getSouthWest: function() { - return sw; - }, - getNorthEast: function() { - return ne; - } - }; - }, - OverlayView: function() { - return {}; - }, - InfoWindow: function() { - return {}; - }, - Marker: function() { - return { - addListener: function() {} - }; - }, - MarkerImage: function() { - return {}; - }, - Map: function() { - return { - addListener: function() {}, - trigger: function() {} - }; - }, - Point: function() { - return {}; - }, - Size: function() { - return {}; - }, - event: { - trigger: function() {} - } - } -}; - -global.google = google; - -documentRef = document; diff --git a/scripts/prepublish.sh b/scripts/prepublish.sh deleted file mode 100644 index c5be5dc..0000000 --- a/scripts/prepublish.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -echo "=> Transpiling..." -echo "" -export NODE_ENV=production -rm -rf ./dist -./node_modules/.bin/babel \ - --plugins 'transform-es2015-modules-umd' \ - --presets 'stage-0,react' \ - --ignore __tests__ \ - --out-dir ./dist \ - src -echo "" -echo "=> Complete" diff --git a/src/__tests__/index.js b/src/__tests__/index.js deleted file mode 100644 index 4dc38d5..0000000 --- a/src/__tests__/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { - expect -} from 'chai'; -import sinon from 'sinon'; - -import {createConstants} from '../' - -describe('index', () => { - it('exports a createConstants() function', () => { - expect(core.createConstants).to.be.defined; - }) -}) diff --git a/src/__tests__/lib/apiClient.spec.js b/src/__tests__/lib/apiClient.spec.js deleted file mode 100644 index dc498a6..0000000 --- a/src/__tests__/lib/apiClient.spec.js +++ /dev/null @@ -1,237 +0,0 @@ -import sinon from 'sinon'; -import {expect} from 'chai'; -import ApiClient from '../../lib/apiClient'; -import configureMockStore from 'redux-mock-store'; -import {makeStore} from '../spec_helpers'; -import fetchMock from 'fetch-mock'; - -const BASE_URL = 'http://fullstackreact.com'; - -const baseOpts = { - _debug: false, - baseUrl: BASE_URL, - appendExt: false, // optional - headers: { - 'X-Requested-With': 'spec' - } -} - -describe('ApiClient', () => { - let client, state; - let helpers; - - beforeEach(() => { - fetchMock - .mock(`${BASE_URL}/foo`, { - status: 200, - body: {msg: 'world'} - }) - }); - - afterEach(() => fetchMock.restore()); - - beforeEach(() => { - state = {}; - client = new ApiClient(baseOpts, () => state); - }); - - describe('option parsing', () => { - const makeOptionParsingTest = (key, val) => { - // common tests for each option - it(`accepts ${key} in defaultOpts as a value`, () => { - let res = client._parseOpt(key, {}, { [key]: val }); - expect(res).to.eql(val) - }); - it(`accepts ${key} in instance options as a value`, () => { - let res = client._parseOpt(key, { [key]: val }); - expect(res).to.eql(val) - }); - it(`accepts ${key} as a function`, () => { - let res = client._parseOpt(key, { [key]: () => val }); - expect(res).to.eql(val) - }); - it(`accepts ${key} as false (nullify)`, () => { - let res = client._parseOpt(key, { [key]: false }); - expect(res).to.be.null; - }); - } - - // Test for appendPath - makeOptionParsingTest('appendPath', `/${Math.random(0, 20)}`) - makeOptionParsingTest('appendExt', 'json') - - }) - - describe('get', () => { - it('defines GET as a function', () => { - expect(typeof client.get).to.eql('function'); - }) - - it('runs a request', (done) => { - client.get({url: `${BASE_URL}/foo`}) - .then((resp) => { - expect(resp.msg).to.eql('world') - done(); - }).catch(done); - }); - - it('accepts a string as a path', done => { - client.get('/foo') - .then((resp) => { - expect(resp.msg).to.eql('world'); - done(); - }).catch(done); - }) - - it('accepts a path (appended to the baseUrl)', done => { - client.get({path: '/foo'}) - .then((resp) => { - expect(resp.msg).to.eql('world'); - done(); - }).catch(done); - }); - - it('accepts appendPath', done => { - fetchMock.mock(`${BASE_URL}/foo/yellow`, '{}') - client.get({path: '/foo', appendPath: '/yellow'}) - .then(() => done()).catch(done); - }); - }); - - describe('post', () => { - it('defines POST as a function', () => { - expect(typeof client.post).to.eql('function'); - }); - - it('sends `data` along with the request', (done) => { - fetchMock.post(`${BASE_URL}/foo`, { - msg: 'world' - }) - client.post({ - path: '/foo', - data: {msg: 'hello'} - }).then((data) => { - expect(data).to.eql({msg: 'world'}) - done(); - }).catch(done); - }) - }) - - describe('error handling', () => { - let client; - const generateError = (status, msg={}, options={}) => { - return fetchMock.mock(`${BASE_URL}/err`, (reqUrl, reqOpts) => { - return { - status, - body: JSON.stringify(msg) - } - }) - } - - beforeEach(() => { - client = new ApiClient(baseOpts, () => state); - }); - - it('responds with the status code', (done) => { - let p = generateError(500, {msg: 'blah'}); - client.get({path: '/err'}) - .catch((err) => { - expect(err.status).to.equal(500); - done(); - }) - }); - - it('responds with the error messsage', (done) => { - generateError(400, {msg: 'error error'}); - client.get({path: '/err'}) - .catch((err) => { - err.body.then((json) => { - expect(json.msg).to.eql('error error') - done(); - }) - }) - }) - }) - - describe('request transforms', () => { - let transform = (state, opts) => req => { - req.headers['X-Name'] = 'Ari'; - return req; - } - beforeEach(() => { - fetchMock.mock(`${BASE_URL}/name`, (reqUrl, reqOpts) => { - return { - status: 200, - body: JSON.stringify(reqOpts) - } - }); - client = new ApiClient(baseOpts, () => state); - }); - - it('can accept a single requestTransform', () => { - baseOpts.requestTransforms = [transform]; - client = new ApiClient(baseOpts, () => state); - client.get({path: '/name'}) - .then((json) => { - expect(res.headers['X-Name']).to.eql('Ari') - }); - }); - - it('accepts multiple requestTransforms', () => { - client = new ApiClient(baseOpts, () => state); - client.get({path: '/name', requestTransforms: [transform]}) - }); - }); - - describe('response transforms', () => { - let time, msg; - let transform = (state, opts) => res => { - res.headers.set('X-Response-Time', time); - return res; - } - let jsonTransform = (state, opts) => (res) => { - let time = res.headers.get('X-Response-Time'); - return res.json() - .then(json => ({...json, time})) - } - beforeEach(() => { - time = new Date() - msg = 'hello world'; - fetchMock.mock(`${BASE_URL}/response/time`, { - status: 200, - body: JSON.stringify({time, msg}) - }); - client = new ApiClient(baseOpts, () => state); - }); - - it('returns parsed JSON by default without responseTransforms', (done) => { - client = new ApiClient(baseOpts, () => state) - .get({path: '/response/time'}) - .then(json => { - expect(json.msg).to.eql(msg); - done(); - }).catch(done) - }); - - it('can accept a single responseTransforms', (done) => { - baseOpts.responseTransforms = [transform]; - client = new ApiClient(baseOpts, () => state); - client.get({path: '/response/time'}) - .then((res) => { - expect(res.headers.get('X-Response-Time')).to.eql(time) - done(); - }).catch(done) - }); - - it('accepts multiple responseTransforms', (done) => { - client = new ApiClient(baseOpts, () => state); - client.get({path: '/response/time', - responseTransforms: [transform, jsonTransform]}) - .then((json) => { - expect(json.time).to.eql(time); - done(); - }).catch(done) - }); - }); - -}) diff --git a/src/__tests__/lib/constants.spec.js b/src/__tests__/lib/constants.spec.js deleted file mode 100644 index 68ada05..0000000 --- a/src/__tests__/lib/constants.spec.js +++ /dev/null @@ -1,177 +0,0 @@ -import { - expect -} from 'chai'; -import { - createConstants, - apiKeys, - apiValues -} from '../../lib/constants'; - -describe('createConstants', () => { - let obj, typeFn; - beforeEach(() => { - obj = {}; - typeFn = createConstants({}); - }); - - const prefixTests = (types, expected) => { - it('creates a constant on the given object', () => { - expect(types.HARRY).to.exist; - }); - - Object.keys(expected).forEach(key => { - it(`creates a ${key} constant to equal ${expected[key]}`, () => { - expect(types[key]).to.eql(expected[key]) - }) - }) - return types; - } - - describe('with prefix', () => { - prefixTests(createConstants({ - prefix: 'person' - })('HARRY'), { - 'HARRY': 'PERSON_HARRY' - }) - }) - - describe('with a string opts', () => { - const types = createConstants('some_prefix')('CREATE'); - expect(types.CREATE).to.eql('SOME_PREFIX_CREATE'); - }); - - - describe('without prefix', () => { - prefixTests(createConstants({})('HARRY'), { - 'HARRY': '_HARRY' - }) - }) - - describe('with initialObject', () => { - const types = prefixTests(createConstants({ - initialObject: { - 'bob': 1 - } - })('HARRY'), { - 'HARRY': '_HARRY', - 'bob': 1 - }); - - it('does not overwrite the initial value', () => { - expect(Object.keys(types)).to.include('HARRY') - }); - }); - - describe('objects', () => { - let types; - beforeEach(() => { - types = createConstants({})({ - 'HISTORY': true, - 'PERSON': { - api: true - }, - 'DOG': { - api: true, - states: ['sit', 'stay'] - } - }) - }) - it('accepts an object definition', () => { - expect(types.HISTORY).to.exist; - }); - it('creates api events for each type', () => { - expect(types.PERSON).to.exist; - expect(types.PERSON_LOADING).to.exist; - expect(types.PERSON_SUCCESS).to.exist; - expect(types.PERSON_ERROR).to.exist; - }); - it('creates api type postfixes', () => { - expect(types.PERSON).to.eql('_PERSON'); - expect(types.PERSON_LOADING).to.eql('API__PERSON_LOADING'); - expect(types.PERSON_SUCCESS).to.eql('API__PERSON_SUCCESS'); - expect(types.PERSON_ERROR).to.eql('API__PERSON_ERROR'); - }); - it('accepts custom states', () => { - expect(types.DOG).to.eql('_DOG'); - expect(types.DOG_SIT).to.eql('API__DOG_SIT') - expect(types.DOG_STAY).to.eql('API__DOG_STAY'); - expect(types.DOG_LOADING).not.to.exist; - }) - }) - - describe('separator', () => { - it('can accept a string separator', () => { - let types = createConstants({ - separator: '/' - })('DOG', 'CAT'); - expect(Object.keys(types).length).to.equal(2); - expect(types.DOG).to.eql('/DOG') - expect(types.CAT).to.eql('/CAT'); - }); - - it('accepts a string prefix', () => { - const types = createConstants({ - separator: '/', - prefix: 'animals' - })('DOG', 'CAT') - expect(types.DOG).to.eql('ANIMALS/DOG'); - expect(types.CAT).to.eql('ANIMALS/CAT'); - }) - - it('accepts an array prefix', () => { - const types = createConstants({ - separator: '/', - prefix: ['test', 'animals'] - })('DOG', 'CAT') - expect(Object.keys(types).length).to.equal(2); - expect(types.DOG).to.eql('TEST/ANIMALS/DOG'); - expect(types.CAT).to.eql('TEST/ANIMALS/CAT'); - }) - }) - - describe('custom types', () => { - let typeCreator; - beforeEach(() => { - typeCreator = createConstants({ - prefix: 'animals', - customTypes: { - 'myApi': ['loading', 'error', 'success'], - 'sockets': ['connected', 'disconnected'] - } - }) - }) - - it('creates a constant with each of the different states of the constant', () => { - let types = typeCreator({'DOG': {types: 'sockets'}}, {'CAT': { types: 'myApi' }}, 'BIRD') - // types should have types: - // types.DOG_CONNECTED: 'ANIMALS_SOCKETS_DOG_CONNECTED', - // types.DOG_DISCONNECTED: 'ANIMALS_SOCKETS_DOG_DISCONNECTED' - expect(types.DOG_CONNECTED).to.be.defined; - expect(types.DOG_CONNECTED).to.eql('ANIMALS_SOCKETS_DOG_CONNECTED') - expect(types.DOG_DISCONNECTED).to.be.defined; - expect(types.DOG_DISCONNECTED).to.eql('ANIMALS_SOCKETS_DOG_DISCONNECTED') - - expect(types.CAT_LOADING).to.be.defined; - expect(types.CAT_LOADING).to.eql('ANIMALS_MYAPI_CAT_LOADING'); - }); - - it('associates api types with api', () => { - let types = typeCreator('BIRD', {'CAT': {api: true}}) - expect(types.CAT_LOADING).to.be.defined; - expect(types.CAT_LOADING).to.eql('API_ANIMALS_CAT_LOADING'); - expect(types.CAT_SUCCESS).to.eql('API_ANIMALS_CAT_SUCCESS'); - }) - - }) -}) - -describe('apiValues', () => { - it('passes api values', () => { - let vals = apiValues('BALL') - expect(vals.BALL_LOADING).to.eql('API_BALL_LOADING') - }); - it('passes api values with custom states', () => { - let vals = apiValues('DOG', ['sit']) - expect(vals.DOG_SIT).to.eql('API_DOG_SIT') - }) -}) diff --git a/src/__tests__/lib/createApiActions.spec.js b/src/__tests__/lib/createApiActions.spec.js deleted file mode 100644 index 0b00ed7..0000000 --- a/src/__tests__/lib/createApiActions.spec.js +++ /dev/null @@ -1,107 +0,0 @@ -import sinon from 'sinon'; -import {expect} from 'chai'; -import ApiClient from '../../lib/apiClient'; -import {createApiAction, API_CONSTANTS} from '../../lib/createApiActions'; -import {REDUX_MODULE_API_ACTION_KEY} from '../../lib/constants'; -import configureMockStore from 'redux-mock-store'; -import {generateResponse, makeBaseOpts, BASE_URL, makeStore, doDispatch, doGetState} from '../spec_helpers'; -import fetchMock from 'fetch-mock'; - -const createStubStore = () => { - let res = makeStore({ - baseUrl: BASE_URL, - }, {numbers: []}) - let store = res.store; - return {res, store}; -} -describe('@api decorator', () => { - let fn, decorated, baseOpts, store; - beforeEach(() => baseOpts = makeBaseOpts({})); - beforeEach(() => { - let stub = createStubStore(); - store = stub.store; - fn = (client) => client.get('/go') - }); - beforeEach(() => decorated = createApiAction('YES')(fn)(baseOpts)) - afterEach(() => fetchMock.restore()); - - it('calls two actions (LOADING, SUCCESS)', (done) => { - generateResponse('/go', 200); - let {type, meta} = decorated(store.dispatch, store.getState); - const {runFn} = meta; - expect(type).to.eql(REDUX_MODULE_API_ACTION_KEY); - expect(typeof runFn).to.eql('function') - - runFn(baseOpts) - .then((json) => { - let actions = store.getActions(); - expect(actions.length).to.equal(2); - expect(actions[0].type).to.eql('API_YES_LOADING'); - expect(actions[1].type).to.eql('API_YES_SUCCESS'); - done(); - }) - .catch((err) => { - console.log('An error occurred', err); - done(err); - }) - }); - - it('calls the error action when error is received', (done) => { - generateResponse('/go', 500); - let {type, meta} = decorated(store.dispatch, store.getState); - const {runFn} = meta; - - runFn(baseOpts) - .then((json) => { - let actions = store.getActions(); - - expect(actions.length).to.equal(2); - expect(actions[0].type).to.eql('API_YES_LOADING'); - expect(actions[1].type).to.eql('API_YES_ERROR'); - - done(); - }).catch(done) - }); - - describe('meta creator', () => { - it('appends isApi to the meta object of loading/success', (done) => { - generateResponse('/go', 200) - let {type, meta} = decorated(store.dispatch, store.getState); - const {runFn} = meta; - - runFn(baseOpts) - .then((json) => { - let actions = store.getActions(); - - expect(actions.length).to.equal(2); - expect(actions[0].meta.isApi).to.be.true; - expect(actions[1].meta.isApi).to.be.true; - - done(); - }).catch(done) - }); - - it('appends the errorStatus to the payload object of error', (done) => { - generateResponse('/go', 418) - - let {type, meta} = decorated(store.dispatch, store.getState); - const {runFn} = meta; - - runFn(baseOpts) - .then((json) => { - let actions = store.getActions(); - - expect(actions.length).to.equal(2); - expect(actions[0].meta.isApi).to.be.true; - - const {payload} = actions[1]; - expect(payload.error).to.be.defined; - expect(payload.status).to.eql(API_CONSTANTS[418]) - - done(); - }).catch(done) - }); - - }) - -}); diff --git a/src/__tests__/lib/createApiHandler.spec.js b/src/__tests__/lib/createApiHandler.spec.js deleted file mode 100644 index d0c13f9..0000000 --- a/src/__tests__/lib/createApiHandler.spec.js +++ /dev/null @@ -1,60 +0,0 @@ -import sinon from 'sinon'; -import {expect} from 'chai'; -import {createApiHandler} from '../../lib/createApiHandler'; -import configureMockStore from 'redux-mock-store'; -import {BASE_URL, generateResponse, makeStore} from '../spec_helpers'; -// import 'whatwg-fetch'; -import fetchMock from 'fetch-mock'; - -describe('apiHandler', () => { - let fn, handlers, store; - let dispatch, getState; - const responseData = 'hello world'; - beforeEach(() => { - fn = (state, action) => responseData; - }); - beforeEach(() => { - handlers = createApiHandler('A_TYPE', (apiStates) => {})(fn); - }); - beforeEach(() => { - let res = makeStore({ - baseUrl: BASE_URL - }, {numbers: []}) - store = res.store; - dispatch = store.dispatch; - getState = store.getState; - }); - - it('can create an apiHandler', () => { - expect(handlers).to.exist; - expect(typeof handlers['A_TYPE_LOADING']).to.eql('function'); - expect(typeof handlers['A_TYPE_ERROR']).to.eql('function'); - expect(handlers['API_A_TYPE_SUCCESS']).to.eql(fn); - }); - - describe('default handlers', () => { - beforeEach(() => { - store = sinon.mock(store); - }) - - it('sets the state to loading without a loading handler taking', () => { - let expectedAction = { - type: 'A_TYPE_SUCCESS', - payload: responseData - } - let res = handlers['A_TYPE_LOADING'](dispatch, getState); - expect(res.loading).to.be.true; - }); - - it('sets runs the success handler function on success', () => { - let res = handlers['A_TYPE_SUCCESS'](dispatch, getState); - expect(typeof res).to.eql('object'); - }) - - it('sets the loading state to false on ERROR', () => { - let res = handlers['A_TYPE_ERROR'](dispatch, getState); - expect(typeof res).to.eql('object'); - }) - }); - -}) diff --git a/src/__tests__/lib/createApiMiddleware.spec.js b/src/__tests__/lib/createApiMiddleware.spec.js deleted file mode 100644 index 2d7c4e1..0000000 --- a/src/__tests__/lib/createApiMiddleware.spec.js +++ /dev/null @@ -1,61 +0,0 @@ -import sinon from 'sinon'; -import {expect} from 'chai'; -import configureMockStore from 'redux-mock-store'; -import {makeStore} from '../spec_helpers'; - -const BASE_URL = 'http://fullstackreact.com'; - -const doDispatch = () => {}; -const doGetState = () => {}; -const makeDispatchAction = (action, fn) => dispatch => { - fn && fn(action); - return dispatch(action) -}; - -describe('ApiClient middleware', () => { - let nextHandler, apiMiddleware; - let store, mockStore; - - let createNext = fn => (action) => fn && fn(action); - - let initialState = { numbers: [] }; - let runtimeOpts = {dispatch: doDispatch, getState: doGetState} - - beforeEach(() => { - let res = makeStore({ - baseUrl: BASE_URL - }, {numbers: []}) - nextHandler = res.nextHandler; - store = res.store; - }); - - - it('returns a function', () => { - const actionHandler = nextHandler(createNext()); - - expect(typeof actionHandler).to.eql('function'); - expect(actionHandler.length).to.equal(1); - }) - - describe('non-api requests', () => { - const testType = 'TEST'; - let action, msg; - beforeEach(() => { - let payload = JSON.stringify({add: 1}) - msg = {type: testType, payload}; - }) - - it('runs the action with dispatch', done => { - action = makeDispatchAction(msg, () => done()); - store.dispatch(action) - expect(store.getActions().length).to.eql(1); - expect(store.getActions()[0].type).to.eql(testType); - }); - }); - - describe('api requests', () => { - // pending... as in... this works differently now - // so the old tests do not make sense any more... - // the functionality is tested in createApiActions.spec - }) -}); diff --git a/src/__tests__/lib/createReducer.spec.js b/src/__tests__/lib/createReducer.spec.js deleted file mode 100644 index c56bcb9..0000000 --- a/src/__tests__/lib/createReducer.spec.js +++ /dev/null @@ -1,29 +0,0 @@ -import {expect} from 'chai'; -import {makeStore} from '../spec_helpers'; -import {createReducer} from '../../lib/createReducer'; - -describe('createReducer', () => { - let reducer, handlers, store; - - beforeEach(() => { - let res = makeStore({}, {msg: 'init'}) - store = res.store; - }); - - beforeEach(() => { - let handlers = { - 'HANDLE_THING': (state, action) => ({msg: 'called'}) - } - reducer = createReducer(handlers) - }) - - it('calls a reducer when defined in the hash', () => { - let resp = reducer(store.getState(), {type: 'HANDLE_THING'}); - expect(resp.msg).to.eql('called'); - }); - - it('returns original state with non-defined type handler', () => { - let resp = reducer(store.getState(), {type: 'HANDLE_OTHER_THING'}); - expect(resp.msg).to.eql('init'); - }) -}); diff --git a/src/__tests__/lib/createRootReducer.spec.js b/src/__tests__/lib/createRootReducer.spec.js deleted file mode 100644 index 8f18b04..0000000 --- a/src/__tests__/lib/createRootReducer.spec.js +++ /dev/null @@ -1,126 +0,0 @@ -import {expect} from 'chai'; -import sinon from 'sinon' -import {makeStore} from '../spec_helpers'; -import {createRootReducer} from '../../lib/createRootReducer'; - -describe('createRootReducer', () => { - let handlers, store; - - beforeEach(() => { - let res = makeStore({}, {msg: 'init'}) - store = res.store; - }); - - beforeEach(() => { - handlers = createRootReducer({ - users: {} - }); - }) - - it('returns an actions object', () => { - expect(handlers.actions).to.be.defined; - }) - it('returns a reducers object', () => { - expect(handlers.reducers).to.be.defined; - }) - it('returns an initialState object', () => { - expect(handlers.initialState).to.be.defined; - }); - - ['actions', 'reducers', 'initialState'].forEach(key => { - // A little meta - it(`has a users key in ${key}`, () => { - expect(handlers[key].users).to.be.defined; - }); - }); - - describe('initialInitialState', () => { - beforeEach(() => { - handlers = createRootReducer({ - users: { - initialState: { currentUser: { name: 'Ari' }} - }, - events: {} - }, { - initialInitialState: { - users: { currentUser: { name: 'fred' }}, - } - }); - }) - - it('overrides the initial state of the user', () => { - expect(handlers.initialState.users.currentUser.name).to.eql('Ari') - }) - it('does not override initial state when none is defined', () => { - expect(handlers.initialState.events).to.eql({}) - }); - }) - - describe('initialActions', () => { - let sayFn, rejectFn, makeFn; - beforeEach(() => { - sayFn = sinon.spy(); - rejectFn = sinon.spy(); - makeFn = sinon.spy(); - - handlers = createRootReducer({ - users: { - actions: { say: sayFn } - }, - events: {} - }, { - initialActions: { - users: {reject: rejectFn} - } - }) - }) - - it('creates actions as an empty object for modules, even if they do not exist', () => { - expect(handlers.actions.events).to.eql({}); - }) - - it('keeps the actions defined for those that do define actions', () => { - expect(handlers.actions.users.say).to.be.defined; - expect(typeof handlers.actions.users.say).to.eql('function'); - }); - - it('does not override a function in actions', () => { - handlers.actions.users.say('bob') - expect(sayFn).to.have.been.calledWith('bob') - handlers.actions.users.reject(); - expect(rejectFn).to.have.been.called - }); - }) - - describe('initialReducers', () => { - let routingReducer, userReducer; - beforeEach(() => { - routingReducer = sinon.spy(); - userReducer = sinon.spy(); - - handlers = createRootReducer({ - users: { - reducer: userReducer - }, - events: {} - }, { - initialReducers: { - routing: routingReducer - } - }) - }); - - it('merges passed reducer in', () => { - expect(handlers.reducers.users).to.eql(userReducer); - }) - - it('sets the reducer to a noop value when no reducer is passed', () => { - expect(typeof handlers.reducers.events).to.eql('function') - }); - - it('sets the reducer to the initial reducer if no module is found', () => { - expect(handlers.reducers.routing).to.eql(routingReducer) - }) - }) - -}); diff --git a/src/__tests__/lib/utils.spec.js b/src/__tests__/lib/utils.spec.js deleted file mode 100644 index c14bf20..0000000 --- a/src/__tests__/lib/utils.spec.js +++ /dev/null @@ -1,55 +0,0 @@ -import {expect} from 'chai'; -import {apiKeys, syncEach} from '../../lib/utils'; - -describe('Utils', () => { - describe('apiKeys', () => { - it('creates state keys', () => { - let keys = apiKeys('PERSON') - expect(keys).to.include('PERSON_LOADING') - expect(keys).to.include('PERSON_SUCCESS') - expect(keys).to.include('PERSON_ERROR') - }); - it('creates state keys with dynamic states', () => { - let keys = apiKeys('PERSON', ['sit']) - expect(keys).to.include('PERSON_SIT') - }); - }) - - describe('syncEach', () => { - - it('returns the initial value with an empty list', (done) => { - syncEach(101, (curr) => curr)([]) - .then((val) => { - expect(val).to.equal(101); - done(); - }).catch(done); - }) - - it('waits for a list of atomic values', (done) => { - syncEach(1, (val, curr) => val + curr)([2, 3]) - .then((val) => { - expect(val).to.equal(6); - done(); - }).catch(done); - }); - - it('waits for a list of promises', (done) => { - const delayedPromise = (n) => (curr) => { - return new Promise(resolve => { - setTimeout(() => resolve(n + curr), n); - }) - } - const list = [ - delayedPromise(5), - delayedPromise(5) - ] - - syncEach(Promise.resolve(10))(list) - .then(val => { - expect(val).to.equal(20); // 10 + 15 + 20 - done(); - }).catch(done); - }) - - }) -}) diff --git a/src/__tests__/spec_helpers.js b/src/__tests__/spec_helpers.js deleted file mode 100644 index 244fa86..0000000 --- a/src/__tests__/spec_helpers.js +++ /dev/null @@ -1,44 +0,0 @@ -import configureMockStore from 'redux-mock-store'; -import fetchMock from 'fetch-mock'; -import thunk from 'redux-thunk'; -import createApiMiddleware from '../lib/createApiMiddleware'; - -import chai from 'chai'; -import sinonChai from 'sinon-chai'; - -chai.use(sinonChai); - -export const makeStore = (opts, initialState) => { - let apiMiddleware = createApiMiddleware(opts); - let nextHandler = apiMiddleware(store) - - let mockStore = configureMockStore([apiMiddleware, thunk]) - let store = mockStore(initialState); - return {nextHandler, store}; -} - -export const BASE_URL = 'http://fullstackreact.com'; - -const handleResponse = (status, msg) => (reqUrl, reqOpts) => { - if (typeof msg === 'function') { - return msg(reqUrl, reqOpts); - } - return {status, body: JSON.stringify(msg)} -} - -export const makeBaseOpts = (opts = {}) => Object.assign({}, { - _debug: false, - autoExecute: false, - baseUrl: BASE_URL, - appendExt: false, // optional - headers: { - 'X-Requested-With': 'spec' - } - }, opts); - -export const generateResponse = (path, status, msg={}) => { - return fetchMock.mock(`${BASE_URL}${path}`, handleResponse(status, msg)); -} - -export const doDispatch = () => {}; -export const doGetState = () => {}; diff --git a/src/api.js b/src/api.js deleted file mode 100644 index 5546ca7..0000000 --- a/src/api.js +++ /dev/null @@ -1,4 +0,0 @@ -export { apiClient } from './lib/apiClient.js'; -export { createApiMiddleware } from './lib/createApiMiddleware'; -export { createApiAction, api } from './lib/createApiActions'; -export { createApiHandler, apiHandler } from './lib/createApiHandler'; diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 428d676..0000000 --- a/src/index.js +++ /dev/null @@ -1,4 +0,0 @@ -export { createConstants } from './lib/constants'; -export { createReducer } from './lib/createReducer'; -export { bindActionCreatorsToStore } from './lib/bindActionCreatorsToStore'; -export { createRootReducer } from './lib/createRootReducer'; diff --git a/src/lib/actions.js b/src/lib/actions.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/lib/apiClient.js b/src/lib/apiClient.js deleted file mode 100644 index 1ed1669..0000000 --- a/src/lib/apiClient.js +++ /dev/null @@ -1,161 +0,0 @@ -import * as qs from 'query-string'; -import {noop, syncEach} from './utils'; - -export const sharedHeaders = { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'X-Requested-With': 'XMLHttpRequest' -}; - -// Helpers -// JSON parser -// const parseJson = (resp) => resp.json(); -const parseJson = (state, opts) => (resp) => { - return resp.json(); -} - -// MIDDLEWARE THINGS -const checkStatus = (resp) => { - if (!resp.ok) { - let err = new Error(resp.statusText); - err.status = resp.status; - err.response = resp; - err.body = Promise.resolve(noop(resp)) - .then(resp => resp.json()) - .catch((err) => Promise.reject({msg: 'Service error'})) - throw err; - } - return resp; -} - -/** - * API Client class - * - * baseOpts - * { - * baseUrl: Required, - * appendExt: boolean || string - * } - */ -export class ApiClient { - constructor(baseOpts, getState) { - // basically makeRequest - this._debugging = baseOpts._debug; - - ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'].forEach((method) => { - this[method.toLowerCase()] = (options) => { - let opts = this.requestDefaults(method, options, baseOpts); - - let url = this._getUrl(options, baseOpts); - - let requestTransforms = - this._parseOpt('requestTransforms', options, baseOpts, []); - let responseTransforms = - this._parseOpt('responseTransforms', options, baseOpts, [parseJson]); - - // let requestTransforms = [].concat(reqTransforms); - // let responseTransforms = [].concat(respTransforms); - - return new Promise((resolve, reject) => { - this.runTransforms(requestTransforms, getState, opts) - .then(this.debugLog(() => 'requesting...')) - .then((transformedOpts) => fetch(url, transformedOpts)) - .then(this.debugLog()) - .then(checkStatus) - .then(this.debugLog(resp => `Status: ${resp}`)) - .then((resp) => this - .runTransforms(responseTransforms, getState, resp)) - .then(this.debugLog(json => `JSON: ${json}`)) - .then(resolve) - .catch(err => reject(err)) - }); - } - }); - } - - runTransforms (transforms:Array, getState:Function, opts:Object) { - return syncEach(noop(opts), (fn, prev) => { - return typeof fn === 'function' ? fn(getState, opts) : fn; - })(transforms) - } - - debugLog(msgFn) { - return (args) => { - if (this._debugging) { - console.log((msgFn || noop)(args)) - } - return args; - } - } - - // Option handling - _parseOpt(key, opts, defaultOpts = {}, defaultValue = null) { - let val = opts[key] || defaultOpts[key]; - if (!val) { - return defaultValue; - } - - return (typeof val === 'function') ? val.call(this, opts) : val; - } - - requestDefaults (method, params, defaultOpts) { - let opts = params || {}; - - let customHeaders = this._parseOpt('headers', opts, defaultOpts, {}); - let headers = Object.assign({}, sharedHeaders, customHeaders); - - let meta = this._parseOpt('meta', opts, defaultOpts, {}); - let data = this._parseOpt('data', opts, defaultOpts, {}); - - let requestOpts = { - method: method, - headers: headers, - meta: meta, - } - - if (method === 'POST' || method === 'PUT' || method === 'PATCH') { - requestOpts.body = JSON.stringify(data); - } - - return requestOpts; - } - - // Get custom url if included in the opts - // otherwise, get the default one - _getUrl(opts, defaultOpts = {}) { - if (typeof opts === 'string') { - opts = {path: opts} - } - let url = this._parseOpt('url', opts); - let path = this._parseOpt('path', opts); - - if (path) { - path = path[0] !== '/' ? '/' + path : path; - } - - if (!url) { - url = [defaultOpts.baseUrl, path].join(''); // eslint-disable-line no-undef - } - - let appendPath = this._parseOpt('appendPath', opts, defaultOpts); - if (appendPath) { - url = [url, appendPath].join('') - } - - // Append (or don't) - let appendExt = this._parseOpt('appendExt', opts, defaultOpts) - if (appendExt) { - url = url + `.${appendExt}`; - } - - let params = this._parseOpt('params', opts, defaultOpts); - if (params) { - let qsParams = qs.stringify(params); - url = url + '?' + qsParams; - } - - return url; - } -} - -export default ApiClient; diff --git a/src/lib/bindActionCreatorsToStore.js b/src/lib/bindActionCreatorsToStore.js deleted file mode 100644 index 65972c5..0000000 --- a/src/lib/bindActionCreatorsToStore.js +++ /dev/null @@ -1,23 +0,0 @@ -import { bindActionCreators } from 'redux' - -export const bindActionCreatorsToStore = (actions, store) => { - Object.keys(actions).forEach(k => { - let theseActions = actions[k]; - - let actionCreators = Object.keys(theseActions) - .reduce((sum, actionName) => { - // theseActions are each of the module actions which - // we export from the `rootReducer.js` file (we'll create shortly) - let fn = theseActions[actionName]; - // We'll bind them to the store - sum[actionName] = fn.bind(store); - return sum; - }, {}); - - // Using react-redux, we'll bind all these actions to the - // store.dispatch - actions[k] = bindActionCreators(actionCreators, store.dispatch); - }); - - return actions; -} diff --git a/src/lib/constants.js b/src/lib/constants.js deleted file mode 100644 index 5232932..0000000 --- a/src/lib/constants.js +++ /dev/null @@ -1,104 +0,0 @@ -import invariant from 'invariant'; -import {upcase, apiKeys, apiStates, getApiTypes, toApiKey} from './utils'; - -export const REDUX_MODULE_ACTION_KEY = 'REDUX_MODULES/ACTION' -export const REDUX_MODULE_API_ACTION_KEY = 'REDUX_MODULES/API_ACTION' - -let customTypes = { - api: apiStates -} -/* - * get the api values - */ -export function apiValues(name, states = apiStates) { - return apiKeys(name, states) - .reduce((sum, key) => ({ - ...sum, - [key]: toApiKey(key) - }), {}); -} - -export function createCustomTypes(name, states = []) { - customTypes[name] = states; -} - -export function createConstants(opts) { - opts = opts || {}; - let separator = opts.separator || '_'; - - if (opts.customTypes) { - Object.keys(opts.customTypes).forEach(key => { - createCustomTypes(key, opts.customTypes[key]) - }); - } - - if (typeof opts === 'string') { - opts = {prefix: opts} - } - - const defineType = (obj, prefix, n, v) => { - if (typeof n === 'object') { - // If it's an API type, decorate with the different - // api states - Object.keys(n).forEach((key) => { - if (n.hasOwnProperty(key)) { - let val = n[key]; - if (val && val.types) { - let types = Array.isArray(val.types) ? val.types : val.types.split(' ,'); - types.forEach(type => { - // Define custom types for each - const states = customTypes[type] || []; - states.forEach((customKey) => { - const name = upcase(`${key}${separator}${customKey}`) - const keyPrefix = upcase([type, customKey].join(separator)) - const valuePrefix = [prefix, type].join(separator); - // const apiPrefix = `api_${prefix}`; - defineType(obj, valuePrefix, name); - }); - }) - } - if (val && val.api) defineApi(obj, prefix, key, val.states); - - // Don't store objects in static arrays - if (typeof val === 'object') { - val = null; - } - - defineType(obj, prefix, key, v); - } - }); - } else { - v = v || [].concat.apply([], [prefix, n]).join(separator).toUpperCase(); - Object.defineProperty(obj, n, { - value: v, - enumerable: true, - writable: false, - configurable: false - }); - } - return obj; - }; - - const defineApi = (obj, prefix, n, states) => { - apiKeys(n, states).forEach((apiKey) => { - const apiPrefix = `api_${prefix}`; - defineType(obj, apiPrefix, apiKey); - }); - }; - - - const definer = (obj, prefix) => { - return (...definitions) => { - definitions.forEach((def) => { - defineType(obj, prefix, def); - }); - return obj; - }; - }; - - let initialObject = opts.initialObject || {}; - let prefix = opts.prefix || ''; - return definer(initialObject, prefix); -} - -export default createConstants; diff --git a/src/lib/createApiActions.js b/src/lib/createApiActions.js deleted file mode 100644 index bca4259..0000000 --- a/src/lib/createApiActions.js +++ /dev/null @@ -1,199 +0,0 @@ -/* eslint-disable func-names */ -/* *********************************************** - * Example usage: - // take the main action, and then return an object with each apiState handler function. - @apiHandler(types.DOTHING, (apiTypes) => ({ - [apiTypes.LOADING]: (state) => ({ - ...state, - loading: true, - time: Date.now(), - errors: [], - }), - [apiTypes.ERROR]: (state, action) => { - // do stuf, like set the biggest thing, and do not return object immediately. - let biggest = state.things.sort().shift - return { - ...state, - biggest: biggest, - loading: false, - errors: action.payload.error, - performance: Date.now() - state.time, - } - }, - })) - handleGetMe: (state, action) => { - return { - ...userForState(state, action), - wait_for_load_with_token: false, - } - }, - // here is a template to start with: - @apiHandler(types.SAVE_ESTIMATE, (apiTypes) => ({ - [apiTypes.LOADING]: (state) => ({...state}), - [apiTypes.ERROR]: (state, action) => ({...state, action.payload}), - })) - handleSaveEstimateSuccess: {} - ************************************************/ -import {createAction} from 'redux-actions'; -import {REDUX_MODULE_API_ACTION_KEY} from './constants'; -import ApiClient from './apiClient'; -import {apiStates, getApiTypes, toApiKey, noop} from './utils'; - -export const API_CONSTANTS = { - 401: 'API_ERROR_FOUR_OH_ONE', - 418: 'API_ERROR_I_AM_A_LITTLE_TEAPOT', - // 422: 'API_ERROR_UNPROCESSABLE', - UNKNOWN_ERROR: 'API_ERROR_UNKNOWN' -} - -const getActionTypesForKeys = (type, actionCreator = noop, metaCreator = noop) => getApiTypes(type) - .reduce((memo, key, idx) => ({ - ...memo, - [apiStates[idx]]: createAction(toApiKey(key), actionCreator, (...args) => ({isApi: true, ...metaCreator(args)})) - }), {}); - -// Define a decorator for a function defined in an object -// that is expected to be an action. -// @param type - string constant -export function createApiAction(type, requestTransforms, responseTransforms, metaCreator) { - // The decorator function wrapper (design: decoratorFn(fn())) - // @param target - function to be decorated - // @param name - the function name - // @param def - the Object.defineProperty definition - // - // Functionally, this works by accepting the object definition type - // from ES5 syntax, i.e. - // Object.defineProperty(this, 'fetchAll', {configurable: false, value: 2}) - // and it manipulates the definition by changing the value to be a function - // that wraps the different api states, aka LOADING, SUCCESS, ERROR - return function decoration(target) { - // ApiTypes is the string constants mapped to a - // createdAction (using `createAction`) - // i.e. { loading: fetchAllLoading(), // anonymouse function - // success: fetchAllSuccess(), - // error: fetchAllError(), - // } - let apiTypes = getActionTypesForKeys(type, (t) => t, metaCreator); - // The new value for the object definition - // return function decorated(globalOpts) { - // globalOpts = globalOpts || {}; - // return a function (for thunk) - return (configurationOpts={}) => (dispatch, getState) => { - - let autoExecute = true; - if (typeof configurationOpts.autoExecute !== 'undefined') { - autoExecute = configurationOpts.autoExecute; - delete configurationOpts['autoExecute']; - } - - // Small helper to turn all functions within this decorated functions - // into a promise - let promiseWrapper = (fn) => { - return (...args) => Promise.resolve(dispatch(fn(...args))); - }; - - // The default handlers for the different states of the api request - let loading = promiseWrapper(apiTypes.loading); - let success = promiseWrapper(apiTypes.success); - let error = (errorObj) => { - const getErrorStatus = status => typeof API_CONSTANTS[status] !== 'undefined' ? - API_CONSTANTS[status] : - API_CONSTANTS.UNKNOWN_ERROR; - - const reduceError = err => dispatch(apiTypes.error({error: errorObj, body: err})); - const errType = toApiKey(getApiTypes(type)[2]); - const errStatus = errorObj && errorObj.status ? - getErrorStatus(errorObj.status) : - API_CONSTANTS.UNKNOWN_ERROR; - - const action = { - type: errType, - payload: { - error: errorObj, - status: errStatus - } - }; - - dispatch(action) - return action; - } - - const runFn = (runtimeOpts) => { - const opts = Object.assign({}, configurationOpts, runtimeOpts); - // The `initializer` is the original pre-decorated function - // let actionFn = def ? def.initializer(opts) : target; - let actionFn = target; - // Every action gets an instance of ApiClient - let client = - new ApiClient(opts, getState, requestTransforms, responseTransforms); - // NOTE, check this: do we need below version ? - // new ApiClient(opts, getState, requestTransforms, responseTransforms); - - // callAction wraps the `async` functionality that ensures - // we always get a promise returned (and can be used to pass along - // other thunk functions) - let callAction = () => { - loading(opts); - let retVal = actionFn.call(null, client, opts, getState, dispatch); - if (typeof retVal.then !== 'function') { - retVal = Promise.resolve(retVal); - } - return retVal - .then(success) - // Should we _also_ fire the original event, even if it's an API request too? - // .then((...args) => dispatch({type: type, payload: args})) - .catch(error); - }; - // NOTE, check this: do we need below version ? - // new ApiClient(opts, getState, requestTransforms, responseTransforms); - - // callAction wraps the `async` functionality that ensures - // we always get a promise returned (and can be used to pass along - // other thunk functions) - return callAction(); - }; - - const action = { - type: REDUX_MODULE_API_ACTION_KEY, - meta: { runFn, type } - } - if (autoExecute) { - dispatch(action); - } else { - return action; - } - // dispatch(action); - // return action; - }; - // }; - }; -} - - // decorator form - // @param type - string constant - // @return function - a nwe decorated function Object.defineProperty -export function api(type, requestTransforms, responseTransforms, metaCreator) { - // The decorator function wrapper (design: decoratorFn(fn())) - // @param target - function to be decorated - // @param name - the function name - // @param def - the Object.defineProperty definition - // - // Functionally, this works by accepting the object definition type - // from ES5 syntax, i.e. - // Object.defineProperty(this, 'fetchAll', {configurable: false, value: 2}) - // and it manipulates the definition by changing the value to be a function - // that wraps the different api states, aka LOADING, SUCCESS< ERROR - return function decoration(target, name, def) { - let newVal = createApiAction(type, requestTransforms, responseTransforms, metaCreator)(def.initializer); - let newDef = { - enumerable: true, - writable: true, - configurable: true, - value: newVal, - }; - - return newDef; - } -} - -export default api; diff --git a/src/lib/createApiHandler.js b/src/lib/createApiHandler.js deleted file mode 100644 index 6afcf46..0000000 --- a/src/lib/createApiHandler.js +++ /dev/null @@ -1,133 +0,0 @@ -import {upcase, apiStates, getApiTypes, toApiKey} from './utils'; - -function apiKeys(name) { - return apiStates.map((state) => upcase(`${name}_${state}`)) -} - -// Default reducers (this happens when you don't define a reducer per state) -// Order is: loading, success, error -const apiStateHandlers = [ - (state) => ({...state, loading: true}), - (state, action) => ({...state, loading: false, data: action.payload, errors: []}), - (state, action) => ({...state, loading: false, data: [], errors: action.payload}) -] - -// Mapping of the state to their default handler from above -// NOTE: this one differs from uncommented version also -const apiReducers = { - [apiStates[0]]: apiStateHandlers[0], - [apiStates[1]]: apiStateHandlers[1], - [apiStates[2]]: apiStateHandlers[2], -} - -/** - * Build the state to names handler reducer and - * the handlers object in one to avoid multiple iterations - * @constructor - * @param {string} type string - the type constant - * @returns {object} object returns with reducers and names - */ -function handlerReducersForType(type:String) { - let apiActionTypes = getApiTypes(type); - let reducers = {}; - let namesToState = {}; - apiActionTypes.forEach((key, idx) => { - reducers[apiActionTypes[idx]] = apiReducers[apiStates[idx]]; - namesToState[upcase(apiStates[idx])] = toApiKey(apiActionTypes[idx]); - }); - return {reducers, namesToState}; -} - -// Decorator for a reducer object property function -// @param String type - string constant type -// @param Function handlerFn (optional) - Function defining custom reducers that -// accepts a single argument of the api types mapped to their -// corresponding constant, i.e. -// { loading: 'FETCH_ALL_LOADING', -// success: 'FETCH_ALL_SUCCESS', -// error: 'FETCH_ALL_ERROR' } -// Usage: -// @apiHandler('FETCH_ALL', (apiStates) => {}) -// i.e: -// @apiHandler(types.SAVE_CROP) -// handleSaveCrop: (state, action) => {return new state after doing stuff..}) -export function createApiHandler(type, handlerFn) { - // The decorator function - // target is the object, - // name is the function name - // def is the object property definition - return function decoratedHandler(target) { - // Default api action handlers - let {reducers, namesToState} = handlerReducersForType(type); - - // If there is no handle function or the handle function is not - // a function, then set it to be a identity function - if (!handlerFn || typeof handlerFn !== 'function') { - handlerFn = (d) => { - return d; - } - } - - // Custom handlers for the different api states from the second argument - // If no function is passed, the identify function is used - let fnHandlerKeys = handlerFn(namesToState) || {}; - - // Compute difference between the default hanlders and our almost new target - // Keys our custom function handler overwrites defaults - let handlerKeys = Object.keys(fnHandlerKeys); - - // All handler keys, i.e. loading, success, error - let defaultReducerKeys = Object.keys(reducers); - - // The difference between what we handled and what we did not - // i.e. allows us to only overwrite 1..n without overwriting other defaults - let missingHandlerKeys = defaultReducerKeys.filter(x => handlerKeys.indexOf(x) === -1); - - let handlers = Object.assign({}, fnHandlerKeys, {}); - // Set defaults for those undefined by custom function - missingHandlerKeys.forEach(k => handlers[k] = reducers[k]); - - // The handler passed by the decorated function - let valueHandler = target; - - // ALWAYS set the function to be the defined function - handlers[namesToState.SUCCESS] = valueHandler; - - // Return the new object property definition - return handlers; - } -} - -// decorator form -// @param type - string constant -// @return function - a nwe decorated function Object.defineProperty -export function apiHandler(type, handlerFn) { - // The decorator function wrapper (design: decoratorFn(fn())) - // @param target - function to be decorated - // @param name - the function name - // @param def - the Object.defineProperty definition - // - // Functionally, this works by accepting the object definition type - // from ES5 syntax, i.e. - // Object.defineProperty(this, 'fetchAll', {configurable: false, value: 2}) - // and it manipulates the definition by changing the value to be a function - // that wraps the different api states, aka LOADING, SUCCESS< ERROR - return function decoration(target, name, def) { - let valueHandler = def.initializer(type); - let handlers = createApiHandler(type, handlerFn)(valueHandler); - - // Dark and murky overwrite object in place -- possibly the grossest thing ever - Object.assign(target, handlers); - - let newDef = { - enumerable: true, - writable: true, - configurable: true, - value: handlers, - }; - - return newDef; - } -} - -export default apiHandler; diff --git a/src/lib/createApiMiddleware.js b/src/lib/createApiMiddleware.js deleted file mode 100644 index ebbcb6a..0000000 --- a/src/lib/createApiMiddleware.js +++ /dev/null @@ -1,20 +0,0 @@ -import {REDUX_MODULE_API_ACTION_KEY} from './constants'; -/* - * Decorate the api actions with common handlers - */ -export const createApiMiddleware = (baseOpts) => { - return store => next => action => { - - if (action.type === REDUX_MODULE_API_ACTION_KEY) { - const { runFn: fn } = action && action.meta || {}; - if (fn) { - const res = fn(baseOpts); - return res; - } - } else { - return next(action); - } - } -} - -export default createApiMiddleware; diff --git a/src/lib/createReducer.js b/src/lib/createReducer.js deleted file mode 100644 index 5fd428e..0000000 --- a/src/lib/createReducer.js +++ /dev/null @@ -1,6 +0,0 @@ -export const createReducer = function(actionHandlers, initialState={}) { - return function(state = initialState, action): Object { - const handler = actionHandlers[action.type]; - return handler ? handler(state, action) : state; - } -} diff --git a/src/lib/createRootReducer.js b/src/lib/createRootReducer.js deleted file mode 100644 index 0bd1ad8..0000000 --- a/src/lib/createRootReducer.js +++ /dev/null @@ -1,53 +0,0 @@ -// create root reducer -import { combineReducers } from 'redux'; - -const noop = (r) => r; -export const createRootReducer = (modules, { - initialActions={}, - initialInitialState={}, - initialReducers={} -}={}) => { - // Copies of initially passed in config objects - let actions = Object.assign({}, initialActions); - let initialState = Object.assign({}, initialInitialState); - let reducers = Object.assign({}, initialReducers); - - Object.keys(modules).forEach(key => { - const module = modules[key]; - - initialState[key] = Object.assign({}, - initialState[key] || {}, - module.initialState || {}); - - actions[key] = Object.assign({}, - actions[key] || {}, - module.actions || {}); - - reducers[key] = module.reducer || noop; - }); - - return {initialState, actions, reducers} -} -// const containers = { -// events: require('./modules/events'), -// currentEvent: require('./modules/currentEvent'), -// images: require('./modules/images'), -// users: require('./modules/users') -// } -// export let actions = { -// routing: { -// navigateTo: path => dispatch => dispatch(push(path)) -// } -// } -// -// export let initialState = {} -// export let reducers = {routing}; -// -// Object.keys(containers).forEach(key => { -// const container = containers[key]; -// initialState[key] = container.initialState || {}; -// actions[key] = container.actions; -// reducers[key] = container.reducer; -// }); -// -// export const rootReducer = combineReducers(reducers); diff --git a/src/lib/utils.js b/src/lib/utils.js deleted file mode 100644 index 9839a43..0000000 --- a/src/lib/utils.js +++ /dev/null @@ -1,59 +0,0 @@ -import invariant from 'invariant'; - -/* - * Default api states - */ -export const apiStates = ['loading', 'success', 'error']; - -export const upcase = (str) => str.toUpperCase(); - -// Polyfill Array.isArray -if (!Array.isArray) { - Array.isArray = function(arg) { - return Object.prototype.toString.call(arg) === '[object Array]'; - }; -} - -/* - * apiKeys to fetch the api key type - */ -export function apiKeys(name, states = apiStates) { - return states.map((state) => upcase(`${name}_${state}`)) -} - -export const toApiKey = (key) => upcase(`api_${key}`) -export const getApiTypes = (type) => apiKeys(type).reduce((memo, key) => memo.concat(key), []); - -export const noop = (r) => r; -export const syncEach = (initial, cb) => { - invariant(initial, 'No initial value defined'); - - cb = typeof cb === 'function' ? cb : noop; - - return (list) => { - return new Promise(function(resolve, reject) { - list = list.slice(0); - - const doNext = (val) => { - if (list.length) { - let item = list.shift(); - - let fn = typeof item === 'function' ? cb(item) : cb; - - let ret; - if ('then' in Object(val)) { - ret = val.then((res) => fn(res, item)) - } else { - ret = Promise.resolve(fn(val, item)) - } - - ret.then(doNext, reject); - } else { - resolve(val); - } - } - - doNext(initial); - }); - } -} diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index bd4f4a5..0000000 --- a/webpack.config.js +++ /dev/null @@ -1,105 +0,0 @@ -// require('babel-register'); - -const env = process.env; -const NODE_ENV = process.env.NODE_ENV; -const isDev = NODE_ENV === 'development'; -const isTest = NODE_ENV === 'test'; - -const webpack = require('webpack'); -const marked = require('marked'); -const fs = require('fs'); -const path = require('path'), - join = path.join, - resolve = path.resolve; - -const root = resolve(__dirname); -const src = join(root, 'src'); -const examples = join(root, 'www'); -const modules = join(root, 'node_modules'); -const dest = join(root, 'public'); - -const getConfig = require('hjs-webpack') - -var config = getConfig({ - isDev, - in: join(examples, 'index.js'), - out: dest, - clearBeforeBuild: true, - html: function(context, cb) { - context.publicPath = isDev ? 'http://localhost:3000/' : '' - - fs.readFile(join(root, 'README.md'), (err, data) => { - if (err) { - return cb(err); - } - cb(null, { - 'index.html': context.defaultTemplate(), - }) - }) - } -}); - -const dotenv = require('dotenv'); -const envVariables = dotenv.config(); - -// Converts keys to be surrounded with __ -const defines = - Object.keys(envVariables) - .reduce((memo, key) => { - const val = JSON.stringify(envVariables[key]); - memo[`__${key.toUpperCase()}__`] = val; - return memo; - }, { - __NODE_ENV__: JSON.stringify(env.NODE_ENV), - __IS_DEV__: isDev - }) - - -config.externals = { - 'window.google': true -} - -// Setup css modules require hook so it works when building for the server -const cssModulesNames = `${isDev ? '[path][name]__[local]__' : ''}[hash:base64:5]`; -const matchCssLoaders = /(^|!)(css-loader)($|!)/; - -const findLoader = (loaders, match, fn) => { - const found = loaders.filter(l => l && l.loader && l.loader.match(match)) - return found ? found[0] : null; -} - -const cssloader = findLoader(config.module.loaders, matchCssLoaders); -const newloader = Object.assign({}, cssloader, { - test: /\.module\.css$/, - include: [src, examples], - loader: cssloader.loader.replace(matchCssLoaders, `$1$2?modules&localIdentName=${cssModulesNames}$3`) -}) -config.module.loaders.push(newloader); -cssloader.test = new RegExp(`[^module]${cssloader.test.source}`) -cssloader.loader = 'style!css!postcss' - -cssloader.include = [src, examples]; - -config.module.loaders.push({ - test: /\.css$/, - include: [modules], - loader: 'style!css' -}); - -config.module.loaders.push({ - test: /\.md$/, - include: [join(root, 'README.md')], - loader: 'markdown-with-front-matter' -}) - -config.plugins = [ - new webpack.DefinePlugin(defines) -].concat(config.plugins); - -config.postcss = [].concat([ - require('precss')({}), - require('autoprefixer')({}), - require('cssnano')({}) -]) - -module.exports = config; diff --git a/www/Container.js b/www/Container.js deleted file mode 100644 index 5b98f77..0000000 --- a/www/Container.js +++ /dev/null @@ -1,50 +0,0 @@ -import React, {PropTypes as T} from 'react' -import ReactDOM from 'react-dom' -import {Link} from 'react-router' -import GitHubForkRibbon from 'react-github-fork-ribbon' - -import styles from './styles.module.css' -import Readme from './readme'; - -export const Container = React.createClass({ - - propTypes: { - children: T.element, - readme: T.object, - highlight: T.func - }, - - contextTypes: { - router: T.object - }, - - componentDidMount: function() { - this.props.highlight(); - }, - - render: function() { - const {routeMap, routeDef} = this.props; - const {router} = this.context; - - return ( -
- - Fork me on GitHub - -
-
-
-

Redux modules

-

Readme

-
- -
-
-
- ) - } -}) - -export default Container; diff --git a/www/global.styles.css b/www/global.styles.css deleted file mode 100644 index 7ef5a59..0000000 --- a/www/global.styles.css +++ /dev/null @@ -1,12 +0,0 @@ -@import url("../node_modules/highlight.js/styles/tomorrow-night.css"); -@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,300); - -#readme { - font-family: 'Open Sans', sans-serif; - font-size: 18px; - padding: 10px; - - img { - width: 100%; - } -} diff --git a/www/index.js b/www/index.js deleted file mode 100644 index 7d9dd09..0000000 --- a/www/index.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom' -import {Router, hashHistory, Redirect, Route, IndexRoute, Link} from 'react-router' - -import styles from './global.styles.css'; -import Container from './Container' - -const highlight = () => { - const hljs = require('highlight.js'); - - const codes = document.querySelectorAll('pre code'); - for (var i = 0; i < codes.length; i++) { - const block = codes[i] - hljs.highlightBlock(block); - } - return hljs; -} - -const mountNode = document.querySelector('#root') -const main = -ReactDOM.render(main, mountNode); diff --git a/www/readme.js b/www/readme.js deleted file mode 100644 index 7bc4c54..0000000 --- a/www/readme.js +++ /dev/null @@ -1,64 +0,0 @@ -import React, {Component, PropTypes as T} from 'react' -import ReactDOM, {render} from 'react-dom'; - -import styles from './styles.module.css'; - -const readme = require("../README.md") - -import Inspector from 'react-inspector'; -import { createConstants } from '../src/index'; - -const Demo = (props) => ( -
-

{props.name || 'Demo'}

- {props.children} -
-) - -// Examples -class CreateConstantsExample extends Component { - render() { - const types = createConstants('TODO')({ - 'CREATE': 'unimportant', - 'FETCH_ALL': { api: true } - }); - const str = `createConstants('TODO')({ - 'CREATE': true, // value is ignored - 'FETCH_ALL': { api: true } -}); ` - return ( - -
 {str} 
- -
- ) - } -} - -export class Readme extends React.Component { - componentDidMount() { - this.props.highlight(); - this.mountExamples(); - } - - componentDidUpdate() { - this.props.highlight(); - this.mountExamples(); - } - - mountExamples() { - const constantExampleNode = document.querySelector('#constantExample') - render(, constantExampleNode); - } - - render() { - return ( -
-
-
- ) - } -} - -export default Readme; diff --git a/www/styles.module.css b/www/styles.module.css deleted file mode 100644 index 3c9ee8b..0000000 --- a/www/styles.module.css +++ /dev/null @@ -1,40 +0,0 @@ -$dominantColor: #1BCAFF; -$listColor: #333; -$textColor: #333; - -@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,300); - -html, body { - height: 100%; - margin: 0; - padding: 0; -} -.container { - font-family: 'Open Sans', sans-serif; - font-weight: lighter; -} -.header { - font-size: 0.5em; - background: color($dominantColor a(80%)); - padding: 10px; - margin: 0; - color: $textColor; - text-align: center; - border-bottom: 2px solid color($dominantColor blackness(40%)); -} -.wrapper { - .content { - position: relative; - min-height: 100%; - } - - .demo { - border: 1px solid #efefef; - padding: 10px; - } - - .container { - padding: 15px; - margin: 10px 0; - } -}