├── .editorconfig ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── README.md ├── examples ├── index.html ├── src │ ├── app.js │ ├── contact-form.js │ ├── dynamic-validation.js │ ├── index.js │ └── redux │ │ ├── index.js │ │ └── root.reducer.js └── style.css ├── package-lock.json ├── package.json ├── src ├── __tests__ │ ├── creditcard.spec.js │ ├── digits.spec.js │ ├── email.spec.js │ ├── equalTo.spec.js │ ├── matchField.spec.js │ ├── max.spec.js │ ├── maxLength.spec.js │ ├── min.spec.js │ ├── minLength.spec.js │ ├── oneOf.spec.js │ ├── pattern.spec.js │ ├── promise.spec.js │ ├── required.spec.js │ └── setup.js ├── basic-validations.js ├── form-messages │ └── index.js ├── index.js └── validation.js └── webpack ├── base.js ├── development.js ├── example.js ├── index.js └── production.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | node_modules 4 | dist 5 | lib 6 | npm-debug.log 7 | .DS_Store -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | examples 3 | test 4 | .babelrc 5 | .eslint* 6 | .idea 7 | .npmignore 8 | .travis.yml 9 | webpack -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10" 4 | script: 5 | - npm run check:src 6 | - npm run build 7 | branches: 8 | only: 9 | - master 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.0.6 2 | - **added** `allValues` and `allProps` params to validator. 3 | It can be used to create validations that requires other fields value and props like `matchField`. 4 | example: 5 | ```javascript 6 | import FormMessages from 'redux-form-validation'; 7 | 8 | FormMessages.addValidation('matchField', function(field, value, prop, dispatch, allValues, allProps){ 9 | return !value ? false : value != allValues[prop]; 10 | }) 11 | ``` 12 | 13 | - **added** `matchField` validation. 14 | must give the matching field name as value. 15 | 16 | example: 17 | ```javascript 18 | var validations = { 19 | email: { 20 | required: true, 21 | email: true, 22 | validateOnBlur: true, 23 | }, 24 | retryEmail: { 25 | validateOnBlur: true, 26 | matchField: 'email', 27 | }, 28 | } 29 | ``` 30 | - **added** example. 31 | To run the example run `npm i -d && npm start` 32 | - **fixed** `FormMessage` component. 33 | Render error when only has one child. 34 | - **added** tests 35 | credits for this one goes to @mcalthrop 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # redux-form-validation THIS LIBRARY NOT MENTAINED 2 | 3 | [![Build Status](https://travis-ci.org/CosticaPuntaru/redux-form-validation.svg?branch=master)](https://travis-ci.org/CosticaPuntaru/redux-form-validation) 4 | [![npm version](https://img.shields.io/npm/v/redux-form-validation.svg?style=flat-square)](https://www.npmjs.com/package/redux-form-validation) 5 | [![npm downloads](https://img.shields.io/npm/dm/redux-form-validation.svg?style=flat-square)](https://www.npmjs.com/package/redux-form-validation) 6 | ## Installation 7 | ```npm install --save redux-form-validation``` 8 | 9 | ## How to use 10 | adds a helper to display and validate fields for redux-form 11 | 12 | builtin validation can be found in /src/basic-validation.js 13 | 14 | 15 | ### How to add your validation: 16 | ```javascript 17 | 18 | import FormMessages from 'redux-form-validation'; 19 | 20 | FormMessages.addValidation('required', function (field, value, prop, dispatch, allValues, allProps) { 21 | return prop ? !value : false 22 | }) 23 | ``` 24 | to make async validation you can return a promise in your validation function 25 | 26 | NOTE: all the validations are indexed by the key, if you add a `require` validation it will overwrite the validation used before 27 | The validation function can return a `message` or `true` if the field is invalid 28 | If the field is valid the validation function must return `false` 29 | 30 | 31 | ### How to display error messages in form 32 | #### Component Props: 33 | 34 | * `tagName`: Specify element type to render as message list container (default is `div`) 35 | * `errorCount` : Specify how many errors to be displayed at once (default `-1`= all) 36 | * `Meta` : The `redux-form` Meta (or for other uses a object with {touch, error}) 37 | 38 | 39 | example for how to use validator: 40 | 41 | ```javascript 42 | import React, {Component} from 'react'; 43 | import {reduxForm, Field} from 'redux-form'; 44 | import {connect} from 'react-redux'; 45 | import FormMessages from 'redux-form-validation'; 46 | import {generateValidation} from 'redux-form-validation'; 47 | 48 | const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) 49 | 50 | 51 | const validations = { 52 | email: { 53 | validateOnBlur: true, 54 | required: true, 55 | minLength: 5, 56 | email: true, 57 | promise: function (fieldName, fieldValue, dispatch) { 58 | return sleep(1000).then(() => { 59 | if (['example@example.com', 'test@example.com'].indexOf(fieldValue.trim()) > -1) { 60 | return Promise.reject('That email ' + fieldValue + ' is taken') 61 | } 62 | }) 63 | } 64 | }, 65 | retryEmail: { 66 | validateOnBlur: true, 67 | required: true, 68 | matchField: 'email', 69 | }, 70 | name: { 71 | required: true 72 | }, 73 | subject: { 74 | required: true, 75 | minLength: 5 76 | }, 77 | message: { 78 | required: true, 79 | minLength: 10 80 | } 81 | }; 82 | 83 | 84 | const submit = (values, dispatch) => { 85 | console.log('sending mail to contact', values); 86 | }; 87 | 88 | @connect() 89 | @reduxForm({ 90 | form: 'contact', 91 | ...generateValidation(validations) 92 | }) 93 | export default class ContactForm extends Component { 94 | // probably you will want to use different messages for different fields but for the demo this is good enough 95 | renderField = ({input, label, type, meta}) => { 96 | return ( 97 |
98 | 99 |
100 | 101 | 102 |
  • 103 | {meta && meta.error && meta.error.promise} 104 |
  • 105 |
  • 106 | the retry email must be the same as the email 107 |
  • 108 |
  • 109 | this field is required 110 |
  • 111 |
  • 112 | please insert a valid email 113 |
  • 114 |
  • 115 | this field must have at least 5 characters 116 |
  • 117 |
    118 |
    119 |
    120 | ); 121 | } 122 | 123 | render() { 124 | const { 125 | handleSubmit, 126 | submitting, 127 | valid, 128 | pristine, 129 | asyncValidating, 130 | } = this.props; 131 | console.log('this.props', this.props); 132 | var submitLabel = "Send"; 133 | 134 | if (pristine) { 135 | submitLabel = "Fill in your message"; 136 | } else if (asyncValidating) { 137 | submitLabel = "Validating..."; 138 | } else if (submitting) { 139 | submitLabel = "Sending..."; 140 | } else if (!valid) { 141 | submitLabel = "Please fill all fields correctly"; 142 | } 143 | return ( 144 |
    145 | 151 | 152 | 158 | 164 | 165 | 171 | 177 | 180 | 181 | ); 182 | } 183 | } 184 | 185 | 186 | ``` 187 | 188 | ## Without ES2015 189 | 190 | ```javascript 191 | var temp = generateValidation(validations); 192 | reduxForm({ 193 | form: 'contact', 194 | asyncValidate: temp.asyncValidate, 195 | asyncBlurFields: temp.asyncBlurFields, 196 | fields: temp.fields, 197 | })(YourComponent) 198 | ``` 199 | 200 | 201 | ## Examples: 202 | to run the example project you need to clone the repo and run `npm i -d && npm start` 203 | 204 | -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 |
    11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/src/app.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ContactForm from './contact-form'; 3 | import DynamicValidation from './dynamic-validation'; 4 | 5 | export default class App extends Component { 6 | render() { 7 | return ( 8 |
    9 |

    Welcome to redux-form-validation

    10 |

    Simple validation

    11 | 12 |
    13 |

    dynamic validation

    14 | 15 |
    16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/src/contact-form.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {reduxForm, Field} from 'redux-form'; 3 | import {connect} from 'react-redux'; 4 | import FormMessages from 'redux-form-validation'; 5 | import {generateValidation} from 'redux-form-validation'; 6 | 7 | const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)) 8 | 9 | 10 | const validations = { 11 | email: { 12 | validateOnBlur: true, 13 | required: true, 14 | minLength: 5, 15 | email: true, 16 | promise: function (fieldName, fieldValue, dispatch) { 17 | return sleep(1000).then(() => { 18 | if (['example@example.com', 'test@example.com'].indexOf(fieldValue.trim()) > -1) { 19 | return Promise.reject('That email ' + fieldValue + ' is taken') 20 | } 21 | }) 22 | } 23 | }, 24 | retryEmail: { 25 | validateOnBlur: true, 26 | required: true, 27 | matchField: 'email', 28 | }, 29 | name: { 30 | required: true 31 | }, 32 | subject: { 33 | required: true, 34 | minLength: 5 35 | }, 36 | message: { 37 | required: true, 38 | minLength: 10 39 | } 40 | }; 41 | 42 | 43 | const submit = (values, dispatch) => { 44 | console.log('sending mail to contact', values); 45 | }; 46 | 47 | @connect() 48 | @reduxForm({ 49 | form: 'contact', 50 | ...generateValidation(validations) 51 | }) 52 | export default class ContactForm extends Component { 53 | // probably you will want to use different messages for different fields but for the demo this is good enough 54 | renderField = ({input, label, type, meta}) => { 55 | return ( 56 |
    57 | 58 |
    59 | 60 | 61 |
  • 62 | {meta && meta.error && meta.error.promise} 63 |
  • 64 |
  • 65 | the retry email must be the same as the email 66 |
  • 67 |
  • 68 | this field is required 69 |
  • 70 |
  • 71 | please insert a valid email 72 |
  • 73 |
  • 74 | this field must have at least 5 characters 75 |
  • 76 |
    77 |
    78 |
    79 | ); 80 | } 81 | 82 | render() { 83 | const { 84 | handleSubmit, 85 | submitting, 86 | valid, 87 | pristine, 88 | asyncValidating, 89 | } = this.props; 90 | console.log('this.props', this.props); 91 | var submitLabel = "Send"; 92 | 93 | if (pristine) { 94 | submitLabel = "Fill in your message"; 95 | } else if (asyncValidating) { 96 | submitLabel = "Validating..."; 97 | } else if (submitting) { 98 | submitLabel = "Sending..."; 99 | } else if (!valid) { 100 | submitLabel = "Please fill all fields correctly"; 101 | } 102 | return ( 103 |
    104 | 110 | 111 | 117 | 123 | 124 | 130 | 136 | 139 | 140 | ); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /examples/src/dynamic-validation.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {reduxForm, Field} from 'redux-form'; 3 | import {connect} from 'react-redux'; 4 | import FormMessages from 'redux-form-validation'; 5 | import {generateValidation} from 'redux-form-validation'; 6 | 7 | 8 | var validations = { 9 | email: { 10 | validateOnBlur: true, 11 | required: true, 12 | minLength: 5, 13 | email: true, 14 | promise: function (fieldName, fieldValue, dispatch) { 15 | return new Promise((resolve, reject) => { 16 | setTimeout(function () { 17 | if (fieldValue == 'example@example.com') { 18 | resolve() 19 | } else { 20 | reject('The mail must be example@example.com, "' + (fieldValue || 'none') + '" given!') 21 | } 22 | }, 1000) 23 | }) 24 | } 25 | }, 26 | retryEmail: { 27 | required: true, 28 | matchField: 'email', 29 | }, 30 | name: { 31 | required: true 32 | }, 33 | subject: { 34 | required: true, 35 | minLength: 5 36 | }, 37 | message: { 38 | required: true, 39 | minLength: 10 40 | } 41 | }; 42 | 43 | 44 | const submit = (values, dispatch) => { 45 | console.log('sending mail to contact', values); 46 | }; 47 | 48 | 49 | export default class FormController extends Component { 50 | constructor() { 51 | super(); 52 | this.state = { 53 | formValidate: {} 54 | }; 55 | this.init(); 56 | } 57 | 58 | init() { 59 | setTimeout(() => { 60 | this.setState({ 61 | formValidate: generateValidation(validations) 62 | }) 63 | }, 3000); 64 | } 65 | 66 | render() { 67 | if (!Object.keys(this.state.formValidate).length) { 68 | return ( 69 |
    70 | loading.... 71 |
    72 | ) 73 | } 74 | return ( 75 |
    76 | 77 |
    78 | ) 79 | } 80 | } 81 | 82 | @connect() 83 | @reduxForm({ 84 | form: 'dynamicContact', 85 | }) 86 | export default class ContactForm extends Component { 87 | // probably you will want to use different messages for different fields but for the demo this is good enough 88 | renderField = ({input, label, type, meta}) => { 89 | return ( 90 |
    91 | 92 |
    93 | 94 | 95 |
  • 96 | {meta && meta.error && meta.error.promise} 97 |
  • 98 |
  • 99 | the retry email must be the same as the email 100 |
  • 101 |
  • 102 | this field is required 103 |
  • 104 |
  • 105 | please insert a valid email 106 |
  • 107 |
  • 108 | this field must have at least 5 characters 109 |
  • 110 |
    111 |
    112 |
    113 | ); 114 | } 115 | 116 | render() { 117 | const { 118 | handleSubmit, 119 | submitting, 120 | valid, 121 | pristine, 122 | asyncValidating, 123 | } = this.props; 124 | console.log('this.props', this.props); 125 | var submitLabel = "Send"; 126 | 127 | if (pristine) { 128 | submitLabel = "Fill in your message"; 129 | } else if (asyncValidating) { 130 | submitLabel = "Validating..."; 131 | } else if (submitting) { 132 | submitLabel = "Sending..."; 133 | } else if (!valid) { 134 | submitLabel = "Please fill all fields correctly"; 135 | } 136 | return ( 137 |
    138 | 144 | 145 | 151 | 157 | 158 | 164 | 170 | 173 | 174 | ); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /examples/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './app'; 4 | 5 | import { Provider } from 'react-redux'; 6 | 7 | import configureStore from './redux'; 8 | 9 | const store = configureStore(); 10 | 11 | ReactDOM.render( 12 | ( 13 | 14 | 15 | 16 | ), 17 | document.getElementById('root') 18 | ); -------------------------------------------------------------------------------- /examples/src/redux/index.js: -------------------------------------------------------------------------------- 1 | import { createStore } from 'redux' 2 | import rootReducer from './root.reducer' 3 | 4 | export default function configureStore(initialState) { 5 | const store = createStore(rootReducer, initialState) 6 | 7 | if (module.hot) { 8 | // Enable Webpack hot module replacement for reducers 9 | module.hot.accept('./root.reducer', () => { 10 | const nextReducer = require('./root.reducer').default 11 | store.replaceReducer(nextReducer) 12 | }) 13 | } 14 | 15 | return store; 16 | } -------------------------------------------------------------------------------- /examples/src/redux/root.reducer.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import {reducer as formReducer} from 'redux-form'; 3 | 4 | export default combineReducers({ 5 | form: formReducer 6 | }); -------------------------------------------------------------------------------- /examples/style.css: -------------------------------------------------------------------------------- 1 | 2 | label{ 3 | width:150px; 4 | display: inline-block; 5 | text-align: left; 6 | } 7 | button{ 8 | margin-top: 20px; 9 | } 10 | body{ 11 | text-align: center; 12 | } 13 | ul{ 14 | list-style: none; 15 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redux-form-validation", 3 | "version": "1.0.0-rc1", 4 | "description": "An helper to make redux form validation easyer", 5 | "main": "lib/index.js", 6 | "module": "./src/index.js", 7 | "jsnext:main": "./src/index.js", 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/CosticaPuntaru/redux-form-validation.git" 11 | }, 12 | "keywords": [ 13 | "react", 14 | "redux", 15 | "redux-form", 16 | "validation", 17 | "redux-form-validation", 18 | "react-component" 19 | ], 20 | "author": "Costica Puntaru ", 21 | "license": "ISC", 22 | "bugs": { 23 | "url": "https://github.com/CosticaPuntaru/redux-form-validation/issues" 24 | }, 25 | "homepage": "https://github.com/CosticaPuntaru/redux-form-validation#readme", 26 | "scripts": { 27 | "start": "webpack-dev-server --config ./webpack/example.js", 28 | "check:src": "npm run lint && npm run test", 29 | "build": "npm run build:lib && npm run build:umd && npm run build:umd:min", 30 | "build:lib": "babel src --out-dir lib", 31 | "build:umd": "webpack src/index.js dist/redux-form-validation.js --config webpack/development.js", 32 | "build:umd:min": "webpack src/index.js dist/redux-form-validation.min.js --config webpack/production.js", 33 | "clean": "rimraf dist lib", 34 | "lint": "eslint src", 35 | "prepublish": "npm run clean && npm run test && npm run build", 36 | "test": "npm run lint && mocha --compilers js:babel/register --recursive src/__tests__/*.spec.js --require src/__tests__/setup.js" 37 | }, 38 | "devDependencies": { 39 | "autoprefixer-loader": "^3.1.0", 40 | "babel": "^5.8.23", 41 | "babel-core": "^5.8.25", 42 | "babel-eslint": "^4.1.3", 43 | "babel-loader": "^5.3.2", 44 | "css-loader": "^0.23.1", 45 | "eslint": "^1.9.0", 46 | "eslint-config-airbnb": "^0.1.0", 47 | "eslint-plugin-react": "^3.5.1", 48 | "expect": "^1.11.1", 49 | "html-loader": "^0.4.0", 50 | "jsdom": "^7.0.1", 51 | "mocha": "^5.2.0", 52 | "react": "^16.0.0", 53 | "react-addons-test-utils": "^15.6.2", 54 | "react-dom": "^16.0.0", 55 | "react-hot-loader": "^1.3.0", 56 | "react-redux": "^5.0.7", 57 | "redux": "^3.3.1", 58 | "redux-form": "^7.4.2", 59 | "rifraf": "^2.0.2", 60 | "rimraf": "^2.4.3", 61 | "style-loader": "^0.13.0", 62 | "webpack": "^1.12.3", 63 | "webpack-dev-server": "^1.14.0" 64 | }, 65 | "npmName": "redux-form-validation", 66 | "npmFileMap": [ 67 | { 68 | "basePath": "/dist/", 69 | "files": [ 70 | "*.js" 71 | ] 72 | } 73 | ], 74 | "dependencies": { 75 | "is-promise": "^2.1.0", 76 | "prop-types": "^15.6.1", 77 | "valid-url": "^1.0.9" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/__tests__/creditcard.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { creditcard } from '../basic-validations'; 3 | // Sample valid CC numbers, split up into 4 separate pieces 4 | // Source: http://www.getcreditcardnumbers.com/ 5 | const cards = [ 6 | { type: 'Amex', pieces: [3483, 2314, 1931, 128] }, 7 | { type: 'Diners', pieces: [3017, 5465, 5838, 90] }, 8 | { type: 'Discover', pieces: [6011, 6855, 1062, 3624] }, 9 | { type: 'JCB', pieces: [3528, 7139, 4254, 4326] }, 10 | { type: 'JCB 15', pieces: [2100, 1991, 2694, 367] }, 11 | { type: 'MasterCard', pieces: [5519, 4093, 1282, 6387] }, 12 | { type: 'Visa 13', pieces: [4225, 4269, 4946, 6] }, 13 | { type: 'Visa 16', pieces: [4532, 6628, 9297, 4839] }, 14 | ]; 15 | 16 | const generate = (pieces, separator = '') => 17 | pieces.join(separator); 18 | 19 | const generateValid = (pieces, separator) => 20 | generate(pieces, separator); 21 | 22 | const generateInvalid = (pieces, separator) => { 23 | // only one digit in the card number needs to be altered in order for it to be invalid 24 | const invalidCardNumber = [pieces[0], pieces[1], pieces[2], pieces[3] + 1]; 25 | 26 | return generate(invalidCardNumber, separator) 27 | }; 28 | 29 | // Note: each validator returns `true` when the `value` is invalid 30 | describe('Validator: creditcard', function () { 31 | describe('Should be valid', () => { 32 | describe('With no separator', () => { 33 | it('When `prop` is valid card number', () => { 34 | cards.forEach((card) => { 35 | console.log('Card type:', card.type); 36 | assert(creditcard(true, generateValid(card.pieces)) === false); 37 | }); 38 | }); 39 | }); 40 | describe('With space separator', () => { 41 | it('When `prop` is valid card number', () => { 42 | cards.forEach((card) => { 43 | console.log('Card type:', card.type); 44 | assert(creditcard(true, generateValid(card.pieces, ' ')) === false); 45 | }); 46 | }); 47 | }); 48 | describe('With "-" separator', () => { 49 | it('When `prop` is valid card number', () => { 50 | cards.forEach((card) => { 51 | console.log('Card type:', card.type); 52 | assert(creditcard(true, generateValid(card.pieces, '-')) === false); 53 | }); 54 | }); 55 | }); 56 | }); 57 | describe('Should be invalid', () => { 58 | describe('With no separator', () => { 59 | it('When `prop` is valid card number', () => { 60 | cards.forEach((card) => { 61 | console.log('Card type:', card.type); 62 | assert(creditcard(true, generateInvalid(card.pieces)) === true); 63 | }); 64 | }); 65 | }); 66 | describe('With space separator', () => { 67 | it('When `prop` is valid card number', () => { 68 | cards.forEach((card) => { 69 | console.log('Card type:', card.type); 70 | assert(creditcard(true, generateInvalid(card.pieces, ' ')) === true); 71 | }); 72 | }); 73 | }); 74 | describe('With "-" separator', () => { 75 | it('When `prop` is valid card number', () => { 76 | cards.forEach((card) => { 77 | console.log('Card type:', card.type); 78 | assert(creditcard(true, generateInvalid(card.pieces, '-')) === true); 79 | }); 80 | }); 81 | }); 82 | describe('With non-number values', () => { 83 | it('When `prop` contains letters', () => { 84 | assert(creditcard(true, 'blah99') === true); 85 | }); 86 | }); 87 | describe('With number too long', () => { 88 | it('When `prop` contains number with length 1 longer than valid number', () => { 89 | cards.forEach((card) => { 90 | console.log('Card type:', card.type); 91 | assert(creditcard(true, `${generateValid(card.pieces)}9`) === true); 92 | }); 93 | }); 94 | }); 95 | }); 96 | }); 97 | -------------------------------------------------------------------------------- /src/__tests__/digits.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { digits } from '../basic-validations'; 3 | 4 | // Note: each validator returns `true` when the `value` is invalid 5 | describe('Validator: digits', function () { 6 | describe('Should be valid', () => { 7 | it('When `prop` is "999"', () => { 8 | assert(digits(true, '999') === false); 9 | }); 10 | it('When `prop` is 999', () => { 11 | assert(digits(true, 999) === false); 12 | }); 13 | }); 14 | describe('Should be invalid', () => { 15 | it('When `prop` is "blah"', () => { 16 | assert(digits({}, 'blah') === true); 17 | }); 18 | it('When `prop` is "blah77"', () => { 19 | assert(digits({}, 'blah77') === true); 20 | }); 21 | it('When `prop` is "4,000"', () => { 22 | assert(digits({}, '4,000') === true); 23 | }); 24 | it('When `prop` is "77.7"', () => { 25 | assert(digits({}, '77.7') === true); 26 | }); 27 | it('When `prop` is 77.7', () => { 28 | assert(digits({}, 77.7) === true); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/__tests__/email.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { email } from '../basic-validations'; 3 | 4 | // Note: each validator returns `true` when the `value` is invalid 5 | describe('Validator: email', function () { 6 | it('should be invalid when `prop` is "x"', function () { 7 | assert(email({}, 'x', true) === true); 8 | }); 9 | it('should be invalid when `prop` is "x@"', function () { 10 | assert(email({}, 'x@', true) === true); 11 | }); 12 | it('should be invalid when `prop` is "@x"', function () { 13 | assert(email({}, '@x', true) === true); 14 | }); 15 | it('should be invalid when `prop` is "x@x"', function () { 16 | // Note: we are assuming that the domain name will contain at least one '.'. 17 | // So `localhost`, for example, will not be considered a valid domain name. 18 | assert(email({}, 'x@x', true) === true); 19 | }); 20 | it('should be invalid when `prop` is "x@x."', function () { 21 | assert(email({}, 'x@x.', true) === true); 22 | }); 23 | it('should be valid when `prop` is "x@x.x"', function () { 24 | assert(email({}, 'x@x.x', true) === false); 25 | }); 26 | it('should be valid when `prop` is "x@x.xx"', function () { 27 | assert(email({}, 'x@x.xx', true) === false); 28 | }); 29 | it('should be valid when `prop` is "x@xx.xx"', function () { 30 | assert(email({}, 'x@xx.xx', true) === false); 31 | }); 32 | it('should be valid when `prop` is "x@192.16.0.10"', function () { 33 | assert(email({}, 'x@192.16.0.10', true) === false); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/__tests__/equalTo.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { equalTo } from '../basic-validations'; 3 | 4 | // Note: each validator returns `true` when the `value` is invalid 5 | describe('Validator: equalTo', function () { 6 | it('should be valid when `value` (string) equals `prop` (string)', function () { 7 | assert(equalTo({}, 'matcher', 'matcher') === false); 8 | }); 9 | it('should be invalid when `value` (string) does not equal `prop` (string)', function () { 10 | assert(equalTo({}, 'non-matcher', 'matcher') === true); 11 | }); 12 | it('should be valid when `value` (number) equals `prop` (number)', function () { 13 | assert(equalTo({}, 77, 77) === false); 14 | }); 15 | it('should be valid when `value` (number) does not equal `prop` (number)', function () { 16 | assert(equalTo({}, 77, 88) === true); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/__tests__/matchField.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { matchField } from '../basic-validations'; 3 | 4 | // Note: each validator returns `true` when the `value` is invalid 5 | describe('Validator: matchField', function () { 6 | it('should be valid when `value` (string) equals value of the given fieldName', function () { 7 | assert(matchField({}, 'matcher', 'otherField', false, {otherField: 'matcher'} ) === false); 8 | }); 9 | 10 | it('should not be valid when `value` (string) not equals value of the given fieldName', function () { 11 | assert(matchField({}, 'matcher', 'otherField', false, {otherField: 'non-matcher'} ) === true); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/__tests__/max.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { max } from '../basic-validations'; 3 | 4 | // Note: each validator returns `true` when the `value` is invalid 5 | describe('Validator: max', function () { 6 | it('should be valid when `value` is undefined and `prop` is 3', function () { 7 | assert(max({}, undefined, 3) === false); 8 | }); 9 | it('should be valid when `value` is "99" and `prop` is 100', function () { 10 | assert(max({}, '99', 100) === false); 11 | }); 12 | it('should be valid when `value` is "100" and `prop` is 100', function () { 13 | assert(max({}, '100', 100) === false); 14 | }); 15 | it('should be invalid when `value` is "101" and `prop` is 100', function () { 16 | assert(max({}, '101', 100) === true); 17 | }); 18 | it('should be valid when `value` is 99 and `prop` is 100', function () { 19 | assert(max({}, 99, 100) === false); 20 | }); 21 | it('should be valid when `value` is 100 and `prop` is 100', function () { 22 | assert(max({}, 100, 100) === false); 23 | }); 24 | it('should be invalid when `value` is 101 and `prop` is 100', function () { 25 | assert(max({}, 101, 100) === true); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/__tests__/maxLength.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { maxLength } from '../basic-validations'; 3 | 4 | // Note: each validator returns `true` when the `value` is invalid 5 | describe('Validator: maxLength', function () { 6 | it('should be valid when `value` is undefined and `prop` is 3', function () { 7 | assert(maxLength({}, undefined, 3) === false); 8 | }); 9 | it('should be invalid when `value` is "1234" and `prop` is 3', function () { 10 | assert(maxLength({}, '1234', 3) === true); 11 | }); 12 | it('should be valid when `value` is "1234" and `prop` is 4', function () { 13 | assert(maxLength({}, '1234', 4) === false); 14 | }); 15 | it('should be valid when `value` is "1234" and `prop` is 5', function () { 16 | assert(maxLength({}, '1234', 5) === false); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/__tests__/min.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { min } from '../basic-validations'; 3 | 4 | // Note: each validator returns `true` when the `value` is invalid 5 | describe('Validator: min', function () { 6 | it('should be valid when `value` is undefined and `prop` is 3', function () { 7 | assert(min({}, undefined, 3) === false); 8 | }); 9 | it('should be invalid when `value` is "99" and `prop` is 100', function () { 10 | assert(min({}, '99', 100) === true); 11 | }); 12 | it('should be valid when `value` is "100" and `prop` is 100', function () { 13 | assert(min({}, '100', 100) === false); 14 | }); 15 | it('should be valid when `value` is "101" and `prop` is 100', function () { 16 | assert(min({}, '101', 100) === false); 17 | }); 18 | it('should be invalid when `value` is 99 and `prop` is 100', function () { 19 | assert(min({}, 99, 100) === true); 20 | }); 21 | it('should be valid when `value` is 100 and `prop` is 100', function () { 22 | assert(min({}, 100, 100) === false); 23 | }); 24 | it('should be valid when `value` is 101 and `prop` is 100', function () { 25 | assert(min({}, 101, 100) === false); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/__tests__/minLength.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { minLength } from '../basic-validations'; 3 | 4 | // Note: each validator returns `true` when the `value` is invalid 5 | describe('Validator: minLength', function () { 6 | it('should be valid when `value` is undefined and `prop` is 3', function () { 7 | assert(minLength({}, undefined, 3) === false); 8 | }); 9 | it('should be valid when `value` is "1234" and `prop` is 3', function () { 10 | assert(minLength({}, '1234', 3) === false); 11 | }); 12 | it('should be valid when `value` is "1234" and `prop` is 4', function () { 13 | assert(minLength({}, '1234', 4) === false); 14 | }); 15 | it('should be invalid when `value` is "1234" and `prop` is 5', function () { 16 | assert(minLength({}, '1234', 5) === true); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/__tests__/oneOf.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { oneOf } from '../basic-validations'; 3 | 4 | // Note: each validator returns `true` when the `value` is invalid 5 | describe('Validator: oneOf', function () { 6 | it('should be valid when `value` (string) is listed in `prop`', function () { 7 | assert(oneOf({}, 'bb', ['aa', 'bb', 'cc']) === false); 8 | }); 9 | it('should be invalid when `value` (string) is not listed in `prop`', function () { 10 | assert(oneOf({}, 'zz', ['aa', 'bb', 'cc']) === true); 11 | }); 12 | it('should be valid when `value` (number) is listed in `prop`', function () { 13 | assert(oneOf({}, 13, [11, 12, 13]) === false); 14 | }); 15 | it('should be invalid when `value` (number) is not in `prop`', function () { 16 | assert(oneOf({}, 99, [11, 12, 13]) === true); 17 | }); 18 | it('should throw TypeError when `prop` is boolean', function () { 19 | assert.throws( 20 | () => oneOf({}, 'z', true), 21 | TypeError 22 | ); 23 | }); 24 | it('should throw TypeError when `prop` is object', function () { 25 | assert.throws( 26 | () => oneOf({}, 'z', {}), 27 | TypeError 28 | ); 29 | }); 30 | it('should throw TypeError when `prop` is number', function () { 31 | assert.throws( 32 | () => oneOf({}, 'z', 99), 33 | TypeError 34 | ); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/__tests__/pattern.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { pattern } from '../basic-validations'; 3 | 4 | // Note: each validator returns `true` when the `value` is invalid 5 | describe('Validator: pattern', function () { 6 | it('should be valid when `value` matches `prop` regex', function () { 7 | assert(pattern({}, '__matcher__', /.*matcher.*/) === false); 8 | }); 9 | it('should be invalid when `value` does not match `prop` regex', function () { 10 | assert(pattern({}, '__doesNotMatch__', /.*matcher.*/) === true); 11 | }); 12 | it('should throw TypeError when `prop` is boolean', function () { 13 | assert.throws( 14 | () => pattern({}, 'z', true), 15 | TypeError 16 | ); 17 | }); 18 | it('should throw TypeError when `prop` is string', function () { 19 | assert.throws( 20 | () => pattern({}, 'z', 'a nice string'), 21 | TypeError 22 | ); 23 | }); 24 | it('should throw TypeError when `prop` is object', function () { 25 | assert.throws( 26 | () => pattern({}, 'z', {}), 27 | TypeError 28 | ); 29 | }); 30 | it('should throw TypeError when `prop` is number', function () { 31 | assert.throws( 32 | () => pattern({}, 'z', 99), 33 | TypeError 34 | ); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/__tests__/promise.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { promise } from '../basic-validations'; 3 | 4 | // Note: each validator returns `true` when the `value` is invalid 5 | describe('Validator: promise', function () { 6 | describe('Should throw Error', () => { 7 | it('When `prop` is boolean', () => { 8 | assert.throws(() => promise({}, '', true), Error); 9 | }); 10 | it('When `prop` is string', () => { 11 | assert.throws(() => promise({}, '', ''), Error); 12 | }); 13 | it('When `prop` is object', () => { 14 | assert.throws(() => promise({}, '', {}), Error); 15 | }); 16 | it('When `prop` is number', () => { 17 | assert.throws(() => promise({}, '', 46), Error); 18 | }); 19 | }); 20 | describe('Should return expected result', () => { 21 | it('When function is provided', (done) => { 22 | const testField = 1; 23 | const testValue = 2; 24 | const dispatchFn = function(){}; 25 | const testProp = (field, value, dispatch) => { 26 | assert(field === testField); 27 | assert(value === testValue); 28 | assert(dispatchFn === dispatch); 29 | done(); 30 | }; 31 | 32 | promise(testField, testValue, testProp, dispatchFn); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/__tests__/required.spec.js: -------------------------------------------------------------------------------- 1 | import assert from 'assert'; 2 | import { required } from '../basic-validations'; 3 | 4 | describe('Validator: required', function () { 5 | it('should be valid when `value` is empty and `prop` is false', function () { 6 | assert(required({}, '', false) === false); 7 | }); 8 | it('should be valid when `value` is truthy and `prop` is false', function () { 9 | assert(required({}, 'true', false) === false); 10 | }); 11 | it('should be invalid when `value` is empty and `prop` is true', function () { 12 | assert(required({}, '', true) === true); 13 | }); 14 | it('should be valid when `value` is truthy and `prop` is true', function () { 15 | assert(required({}, 'true', true) === false); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/__tests__/setup.js: -------------------------------------------------------------------------------- 1 | // test setup to be implemented -------------------------------------------------------------------------------- /src/basic-validations.js: -------------------------------------------------------------------------------- 1 | import validUrl from 'valid-url'; 2 | 3 | export default { 4 | required: function (field, value, prop) { 5 | return prop ? !value : false 6 | }, 7 | 8 | minLength: function (field, value, prop) { 9 | return prop && value ? value.length < prop : false; 10 | }, 11 | 12 | maxLength: function (field, value, prop) { 13 | return prop && value ? value.length > prop : false; 14 | }, 15 | 16 | email: function (field, value, prop) { 17 | return prop && value ? !(/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/.test(value)) : false 18 | }, 19 | 20 | min: function (field, value, prop) { 21 | return prop && value ? !isFinite(value) || parseFloat(value) < prop : false; 22 | }, 23 | 24 | max: function (field, value, prop) { 25 | return prop && value ? !isFinite(value) || parseFloat(value) > prop : false; 26 | }, 27 | 28 | pattern: function (field, value, prop) { 29 | return !value ? false : !prop.test(value); 30 | }, 31 | 32 | equalTo: function (field, value, prop) { 33 | return !value ? false : prop != value; 34 | }, 35 | 36 | oneOf: function (field, value, prop) { 37 | return !value ? false : prop.indexOf(value) == -1; 38 | }, 39 | 40 | url: function (field, value, prop) { 41 | return !value ? false : !validUrl.isUri(value); 42 | }, 43 | 44 | promise: function (field, value, prop, dispatch, values, validation, props, blurredField) { 45 | if (typeof prop == 'function') { 46 | return prop(field, value, dispatch, values, validation, props, blurredField) 47 | } 48 | throw new Error("FormValidation: type promise must be a function!") 49 | }, 50 | digits: function (field, value) { 51 | return !field || !/^\d+$/.test(value); 52 | }, 53 | creditcard: function (field, value, prop) { 54 | if (!value) { 55 | return false; 56 | } 57 | // accept only spaces, digits and dashes 58 | if (/[^0-9 \-]+/.test(value)) { 59 | return true; 60 | } 61 | var nCheck = 0, 62 | nDigit = 0, 63 | bEven = false, 64 | n, cDigit; 65 | 66 | value = value.replace(/\D/g, ""); 67 | 68 | // Basing min and max length on 69 | // http://developer.ean.com/general-info/valid-card-types/ 70 | if (value.length < 13 || value.length > 19) { 71 | return false; 72 | } 73 | 74 | for (n = value.length - 1; n >= 0; n--) { 75 | cDigit = value.charAt(n); 76 | nDigit = parseInt(cDigit, 10); 77 | if (bEven) { 78 | if ((nDigit *= 2) > 9) { 79 | nDigit -= 9; 80 | } 81 | } 82 | nCheck += nDigit; 83 | bEven = !bEven; 84 | } 85 | 86 | return (nCheck % 10) !== 0; 87 | }, 88 | matchField: function (field, value, prop, dispatch, allValues) { 89 | console.log('aaaaa', {field, value, prop, dispatch, allValues}); 90 | if (!value) { 91 | return false; 92 | } 93 | if (typeof allValues.get === 'function') { // immutableJS 94 | return value !== allValues.get(prop); 95 | } else { 96 | return value !== allValues[prop] 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/form-messages/index.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | export default class FormMessages extends PureComponent { 5 | renderChildren(children, meta, errorCount) { 6 | const {error, touched} = meta; 7 | 8 | if (touched && error) { 9 | const errorList = React.Children.toArray(children) 10 | .filter(function (child) { 11 | return child.props.when && error[child.props.when] 12 | }); 13 | 14 | const displayErrorCount = parseInt(errorCount, 10); 15 | if (displayErrorCount < 0) { 16 | return errorList; 17 | } 18 | return errorList.slice(0, displayErrorCount); 19 | } 20 | } 21 | 22 | render() { 23 | const {children, meta, errorCount, tagName: TagName, ...rest} = this.props; 24 | const errorList = this.renderChildren(children, meta, errorCount); 25 | 26 | if (!errorList || !errorList.length) { 27 | return null; 28 | } 29 | 30 | return ( 31 | 32 | {errorList} 33 | 34 | ) 35 | } 36 | }; 37 | 38 | FormMessages.defaultProps = { 39 | errorCount: -1, 40 | tagName: 'div' 41 | }; 42 | 43 | FormMessages.propTypes = { 44 | meta: PropTypes.object.isRequired, 45 | tagName: PropTypes.oneOfType([PropTypes.element, PropTypes.string]), 46 | errorCount: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) 47 | }; 48 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import FormMessages from './form-messages' 2 | import basicValidations from './basic-validations.js' 3 | 4 | import {generateValidation, addValidation, addMultipleValidations, generateAsyncBlurFields, generateAsyncValidation} from './validation.js'; 5 | 6 | export default FormMessages; 7 | export {generateValidation, addValidation, addMultipleValidations, generateAsyncBlurFields, generateAsyncValidation, basicValidations}; 8 | -------------------------------------------------------------------------------- /src/validation.js: -------------------------------------------------------------------------------- 1 | import basicValidations from './basic-validations.js' 2 | import isPromise from 'is-promise'; 3 | var validationStore = {}; 4 | 5 | export function addValidation(key, fn) { 6 | validationStore[key] = fn; 7 | } 8 | 9 | export function addMultipleValidations(obj) { 10 | Object.keys(obj).forEach((key)=> addValidation(key, obj[key])) 11 | } 12 | 13 | addMultipleValidations(basicValidations); 14 | 15 | 16 | export function generateAsyncValidation(validationConfig) { 17 | return (values, dispatch, props, blurredField) => { 18 | var promiseList = [Promise.resolve()]; 19 | var errors = {}; 20 | 21 | function addError(field, validatorName, message = true) { 22 | if (!errors[field]) { 23 | errors[field] = {}; 24 | } 25 | errors[field][validatorName] = message; 26 | 27 | } 28 | 29 | Object.keys(validationConfig).map((fieldName) => { 30 | var validation = validationConfig[fieldName]; 31 | if (typeof validation === 'object') { 32 | Object.keys(validation).map((validationType) => { 33 | if (typeof validationStore[validationType] != 'function') { 34 | return; 35 | } 36 | var value = typeof values.get == 'function' ? values.get(fieldName) : values[fieldName]; // immutableJS 37 | var hasError = validationStore[validationType](fieldName, value, validation[validationType], dispatch, values, validation, props, blurredField); 38 | if (isPromise(hasError)) { 39 | promiseList.push(new Promise((resolve, reject)=> { 40 | hasError.then(resolve).catch((msg) => { 41 | addError(fieldName, validationType, msg); 42 | resolve(); 43 | }) 44 | })) 45 | 46 | } else if (hasError) { 47 | addError(fieldName, validationType, hasError); 48 | } 49 | }) 50 | } 51 | }); 52 | return Promise.all(promiseList).then(()=> { 53 | if (Object.keys(errors).length) { 54 | return Promise.reject(errors); 55 | } 56 | }); 57 | } 58 | } 59 | 60 | export function generateAsyncBlurFields(validationConfig) { 61 | return Object.keys(validationConfig).filter((fieldName) => { 62 | return typeof(validationConfig[fieldName]) === 'object' && validationConfig[fieldName].validateOnBlur 63 | }) 64 | } 65 | 66 | export function generateValidation(validationConfig) { 67 | return { 68 | asyncValidate: generateAsyncValidation(validationConfig), 69 | asyncBlurFields: generateAsyncBlurFields(validationConfig), 70 | fields: Object.keys(validationConfig) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /webpack/base.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | 3 | var reactExternal = { 4 | root: 'React', 5 | commonjs2: 'react', 6 | commonjs: 'react', 7 | amd: 'react' 8 | }; 9 | 10 | module.exports = { 11 | externals: { 12 | 'react': reactExternal 13 | }, 14 | module: { 15 | loaders: [ 16 | { test: /\.js$/, loaders: ['babel-loader?{"stage" : 0}'], exclude: /node_modules/ } 17 | ] 18 | }, 19 | output: { 20 | library: 'ReduxFormValidation', 21 | libraryTarget: 'umd' 22 | }, 23 | resolve: { 24 | extensions: ['', '.js'] 25 | } 26 | }; -------------------------------------------------------------------------------- /webpack/development.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var webpack = require('webpack'); 4 | var baseConfig = require('./base'); 5 | 6 | var config = Object.create(baseConfig); 7 | config.plugins = [ 8 | new webpack.optimize.OccurenceOrderPlugin() 9 | ]; 10 | 11 | module.exports = config; -------------------------------------------------------------------------------- /webpack/example.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | var webpack = require('webpack'); 3 | var PORT = process.env.PORT || 3003; 4 | 5 | module.exports = { 6 | devtool: 'inline-source-map', 7 | entry: "./src/index.js", 8 | context: path.resolve(process.cwd(), "examples"), 9 | output: { 10 | path: 'examples/.tmp', 11 | filename: "bundle.js" 12 | }, 13 | module: { 14 | loaders: [ 15 | { 16 | test: /\.(js|jsx)$/, 17 | loaders: ['babel-loader?{"stage" : 0}'], 18 | exclude: /node_modules/ 19 | }, 20 | { 21 | test: /\.html$/, 22 | loaders: ['html-loader'], 23 | } 24 | ] 25 | }, 26 | plugins: [ 27 | new webpack.HotModuleReplacementPlugin(), 28 | ], 29 | resolve: { 30 | root: [path.join(process.cwd(), 'examples')], 31 | alias: {'redux-form-validation': path.join(process.cwd(), 'src/index.js')}, 32 | extensions: ['', '.js', '.jsx', '.es6', '.scss', '.css'], 33 | modulesDirectories: [ 34 | 'examples', 35 | 'node_modules', 36 | ], 37 | }, 38 | devServer: { 39 | contentBase: 'examples', 40 | noInfo: false, // --no-info option 41 | historyApiFallback: true, 42 | progress: true, 43 | hot: true, 44 | inline: true, 45 | port: PORT, 46 | }, 47 | }; -------------------------------------------------------------------------------- /webpack/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var webpack = require('webpack'); 4 | var baseConfig = require('./base'); 5 | 6 | var config = Object.create(baseConfig); 7 | config.plugins = [ 8 | new webpack.optimize.OccurenceOrderPlugin(), 9 | new webpack.DefinePlugin({ 10 | 'process.env.NODE_ENV': JSON.stringify('production') 11 | }), 12 | new webpack.optimize.UglifyJsPlugin({ 13 | compressor: { 14 | screw_ie8: true, 15 | warnings: false 16 | } 17 | }) 18 | ]; 19 | 20 | module.exports = config; -------------------------------------------------------------------------------- /webpack/production.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var webpack = require('webpack'); 4 | var baseConfig = require('./base'); 5 | 6 | var config = Object.create(baseConfig); 7 | config.plugins = [ 8 | new webpack.optimize.OccurenceOrderPlugin(), 9 | new webpack.optimize.UglifyJsPlugin({ 10 | compressor: { 11 | screw_ie8: true, 12 | warnings: false 13 | } 14 | }) 15 | ]; 16 | 17 | module.exports = config; --------------------------------------------------------------------------------