├── .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 | [](https://travis-ci.org/CosticaPuntaru/redux-form-validation)
4 | [](https://www.npmjs.com/package/redux-form-validation)
5 | [](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 |
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 |
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 |
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;
--------------------------------------------------------------------------------