diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..823d22f
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,32 @@
+module.exports = {
+ 'env': {
+ 'es6': true,
+ 'browser': true,
+ 'meteor': true,
+ 'node': true
+ },
+ 'extends': 'eslint:recommended',
+ 'ecmaFeatures': {
+ 'jsx': true,
+ 'experimentalObjectRestSpread': true
+ },
+ 'plugins': [
+ 'react'
+ ],
+ 'globals': {
+ 'BlazeLayout': true,
+ 'Documents': true,
+ 'FlowRouter': true
+ },
+ 'rules': {
+ 'comma-dangle': [2, 'never'],
+ 'eqeqeq': [2, 'smart'],
+ 'indent': [2, 2],
+ 'linebreak-style': [2, 'unix'],
+ 'no-unneeded-ternary': [2],
+ 'quotes': [2, 'single'],
+ 'semi': [2, 'always'],
+ 'space-infix-ops': [2],
+ 'space-in-parens': [2, 'always', { 'exceptions': ['{}'] }]
+ }
+};
diff --git a/.gitignore b/.gitignore
index 99c6e10..0f14a3d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
.DS_Store
+settings-development.json
settings-production.json
npm-debug.log
diff --git a/.meteor/packages b/.meteor/packages
index 73b738b..f27e710 100644
--- a/.meteor/packages
+++ b/.meteor/packages
@@ -9,9 +9,7 @@ jquery
check
audit-argument-checks
themeteorchef:jquery-validation
-twbs:bootstrap
browser-policy
-meteorhacks:npm
themeteorchef:bert
@@ -19,9 +17,7 @@ meteorhacks:ssr
standard-minifiers
-npm-container
ecmascript
-digilord:faker
kadira:flow-router
kadira:blaze-layout
meteorhacks:fast-render
@@ -34,3 +30,6 @@ reactive-var
reactive-dict
aldeed:collection2
tracker
+twbs:bootstrap
+momentjs:moment
+alanning:roles
diff --git a/.meteor/release b/.meteor/release
index 3a05e0a..9503ef1 100644
--- a/.meteor/release
+++ b/.meteor/release
@@ -1 +1 @@
-METEOR@1.2.1
+METEOR@1.3-modules-beta.6
diff --git a/.meteor/versions b/.meteor/versions
index 4483829..a2e1078 100644
--- a/.meteor/versions
+++ b/.meteor/versions
@@ -1,94 +1,100 @@
-accounts-base@1.2.2
-accounts-password@1.1.4
+accounts-base@1.2.3-modules.6
+accounts-password@1.1.5-modules.6
+alanning:roles@1.2.14
aldeed:collection2@2.6.1
aldeed:simple-schema@1.3.3
+allow-deny@1.0.1-modules.6
audit-argument-checks@1.0.4
-autoupdate@1.2.4
-babel-compiler@5.8.24_1
-babel-runtime@0.1.4
-base64@1.0.4
-binary-heap@1.0.4
-blaze@2.1.3
-blaze-tools@1.0.4
-boilerplate-generator@1.0.4
-browser-policy@1.0.5
-browser-policy-common@1.0.4
-browser-policy-content@1.0.6
-browser-policy-framing@1.0.6
-caching-compiler@1.0.0
-caching-html-compiler@1.0.2
-callback-hook@1.0.4
-check@1.1.0
+autoupdate@1.2.5-modules.6
+babel-compiler@6.4.0-modules.6
+babel-runtime@0.1.5-modules.6
+base64@1.0.5-modules.6
+binary-heap@1.0.5-modules.6
+blaze@2.1.4-modules.6
+blaze-tools@1.0.5-modules.6
+boilerplate-generator@1.0.5-modules.6
+browser-policy@1.0.6-modules.6
+browser-policy-common@1.0.5-modules.6
+browser-policy-content@1.0.7-modules.6
+browser-policy-framing@1.0.7-modules.6
+caching-compiler@1.0.1-modules.6
+caching-html-compiler@1.0.3-modules.6
+callback-hook@1.0.5-modules.6
+check@1.1.1-modules.6
chuangbo:cookie@1.1.0
-coffeescript@1.0.11
+coffeescript@1.0.12-modules.6
cosmos:browserify@0.9.2
ddp@1.2.2
-ddp-client@1.2.1
+ddp-client@1.2.2-modules.6
ddp-common@1.2.2
-ddp-rate-limiter@1.0.0
-ddp-server@1.2.2
+ddp-rate-limiter@1.0.1-modules.6
+ddp-server@1.2.3-modules.6
deps@1.0.9
-diff-sequence@1.0.1
-digilord:faker@1.0.7
-ecmascript@0.1.6
-ecmascript-runtime@0.2.6
-ejson@1.0.7
-email@1.0.8
+diff-sequence@1.0.2-modules.6
+ecmascript@0.4.0-modules.6
+ecmascript-runtime@0.2.7-modules.6
+ejson@1.0.8-modules.6
+email@1.0.9-modules.6
fortawesome:fontawesome@4.4.0_1
fourseven:scss@3.4.1
-geojson-utils@1.0.4
-hot-code-push@1.0.0
-html-tools@1.0.5
-htmljs@1.0.5
-http@1.1.1
+geojson-utils@1.0.5-modules.6
+hot-code-push@1.0.1-modules.6
+html-tools@1.0.6-modules.6
+htmljs@1.0.6-modules.6
+http@1.1.2-modules.6
id-map@1.0.4
-jquery@1.11.4
+jquery@1.11.5-modules.6
kadira:blaze-layout@2.3.0
kadira:flow-router@2.10.0
livedata@1.0.15
-localstorage@1.0.5
-logging@1.0.8
-meteor@1.1.10
+localstorage@1.0.6-modules.6
+logging@1.0.9-modules.6
+meteor@1.1.11-modules.6
meteor-base@1.0.1
-meteorhacks:async@1.0.0
+meteor-env-dev@0.0.1-modules.6
+meteor-env-prod@0.0.1-modules.6
meteorhacks:fast-render@2.11.0
meteorhacks:inject-data@1.4.1
-meteorhacks:npm@1.5.0
meteorhacks:picker@1.0.3
meteorhacks:ssr@2.2.0
-minifiers@1.1.7
-minimongo@1.0.10
-mongo@1.1.3
+minifiers-css@1.1.8-modules.6
+minifiers-js@1.1.8-modules.6
+minimongo@1.0.11-modules.6
+modules@0.5.0-modules.6
+modules-runtime@0.5.0-modules.6
+momentjs:moment@2.11.2
+mongo@1.1.4-modules.6
mongo-id@1.0.1
npm-bcrypt@0.7.8_2
-npm-container@1.2.0
-npm-mongo@1.4.39_1
-observe-sequence@1.0.7
+npm-mongo@1.4.40-modules.6
+observe-sequence@1.0.8-modules.6
ordered-dict@1.0.4
-promise@0.5.1
-random@1.0.5
-rate-limit@1.0.0
-reactive-dict@1.1.3
+promise@0.5.2-modules.6
+random@1.0.6-modules.6
+rate-limit@1.0.1-modules.6
+reactive-dict@1.1.4-modules.6
reactive-var@1.0.6
-reload@1.1.4
+reload@1.1.5-modules.6
retry@1.0.4
-routepolicy@1.0.6
-service-configuration@1.0.5
-session@1.1.1
+routepolicy@1.0.7-modules.6
+service-configuration@1.0.6-modules.6
+session@1.1.2-modules.6
sha@1.0.4
-spacebars@1.0.7
-spacebars-compiler@1.0.7
-srp@1.0.4
-standard-minifiers@1.0.2
+spacebars@1.0.8-modules.6
+spacebars-compiler@1.0.8-modules.6
+srp@1.0.5-modules.6
+standard-minifiers@1.0.3-modules.6
+standard-minifiers-css@1.0.3-modules.6
+standard-minifiers-js@1.0.3-modules.6
stevezhu:lodash@3.10.1
-templating@1.1.5
-templating-tools@1.0.0
-themeteorchef:bert@2.1.0
+templating@1.1.6-modules.6
+templating-tools@1.0.1-modules.6
+themeteorchef:bert@2.2.0
themeteorchef:jquery-validation@1.14.0
-tracker@1.0.9
+tracker@1.0.10-modules.6
twbs:bootstrap@3.3.6
ui@1.0.8
-underscore@1.0.4
-url@1.0.5
-webapp@1.2.3
-webapp-hashing@1.0.5
+underscore@1.0.5-modules.6
+url@1.0.6-modules.6
+webapp@1.2.4-modules.6
+webapp-hashing@1.0.6-modules.6
diff --git a/both/methods/insert/collection-name.js b/both/methods/insert/collection-name.js
new file mode 100644
index 0000000..f270fa8
--- /dev/null
+++ b/both/methods/insert/collection-name.js
@@ -0,0 +1,11 @@
+Meteor.methods({
+ insert( object ) {
+ check( object, Object );
+
+ try {
+ return Documents.insert( object );
+ } catch ( exception ) {
+ throw new Meteor.Error( '500', `${ exception }` );
+ }
+ }
+});
diff --git a/both/methods/insert/collection.js b/both/methods/insert/collection.js
deleted file mode 100644
index 85224ed..0000000
--- a/both/methods/insert/collection.js
+++ /dev/null
@@ -1,12 +0,0 @@
-Meteor.methods({
- insertMethod( argument ) {
- check( argument, Object );
-
- try {
- var documentId = Collection.insert( argument );
- return documentId;
- } catch( exception ) {
- return exception;
- }
- }
-});
diff --git a/both/methods/read/collection.js b/both/methods/read/collection.js
deleted file mode 100644
index 9450a51..0000000
--- a/both/methods/read/collection.js
+++ /dev/null
@@ -1,13 +0,0 @@
-Meteor.methods({
- readMethod( argument ) {
- check( argument, String );
-
- var document = Collection.findOne( argument );
-
- if ( !document ) {
- throw new Meteor.Error( 'document-not-found', 'No documents found matching this query.' );
- }
-
- return document;
- }
-});
diff --git a/both/methods/remove/collection-name.js b/both/methods/remove/collection-name.js
new file mode 100644
index 0000000..595de13
--- /dev/null
+++ b/both/methods/remove/collection-name.js
@@ -0,0 +1,11 @@
+Meteor.methods({
+ remove( argument ) {
+ check( documentId, String );
+
+ try {
+ return Documents.remove( documentId );
+ } catch ( exception ) {
+ throw new Meteor.Error( '500', `${ exception }` );
+ }
+ }
+});
diff --git a/both/methods/remove/collection.js b/both/methods/remove/collection.js
deleted file mode 100644
index 8e1f377..0000000
--- a/both/methods/remove/collection.js
+++ /dev/null
@@ -1,11 +0,0 @@
-Meteor.methods({
- removeMethod( argument ) {
- check( argument, String );
-
- try {
- Collection.remove( argument );
- } catch( exception ) {
- return exception;
- }
- }
-});
diff --git a/both/methods/update/collection-name.js b/both/methods/update/collection-name.js
new file mode 100644
index 0000000..79d79b9
--- /dev/null
+++ b/both/methods/update/collection-name.js
@@ -0,0 +1,13 @@
+Meteor.methods({
+ update( update ) {
+ check( update, String );
+
+ try {
+ return Documents.update( update._id, {
+ $set: update
+ });
+ } catch ( exception ) {
+ throw new Meteor.Error( '500', `${ exception }` );
+ }
+ }
+});
diff --git a/both/methods/update/collection.js b/both/methods/update/collection.js
deleted file mode 100644
index d392721..0000000
--- a/both/methods/update/collection.js
+++ /dev/null
@@ -1,14 +0,0 @@
-Meteor.methods({
- updateMethod( argument ) {
- check( argument, Object );
-
- try {
- var documentId = Collection.update( argument._id, {
- $set: { 'key': argument.key }
- });
- return documentId;
- } catch( exception ) {
- return exception;
- }
- }
-});
diff --git a/both/modules/_modules.js b/both/modules/_modules.js
deleted file mode 100644
index f13b910..0000000
--- a/both/modules/_modules.js
+++ /dev/null
@@ -1,2 +0,0 @@
-Modules = {};
-Modules.both = {};
diff --git a/both/modules/startup.js b/both/modules/startup.js
index 34a2173..d6ccada 100644
--- a/both/modules/startup.js
+++ b/both/modules/startup.js
@@ -1,3 +1,3 @@
let startup = () => {};
-Modules.both.startup = startup;
+export { startup };
diff --git a/both/routes/authenticated.js b/both/routes/authenticated.js
index 1e87e75..f565e4c 100644
--- a/both/routes/authenticated.js
+++ b/both/routes/authenticated.js
@@ -12,6 +12,6 @@ authenticatedRoutes.route( '/', {
authenticatedRoutes.route( '/dashboard', {
name: 'dashboard',
action() {
- BlazeLayout.render( 'default', { yield: 'dashboard' } );
+ BlazeLayout.render( 'default', { yield: 'dashboard' }, hello );
}
});
diff --git a/both/startup.js b/both/startup.js
index 4555fab..2808e80 100644
--- a/both/startup.js
+++ b/both/startup.js
@@ -1 +1,3 @@
-Meteor.startup( () => Modules.both.startup() );
+import { startup } from './modules/startup';
+
+Meteor.startup( () => startup() );
diff --git a/client/modules/_modules.js b/client/modules/_modules.js
deleted file mode 100644
index b97216f..0000000
--- a/client/modules/_modules.js
+++ /dev/null
@@ -1 +0,0 @@
-Modules.client = {};
diff --git a/client/modules/login.js b/client/modules/login.js
index c2acba4..0266d7f 100644
--- a/client/modules/login.js
+++ b/client/modules/login.js
@@ -1,9 +1,14 @@
-let login = ( options ) => {
- _validate( options.form, options.template );
-};
+let _handleLogin = ( template ) => {
+ let email = template.find( '[name="emailAddress"]' ).value,
+ password = template.find( '[name="password"]' ).value;
-let _validate = ( form, template ) => {
- $( form ).validate( validation( template ) );
+ Meteor.loginWithPassword( email, password, ( error ) => {
+ if ( error ) {
+ Bert.alert( error.reason, 'warning' );
+ } else {
+ Bert.alert( 'Logged in!', 'success' );
+ }
+ });
};
let validation = ( template ) => {
@@ -30,17 +35,10 @@ let validation = ( template ) => {
};
};
-let _handleLogin = ( template ) => {
- let email = template.find( '[name="emailAddress"]' ).value,
- password = template.find( '[name="password"]' ).value;
-
- Meteor.loginWithPassword( email, password, ( error ) => {
- if ( error ) {
- Bert.alert( error.reason, 'warning' );
- } else {
- Bert.alert( 'Logged in!', 'success' );
- }
- });
+let _validate = ( form, template ) => {
+ $( form ).validate( validation( template ) );
};
-Modules.client.login = login;
+export function login( options ) {
+ _validate( options.form, options.template );
+}
diff --git a/client/modules/recover-password.js b/client/modules/recover-password.js
index 831af9d..78f6b1e 100644
--- a/client/modules/recover-password.js
+++ b/client/modules/recover-password.js
@@ -1,9 +1,13 @@
-let recoverPassword = ( options ) => {
- _validate( options.form, options.template );
-};
+let _handleRecovery = ( template ) => {
+ let email = template.find( '[name="emailAddress"]' ).value;
-let _validate = ( form, template ) => {
- $( form ).validate( validation( template ) );
+ Accounts.forgotPassword( { email: email }, ( error ) => {
+ if ( error ) {
+ Bert.alert( error.reason, 'warning' );
+ } else {
+ Bert.alert( 'Check your inbox for a reset link!', 'success' );
+ }
+ });
};
let validation = ( template ) => {
@@ -24,16 +28,10 @@ let validation = ( template ) => {
};
};
-let _handleRecovery = ( template ) => {
- let email = template.find( '[name="emailAddress"]' ).value;
-
- Accounts.forgotPassword( { email: email }, ( error ) => {
- if ( error ) {
- Bert.alert( error.reason, 'warning' );
- } else {
- Bert.alert( 'Check your inbox for a reset link!', 'success' );
- }
- });
+let _validate = ( form, template ) => {
+ $( form ).validate( validation( template ) );
};
-Modules.client.recoverPassword = recoverPassword;
+export function recoverPassword( options ) {
+ _validate( options.form, options.template );
+}
diff --git a/client/modules/reset-password.js b/client/modules/reset-password.js
index a366c7c..e3f11ee 100644
--- a/client/modules/reset-password.js
+++ b/client/modules/reset-password.js
@@ -1,9 +1,14 @@
-let resetPassword = ( options ) => {
- _validate( options.form, options.template );
-};
+let _handleReset = ( template ) => {
+ var token = FlowRouter.current().params.token,
+ password = template.find( '[name="newPassword"]' ).value;
-let _validate = ( form, template ) => {
- $( form ).validate( validation( template ) );
+ Accounts.resetPassword( token, password, ( error ) => {
+ if ( error ) {
+ Bert.alert( error.reason, 'danger' );
+ } else {
+ Bert.alert( 'Password reset!', 'success' );
+ }
+ });
};
let validation = ( template ) => {
@@ -33,17 +38,10 @@ let validation = ( template ) => {
};
};
-let _handleReset = ( template ) => {
- var token = FlowRouter.current().params.token,
- password = template.find( '[name="newPassword"]' ).value;
-
- Accounts.resetPassword( token, password, ( error ) => {
- if ( error ) {
- Bert.alert( error.reason, 'danger' );
- } else {
- Bert.alert( 'Password reset!', 'success' );
- }
- });
+let _validate = ( form, template ) => {
+ $( form ).validate( validation( template ) );
};
-Modules.client.resetPassword = resetPassword;
+export function resetPassword( options ) {
+ _validate( options.form, options.template );
+}
diff --git a/client/modules/signup.js b/client/modules/signup.js
index 6834973..3cd4cd0 100644
--- a/client/modules/signup.js
+++ b/client/modules/signup.js
@@ -1,9 +1,16 @@
-let signup = ( options ) => {
- _validate( options.form, options.template );
-};
+let _handleSignup = ( template ) => {
+ let user = {
+ email: template.find( '[name="emailAddress"]' ).value,
+ password: template.find( '[name="password"]' ).value
+ };
-let _validate = ( form, template ) => {
- $( form ).validate( validation( template ) );
+ Accounts.createUser( user, ( error ) => {
+ if ( error ) {
+ Bert.alert( error.reason, 'danger' );
+ } else {
+ Bert.alert( 'Welcome!', 'success' );
+ }
+ });
};
let validation = ( template ) => {
@@ -32,19 +39,10 @@ let validation = ( template ) => {
};
};
-let _handleSignup = ( template ) => {
- let user = {
- email: template.find( '[name="emailAddress"]' ).value,
- password: template.find( '[name="password"]' ).value
- };
-
- Accounts.createUser( user, ( error ) => {
- if ( error ) {
- Bert.alert( error.reason, 'danger' );
- } else {
- Bert.alert( 'Welcome!', 'success' );
- }
- });
+let _validate = ( form, template ) => {
+ $( form ).validate( validation( template ) );
};
-Modules.client.signup = signup;
+export function signup( options ) {
+ _validate( options.form, options.template );
+}
diff --git a/client/modules/startup.js b/client/modules/startup.js
index 68db501..04eed13 100644
--- a/client/modules/startup.js
+++ b/client/modules/startup.js
@@ -1,3 +1,3 @@
-let startup = () => {};
-
-Modules.client.startup = startup;
+export function startup() {
+ Bert.defaults.style = 'growl-top-right';
+}
diff --git a/client/startup.js b/client/startup.js
index 552c183..2808e80 100644
--- a/client/startup.js
+++ b/client/startup.js
@@ -1 +1,3 @@
-Meteor.startup( () => Modules.client.startup() );
+import { startup } from './modules/startup';
+
+Meteor.startup( () => startup() );
diff --git a/client/stylesheets/application.scss b/client/stylesheets/application.scss
index 08d4bdf..7d4dae8 100644
--- a/client/stylesheets/application.scss
+++ b/client/stylesheets/application.scss
@@ -2,4 +2,5 @@
@import "objects/forms";
+@import "components/loading";
@import "components/login";
diff --git a/client/stylesheets/components/_loading.scss b/client/stylesheets/components/_loading.scss
new file mode 100644
index 0000000..4f3d717
--- /dev/null
+++ b/client/stylesheets/components/_loading.scss
@@ -0,0 +1,20 @@
+@keyframes rotate {
+ from { transform: rotate( 0deg ); }
+ to { transform: rotate( 360deg ); }
+}
+
+@-webkit-keyframes rotate {
+ from { -webkit-transform: rotate( 0deg ); }
+ to { -webkit-transform: rotate( 360deg ); }
+}
+
+.loading {
+ -webkit-animation-name: rotate;
+ -webkit-animation-duration: 0.5s;
+ -webkit-animation-iteration-count: infinite;
+ -webkit-animation-timing-function: linear;
+ animation-name: rotate;
+ animation-duration: 0.5s;
+ animation-iteration-count: infinite;
+ animation-timing-function: linear;
+}
diff --git a/client/templates/authenticated/index.html b/client/templates/authenticated/index.html
index 10989c5..be4c372 100644
--- a/client/templates/authenticated/index.html
+++ b/client/templates/authenticated/index.html
@@ -2,7 +2,7 @@
Base
A starting point for Meteor applications.
-
Read the Documentation
-
Currently at v3.4.0
+
Read the Documentation
+
Currently at v4.0.0
diff --git a/client/templates/globals/loading.html b/client/templates/globals/loading.html
index e7f7d5d..72a1d5c 100644
--- a/client/templates/globals/loading.html
+++ b/client/templates/globals/loading.html
@@ -1,18 +1,11 @@
-
diff --git a/client/templates/public/login.js b/client/templates/public/login.js
index 412891c..d49c1d1 100644
--- a/client/templates/public/login.js
+++ b/client/templates/public/login.js
@@ -1,5 +1,7 @@
+import { login } from '../../modules/login';
+
Template.login.onRendered( () => {
- Modules.client.login( { form: "#login", template: Template.instance() } );
+ login( { form: "#login", template: Template.instance() } );
});
Template.login.events({
diff --git a/client/templates/public/recover-password.js b/client/templates/public/recover-password.js
index 0843e4a..f4a5fbf 100644
--- a/client/templates/public/recover-password.js
+++ b/client/templates/public/recover-password.js
@@ -1,5 +1,7 @@
+import { recoverPassword } from '../../modules/recover-password';
+
Template.recoverPassword.onRendered( () => {
- Modules.client.recoverPassword({
+ recoverPassword({
form: "#recover-password",
template: Template.instance()
});
diff --git a/client/templates/public/reset-password.js b/client/templates/public/reset-password.js
index a45dc34..05595fa 100644
--- a/client/templates/public/reset-password.js
+++ b/client/templates/public/reset-password.js
@@ -1,5 +1,7 @@
+import { resetPassword } from '../../modules/login';
+
Template.resetPassword.onRendered( () => {
- Modules.client.resetPassword({
+ resetPassword({
form: "#reset-password",
template: Template.instance()
});
diff --git a/client/templates/public/signup.js b/client/templates/public/signup.js
index 4aa2ae1..70ed3b0 100644
--- a/client/templates/public/signup.js
+++ b/client/templates/public/signup.js
@@ -1,8 +1,7 @@
+import { signup } from '../../modules/signup';
+
Template.signup.onRendered( () => {
- Modules.client.signup({
- form: "#signup",
- template: Template.instance()
- });
+ signup( { form: "#signup", template: Template.instance() } );
});
Template.signup.events({
diff --git a/collections/collection.js b/collections/collection.js
deleted file mode 100644
index bb185ad..0000000
--- a/collections/collection.js
+++ /dev/null
@@ -1,22 +0,0 @@
-Collection = new Meteor.Collection( 'collection' );
-
-Collection.allow({
- insert: () => false,
- update: () => false,
- remove: () => false
-});
-
-Collection.deny({
- insert: () => true,
- update: () => true,
- remove: () => true
-});
-
-let CollectionSchema = new SimpleSchema({
- "owner": {
- type: String,
- label: "The ID of the owner of this document."
- }
-});
-
-Collection.attachSchema( CollectionSchema );
diff --git a/collections/documents.js b/collections/documents.js
new file mode 100644
index 0000000..7f0f8cb
--- /dev/null
+++ b/collections/documents.js
@@ -0,0 +1,22 @@
+Documents = new Mongo.Collection( 'documents' );
+
+Documents.allow({
+ insert: () => false,
+ update: () => false,
+ remove: () => false
+});
+
+Documents.deny({
+ insert: () => true,
+ update: () => true,
+ remove: () => true
+});
+
+let DocumentsSchema = new SimpleSchema({
+ "title": {
+ type: String,
+ label: "The title of this document."
+ }
+});
+
+Documents.attachSchema( DocumentsSchema );
diff --git a/node_modules/.bin/eslint b/node_modules/.bin/eslint
new file mode 120000
index 0000000..810e4bc
--- /dev/null
+++ b/node_modules/.bin/eslint
@@ -0,0 +1 @@
+../eslint/bin/eslint.js
\ No newline at end of file
diff --git a/node_modules/eslint-config-airbnb/.eslintrc b/node_modules/eslint-config-airbnb/.eslintrc
new file mode 100644
index 0000000..4b3b1fa
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/.eslintrc
@@ -0,0 +1,8 @@
+{
+ "extends": "airbnb",
+ "rules": {
+ // disable requiring trailing commas because it might be nice to revert to
+ // being JSON at some point, and I don't want to make big changes now.
+ "comma-dangle": 0
+ }
+}
diff --git a/node_modules/eslint-config-airbnb/CHANGELOG.md b/node_modules/eslint-config-airbnb/CHANGELOG.md
new file mode 100644
index 0000000..75febdd
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/CHANGELOG.md
@@ -0,0 +1,102 @@
+5.0.0 / 2016-02-03
+==================
+ - [breaking] disallow unneeded ternary expressions
+ - [breaking] Avoid lexical declarations in case/default clauses
+ - [dev deps] update `babel-tape-runner`, `eslint-plugin-react`, `react`, `tape`
+
+4.0.0 / 2016-01-22
+==================
+ - [breaking] require outer IIFE wrapping; flesh out guide section
+ - [minor] Add missing `arrow-body-style`, `prefer-template` rules (#678)
+ - [minor] Add `prefer-arrow-callback` to ES6 rules (to match the guide) (#677)
+ - [Tests] run `npm run lint` as part of tests; fix errors
+ - [Tests] use `parallelshell` to parallelize npm run-scripts
+
+3.1.0 / 2016-01-07
+==================
+ - [minor] Allow multiple stateless components in a single file
+
+3.0.2 / 2016-01-06
+==================
+ - [fix] Ignore URLs in `max-len` (#664)
+
+3.0.1 / 2016-01-06
+==================
+ - [fix] because we use babel, keywords should not be quoted
+
+3.0.0 / 2016-01-04
+==================
+ - [breaking] enable `quote-props` rule (#632)
+ - [breaking] Define a max line length of 100 characters (#639)
+ - [breaking] [react] Minor cleanup for the React styleguide, add `react/jsx-no-bind` (#619)
+ - [breaking] update best-practices config to prevent parameter object manipulation (#627)
+ - [minor] Enable react/no-is-mounted rule (#635, #633)
+ - [minor] Sort react/prefer-es6-class alphabetically (#634)
+ - [minor] enable react/prefer-es6-class rule
+ - Permit strict mode in "legacy" config
+ - [react] add missing rules from eslint-plugin-react (enforcing where necessary) (#581)
+ - [dev deps] update `eslint-plugin-react`
+
+2.1.1 / 2015-12-15
+==================
+ - [fix] Remove deprecated react/jsx-quotes (#622)
+
+2.1.0 / 2015-12-15
+==================
+ - [fix] use `require.resolve` to allow nested `extend`s (#582)
+ - [new] enable `object-shorthand` rule (#621)
+ - [new] enable `arrow-spacing` rule (#517)
+ - [docs] flesh out react rule defaults (#618)
+
+2.0.0 / 2015-12-03
+==================
+ - [breaking] `space-before-function-paren`: require function spacing: `function (` (#605)
+ - [breaking] `indent`: Fix switch statement indentation rule (#606)
+ - [breaking] `array-bracket-spacing`, `computed-property-spacing`: disallow spacing inside brackets (#594)
+ - [breaking] `object-curly-spacing`: require padding inside curly braces (#594)
+ - [breaking] `space-in-parens`: disallow spaces in parens (#594)
+
+1.0.2 / 2015-11-25
+==================
+ - [breaking] `no-multiple-empty-lines`: only allow 1 blank line at EOF (#578)
+ - [new] `restParams`: enable rest params (#592)
+
+1.0.1 / 2015-11-25
+==================
+ - *erroneous publish*
+
+1.0.0 / 2015-11-08
+==================
+ - require `eslint` `v1.0.0` or higher
+ - remove `babel-eslint` dependency
+
+0.1.1 / 2015-11-05
+==================
+ - remove id-length rule (#569)
+ - enable `no-mixed-spaces-and-tabs` (#539)
+ - enable `no-const-assign` (#560)
+ - enable `space-before-keywords` (#554)
+
+0.1.0 / 2015-11-05
+==================
+ - switch to modular rules files courtesy the [eslint-config-default][ecd] project and [@taion][taion]. [PR][pr-modular]
+ - export `eslint-config-airbnb/legacy` for ES5-only users. `eslint-config-airbnb/legacy` does not require the `babel-eslint` parser. [PR][pr-legacy]
+
+0.0.9 / 2015-09-24
+==================
+- add rule `no-undef`
+- add rule `id-length`
+
+0.0.8 / 2015-08-21
+==================
+ - now has a changelog
+ - now is modular (see instructions above for with react and without react versions)
+
+0.0.7 / 2015-07-30
+==================
+ - TODO: fill in
+
+[ecd]: https://github.com/walmartlabs/eslint-config-defaults
+[taion]: https://github.com/taion
+[pr-modular]: https://github.com/airbnb/javascript/pull/526
+[pr-legacy]: https://github.com/airbnb/javascript/pull/527
diff --git a/node_modules/eslint-config-airbnb/README.md b/node_modules/eslint-config-airbnb/README.md
new file mode 100644
index 0000000..26c795f
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/README.md
@@ -0,0 +1,45 @@
+# eslint-config-airbnb
+
+[](http://badge.fury.io/js/eslint-config-airbnb)
+
+This package provides Airbnb's .eslintrc as an extensible shared config.
+
+## Usage
+
+We export three ESLint configurations for your usage.
+
+### eslint-config-airbnb
+
+Our default export contains all of our ESLint rules, including EcmaScript 6+
+and React. It requires `eslint` and `eslint-plugin-react`.
+
+1. `npm install --save-dev eslint-config-airbnb eslint-plugin-react eslint`
+2. add `"extends": "airbnb"` to your .eslintrc
+
+### eslint-config-airbnb/base
+
+Lints ES6+ but does not lint React. Requires `eslint`.
+
+1. `npm install --save-dev eslint-config-airbnb eslint`
+2. add `"extends": "airbnb/base"` to your .eslintrc
+
+### eslint-config-airbnb/legacy
+
+Lints ES5 and below. Only requires `eslint`.
+
+1. `npm install --save-dev eslint-config-airbnb eslint`
+2. add `"extends": "airbnb/legacy"` to your .eslintrc
+
+See [Airbnb's Javascript styleguide](https://github.com/airbnb/javascript) and
+the [ESlint config docs](http://eslint.org/docs/user-guide/configuring#extending-configuration-files)
+for more information.
+
+## Improving this config
+
+Consider adding test cases if you're making complicated rules changes, like
+anything involving regexes. Perhaps in a distant future, we could use literate
+programming to structure our README as test cases for our .eslintrc?
+
+You can run tests with `npm test`.
+
+You can make sure this module lints with itself using `npm run lint`.
diff --git a/node_modules/eslint-config-airbnb/base.js b/node_modules/eslint-config-airbnb/base.js
new file mode 100644
index 0000000..1cfea52
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/base.js
@@ -0,0 +1,7 @@
+module.exports = {
+ extends: [
+ 'eslint-config-airbnb/legacy',
+ 'eslint-config-airbnb/rules/es6',
+ ].map(require.resolve),
+ rules: {}
+};
diff --git a/node_modules/eslint-config-airbnb/index.js b/node_modules/eslint-config-airbnb/index.js
new file mode 100644
index 0000000..e9ce995
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/index.js
@@ -0,0 +1,8 @@
+module.exports = {
+ extends: [
+ 'eslint-config-airbnb/base',
+ 'eslint-config-airbnb/rules/strict',
+ 'eslint-config-airbnb/rules/react',
+ ].map(require.resolve),
+ rules: {}
+};
diff --git a/node_modules/eslint-config-airbnb/legacy.js b/node_modules/eslint-config-airbnb/legacy.js
new file mode 100644
index 0000000..b404cf7
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/legacy.js
@@ -0,0 +1,20 @@
+module.exports = {
+ extends: [
+ 'eslint-config-airbnb/rules/best-practices',
+ 'eslint-config-airbnb/rules/errors',
+ 'eslint-config-airbnb/rules/legacy',
+ 'eslint-config-airbnb/rules/node',
+ 'eslint-config-airbnb/rules/style',
+ 'eslint-config-airbnb/rules/variables'
+ ].map(require.resolve),
+ env: {
+ browser: true,
+ node: true,
+ amd: false,
+ mocha: false,
+ jasmine: false
+ },
+ ecmaFeatures: {},
+ globals: {},
+ rules: {}
+};
diff --git a/node_modules/eslint-config-airbnb/package.json b/node_modules/eslint-config-airbnb/package.json
new file mode 100644
index 0000000..196bcbb
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/package.json
@@ -0,0 +1,87 @@
+{
+ "name": "eslint-config-airbnb",
+ "version": "5.0.0",
+ "description": "Airbnb's ESLint config, following our styleguide",
+ "main": "index.js",
+ "scripts": {
+ "lint": "eslint .",
+ "tests-only": "babel-tape-runner ./test/test-*.js",
+ "test": "parallelshell 'npm run lint' 'npm run tests-only'"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/airbnb/javascript.git"
+ },
+ "keywords": [
+ "eslint",
+ "eslintconfig",
+ "config",
+ "airbnb",
+ "javascript",
+ "styleguide"
+ ],
+ "author": {
+ "name": "Jake Teton-Landis",
+ "url": "https://twitter.com/@jitl"
+ },
+ "contributors": [
+ {
+ "name": "Jake Teton-Landis",
+ "url": "https://twitter.com/jitl"
+ },
+ {
+ "name": "Jordan Harband",
+ "email": "ljharb@gmail.com",
+ "url": "http://ljharb.codes"
+ }
+ ],
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/airbnb/javascript/issues"
+ },
+ "homepage": "https://github.com/airbnb/javascript",
+ "devDependencies": {
+ "babel-tape-runner": "1.2.0",
+ "eslint": "^1.10.3",
+ "eslint-plugin-react": "^3.16.1",
+ "react": "^0.14.7",
+ "tape": "^4.4.0",
+ "parallelshell": "^2.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=1.0.0"
+ },
+ "_id": "eslint-config-airbnb@5.0.0",
+ "_shasum": "16886a3a613028f62213c6330e9b42554364ec3f",
+ "_from": "eslint-config-airbnb@*",
+ "_npmVersion": "3.3.12",
+ "_nodeVersion": "5.5.0",
+ "_npmUser": {
+ "name": "ljharb",
+ "email": "ljharb@gmail.com"
+ },
+ "dist": {
+ "shasum": "16886a3a613028f62213c6330e9b42554364ec3f",
+ "tarball": "http://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-5.0.0.tgz"
+ },
+ "maintainers": [
+ {
+ "name": "airbnb",
+ "email": "jordan.harband+npm@airbnb.com"
+ },
+ {
+ "name": "jitl",
+ "email": "just.1.jake@gmail.com"
+ },
+ {
+ "name": "ljharb",
+ "email": "ljharb@gmail.com"
+ }
+ ],
+ "_npmOperationalInternal": {
+ "host": "packages-5-east.internal.npmjs.com",
+ "tmp": "tmp/eslint-config-airbnb-5.0.0.tgz_1454552979702_0.033046064199879766"
+ },
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-5.0.0.tgz"
+}
diff --git a/node_modules/eslint-config-airbnb/rules/.eslintrc.json b/node_modules/eslint-config-airbnb/rules/.eslintrc.json
new file mode 100644
index 0000000..de68aa2
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/rules/.eslintrc.json
@@ -0,0 +1,5 @@
+{
+ "rules": {
+ "quote-props": 0
+ }
+}
diff --git a/node_modules/eslint-config-airbnb/rules/best-practices.js b/node_modules/eslint-config-airbnb/rules/best-practices.js
new file mode 100644
index 0000000..a221d4d
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/rules/best-practices.js
@@ -0,0 +1,119 @@
+module.exports = {
+ 'rules': {
+ // enforces getter/setter pairs in objects
+ 'accessor-pairs': 0,
+ // treat var statements as if they were block scoped
+ 'block-scoped-var': 2,
+ // specify the maximum cyclomatic complexity allowed in a program
+ 'complexity': [0, 11],
+ // require return statements to either always or never specify values
+ 'consistent-return': 2,
+ // specify curly brace conventions for all control statements
+ 'curly': [2, 'multi-line'],
+ // require default case in switch statements
+ 'default-case': 2,
+ // encourages use of dot notation whenever possible
+ 'dot-notation': [2, { 'allowKeywords': true }],
+ // enforces consistent newlines before or after dots
+ 'dot-location': 0,
+ // require the use of === and !==
+ 'eqeqeq': 2,
+ // make sure for-in loops have an if statement
+ 'guard-for-in': 2,
+ // disallow the use of alert, confirm, and prompt
+ 'no-alert': 1,
+ // disallow use of arguments.caller or arguments.callee
+ 'no-caller': 2,
+ // disallow lexical declarations in case/default clauses
+ // http://eslint.org/docs/rules/no-case-declarations.html
+ 'no-case-declarations': 2,
+ // disallow division operators explicitly at beginning of regular expression
+ 'no-div-regex': 0,
+ // disallow else after a return in an if
+ 'no-else-return': 2,
+ // disallow use of labels for anything other then loops and switches
+ 'no-empty-label': 2,
+ // disallow comparisons to null without a type-checking operator
+ 'no-eq-null': 0,
+ // disallow use of eval()
+ 'no-eval': 2,
+ // disallow adding to native types
+ 'no-extend-native': 2,
+ // disallow unnecessary function binding
+ 'no-extra-bind': 2,
+ // disallow fallthrough of case statements
+ 'no-fallthrough': 2,
+ // disallow the use of leading or trailing decimal points in numeric literals
+ 'no-floating-decimal': 2,
+ // disallow the type conversions with shorter notations
+ 'no-implicit-coercion': 0,
+ // disallow use of eval()-like methods
+ 'no-implied-eval': 2,
+ // disallow this keywords outside of classes or class-like objects
+ 'no-invalid-this': 0,
+ // disallow usage of __iterator__ property
+ 'no-iterator': 2,
+ // disallow use of labeled statements
+ 'no-labels': 2,
+ // disallow unnecessary nested blocks
+ 'no-lone-blocks': 2,
+ // disallow creation of functions within loops
+ 'no-loop-func': 2,
+ // disallow use of multiple spaces
+ 'no-multi-spaces': 2,
+ // disallow use of multiline strings
+ 'no-multi-str': 2,
+ // disallow reassignments of native objects
+ 'no-native-reassign': 2,
+ // disallow use of new operator when not part of the assignment or comparison
+ 'no-new': 2,
+ // disallow use of new operator for Function object
+ 'no-new-func': 2,
+ // disallows creating new instances of String,Number, and Boolean
+ 'no-new-wrappers': 2,
+ // disallow use of (old style) octal literals
+ 'no-octal': 2,
+ // disallow use of octal escape sequences in string literals, such as
+ // var foo = 'Copyright \251';
+ 'no-octal-escape': 2,
+ // disallow reassignment of function parameters
+ // disallow parameter object manipulation
+ // rule: http://eslint.org/docs/rules/no-param-reassign.html
+ 'no-param-reassign': [2, { 'props': true }],
+ // disallow use of process.env
+ 'no-process-env': 0,
+ // disallow usage of __proto__ property
+ 'no-proto': 2,
+ // disallow declaring the same variable more then once
+ 'no-redeclare': 2,
+ // disallow use of assignment in return statement
+ 'no-return-assign': 2,
+ // disallow use of `javascript:` urls.
+ 'no-script-url': 2,
+ // disallow comparisons where both sides are exactly the same
+ 'no-self-compare': 2,
+ // disallow use of comma operator
+ 'no-sequences': 2,
+ // restrict what can be thrown as an exception
+ 'no-throw-literal': 2,
+ // disallow usage of expressions in statement position
+ 'no-unused-expressions': 2,
+ // disallow unnecessary .call() and .apply()
+ 'no-useless-call': 0,
+ // disallow use of void operator
+ 'no-void': 0,
+ // disallow usage of configurable warning terms in comments: e.g. todo
+ 'no-warning-comments': [0, { 'terms': ['todo', 'fixme', 'xxx'], 'location': 'start' }],
+ // disallow use of the with statement
+ 'no-with': 2,
+ // require use of the second argument for parseInt()
+ 'radix': 2,
+ // requires to declare all vars on top of their containing scope
+ 'vars-on-top': 2,
+ // require immediate function invocation to be wrapped in parentheses
+ // http://eslint.org/docs/rules/wrap-iife.html
+ 'wrap-iife': [2, 'outside'],
+ // require or disallow Yoda conditions
+ 'yoda': 2
+ }
+};
diff --git a/node_modules/eslint-config-airbnb/rules/errors.js b/node_modules/eslint-config-airbnb/rules/errors.js
new file mode 100644
index 0000000..ec1b1aa
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/rules/errors.js
@@ -0,0 +1,60 @@
+module.exports = {
+ 'rules': {
+ // disallow trailing commas in object literals
+ 'comma-dangle': [2, 'always-multiline'],
+ // disallow assignment in conditional expressions
+ 'no-cond-assign': [2, 'always'],
+ // disallow use of console
+ 'no-console': 1,
+ // disallow use of constant expressions in conditions
+ 'no-constant-condition': 1,
+ // disallow control characters in regular expressions
+ 'no-control-regex': 2,
+ // disallow use of debugger
+ 'no-debugger': 1,
+ // disallow duplicate arguments in functions
+ 'no-dupe-args': 2,
+ // disallow duplicate keys when creating object literals
+ 'no-dupe-keys': 2,
+ // disallow a duplicate case label.
+ 'no-duplicate-case': 2,
+ // disallow the use of empty character classes in regular expressions
+ 'no-empty-character-class': 2,
+ // disallow empty statements
+ 'no-empty': 2,
+ // disallow assigning to the exception in a catch block
+ 'no-ex-assign': 2,
+ // disallow double-negation boolean casts in a boolean context
+ 'no-extra-boolean-cast': 0,
+ // disallow unnecessary parentheses
+ 'no-extra-parens': [2, 'functions'],
+ // disallow unnecessary semicolons
+ 'no-extra-semi': 2,
+ // disallow overwriting functions written as function declarations
+ 'no-func-assign': 2,
+ // disallow function or variable declarations in nested blocks
+ 'no-inner-declarations': 2,
+ // disallow invalid regular expression strings in the RegExp constructor
+ 'no-invalid-regexp': 2,
+ // disallow irregular whitespace outside of strings and comments
+ 'no-irregular-whitespace': 2,
+ // disallow negation of the left operand of an in expression
+ 'no-negated-in-lhs': 2,
+ // disallow the use of object properties of the global object (Math and JSON) as functions
+ 'no-obj-calls': 2,
+ // disallow multiple spaces in a regular expression literal
+ 'no-regex-spaces': 2,
+ // disallow sparse arrays
+ 'no-sparse-arrays': 2,
+ // disallow unreachable statements after a return, throw, continue, or break statement
+ 'no-unreachable': 2,
+ // disallow comparisons with the value NaN
+ 'use-isnan': 2,
+ // ensure JSDoc comments are valid
+ 'valid-jsdoc': 0,
+ // ensure that the results of typeof are compared against a valid string
+ 'valid-typeof': 2,
+ // Avoid code that looks like two expressions but is actually one
+ 'no-unexpected-multiline': 0
+ }
+};
diff --git a/node_modules/eslint-config-airbnb/rules/es6.js b/node_modules/eslint-config-airbnb/rules/es6.js
new file mode 100644
index 0000000..e346843
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/rules/es6.js
@@ -0,0 +1,62 @@
+module.exports = {
+ 'env': {
+ 'es6': false
+ },
+ 'ecmaFeatures': {
+ 'arrowFunctions': true,
+ 'blockBindings': true,
+ 'classes': true,
+ 'defaultParams': true,
+ 'destructuring': true,
+ 'forOf': true,
+ 'generators': false,
+ 'modules': true,
+ 'objectLiteralComputedProperties': true,
+ 'objectLiteralDuplicateProperties': false,
+ 'objectLiteralShorthandMethods': true,
+ 'objectLiteralShorthandProperties': true,
+ 'restParams': true,
+ 'spread': true,
+ 'superInFunctions': true,
+ 'templateStrings': true,
+ 'jsx': true
+ },
+ 'rules': {
+ // enforces no braces where they can be omitted
+ // http://eslint.org/docs/rules/arrow-body-style
+ 'arrow-body-style': [2, 'as-needed'],
+ // require parens in arrow function arguments
+ 'arrow-parens': 0,
+ // require space before/after arrow function's arrow
+ // https://github.com/eslint/eslint/blob/master/docs/rules/arrow-spacing.md
+ 'arrow-spacing': [2, { 'before': true, 'after': true }],
+ // verify super() callings in constructors
+ 'constructor-super': 0,
+ // enforce the spacing around the * in generator functions
+ 'generator-star-spacing': 0,
+ // disallow modifying variables of class declarations
+ 'no-class-assign': 0,
+ // disallow modifying variables that are declared using const
+ 'no-const-assign': 2,
+ // disallow to use this/super before super() calling in constructors.
+ 'no-this-before-super': 0,
+ // require let or const instead of var
+ 'no-var': 2,
+ // require method and property shorthand syntax for object literals
+ // https://github.com/eslint/eslint/blob/master/docs/rules/object-shorthand.md
+ 'object-shorthand': [2, 'always'],
+ // suggest using arrow functions as callbacks
+ 'prefer-arrow-callback': 2,
+ // suggest using of const declaration for variables that are never modified after declared
+ 'prefer-const': 2,
+ // suggest using the spread operator instead of .apply()
+ 'prefer-spread': 0,
+ // suggest using Reflect methods where applicable
+ 'prefer-reflect': 0,
+ // suggest using template literals instead of string concatenation
+ // http://eslint.org/docs/rules/prefer-template
+ 'prefer-template': 2,
+ // disallow generator functions that do not have yield
+ 'require-yield': 0
+ }
+};
diff --git a/node_modules/eslint-config-airbnb/rules/legacy.js b/node_modules/eslint-config-airbnb/rules/legacy.js
new file mode 100644
index 0000000..e94c774
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/rules/legacy.js
@@ -0,0 +1,14 @@
+module.exports = {
+ 'rules': {
+ // specify the maximum depth that blocks can be nested
+ 'max-depth': [0, 4],
+ // limits the number of parameters that can be used in the function declaration.
+ 'max-params': [0, 3],
+ // specify the maximum number of statement allowed in a function
+ 'max-statements': [0, 10],
+ // disallow use of bitwise operators
+ 'no-bitwise': 0,
+ // disallow use of unary operators, ++ and --
+ 'no-plusplus': 0
+ }
+};
diff --git a/node_modules/eslint-config-airbnb/rules/node.js b/node_modules/eslint-config-airbnb/rules/node.js
new file mode 100644
index 0000000..16b6f20
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/rules/node.js
@@ -0,0 +1,23 @@
+module.exports = {
+ 'env': {
+ 'node': true
+ },
+ 'rules': {
+ // enforce return after a callback
+ 'callback-return': 0,
+ // enforces error handling in callbacks (node environment)
+ 'handle-callback-err': 0,
+ // disallow mixing regular variable and require declarations
+ 'no-mixed-requires': [0, false],
+ // disallow use of new operator with the require function
+ 'no-new-require': 0,
+ // disallow string concatenation with __dirname and __filename
+ 'no-path-concat': 0,
+ // disallow process.exit()
+ 'no-process-exit': 0,
+ // restrict usage of specified node modules
+ 'no-restricted-modules': 0,
+ // disallow use of synchronous methods (off by default)
+ 'no-sync': 0
+ }
+};
diff --git a/node_modules/eslint-config-airbnb/rules/react.js b/node_modules/eslint-config-airbnb/rules/react.js
new file mode 100644
index 0000000..e1e1c25
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/rules/react.js
@@ -0,0 +1,139 @@
+module.exports = {
+ 'plugins': [
+ 'react'
+ ],
+ 'ecmaFeatures': {
+ 'jsx': true
+ },
+ // View link below for react rules documentation
+ // https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules
+ 'rules': {
+ // Prevent missing displayName in a React component definition
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md
+ 'react/display-name': [0, { 'acceptTranspilerName': false }],
+ // Forbid certain propTypes (any, array, object)
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-prop-types.md
+ 'react/forbid-prop-types': [0, { 'forbid': ['any', 'array', 'object'] }],
+ // Enforce boolean attributes notation in JSX
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md
+ 'react/jsx-boolean-value': [2, 'never'],
+ // Validate closing bracket location in JSX
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md
+ 'react/jsx-closing-bracket-location': [2, 'line-aligned'],
+ // Enforce or disallow spaces inside of curly braces in JSX attributes
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md
+ 'react/jsx-curly-spacing': [0, 'never', { 'allowMultiline': true }],
+ // Enforce event handler naming conventions in JSX
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md
+ 'react/jsx-handler-names': [0, {
+ 'eventHandlerPrefix': 'handle',
+ 'eventHandlerPropPrefix': 'on',
+ }],
+ // Validate props indentation in JSX
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent-props.md
+ 'react/jsx-indent-props': [2, 2],
+ // Validate JSX has key prop when in array or iterator
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-key.md
+ 'react/jsx-key': 0,
+ // Limit maximum of props on a single line in JSX
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-max-props-per-line.md
+ 'react/jsx-max-props-per-line': [0, { 'maximum': 1 }],
+ // Prevent usage of .bind() and arrow functions in JSX props
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
+ 'react/jsx-no-bind': 2,
+ // Prevent duplicate props in JSX
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-duplicate-props.md
+ 'react/jsx-no-duplicate-props': [0, { 'ignoreCase': false }],
+ // Prevent usage of unwrapped JSX strings
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-literals.md
+ 'react/jsx-no-literals': 0,
+ // Disallow undeclared variables in JSX
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md
+ 'react/jsx-no-undef': 2,
+ // Enforce PascalCase for user-defined JSX components
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md
+ 'react/jsx-pascal-case': 0,
+ // Enforce propTypes declarations alphabetical sorting
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-prop-types.md
+ 'react/jsx-sort-prop-types': [0, {
+ 'ignoreCase': false,
+ 'callbacksLast': false,
+ }],
+ // Enforce props alphabetical sorting
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md
+ 'react/jsx-sort-props': [0, {
+ 'ignoreCase': false,
+ 'callbacksLast': false,
+ }],
+ // Prevent React to be incorrectly marked as unused
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md
+ 'react/jsx-uses-react': [2, { 'pragma': 'React' }],
+ // Prevent variables used in JSX to be incorrectly marked as unused
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md
+ 'react/jsx-uses-vars': 2,
+ // Prevent usage of dangerous JSX properties
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger.md
+ 'react/no-danger': 0,
+ // Prevent usage of deprecated methods
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-deprecated.md
+ 'react/no-deprecated': [1, { 'react': '0.14.0' }],
+ // Prevent usage of setState in componentDidMount
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md
+ 'react/no-did-mount-set-state': [2, 'allow-in-func'],
+ // Prevent usage of setState in componentDidUpdate
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md
+ 'react/no-did-update-set-state': [2, 'allow-in-func'],
+ // Prevent direct mutation of this.state
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-direct-mutation-state.md
+ 'react/no-direct-mutation-state': 0,
+ // Prevent usage of isMounted
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md
+ 'react/no-is-mounted': 2,
+ // Prevent multiple component definition per file
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md
+ 'react/no-multi-comp': [2, { 'ignoreStateless': true }],
+ // Prevent usage of setState
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-set-state.md
+ 'react/no-set-state': 0,
+ // Prevent using string references
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md
+ 'react/no-string-refs': 0,
+ // Prevent usage of unknown DOM property
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md
+ 'react/no-unknown-property': 2,
+ // Require ES6 class declarations over React.createClass
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md
+ 'react/prefer-es6-class': [2, 'always'],
+ // Prevent missing props validation in a React component definition
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md
+ 'react/prop-types': [2, { 'ignore': [], customValidators: [] }],
+ // Prevent missing React when using JSX
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md
+ 'react/react-in-jsx-scope': 2,
+ // Restrict file extensions that may be required
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-extension.md
+ 'react/require-extension': [0, { 'extensions': ['.jsx'] }],
+ // Prevent extra closing tags for components without children
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md
+ 'react/self-closing-comp': 2,
+ // Enforce component methods order
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md
+ 'react/sort-comp': [2, {
+ 'order': [
+ 'lifecycle',
+ '/^on.+$/',
+ '/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/',
+ 'everything-else',
+ '/^render.+$/',
+ 'render'
+ ]
+ }],
+ // Prevent missing parentheses around multilines JSX
+ // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/wrap-multilines.md
+ 'react/wrap-multilines': [2, {
+ declaration: true,
+ assignment: true,
+ return: true
+ }],
+ }
+};
diff --git a/node_modules/eslint-config-airbnb/rules/strict.js b/node_modules/eslint-config-airbnb/rules/strict.js
new file mode 100644
index 0000000..6d32f8c
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/rules/strict.js
@@ -0,0 +1,6 @@
+module.exports = {
+ 'rules': {
+ // babel inserts `'use strict';` for us
+ 'strict': [2, 'never']
+ }
+};
diff --git a/node_modules/eslint-config-airbnb/rules/style.js b/node_modules/eslint-config-airbnb/rules/style.js
new file mode 100644
index 0000000..25c5326
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/rules/style.js
@@ -0,0 +1,128 @@
+module.exports = {
+ 'rules': {
+ // enforce spacing inside array brackets
+ 'array-bracket-spacing': [2, 'never'],
+ // enforce one true brace style
+ 'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
+ // require camel case names
+ 'camelcase': [2, { 'properties': 'never' }],
+ // enforce spacing before and after comma
+ 'comma-spacing': [2, { 'before': false, 'after': true }],
+ // enforce one true comma style
+ 'comma-style': [2, 'last'],
+ // disallow padding inside computed properties
+ 'computed-property-spacing': [2, 'never'],
+ // enforces consistent naming when capturing the current execution context
+ 'consistent-this': 0,
+ // enforce newline at the end of file, with no multiple empty lines
+ 'eol-last': 2,
+ // require function expressions to have a name
+ 'func-names': 1,
+ // enforces use of function declarations or expressions
+ 'func-style': 0,
+ // this option enforces minimum and maximum identifier lengths
+ // (variable names, property names etc.)
+ 'id-length': 0,
+ // this option sets a specific tab width for your code
+ // https://github.com/eslint/eslint/blob/master/docs/rules/indent.md
+ 'indent': [2, 2, { 'SwitchCase': 1, 'VariableDeclarator': 1 }],
+ // specify whether double or single quotes should be used in JSX attributes
+ // http://eslint.org/docs/rules/jsx-quotes
+ 'jsx-quotes': [2, 'prefer-double'],
+ // enforces spacing between keys and values in object literal properties
+ 'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }],
+ // enforces empty lines around comments
+ 'lines-around-comment': 0,
+ // disallow mixed 'LF' and 'CRLF' as linebreaks
+ 'linebreak-style': 0,
+ // specify the maximum length of a line in your program
+ // https://github.com/eslint/eslint/blob/master/docs/rules/max-len.md
+ 'max-len': [2, 100, 2, {
+ 'ignoreUrls': true,
+ 'ignoreComments': false
+ }],
+ // specify the maximum depth callbacks can be nested
+ 'max-nested-callbacks': 0,
+ // require a capital letter for constructors
+ 'new-cap': [2, { 'newIsCap': true }],
+ // disallow the omission of parentheses when invoking a constructor with no arguments
+ 'new-parens': 0,
+ // allow/disallow an empty newline after var statement
+ 'newline-after-var': 0,
+ // disallow use of the Array constructor
+ 'no-array-constructor': 0,
+ // disallow use of the continue statement
+ 'no-continue': 0,
+ // disallow comments inline after code
+ 'no-inline-comments': 0,
+ // disallow if as the only statement in an else block
+ 'no-lonely-if': 0,
+ // disallow mixed spaces and tabs for indentation
+ 'no-mixed-spaces-and-tabs': 2,
+ // disallow multiple empty lines and only one newline at the end
+ 'no-multiple-empty-lines': [2, { 'max': 2, 'maxEOF': 1 }],
+ // disallow nested ternary expressions
+ 'no-nested-ternary': 2,
+ // disallow use of the Object constructor
+ 'no-new-object': 2,
+ // disallow space between function identifier and application
+ 'no-spaced-func': 2,
+ // disallow the use of ternary operators
+ 'no-ternary': 0,
+ // disallow trailing whitespace at the end of lines
+ 'no-trailing-spaces': 2,
+ // disallow dangling underscores in identifiers
+ 'no-underscore-dangle': 0,
+ // disallow the use of Boolean literals in conditional expressions
+ // also, prefer `a || b` over `a ? a : b`
+ // http://eslint.org/docs/rules/no-unneeded-ternary
+ 'no-unneeded-ternary': [2, { 'defaultAssignment': false }],
+ // require padding inside curly braces
+ 'object-curly-spacing': [2, 'always'],
+ // allow just one var statement per function
+ 'one-var': [2, 'never'],
+ // require assignment operator shorthand where possible or prohibit it entirely
+ 'operator-assignment': 0,
+ // enforce operators to be placed before or after line breaks
+ 'operator-linebreak': 0,
+ // enforce padding within blocks
+ 'padded-blocks': [2, 'never'],
+ // require quotes around object literal property names
+ // http://eslint.org/docs/rules/quote-props.html
+ 'quote-props': [2, 'as-needed', { 'keywords': false, 'unnecessary': true, 'numbers': false }],
+ // specify whether double or single quotes should be used
+ 'quotes': [2, 'single', 'avoid-escape'],
+ // require identifiers to match the provided regular expression
+ 'id-match': 0,
+ // enforce spacing before and after semicolons
+ 'semi-spacing': [2, { 'before': false, 'after': true }],
+ // require or disallow use of semicolons instead of ASI
+ 'semi': [2, 'always'],
+ // sort variables within the same declaration block
+ 'sort-vars': 0,
+ // require a space before certain keywords
+ 'space-before-keywords': [2, 'always'],
+ // require a space after certain keywords
+ 'space-after-keywords': [2, 'always'],
+ // require or disallow space before blocks
+ 'space-before-blocks': 2,
+ // require or disallow space before function opening parenthesis
+ // https://github.com/eslint/eslint/blob/master/docs/rules/space-before-function-paren.md
+ 'space-before-function-paren': [2, { 'anonymous': 'always', 'named': 'never' }],
+ // require or disallow spaces inside parentheses
+ 'space-in-parens': [2, 'never'],
+ // require spaces around operators
+ 'space-infix-ops': 2,
+ // require a space after return, throw, and case
+ 'space-return-throw-case': 2,
+ // Require or disallow spaces before/after unary operators
+ 'space-unary-ops': 0,
+ // require or disallow a space immediately following the // or /* in a comment
+ 'spaced-comment': [2, 'always', {
+ 'exceptions': ['-', '+'],
+ 'markers': ['=', '!'] // space here to support sprockets directives
+ }],
+ // require regex literals to be wrapped in parentheses
+ 'wrap-regex': 0
+ }
+};
diff --git a/node_modules/eslint-config-airbnb/rules/variables.js b/node_modules/eslint-config-airbnb/rules/variables.js
new file mode 100644
index 0000000..5991431
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/rules/variables.js
@@ -0,0 +1,26 @@
+module.exports = {
+ 'rules': {
+ // enforce or disallow variable initializations at definition
+ 'init-declarations': 0,
+ // disallow the catch clause parameter name being the same as a variable in the outer scope
+ 'no-catch-shadow': 0,
+ // disallow deletion of variables
+ 'no-delete-var': 2,
+ // disallow labels that share a name with a variable
+ 'no-label-var': 0,
+ // disallow shadowing of names such as arguments
+ 'no-shadow-restricted-names': 2,
+ // disallow declaration of variables already declared in the outer scope
+ 'no-shadow': 2,
+ // disallow use of undefined when initializing variables
+ 'no-undef-init': 0,
+ // disallow use of undeclared variables unless mentioned in a /*global */ block
+ 'no-undef': 2,
+ // disallow use of undefined variable
+ 'no-undefined': 0,
+ // disallow declaration of variables that are not used in the code
+ 'no-unused-vars': [2, { 'vars': 'local', 'args': 'after-used' }],
+ // disallow use of variables before they are defined
+ 'no-use-before-define': 2
+ }
+};
diff --git a/node_modules/eslint-config-airbnb/test/.eslintrc b/node_modules/eslint-config-airbnb/test/.eslintrc
new file mode 100644
index 0000000..7f79874
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/test/.eslintrc
@@ -0,0 +1,9 @@
+{
+ "rules": {
+ // disabled because I find it tedious to write tests while following this
+ // rule
+ "no-shadow": 0,
+ // tests uses `t` for tape
+ "id-length": [2, {"min": 2, "properties": "never", "exceptions": ["t"]}]
+ }
+}
diff --git a/node_modules/eslint-config-airbnb/test/test-base.js b/node_modules/eslint-config-airbnb/test/test-base.js
new file mode 100644
index 0000000..24aa884
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/test/test-base.js
@@ -0,0 +1,30 @@
+import fs from 'fs';
+import path from 'path';
+import test from 'tape';
+
+const files = {
+ base: require('../base')
+};
+
+fs.readdirSync(path.join(__dirname, '../rules')).forEach(name => {
+ if (name === 'react.js') {
+ return;
+ }
+
+ files[name] = require(`../rules/${name}`);
+});
+
+Object.keys(files).forEach(name => {
+ const config = files[name];
+
+ test(`${name}: does not reference react`, t => {
+ t.plan(2);
+
+ t.notOk(config.plugins, 'plugins is unspecified');
+
+ // scan rules for react/ and fail if any exist
+ const reactRuleIds = Object.keys(config.rules)
+ .filter(ruleId => ruleId.indexOf('react/') === 0);
+ t.deepEquals(reactRuleIds, [], 'there are no react/ rules');
+ });
+});
diff --git a/node_modules/eslint-config-airbnb/test/test-react-order.js b/node_modules/eslint-config-airbnb/test/test-react-order.js
new file mode 100644
index 0000000..77448cf
--- /dev/null
+++ b/node_modules/eslint-config-airbnb/test/test-react-order.js
@@ -0,0 +1,86 @@
+import test from 'tape';
+import { CLIEngine } from 'eslint';
+import eslintrc from '../';
+import reactRules from '../rules/react';
+
+const cli = new CLIEngine({
+ useEslintrc: false,
+ baseConfig: eslintrc,
+
+ // This rule fails when executing on text.
+ rules: { indent: 0 },
+});
+
+function lint(text) {
+ // @see http://eslint.org/docs/developer-guide/nodejs-api.html#executeonfiles
+ // @see http://eslint.org/docs/developer-guide/nodejs-api.html#executeontext
+ return cli.executeOnText(text).results[0];
+}
+
+function wrapComponent(body) {
+ return `
+import React from 'react';
+export default class MyComponent extends React.Component {
+${body}
+}
+`;
+}
+
+test('validate react prop order', t => {
+ t.test('make sure our eslintrc has React linting dependencies', t => {
+ t.plan(1);
+ t.equal(reactRules.plugins[0], 'react', 'uses eslint-plugin-react');
+ });
+
+ t.test('passes a good component', t => {
+ t.plan(3);
+ const result = lint(wrapComponent(`
+ componentWillMount() {}
+ componentDidMount() {}
+ setFoo() {}
+ getFoo() {}
+ setBar() {}
+ someMethod() {}
+ renderDogs() {}
+ render() { return ; }
+`));
+
+ t.notOk(result.warningCount, 'no warnings');
+ t.notOk(result.errorCount, 'no errors');
+ t.deepEquals(result.messages, [], 'no messages in results');
+ });
+
+ t.test('order: when random method is first', t => {
+ t.plan(2);
+ const result = lint(wrapComponent(`
+ someMethod() {}
+ componentWillMount() {}
+ componentDidMount() {}
+ setFoo() {}
+ getFoo() {}
+ setBar() {}
+ renderDogs() {}
+ render() { return ; }
+`));
+
+ t.ok(result.errorCount, 'fails');
+ t.equal(result.messages[0].ruleId, 'react/sort-comp', 'fails due to sort');
+ });
+
+ t.test('order: when random method after lifecycle methods', t => {
+ t.plan(2);
+ const result = lint(wrapComponent(`
+ componentWillMount() {}
+ componentDidMount() {}
+ someMethod() {}
+ setFoo() {}
+ getFoo() {}
+ setBar() {}
+ renderDogs() {}
+ render() { return ; }
+`));
+
+ t.ok(result.errorCount, 'fails');
+ t.equal(result.messages[0].ruleId, 'react/sort-comp', 'fails due to sort');
+ });
+});
diff --git a/node_modules/eslint-plugin-react/CHANGELOG.md b/node_modules/eslint-plugin-react/CHANGELOG.md
new file mode 100644
index 0000000..49ffa6e
--- /dev/null
+++ b/node_modules/eslint-plugin-react/CHANGELOG.md
@@ -0,0 +1,877 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+This project adheres to [Semantic Versioning](http://semver.org/).
+This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com).
+
+## [3.16.1] - 2016-01-24
+### Fixed
+* Fix `jsx-sort-prop-types` issue with custom propTypes ([#408][] @alitaheri)
+
+[3.16.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.16.0...v3.16.1
+[#408]: https://github.com/yannickcr/eslint-plugin-react/issues/408
+
+## [3.16.0] - 2016-01-24
+### Added
+* Add `jsx-equals-spacing` rule ([#394][] @ryym)
+* Add auto fix for `wrap-multiline`
+* Add auto fix for `jsx-boolean-value`
+* Add auto fix for `no-unknown-property`
+* Add auto fix for `jsx-curly-spacing` ([#407][] @ewendel)
+* Add `requiredFirst` option to `jsx-sort-prop-types` ([#392][] @chrislaskey)
+* Add `ignoreRefs` option to `jsx-no-bind` ([#330][] @silvenon)
+
+### Fixed
+* Ignore `ref` in `jsx-handler-names` (again) ([#396][])
+
+### Changed
+* Update dependencies
+
+[3.16.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.15.0...v3.16.0
+[#394]: https://github.com/yannickcr/eslint-plugin-react/issues/394
+[#407]: https://github.com/yannickcr/eslint-plugin-react/pull/407
+[#392]: https://github.com/yannickcr/eslint-plugin-react/pull/392
+[#330]: https://github.com/yannickcr/eslint-plugin-react/issues/330
+[#396]: https://github.com/yannickcr/eslint-plugin-react/issues/396
+
+## [3.15.0] - 2016-01-12
+### Added
+* Add support for flow annotations to `prop-types` ([#382][] @phpnode)
+
+### Fixed
+* Fix `prop-types` crash when initializing class variable with an empty object ([#383][])
+* Fix `prop-types` crash when propTypes are using the spread operator ([#389][])
+
+### Changed
+* Improve `sort-comp` error messages ([#372][] @SystemParadox)
+* Update dependencies
+
+[3.15.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.14.0...v3.15.0
+[#382]: https://github.com/yannickcr/eslint-plugin-react/pull/382
+[#383]: https://github.com/yannickcr/eslint-plugin-react/issues/383
+[#389]: https://github.com/yannickcr/eslint-plugin-react/issues/389
+[#372]: https://github.com/yannickcr/eslint-plugin-react/pull/372
+
+## [3.14.0] - 2016-01-05
+### Added
+* Add `jsx-indent` rule ([#342][])
+* Add shared setting for pragma configuration ([#228][] @NickStefan)
+
+### Fixed
+* Fix crash in `jsx-key` ([#380][] @nfcampos)
+* Fix crash in `forbid-prop-types` ([#377][] @nfcampos)
+* Ignore `ref` in `jsx-handler-names` ([#375][])
+
+### Changed
+* Add AppVeyor CI to run tests on a Windows platform
+* Add `sort-comp` codemod to `sort-comp` documentation ([#381][] @turadg)
+
+[3.14.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.13.1...v3.14.0
+[#342]: https://github.com/yannickcr/eslint-plugin-react/issues/342
+[#228]: https://github.com/yannickcr/eslint-plugin-react/issues/228
+[#380]: https://github.com/yannickcr/eslint-plugin-react/pull/380
+[#377]: https://github.com/yannickcr/eslint-plugin-react/pull/377
+[#375]: https://github.com/yannickcr/eslint-plugin-react/issues/375
+[#381]: https://github.com/yannickcr/eslint-plugin-react/pull/381
+
+## [3.13.1] - 2015-12-26
+### Fixed
+* Fix crash in `jsx-key` ([#373][] @lukekarrys)
+
+[3.13.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.13.0...v3.13.1
+[#373]: https://github.com/yannickcr/eslint-plugin-react/issues/373
+
+## [3.13.0] - 2015-12-24
+### Added
+* Add `no-string-refs` rule ([#341][] @Intellicode)
+* Add support for propTypes assigned via a variable in `prop-types` ([#355][])
+
+### Fixed
+* Fix `never` option in `prefer-es6-class`
+* Fix `jsx-key` false-positives ([#320][] @silvenon)
+
+### Changed
+* Documentation improvements ([#368][] @lencioni, [#370][] @tmcw, [#371][])
+* Update dependencies
+
+[3.13.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.12.0...v3.13.0
+[#341]: https://github.com/yannickcr/eslint-plugin-react/issues/341
+[#355]: https://github.com/yannickcr/eslint-plugin-react/issues/355
+[#320]: https://github.com/yannickcr/eslint-plugin-react/issues/320
+
+[#368]: https://github.com/yannickcr/eslint-plugin-react/pull/368
+[#370]: https://github.com/yannickcr/eslint-plugin-react/pull/370
+[#371]: https://github.com/yannickcr/eslint-plugin-react/issues/371
+
+## [3.12.0] - 2015-12-20
+### Added
+* Add `no-deprecated` rule ([#356][] @graue)
+* Add `no-is-mounted` rule ([#37][] @lencioni)
+* Add `never` option to `prefer-es6-class` rule ([#359][] @pwmckenna)
+
+### Fixed
+* Fix `jsx-pascal-case` to stop checking lower cased components ([#329][])
+* Fix crash in component detection class ([#364][])
+
+### Changed
+* Add link to [eslint-plugin-react-native](https://github.com/Intellicode/eslint-plugin-react-native) in Readme
+* Update dependencies
+
+[3.12.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.11.3...v3.12.0
+[#356]: https://github.com/yannickcr/eslint-plugin-react/pull/356
+[#37]: https://github.com/yannickcr/eslint-plugin-react/issues/37
+[#359]: https://github.com/yannickcr/eslint-plugin-react/pull/359
+[#329]: https://github.com/yannickcr/eslint-plugin-react/issues/329
+[#364]: https://github.com/yannickcr/eslint-plugin-react/issues/364
+
+## [3.11.3] - 2015-12-05
+### Fixed
+* Fix crash in `prop-types` when reassigning props ([#345][])
+* Fix `jsx-handler-names` for stateless components ([#346][])
+
+### Changed
+* Update `jsx-handler-names` error messages to be less specific ([#348][] @jakemmarsh)
+
+[3.11.3]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.11.2...v3.11.3
+[#345]: https://github.com/yannickcr/eslint-plugin-react/issues/345
+[#346]: https://github.com/yannickcr/eslint-plugin-react/issues/346
+[#348]: https://github.com/yannickcr/eslint-plugin-react/pull/348
+
+## [3.11.2] - 2015-12-01
+### Fixed
+* Allow numbers in `jsx-pascal-case` ([#339][])
+* Fix `jsx-handler-names` crash with arrays ([#340][])
+
+### Changed
+* Add allow-in-func option to `no-did-update-set-state` documentation
+
+[3.11.2]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.11.1...v3.11.2
+[#339]: https://github.com/yannickcr/eslint-plugin-react/issues/339
+[#340]: https://github.com/yannickcr/eslint-plugin-react/issues/340
+
+## [3.11.1] - 2015-11-29
+### Fixed
+* Fix SVG attributes support for `no-unknown-property` ([#338][])
+
+[3.11.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.11.0...v3.11.1
+[#338]: https://github.com/yannickcr/eslint-plugin-react/issues/338
+
+## [3.11.0] - 2015-11-29
+### Added
+* Add `jsx-handler-names` rule ([#315][] @jakemmarsh)
+* Add SVG attributes support to `no-unknown-property` ([#318][])
+* Add shorthandFirst option to `jsx-sort-props` ([#336][] @lucasmotta)
+
+### Fixed
+* Fix destructured props detection in stateless components ([#326][])
+* Fix props validation for nested stateless components ([#331][])
+* Fix `require-extension` to ignore extension if it's part of the package name ([#319][])
+
+### Changed
+* Allow consecutive uppercase letters in `jsx-pascal-case` ([#328][] @lencioni)
+* Update dependencies
+
+[3.11.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.10.0...v3.11.0
+[#315]: https://github.com/yannickcr/eslint-plugin-react/pull/315
+[#318]: https://github.com/yannickcr/eslint-plugin-react/issues/318
+[#336]: https://github.com/yannickcr/eslint-plugin-react/pull/336
+[#326]: https://github.com/yannickcr/eslint-plugin-react/issues/326
+[#331]: https://github.com/yannickcr/eslint-plugin-react/issues/331
+[#319]: https://github.com/yannickcr/eslint-plugin-react/issues/319
+[#328]: https://github.com/yannickcr/eslint-plugin-react/issues/328
+
+## [3.10.0] - 2015-11-21
+### Added
+* Add `jsx-pascal-case` rule ([#306][] @jakemmarsh)
+
+### Fixed
+* Fix crash on incomplete class property declaration ([#317][] @dapetcu21)
+* Fix crash with ESLint 1.10.0 ([#323][] @lukekarrys)
+
+[3.10.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.9.0...v3.10.0
+[#306]: https://github.com/yannickcr/eslint-plugin-react/pull/306
+[#317]: https://github.com/yannickcr/eslint-plugin-react/issues/317
+[#323]: https://github.com/yannickcr/eslint-plugin-react/issues/323
+
+## [3.9.0] - 2015-11-17
+### Added
+* Add `jsx-key` rule ([#293][] @benmosher)
+* Add `allow-in-func` option to `no-did-update-set-state` ([#300][])
+* Add option to only enforce `jsx-closing-bracket-location` rule to one type of tag (nonEmpty or selfClosing) ([#307][])
+
+### Fixed
+* Fix crash when destructuring with only the rest spread ([#269][])
+* Fix variables detection when searching for related components ([#303][])
+* Fix `no-unknown-property` to not check custom elements ([#308][] @zertosh)
+
+### Changed
+* Improve `jsx-closing-bracket-location` error message ([#301][] @alopatin)
+* Update dependencies
+
+[3.9.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.8.0...v3.9.0
+[#293]: https://github.com/yannickcr/eslint-plugin-react/pull/293
+[#300]: https://github.com/yannickcr/eslint-plugin-react/issues/300
+[#307]: https://github.com/yannickcr/eslint-plugin-react/issues/307
+[#269]: https://github.com/yannickcr/eslint-plugin-react/issues/269
+[#303]: https://github.com/yannickcr/eslint-plugin-react/issues/303
+[#308]: https://github.com/yannickcr/eslint-plugin-react/pull/308
+[#301]: https://github.com/yannickcr/eslint-plugin-react/pull/301
+
+## [3.8.0] - 2015-11-07
+### Added
+* Add ignoreStateless option to `no-multi-comp` ([#290][])
+
+### Fixed
+* Fix classes with properties to always be marked as components ([#291][])
+* Fix ES5 class detection when using `createClass` by itself ([#297][])
+* Fix direct props detection ([#298][])
+* Ignore functions containing the keyword `this` during component detection
+
+[3.8.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.7.1...v3.8.0
+[#290]: https://github.com/yannickcr/eslint-plugin-react/issues/290
+[#291]: https://github.com/yannickcr/eslint-plugin-react/issues/291
+[#297]: https://github.com/yannickcr/eslint-plugin-react/issues/297
+[#298]: https://github.com/yannickcr/eslint-plugin-react/issues/298
+
+## [3.7.1] - 2015-11-05
+### Fixed
+* Fix `sort-comp` crash on stateless components ([#285][])
+* Fix crash in ES5 components detection ([#286][])
+* Fix ES5 components detection from nested functions ([#287][])
+
+[3.7.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.7.0...v3.7.1
+[#285]: https://github.com/yannickcr/eslint-plugin-react/issues/285
+[#286]: https://github.com/yannickcr/eslint-plugin-react/issues/286
+[#287]: https://github.com/yannickcr/eslint-plugin-react/issues/287
+
+## [3.7.0] - 2015-11-05
+### Added
+* Add `jsx-no-bind` rule ([#184][] @Daniel15)
+* Add line-aligned option to `jsx-closing-bracket-location` ([#243][] @alopatin)
+
+### Fixed
+* Fix a lot of issues about components detection, mostly related to stateless components ([#264][], [#267][], [#268][], [#276][], [#277][], [#280][])
+
+### Changed
+* Update dependencies
+
+[3.7.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.6.3...v3.7.0
+[#184]: https://github.com/yannickcr/eslint-plugin-react/issues/184
+[#243]: https://github.com/yannickcr/eslint-plugin-react/issues/243
+[#264]: https://github.com/yannickcr/eslint-plugin-react/issues/264
+[#267]: https://github.com/yannickcr/eslint-plugin-react/issues/267
+[#268]: https://github.com/yannickcr/eslint-plugin-react/issues/268
+[#276]: https://github.com/yannickcr/eslint-plugin-react/issues/276
+[#277]: https://github.com/yannickcr/eslint-plugin-react/issues/277
+[#280]: https://github.com/yannickcr/eslint-plugin-react/issues/280
+
+## [3.6.3] - 2015-10-20
+### Fixed
+* Fix `display-name` for stateless components ([#256][])
+* Fix `prop-types` props validation in constructor ([#259][])
+* Fix typo in README ([#261][] @chiedojohn)
+
+[3.6.3]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.6.2...v3.6.3
+[#256]: https://github.com/yannickcr/eslint-plugin-react/issues/256
+[#259]: https://github.com/yannickcr/eslint-plugin-react/issues/259
+[#261]: https://github.com/yannickcr/eslint-plugin-react/pull/261
+
+## [3.6.2] - 2015-10-18
+### Fixed
+* Fix wrong prop-types detection ([#255][])
+
+[3.6.2]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.6.1...v3.6.2
+[#255]: https://github.com/yannickcr/eslint-plugin-react/issues/255
+
+## [3.6.1] - 2015-10-18
+### Fixed
+* Fix props validation in constructor ([#254][])
+
+[3.6.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.6.0...v3.6.1
+[#254]: https://github.com/yannickcr/eslint-plugin-react/issues/254
+
+## [3.6.0] - 2015-10-18
+### Added
+* Add support for stateless function components to `display-name` and `prop-types` ([#237][])
+* Add callbacksLast option to `jsx-sort-props` and `jsx-sort-prop-types` ([#242][] @Daniel15)
+* Add `prefer-es6-class` rule ([#247][] @hamiltondanielb)
+
+### Fixed
+* Fix `forbid-prop-types` crash with destructured PropTypes ([#230][] @epmatsw)
+* Fix `forbid-prop-types` to do not modify AST directly ([#249][] @rhysd)
+* Fix `prop-types` crash with empty destructuring ([#251][])
+* Fix `prop-types` to not validate computed keys in destructuring ([#236][])
+
+### Changed
+* Update dependencies
+* Improve components detection ([#233][])
+* Documentation improvements ([#248][] @dguo)
+
+[3.6.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.5.1...v3.6.0
+[#237]: https://github.com/yannickcr/eslint-plugin-react/issues/237
+[#242]: https://github.com/yannickcr/eslint-plugin-react/pull/242
+[#247]: https://github.com/yannickcr/eslint-plugin-react/issues/247
+[#230]: https://github.com/yannickcr/eslint-plugin-react/issues/230
+[#249]: https://github.com/yannickcr/eslint-plugin-react/issues/249
+[#251]: https://github.com/yannickcr/eslint-plugin-react/issues/251
+[#236]: https://github.com/yannickcr/eslint-plugin-react/issues/236
+[#233]: https://github.com/yannickcr/eslint-plugin-react/issues/233
+[#248]: https://github.com/yannickcr/eslint-plugin-react/pull/248
+
+## [3.5.1] - 2015-10-01
+### Fixed
+* Fix `no-direct-mutation-state` to report only in React components ([#229][])
+* Fix `forbid-prop-types` for arrayOf and instanceOf ([#230][])
+
+### Changed
+* Documentation improvements ([#232][] @edge)
+
+[3.5.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.5.0...v3.5.1
+[#229]: https://github.com/yannickcr/eslint-plugin-react/issues/229
+[#230]: https://github.com/yannickcr/eslint-plugin-react/issues/230
+[#232]: https://github.com/yannickcr/eslint-plugin-react/pull/232
+
+## [3.5.0] - 2015-09-28
+### Added
+* Add `no-direct-mutation-state` rule ([#133][], [#201][] @petersendidit)
+* Add `forbid-prop-types` rule ([#215][] @pwmckenna)
+
+### Fixed
+* Fix no-did-mount/update-set-state rules, these rules were not working on ES6 classes
+
+### Changed
+* Update dependencies
+* Documentation improvements ([#222][] @Andersos)
+
+[3.5.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.4.2...v3.5.0
+[#133]: https://github.com/yannickcr/eslint-plugin-react/issues/133
+[#201]: https://github.com/yannickcr/eslint-plugin-react/issues/201
+[#215]: https://github.com/yannickcr/eslint-plugin-react/issues/215
+[#222]: https://github.com/yannickcr/eslint-plugin-react/pull/222
+
+## [3.4.2] - 2015-09-18
+### Fixed
+* Only display the `jsx-quotes` deprecation warning with the default formatter ([#221][])
+
+[3.4.2]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.4.1...v3.4.2
+[#221]: https://github.com/yannickcr/eslint-plugin-react/issues/221
+
+## [3.4.1] - 2015-09-17
+### Fixed
+* Fix `jsx-quotes` rule deprecation message ([#220][])
+
+[3.4.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.4.0...v3.4.1
+[#220]: https://github.com/yannickcr/eslint-plugin-react/issues/220
+
+## [3.4.0] - 2015-09-16
+### Added
+* Add namespaced JSX support to `jsx-no-undef` ([#219][] @zertosh)
+* Add option to `jsx-closing-bracket-location` to configure different styles for self-closing and non-empty tags ([#208][] @evocateur)
+
+### Deprecated
+* Deprecate `jsx-quotes` rule, will now trigger a warning if used ([#217][])
+
+[3.4.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.3.2...v3.4.0
+[#219]: https://github.com/yannickcr/eslint-plugin-react/pull/219
+[#208]: https://github.com/yannickcr/eslint-plugin-react/pull/208
+[#217]: https://github.com/yannickcr/eslint-plugin-react/issues/217
+
+## [3.3.2] - 2015-09-10
+### Changed
+* Add `state` in lifecycle methods for `sort-comp` rule ([#197][] @mathieudutour)
+* Treat component with render which returns `createElement` as valid ([#206][] @epmatsw)
+
+### Fixed
+* Fix allowed methods on arrayOf in `prop-types` ([#146][])
+* Fix default configuration for `jsx-boolean-value` ([#210][])
+
+[3.3.2]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.3.1...v3.3.2
+[#146]: https://github.com/yannickcr/eslint-plugin-react/issues/146
+[#197]: https://github.com/yannickcr/eslint-plugin-react/pull/197
+[#206]: https://github.com/yannickcr/eslint-plugin-react/pull/206
+[#210]: https://github.com/yannickcr/eslint-plugin-react/issues/210
+
+## [3.3.1] - 2015-09-01
+### Changed
+* Update dependencies
+* Update changelog to follow the Keep a CHANGELOG standards
+* Documentation improvements ([#198][] @lencioni)
+
+### Fixed
+* Fix `jsx-closing-bracket-location` for multiline props ([#199][])
+
+[3.3.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.3.0...v3.3.1
+[#198]: https://github.com/yannickcr/eslint-plugin-react/pull/198
+[#199]: https://github.com/yannickcr/eslint-plugin-react/issues/199
+
+## [3.3.0] - 2015-08-26
+### Added
+* Add `jsx-indent-props` rule ([#15][], [#181][])
+* Add `no-set-state rule` ([#197][] @markdalgleish)
+* Add `jsx-closing-bracket-location` rule ([#14][], [#64][])
+
+### Changed
+* Update dependencies
+
+### Fixed
+* Fix crash on propTypes declarations with an empty body ([#193][] @mattyod)
+
+[3.3.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.2.3...v3.3.0
+[#15]: https://github.com/yannickcr/eslint-plugin-react/issues/15
+[#181]: https://github.com/yannickcr/eslint-plugin-react/issues/181
+[#197]: https://github.com/yannickcr/eslint-plugin-react/pull/197
+[#14]: https://github.com/yannickcr/eslint-plugin-react/issues/14
+[#64]: https://github.com/yannickcr/eslint-plugin-react/issues/64
+[#193]: https://github.com/yannickcr/eslint-plugin-react/pull/193
+
+## [3.2.3] - 2015-08-16
+### Changed
+* Update dependencies
+
+### Fixed
+* Fix object rest/spread handling ([#187][] @xjamundx, [#189][] @Morantron)
+
+[3.2.3]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.2.2...v3.2.3
+[#187]: https://github.com/yannickcr/eslint-plugin-react/pull/187
+[#189]: https://github.com/yannickcr/eslint-plugin-react/pull/189
+
+## [3.2.2] - 2015-08-11
+### Changed
+* Remove peerDependencies ([#178][])
+
+[3.2.2]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.2.1...v3.2.2
+[#178]: https://github.com/yannickcr/eslint-plugin-react/issues/178
+
+## [3.2.1] - 2015-08-08
+### Fixed
+* Fix crash when propTypes don't have any parent ([#182][])
+* Fix jsx-no-literals reporting errors outside JSX ([#183][] @CalebMorris)
+
+[3.2.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.2.0...v3.2.1
+[#182]: https://github.com/yannickcr/eslint-plugin-react/issues/182
+[#183]: https://github.com/yannickcr/eslint-plugin-react/pull/183
+
+## [3.2.0] - 2015-08-04
+### Added
+* Add `jsx-max-props-per-line` rule ([#13][])
+* Add `jsx-no-literals` rule ([#176][] @CalebMorris)
+
+### Changed
+* Update dependencies
+
+### Fixed
+* Fix object access in `jsx-no-undef` ([#172][])
+
+[3.2.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.1.0...v3.2.0
+[#13]: https://github.com/yannickcr/eslint-plugin-react/issues/13
+[#176]: https://github.com/yannickcr/eslint-plugin-react/pull/176
+[#172]: https://github.com/yannickcr/eslint-plugin-react/issues/172
+
+## [3.1.0] - 2015-07-28
+### Added
+* Add event handlers to `no-unknown-property` ([#164][] @mkenyon)
+* Add customValidators option to `prop-types` ([#145][] @CalebMorris)
+
+### Changed
+* Update dependencies
+* Documentation improvements ([#167][] @ngbrown)
+
+### Fixed
+* Fix comment handling in `jsx-curly-spacing` ([#165][])
+
+[3.1.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v3.0.0...v3.1.0
+[#164]: https://github.com/yannickcr/eslint-plugin-react/pull/164
+[#145]: https://github.com/yannickcr/eslint-plugin-react/issues/145
+[#165]: https://github.com/yannickcr/eslint-plugin-react/issues/165
+[#167]: https://github.com/yannickcr/eslint-plugin-react/pull/167
+
+## [3.0.0] - 2015-07-21
+### Added
+* Add jsx-no-duplicate-props rule ([#161][] @hummlas)
+* Add allowMultiline option to the `jsx-curly-spacing` rule ([#156][] @mathieumg)
+
+## Breaking
+* In `jsx-curly-spacing` braces spanning multiple lines are now allowed with `never` option ([#156][] @mathieumg)
+
+### Fixed
+* Fix multiple var and destructuring handling in `props-types` ([#159][])
+* Fix crash when retrieving propType name ([#163][])
+
+[3.0.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.7.1...v3.0.0
+[#161]: https://github.com/yannickcr/eslint-plugin-react/pull/161
+[#156]: https://github.com/yannickcr/eslint-plugin-react/pull/156
+[#159]: https://github.com/yannickcr/eslint-plugin-react/issues/159
+[#163]: https://github.com/yannickcr/eslint-plugin-react/issues/163
+
+## [2.7.1] - 2015-07-16
+### Changed
+* Update peerDependencies requirements ([#154][])
+* Update codebase for ESLint v1.0.0
+* Change oneOfType to actually keep the child types ([#148][] @CalebMorris)
+* Documentation improvements ([#147][] @lencioni)
+
+[2.7.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.7.0...v2.7.1
+[#154]: https://github.com/yannickcr/eslint-plugin-react/issues/154
+[#148]: https://github.com/yannickcr/eslint-plugin-react/issues/148
+[#147]: https://github.com/yannickcr/eslint-plugin-react/pull/147
+
+## [2.7.0] - 2015-07-11
+### Added
+* Add `no-danger` rule ([#138][] @scothis)
+* Add `jsx-curly-spacing` rule ([#142][])
+
+### Fixed
+* Fix properties limitations on propTypes ([#139][])
+* Fix component detection ([#144][])
+
+[2.7.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.6.4...v2.7.0
+[#138]: https://github.com/yannickcr/eslint-plugin-react/pull/138
+[#142]: https://github.com/yannickcr/eslint-plugin-react/issues/142
+[#139]: https://github.com/yannickcr/eslint-plugin-react/issues/139
+[#144]: https://github.com/yannickcr/eslint-plugin-react/issues/144
+
+## [2.6.4] - 2015-07-02
+### Fixed
+* Fix simple destructuring handling ([#137][])
+
+[2.6.4]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.6.3...v2.6.4
+[#137]: https://github.com/yannickcr/eslint-plugin-react/issues/137
+
+## [2.6.3] - 2015-06-30
+### Fixed
+* Fix ignore option for `prop-types` rule ([#135][])
+* Fix nested props destructuring ([#136][])
+
+[2.6.3]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.6.2...v2.6.3
+[#135]: https://github.com/yannickcr/eslint-plugin-react/issues/135
+[#136]: https://github.com/yannickcr/eslint-plugin-react/issues/136
+
+## [2.6.2] - 2015-06-28
+### Fixed
+* Fix props validation when using a prop as an object key ([#132][])
+
+[2.6.2]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.6.1...v2.6.2
+[#132]: https://github.com/yannickcr/eslint-plugin-react/issues/132
+
+## [2.6.1] - 2015-06-28
+### Fixed
+* Fix crash in `prop-types` when encountering an empty variable declaration ([#130][])
+
+[2.6.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.6.0...v2.6.1
+[#130]: https://github.com/yannickcr/eslint-plugin-react/issues/130
+
+## [2.6.0] - 2015-06-28
+### Added
+* Add support for nested propTypes ([#62][] [#105][] @Cellule)
+* Add `require-extension` rule ([#117][] @scothis)
+* Add support for computed string format in `prop-types` ([#127][] @Cellule)
+* Add ES6 methods to `sort-comp` default configuration ([#97][] [#122][])
+* Add support for props destructuring directly on the this keyword
+* Add `acceptTranspilerName` option to `display-name` rule ([#75][])
+* Add schema to validate rules options
+
+### Changed
+* Update dependencies
+
+### Fixed
+* Fix test command for Windows ([#114][] @Cellule)
+* Fix detection of missing displayName and propTypes when `ecmaFeatures.jsx` is false ([#119][] @rpl)
+* Fix propTypes destructuring with properties as string ([#118][] @Cellule)
+* Fix `jsx-sort-prop-types` support for keys as string ([#123][] @Cellule)
+* Fix crash if a ClassProperty has only one token ([#125][])
+* Fix invalid class property handling in `jsx-sort-prop-types` ([#129][])
+
+[2.6.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.5.2...v2.6.0
+[#62]: https://github.com/yannickcr/eslint-plugin-react/issues/62
+[#105]: https://github.com/yannickcr/eslint-plugin-react/issues/105
+[#114]: https://github.com/yannickcr/eslint-plugin-react/pull/114
+[#117]: https://github.com/yannickcr/eslint-plugin-react/pull/117
+[#119]: https://github.com/yannickcr/eslint-plugin-react/pull/119
+[#118]: https://github.com/yannickcr/eslint-plugin-react/issues/118
+[#123]: https://github.com/yannickcr/eslint-plugin-react/pull/123
+[#125]: https://github.com/yannickcr/eslint-plugin-react/issues/125
+[#127]: https://github.com/yannickcr/eslint-plugin-react/pull/127
+[#97]: https://github.com/yannickcr/eslint-plugin-react/issues/97
+[#122]: https://github.com/yannickcr/eslint-plugin-react/issues/122
+[#129]: https://github.com/yannickcr/eslint-plugin-react/issues/129
+[#75]: https://github.com/yannickcr/eslint-plugin-react/issues/75
+
+## [2.5.2] - 2015-06-14
+### Fixed
+* Fix regression in `jsx-uses-vars` with `babel-eslint` ([#110][])
+
+[2.5.2]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.5.1...v2.5.2
+[#110]: https://github.com/yannickcr/eslint-plugin-react/issues/110
+
+## [2.5.1] - 2015-06-14
+### Changed
+* Update dependencies
+* Documentation improvements ([#99][] @morenoh149)
+
+### Fixed
+* Fix `prop-types` crash when propTypes definition is invalid ([#95][])
+* Fix `jsx-uses-vars` for ES6 classes ([#96][])
+* Fix hasOwnProperty that is taken for a prop ([#102][])
+
+[2.5.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.5.0...v2.5.1
+[#95]: https://github.com/yannickcr/eslint-plugin-react/issues/95
+[#96]: https://github.com/yannickcr/eslint-plugin-react/issues/96
+[#102]: https://github.com/yannickcr/eslint-plugin-react/issues/102
+[#99]: https://github.com/yannickcr/eslint-plugin-react/pull/99
+
+## [2.5.0] - 2015-06-04
+### Added
+* Add option to make `wrap-multilines` more granular ([#94][] @PiPeep)
+
+### Changed
+* Update dependencies
+* Documentation improvements ([#92][] [#93][] @lencioni)
+
+[2.5.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.4.0...v2.5.0
+[#94]: https://github.com/yannickcr/eslint-plugin-react/pull/94
+[#92]: https://github.com/yannickcr/eslint-plugin-react/pull/92
+[#93]: https://github.com/yannickcr/eslint-plugin-react/pull/93
+
+## [2.4.0] - 2015-05-30
+### Added
+* Add pragma option to `jsx-uses-react` ([#82][] @dominicbarnes)
+* Add context props to `sort-comp` ([#89][] @zertosh)
+
+### Changed
+* Update dependencies
+* Documentation improvement ([#91][] @matthewwithanm)
+
+### Fixed
+* Fix itemID in `no-unknown-property` rule ([#85][] @cody)
+* Fix license field in package.json ([#90][] @zertosh)
+* Fix usage of contructor in `sort-comp` options ([#88][])
+
+[2.4.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.3.0...v2.4.0
+[#82]: https://github.com/yannickcr/eslint-plugin-react/pull/82
+[#89]: https://github.com/yannickcr/eslint-plugin-react/pull/89
+[#85]: https://github.com/yannickcr/eslint-plugin-react/pull/85
+[#90]: https://github.com/yannickcr/eslint-plugin-react/pull/90
+[#88]: https://github.com/yannickcr/eslint-plugin-react/issues/88
+[#91]: https://github.com/yannickcr/eslint-plugin-react/pull/91
+
+## [2.3.0] - 2015-05-14
+### Added
+* Add `sort-comp` rule ([#39][])
+* Add `allow-in-func` option to `no-did-mount-set-state` ([#56][])
+
+### Changed
+* Update dependencies
+* Improve errors locations for `prop-types`
+
+### Fixed
+* Fix quoted propTypes in ES6 ([#77][])
+
+[2.3.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.2.0...v2.3.0
+[#39]: https://github.com/yannickcr/eslint-plugin-react/issues/39
+[#77]: https://github.com/yannickcr/eslint-plugin-react/issues/77
+[#56]: https://github.com/yannickcr/eslint-plugin-react/issues/56
+
+## [2.2.0] - 2015-04-22
+### Added
+* Add `jsx-sort-prop-types` rule ([#38][] @AlexKVal)
+
+### Changed
+* Documentation improvements ([#71][] @AlexKVal)
+
+### Fixed
+* Fix variables marked as used when a prop has the same name ([#69][] @burnnat)
+
+[2.2.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.1.1...v2.2.0
+[#38]: https://github.com/yannickcr/eslint-plugin-react/issues/38
+[#69]: https://github.com/yannickcr/eslint-plugin-react/pull/69
+[#71]: https://github.com/yannickcr/eslint-plugin-react/pull/71
+
+## [2.1.1] - 2015-04-17
+### Added
+* Add support for classes static properties ([#43][])
+* Add tests for the `babel-eslint` parser
+* Add ESLint as peerDependency ([#63][] @AlexKVal)
+
+### Changed
+* Documentation improvements ([#55][] @AlexKVal, [#60][] @chriscalo)
+
+[2.1.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.1.0...v2.1.1
+[#43]: https://github.com/yannickcr/eslint-plugin-react/issues/43
+[#63]: https://github.com/yannickcr/eslint-plugin-react/pull/63
+[#55]: https://github.com/yannickcr/eslint-plugin-react/pull/55
+[#60]: https://github.com/yannickcr/eslint-plugin-react/pull/60
+
+## [2.1.0] - 2015-04-06
+### Added
+* Add `jsx-boolean-value` rule ([#11][])
+* Add support for static methods in `display-name` and `prop-types` ([#48][])
+
+### Changed
+* Update `jsx-sort-props` to reset the alphabetical verification on spread ([#47][] @zertosh)
+* Update `jsx-uses-vars` to be enabled by default ([#49][] @banderson)
+
+### Fixed
+* Fix describing comment for hasSpreadOperator() method ([#53][] @AlexKVal)
+
+[2.1.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.0.2...v2.1.0
+[#47]: https://github.com/yannickcr/eslint-plugin-react/pull/47
+[#49]: https://github.com/yannickcr/eslint-plugin-react/pull/49
+[#11]: https://github.com/yannickcr/eslint-plugin-react/issues/11
+[#48]: https://github.com/yannickcr/eslint-plugin-react/issues/48
+[#53]: https://github.com/yannickcr/eslint-plugin-react/pull/53
+
+## [2.0.2] - 2015-03-31
+### Fixed
+* Fix ignore rest spread when destructuring props ([#46][])
+* Fix component detection in `prop-types` and `display-name` ([#45][])
+* Fix spread handling in `jsx-sort-props` ([#42][] @zertosh)
+
+[2.0.2]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.0.1...v2.0.2
+[#46]: https://github.com/yannickcr/eslint-plugin-react/issues/46
+[#45]: https://github.com/yannickcr/eslint-plugin-react/issues/45
+[#42]: https://github.com/yannickcr/eslint-plugin-react/pull/42
+
+## [2.0.1] - 2015-03-30
+### Fixed
+* Fix props detection when used in an object ([#41][])
+
+[2.0.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v2.0.0...v2.0.1
+[#41]: https://github.com/yannickcr/eslint-plugin-react/issues/41
+
+## [2.0.0] - 2015-03-29
+### Added
+* Add `jsx-sort-props` rule ([#16][])
+* Add `no-unknown-property` rule ([#28][])
+* Add ignore option to `prop-types` rule
+
+### Changed
+* Update dependencies
+
+## Breaking
+* In `prop-types` the children prop is no longer ignored
+
+### Fixed
+* Fix components are now detected when using ES6 classes ([#24][])
+* Fix `prop-types` now return the right line/column ([#33][])
+* Fix props are now detected when destructuring ([#27][])
+* Fix only check for computed property names in `prop-types` ([#36][] @burnnat)
+
+[2.0.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v1.6.1...v2.0.0
+[#16]: https://github.com/yannickcr/eslint-plugin-react/issues/16
+[#28]: https://github.com/yannickcr/eslint-plugin-react/issues/28
+[#24]: https://github.com/yannickcr/eslint-plugin-react/issues/24
+[#33]: https://github.com/yannickcr/eslint-plugin-react/issues/33
+[#27]: https://github.com/yannickcr/eslint-plugin-react/issues/27
+[#36]: https://github.com/yannickcr/eslint-plugin-react/pull/36
+
+## [1.6.1] - 2015-03-25
+### Changed
+* Update `jsx-quotes` documentation
+
+### Fixed
+* Fix `jsx-no-undef` with `babel-eslint` ([#30][])
+* Fix `jsx-quotes` on Literal childs ([#29][])
+
+[1.6.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v1.6.0...v1.6.1
+[#30]: https://github.com/yannickcr/eslint-plugin-react/issues/30
+[#29]: https://github.com/yannickcr/eslint-plugin-react/issues/29
+
+## [1.6.0] - 2015-03-22
+### Added
+* Add `jsx-no-undef` rule
+* Add `jsx-quotes` rule ([#12][])
+* Add `@jsx` pragma support ([#23][])
+
+### Changed
+* Allow `this.getState` references (not calls) in lifecycle methods ([#22][] @benmosher)
+* Update dependencies
+
+### Fixed
+* Fix `react-in-jsx-scope` in Node.js env
+* Fix usage of propTypes with an external object ([#9][])
+
+[1.6.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v1.5.0...v1.6.0
+[#12]: https://github.com/yannickcr/eslint-plugin-react/issues/12
+[#23]: https://github.com/yannickcr/eslint-plugin-react/issues/23
+[#9]: https://github.com/yannickcr/eslint-plugin-react/issues/9
+[#22]: https://github.com/yannickcr/eslint-plugin-react/pull/22
+
+## [1.5.0] - 2015-03-14
+### Added
+* Add `jsx-uses-vars` rule
+
+### Fixed
+* Fix `jsx-uses-react` for ESLint 0.17.0
+
+[1.5.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v1.4.1...v1.5.0
+
+## [1.4.1] - 2015-03-03
+### Fixed
+* Fix `this.props.children` marked as missing in props validation ([#7][])
+* Fix usage of `this.props` without property ([#8][])
+
+[1.4.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v1.4.0...v1.4.1
+[#7]: https://github.com/yannickcr/eslint-plugin-react/issues/7
+[#8]: https://github.com/yannickcr/eslint-plugin-react/issues/8
+
+## [1.4.0] - 2015-02-24
+### Added
+* Add `react-in-jsx-scope` rule ([#5][] @glenjamin)
+* Add `jsx-uses-react` rule ([#6][] @glenjamin)
+
+### Changed
+* Update `prop-types` to check props usage insead of propTypes presence ([#4][])
+
+[1.4.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v1.3.0...v1.4.0
+[#4]: https://github.com/yannickcr/eslint-plugin-react/issues/4
+[#5]: https://github.com/yannickcr/eslint-plugin-react/pull/5
+[#6]: https://github.com/yannickcr/eslint-plugin-react/pull/6
+
+## [1.3.0] - 2015-02-24
+### Added
+* Add `no-did-mount-set-state` rule
+* Add `no-did-update-set-state` rule
+
+### Changed
+* Update dependencies
+
+[1.3.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v1.2.2...v1.3.0
+
+## [1.2.2] - 2015-02-09
+### Changed
+* Update dependencies
+
+### Fixed
+* Fix childs detection in `self-closing-comp` ([#3][])
+
+[1.2.2]: https://github.com/yannickcr/eslint-plugin-react/compare/v1.2.1...v1.2.2
+[#3]: https://github.com/yannickcr/eslint-plugin-react/issues/3
+
+## [1.2.1] - 2015-01-29
+### Changed
+* Update Readme
+* Update dependencies
+* Update `wrap-multilines` and `self-closing-comp` rules for ESLint 0.13.0
+
+[1.2.1]: https://github.com/yannickcr/eslint-plugin-react/compare/v1.2.0...v1.2.1
+
+## [1.2.0] - 2014-12-29
+### Added
+* Add `self-closing-comp` rule
+
+### Fixed
+* Fix `display-name` and `prop-types` rules
+
+[1.2.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v1.1.0...v1.2.0
+
+## [1.1.0] - 2014-12-28
+### Added
+ * Add `display-name` rule
+ * Add `wrap-multilines` rule
+ * Add rules documentation
+ * Add rules tests
+
+[1.1.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v1.0.0...v1.1.0
+
+## 1.0.0 - 2014-12-16
+### Added
+ * First revision
diff --git a/node_modules/eslint-plugin-react/LICENSE b/node_modules/eslint-plugin-react/LICENSE
new file mode 100644
index 0000000..6b5a43e
--- /dev/null
+++ b/node_modules/eslint-plugin-react/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Yannick Croissant
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/node_modules/eslint-plugin-react/README.md b/node_modules/eslint-plugin-react/README.md
new file mode 100644
index 0000000..a9c82af
--- /dev/null
+++ b/node_modules/eslint-plugin-react/README.md
@@ -0,0 +1,186 @@
+ESLint-plugin-React
+===================
+
+[![Maintenance Status][status-image]][status-url] [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Build Status][appveyor-image]][appveyor-url] [![Dependency Status][deps-image]][deps-url] [![Coverage Status][coverage-image]][coverage-url] [![Code Climate][climate-image]][climate-url]
+
+React specific linting rules for ESLint
+
+# Installation
+
+Install [ESLint](https://www.github.com/eslint/eslint) either locally or globally.
+
+```sh
+$ npm install eslint
+```
+
+If you installed `ESLint` globally, you have to install React plugin globally too. Otherwise, install it locally.
+
+```sh
+$ npm install eslint-plugin-react
+```
+
+# Configuration
+
+Add `plugins` section and specify ESLint-plugin-React as a plugin.
+
+```json
+{
+ "plugins": [
+ "react"
+ ]
+}
+```
+
+You can also specify some settings that will be shared across all the plugin rules.
+
+```js
+{
+ "settings": {
+ "react": {
+ "pragma": "React" // Pragma to use, default to "React"
+ }
+ }
+}
+```
+
+If it is not already the case you must also configure `ESLint` to support JSX.
+
+With ESLint 1.x.x:
+
+```json
+{
+ "ecmaFeatures": {
+ "jsx": true
+ }
+}
+```
+
+With ESLint 2.x.x:
+
+```json
+{
+ "parserOptions": {
+ "ecmaFeatures": {
+ "jsx": true
+ }
+ }
+}
+```
+
+Finally, enable all of the rules that you would like to use.
+
+```json
+{
+ "rules": {
+ "react/display-name": 1,
+ "react/forbid-prop-types": 1,
+ "react/jsx-boolean-value": 1,
+ "react/jsx-closing-bracket-location": 1,
+ "react/jsx-curly-spacing": 1,
+ "react/jsx-equals-spacing": 1,
+ "react/jsx-handler-names": 1,
+ "react/jsx-indent-props": 1,
+ "react/jsx-indent": 1,
+ "react/jsx-key": 1,
+ "react/jsx-max-props-per-line": 1,
+ "react/jsx-no-bind": 1,
+ "react/jsx-no-duplicate-props": 1,
+ "react/jsx-no-literals": 1,
+ "react/jsx-no-undef": 1,
+ "react/jsx-pascal-case": 1,
+ "react/jsx-quotes": 1,
+ "react/jsx-sort-prop-types": 1,
+ "react/jsx-sort-props": 1,
+ "react/jsx-uses-react": 1,
+ "react/jsx-uses-vars": 1,
+ "react/no-danger": 1,
+ "react/no-deprecated": 1,
+ "react/no-did-mount-set-state": 1,
+ "react/no-did-update-set-state": 1,
+ "react/no-direct-mutation-state": 1,
+ "react/no-is-mounted": 1,
+ "react/no-multi-comp": 1,
+ "react/no-set-state": 1,
+ "react/no-string-refs": 1,
+ "react/no-unknown-property": 1,
+ "react/prefer-es6-class": 1,
+ "react/prop-types": 1,
+ "react/react-in-jsx-scope": 1,
+ "react/require-extension": 1,
+ "react/self-closing-comp": 1,
+ "react/sort-comp": 1,
+ "react/wrap-multilines": 1
+ }
+}
+```
+
+# List of supported rules
+
+* [display-name](docs/rules/display-name.md): Prevent missing `displayName` in a React component definition
+* [forbid-prop-types](docs/rules/forbid-prop-types.md): Forbid certain propTypes
+* [jsx-boolean-value](docs/rules/jsx-boolean-value.md): Enforce boolean attributes notation in JSX (fixable)
+* [jsx-closing-bracket-location](docs/rules/jsx-closing-bracket-location.md): Validate closing bracket location in JSX
+* [jsx-curly-spacing](docs/rules/jsx-curly-spacing.md): Enforce or disallow spaces inside of curly braces in JSX attributes (fixable)
+* [jsx-equals-spacing](docs/rules/jsx-equals-spacing.md): Enforce or disallow spaces around equal signs in JSX attributes
+* [jsx-handler-names](docs/rules/jsx-handler-names.md): Enforce event handler naming conventions in JSX
+* [jsx-indent-props](docs/rules/jsx-indent-props.md): Validate props indentation in JSX
+* [jsx-indent](docs/rules/jsx-indent.md): Validate JSX indentation
+* [jsx-key](docs/rules/jsx-key.md): Validate JSX has key prop when in array or iterator
+* [jsx-max-props-per-line](docs/rules/jsx-max-props-per-line.md): Limit maximum of props on a single line in JSX
+* [jsx-no-bind](docs/rules/jsx-no-bind.md): Prevent usage of `.bind()` and arrow functions in JSX props
+* [jsx-no-duplicate-props](docs/rules/jsx-no-duplicate-props.md): Prevent duplicate props in JSX
+* [jsx-no-literals](docs/rules/jsx-no-literals.md): Prevent usage of unwrapped JSX strings
+* [jsx-no-undef](docs/rules/jsx-no-undef.md): Disallow undeclared variables in JSX
+* [jsx-pascal-case](docs/rules/jsx-pascal-case.md): Enforce PascalCase for user-defined JSX components
+* [jsx-quotes](docs/rules/jsx-quotes.md): Enforce quote style for JSX attributes
+* [jsx-sort-prop-types](docs/rules/jsx-sort-prop-types.md): Enforce propTypes declarations alphabetical sorting
+* [jsx-sort-props](docs/rules/jsx-sort-props.md): Enforce props alphabetical sorting
+* [jsx-uses-react](docs/rules/jsx-uses-react.md): Prevent React to be incorrectly marked as unused
+* [jsx-uses-vars](docs/rules/jsx-uses-vars.md): Prevent variables used in JSX to be incorrectly marked as unused
+* [no-danger](docs/rules/no-danger.md): Prevent usage of dangerous JSX properties
+* [no-deprecated](docs/rules/no-deprecated.md): Prevent usage of deprecated methods
+* [no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of `setState` in `componentDidMount`
+* [no-did-update-set-state](docs/rules/no-did-update-set-state.md): Prevent usage of `setState` in `componentDidUpdate`
+* [no-direct-mutation-state](docs/rules/no-direct-mutation-state.md): Prevent direct mutation of `this.state`
+* [no-is-mounted](docs/rules/no-is-mounted.md): Prevent usage of `isMounted`
+* [no-multi-comp](docs/rules/no-multi-comp.md): Prevent multiple component definition per file
+* [no-set-state](docs/rules/no-set-state.md): Prevent usage of `setState`
+* [no-string-refs](docs/rules/no-string-refs.md): Prevent using string references in `ref` attribute.
+* [no-unknown-property](docs/rules/no-unknown-property.md): Prevent usage of unknown DOM property (fixable)
+* [prefer-es6-class](docs/rules/prefer-es6-class.md): Enforce ES5 or ES6 class for React Components
+* [prop-types](docs/rules/prop-types.md): Prevent missing props validation in a React component definition
+* [react-in-jsx-scope](docs/rules/react-in-jsx-scope.md): Prevent missing `React` when using JSX
+* [require-extension](docs/rules/require-extension.md): Restrict file extensions that may be required
+* [self-closing-comp](docs/rules/self-closing-comp.md): Prevent extra closing tags for components without children
+* [sort-comp](docs/rules/sort-comp.md): Enforce component methods order
+* [wrap-multilines](docs/rules/wrap-multilines.md): Prevent missing parentheses around multilines JSX (fixable)
+
+## React Native
+
+If you're searching for React Native specific linting rules, check out [eslint-plugin-react-native](https://github.com/Intellicode/eslint-plugin-react-native).
+
+# License
+
+ESLint-plugin-React is licensed under the [MIT License](http://www.opensource.org/licenses/mit-license.php).
+
+
+[npm-url]: https://npmjs.org/package/eslint-plugin-react
+[npm-image]: https://img.shields.io/npm/v/eslint-plugin-react.svg
+
+[travis-url]: https://travis-ci.org/yannickcr/eslint-plugin-react
+[travis-image]: https://img.shields.io/travis/yannickcr/eslint-plugin-react/master.svg?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSItMTQyLjUgLTE0Mi41IDI4NSAyODUiPjxjaXJjbGUgcj0iMTQxLjciIGZpbGw9IiNERDQ4MTQiLz48ZyBpZD0iYSIgZmlsbD0iI0ZGRiI%2BPGNpcmNsZSBjeD0iLTk2LjQiIHI9IjE4LjkiLz48cGF0aCBkPSJNLTQ1LjYgNjguNGMtMTYuNi0xMS0yOS0yOC0zNC00Ny44IDYtNSA5LjgtMTIuMyA5LjgtMjAuNnMtMy44LTE1LjctOS44LTIwLjZjNS0xOS44IDE3LjQtMzYuNyAzNC00Ny44bDEzLjggMjMuMkMtNDYtMzUuMi01NS4zLTE4LjctNTUuMyAwYzAgMTguNyA5LjMgMzUuMiAyMy41IDQ1LjJ6Ii8%2BPC9nPjx1c2UgeGxpbms6aHJlZj0iI2EiIHRyYW5zZm9ybT0icm90YXRlKDEyMCkiLz48dXNlIHhsaW5rOmhyZWY9IiNhIiB0cmFuc2Zvcm09InJvdGF0ZSgyNDApIi8%2BPC9zdmc%2B
+
+[appveyor-url]: https://ci.appveyor.com/project/yannickcr/eslint-plugin-react
+[appveyor-image]: https://img.shields.io/appveyor/ci/yannickcr/eslint-plugin-react/master.svg?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjEyOCIgaGVpZ2h0PSIxMjgiIHZpZXdCb3g9IjAgMCAxMjggMTI4Ij48ZyBmaWxsPSIjMUJBMUUyIiB0cmFuc2Zvcm09InNjYWxlKDgpIj48cGF0aCBkPSJNMCAyLjI2NWw2LjUzOS0uODg4LjAwMyA2LjI4OC02LjUzNi4wMzd6Ii8%2BPHBhdGggZD0iTTYuNTM2IDguMzlsLjAwNSA2LjI5My02LjUzNi0uODk2di01LjQ0eiIvPjxwYXRoIGQ9Ik03LjMyOCAxLjI2MWw4LjY3LTEuMjYxdjcuNTg1bC04LjY3LjA2OXoiLz48cGF0aCBkPSJNMTYgOC40NDlsLS4wMDIgNy41NTEtOC42Ny0xLjIyLS4wMTItNi4zNDV6Ii8%2BPC9nPjwvc3ZnPg==
+
+[deps-url]: https://david-dm.org/yannickcr/eslint-plugin-react
+[deps-image]: https://img.shields.io/david/dev/yannickcr/eslint-plugin-react.svg
+
+[coverage-url]: https://coveralls.io/r/yannickcr/eslint-plugin-react?branch=master
+[coverage-image]: https://img.shields.io/coveralls/yannickcr/eslint-plugin-react/master.svg
+
+[climate-url]: https://codeclimate.com/github/yannickcr/eslint-plugin-react
+[climate-image]: https://img.shields.io/codeclimate/github/yannickcr/eslint-plugin-react.svg
+
+[status-url]: https://github.com/yannickcr/eslint-plugin-react/pulse
+[status-image]: https://img.shields.io/badge/status-maintained-brightgreen.svg
diff --git a/node_modules/eslint-plugin-react/index.js b/node_modules/eslint-plugin-react/index.js
new file mode 100644
index 0000000..ad33d96
--- /dev/null
+++ b/node_modules/eslint-plugin-react/index.js
@@ -0,0 +1,84 @@
+'use strict';
+
+module.exports = {
+ rules: {
+ 'jsx-uses-react': require('./lib/rules/jsx-uses-react'),
+ 'no-multi-comp': require('./lib/rules/no-multi-comp'),
+ 'prop-types': require('./lib/rules/prop-types'),
+ 'display-name': require('./lib/rules/display-name'),
+ 'wrap-multilines': require('./lib/rules/wrap-multilines'),
+ 'self-closing-comp': require('./lib/rules/self-closing-comp'),
+ 'no-danger': require('./lib/rules/no-danger'),
+ 'no-set-state': require('./lib/rules/no-set-state'),
+ 'no-is-mounted': require('./lib/rules/no-is-mounted'),
+ 'no-deprecated': require('./lib/rules/no-deprecated'),
+ 'no-did-mount-set-state': require('./lib/rules/no-did-mount-set-state'),
+ 'no-did-update-set-state': require('./lib/rules/no-did-update-set-state'),
+ 'react-in-jsx-scope': require('./lib/rules/react-in-jsx-scope'),
+ 'jsx-uses-vars': require('./lib/rules/jsx-uses-vars'),
+ 'jsx-handler-names': require('./lib/rules/jsx-handler-names'),
+ 'jsx-pascal-case': require('./lib/rules/jsx-pascal-case'),
+ 'jsx-no-bind': require('./lib/rules/jsx-no-bind'),
+ 'jsx-no-undef': require('./lib/rules/jsx-no-undef'),
+ 'jsx-quotes': require('./lib/rules/jsx-quotes'),
+ 'no-unknown-property': require('./lib/rules/no-unknown-property'),
+ 'jsx-curly-spacing': require('./lib/rules/jsx-curly-spacing'),
+ 'jsx-equals-spacing': require('./lib/rules/jsx-equals-spacing'),
+ 'jsx-sort-props': require('./lib/rules/jsx-sort-props'),
+ 'jsx-sort-prop-types': require('./lib/rules/jsx-sort-prop-types'),
+ 'jsx-boolean-value': require('./lib/rules/jsx-boolean-value'),
+ 'sort-comp': require('./lib/rules/sort-comp'),
+ 'require-extension': require('./lib/rules/require-extension'),
+ 'jsx-no-duplicate-props': require('./lib/rules/jsx-no-duplicate-props'),
+ 'jsx-max-props-per-line': require('./lib/rules/jsx-max-props-per-line'),
+ 'jsx-no-literals': require('./lib/rules/jsx-no-literals'),
+ 'jsx-indent-props': require('./lib/rules/jsx-indent-props'),
+ 'jsx-indent': require('./lib/rules/jsx-indent'),
+ 'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),
+ 'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state'),
+ 'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
+ 'prefer-es6-class': require('./lib/rules/prefer-es6-class'),
+ 'jsx-key': require('./lib/rules/jsx-key'),
+ 'no-string-refs': require('./lib/rules/no-string-refs')
+ },
+ rulesConfig: {
+ 'jsx-uses-react': 0,
+ 'no-multi-comp': 0,
+ 'prop-types': 0,
+ 'display-name': 0,
+ 'wrap-multilines': 0,
+ 'self-closing-comp': 0,
+ 'no-deprecated': 0,
+ 'no-danger': 0,
+ 'no-set-state': 0,
+ 'no-is-mounted': 0,
+ 'no-did-mount-set-state': 0,
+ 'no-did-update-set-state': 0,
+ 'react-in-jsx-scope': 0,
+ 'jsx-uses-vars': 1,
+ 'jsx-handler-names': 0,
+ 'jsx-pascal-case': 0,
+ 'jsx-no-bind': 0,
+ 'jsx-no-undef': 0,
+ 'jsx-quotes': 0,
+ 'no-unknown-property': 0,
+ 'jsx-curly-spacing': 0,
+ 'jsx-equals-spacing': 0,
+ 'jsx-sort-props': 0,
+ 'jsx-sort-prop-types': 0,
+ 'jsx-boolean-value': 0,
+ 'sort-comp': 0,
+ 'require-extension': 0,
+ 'jsx-no-duplicate-props': 0,
+ 'jsx-max-props-per-line': 0,
+ 'jsx-no-literals': 0,
+ 'jsx-indent-props': 0,
+ 'jsx-indent': 0,
+ 'jsx-closing-bracket-location': 0,
+ 'no-direct-mutation-state': 0,
+ 'forbid-prop-types': 0,
+ 'prefer-es6-class': 0,
+ 'jsx-key': 0,
+ 'no-string-refs': 0
+ }
+};
diff --git a/node_modules/eslint-plugin-react/lib/rules/display-name.js b/node_modules/eslint-plugin-react/lib/rules/display-name.js
new file mode 100644
index 0000000..728c843
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/display-name.js
@@ -0,0 +1,211 @@
+/**
+ * @fileoverview Prevent missing displayName in a React component definition
+ * @author Yannick Croissant
+ */
+'use strict';
+
+var Components = require('../util/Components');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = Components.detect(function(context, components, utils) {
+
+ var config = context.options[0] || {};
+ var acceptTranspilerName = config.acceptTranspilerName || false;
+
+ var MISSING_MESSAGE = 'Component definition is missing display name';
+
+ /**
+ * Checks if we are declaring a display name
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Boolean} True if we are declaring a display name, false if not.
+ */
+ function isDisplayNameDeclaration(node) {
+ // Special case for class properties
+ // (babel-eslint does not expose property name so we have to rely on tokens)
+ if (node.type === 'ClassProperty') {
+ var tokens = context.getFirstTokens(node, 2);
+ if (
+ tokens[0].value === 'displayName' ||
+ (tokens[1] && tokens[1].value === 'displayName')
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ return Boolean(
+ node &&
+ node.name === 'displayName'
+ );
+ }
+
+ /**
+ * Mark a prop type as declared
+ * @param {ASTNode} node The AST node being checked.
+ */
+ function markDisplayNameAsDeclared(node) {
+ components.set(node, {
+ hasDisplayName: true
+ });
+ }
+
+ /**
+ * Reports missing display name for a given component
+ * @param {Object} component The component to process
+ */
+ function reportMissingDisplayName(component) {
+ context.report(
+ component.node,
+ MISSING_MESSAGE, {
+ component: component.name
+ }
+ );
+ }
+
+ /**
+ * Checks if the component have a name set by the transpiler
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Boolean} True ifcomponent have a name, false if not.
+ */
+ function hasTranspilerName(node) {
+ var namedObjectAssignment = (
+ node.type === 'ObjectExpression' &&
+ node.parent &&
+ node.parent.parent &&
+ node.parent.parent.type === 'AssignmentExpression' && (
+ !node.parent.parent.left.object ||
+ node.parent.parent.left.object.name !== 'module' ||
+ node.parent.parent.left.property.name !== 'exports'
+ )
+ );
+ var namedObjectDeclaration = (
+ node.type === 'ObjectExpression' &&
+ node.parent &&
+ node.parent.parent &&
+ node.parent.parent.type === 'VariableDeclarator'
+ );
+ var namedClass = (
+ node.type === 'ClassDeclaration' &&
+ node.id && node.id.name
+ );
+
+ var namedFunctionDeclaration = (
+ (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') &&
+ node.id &&
+ node.id.name
+ );
+
+ var namedFunctionExpression = (
+ (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') &&
+ node.parent &&
+ (node.parent.type === 'VariableDeclarator' || node.parent.method === true)
+ );
+
+ if (
+ namedObjectAssignment || namedObjectDeclaration ||
+ namedClass ||
+ namedFunctionDeclaration || namedFunctionExpression
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+
+ ClassProperty: function(node) {
+ if (!isDisplayNameDeclaration(node)) {
+ return;
+ }
+ markDisplayNameAsDeclared(node);
+ },
+
+ MemberExpression: function(node) {
+ if (!isDisplayNameDeclaration(node.property)) {
+ return;
+ }
+ var component = utils.getRelatedComponent(node);
+ if (!component) {
+ return;
+ }
+ markDisplayNameAsDeclared(component.node);
+ },
+
+ FunctionExpression: function(node) {
+ if (!acceptTranspilerName || !hasTranspilerName(node)) {
+ return;
+ }
+ markDisplayNameAsDeclared(node);
+ },
+
+ FunctionDeclaration: function(node) {
+ if (!acceptTranspilerName || !hasTranspilerName(node)) {
+ return;
+ }
+ markDisplayNameAsDeclared(node);
+ },
+
+ ArrowFunctionExpression: function(node) {
+ if (!acceptTranspilerName || !hasTranspilerName(node)) {
+ return;
+ }
+ markDisplayNameAsDeclared(node);
+ },
+
+ MethodDefinition: function(node) {
+ if (!isDisplayNameDeclaration(node.key)) {
+ return;
+ }
+ markDisplayNameAsDeclared(node);
+ },
+
+ ClassDeclaration: function(node) {
+ if (!acceptTranspilerName || !hasTranspilerName(node)) {
+ return;
+ }
+ markDisplayNameAsDeclared(node);
+ },
+
+ ObjectExpression: function(node) {
+ if (!acceptTranspilerName || !hasTranspilerName(node)) {
+ // Search for the displayName declaration
+ node.properties.forEach(function(property) {
+ if (!property.key || !isDisplayNameDeclaration(property.key)) {
+ return;
+ }
+ markDisplayNameAsDeclared(node);
+ });
+ return;
+ }
+ markDisplayNameAsDeclared(node);
+ },
+
+ 'Program:exit': function() {
+ var list = components.list();
+ // Report missing display name for all components
+ for (var component in list) {
+ if (!list.hasOwnProperty(component) || list[component].hasDisplayName) {
+ continue;
+ }
+ reportMissingDisplayName(list[component]);
+ }
+ }
+ };
+});
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ acceptTranspilerName: {
+ type: 'boolean'
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/forbid-prop-types.js b/node_modules/eslint-plugin-react/lib/rules/forbid-prop-types.js
new file mode 100644
index 0000000..bebce09
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/forbid-prop-types.js
@@ -0,0 +1,128 @@
+/**
+ * @fileoverview Forbid certain propTypes
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+var DEFAULTS = ['any', 'array', 'object'];
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ function isForbidden(type) {
+ var configuration = context.options[0] || {};
+
+ var forbid = configuration.forbid || DEFAULTS;
+ return forbid.indexOf(type) >= 0;
+ }
+
+ /**
+ * Checks if node is `propTypes` declaration
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Boolean} True if node is `propTypes` declaration, false if not.
+ */
+ function isPropTypesDeclaration(node) {
+
+ // Special case for class properties
+ // (babel-eslint does not expose property name so we have to rely on tokens)
+ if (node.type === 'ClassProperty') {
+ var tokens = context.getFirstTokens(node, 2);
+ if (tokens[0].value === 'propTypes' || (tokens[1] && tokens[1].value === 'propTypes')) {
+ return true;
+ }
+ return false;
+ }
+
+ return Boolean(
+ node &&
+ node.name === 'propTypes'
+ );
+ }
+
+
+ /**
+ * Checks if propTypes declarations are forbidden
+ * @param {Array} declarations The array of AST nodes being checked.
+ * @returns {void}
+ */
+ function checkForbidden(declarations) {
+ declarations.forEach(function(declaration) {
+ var target;
+ var value = declaration.value;
+ if (
+ value.type === 'MemberExpression' &&
+ value.property &&
+ value.property.name &&
+ value.property.name === 'isRequired'
+ ) {
+ value = value.object;
+ }
+ if (
+ value.type === 'CallExpression' &&
+ value.callee.type === 'MemberExpression'
+ ) {
+ value = value.callee;
+ }
+ if (value.property) {
+ target = value.property.name;
+ } else if (value.type === 'Identifier') {
+ target = value.name;
+ }
+ if (isForbidden(target)) {
+ context.report(declaration, 'Prop type `' + target + '` is forbidden');
+ }
+ });
+ }
+
+ return {
+ ClassProperty: function(node) {
+ if (isPropTypesDeclaration(node) && node.value && node.value.type === 'ObjectExpression') {
+ checkForbidden(node.value.properties);
+ }
+ },
+
+ MemberExpression: function(node) {
+ if (isPropTypesDeclaration(node.property)) {
+ var right = node.parent.right;
+ if (right && right.type === 'ObjectExpression') {
+ checkForbidden(right.properties);
+ }
+ }
+ },
+
+ ObjectExpression: function(node) {
+ node.properties.forEach(function(property) {
+ if (!property.key) {
+ return;
+ }
+
+ if (!isPropTypesDeclaration(property.key)) {
+ return;
+ }
+ if (property.value.type === 'ObjectExpression') {
+ checkForbidden(property.value.properties);
+ }
+ });
+ }
+
+ };
+};
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ forbid: {
+ type: 'array',
+ items: {
+ type: 'string'
+ }
+ }
+ },
+ additionalProperties: true
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-boolean-value.js b/node_modules/eslint-plugin-react/lib/rules/jsx-boolean-value.js
new file mode 100644
index 0000000..e4169e0
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-boolean-value.js
@@ -0,0 +1,52 @@
+/**
+ * @fileoverview Enforce boolean attributes notation in JSX
+ * @author Yannick Croissant
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ var configuration = context.options[0] || 'never';
+
+ var NEVER_MESSAGE = 'Value must be omitted for boolean attributes';
+ var ALWAYS_MESSAGE = 'Value must be set for boolean attributes';
+
+ return {
+ JSXAttribute: function(node) {
+ switch (configuration) {
+ case 'always':
+ if (node.value === null) {
+ context.report({
+ node: node,
+ message: ALWAYS_MESSAGE,
+ fix: function(fixer) {
+ return fixer.insertTextAfter(node, '={true}');
+ }
+ });
+ }
+ break;
+ case 'never':
+ if (node.value && node.value.type === 'JSXExpressionContainer' && node.value.expression.value === true) {
+ context.report({
+ node: node,
+ message: NEVER_MESSAGE,
+ fix: function(fixer) {
+ return fixer.removeRange([node.name.range[1], node.value.range[1]]);
+ }
+ });
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ };
+};
+
+module.exports.schema = [{
+ enum: ['always', 'never']
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-closing-bracket-location.js b/node_modules/eslint-plugin-react/lib/rules/jsx-closing-bracket-location.js
new file mode 100644
index 0000000..f6202af
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-closing-bracket-location.js
@@ -0,0 +1,199 @@
+/**
+ * @fileoverview Validate closing bracket location in JSX
+ * @author Yannick Croissant
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+module.exports = function(context) {
+
+ var MESSAGE = 'The closing bracket must be {{location}}{{details}}';
+ var MESSAGE_LOCATION = {
+ 'after-props': 'placed after the last prop',
+ 'after-tag': 'placed after the opening tag',
+ 'props-aligned': 'aligned with the last prop',
+ 'tag-aligned': 'aligned with the opening tag',
+ 'line-aligned': 'aligned with the line containing the opening tag'
+ };
+ var DEFAULT_LOCATION = 'tag-aligned';
+
+ var config = context.options[0];
+ var options = {
+ nonEmpty: DEFAULT_LOCATION,
+ selfClosing: DEFAULT_LOCATION
+ };
+
+ if (typeof config === 'string') {
+ // simple shorthand [1, 'something']
+ options.nonEmpty = config;
+ options.selfClosing = config;
+ } else if (typeof config === 'object') {
+ // [1, {location: 'something'}] (back-compat)
+ if (config.hasOwnProperty('location')) {
+ options.nonEmpty = config.location;
+ options.selfClosing = config.location;
+ }
+ // [1, {nonEmpty: 'something'}]
+ if (config.hasOwnProperty('nonEmpty')) {
+ options.nonEmpty = config.nonEmpty;
+ }
+ // [1, {selfClosing: 'something'}]
+ if (config.hasOwnProperty('selfClosing')) {
+ options.selfClosing = config.selfClosing;
+ }
+ }
+
+ /**
+ * Get expected location for the closing bracket
+ * @param {Object} tokens Locations of the opening bracket, closing bracket and last prop
+ * @return {String} Expected location for the closing bracket
+ */
+ function getExpectedLocation(tokens) {
+ var location;
+ // Is always after the opening tag if there is no props
+ if (typeof tokens.lastProp === 'undefined') {
+ location = 'after-tag';
+ // Is always after the last prop if this one is on the same line as the opening bracket
+ } else if (tokens.opening.line === tokens.lastProp.line) {
+ location = 'after-props';
+ // Else use configuration dependent on selfClosing property
+ } else {
+ location = tokens.selfClosing ? options.selfClosing : options.nonEmpty;
+ }
+ return location;
+ }
+
+ /**
+ * Get the correct 0-indexed column for the closing bracket, given the
+ * expected location.
+ * @param {Object} tokens Locations of the opening bracket, closing bracket and last prop
+ * @param {String} expectedLocation Expected location for the closing bracket
+ * @return {?Number} The correct column for the closing bracket, or null
+ */
+ function getCorrectColumn(tokens, expectedLocation) {
+ switch (expectedLocation) {
+ case 'props-aligned':
+ return tokens.lastProp.column;
+ case 'tag-aligned':
+ return tokens.opening.column;
+ case 'line-aligned':
+ return tokens.openingStartOfLine.column;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Check if the closing bracket is correctly located
+ * @param {Object} tokens Locations of the opening bracket, closing bracket and last prop
+ * @param {String} expectedLocation Expected location for the closing bracket
+ * @return {Boolean} True if the closing bracket is correctly located, false if not
+ */
+ function hasCorrectLocation(tokens, expectedLocation) {
+ switch (expectedLocation) {
+ case 'after-tag':
+ return tokens.tag.line === tokens.closing.line;
+ case 'after-props':
+ return tokens.lastProp.line === tokens.closing.line;
+ case 'props-aligned':
+ case 'tag-aligned':
+ case 'line-aligned':
+ var correctColumn = getCorrectColumn(tokens, expectedLocation);
+ return correctColumn === tokens.closing.column;
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Get the locations of the opening bracket, closing bracket, last prop, and
+ * start of opening line.
+ * @param {ASTNode} node The node to check
+ * @return {Object} Locations of the opening bracket, closing bracket, last
+ * prop and start of opening line.
+ */
+ function getTokensLocations(node) {
+ var opening = context.getFirstToken(node).loc.start;
+ var closing = context.getLastTokens(node, node.selfClosing ? 2 : 1)[0].loc.start;
+ var tag = context.getFirstToken(node.name).loc.start;
+ var lastProp;
+ if (node.attributes.length) {
+ lastProp = node.attributes[node.attributes.length - 1];
+ lastProp = {
+ column: context.getFirstToken(lastProp).loc.start.column,
+ line: context.getLastToken(lastProp).loc.end.line
+ };
+ }
+ var openingLine = context.getSourceCode().lines[opening.line - 1];
+ var openingStartOfLine = {
+ column: /^\s*/.exec(openingLine)[0].length,
+ line: opening.line
+ };
+ return {
+ tag: tag,
+ opening: opening,
+ closing: closing,
+ lastProp: lastProp,
+ selfClosing: node.selfClosing,
+ openingStartOfLine: openingStartOfLine
+ };
+ }
+
+ return {
+ JSXOpeningElement: function(node) {
+ var tokens = getTokensLocations(node);
+ var expectedLocation = getExpectedLocation(tokens);
+ if (hasCorrectLocation(tokens, expectedLocation)) {
+ return;
+ }
+
+ var data = {location: MESSAGE_LOCATION[expectedLocation], details: ''};
+ var correctColumn = getCorrectColumn(tokens, expectedLocation);
+
+ if (correctColumn !== null) {
+ var expectedNextLine = tokens.lastProp &&
+ (tokens.lastProp.line === tokens.closing.line);
+ data.details = ' (expected column ' + (correctColumn + 1) +
+ (expectedNextLine ? ' on the next line)' : ')');
+ }
+
+ context.report({
+ node: node,
+ loc: tokens.closing,
+ message: MESSAGE,
+ data: data
+ });
+ }
+ };
+
+};
+
+module.exports.schema = [{
+ oneOf: [
+ {
+ enum: ['after-props', 'props-aligned', 'tag-aligned', 'line-aligned']
+ },
+ {
+ type: 'object',
+ properties: {
+ location: {
+ enum: ['after-props', 'props-aligned', 'tag-aligned', 'line-aligned']
+ }
+ },
+ additionalProperties: false
+ }, {
+ type: 'object',
+ properties: {
+ nonEmpty: {
+ enum: ['after-props', 'props-aligned', 'tag-aligned', 'line-aligned', false]
+ },
+ selfClosing: {
+ enum: ['after-props', 'props-aligned', 'tag-aligned', 'line-aligned', false]
+ }
+ },
+ additionalProperties: false
+ }
+ ]
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-curly-spacing.js b/node_modules/eslint-plugin-react/lib/rules/jsx-curly-spacing.js
new file mode 100644
index 0000000..8dbc78d
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-curly-spacing.js
@@ -0,0 +1,217 @@
+/**
+ * @fileoverview Enforce or disallow spaces inside of curly braces in JSX attributes.
+ * @author Jamund Ferguson
+ * @author Brandyn Bennett
+ * @author Michael Ficarra
+ * @author Vignesh Anand
+ * @author Jamund Ferguson
+ * @author Yannick Croissant
+ * @author Erik Wendel
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+ var spaced = context.options[0] === 'always';
+ var multiline = context.options[1] ? context.options[1].allowMultiline : true;
+
+ // --------------------------------------------------------------------------
+ // Helpers
+ // --------------------------------------------------------------------------
+
+ /**
+ * Determines whether two adjacent tokens have a newline between them.
+ * @param {Object} left - The left token object.
+ * @param {Object} right - The right token object.
+ * @returns {boolean} Whether or not there is a newline between the tokens.
+ */
+ function isMultiline(left, right) {
+ return left.loc.start.line !== right.loc.start.line;
+ }
+
+ /**
+ * Determines whether two adjacent tokens have whitespace between them.
+ * @param {Object} left - The left token object.
+ * @param {Object} right - The right token object.
+ * @returns {boolean} Whether or not there is space between the tokens.
+ */
+ function isSpaced(left, right) {
+ return left.range[1] < right.range[0];
+ }
+
+ /**
+ * Reports that there shouldn't be a newline after the first token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportNoBeginningNewline(node, token) {
+ context.report({
+ node: node,
+ loc: token.loc.start,
+ message: 'There should be no newline after \'' + token.value + '\'',
+ fix: function(fixer) {
+ var nextToken = context.getSourceCode().getTokenAfter(token);
+ return fixer.replaceTextRange([token.range[1], nextToken.range[0]], spaced ? ' ' : '');
+ }
+ });
+ }
+
+ /**
+ * Reports that there shouldn't be a newline before the last token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportNoEndingNewline(node, token) {
+ context.report({
+ node: node,
+ loc: token.loc.start,
+ message: 'There should be no newline before \'' + token.value + '\'',
+ fix: function(fixer) {
+ var previousToken = context.getSourceCode().getTokenBefore(token);
+ return fixer.replaceTextRange([previousToken.range[1], token.range[0]], spaced ? ' ' : '');
+ }
+ });
+ }
+
+ /**
+ * Reports that there shouldn't be a space after the first token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportNoBeginningSpace(node, token) {
+ context.report({
+ node: node,
+ loc: token.loc.start,
+ message: 'There should be no space after \'' + token.value + '\'',
+ fix: function(fixer) {
+ var nextToken = context.getSourceCode().getTokenAfter(token);
+ return fixer.removeRange([token.range[1], nextToken.range[0]]);
+ }
+ });
+ }
+
+ /**
+ * Reports that there shouldn't be a space before the last token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportNoEndingSpace(node, token) {
+ context.report({
+ node: node,
+ loc: token.loc.start,
+ message: 'There should be no space before \'' + token.value + '\'',
+ fix: function(fixer) {
+ var previousToken = context.getSourceCode().getTokenBefore(token);
+ return fixer.removeRange([previousToken.range[1], token.range[0]]);
+ }
+ });
+ }
+
+ /**
+ * Reports that there should be a space after the first token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportRequiredBeginningSpace(node, token) {
+ context.report({
+ node: node,
+ loc: token.loc.start,
+ message: 'A space is required after \'' + token.value + '\'',
+ fix: function(fixer) {
+ return fixer.insertTextAfter(token, ' ');
+ }
+ });
+ }
+
+ /**
+ * Reports that there should be a space before the last token
+ * @param {ASTNode} node - The node to report in the event of an error.
+ * @param {Token} token - The token to use for the report.
+ * @returns {void}
+ */
+ function reportRequiredEndingSpace(node, token) {
+ context.report({
+ node: node,
+ loc: token.loc.start,
+ message: 'A space is required before \'' + token.value + '\'',
+ fix: function(fixer) {
+ return fixer.insertTextBefore(token, ' ');
+ }
+ });
+ }
+
+ /**
+ * Determines if spacing in curly braces is valid.
+ * @param {ASTNode} node The AST node to check.
+ * @param {Token} first The first token to check (should be the opening brace)
+ * @param {Token} second The second token to check (should be first after the opening brace)
+ * @param {Token} penultimate The penultimate token to check (should be last before closing brace)
+ * @param {Token} last The last token to check (should be closing brace)
+ * @returns {void}
+ */
+ function validateBraceSpacing(node, first, second, penultimate, last) {
+ if (spaced) {
+ if (!isSpaced(first, second)) {
+ reportRequiredBeginningSpace(node, first);
+ } else if (!multiline && isMultiline(first, second)) {
+ reportNoBeginningNewline(node, first);
+ }
+
+ if (!isSpaced(penultimate, last)) {
+ reportRequiredEndingSpace(node, last);
+ } else if (!multiline && isMultiline(penultimate, last)) {
+ reportNoEndingNewline(node, last);
+ }
+
+ return;
+ }
+
+ // "never" setting if we get here.
+ if (isSpaced(first, second) && !(multiline && isMultiline(first, second))) {
+ reportNoBeginningSpace(node, first);
+ }
+
+ if (isSpaced(penultimate, last) && !(multiline && isMultiline(penultimate, last))) {
+ reportNoEndingSpace(node, last);
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+ JSXExpressionContainer: function(node) {
+ var first = context.getFirstToken(node);
+ var second = context.getFirstToken(node, 1);
+ var penultimate = context.getLastToken(node, 1);
+ var last = context.getLastToken(node);
+
+ if (first === penultimate && second === last) {
+ return;
+ }
+
+ validateBraceSpacing(node, first, second, penultimate, last);
+ }
+ };
+};
+
+module.exports.schema = [{
+ enum: ['always', 'never']
+}, {
+ type: 'object',
+ properties: {
+ allowMultiline: {
+ type: 'boolean'
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-equals-spacing.js b/node_modules/eslint-plugin-react/lib/rules/jsx-equals-spacing.js
new file mode 100644
index 0000000..7773c10
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-equals-spacing.js
@@ -0,0 +1,69 @@
+/**
+ * @fileoverview Disallow or enforce spaces around equal signs in JSX attributes.
+ * @author ryym
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+ var config = context.options[0];
+ var sourceCode = context.getSourceCode();
+
+ /**
+ * Determines a given attribute node has an equal sign.
+ * @param {ASTNode} attrNode - The attribute node.
+ * @returns {boolean} Whether or not the attriute node has an equal sign.
+ */
+ function hasEqual(attrNode) {
+ return attrNode.type !== 'JSXSpreadAttribute' && attrNode.value !== null;
+ }
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+ JSXOpeningElement: function(node) {
+ node.attributes.forEach(function(attrNode) {
+ if (!hasEqual(attrNode)) {
+ return;
+ }
+
+ var equalToken = sourceCode.getTokenAfter(attrNode.name);
+ var spacedBefore = sourceCode.isSpaceBetweenTokens(attrNode.name, equalToken);
+ var spacedAfter = sourceCode.isSpaceBetweenTokens(equalToken, attrNode.value);
+
+ switch (config) {
+ default:
+ case 'never':
+ if (spacedBefore) {
+ context.report(attrNode, equalToken.loc.start,
+ 'There should be no space before \'=\'');
+ }
+ if (spacedAfter) {
+ context.report(attrNode, equalToken.loc.start,
+ 'There should be no space after \'=\'');
+ }
+ break;
+ case 'always':
+ if (!spacedBefore) {
+ context.report(attrNode, equalToken.loc.start,
+ 'A space is required before \'=\'');
+ }
+ if (!spacedAfter) {
+ context.report(attrNode, equalToken.loc.start,
+ 'A space is required after \'=\'');
+ }
+ break;
+ }
+ });
+ }
+ };
+};
+
+module.exports.schema = [{
+ enum: ['always', 'never']
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-handler-names.js b/node_modules/eslint-plugin-react/lib/rules/jsx-handler-names.js
new file mode 100644
index 0000000..2d2861e
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-handler-names.js
@@ -0,0 +1,64 @@
+/**
+ * @fileoverview Enforce event handler naming conventions in JSX
+ * @author Jake Marsh
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ var configuration = context.options[0] || {};
+ var eventHandlerPrefix = configuration.eventHandlerPrefix || 'handle';
+ var eventHandlerPropPrefix = configuration.eventHandlerPropPrefix || 'on';
+
+ var EVENT_HANDLER_REGEX = new RegExp('^((props\.' + eventHandlerPropPrefix + ')'
+ + '|((.*\.)?' + eventHandlerPrefix + ')).+$');
+ var PROP_EVENT_HANDLER_REGEX = new RegExp('^(' + eventHandlerPropPrefix + '.+|ref)$');
+
+ return {
+ JSXAttribute: function(node) {
+ if (!node.value || !node.value.expression || !node.value.expression.object) {
+ return;
+ }
+
+ var propKey = typeof node.name === 'object' ? node.name.name : node.name;
+ var propValue = context.getSource(node.value.expression).replace(/^this\./, '');
+
+ if (propKey === 'ref') {
+ return;
+ }
+
+ var propIsEventHandler = PROP_EVENT_HANDLER_REGEX.test(propKey);
+ var propFnIsNamedCorrectly = EVENT_HANDLER_REGEX.test(propValue);
+
+ if (propIsEventHandler && !propFnIsNamedCorrectly) {
+ context.report(
+ node,
+ 'Handler function for ' + propKey + ' prop key must begin with \'' + eventHandlerPrefix + '\''
+ );
+ } else if (propFnIsNamedCorrectly && !propIsEventHandler) {
+ context.report(
+ node,
+ 'Prop key for ' + propValue + ' must begin with \'' + eventHandlerPropPrefix + '\''
+ );
+ }
+ }
+ };
+
+};
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ eventHandlerPrefix: {
+ type: 'string'
+ },
+ eventHandlerPropPrefix: {
+ type: 'string'
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-indent-props.js b/node_modules/eslint-plugin-react/lib/rules/jsx-indent-props.js
new file mode 100644
index 0000000..4ea259f
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-indent-props.js
@@ -0,0 +1,154 @@
+/**
+ * @fileoverview Validate props indentation in JSX
+ * @author Yannick Croissant
+
+ * This rule has been ported and modified from eslint and nodeca.
+ * @author Vitaly Puzrin
+ * @author Gyandeep Singh
+ * @copyright 2015 Vitaly Puzrin. All rights reserved.
+ * @copyright 2015 Gyandeep Singh. All rights reserved.
+ Copyright (C) 2014 by Vitaly Puzrin
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the 'Software'), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+module.exports = function(context) {
+
+ var MESSAGE = 'Expected indentation of {{needed}} {{type}} {{characters}} but found {{gotten}}.';
+
+ var extraColumnStart = 0;
+ var indentType = 'space';
+ var indentSize = 4;
+
+ if (context.options.length) {
+ if (context.options[0] === 'tab') {
+ indentSize = 1;
+ indentType = 'tab';
+ } else if (typeof context.options[0] === 'number') {
+ indentSize = context.options[0];
+ indentType = 'space';
+ }
+ }
+
+ /**
+ * Reports a given indent violation and properly pluralizes the message
+ * @param {ASTNode} node Node violating the indent rule
+ * @param {Number} needed Expected indentation character count
+ * @param {Number} gotten Indentation character count in the actual node/code
+ * @param {Object=} loc Error line and column location
+ */
+ function report(node, needed, gotten, loc) {
+ var msgContext = {
+ needed: needed,
+ type: indentType,
+ characters: needed === 1 ? 'character' : 'characters',
+ gotten: gotten
+ };
+
+ if (loc) {
+ context.report(node, loc, MESSAGE, msgContext);
+ } else {
+ context.report(node, MESSAGE, msgContext);
+ }
+ }
+
+ /**
+ * Get node indent
+ * @param {ASTNode} node Node to examine
+ * @param {Boolean} byLastLine get indent of node's last line
+ * @param {Boolean} excludeCommas skip comma on start of line
+ * @return {Number} Indent
+ */
+ function getNodeIndent(node, byLastLine, excludeCommas) {
+ byLastLine = byLastLine || false;
+ excludeCommas = excludeCommas || false;
+
+ var src = context.getSource(node, node.loc.start.column + extraColumnStart);
+ var lines = src.split('\n');
+ if (byLastLine) {
+ src = lines[lines.length - 1];
+ } else {
+ src = lines[0];
+ }
+
+ var skip = excludeCommas ? ',' : '';
+
+ var regExp;
+ if (indentType === 'space') {
+ regExp = new RegExp('^[ ' + skip + ']+');
+ } else {
+ regExp = new RegExp('^[\t' + skip + ']+');
+ }
+
+ var indent = regExp.exec(src);
+ return indent ? indent[0].length : 0;
+ }
+
+ /**
+ * Checks node is the first in its own start line. By default it looks by start line.
+ * @param {ASTNode} node The node to check
+ * @param {Boolean} [byEndLocation] Lookup based on start position or end
+ * @return {Boolean} true if its the first in the its start line
+ */
+ function isNodeFirstInLine(node, byEndLocation) {
+ var firstToken = byEndLocation === true ? context.getLastToken(node, 1) : context.getTokenBefore(node);
+ var startLine = byEndLocation === true ? node.loc.end.line : node.loc.start.line;
+ var endLine = firstToken ? firstToken.loc.end.line : -1;
+
+ return startLine !== endLine;
+ }
+
+ /**
+ * Check indent for nodes list
+ * @param {ASTNode[]} nodes list of node objects
+ * @param {Number} indent needed indent
+ * @param {Boolean} excludeCommas skip comma on start of line
+ */
+ function checkNodesIndent(nodes, indent, excludeCommas) {
+ nodes.forEach(function(node) {
+ var nodeIndent = getNodeIndent(node, false, excludeCommas);
+ if (
+ node.type !== 'ArrayExpression' && node.type !== 'ObjectExpression' &&
+ nodeIndent !== indent && isNodeFirstInLine(node)
+ ) {
+ report(node, indent, nodeIndent);
+ }
+ });
+ }
+
+ return {
+ JSXOpeningElement: function(node) {
+ var elementIndent = getNodeIndent(node);
+ checkNodesIndent(node.attributes, elementIndent + indentSize);
+ }
+ };
+
+};
+
+module.exports.schema = [{
+ oneOf: [{
+ enum: ['tab']
+ }, {
+ type: 'integer'
+ }]
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-indent.js b/node_modules/eslint-plugin-react/lib/rules/jsx-indent.js
new file mode 100644
index 0000000..4e8adb3
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-indent.js
@@ -0,0 +1,162 @@
+/**
+ * @fileoverview Validate props indentation in JSX
+ * @author Yannick Croissant
+
+ * This rule has been ported and modified from eslint and nodeca.
+ * @author Vitaly Puzrin
+ * @author Gyandeep Singh
+ * @copyright 2015 Vitaly Puzrin. All rights reserved.
+ * @copyright 2015 Gyandeep Singh. All rights reserved.
+ Copyright (C) 2014 by Vitaly Puzrin
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the 'Software'), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+module.exports = function(context) {
+
+ var MESSAGE = 'Expected indentation of {{needed}} {{type}} {{characters}} but found {{gotten}}.';
+
+ var extraColumnStart = 0;
+ var indentType = 'space';
+ var indentSize = 4;
+
+ if (context.options.length) {
+ if (context.options[0] === 'tab') {
+ indentSize = 1;
+ indentType = 'tab';
+ } else if (typeof context.options[0] === 'number') {
+ indentSize = context.options[0];
+ indentType = 'space';
+ }
+ }
+
+ /**
+ * Reports a given indent violation and properly pluralizes the message
+ * @param {ASTNode} node Node violating the indent rule
+ * @param {Number} needed Expected indentation character count
+ * @param {Number} gotten Indentation character count in the actual node/code
+ * @param {Object=} loc Error line and column location
+ */
+ function report(node, needed, gotten, loc) {
+ var msgContext = {
+ needed: needed,
+ type: indentType,
+ characters: needed === 1 ? 'character' : 'characters',
+ gotten: gotten
+ };
+
+ if (loc) {
+ context.report(node, loc, MESSAGE, msgContext);
+ } else {
+ context.report(node, MESSAGE, msgContext);
+ }
+ }
+
+ /**
+ * Get node indent
+ * @param {ASTNode} node Node to examine
+ * @param {Boolean} byLastLine get indent of node's last line
+ * @param {Boolean} excludeCommas skip comma on start of line
+ * @return {Number} Indent
+ */
+ function getNodeIndent(node, byLastLine, excludeCommas) {
+ byLastLine = byLastLine || false;
+ excludeCommas = excludeCommas || false;
+
+ var src = context.getSource(node, node.loc.start.column + extraColumnStart);
+ var lines = src.split('\n');
+ if (byLastLine) {
+ src = lines[lines.length - 1];
+ } else {
+ src = lines[0];
+ }
+
+ var skip = excludeCommas ? ',' : '';
+
+ var regExp;
+ if (indentType === 'space') {
+ regExp = new RegExp('^[ ' + skip + ']+');
+ } else {
+ regExp = new RegExp('^[\t' + skip + ']+');
+ }
+
+ var indent = regExp.exec(src);
+ return indent ? indent[0].length : 0;
+ }
+
+ /**
+ * Checks node is the first in its own start line. By default it looks by start line.
+ * @param {ASTNode} node The node to check
+ * @return {Boolean} true if its the first in the its start line
+ */
+ function isNodeFirstInLine(node) {
+ var token = node;
+ do {
+ token = context.getTokenBefore(token);
+ } while (token.type === 'JSXText');
+ var startLine = node.loc.start.line;
+ var endLine = token ? token.loc.end.line : -1;
+
+ return startLine !== endLine;
+ }
+
+ /**
+ * Check indent for nodes list
+ * @param {ASTNode[]} nodes list of node objects
+ * @param {Number} indent needed indent
+ * @param {Boolean} excludeCommas skip comma on start of line
+ */
+ function checkNodesIndent(node, indent, excludeCommas) {
+ var nodeIndent = getNodeIndent(node, false, excludeCommas);
+ if (nodeIndent !== indent && isNodeFirstInLine(node)) {
+ report(node, indent, nodeIndent);
+ }
+ }
+
+ return {
+ JSXOpeningElement: function(node) {
+ if (!node.parent || !node.parent.parent) {
+ return;
+ }
+ var parentElementIndent = getNodeIndent(node.parent.parent);
+ var indent = node.parent.parent.loc.start.line === node.loc.start.line ? 0 : indentSize;
+ checkNodesIndent(node, parentElementIndent + indent);
+ },
+ JSXClosingElement: function(node) {
+ if (!node.parent) {
+ return;
+ }
+ var peerElementIndent = getNodeIndent(node.parent.openingElement);
+ checkNodesIndent(node, peerElementIndent);
+ }
+ };
+
+};
+
+module.exports.schema = [{
+ oneOf: [{
+ enum: ['tab']
+ }, {
+ type: 'integer'
+ }]
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-key.js b/node_modules/eslint-plugin-react/lib/rules/jsx-key.js
new file mode 100644
index 0000000..11f4cd2
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-key.js
@@ -0,0 +1,73 @@
+/**
+ * @fileoverview Report missing `key` props in iterators/collection literals.
+ * @author Ben Mosher
+ */
+'use strict';
+
+// var Components = require('../util/Components');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ function hasKeyProp(node) {
+ return node.openingElement.attributes.some(function(decl) {
+ if (decl.type === 'JSXSpreadAttribute') {
+ return false;
+ }
+ return (decl.name.name === 'key');
+ });
+ }
+
+ function checkIteratorElement(node) {
+ if (node.type === 'JSXElement' && !hasKeyProp(node)) {
+ context.report(node, 'Missing "key" prop for element in iterator');
+ }
+ }
+
+ function getReturnStatement(body) {
+ return body.filter(function(item) {
+ return item.type === 'ReturnStatement';
+ })[0];
+ }
+
+ return {
+ JSXElement: function(node) {
+ if (hasKeyProp(node)) {
+ return;
+ }
+
+ if (node.parent.type === 'ArrayExpression') {
+ context.report(node, 'Missing "key" prop for element in array');
+ }
+ },
+
+ // Array.prototype.map
+ CallExpression: function (node) {
+ if (node.callee && node.callee.property && node.callee.property.name !== 'map') {
+ return;
+ }
+
+ var fn = node.arguments[0];
+ var isFn = fn && fn.type === 'FunctionExpression';
+ var isArrFn = fn && fn.type === 'ArrowFunctionExpression';
+
+ if (isArrFn && fn.body.type === 'JSXElement') {
+ checkIteratorElement(fn.body);
+ }
+
+ if (isFn || isArrFn) {
+ if (fn.body.type === 'BlockStatement') {
+ var returnStatement = getReturnStatement(fn.body.body);
+ if (returnStatement && returnStatement.argument) {
+ checkIteratorElement(returnStatement.argument);
+ }
+ }
+ }
+ }
+ };
+};
+
+module.exports.schema = [];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-max-props-per-line.js b/node_modules/eslint-plugin-react/lib/rules/jsx-max-props-per-line.js
new file mode 100644
index 0000000..817974f
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-max-props-per-line.js
@@ -0,0 +1,59 @@
+/**
+ * @fileoverview Limit maximum of props on a single line in JSX
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function (context) {
+
+ var configuration = context.options[0] || {};
+ var maximum = configuration.maximum || 1;
+
+ function getPropName(propNode) {
+ if (propNode.type === 'JSXSpreadAttribute') {
+ return context.getSource(propNode.argument);
+ }
+ return propNode.name.name;
+ }
+
+ return {
+ JSXOpeningElement: function (node) {
+ var props = {};
+
+ node.attributes.forEach(function(decl) {
+ var line = decl.loc.start.line;
+ if (props[line]) {
+ props[line].push(decl);
+ } else {
+ props[line] = [decl];
+ }
+ });
+
+ for (var line in props) {
+ if (!props.hasOwnProperty(line)) {
+ continue;
+ }
+ if (props[line].length > maximum) {
+ var name = getPropName(props[line][maximum]);
+ context.report(props[line][maximum], 'Prop `' + name + '` must be placed on a new line');
+ break;
+ }
+ }
+ }
+ };
+};
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ maximum: {
+ type: 'integer',
+ minimum: 1
+ }
+ }
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.js b/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.js
new file mode 100644
index 0000000..349741c
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.js
@@ -0,0 +1,56 @@
+/**
+ * @fileoverview Prevents usage of Function.prototype.bind and arrow functions
+ * in React component definition.
+ * @author Daniel Lo Nigro
+ */
+'use strict';
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+module.exports = function(context) {
+ var configuration = context.options[0] || {};
+
+ return {
+ JSXAttribute: function(node) {
+ var isRef = configuration.ignoreRefs && node.name.name === 'ref';
+ if (isRef || !node.value || !node.value.expression) {
+ return;
+ }
+ var valueNode = node.value.expression;
+ if (
+ !configuration.allowBind &&
+ valueNode.type === 'CallExpression' &&
+ valueNode.callee.type === 'MemberExpression' &&
+ valueNode.callee.property.name === 'bind'
+ ) {
+ context.report(node, 'JSX props should not use .bind()');
+ } else if (
+ !configuration.allowArrowFunctions &&
+ valueNode.type === 'ArrowFunctionExpression'
+ ) {
+ context.report(node, 'JSX props should not use arrow functions');
+ }
+ }
+ };
+};
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ allowArrowFunctions: {
+ default: false,
+ type: 'boolean'
+ },
+ allowBind: {
+ default: false,
+ type: 'boolean'
+ },
+ ignoreRefs: {
+ default: false,
+ type: 'boolean'
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-no-duplicate-props.js b/node_modules/eslint-plugin-react/lib/rules/jsx-no-duplicate-props.js
new file mode 100644
index 0000000..0bdb84b
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-no-duplicate-props.js
@@ -0,0 +1,50 @@
+/**
+ * @fileoverview Enforce no duplicate props
+ * @author Markus Ånöstam
+ */
+
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function (context) {
+
+ var configuration = context.options[0] || {};
+ var ignoreCase = configuration.ignoreCase || false;
+
+ return {
+ JSXOpeningElement: function (node) {
+ var props = {};
+
+ node.attributes.forEach(function(decl) {
+ if (decl.type === 'JSXSpreadAttribute') {
+ return;
+ }
+
+ var name = decl.name.name;
+
+ if (ignoreCase) {
+ name = name.toLowerCase();
+ }
+
+ if (props.hasOwnProperty(name)) {
+ context.report(decl, 'No duplicate props allowed');
+ } else {
+ props[name] = 1;
+ }
+ });
+ }
+ };
+};
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ ignoreCase: {
+ type: 'boolean'
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-no-literals.js b/node_modules/eslint-plugin-react/lib/rules/jsx-no-literals.js
new file mode 100644
index 0000000..eaf29bf
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-no-literals.js
@@ -0,0 +1,43 @@
+/*
+* @fileoverview Prevent using string literals in React component definition
+* @author Caleb Morris
+*/
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ function reportLiteralNode(node) {
+ context.report(node, 'Missing JSX expression container around literal string');
+ }
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+
+ Literal: function(node) {
+ if (
+ !/^[\s]+$/.test(node.value) &&
+ node.parent &&
+ node.parent.type !== 'JSXExpressionContainer' &&
+ node.parent.type !== 'JSXAttribute' &&
+ node.parent.type.indexOf('JSX') !== -1
+ ) {
+ reportLiteralNode(node);
+ }
+ }
+
+ };
+
+};
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {},
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-no-undef.js b/node_modules/eslint-plugin-react/lib/rules/jsx-no-undef.js
new file mode 100644
index 0000000..baeb751
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-no-undef.js
@@ -0,0 +1,80 @@
+/**
+ * @fileoverview Disallow undeclared variables in JSX
+ * @author Yannick Croissant
+ */
+
+'use strict';
+
+/**
+ * Checks if a node name match the JSX tag convention.
+ * @param {String} name - Name of the node to check.
+ * @returns {boolean} Whether or not the node name match the JSX tag convention.
+ */
+var tagConvention = /^[a-z]|\-/;
+function isTagName(name) {
+ return tagConvention.test(name);
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ /**
+ * Compare an identifier with the variables declared in the scope
+ * @param {ASTNode} node - Identifier or JSXIdentifier node
+ * @returns {void}
+ */
+ function checkIdentifierInJSX(node) {
+ var scope = context.getScope();
+ var variables = scope.variables;
+ var i;
+ var len;
+
+ while (scope.type !== 'global') {
+ scope = scope.upper;
+ variables = scope.variables.concat(variables);
+ }
+ if (scope.childScopes.length) {
+ variables = scope.childScopes[0].variables.concat(variables);
+ // Temporary fix for babel-eslint
+ if (scope.childScopes[0].childScopes.length) {
+ variables = scope.childScopes[0].childScopes[0].variables.concat(variables);
+ }
+ }
+
+ for (i = 0, len = variables.length; i < len; i++) {
+ if (variables[i].name === node.name) {
+ return;
+ }
+ }
+
+ context.report(node, '\'' + node.name + '\' is not defined.');
+ }
+
+ return {
+ JSXOpeningElement: function(node) {
+ switch (node.name.type) {
+ case 'JSXIdentifier':
+ node = node.name;
+ break;
+ case 'JSXMemberExpression':
+ node = node.name.object;
+ break;
+ case 'JSXNamespacedName':
+ node = node.name.namespace;
+ break;
+ default:
+ break;
+ }
+ if (isTagName(node.name)) {
+ return;
+ }
+ checkIdentifierInJSX(node);
+ }
+ };
+
+};
+
+module.exports.schema = [];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-pascal-case.js b/node_modules/eslint-plugin-react/lib/rules/jsx-pascal-case.js
new file mode 100644
index 0000000..918df86
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-pascal-case.js
@@ -0,0 +1,46 @@
+/**
+ * @fileoverview Enforce PasalCase for user-defined JSX components
+ * @author Jake Marsh
+ */
+
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+var PASCAL_CASE_REGEX = /^[A-Z0-9]+[a-z0-9]+(?:[A-Z0-9]+[a-z0-9]*)*$/;
+var COMPAT_TAG_REGEX = /^[a-z]|\-/;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ return {
+ JSXOpeningElement: function(node) {
+ switch (node.name.type) {
+ case 'JSXIdentifier':
+ node = node.name;
+ break;
+ case 'JSXMemberExpression':
+ node = node.name.object;
+ break;
+ case 'JSXNamespacedName':
+ node = node.name.namespace;
+ break;
+ default:
+ break;
+ }
+
+ var isPascalCase = PASCAL_CASE_REGEX.test(node.name);
+ var isCompatTag = COMPAT_TAG_REGEX.test(node.name);
+
+ if (!isPascalCase && !isCompatTag) {
+ context.report(node, 'Imported JSX component ' + node.name + ' must be in PascalCase');
+ }
+ }
+ };
+
+};
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-quotes.js b/node_modules/eslint-plugin-react/lib/rules/jsx-quotes.js
new file mode 100644
index 0000000..ca97b02
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-quotes.js
@@ -0,0 +1,89 @@
+/**
+ * @fileoverview Enforce props quotes style
+ * @author Matt DuVall , Brandon Payton, Yannick Croissant
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+var QUOTE_SETTINGS = {
+ double: {
+ quote: '"',
+ alternateQuote: '\'',
+ description: 'doublequote'
+ },
+ single: {
+ quote: '\'',
+ alternateQuote: '"',
+ description: 'singlequote'
+ }
+};
+
+var AVOID_ESCAPE = 'avoid-escape';
+
+var isWarnedForDeprecation = false;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ /**
+ * Validate that a string passed in is surrounded by the specified character
+ * @param {string} val The text to check.
+ * @param {string} character The character to see if it's surrounded by.
+ * @returns {boolean} True if the text is surrounded by the character, false if not.
+ * @private
+ */
+ function isSurroundedBy(val, character) {
+ return val[0] === character && val[val.length - 1] === character;
+ }
+
+ return {
+
+ Program: function() {
+ if (isWarnedForDeprecation || /\=-(f|-format)=/.test(process.argv.join('='))) {
+ return;
+ }
+ /* eslint-disable no-console */
+ console.log('The react/jsx-quotes rule is deprecated. Please use the jsx-quotes rule instead.');
+ /* eslint-enable no-console */
+ isWarnedForDeprecation = true;
+ },
+
+ Literal: function(node) {
+
+ if (node.parent.type !== 'JSXAttribute') {
+ return;
+ }
+ var val = node.value;
+ var rawVal = node.raw;
+ var quoteOption = context.options[0];
+ var settings = QUOTE_SETTINGS[quoteOption];
+ var avoidEscape = context.options[1] === AVOID_ESCAPE;
+ var isValid;
+
+ if (settings && typeof val === 'string') {
+ isValid = isSurroundedBy(rawVal, settings.quote);
+
+ if (!isValid && avoidEscape) {
+ isValid = isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.indexOf(settings.quote) >= 0;
+ }
+
+ if (!isValid) {
+ context.report(node, 'JSX attributes must use ' + settings.description + '.');
+ }
+ }
+ }
+ };
+
+};
+
+module.exports.schema = [{
+ enum: ['single', 'double']
+}, {
+ enum: ['avoid-escape']
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-sort-prop-types.js b/node_modules/eslint-plugin-react/lib/rules/jsx-sort-prop-types.js
new file mode 100644
index 0000000..8dd0758
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-sort-prop-types.js
@@ -0,0 +1,154 @@
+/**
+ * @fileoverview Enforce propTypes declarations alphabetical sorting
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ var configuration = context.options[0] || {};
+ var requiredFirst = configuration.requiredFirst || false;
+ var callbacksLast = configuration.callbacksLast || false;
+ var ignoreCase = configuration.ignoreCase || false;
+
+ /**
+ * Checks if node is `propTypes` declaration
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Boolean} True if node is `propTypes` declaration, false if not.
+ */
+ function isPropTypesDeclaration(node) {
+
+ // Special case for class properties
+ // (babel-eslint does not expose property name so we have to rely on tokens)
+ if (node.type === 'ClassProperty') {
+ var tokens = context.getFirstTokens(node, 2);
+ return (tokens[0] && tokens[0].value === 'propTypes') ||
+ (tokens[1] && tokens[1].value === 'propTypes');
+ }
+
+ return Boolean(
+ node &&
+ node.name === 'propTypes'
+ );
+ }
+
+ function getKey(node) {
+ return node.key.type === 'Identifier' ? node.key.name : node.key.value;
+ }
+
+ function getValueName(node) {
+ return node.value.property && node.value.property.name;
+ }
+
+ function isCallbackPropName(propName) {
+ return /^on[A-Z]/.test(propName);
+ }
+
+ function isRequiredProp(node) {
+ return getValueName(node) === 'isRequired';
+ }
+
+ /**
+ * Checks if propTypes declarations are sorted
+ * @param {Array} declarations The array of AST nodes being checked.
+ * @returns {void}
+ */
+ function checkSorted(declarations) {
+ declarations.reduce(function(prev, curr) {
+ var prevPropName = getKey(prev);
+ var currentPropName = getKey(curr);
+ var previousIsRequired = isRequiredProp(prev);
+ var currentIsRequired = isRequiredProp(curr);
+ var previousIsCallback = isCallbackPropName(prevPropName);
+ var currentIsCallback = isCallbackPropName(currentPropName);
+
+ if (ignoreCase) {
+ prevPropName = prevPropName.toLowerCase();
+ currentPropName = currentPropName.toLowerCase();
+ }
+
+ if (requiredFirst) {
+ if (previousIsRequired && !currentIsRequired) {
+ // Transition between required and non-required. Don't compare for alphabetical.
+ return curr;
+ }
+ if (!previousIsRequired && currentIsRequired) {
+ // Encountered a non-required prop after a required prop
+ context.report(curr, 'Required prop types must be listed before all other prop types');
+ return curr;
+ }
+ }
+
+ if (callbacksLast) {
+ if (!previousIsCallback && currentIsCallback) {
+ // Entering the callback prop section
+ return curr;
+ }
+ if (previousIsCallback && !currentIsCallback) {
+ // Encountered a non-callback prop after a callback prop
+ context.report(prev, 'Callback prop types must be listed after all other prop types');
+ return prev;
+ }
+ }
+
+ if (currentPropName < prevPropName) {
+ context.report(curr, 'Prop types declarations should be sorted alphabetically');
+ return prev;
+ }
+
+ return curr;
+ }, declarations[0]);
+ }
+
+ return {
+ ClassProperty: function(node) {
+ if (isPropTypesDeclaration(node) && node.value && node.value.type === 'ObjectExpression') {
+ checkSorted(node.value.properties);
+ }
+ },
+
+ MemberExpression: function(node) {
+ if (isPropTypesDeclaration(node.property)) {
+ var right = node.parent.right;
+ if (right && right.type === 'ObjectExpression') {
+ checkSorted(right.properties);
+ }
+ }
+ },
+
+ ObjectExpression: function(node) {
+ node.properties.forEach(function(property) {
+ if (!property.key) {
+ return;
+ }
+
+ if (!isPropTypesDeclaration(property.key)) {
+ return;
+ }
+ if (property.value.type === 'ObjectExpression') {
+ checkSorted(property.value.properties);
+ }
+ });
+ }
+
+ };
+};
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ requiredFirst: {
+ type: 'boolean'
+ },
+ callbacksLast: {
+ type: 'boolean'
+ },
+ ignoreCase: {
+ type: 'boolean'
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-sort-props.js b/node_modules/eslint-plugin-react/lib/rules/jsx-sort-props.js
new file mode 100644
index 0000000..5e345b3
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-sort-props.js
@@ -0,0 +1,91 @@
+/**
+ * @fileoverview Enforce props alphabetical sorting
+ * @author Ilya Volodin, Yannick Croissant
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function isCallbackPropName(propName) {
+ return /^on[A-Z]/.test(propName);
+}
+
+module.exports = function(context) {
+
+ var configuration = context.options[0] || {};
+ var ignoreCase = configuration.ignoreCase || false;
+ var callbacksLast = configuration.callbacksLast || false;
+ var shorthandFirst = configuration.shorthandFirst || false;
+
+ return {
+ JSXOpeningElement: function(node) {
+ node.attributes.reduce(function(memo, decl, idx, attrs) {
+ if (decl.type === 'JSXSpreadAttribute') {
+ return attrs[idx + 1];
+ }
+
+ var previousPropName = memo.name.name;
+ var currentPropName = decl.name.name;
+ var previousValue = memo.value;
+ var currentValue = decl.value;
+ var previousIsCallback = isCallbackPropName(previousPropName);
+ var currentIsCallback = isCallbackPropName(currentPropName);
+
+ if (ignoreCase) {
+ previousPropName = previousPropName.toLowerCase();
+ currentPropName = currentPropName.toLowerCase();
+ }
+
+ if (callbacksLast) {
+ if (!previousIsCallback && currentIsCallback) {
+ // Entering the callback prop section
+ return decl;
+ }
+ if (previousIsCallback && !currentIsCallback) {
+ // Encountered a non-callback prop after a callback prop
+ context.report(memo, 'Callbacks must be listed after all other props');
+ return memo;
+ }
+ }
+
+ if (shorthandFirst) {
+ if (currentValue && !previousValue) {
+ return decl;
+ }
+ if (!currentValue && previousValue) {
+ context.report(memo, 'Shorthand props must be listed before all other props');
+ return memo;
+ }
+ }
+
+ if (currentPropName < previousPropName) {
+ context.report(decl, 'Props should be sorted alphabetically');
+ return memo;
+ }
+
+ return decl;
+ }, node.attributes[0]);
+ }
+ };
+};
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ // Whether callbacks (prefixed with "on") should be listed at the very end,
+ // after all other props.
+ callbacksLast: {
+ type: 'boolean'
+ },
+ // Whether shorthand properties (without a value) should be listed first
+ shorthandFirst: {
+ type: 'boolean'
+ },
+ ignoreCase: {
+ type: 'boolean'
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-uses-react.js b/node_modules/eslint-plugin-react/lib/rules/jsx-uses-react.js
new file mode 100644
index 0000000..c5fd742
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-uses-react.js
@@ -0,0 +1,44 @@
+/**
+ * @fileoverview Prevent React to be marked as unused
+ * @author Glen Mailer
+ */
+'use strict';
+
+var variableUtil = require('../util/variable');
+var pragmaUtil = require('../util/pragma');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ var pragma = pragmaUtil.getFromContext(context);
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+
+ JSXOpeningElement: function() {
+ variableUtil.markVariableAsUsed(context, pragma);
+ },
+
+ BlockComment: function(node) {
+ pragma = pragmaUtil.getFromNode(node) || pragma;
+ }
+
+ };
+
+};
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ pragma: {
+ type: 'string'
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/jsx-uses-vars.js b/node_modules/eslint-plugin-react/lib/rules/jsx-uses-vars.js
new file mode 100644
index 0000000..9eaf8a0
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/jsx-uses-vars.js
@@ -0,0 +1,34 @@
+/**
+ * @fileoverview Prevent variables used in JSX to be marked as unused
+ * @author Yannick Croissant
+ */
+'use strict';
+
+var variableUtil = require('../util/variable');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ return {
+ JSXExpressionContainer: function(node) {
+ if (node.expression.type !== 'Identifier') {
+ return;
+ }
+ variableUtil.markVariableAsUsed(context, node.expression.name);
+ },
+
+ JSXIdentifier: function(node) {
+ if (node.parent.type === 'JSXAttribute') {
+ return;
+ }
+ variableUtil.markVariableAsUsed(context, node.name);
+ }
+
+ };
+
+};
+
+module.exports.schema = [];
diff --git a/node_modules/eslint-plugin-react/lib/rules/no-danger.js b/node_modules/eslint-plugin-react/lib/rules/no-danger.js
new file mode 100644
index 0000000..2f60dd8
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/no-danger.js
@@ -0,0 +1,65 @@
+/**
+ * @fileoverview Prevent usage of dangerous JSX props
+ * @author Scott Andrews
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+var DANGEROUS_MESSAGE = 'Dangerous property \'{{name}}\' found';
+
+var DANGEROUS_PROPERTY_NAMES = [
+ 'dangerouslySetInnerHTML'
+];
+
+var DANGEROUS_PROPERTIES = DANGEROUS_PROPERTY_NAMES.reduce(function (props, prop) {
+ props[prop] = prop;
+ return props;
+}, Object.create(null));
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+/**
+ * Checks if a node name match the JSX tag convention.
+ * @param {String} name - Name of the node to check.
+ * @returns {boolean} Whether or not the node name match the JSX tag convention.
+ */
+var tagConvention = /^[a-z]|\-/;
+function isTagName(name) {
+ return tagConvention.test(name);
+}
+
+/**
+ * Checks if a JSX attribute is dangerous.
+ * @param {String} name - Name of the attribute to check.
+ * @returns {boolean} Whether or not the attribute is dnagerous.
+ */
+function isDangerous(name) {
+ return name in DANGEROUS_PROPERTIES;
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ return {
+
+ JSXAttribute: function(node) {
+ if (isTagName(node.parent.name.name) && isDangerous(node.name.name)) {
+ context.report(node, DANGEROUS_MESSAGE, {
+ name: node.name.name
+ });
+ }
+ }
+
+ };
+
+};
+
+module.exports.schema = [];
diff --git a/node_modules/eslint-plugin-react/lib/rules/no-deprecated.js b/node_modules/eslint-plugin-react/lib/rules/no-deprecated.js
new file mode 100644
index 0000000..294fc57
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/no-deprecated.js
@@ -0,0 +1,118 @@
+/**
+ * @fileoverview Prevent usage of deprecated methods
+ * @author Yannick Croissant
+ * @author Scott Feeney
+ */
+'use strict';
+
+var pragmaUtil = require('../util/pragma');
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+var DEPRECATED_MESSAGE = '{{oldMethod}} is deprecated since React {{version}}{{newMethod}}';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ // Validate React version passed in options
+ // (append the patch version if missing, allowing shorthands like 0.12 for React 0.12.0)
+ var optVer = context.options[0] ? context.options[0].react : '999.999.999';
+ optVer = /^[0-9]+\.[0-9]+$/.test(optVer) ? optVer + '.0' : optVer;
+ optVer = optVer.split('.').map(function(part) {
+ return Number(part);
+ });
+
+ var pragma = pragmaUtil.getFromContext(context);
+
+ function getDeprecated() {
+ var deprecated = {
+ MemberExpression: {}
+ };
+ // 0.12.0
+ deprecated.MemberExpression[pragma + '.renderComponent'] = ['0.12.0', pragma + '.render'];
+ deprecated.MemberExpression[pragma + '.renderComponentToString'] = ['0.12.0', pragma + '.renderToString'];
+ deprecated.MemberExpression[pragma + '.renderComponentToStaticMarkup'] = [
+ '0.12.0',
+ pragma + '.renderToStaticMarkup'
+ ];
+ deprecated.MemberExpression[pragma + '.isValidComponent'] = ['0.12.0', pragma + '.isValidElement'];
+ deprecated.MemberExpression[pragma + '.PropTypes.component'] = ['0.12.0', pragma + '.PropTypes.element'];
+ deprecated.MemberExpression[pragma + '.PropTypes.renderable'] = ['0.12.0', pragma + '.PropTypes.node'];
+ deprecated.MemberExpression[pragma + '.isValidClass'] = ['0.12.0'];
+ deprecated.MemberExpression['this.transferPropsTo'] = ['0.12.0', 'spread operator ({...})'];
+ // 0.13.0
+ deprecated.MemberExpression[pragma + '.addons.classSet'] = ['0.13.0', 'the npm module classnames'];
+ deprecated.MemberExpression[pragma + '.addons.cloneWithProps'] = ['0.13.0', pragma + '.cloneElement'];
+ // 0.14.0
+ deprecated.MemberExpression[pragma + '.render'] = ['0.14.0', 'ReactDOM.render'];
+ deprecated.MemberExpression[pragma + '.unmountComponentAtNode'] = ['0.14.0', 'ReactDOM.unmountComponentAtNode'];
+ deprecated.MemberExpression[pragma + '.findDOMNode'] = ['0.14.0', 'ReactDOM.findDOMNode'];
+ deprecated.MemberExpression[pragma + '.renderToString'] = ['0.14.0', 'ReactDOMServer.renderToString'];
+ deprecated.MemberExpression[pragma + '.renderToStaticMarkup'] = ['0.14.0', 'ReactDOMServer.renderToStaticMarkup'];
+
+ return deprecated;
+ }
+
+ function checkVersion(methodVer) {
+ methodVer = methodVer.split('.').map(function(part) {
+ return Number(part);
+ });
+ var higherMajor = methodVer[0] < optVer[0];
+ var higherMinor = methodVer[0] === optVer[0] && methodVer[1] < optVer[1];
+ var higherOrEqualPatch = methodVer[0] === optVer[0] && methodVer[1] === optVer[1] && methodVer[2] <= optVer[2];
+
+ return higherMajor || higherMinor || higherOrEqualPatch;
+ }
+
+ function isDeprecated(type, method) {
+ var deprecated = getDeprecated();
+
+ return (
+ deprecated[type] &&
+ deprecated[type][method] &&
+ checkVersion(deprecated[type][method][0])
+ );
+ }
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+
+ MemberExpression: function(node) {
+ var method = context.getSource(node);
+ if (!isDeprecated(node.type, method)) {
+ return;
+ }
+ var deprecated = getDeprecated();
+ context.report(node, DEPRECATED_MESSAGE, {
+ oldMethod: method,
+ version: deprecated[node.type][method][0],
+ newMethod: deprecated[node.type][method][1] ? ', use ' + deprecated[node.type][method][1] + ' instead' : ''
+ });
+ },
+
+ BlockComment: function(node) {
+ pragma = pragmaUtil.getFromNode(node) || pragma;
+ }
+
+ };
+
+};
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ react: {
+ type: 'string',
+ pattern: '^[0-9]+\.[0-9]+(\.[0-9]+)?$'
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/no-did-mount-set-state.js b/node_modules/eslint-plugin-react/lib/rules/no-did-mount-set-state.js
new file mode 100644
index 0000000..6869eac
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/no-did-mount-set-state.js
@@ -0,0 +1,53 @@
+/**
+ * @fileoverview Prevent usage of setState in componentDidMount
+ * @author Yannick Croissant
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ var mode = context.options[0] || 'never';
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+
+ CallExpression: function(node) {
+ var callee = node.callee;
+ if (
+ callee.type !== 'MemberExpression' ||
+ callee.object.type !== 'ThisExpression' ||
+ callee.property.name !== 'setState'
+ ) {
+ return;
+ }
+ var ancestors = context.getAncestors(callee).reverse();
+ var depth = 0;
+ for (var i = 0, j = ancestors.length; i < j; i++) {
+ if (/Function(Expression|Declaration)$/.test(ancestors[i].type)) {
+ depth++;
+ }
+ if (
+ (ancestors[i].type !== 'Property' && ancestors[i].type !== 'MethodDefinition') ||
+ ancestors[i].key.name !== 'componentDidMount' ||
+ (mode === 'allow-in-func' && depth > 1)
+ ) {
+ continue;
+ }
+ context.report(callee, 'Do not use setState in componentDidMount');
+ break;
+ }
+ }
+ };
+
+};
+
+module.exports.schema = [{
+ enum: ['allow-in-func']
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/no-did-update-set-state.js b/node_modules/eslint-plugin-react/lib/rules/no-did-update-set-state.js
new file mode 100644
index 0000000..c26f273
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/no-did-update-set-state.js
@@ -0,0 +1,53 @@
+/**
+ * @fileoverview Prevent usage of setState in componentDidUpdate
+ * @author Yannick Croissant
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ var mode = context.options[0] || 'never';
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+
+ CallExpression: function(node) {
+ var callee = node.callee;
+ if (
+ callee.type !== 'MemberExpression' ||
+ callee.object.type !== 'ThisExpression' ||
+ callee.property.name !== 'setState'
+ ) {
+ return;
+ }
+ var ancestors = context.getAncestors(callee).reverse();
+ var depth = 0;
+ for (var i = 0, j = ancestors.length; i < j; i++) {
+ if (/Function(Expression|Declaration)$/.test(ancestors[i].type)) {
+ depth++;
+ }
+ if (
+ (ancestors[i].type !== 'Property' && ancestors[i].type !== 'MethodDefinition') ||
+ ancestors[i].key.name !== 'componentDidUpdate' ||
+ (mode === 'allow-in-func' && depth > 1)
+ ) {
+ continue;
+ }
+ context.report(callee, 'Do not use setState in componentDidUpdate');
+ break;
+ }
+ }
+ };
+
+};
+
+module.exports.schema = [{
+ enum: ['allow-in-func']
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/no-direct-mutation-state.js b/node_modules/eslint-plugin-react/lib/rules/no-direct-mutation-state.js
new file mode 100644
index 0000000..5fd9620
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/no-direct-mutation-state.js
@@ -0,0 +1,75 @@
+/**
+ * @fileoverview Prevent usage of setState in componentDidMount
+ * @author David Petersen
+ */
+'use strict';
+
+var Components = require('../util/Components');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = Components.detect(function(context, components, utils) {
+
+ /**
+ * Checks if the component is valid
+ * @param {Object} component The component to process
+ * @returns {Boolean} True if the component is valid, false if not.
+ */
+ function isValid(component) {
+ return Boolean(component && !component.mutateSetState);
+ }
+
+ /**
+ * Reports undeclared proptypes for a given component
+ * @param {Object} component The component to process
+ */
+ function reportMutations(component) {
+ var mutation;
+ for (var i = 0, j = component.mutations.length; i < j; i++) {
+ mutation = component.mutations[i];
+ context.report(mutation, 'Do not mutate state directly. Use setState().');
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+ AssignmentExpression: function(node) {
+ var item;
+ if (!node.left || !node.left.object || !node.left.object.object) {
+ return;
+ }
+ item = node.left.object;
+ while (item.object.property) {
+ item = item.object;
+ }
+ if (
+ item.object.type === 'ThisExpression' &&
+ item.property.name === 'state'
+ ) {
+ var component = components.get(utils.getParentComponent());
+ var mutations = component && component.mutations || [];
+ mutations.push(node.left.object);
+ components.set(node, {
+ mutateSetState: true,
+ mutations: mutations
+ });
+ }
+ },
+
+ 'Program:exit': function() {
+ var list = components.list();
+ for (var component in list) {
+ if (!list.hasOwnProperty(component) || isValid(list[component])) {
+ continue;
+ }
+ reportMutations(list[component]);
+ }
+ }
+ };
+
+});
diff --git a/node_modules/eslint-plugin-react/lib/rules/no-is-mounted.js b/node_modules/eslint-plugin-react/lib/rules/no-is-mounted.js
new file mode 100644
index 0000000..bfd8c1d
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/no-is-mounted.js
@@ -0,0 +1,39 @@
+/**
+ * @fileoverview Prevent usage of isMounted
+ * @author Joe Lencioni
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+
+ CallExpression: function(node) {
+ var callee = node.callee;
+ if (callee.type !== 'MemberExpression') {
+ return;
+ }
+ if (callee.object.type !== 'ThisExpression' || callee.property.name !== 'isMounted') {
+ return;
+ }
+ var ancestors = context.getAncestors(callee);
+ for (var i = 0, j = ancestors.length; i < j; i++) {
+ if (ancestors[i].type === 'Property' || ancestors[i].type === 'MethodDefinition') {
+ context.report(callee, 'Do not use isMounted');
+ break;
+ }
+ }
+ }
+ };
+
+};
+
+module.exports.schema = [];
diff --git a/node_modules/eslint-plugin-react/lib/rules/no-multi-comp.js b/node_modules/eslint-plugin-react/lib/rules/no-multi-comp.js
new file mode 100644
index 0000000..179cdc9
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/no-multi-comp.js
@@ -0,0 +1,61 @@
+/**
+ * @fileoverview Prevent multiple component definition per file
+ * @author Yannick Croissant
+ */
+'use strict';
+
+var Components = require('../util/Components');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = Components.detect(function(context, components) {
+
+ var configuration = context.options[0] || {};
+ var ignoreStateless = configuration.ignoreStateless || false;
+
+ var MULTI_COMP_MESSAGE = 'Declare only one React component per file';
+
+ /**
+ * Checks if the component is ignored
+ * @param {Object} component The component being checked.
+ * @returns {Boolean} True if the component is ignored, false if not.
+ */
+ function isIgnored(component) {
+ return ignoreStateless === true && /Function/.test(component.node.type);
+ }
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+ 'Program:exit': function() {
+ if (components.length() <= 1) {
+ return;
+ }
+
+ var list = components.list();
+ var i = 0;
+
+ for (var component in list) {
+ if (!list.hasOwnProperty(component) || isIgnored(list[component]) || ++i === 1) {
+ continue;
+ }
+ context.report(list[component].node, MULTI_COMP_MESSAGE);
+ }
+ }
+ };
+});
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ ignoreStateless: {
+ default: false,
+ type: 'boolean'
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/no-set-state.js b/node_modules/eslint-plugin-react/lib/rules/no-set-state.js
new file mode 100644
index 0000000..94f2217
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/no-set-state.js
@@ -0,0 +1,39 @@
+/**
+ * @fileoverview Prevent usage of setState
+ * @author Mark Dalgleish
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+
+ CallExpression: function(node) {
+ var callee = node.callee;
+ if (callee.type !== 'MemberExpression') {
+ return;
+ }
+ if (callee.object.type !== 'ThisExpression' || callee.property.name !== 'setState') {
+ return;
+ }
+ var ancestors = context.getAncestors(callee);
+ for (var i = 0, j = ancestors.length; i < j; i++) {
+ if (ancestors[i].type === 'Property' || ancestors[i].type === 'MethodDefinition') {
+ context.report(callee, 'Do not use setState');
+ break;
+ }
+ }
+ }
+ };
+
+};
+
+module.exports.schema = [];
diff --git a/node_modules/eslint-plugin-react/lib/rules/no-string-refs.js b/node_modules/eslint-plugin-react/lib/rules/no-string-refs.js
new file mode 100644
index 0000000..e6cd65d
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/no-string-refs.js
@@ -0,0 +1,88 @@
+/**
+ * @fileoverview Prevent string definitions for references and prevent referencing this.refs
+ * @author Tom Hastjarjanto
+ */
+'use strict';
+
+var Components = require('../util/Components');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = Components.detect(function(context, components, utils) {
+ /**
+ * Checks if we are using refs
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Boolean} True if we are using refs, false if not.
+ */
+ function isRefsUsage(node) {
+ return Boolean(
+ (
+ utils.getParentES6Component() ||
+ utils.getParentES5Component()
+ ) &&
+ node.object.type === 'ThisExpression' &&
+ node.property.name === 'refs'
+ );
+ }
+
+ /**
+ * Checks if we are using a ref attribute
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Boolean} True if we are using a ref attribute, false if not.
+ */
+ function isRefAttribute(node) {
+ return Boolean(
+ node.type === 'JSXAttribute' &&
+ node.name &&
+ node.name.name === 'ref'
+ );
+ }
+
+ /**
+ * Checks if a node contains a string value
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Boolean} True if the node contains a string value, false if not.
+ */
+ function containsStringLiteral(node) {
+ return Boolean(
+ node.value &&
+ node.value.type === 'Literal' &&
+ typeof node.value.value === 'string'
+ );
+ }
+
+ /**
+ * Checks if a node contains a string value within a jsx expression
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Boolean} True if the node contains a string value within a jsx expression, false if not.
+ */
+ function containsStringExpressionContainer(node) {
+ return Boolean(
+ node.value &&
+ node.value.type === 'JSXExpressionContainer' &&
+ node.value.expression &&
+ node.value.expression.type === 'Literal' &&
+ typeof node.value.expression.value === 'string'
+ );
+ }
+
+ return {
+ MemberExpression: function(node) {
+ if (isRefsUsage(node)) {
+ context.report(node, 'Using this.refs is deprecated.');
+ }
+ },
+ JSXAttribute: function(node) {
+ if (
+ isRefAttribute(node) &&
+ (containsStringLiteral(node) || containsStringExpressionContainer(node))
+ ) {
+ context.report(node, 'Using string literals in ref attributes is deprecated.');
+ }
+ }
+ };
+});
+
+module.exports.schema = [];
diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unknown-property.js b/node_modules/eslint-plugin-react/lib/rules/no-unknown-property.js
new file mode 100644
index 0000000..7e9147d
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/no-unknown-property.js
@@ -0,0 +1,135 @@
+/**
+ * @fileoverview Prevent usage of unknown DOM property
+ * @author Yannick Croissant
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+var UNKNOWN_MESSAGE = 'Unknown property \'{{name}}\' found, use \'{{standardName}}\' instead';
+
+var DOM_ATTRIBUTE_NAMES = {
+ // Standard
+ 'accept-charset': 'acceptCharset',
+ class: 'className',
+ for: 'htmlFor',
+ 'http-equiv': 'httpEquiv',
+ // SVG
+ 'clip-path': 'clipPath',
+ 'fill-opacity': 'fillOpacity',
+ 'font-family': 'fontFamily',
+ 'font-size': 'fontSize',
+ 'marker-end': 'markerEnd',
+ 'marker-mid': 'markerMid',
+ 'marker-start': 'markerStart',
+ 'stop-color': 'stopColor',
+ 'stop-opacity': 'stopOpacity',
+ 'stroke-dasharray': 'strokeDasharray',
+ 'stroke-linecap': 'strokeLinecap',
+ 'stroke-opacity': 'strokeOpacity',
+ 'stroke-width': 'strokeWidth',
+ 'text-anchor': 'textAnchor',
+ 'xlink:actuate': 'xlinkActuate',
+ 'xlink:arcrole': 'xlinkArcrole',
+ 'xlink:href': 'xlinkHref',
+ 'xlink:role': 'xlinkRole',
+ 'xlink:show': 'xlinkShow',
+ 'xlink:title': 'xlinkTitle',
+ 'xlink:type': 'xlinkType',
+ 'xml:base': 'xmlBase',
+ 'xml:lang': 'xmlLang',
+ 'xml:space': 'xmlSpace'
+};
+
+var DOM_PROPERTY_NAMES = [
+ // Standard
+ 'acceptCharset', 'accessKey', 'allowFullScreen', 'allowTransparency', 'autoComplete', 'autoFocus', 'autoPlay',
+ 'cellPadding', 'cellSpacing', 'charSet', 'classID', 'className', 'colSpan', 'contentEditable', 'contextMenu',
+ 'crossOrigin', 'dateTime', 'encType', 'formAction', 'formEncType', 'formMethod', 'formNoValidate', 'formTarget',
+ 'frameBorder', 'hrefLang', 'htmlFor', 'httpEquiv', 'marginHeight', 'marginWidth', 'maxLength', 'mediaGroup',
+ 'noValidate', 'onBlur', 'onChange', 'onClick', 'onContextMenu', 'onCopy', 'onCut', 'onDoubleClick',
+ 'onDrag', 'onDragEnd', 'onDragEnter', 'onDragExit', 'onDragLeave', 'onDragOver', 'onDragStart', 'onDrop',
+ 'onFocus', 'onInput', 'onKeyDown', 'onKeyPress', 'onKeyUp', 'onMouseDown', 'onMouseEnter', 'onMouseLeave',
+ 'onMouseMove', 'onMouseOut', 'onMouseOver', 'onMouseUp', 'onPaste', 'onScroll', 'onSubmit', 'onTouchCancel',
+ 'onTouchEnd', 'onTouchMove', 'onTouchStart', 'onWheel',
+ 'radioGroup', 'readOnly', 'rowSpan', 'spellCheck', 'srcDoc', 'srcSet', 'tabIndex', 'useMap',
+ // Non standard
+ 'autoCapitalize', 'autoCorrect',
+ 'autoSave',
+ 'itemProp', 'itemScope', 'itemType', 'itemRef', 'itemID'
+];
+
+// ------------------------------------------------------------------------------
+// Helpers
+// ------------------------------------------------------------------------------
+
+/**
+ * Checks if a node matches the JSX tag convention.
+ * @param {Object} node - JSX element being tested.
+ * @returns {boolean} Whether or not the node name match the JSX tag convention.
+ */
+var tagConvention = /^[a-z][^-]*$/;
+function isTagName(node) {
+ if (tagConvention.test(node.parent.name.name)) {
+ // http://www.w3.org/TR/custom-elements/#type-extension-semantics
+ return !node.parent.attributes.some(function(attrNode) {
+ return (
+ attrNode.type === 'JSXAttribute' &&
+ attrNode.name.type === 'JSXIdentifier' &&
+ attrNode.name.name === 'is'
+ );
+ });
+ }
+ return false;
+}
+
+/**
+ * Get the standard name of the attribute.
+ * @param {String} name - Name of the attribute.
+ * @returns {String} The standard name of the attribute.
+ */
+function getStandardName(name) {
+ if (DOM_ATTRIBUTE_NAMES[name]) {
+ return DOM_ATTRIBUTE_NAMES[name];
+ }
+ var i;
+ var found = DOM_PROPERTY_NAMES.some(function(element, index) {
+ i = index;
+ return element.toLowerCase() === name;
+ });
+ return found ? DOM_PROPERTY_NAMES[i] : null;
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ return {
+
+ JSXAttribute: function(node) {
+ var name = context.getSource(node.name);
+ var standardName = getStandardName(name);
+ if (!isTagName(node) || !standardName) {
+ return;
+ }
+ context.report({
+ node: node,
+ message: UNKNOWN_MESSAGE,
+ data: {
+ name: name,
+ standardName: standardName
+ },
+ fix: function(fixer) {
+ return fixer.replaceText(node.name, standardName);
+ }
+ });
+ }
+ };
+
+};
+
+module.exports.schema = [];
diff --git a/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.js b/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.js
new file mode 100644
index 0000000..618b295
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.js
@@ -0,0 +1,32 @@
+/**
+ * @fileoverview Enforce ES5 or ES6 class for React Components
+ * @author Dan Hamilton
+ */
+'use strict';
+
+var Components = require('../util/Components');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = Components.detect(function(context, components, utils) {
+ var configuration = context.options[0] || 'always';
+
+ return {
+ ObjectExpression: function(node) {
+ if (utils.isES5Component(node) && configuration === 'always') {
+ context.report(node, 'Component should use es6 class instead of createClass');
+ }
+ },
+ ClassDeclaration: function(node) {
+ if (utils.isES6Component(node) && configuration === 'never') {
+ context.report(node, 'Component should use createClass instead of es6 class');
+ }
+ }
+ };
+});
+
+module.exports.schema = [{
+ enum: ['always', 'never']
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/prop-types.js b/node_modules/eslint-plugin-react/lib/rules/prop-types.js
new file mode 100644
index 0000000..b339823
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/prop-types.js
@@ -0,0 +1,829 @@
+/**
+ * @fileoverview Prevent missing props validation in a React component definition
+ * @author Yannick Croissant
+ */
+'use strict';
+
+// As for exceptions for props.children or props.className (and alike) look at
+// https://github.com/yannickcr/eslint-plugin-react/issues/7
+
+var Components = require('../util/Components');
+var variable = require('../util/variable');
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = Components.detect(function(context, components, utils) {
+
+ var configuration = context.options[0] || {};
+ var ignored = configuration.ignore || [];
+ var customValidators = configuration.customValidators || [];
+ // Used to track the type annotations in scope.
+ // Necessary because babel's scopes do not track type annotations.
+ var stack = null;
+
+ var MISSING_MESSAGE = '\'{{name}}\' is missing in props validation';
+
+ /**
+ * Helper for accessing the current scope in the stack.
+ * @param {string} key The name of the identifier to access. If omitted, returns the full scope.
+ * @param {ASTNode} value If provided sets the new value for the identifier.
+ * @returns {Object|ASTNode} Either the whole scope or the ASTNode associated with the given identifier.
+ */
+ function typeScope(key, value) {
+ if (arguments.length === 0) {
+ return stack[stack.length - 1];
+ } else if (arguments.length === 1) {
+ return stack[stack.length - 1][key];
+ }
+ stack[stack.length - 1][key] = value;
+ return value;
+ }
+
+ /**
+ * Checks if we are using a prop
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Boolean} True if we are using a prop, false if not.
+ */
+ function isPropTypesUsage(node) {
+ var isClassUsage = (
+ (utils.getParentES6Component() || utils.getParentES5Component()) &&
+ node.object.type === 'ThisExpression' && node.property.name === 'props'
+ );
+ var isStatelessFunctionUsage = node.object.name === 'props';
+ return isClassUsage || isStatelessFunctionUsage;
+ }
+
+ /**
+ * Checks if we are declaring a `props` class property with a flow type annotation.
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Boolean} True if the node is a type annotated props declaration, false if not.
+ */
+ function isAnnotatedPropsDeclaration(node) {
+ if (node && node.type === 'ClassProperty') {
+ var tokens = context.getFirstTokens(node, 2);
+ if (
+ node.typeAnnotation && (
+ tokens[0].value === 'props' ||
+ (tokens[1] && tokens[1].value === 'props')
+ )
+ ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks if we are declaring a prop
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Boolean} True if we are declaring a prop, false if not.
+ */
+ function isPropTypesDeclaration(node) {
+
+ // Special case for class properties
+ // (babel-eslint does not expose property name so we have to rely on tokens)
+ if (node && node.type === 'ClassProperty') {
+ var tokens = context.getFirstTokens(node, 2);
+ if (
+ tokens[0].value === 'propTypes' ||
+ (tokens[1] && tokens[1].value === 'propTypes')
+ ) {
+ return true;
+ }
+ return false;
+ }
+
+ return Boolean(
+ node &&
+ node.name === 'propTypes'
+ );
+
+ }
+
+ /**
+ * Checks if the prop is ignored
+ * @param {String} name Name of the prop to check.
+ * @returns {Boolean} True if the prop is ignored, false if not.
+ */
+ function isIgnored(name) {
+ return ignored.indexOf(name) !== -1;
+ }
+
+ /**
+ * Checks if prop should be validated by plugin-react-proptypes
+ * @param {String} validator Name of validator to check.
+ * @returns {Boolean} True if validator should be checked by custom validator.
+ */
+ function hasCustomValidator(validator) {
+ return customValidators.indexOf(validator) !== -1;
+ }
+
+ /**
+ * Checks if the component must be validated
+ * @param {Object} component The component to process
+ * @returns {Boolean} True if the component must be validated, false if not.
+ */
+ function mustBeValidated(component) {
+ return Boolean(
+ component &&
+ component.usedPropTypes &&
+ !component.ignorePropsValidation
+ );
+ }
+
+ /**
+ * Internal: Checks if the prop is declared
+ * @param {Object} declaredPropTypes Description of propTypes declared in the current component
+ * @param {String[]} keyList Dot separated name of the prop to check.
+ * @returns {Boolean} True if the prop is declared, false if not.
+ */
+ function _isDeclaredInComponent(declaredPropTypes, keyList) {
+ for (var i = 0, j = keyList.length; i < j; i++) {
+ var key = keyList[i];
+ var propType = (
+ // Check if this key is declared
+ declaredPropTypes[key] ||
+ // If not, check if this type accepts any key
+ declaredPropTypes.__ANY_KEY__
+ );
+
+ if (!propType) {
+ // If it's a computed property, we can't make any further analysis, but is valid
+ return key === '__COMPUTED_PROP__';
+ }
+ if (propType === true) {
+ return true;
+ }
+ // Consider every children as declared
+ if (propType.children === true) {
+ return true;
+ }
+ if (propType.acceptedProperties) {
+ return key in propType.acceptedProperties;
+ }
+ if (propType.type === 'union') {
+ // If we fall in this case, we know there is at least one complex type in the union
+ if (i + 1 >= j) {
+ // this is the last key, accept everything
+ return true;
+ }
+ // non trivial, check all of them
+ var unionTypes = propType.children;
+ var unionPropType = {};
+ for (var k = 0, z = unionTypes.length; k < z; k++) {
+ unionPropType[key] = unionTypes[k];
+ var isValid = _isDeclaredInComponent(
+ unionPropType,
+ keyList.slice(i)
+ );
+ if (isValid) {
+ return true;
+ }
+ }
+
+ // every possible union were invalid
+ return false;
+ }
+ declaredPropTypes = propType.children;
+ }
+ return true;
+ }
+
+ /**
+ * Checks if the prop is declared
+ * @param {ASTNode} node The AST node being checked.
+ * @param {String[]} names List of names of the prop to check.
+ * @returns {Boolean} True if the prop is declared, false if not.
+ */
+ function isDeclaredInComponent(node, names) {
+ while (node) {
+ var component = components.get(node);
+ var isDeclared =
+ component && component.confidence === 2 &&
+ _isDeclaredInComponent(component.declaredPropTypes || {}, names)
+ ;
+ if (isDeclared) {
+ return true;
+ }
+ node = node.parent;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the prop has spread operator.
+ * @param {ASTNode} node The AST node being marked.
+ * @returns {Boolean} True if the prop has spread operator, false if not.
+ */
+ function hasSpreadOperator(node) {
+ var tokens = context.getTokens(node);
+ return tokens.length && tokens[0].value === '...';
+ }
+
+ /**
+ * Retrieve the name of a key node
+ * @param {ASTNode} node The AST node with the key.
+ * @return {string} the name of the key
+ */
+ function getKeyValue(node) {
+ if (node.type === 'ObjectTypeProperty') {
+ var tokens = context.getFirstTokens(node, 1);
+ return tokens[0].value;
+ }
+ var key = node.key || node.argument;
+ return key.type === 'Identifier' ? key.name : key.value;
+ }
+
+ /**
+ * Iterates through a properties node, like a customized forEach.
+ * @param {Object[]} properties Array of properties to iterate.
+ * @param {Function} fn Function to call on each property, receives property key
+ and property value. (key, value) => void
+ */
+ function iterateProperties(properties, fn) {
+ if (properties.length && typeof fn === 'function') {
+ for (var i = 0, j = properties.length; i < j; i++) {
+ var node = properties[i];
+ var key = getKeyValue(node);
+
+ var value = node.value;
+ fn(key, value);
+ }
+ }
+ }
+
+ /**
+ * Creates the representation of the React propTypes for the component.
+ * The representation is used to verify nested used properties.
+ * @param {ASTNode} value Node of the React.PropTypes for the desired property
+ * @return {Object|Boolean} The representation of the declaration, true means
+ * the property is declared without the need for further analysis.
+ */
+ function buildReactDeclarationTypes(value) {
+ if (
+ value &&
+ value.callee &&
+ value.callee.object &&
+ hasCustomValidator(value.callee.object.name)
+ ) {
+ return true;
+ }
+
+ if (
+ value &&
+ value.type === 'MemberExpression' &&
+ value.property &&
+ value.property.name &&
+ value.property.name === 'isRequired'
+ ) {
+ value = value.object;
+ }
+
+ // Verify React.PropTypes that are functions
+ if (
+ value &&
+ value.type === 'CallExpression' &&
+ value.callee &&
+ value.callee.property &&
+ value.callee.property.name &&
+ value.arguments &&
+ value.arguments.length > 0
+ ) {
+ var callName = value.callee.property.name;
+ var argument = value.arguments[0];
+ switch (callName) {
+ case 'shape':
+ if (argument.type !== 'ObjectExpression') {
+ // Invalid proptype or cannot analyse statically
+ return true;
+ }
+ var shapeTypeDefinition = {
+ type: 'shape',
+ children: {}
+ };
+ iterateProperties(argument.properties, function(childKey, childValue) {
+ shapeTypeDefinition.children[childKey] = buildReactDeclarationTypes(childValue);
+ });
+ return shapeTypeDefinition;
+ case 'arrayOf':
+ case 'objectOf':
+ return {
+ type: 'object',
+ children: {
+ __ANY_KEY__: buildReactDeclarationTypes(argument)
+ }
+ };
+ case 'oneOfType':
+ if (
+ !argument.elements ||
+ !argument.elements.length
+ ) {
+ // Invalid proptype or cannot analyse statically
+ return true;
+ }
+ var unionTypeDefinition = {
+ type: 'union',
+ children: []
+ };
+ for (var i = 0, j = argument.elements.length; i < j; i++) {
+ var type = buildReactDeclarationTypes(argument.elements[i]);
+ // keep only complex type
+ if (type !== true) {
+ if (type.children === true) {
+ // every child is accepted for one type, abort type analysis
+ unionTypeDefinition.children = true;
+ return unionTypeDefinition;
+ }
+ }
+
+ unionTypeDefinition.children.push(type);
+ }
+ if (unionTypeDefinition.length === 0) {
+ // no complex type found, simply accept everything
+ return true;
+ }
+ return unionTypeDefinition;
+ case 'instanceOf':
+ return {
+ type: 'instance',
+ // Accept all children because we can't know what type they are
+ children: true
+ };
+ case 'oneOf':
+ default:
+ return true;
+ }
+ }
+ // Unknown property or accepts everything (any, object, ...)
+ return true;
+ }
+
+ /**
+ * Creates the representation of the React props type annotation for the component.
+ * The representation is used to verify nested used properties.
+ * @param {ASTNode} annotation Type annotation for the props class property.
+ * @return {Object|Boolean} The representation of the declaration, true means
+ * the property is declared without the need for further analysis.
+ */
+ function buildTypeAnnotationDeclarationTypes(annotation) {
+ switch (annotation.type) {
+ case 'GenericTypeAnnotation':
+ if (typeScope(annotation.id.name)) {
+ return buildTypeAnnotationDeclarationTypes(typeScope(annotation.id.name));
+ }
+ return true;
+ case 'ObjectTypeAnnotation':
+ var shapeTypeDefinition = {
+ type: 'shape',
+ children: {}
+ };
+ iterateProperties(annotation.properties, function(childKey, childValue) {
+ shapeTypeDefinition.children[childKey] = buildTypeAnnotationDeclarationTypes(childValue);
+ });
+ return shapeTypeDefinition;
+ case 'UnionTypeAnnotation':
+ var unionTypeDefinition = {
+ type: 'union',
+ children: []
+ };
+ for (var i = 0, j = annotation.types.length; i < j; i++) {
+ var type = buildTypeAnnotationDeclarationTypes(annotation.types[i]);
+ // keep only complex type
+ if (type !== true) {
+ if (type.children === true) {
+ // every child is accepted for one type, abort type analysis
+ unionTypeDefinition.children = true;
+ return unionTypeDefinition;
+ }
+ }
+
+ unionTypeDefinition.children.push(type);
+ }
+ if (unionTypeDefinition.children.length === 0) {
+ // no complex type found, simply accept everything
+ return true;
+ }
+ return unionTypeDefinition;
+ case 'ArrayTypeAnnotation':
+ return {
+ type: 'object',
+ children: {
+ __ANY_KEY__: buildTypeAnnotationDeclarationTypes(annotation.elementType)
+ }
+ };
+ default:
+ // Unknown or accepts everything.
+ return true;
+ }
+ }
+
+ /**
+ * Check if we are in a class constructor
+ * @return {boolean} true if we are in a class constructor, false if not
+ */
+ function inConstructor() {
+ var scope = context.getScope();
+ while (scope) {
+ if (scope.block && scope.block.parent && scope.block.parent.kind === 'constructor') {
+ return true;
+ }
+ scope = scope.upper;
+ }
+ return false;
+ }
+
+ /**
+ * Retrieve the name of a property node
+ * @param {ASTNode} node The AST node with the property.
+ * @return {string} the name of the property or undefined if not found
+ */
+ function getPropertyName(node) {
+ var isDirectProp = /^props(\.|\[)/.test(context.getSource(node));
+ var isInClassComponent = utils.getParentES6Component() || utils.getParentES5Component();
+ var isNotInConstructor = !inConstructor(node);
+ if (isDirectProp && isInClassComponent && isNotInConstructor) {
+ return void 0;
+ }
+ if (!isDirectProp) {
+ node = node.parent;
+ }
+ var property = node.property;
+ if (property) {
+ switch (property.type) {
+ case 'Identifier':
+ if (node.computed) {
+ return '__COMPUTED_PROP__';
+ }
+ return property.name;
+ case 'MemberExpression':
+ return void 0;
+ case 'Literal':
+ // Accept computed properties that are literal strings
+ if (typeof property.value === 'string') {
+ return property.value;
+ }
+ // falls through
+ default:
+ if (node.computed) {
+ return '__COMPUTED_PROP__';
+ }
+ break;
+ }
+ }
+ return void 0;
+ }
+
+ /**
+ * Mark a prop type as used
+ * @param {ASTNode} node The AST node being marked.
+ */
+ function markPropTypesAsUsed(node, parentNames) {
+ parentNames = parentNames || [];
+ var type;
+ var name;
+ var allNames;
+ var properties;
+ switch (node.type) {
+ case 'MemberExpression':
+ name = getPropertyName(node);
+ if (name) {
+ allNames = parentNames.concat(name);
+ if (node.parent.type === 'MemberExpression') {
+ markPropTypesAsUsed(node.parent, allNames);
+ }
+ // Do not mark computed props as used.
+ type = name !== '__COMPUTED_PROP__' ? 'direct' : null;
+ } else if (
+ node.parent.id &&
+ node.parent.id.properties &&
+ node.parent.id.properties.length &&
+ getKeyValue(node.parent.id.properties[0])
+ ) {
+ type = 'destructuring';
+ properties = node.parent.id.properties;
+ }
+ break;
+ case 'VariableDeclarator':
+ for (var i = 0, j = node.id.properties.length; i < j; i++) {
+ // let {props: {firstname}} = this
+ var thisDestructuring = (
+ (node.id.properties[i].key.name === 'props' || node.id.properties[i].key.value === 'props') &&
+ node.id.properties[i].value.type === 'ObjectPattern'
+ );
+ // let {firstname} = props
+ var statelessDestructuring = node.init.name === 'props' && utils.getParentStatelessComponent();
+
+ if (thisDestructuring) {
+ properties = node.id.properties[i].value.properties;
+ } else if (statelessDestructuring) {
+ properties = node.id.properties;
+ } else {
+ continue;
+ }
+ type = 'destructuring';
+ break;
+ }
+ break;
+ default:
+ throw new Error(node.type + ' ASTNodes are not handled by markPropTypesAsUsed');
+ }
+
+ var component = components.get(utils.getParentComponent());
+ var usedPropTypes = component && component.usedPropTypes || [];
+
+ switch (type) {
+ case 'direct':
+ // Ignore Object methods
+ if (Object.prototype[name]) {
+ break;
+ }
+
+ var isDirectProp = /^props(\.|\[)/.test(context.getSource(node));
+
+ usedPropTypes.push({
+ name: name,
+ allNames: allNames,
+ node: !isDirectProp && !inConstructor(node) ? node.parent.property : node.property
+ });
+ break;
+ case 'destructuring':
+ for (var k = 0, l = properties.length; k < l; k++) {
+ if (hasSpreadOperator(properties[k]) || properties[k].computed) {
+ continue;
+ }
+ var propName = getKeyValue(properties[k]);
+
+ var currentNode = node;
+ allNames = [];
+ while (currentNode.property && currentNode.property.name !== 'props') {
+ allNames.unshift(currentNode.property.name);
+ currentNode = currentNode.object;
+ }
+ allNames.push(propName);
+
+ if (propName) {
+ usedPropTypes.push({
+ name: propName,
+ allNames: allNames,
+ node: properties[k]
+ });
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ components.set(node, {
+ usedPropTypes: usedPropTypes
+ });
+ }
+
+ /**
+ * Mark a prop type as declared
+ * @param {ASTNode} node The AST node being checked.
+ * @param {propTypes} node The AST node containing the proptypes
+ */
+ function markPropTypesAsDeclared(node, propTypes) {
+ var component = components.get(node);
+ var declaredPropTypes = component && component.declaredPropTypes || {};
+ var ignorePropsValidation = false;
+
+ switch (propTypes && propTypes.type) {
+ case 'ObjectTypeAnnotation':
+ iterateProperties(propTypes.properties, function(key, value) {
+ declaredPropTypes[key] = buildTypeAnnotationDeclarationTypes(value);
+ });
+ break;
+ case 'ObjectExpression':
+ iterateProperties(propTypes.properties, function(key, value) {
+ if (!value) {
+ ignorePropsValidation = true;
+ return;
+ }
+ declaredPropTypes[key] = buildReactDeclarationTypes(value);
+ });
+ break;
+ case 'MemberExpression':
+ var curDeclaredPropTypes = declaredPropTypes;
+ // Walk the list of properties, until we reach the assignment
+ // ie: ClassX.propTypes.a.b.c = ...
+ while (
+ propTypes &&
+ propTypes.parent &&
+ propTypes.parent.type !== 'AssignmentExpression' &&
+ propTypes.property &&
+ curDeclaredPropTypes
+ ) {
+ var propName = propTypes.property.name;
+ if (propName in curDeclaredPropTypes) {
+ curDeclaredPropTypes = curDeclaredPropTypes[propName].children;
+ propTypes = propTypes.parent;
+ } else {
+ // This will crash at runtime because we haven't seen this key before
+ // stop this and do not declare it
+ propTypes = null;
+ }
+ }
+ if (propTypes && propTypes.parent && propTypes.property) {
+ curDeclaredPropTypes[propTypes.property.name] =
+ buildReactDeclarationTypes(propTypes.parent.right);
+ }
+ break;
+ case 'Identifier':
+ var variablesInScope = variable.variablesInScope(context);
+ for (var i = 0, j = variablesInScope.length; i < j; i++) {
+ if (variablesInScope[i].name !== propTypes.name) {
+ continue;
+ }
+ var defInScope = variablesInScope[i].defs[variablesInScope[i].defs.length - 1];
+ markPropTypesAsDeclared(node, defInScope.node && defInScope.node.init);
+ return;
+ }
+ ignorePropsValidation = true;
+ break;
+ case null:
+ break;
+ default:
+ ignorePropsValidation = true;
+ break;
+ }
+
+ components.set(node, {
+ declaredPropTypes: declaredPropTypes,
+ ignorePropsValidation: ignorePropsValidation
+ });
+ }
+
+ /**
+ * Reports undeclared proptypes for a given component
+ * @param {Object} component The component to process
+ */
+ function reportUndeclaredPropTypes(component) {
+ var allNames;
+ for (var i = 0, j = component.usedPropTypes.length; i < j; i++) {
+ allNames = component.usedPropTypes[i].allNames;
+ if (
+ isIgnored(allNames[0]) ||
+ isDeclaredInComponent(component.node, allNames)
+ ) {
+ continue;
+ }
+ context.report(
+ component.usedPropTypes[i].node,
+ MISSING_MESSAGE, {
+ name: allNames.join('.').replace(/\.__COMPUTED_PROP__/g, '[]')
+ }
+ );
+ }
+ }
+
+ /**
+ * Resolve the type annotation for a given node.
+ * Flow annotations are sometimes wrapped in outer `TypeAnnotation`
+ * and `NullableTypeAnnotation` nodes which obscure the annotation we're
+ * interested in.
+ * This method also resolves type aliases where possible.
+ *
+ * @param {ASTNode} node The annotation or a node containing the type annotation.
+ * @returns {ASTNode} The resolved type annotation for the node.
+ */
+ function resolveTypeAnnotation(node) {
+ var annotation = node.typeAnnotation || node;
+ while (annotation && (annotation.type === 'TypeAnnotation' || annotation.type === 'NullableTypeAnnotation')) {
+ annotation = annotation.typeAnnotation;
+ }
+ if (annotation.type === 'GenericTypeAnnotation' && typeScope(annotation.id.name)) {
+ return typeScope(annotation.id.name);
+ }
+ return annotation;
+ }
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+ ClassProperty: function(node) {
+ if (isAnnotatedPropsDeclaration(node)) {
+ markPropTypesAsDeclared(node, resolveTypeAnnotation(node));
+ } else if (isPropTypesDeclaration(node)) {
+ markPropTypesAsDeclared(node, node.value);
+ }
+ },
+
+ VariableDeclarator: function(node) {
+ var destructuring = node.init && node.id && node.id.type === 'ObjectPattern';
+ // let {props: {firstname}} = this
+ var thisDestructuring = destructuring && node.init.type === 'ThisExpression';
+ // let {firstname} = props
+ var statelessDestructuring = destructuring && node.init.name === 'props' && utils.getParentStatelessComponent();
+
+ if (!thisDestructuring && !statelessDestructuring) {
+ return;
+ }
+ markPropTypesAsUsed(node);
+ },
+
+ MemberExpression: function(node) {
+ var type;
+ if (isPropTypesUsage(node)) {
+ type = 'usage';
+ } else if (isPropTypesDeclaration(node.property)) {
+ type = 'declaration';
+ }
+
+ switch (type) {
+ case 'usage':
+ markPropTypesAsUsed(node);
+ break;
+ case 'declaration':
+ var component = utils.getRelatedComponent(node);
+ if (!component) {
+ return;
+ }
+ markPropTypesAsDeclared(component.node, node.parent.right || node.parent);
+ break;
+ default:
+ break;
+ }
+ },
+
+ MethodDefinition: function(node) {
+ if (!isPropTypesDeclaration(node.key)) {
+ return;
+ }
+
+ var i = node.value.body.body.length - 1;
+ for (; i >= 0; i--) {
+ if (node.value.body.body[i].type === 'ReturnStatement') {
+ break;
+ }
+ }
+
+ if (i >= 0) {
+ markPropTypesAsDeclared(node, node.value.body.body[i].argument);
+ }
+ },
+
+ ObjectExpression: function(node) {
+ // Search for the proptypes declaration
+ node.properties.forEach(function(property) {
+ if (!isPropTypesDeclaration(property.key)) {
+ return;
+ }
+ markPropTypesAsDeclared(node, property.value);
+ });
+ },
+
+ TypeAlias: function(node) {
+ typeScope(node.id.name, node.right);
+ },
+
+ Program: function() {
+ stack = [{}];
+ },
+
+ BlockStatement: function () {
+ stack.push(Object.create(typeScope()));
+ },
+
+ 'BlockStatement:exit': function () {
+ stack.pop();
+ },
+
+ 'Program:exit': function() {
+ stack = null;
+ var list = components.list();
+ // Report undeclared proptypes for all classes
+ for (var component in list) {
+ if (!list.hasOwnProperty(component) || !mustBeValidated(list[component])) {
+ continue;
+ }
+ reportUndeclaredPropTypes(list[component]);
+ }
+ }
+ };
+
+});
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ ignore: {
+ type: 'array',
+ items: {
+ type: 'string'
+ }
+ },
+ customValidators: {
+ type: 'array',
+ items: {
+ type: 'string'
+ }
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.js b/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.js
new file mode 100644
index 0000000..550fddc
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.js
@@ -0,0 +1,39 @@
+/**
+ * @fileoverview Prevent missing React when using JSX
+ * @author Glen Mailer
+ */
+'use strict';
+
+var variableUtil = require('../util/variable');
+var pragmaUtil = require('../util/pragma');
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ var pragma = pragmaUtil.getFromContext(context);
+ var NOT_DEFINED_MESSAGE = '\'{{name}}\' must be in scope when using JSX';
+
+ return {
+
+ JSXOpeningElement: function(node) {
+ var variables = variableUtil.variablesInScope(context);
+ if (variableUtil.findVariable(variables, pragma)) {
+ return;
+ }
+ context.report(node, NOT_DEFINED_MESSAGE, {
+ name: pragma
+ });
+ },
+
+ BlockComment: function(node) {
+ pragma = pragmaUtil.getFromNode(node) || pragma;
+ }
+
+ };
+
+};
+
+module.exports.schema = [];
diff --git a/node_modules/eslint-plugin-react/lib/rules/require-extension.js b/node_modules/eslint-plugin-react/lib/rules/require-extension.js
new file mode 100644
index 0000000..3edbf12
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/require-extension.js
@@ -0,0 +1,85 @@
+/**
+ * @fileoverview Restrict file extensions that may be required
+ * @author Scott Andrews
+ */
+'use strict';
+
+var path = require('path');
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+var DEFAULTS = {
+ extentions: ['.jsx']
+};
+
+var PKG_REGEX = /^[^\.]((?!\/).)*$/;
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ function isPackage(id) {
+ return PKG_REGEX.test(id);
+ }
+
+ function isRequire(expression) {
+ return expression.callee.name === 'require';
+ }
+
+ function getId(expression) {
+ return expression.arguments[0] && expression.arguments[0].value;
+ }
+
+ function getExtension(id) {
+ return path.extname(id || '');
+ }
+
+ function getExtentionsConfig() {
+ return context.options[0] && context.options[0].extensions || DEFAULTS.extentions;
+ }
+
+ var forbiddenExtensions = getExtentionsConfig().reduce(function (extensions, extension) {
+ extensions[extension] = true;
+ return extensions;
+ }, Object.create(null));
+
+ function isForbiddenExtension(ext) {
+ return ext in forbiddenExtensions;
+ }
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+
+ CallExpression: function(node) {
+ if (isRequire(node)) {
+ var id = getId(node);
+ var ext = getExtension(id);
+ if (!isPackage(id) && isForbiddenExtension(ext)) {
+ context.report(node, 'Unable to require module with extension \'' + ext + '\'');
+ }
+ }
+ }
+
+ };
+
+};
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ extensions: {
+ type: 'array',
+ items: {
+ type: 'string'
+ }
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.js b/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.js
new file mode 100644
index 0000000..37adfc1
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.js
@@ -0,0 +1,49 @@
+/**
+ * @fileoverview Prevent extra closing tags for components without children
+ * @author Yannick Croissant
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ var tagConvention = /^[a-z]|\-/;
+ function isTagName(name) {
+ return tagConvention.test(name);
+ }
+
+ function isComponent(node) {
+ return node.name && node.name.type === 'JSXIdentifier' && !isTagName(node.name.name);
+ }
+
+ function hasChildren(node) {
+ var childrens = node.parent.children;
+ if (
+ !childrens.length ||
+ (childrens.length === 1 && childrens[0].type === 'Literal' && !childrens[0].value.trim())
+ ) {
+ return false;
+ }
+ return true;
+ }
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+
+ JSXOpeningElement: function(node) {
+ if (!isComponent(node) || node.selfClosing || hasChildren(node)) {
+ return;
+ }
+ context.report(node, 'Empty components are self-closing');
+ }
+ };
+
+};
+
+module.exports.schema = [];
diff --git a/node_modules/eslint-plugin-react/lib/rules/sort-comp.js b/node_modules/eslint-plugin-react/lib/rules/sort-comp.js
new file mode 100644
index 0000000..93d6cff
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/sort-comp.js
@@ -0,0 +1,376 @@
+/**
+ * @fileoverview Enforce component methods order
+ * @author Yannick Croissant
+ */
+'use strict';
+
+var util = require('util');
+
+var Components = require('../util/Components');
+
+/**
+ * Get the methods order from the default config and the user config
+ * @param {Object} defaultConfig The default configuration.
+ * @param {Object} userConfig The user configuration.
+ * @returns {Array} Methods order
+ */
+function getMethodsOrder(defaultConfig, userConfig) {
+ userConfig = userConfig || {};
+
+ var groups = util._extend(defaultConfig.groups, userConfig.groups);
+ var order = userConfig.order || defaultConfig.order;
+
+ var config = [];
+ var entry;
+ for (var i = 0, j = order.length; i < j; i++) {
+ entry = order[i];
+ if (groups.hasOwnProperty(entry)) {
+ config = config.concat(groups[entry]);
+ } else {
+ config.push(entry);
+ }
+ }
+
+ return config;
+}
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = Components.detect(function(context, components) {
+
+ var errors = {};
+
+ var MISPOSITION_MESSAGE = '{{propA}} should be placed {{position}} {{propB}}';
+
+ var methodsOrder = getMethodsOrder({
+ order: [
+ 'lifecycle',
+ 'everything-else',
+ 'render'
+ ],
+ groups: {
+ lifecycle: [
+ 'displayName',
+ 'propTypes',
+ 'contextTypes',
+ 'childContextTypes',
+ 'mixins',
+ 'statics',
+ 'defaultProps',
+ 'constructor',
+ 'getDefaultProps',
+ 'state',
+ 'getInitialState',
+ 'getChildContext',
+ 'componentWillMount',
+ 'componentDidMount',
+ 'componentWillReceiveProps',
+ 'shouldComponentUpdate',
+ 'componentWillUpdate',
+ 'componentDidUpdate',
+ 'componentWillUnmount'
+ ]
+ }
+ }, context.options[0]);
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ var regExpRegExp = /\/(.*)\/([g|y|i|m]*)/;
+
+ /**
+ * Get indexes of the matching patterns in methods order configuration
+ * @param {String} method - Method name.
+ * @returns {Array} The matching patterns indexes. Return [Infinity] if there is no match.
+ */
+ function getRefPropIndexes(method) {
+ var isRegExp;
+ var matching;
+ var i;
+ var j;
+ var indexes = [];
+ for (i = 0, j = methodsOrder.length; i < j; i++) {
+ isRegExp = methodsOrder[i].match(regExpRegExp);
+ if (isRegExp) {
+ matching = new RegExp(isRegExp[1], isRegExp[2]).test(method);
+ } else {
+ matching = methodsOrder[i] === method;
+ }
+ if (matching) {
+ indexes.push(i);
+ }
+ }
+
+ // No matching pattern, return 'everything-else' index
+ if (indexes.length === 0) {
+ for (i = 0, j = methodsOrder.length; i < j; i++) {
+ if (methodsOrder[i] === 'everything-else') {
+ indexes.push(i);
+ }
+ }
+ }
+
+ // No matching pattern and no 'everything-else' group
+ if (indexes.length === 0) {
+ indexes.push(Infinity);
+ }
+
+ return indexes;
+ }
+
+ /**
+ * Get properties name
+ * @param {Object} node - Property.
+ * @returns {String} Property name.
+ */
+ function getPropertyName(node) {
+
+ // Special case for class properties
+ // (babel-eslint does not expose property name so we have to rely on tokens)
+ if (node.type === 'ClassProperty') {
+ var tokens = context.getFirstTokens(node, 2);
+ return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value;
+ }
+
+ return node.key.name;
+ }
+
+ /**
+ * Store a new error in the error list
+ * @param {Object} propA - Mispositioned property.
+ * @param {Object} propB - Reference property.
+ */
+ function storeError(propA, propB) {
+ // Initialize the error object if needed
+ if (!errors[propA.index]) {
+ errors[propA.index] = {
+ node: propA.node,
+ score: 0,
+ closest: {
+ distance: Infinity,
+ ref: {
+ node: null,
+ index: 0
+ }
+ }
+ };
+ }
+ // Increment the prop score
+ errors[propA.index].score++;
+ // Stop here if we already have a closer reference
+ if (Math.abs(propA.index - propB.index) > errors[propA.index].closest.distance) {
+ return;
+ }
+ // Update the closest reference
+ errors[propA.index].closest.distance = Math.abs(propA.index - propB.index);
+ errors[propA.index].closest.ref.node = propB.node;
+ errors[propA.index].closest.ref.index = propB.index;
+ }
+
+ /**
+ * Dedupe errors, only keep the ones with the highest score and delete the others
+ */
+ function dedupeErrors() {
+ for (var i in errors) {
+ if (!errors.hasOwnProperty(i)) {
+ continue;
+ }
+ var index = errors[i].closest.ref.index;
+ if (!errors[index]) {
+ continue;
+ }
+ if (errors[i].score > errors[index].score) {
+ delete errors[index];
+ } else {
+ delete errors[i];
+ }
+ }
+ }
+
+ /**
+ * Report errors
+ */
+ function reportErrors() {
+ dedupeErrors();
+
+ var nodeA;
+ var nodeB;
+ var indexA;
+ var indexB;
+ for (var i in errors) {
+ if (!errors.hasOwnProperty(i)) {
+ continue;
+ }
+
+ nodeA = errors[i].node;
+ nodeB = errors[i].closest.ref.node;
+ indexA = i;
+ indexB = errors[i].closest.ref.index;
+
+ context.report(nodeA, MISPOSITION_MESSAGE, {
+ propA: getPropertyName(nodeA),
+ propB: getPropertyName(nodeB),
+ position: indexA < indexB ? 'before' : 'after'
+ });
+ }
+ }
+
+ /**
+ * Get properties for a given AST node
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Array} Properties array.
+ */
+ function getComponentProperties(node) {
+ switch (node.type) {
+ case 'ClassDeclaration':
+ return node.body.body;
+ case 'ObjectExpression':
+ return node.properties;
+ default:
+ return [];
+ }
+ }
+
+ /**
+ * Compare two properties and find out if they are in the right order
+ * @param {Array} propertiesNames Array containing all the properties names.
+ * @param {String} propA First property name.
+ * @param {String} propB Second property name.
+ * @returns {Object} Object containing a correct true/false flag and the correct indexes for the two properties.
+ */
+ function comparePropsOrder(propertiesNames, propA, propB) {
+ var i;
+ var j;
+ var k;
+ var l;
+ var refIndexA;
+ var refIndexB;
+
+ // Get references indexes (the correct position) for given properties
+ var refIndexesA = getRefPropIndexes(propA);
+ var refIndexesB = getRefPropIndexes(propB);
+
+ // Get current indexes for given properties
+ var classIndexA = propertiesNames.indexOf(propA);
+ var classIndexB = propertiesNames.indexOf(propB);
+
+ // Loop around the references indexes for the 1st property
+ for (i = 0, j = refIndexesA.length; i < j; i++) {
+ refIndexA = refIndexesA[i];
+
+ // Loop around the properties for the 2nd property (for comparison)
+ for (k = 0, l = refIndexesB.length; k < l; k++) {
+ refIndexB = refIndexesB[k];
+
+ if (
+ // Comparing the same properties
+ refIndexA === refIndexB ||
+ // 1st property is placed before the 2nd one in reference and in current component
+ refIndexA < refIndexB && classIndexA < classIndexB ||
+ // 1st property is placed after the 2nd one in reference and in current component
+ refIndexA > refIndexB && classIndexA > classIndexB
+ ) {
+ return {
+ correct: true,
+ indexA: classIndexA,
+ indexB: classIndexB
+ };
+ }
+
+ }
+ }
+
+ // We did not find any correct match between reference and current component
+ return {
+ correct: false,
+ indexA: refIndexA,
+ indexB: refIndexB
+ };
+ }
+
+ /**
+ * Check properties order from a properties list and store the eventual errors
+ * @param {Array} properties Array containing all the properties.
+ */
+ function checkPropsOrder(properties) {
+ var propertiesNames = properties.map(getPropertyName);
+ var i;
+ var j;
+ var k;
+ var l;
+ var propA;
+ var propB;
+ var order;
+
+ // Loop around the properties
+ for (i = 0, j = propertiesNames.length; i < j; i++) {
+ propA = propertiesNames[i];
+
+ // Loop around the properties a second time (for comparison)
+ for (k = 0, l = propertiesNames.length; k < l; k++) {
+ propB = propertiesNames[k];
+
+ // Compare the properties order
+ order = comparePropsOrder(propertiesNames, propA, propB);
+
+ // Continue to next comparison is order is correct
+ if (order.correct === true) {
+ continue;
+ }
+
+ // Store an error if the order is incorrect
+ storeError({
+ node: properties[i],
+ index: order.indexA
+ }, {
+ node: properties[k],
+ index: order.indexB
+ });
+ }
+ }
+
+ }
+
+ return {
+ 'Program:exit': function() {
+ var list = components.list();
+ for (var component in list) {
+ if (!list.hasOwnProperty(component)) {
+ continue;
+ }
+ var properties = getComponentProperties(list[component].node);
+ checkPropsOrder(properties);
+ }
+
+ reportErrors();
+ }
+ };
+
+});
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ order: {
+ type: 'array',
+ items: {
+ type: 'string'
+ }
+ },
+ groups: {
+ type: 'object',
+ patternProperties: {
+ '^.*$': {
+ type: 'array',
+ items: {
+ type: 'string'
+ }
+ }
+ }
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/rules/wrap-multilines.js b/node_modules/eslint-plugin-react/lib/rules/wrap-multilines.js
new file mode 100644
index 0000000..0fc0a73
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/rules/wrap-multilines.js
@@ -0,0 +1,103 @@
+/**
+ * @fileoverview Prevent missing parentheses around multilines JSX
+ * @author Yannick Croissant
+ */
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Constants
+// ------------------------------------------------------------------------------
+
+var DEFAULTS = {
+ declaration: true,
+ assignment: true,
+ return: true
+};
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+module.exports = function(context) {
+
+ var sourceCode = context.getSourceCode();
+
+ function isParenthesised(node) {
+ var previousToken = context.getTokenBefore(node);
+ var nextToken = context.getTokenAfter(node);
+
+ return previousToken && nextToken &&
+ previousToken.value === '(' && previousToken.range[1] <= node.range[0] &&
+ nextToken.value === ')' && nextToken.range[0] >= node.range[1];
+ }
+
+ function isMultilines(node) {
+ return node.loc.start.line !== node.loc.end.line;
+ }
+
+ function check(node) {
+ if (!node || node.type !== 'JSXElement') {
+ return;
+ }
+
+ if (!isParenthesised(node) && isMultilines(node)) {
+ context.report({
+ node: node,
+ message: 'Missing parentheses around multilines JSX',
+ fix: function(fixer) {
+ return fixer.replaceText(node, '(' + sourceCode.getText(node) + ')');
+ }
+ });
+ }
+ }
+
+ function isEnabled(type) {
+ var userOptions = context.options[0] || {};
+ if (({}).hasOwnProperty.call(userOptions, type)) {
+ return userOptions[type];
+ }
+ return DEFAULTS[type];
+ }
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ return {
+
+ VariableDeclarator: function(node) {
+ if (isEnabled('declaration')) {
+ check(node.init);
+ }
+ },
+
+ AssignmentExpression: function(node) {
+ if (isEnabled('assignment')) {
+ check(node.right);
+ }
+ },
+
+ ReturnStatement: function(node) {
+ if (isEnabled('return')) {
+ check(node.argument);
+ }
+ }
+ };
+
+};
+
+module.exports.schema = [{
+ type: 'object',
+ properties: {
+ declaration: {
+ type: 'boolean'
+ },
+ assignment: {
+ type: 'boolean'
+ },
+ return: {
+ type: 'boolean'
+ }
+ },
+ additionalProperties: false
+}];
diff --git a/node_modules/eslint-plugin-react/lib/util/Components.js b/node_modules/eslint-plugin-react/lib/util/Components.js
new file mode 100644
index 0000000..b0e190a
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/util/Components.js
@@ -0,0 +1,412 @@
+/**
+ * @fileoverview Utility class and functions for React components detection
+ * @author Yannick Croissant
+ */
+'use strict';
+
+var util = require('util');
+var variableUtil = require('./variable');
+var pragmaUtil = require('./pragma');
+
+/**
+ * Components
+ * @class
+ */
+function Components() {
+ this._list = {};
+ this._getId = function(node) {
+ return node && node.range.join(':');
+ };
+}
+
+/**
+ * Add a node to the components list, or update it if it's already in the list
+ *
+ * @param {ASTNode} node The AST node being added.
+ * @param {Number} confidence Confidence in the component detection (0=banned, 1=maybe, 2=yes)
+ */
+Components.prototype.add = function(node, confidence) {
+ var id = this._getId(node);
+ if (this._list[id]) {
+ if (confidence === 0 || this._list[id].confidence === 0) {
+ this._list[id].confidence = 0;
+ } else {
+ this._list[id].confidence = Math.max(this._list[id].confidence, confidence);
+ }
+ return;
+ }
+ this._list[id] = {
+ node: node,
+ confidence: confidence
+ };
+};
+
+/**
+ * Find a component in the list using its node
+ *
+ * @param {ASTNode} node The AST node being searched.
+ * @returns {Object} Component object, undefined if the component is not found
+ */
+Components.prototype.get = function(node) {
+ var id = this._getId(node);
+ return this._list[id];
+};
+
+/**
+ * Update a component in the list
+ *
+ * @param {ASTNode} node The AST node being updated.
+ * @param {Object} props Additional properties to add to the component.
+ */
+Components.prototype.set = function(node, props) {
+ while (node && !this._list[this._getId(node)]) {
+ node = node.parent;
+ }
+ if (!node) {
+ return;
+ }
+ var id = this._getId(node);
+ this._list[id] = util._extend(this._list[id], props);
+};
+
+/**
+ * Return the components list
+ * Components for which we are not confident are not returned
+ *
+ * @returns {Object} Components list
+ */
+Components.prototype.list = function() {
+ var list = {};
+ for (var i in this._list) {
+ if (!this._list.hasOwnProperty(i) || this._list[i].confidence < 2) {
+ continue;
+ }
+ list[i] = this._list[i];
+ }
+ return list;
+};
+
+/**
+ * Return the length of the components list
+ * Components for which we are not confident are not counted
+ *
+ * @returns {Number} Components list length
+ */
+Components.prototype.length = function() {
+ var length = 0;
+ for (var i in this._list) {
+ if (!this._list.hasOwnProperty(i) || this._list[i].confidence < 2) {
+ continue;
+ }
+ length++;
+ }
+ return length;
+};
+
+function componentRule(rule, context) {
+
+ var pragma = pragmaUtil.getFromContext(context);
+ var sourceCode = context.getSourceCode();
+ var components = new Components();
+
+ // Utilities for component detection
+ var utils = {
+
+ /**
+ * Check if the node is a React ES5 component
+ *
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Boolean} True if the node is a React ES5 component, false if not
+ */
+ isES5Component: function(node) {
+ if (!node.parent) {
+ return false;
+ }
+ return new RegExp('^(' + pragma + '\\.)?createClass$').test(sourceCode.getText(node.parent.callee));
+ },
+
+ /**
+ * Check if the node is a React ES6 component
+ *
+ * @param {ASTNode} node The AST node being checked.
+ * @returns {Boolean} True if the node is a React ES6 component, false if not
+ */
+ isES6Component: function(node) {
+ if (!node.superClass) {
+ return false;
+ }
+ return new RegExp('^(' + pragma + '\\.)?Component$').test(sourceCode.getText(node.superClass));
+ },
+
+ /**
+ * Check if the node is returning JSX
+ *
+ * @param {ASTNode} node The AST node being checked (must be a ReturnStatement).
+ * @returns {Boolean} True if the node is returning JSX, false if not
+ */
+ isReturningJSX: function(node) {
+ var property;
+ switch (node.type) {
+ case 'ReturnStatement':
+ property = 'argument';
+ break;
+ case 'ArrowFunctionExpression':
+ property = 'body';
+ break;
+ default:
+ return false;
+ }
+
+ var returnsJSX =
+ node[property] &&
+ node[property].type === 'JSXElement'
+ ;
+ var returnsReactCreateElement =
+ node[property] &&
+ node[property].callee &&
+ node[property].callee.property &&
+ node[property].callee.property.name === 'createElement'
+ ;
+
+ return Boolean(returnsJSX || returnsReactCreateElement);
+ },
+
+ /**
+ * Get the parent component node from the current scope
+ *
+ * @returns {ASTNode} component node, null if we are not in a component
+ */
+ getParentComponent: function() {
+ return (
+ utils.getParentES6Component() ||
+ utils.getParentES5Component() ||
+ utils.getParentStatelessComponent()
+ );
+ },
+
+ /**
+ * Get the parent ES5 component node from the current scope
+ *
+ * @returns {ASTNode} component node, null if we are not in a component
+ */
+ getParentES5Component: function() {
+ var scope = context.getScope();
+ while (scope) {
+ var node = scope.block && scope.block.parent && scope.block.parent.parent;
+ if (node && utils.isES5Component(node)) {
+ return node;
+ }
+ scope = scope.upper;
+ }
+ return null;
+ },
+
+ /**
+ * Get the parent ES6 component node from the current scope
+ *
+ * @returns {ASTNode} component node, null if we are not in a component
+ */
+ getParentES6Component: function() {
+ var scope = context.getScope();
+ while (scope && scope.type !== 'class') {
+ scope = scope.upper;
+ }
+ var node = scope && scope.block;
+ if (!node || !utils.isES6Component(node)) {
+ return null;
+ }
+ return node;
+ },
+
+ /**
+ * Get the parent stateless component node from the current scope
+ *
+ * @returns {ASTNode} component node, null if we are not in a component
+ */
+ getParentStatelessComponent: function() {
+ var scope = context.getScope();
+ while (scope) {
+ var node = scope.block;
+ var isFunction = /Function/.test(node.type); // Ignore non functions
+ var isNotMethod = !node.parent || node.parent.type !== 'MethodDefinition'; // Ignore classes methods
+ var isNotArgument = !node.parent || node.parent.type !== 'CallExpression'; // Ignore arguments (callback, etc.)
+ if (isFunction && isNotMethod && isNotArgument) {
+ return node;
+ }
+ scope = scope.upper;
+ }
+ return null;
+ },
+
+ /**
+ * Get the related component from a node
+ *
+ * @param {ASTNode} node The AST node being checked (must be a MemberExpression).
+ * @returns {ASTNode} component node, null if we cannot find the component
+ */
+ getRelatedComponent: function(node) {
+ var i;
+ var j;
+ var k;
+ var l;
+ // Get the component path
+ var componentPath = [];
+ while (node) {
+ if (node.property && node.property.type === 'Identifier') {
+ componentPath.push(node.property.name);
+ }
+ if (node.object && node.object.type === 'Identifier') {
+ componentPath.push(node.object.name);
+ }
+ node = node.object;
+ }
+ componentPath.reverse();
+
+ // Find the variable in the current scope
+ var variableName = componentPath.shift();
+ if (!variableName) {
+ return null;
+ }
+ var variableInScope;
+ var variables = variableUtil.variablesInScope(context);
+ for (i = 0, j = variables.length; i < j; i++) {
+ if (variables[i].name === variableName) {
+ variableInScope = variables[i];
+ break;
+ }
+ }
+ if (!variableInScope) {
+ return null;
+ }
+
+ // Find the variable declaration
+ var defInScope;
+ var defs = variableInScope.defs;
+ for (i = 0, j = defs.length; i < j; i++) {
+ if (defs[i].type === 'ClassName' || defs[i].type === 'FunctionName' || defs[i].type === 'Variable') {
+ defInScope = defs[i];
+ break;
+ }
+ }
+ if (!defInScope || !defInScope.node) {
+ return null;
+ }
+ node = defInScope.node.init || defInScope.node;
+
+ // Traverse the node properties to the component declaration
+ for (i = 0, j = componentPath.length; i < j; i++) {
+ if (!node.properties) {
+ continue;
+ }
+ for (k = 0, l = node.properties.length; k < l; k++) {
+ if (node.properties[k].key.name === componentPath[i]) {
+ node = node.properties[k];
+ break;
+ }
+ }
+ if (!node || !node.value) {
+ return null;
+ }
+ node = node.value;
+ }
+
+ // Return the component
+ return components.get(node);
+ }
+ };
+
+ // Component detection instructions
+ var detectionInstructions = {
+ ClassDeclaration: function(node) {
+ if (!utils.isES6Component(node)) {
+ return;
+ }
+ components.add(node, 2);
+ },
+
+ ClassProperty: function(node) {
+ node = utils.getParentComponent();
+ if (!node) {
+ return;
+ }
+ components.add(node, 2);
+ },
+
+ ObjectExpression: function(node) {
+ if (!utils.isES5Component(node)) {
+ return;
+ }
+ components.add(node, 2);
+ },
+
+ FunctionExpression: function(node) {
+ node = utils.getParentComponent();
+ if (!node) {
+ return;
+ }
+ components.add(node, 1);
+ },
+
+ FunctionDeclaration: function(node) {
+ node = utils.getParentComponent();
+ if (!node) {
+ return;
+ }
+ components.add(node, 1);
+ },
+
+ ArrowFunctionExpression: function(node) {
+ node = utils.getParentComponent();
+ if (!node) {
+ return;
+ }
+ if (node.expression && utils.isReturningJSX(node)) {
+ components.add(node, 2);
+ } else {
+ components.add(node, 1);
+ }
+ },
+
+ ThisExpression: function(node) {
+ node = utils.getParentComponent();
+ if (!node || !/Function/.test(node.type)) {
+ return;
+ }
+ // Ban functions with a ThisExpression
+ components.add(node, 0);
+ },
+
+ BlockComment: function(node) {
+ pragma = pragmaUtil.getFromNode(node) || pragma;
+ },
+
+ ReturnStatement: function(node) {
+ if (!utils.isReturningJSX(node)) {
+ return;
+ }
+ node = utils.getParentComponent();
+ if (!node) {
+ return;
+ }
+ components.add(node, 2);
+ }
+ };
+
+ // Update the provided rule instructions to add the component detection
+ var ruleInstructions = rule(context, components, utils);
+ var updatedRuleInstructions = util._extend({}, ruleInstructions);
+ Object.keys(detectionInstructions).forEach(function(instruction) {
+ updatedRuleInstructions[instruction] = function(node) {
+ detectionInstructions[instruction](node);
+ return ruleInstructions[instruction] ? ruleInstructions[instruction](node) : void 0;
+ };
+ });
+ // Return the updated rule instructions
+ return updatedRuleInstructions;
+}
+
+Components.detect = function(rule) {
+ return componentRule.bind(this, rule);
+};
+
+module.exports = Components;
diff --git a/node_modules/eslint-plugin-react/lib/util/pragma.js b/node_modules/eslint-plugin-react/lib/util/pragma.js
new file mode 100644
index 0000000..479257f
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/util/pragma.js
@@ -0,0 +1,32 @@
+/**
+ * @fileoverview Utility functions for React pragma configuration
+ * @author Yannick Croissant
+ */
+'use strict';
+
+var JSX_ANNOTATION_REGEX = /^\*\s*@jsx\s+([^\s]+)/;
+
+function getFromContext(context) {
+ var pragma = 'React';
+ // .eslintrc shared settings (http://eslint.org/docs/user-guide/configuring#adding-shared-settings)
+ if (context.settings.react && context.settings.react.pragma) {
+ pragma = context.settings.react.pragma;
+ // Deprecated pragma option, here for backward compatibility
+ } else if (context.options[0] && context.options[0].pragma) {
+ pragma = context.options[0].pragma;
+ }
+ return pragma.split('.')[0];
+}
+
+function getFromNode(node) {
+ var matches = JSX_ANNOTATION_REGEX.exec(node.value);
+ if (!matches) {
+ return false;
+ }
+ return matches[1].split('.')[0];
+}
+
+module.exports = {
+ getFromContext: getFromContext,
+ getFromNode: getFromNode
+};
diff --git a/node_modules/eslint-plugin-react/lib/util/variable.js b/node_modules/eslint-plugin-react/lib/util/variable.js
new file mode 100644
index 0000000..5ba2f62
--- /dev/null
+++ b/node_modules/eslint-plugin-react/lib/util/variable.js
@@ -0,0 +1,92 @@
+/**
+ * @fileoverview Utility functions for React components detection
+ * @author Yannick Croissant
+ */
+'use strict';
+
+/**
+ * Record that a particular variable has been used in code
+ *
+ * @param {Object} context The current rule context.
+ * @param {String} name The name of the variable to mark as used.
+ * @returns {Boolean} True if the variable was found and marked as used, false if not.
+ */
+function markVariableAsUsed(context, name) {
+ var scope = context.getScope();
+ var variables;
+ var i;
+ var len;
+ var found = false;
+
+ // Special Node.js scope means we need to start one level deeper
+ if (scope.type === 'global') {
+ while (scope.childScopes.length) {
+ scope = scope.childScopes[0];
+ }
+ }
+
+ do {
+ variables = scope.variables;
+ for (i = 0, len = variables.length; i < len; i++) {
+ if (variables[i].name === name) {
+ variables[i].eslintUsed = true;
+ found = true;
+ }
+ }
+ scope = scope.upper;
+ } while (scope);
+
+ return found;
+}
+
+/**
+ * Search a particular variable in a list
+ * @param {Array} variables The variables list.
+ * @param {Array} name The name of the variable to search.
+ * @returns {Boolean} True if the variable was found, false if not.
+ */
+function findVariable(variables, name) {
+ var i;
+ var len;
+
+ for (i = 0, len = variables.length; i < len; i++) {
+ if (variables[i].name === name) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * List all variable in a given scope
+ *
+ * Contain a patch for babel-eslint to avoid https://github.com/babel/babel-eslint/issues/21
+ *
+ * @param {Object} context The current rule context.
+ * @param {Array} name The name of the variable to search.
+ * @returns {Boolean} True if the variable was found, false if not.
+ */
+function variablesInScope(context) {
+ var scope = context.getScope();
+ var variables = scope.variables;
+
+ while (scope.type !== 'global') {
+ scope = scope.upper;
+ variables = scope.variables.concat(variables);
+ }
+ if (scope.childScopes.length) {
+ variables = scope.childScopes[0].variables.concat(variables);
+ if (scope.childScopes[0].childScopes.length) {
+ variables = scope.childScopes[0].childScopes[0].variables.concat(variables);
+ }
+ }
+
+ return variables;
+}
+
+module.exports = {
+ findVariable: findVariable,
+ variablesInScope: variablesInScope,
+ markVariableAsUsed: markVariableAsUsed
+};
diff --git a/node_modules/eslint-plugin-react/package.json b/node_modules/eslint-plugin-react/package.json
new file mode 100644
index 0000000..3cb4a57
--- /dev/null
+++ b/node_modules/eslint-plugin-react/package.json
@@ -0,0 +1,67 @@
+{
+ "name": "eslint-plugin-react",
+ "version": "3.16.1",
+ "author": {
+ "name": "Yannick Croissant",
+ "email": "yannick.croissant+npm@gmail.com"
+ },
+ "description": "React specific linting rules for ESLint",
+ "main": "index.js",
+ "scripts": {
+ "coveralls": "cat ./reports/coverage/lcov.info | coveralls",
+ "lint": "eslint ./",
+ "test": "npm run lint && npm run unit-test",
+ "unit-test": "istanbul cover --dir reports/coverage node_modules/mocha/bin/_mocha tests/**/*.js -- --reporter dot"
+ },
+ "files": [
+ "LICENSE",
+ "README.md",
+ "index.js",
+ "lib"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/yannickcr/eslint-plugin-react.git"
+ },
+ "homepage": "https://github.com/yannickcr/eslint-plugin-react",
+ "bugs": {
+ "url": "https://github.com/yannickcr/eslint-plugin-react/issues"
+ },
+ "devDependencies": {
+ "babel-eslint": "5.0.0-beta6",
+ "coveralls": "2.11.6",
+ "eslint": "2.0.0-beta.2",
+ "istanbul": "0.4.2",
+ "mocha": "2.3.4"
+ },
+ "keywords": [
+ "eslint",
+ "eslint-plugin",
+ "eslintplugin",
+ "react"
+ ],
+ "license": "MIT",
+ "gitHead": "c06ebd84bd5e5655a687d0fd7a150b3c10bf8c93",
+ "_id": "eslint-plugin-react@3.16.1",
+ "_shasum": "262d96b77d7c4a42af809a73c0e527a58612293c",
+ "_from": "eslint-plugin-react@*",
+ "_npmVersion": "3.2.2",
+ "_nodeVersion": "4.0.0",
+ "_npmUser": {
+ "name": "yannickcr",
+ "email": "yannick.croissant+npm@gmail.com"
+ },
+ "dist": {
+ "shasum": "262d96b77d7c4a42af809a73c0e527a58612293c",
+ "tarball": "http://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-3.16.1.tgz"
+ },
+ "maintainers": [
+ {
+ "name": "yannickcr",
+ "email": "yannick.croissant+npm@gmail.com"
+ }
+ ],
+ "directories": {},
+ "_resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-3.16.1.tgz",
+ "readme": "ERROR: No README data found!"
+}
diff --git a/node_modules/eslint/LICENSE b/node_modules/eslint/LICENSE
new file mode 100644
index 0000000..3f7b4ba
--- /dev/null
+++ b/node_modules/eslint/LICENSE
@@ -0,0 +1,20 @@
+ESLint
+Copyright (c) 2013 Nicholas C. Zakas. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/node_modules/eslint/README.md b/node_modules/eslint/README.md
new file mode 100644
index 0000000..099a0df
--- /dev/null
+++ b/node_modules/eslint/README.md
@@ -0,0 +1,126 @@
+[![NPM version][npm-image]][npm-url]
+[![build status][travis-image]][travis-url]
+[![Test coverage][coveralls-image]][coveralls-url]
+[![Downloads][downloads-image]][downloads-url]
+[](https://www.bountysource.com/trackers/282608-eslint?utm_source=282608&utm_medium=shield&utm_campaign=TRACKER_BADGE)
+[](https://gitter.im/eslint/eslint?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+# ESLint
+
+[Website](http://eslint.org) | [Configuring](http://eslint.org/docs/user-guide/configuring) | [Rules](http://eslint.org/docs/rules/) | [Contributing](http://eslint.org/docs/developer-guide/contributing) | [Twitter](https://twitter.com/geteslint) | [Mailing List](https://groups.google.com/group/eslint)
+
+ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code. In many ways, it is similar to JSLint and JSHint with a few exceptions:
+
+* ESLint uses [Espree](https://github.com/eslint/espree) for JavaScript parsing.
+* ESLint uses an AST to evaluate patterns in code.
+* ESLint is completely pluggable, every single rule is a plugin and you can add more at runtime.
+
+## Installation
+
+You can install ESLint using npm:
+
+ npm install -g eslint
+
+## Usage
+
+If it's your first time using ESLint, you should set up a config file using `--init`:
+
+ eslint --init
+
+After that, you can run ESLint on any JavaScript file:
+
+ eslint test.js test2.js
+
+## Configuration
+
+After running `eslint --init`, you'll have a `.eslintrc` file in your directory. In it, you'll see some rules configured like this:
+
+```json
+{
+ "rules": {
+ "semi": [2, "always"],
+ "quotes": [2, "double"]
+ }
+}
+```
+
+The names `"semi"` and `"quotes"` are the names of [rules](http://eslint.org/docs/rules) in ESLint. The number is the error level of the rule and can be one of the three values:
+
+* `0` - turn the rule off
+* `1` - turn the rule on as a warning (doesn't affect exit code)
+* `2` - turn the rule on as an error (exit code will be 1)
+
+The three error levels allow you fine-grained control over how ESLint applies rules (for more configuration options and details, see the [configuration docs](http://eslint.org/docs/user-guide/configuring)).
+
+## Sponsors
+
+* Development is sponsored by [Box](https://box.com)
+
+## Team
+
+These folks keep the project moving and are resources for help:
+
+* Nicholas C. Zakas ([@nzakas](https://github.com/nzakas)) - project lead
+* Ilya Volodin ([@ilyavolodin](https://github.com/ilyavolodin)) - reviewer
+* Brandon Mills ([@btmills](https://github.com/btmills)) - reviewer
+* Gyandeep Singh ([@gyandeeps](https://github.com/gyandeeps)) - reviewer
+* Mathias Schreck ([@lo1tuma](https://github.com/lo1tuma)) - committer
+* Jamund Ferguson ([@xjamundx](https://github.com/xjamundx)) - committer
+* Ian VanSchooten ([@ianvs](https://github.com/ianvs)) - committer
+* Toru Nagashima ([@mysticatea](https://github.com/mysticatea)) - committer
+* Burak Yiğit Kaya ([@byk](https://github.com/byk)) - committer
+* Alberto Rodríguez ([@alberto](https://github.com/alberto)) - committer
+
+## Releases
+
+We have scheduled releases every two weeks on Friday or Saturday.
+
+## Frequently Asked Questions
+
+### Why don't you like JSHint???
+
+I do like JSHint. And I like Anton and Rick. Neither of those were deciding factors in creating this tool. The fact is that I've had a dire need for a JavaScript tool with pluggable linting rules. I had hoped JSHint would be able to do this, however after chatting with Anton, I found that the planned plugin infrastructure wasn't going to suit my purpose.
+
+### I'm not giving up JSHint for this!
+
+That's not really a question, but I got it. I'm not trying to convince you that ESLint is better than JSHint. The only thing I know is that ESLint is better than JSHint for what I'm doing. In the off chance you're doing something similar, it might be better for you. Otherwise, keep using JSHint, I'm certainly not going to tell you to stop using it.
+
+### How does ESLint performance compare to JSHint and JSCS?
+
+ESLint is slower than JSHint, usually 2-3x slower on a single file. This is because ESLint uses Espree to construct an AST before it can evaluate your code whereas JSHint evaluates your code as it's being parsed. The speed is also based on the number of rules you enable; the more rules you enable, the slower the process.
+
+Despite being slower, we believe that ESLint is fast enough to replace JSHint without causing significant pain.
+
+ESLint is faster than JSCS, as ESLint uses a single-pass traversal for analysis whereas JSCS using a querying model.
+
+If you are using both JSHint and JSCS on your files, then using just ESLint will be faster.
+
+### Is ESLint just linting or does it also check style?
+
+ESLint does both traditional linting (looking for problematic patterns) and style checking (enforcement of conventions). You can use it for both.
+
+### What about ECMAScript 6 support?
+
+ESLint has full support for ECMAScript 6. By default, this support is off. You can enable ECMAScript 6 support through [configuration](http://eslint.org/docs/user-guide/configuring).
+
+### Does ESLint support JSX?
+
+Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [configuration](http://eslint.org/docs/user-guide/configuring).). Please note that supporting JSX syntax *is not* the same as supporting React. React applies specific semantics to JSX syntax that ESLint doesn't recognize. We recommend using [eslint-plugin-react](https://www.npmjs.com/package/eslint-plugin-react) if you are using React and want React semantics.
+
+### What about ECMAScript 7/2016 and experimental features?
+
+ESLint doesn't natively support experimental ECMAScript language features. You can use [babel-eslint](https://github.com/babel/babel-eslint) to use any option available in Babel.
+
+### Where to ask for help?
+
+Join our [Mailing List](https://groups.google.com/group/eslint) or [Chatroom](https://gitter.im/eslint/eslint)
+
+
+[npm-image]: https://img.shields.io/npm/v/eslint.svg?style=flat-square
+[npm-url]: https://www.npmjs.com/package/eslint
+[travis-image]: https://img.shields.io/travis/eslint/eslint/master.svg?style=flat-square
+[travis-url]: https://travis-ci.org/eslint/eslint
+[coveralls-image]: https://img.shields.io/coveralls/eslint/eslint/master.svg?style=flat-square
+[coveralls-url]: https://coveralls.io/r/eslint/eslint?branch=master
+[downloads-image]: https://img.shields.io/npm/dm/eslint.svg?style=flat-square
+[downloads-url]: https://www.npmjs.com/package/eslint
diff --git a/node_modules/eslint/bin/eslint.js b/node_modules/eslint/bin/eslint.js
new file mode 100755
index 0000000..8c7bce9
--- /dev/null
+++ b/node_modules/eslint/bin/eslint.js
@@ -0,0 +1,69 @@
+#!/usr/bin/env node
+
+/**
+ * @fileoverview Main CLI that is run via the eslint command.
+ * @author Nicholas C. Zakas
+ * @copyright 2013 Nicholas C. Zakas. All rights reserved.
+ * See LICENSE file in root directory for full license.
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+var exitCode = 0,
+ useStdIn = (process.argv.indexOf("--stdin") > -1),
+ init = (process.argv.indexOf("--init") > -1),
+ debug = (process.argv.indexOf("--debug") > -1);
+
+// must do this initialization *before* other requires in order to work
+if (debug) {
+ require("debug").enable("eslint:*");
+}
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+// now we can safely include the other modules that use debug
+var concat = require("concat-stream"),
+ cli = require("../lib/cli");
+
+//------------------------------------------------------------------------------
+// Execution
+//------------------------------------------------------------------------------
+
+if (useStdIn) {
+ process.stdin.pipe(concat({ encoding: "string" }, function(text) {
+ try {
+ exitCode = cli.execute(process.argv, text);
+ } catch (ex) {
+ console.error(ex.message);
+ console.error(ex.stack);
+ exitCode = 1;
+ }
+ }));
+} else if (init) {
+ var configInit = require("../lib/config/config-initializer");
+ configInit.initializeConfig(function(err) {
+ if (err) {
+ exitCode = 1;
+ console.error(err.message);
+ console.error(err.stack);
+ } else {
+ exitCode = 0;
+ }
+ });
+} else {
+ exitCode = cli.execute(process.argv);
+}
+
+/*
+ * Wait for the stdout buffer to drain.
+ * See https://github.com/eslint/eslint/issues/317
+ */
+process.on("exit", function() {
+ process.exit(exitCode);
+});
diff --git a/node_modules/eslint/conf/blank-script.json b/node_modules/eslint/conf/blank-script.json
new file mode 100644
index 0000000..d7d7d37
--- /dev/null
+++ b/node_modules/eslint/conf/blank-script.json
@@ -0,0 +1,21 @@
+{
+ "type": "Program",
+ "body": [],
+ "sourceType": "script",
+ "range": [
+ 0,
+ 0
+ ],
+ "loc": {
+ "start": {
+ "line": 0,
+ "column": 0
+ },
+ "end": {
+ "line": 0,
+ "column": 0
+ }
+ },
+ "comments": [],
+ "tokens": []
+}
diff --git a/node_modules/eslint/conf/environments.js b/node_modules/eslint/conf/environments.js
new file mode 100644
index 0000000..f66aebc
--- /dev/null
+++ b/node_modules/eslint/conf/environments.js
@@ -0,0 +1,114 @@
+/**
+ * @fileoverview Defines environment settings and globals.
+ * @author Elan Shanker
+ * @copyright 2014 Elan Shanker. All rights reserved.
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var globals = require("globals");
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+ builtin: globals.builtin,
+ browser: {
+ globals: globals.browser
+ },
+ node: {
+ globals: globals.node,
+ ecmaFeatures: {
+ globalReturn: true
+ }
+ },
+ commonjs: {
+ globals: globals.commonjs,
+ ecmaFeatures: {
+ globalReturn: true
+ }
+ },
+ worker: {
+ globals: globals.worker
+ },
+ amd: {
+ globals: globals.amd
+ },
+ mocha: {
+ globals: globals.mocha
+ },
+ jasmine: {
+ globals: globals.jasmine
+ },
+ jest: {
+ globals: globals.jest
+ },
+ phantomjs: {
+ globals: globals.phantomjs
+ },
+ jquery: {
+ globals: globals.jquery
+ },
+ qunit: {
+ globals: globals.qunit
+ },
+ prototypejs: {
+ globals: globals.prototypejs
+ },
+ shelljs: {
+ globals: globals.shelljs
+ },
+ meteor: {
+ globals: globals.meteor
+ },
+ mongo: {
+ globals: globals.mongo
+ },
+ protractor: {
+ globals: globals.protractor
+ },
+ applescript: {
+ globals: globals.applescript
+ },
+ nashorn: {
+ globals: globals.nashorn
+ },
+ serviceworker: {
+ globals: globals.serviceworker
+ },
+ embertest: {
+ globals: globals.embertest
+ },
+ webextensions: {
+ globals: globals.webextensions
+ },
+ es6: {
+ ecmaFeatures: {
+ arrowFunctions: true,
+ blockBindings: true,
+ regexUFlag: true,
+ regexYFlag: true,
+ templateStrings: true,
+ binaryLiterals: true,
+ octalLiterals: true,
+ unicodeCodePointEscapes: true,
+ superInFunctions: true,
+ defaultParams: true,
+ restParams: true,
+ forOf: true,
+ objectLiteralComputedProperties: true,
+ objectLiteralShorthandMethods: true,
+ objectLiteralShorthandProperties: true,
+ objectLiteralDuplicateProperties: true,
+ generators: true,
+ destructuring: true,
+ classes: true,
+ spread: true,
+ newTarget: true
+ }
+ }
+};
diff --git a/node_modules/eslint/conf/eslint.json b/node_modules/eslint/conf/eslint.json
new file mode 100644
index 0000000..4984ba8
--- /dev/null
+++ b/node_modules/eslint/conf/eslint.json
@@ -0,0 +1,196 @@
+{
+ "parser": "espree",
+ "ecmaFeatures": {},
+ "rules": {
+ "no-alert": 0,
+ "no-array-constructor": 0,
+ "no-arrow-condition": 0,
+ "no-bitwise": 0,
+ "no-caller": 0,
+ "no-case-declarations": 0,
+ "no-catch-shadow": 0,
+ "no-class-assign": 0,
+ "no-cond-assign": 2,
+ "no-console": 2,
+ "no-const-assign": 0,
+ "no-constant-condition": 2,
+ "no-continue": 0,
+ "no-control-regex": 2,
+ "no-debugger": 2,
+ "no-delete-var": 2,
+ "no-div-regex": 0,
+ "no-dupe-class-members": 0,
+ "no-dupe-keys": 2,
+ "no-dupe-args": 2,
+ "no-duplicate-case": 2,
+ "no-else-return": 0,
+ "no-empty": 2,
+ "no-empty-character-class": 2,
+ "no-empty-label": 0,
+ "no-empty-pattern": 0,
+ "no-eq-null": 0,
+ "no-eval": 0,
+ "no-ex-assign": 2,
+ "no-extend-native": 0,
+ "no-extra-bind": 0,
+ "no-extra-boolean-cast": 2,
+ "no-extra-parens": 0,
+ "no-extra-semi": 2,
+ "no-fallthrough": 2,
+ "no-floating-decimal": 0,
+ "no-func-assign": 2,
+ "no-implicit-coercion": 0,
+ "no-implied-eval": 0,
+ "no-inline-comments": 0,
+ "no-inner-declarations": [2, "functions"],
+ "no-invalid-regexp": 2,
+ "no-invalid-this": 0,
+ "no-irregular-whitespace": 2,
+ "no-iterator": 0,
+ "no-label-var": 0,
+ "no-labels": 0,
+ "no-lone-blocks": 0,
+ "no-lonely-if": 0,
+ "no-loop-func": 0,
+ "no-mixed-requires": [0, false],
+ "no-mixed-spaces-and-tabs": [2, false],
+ "linebreak-style": [0, "unix"],
+ "no-multi-spaces": 0,
+ "no-multi-str": 0,
+ "no-multiple-empty-lines": [0, {"max": 2}],
+ "no-native-reassign": 0,
+ "no-negated-condition": 0,
+ "no-negated-in-lhs": 2,
+ "no-nested-ternary": 0,
+ "no-new": 0,
+ "no-new-func": 0,
+ "no-new-object": 0,
+ "no-new-require": 0,
+ "no-new-wrappers": 0,
+ "no-obj-calls": 2,
+ "no-octal": 2,
+ "no-octal-escape": 0,
+ "no-param-reassign": 0,
+ "no-path-concat": 0,
+ "no-plusplus": 0,
+ "no-process-env": 0,
+ "no-process-exit": 0,
+ "no-proto": 0,
+ "no-redeclare": 2,
+ "no-regex-spaces": 2,
+ "no-restricted-modules": 0,
+ "no-restricted-syntax": 0,
+ "no-return-assign": 0,
+ "no-script-url": 0,
+ "no-self-compare": 0,
+ "no-sequences": 0,
+ "no-shadow": 0,
+ "no-shadow-restricted-names": 0,
+ "no-spaced-func": 0,
+ "no-sparse-arrays": 2,
+ "no-sync": 0,
+ "no-ternary": 0,
+ "no-trailing-spaces": 0,
+ "no-this-before-super": 0,
+ "no-throw-literal": 0,
+ "no-undef": 2,
+ "no-undef-init": 0,
+ "no-undefined": 0,
+ "no-unexpected-multiline": 0,
+ "no-underscore-dangle": 0,
+ "no-unneeded-ternary": 0,
+ "no-unreachable": 2,
+ "no-unused-expressions": 0,
+ "no-unused-vars": [2, {"vars": "all", "args": "after-used"}],
+ "no-use-before-define": 0,
+ "no-useless-call": 0,
+ "no-useless-concat": 0,
+ "no-void": 0,
+ "no-var": 0,
+ "no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],
+ "no-with": 0,
+ "no-magic-numbers": 0,
+
+ "array-bracket-spacing": [0, "never"],
+ "arrow-body-style": [0, "as-needed"],
+ "arrow-parens": 0,
+ "arrow-spacing": 0,
+ "accessor-pairs": 0,
+ "block-scoped-var": 0,
+ "block-spacing": 0,
+ "brace-style": [0, "1tbs"],
+ "callback-return": 0,
+ "camelcase": 0,
+ "comma-dangle": [2, "never"],
+ "comma-spacing": 0,
+ "comma-style": 0,
+ "complexity": [0, 11],
+ "computed-property-spacing": [0, "never"],
+ "consistent-return": 0,
+ "consistent-this": [0, "that"],
+ "constructor-super": 0,
+ "curly": [0, "all"],
+ "default-case": 0,
+ "dot-location": 0,
+ "dot-notation": [0, { "allowKeywords": true }],
+ "eol-last": 0,
+ "eqeqeq": 0,
+ "func-names": 0,
+ "func-style": [0, "declaration"],
+ "generator-star-spacing": 0,
+ "global-require": 0,
+ "guard-for-in": 0,
+ "handle-callback-err": 0,
+ "id-length": 0,
+ "indent": 0,
+ "init-declarations": 0,
+ "jsx-quotes": [0, "prefer-double"],
+ "key-spacing": [0, { "beforeColon": false, "afterColon": true }],
+ "lines-around-comment": 0,
+ "max-depth": [0, 4],
+ "max-len": [0, 80, 4],
+ "max-nested-callbacks": [0, 2],
+ "max-params": [0, 3],
+ "max-statements": [0, 10],
+ "new-cap": 0,
+ "new-parens": 0,
+ "newline-after-var": 0,
+ "object-curly-spacing": [0, "never"],
+ "object-shorthand": 0,
+ "one-var": [0, "always"],
+ "operator-assignment": [0, "always"],
+ "operator-linebreak": 0,
+ "padded-blocks": 0,
+ "prefer-arrow-callback": 0,
+ "prefer-const": 0,
+ "prefer-spread": 0,
+ "prefer-reflect": 0,
+ "prefer-template": 0,
+ "quote-props": 0,
+ "quotes": [0, "double"],
+ "radix": 0,
+ "id-match": 0,
+ "require-jsdoc": 0,
+ "require-yield": 0,
+ "semi": 0,
+ "semi-spacing": [0, {"before": false, "after": true}],
+ "sort-vars": 0,
+ "space-after-keywords": [0, "always"],
+ "space-before-keywords": [0, "always"],
+ "space-before-blocks": [0, "always"],
+ "space-before-function-paren": [0, "always"],
+ "space-in-parens": [0, "never"],
+ "space-infix-ops": 0,
+ "space-return-throw-case": 0,
+ "space-unary-ops": [0, { "words": true, "nonwords": false }],
+ "spaced-comment": 0,
+ "strict": 0,
+ "use-isnan": 2,
+ "valid-jsdoc": 0,
+ "valid-typeof": 2,
+ "vars-on-top": 0,
+ "wrap-iife": 0,
+ "wrap-regex": 0,
+ "yoda": [0, "never"]
+ }
+}
diff --git a/node_modules/eslint/conf/json-schema-schema.json b/node_modules/eslint/conf/json-schema-schema.json
new file mode 100644
index 0000000..85eb502
--- /dev/null
+++ b/node_modules/eslint/conf/json-schema-schema.json
@@ -0,0 +1,150 @@
+{
+ "id": "http://json-schema.org/draft-04/schema#",
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "description": "Core schema meta-schema",
+ "definitions": {
+ "schemaArray": {
+ "type": "array",
+ "minItems": 1,
+ "items": { "$ref": "#" }
+ },
+ "positiveInteger": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "positiveIntegerDefault0": {
+ "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]
+ },
+ "simpleTypes": {
+ "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
+ },
+ "stringArray": {
+ "type": "array",
+ "items": { "type": "string" },
+ "minItems": 1,
+ "uniqueItems": true
+ }
+ },
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "format": "uri"
+ },
+ "$schema": {
+ "type": "string",
+ "format": "uri"
+ },
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "default": {},
+ "multipleOf": {
+ "type": "number",
+ "minimum": 0,
+ "exclusiveMinimum": true
+ },
+ "maximum": {
+ "type": "number"
+ },
+ "exclusiveMaximum": {
+ "type": "boolean",
+ "default": false
+ },
+ "minimum": {
+ "type": "number"
+ },
+ "exclusiveMinimum": {
+ "type": "boolean",
+ "default": false
+ },
+ "maxLength": { "$ref": "#/definitions/positiveInteger" },
+ "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
+ "pattern": {
+ "type": "string",
+ "format": "regex"
+ },
+ "additionalItems": {
+ "anyOf": [
+ { "type": "boolean" },
+ { "$ref": "#" }
+ ],
+ "default": {}
+ },
+ "items": {
+ "anyOf": [
+ { "$ref": "#" },
+ { "$ref": "#/definitions/schemaArray" }
+ ],
+ "default": {}
+ },
+ "maxItems": { "$ref": "#/definitions/positiveInteger" },
+ "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
+ "uniqueItems": {
+ "type": "boolean",
+ "default": false
+ },
+ "maxProperties": { "$ref": "#/definitions/positiveInteger" },
+ "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
+ "required": { "$ref": "#/definitions/stringArray" },
+ "additionalProperties": {
+ "anyOf": [
+ { "type": "boolean" },
+ { "$ref": "#" }
+ ],
+ "default": {}
+ },
+ "definitions": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "properties": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "patternProperties": {
+ "type": "object",
+ "additionalProperties": { "$ref": "#" },
+ "default": {}
+ },
+ "dependencies": {
+ "type": "object",
+ "additionalProperties": {
+ "anyOf": [
+ { "$ref": "#" },
+ { "$ref": "#/definitions/stringArray" }
+ ]
+ }
+ },
+ "enum": {
+ "type": "array",
+ "minItems": 1,
+ "uniqueItems": true
+ },
+ "type": {
+ "anyOf": [
+ { "$ref": "#/definitions/simpleTypes" },
+ {
+ "type": "array",
+ "items": { "$ref": "#/definitions/simpleTypes" },
+ "minItems": 1,
+ "uniqueItems": true
+ }
+ ]
+ },
+ "allOf": { "$ref": "#/definitions/schemaArray" },
+ "anyOf": { "$ref": "#/definitions/schemaArray" },
+ "oneOf": { "$ref": "#/definitions/schemaArray" },
+ "not": { "$ref": "#" }
+ },
+ "dependencies": {
+ "exclusiveMaximum": [ "maximum" ],
+ "exclusiveMinimum": [ "minimum" ]
+ },
+ "default": {}
+}
diff --git a/node_modules/eslint/conf/replacements.json b/node_modules/eslint/conf/replacements.json
new file mode 100644
index 0000000..34a09d5
--- /dev/null
+++ b/node_modules/eslint/conf/replacements.json
@@ -0,0 +1,17 @@
+{
+ "rules": {
+ "generator-star": ["generator-star-spacing"],
+ "global-strict": ["strict"],
+ "no-comma-dangle": ["comma-dangle"],
+ "no-empty-class": ["no-empty-character-class"],
+ "no-extra-strict": ["strict"],
+ "no-reserved-keys": ["quote-props"],
+ "no-space-before-semi": ["semi-spacing"],
+ "no-wrap-func": ["no-extra-parens"],
+ "space-after-function-name": ["space-before-function-paren"],
+ "space-before-function-parentheses": ["space-before-function-paren"],
+ "space-in-brackets": ["object-curly-spacing", "array-bracket-spacing", "computed-property-spacing"],
+ "space-unary-word-ops": ["space-unary-ops"],
+ "spaced-line-comment": ["spaced-comment"]
+ }
+}
diff --git a/node_modules/eslint/lib/api.js b/node_modules/eslint/lib/api.js
new file mode 100644
index 0000000..664e9a5
--- /dev/null
+++ b/node_modules/eslint/lib/api.js
@@ -0,0 +1,13 @@
+/**
+ * @fileoverview Expose out ESLint and CLI to require.
+ * @author Ian Christian Myers
+ */
+
+"use strict";
+
+module.exports = {
+ linter: require("./eslint"),
+ CLIEngine: require("./cli-engine"),
+ RuleTester: require("./testers/rule-tester"),
+ SourceCode: require("./util/source-code")
+};
diff --git a/node_modules/eslint/lib/ast-utils.js b/node_modules/eslint/lib/ast-utils.js
new file mode 100644
index 0000000..397d8da
--- /dev/null
+++ b/node_modules/eslint/lib/ast-utils.js
@@ -0,0 +1,154 @@
+/**
+ * @fileoverview Common utils for AST.
+ * @author Gyandeep Singh
+ * @copyright 2015 Gyandeep Singh. All rights reserved.
+ * See LICENSE file in root directory for full license.
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var esutils = require("esutils");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Checks reference if is non initializer and writable.
+ * @param {Reference} reference - A reference to check.
+ * @param {int} index - The index of the reference in the references.
+ * @param {Reference[]} references - The array that the reference belongs to.
+ * @returns {boolean} Success/Failure
+ * @private
+ */
+function isModifyingReference(reference, index, references) {
+ var identifier = reference.identifier;
+
+ return (identifier &&
+ reference.init === false &&
+ reference.isWrite() &&
+ // Destructuring assignments can have multiple default value,
+ // so possibly there are multiple writeable references for the same identifier.
+ (index === 0 || references[index - 1].identifier !== identifier)
+ );
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+
+ /**
+ * Determines whether two adjacent tokens are on the same line.
+ * @param {Object} left - The left token object.
+ * @param {Object} right - The right token object.
+ * @returns {boolean} Whether or not the tokens are on the same line.
+ * @public
+ */
+ isTokenOnSameLine: function(left, right) {
+ return left.loc.end.line === right.loc.start.line;
+ },
+
+ /**
+ * Checks whether or not a node is `null` or `undefined`.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} Whether or not the node is a `null` or `undefined`.
+ * @public
+ */
+ isNullOrUndefined: function(node) {
+ return (
+ (node.type === "Literal" && node.value === null) ||
+ (node.type === "Identifier" && node.name === "undefined") ||
+ (node.type === "UnaryExpression" && node.operator === "void")
+ );
+ },
+
+ /**
+ * Checks whether or not a given node is a string literal.
+ * @param {ASTNode} node - A node to check.
+ * @returns {boolean} `true` if the node is a string literal.
+ */
+ isStringLiteral: function(node) {
+ return (
+ (node.type === "Literal" && typeof node.value === "string") ||
+ node.type === "TemplateLiteral"
+ );
+ },
+
+ /**
+ * Gets references which are non initializer and writable.
+ * @param {Reference[]} references - An array of references.
+ * @returns {Reference[]} An array of only references which are non initializer and writable.
+ * @public
+ */
+ getModifyingReferences: function(references) {
+ return references.filter(isModifyingReference);
+ },
+
+ /**
+ * Validate that a string passed in is surrounded by the specified character
+ * @param {string} val The text to check.
+ * @param {string} character The character to see if it's surrounded by.
+ * @returns {boolean} True if the text is surrounded by the character, false if not.
+ * @private
+ */
+ isSurroundedBy: function(val, character) {
+ return val[0] === character && val[val.length - 1] === character;
+ },
+
+ /**
+ * Returns whether the provided node is an ESLint directive comment or not
+ * @param {LineComment|BlockComment} node The node to be checked
+ * @returns {boolean} `true` if the node is an ESLint directive comment
+ */
+ isDirectiveComment: function(node) {
+ var comment = node.value.trim();
+ return (
+ node.type === "Line" && comment.indexOf("eslint-") === 0 ||
+ node.type === "Block" && (
+ comment.indexOf("global ") === 0 ||
+ comment.indexOf("eslint ") === 0 ||
+ comment.indexOf("eslint-") === 0
+ )
+ );
+ },
+
+ /**
+ * Gets the trailing statement of a given node.
+ *
+ * if (code)
+ * consequent;
+ *
+ * When taking this `IfStatement`, returns `consequent;` statement.
+ *
+ * @param {ASTNode} A node to get.
+ * @returns {ASTNode|null} The trailing statement's node.
+ */
+ getTrailingStatement: esutils.ast.trailingStatement,
+
+ /**
+ * Finds the variable by a given name in a given scope and its upper scopes.
+ *
+ * @param {escope.Scope} initScope - A scope to start find.
+ * @param {string} name - A variable name to find.
+ * @returns {escope.Variable|null} A found variable or `null`.
+ */
+ getVariableByName: function(initScope, name) {
+ var scope = initScope;
+ while (scope) {
+ var variable = scope.set.get(name);
+ if (variable) {
+ return variable;
+ }
+
+ scope = scope.upper;
+ }
+
+ return null;
+ }
+};
diff --git a/node_modules/eslint/lib/cli-engine.js b/node_modules/eslint/lib/cli-engine.js
new file mode 100644
index 0000000..3fff395
--- /dev/null
+++ b/node_modules/eslint/lib/cli-engine.js
@@ -0,0 +1,730 @@
+/**
+ * @fileoverview Main CLI object.
+ * @author Nicholas C. Zakas
+ * @copyright 2014 Nicholas C. Zakas. All rights reserved.
+ * See LICENSE in root directory for full license.
+ */
+
+"use strict";
+
+/*
+ * The CLI object should *not* call process.exit() directly. It should only return
+ * exit codes. This allows other programs to use the CLI object and still control
+ * when the program exits.
+ */
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var fs = require("fs"),
+ path = require("path"),
+
+ assign = require("object-assign"),
+ debug = require("debug"),
+ shell = require("shelljs"),
+
+ rules = require("./rules"),
+ eslint = require("./eslint"),
+ IgnoredPaths = require("./ignored-paths"),
+ Config = require("./config"),
+ util = require("./util"),
+ fileEntryCache = require("file-entry-cache"),
+ globUtil = require("./util/glob-util"),
+ SourceCodeFixer = require("./util/source-code-fixer"),
+ validator = require("./config/config-validator"),
+ stringify = require("json-stable-stringify"),
+
+ crypto = require( "crypto" ),
+ pkg = require("../package.json");
+
+var DEFAULT_PARSER = require("../conf/eslint.json").parser;
+
+//------------------------------------------------------------------------------
+// Typedefs
+//------------------------------------------------------------------------------
+
+/**
+ * The options to configure a CLI engine with.
+ * @typedef {Object} CLIEngineOptions
+ * @property {string} configFile The configuration file to use.
+ * @property {boolean|object} baseConfig Base config object. True enables recommend rules and environments.
+ * @property {boolean} ignore False disables use of .eslintignore.
+ * @property {string[]} rulePaths An array of directories to load custom rules from.
+ * @property {boolean} useEslintrc False disables looking for .eslintrc
+ * @property {string[]} envs An array of environments to load.
+ * @property {string[]} globals An array of global variables to declare.
+ * @property {string[]} extensions An array of file extensions to check.
+ * @property {Object} rules An object of rules to use.
+ * @property {string} ignorePath The ignore file to use instead of .eslintignore.
+ */
+
+/**
+ * A linting warning or error.
+ * @typedef {Object} LintMessage
+ * @property {string} message The message to display to the user.
+ */
+
+/**
+ * A linting result.
+ * @typedef {Object} LintResult
+ * @property {string} filePath The path to the file that was linted.
+ * @property {LintMessage[]} messages All of the messages for the result.
+ */
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+
+var defaultOptions = {
+ configFile: null,
+ baseConfig: false,
+ rulePaths: [],
+ useEslintrc: true,
+ envs: [],
+ globals: [],
+ rules: {},
+ extensions: [".js"],
+ ignore: true,
+ ignorePath: null,
+ parser: DEFAULT_PARSER,
+ cache: false,
+ // in order to honor the cacheFile option if specified
+ // this option should not have a default value otherwise
+ // it will always be used
+ cacheLocation: "",
+ cacheFile: ".eslintcache",
+ fix: false,
+ allowInlineConfig: true
+ },
+ loadedPlugins = Object.create(null);
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+debug = debug("eslint:cli-engine");
+
+/**
+ * Load the given plugins if they are not loaded already.
+ * @param {string[]} pluginNames An array of plugin names which should be loaded.
+ * @returns {void}
+ */
+function loadPlugins(pluginNames) {
+ if (pluginNames) {
+ pluginNames.forEach(function(pluginName) {
+ var pluginNamespace = util.getNamespace(pluginName),
+ pluginNameWithoutNamespace = util.removeNameSpace(pluginName),
+ pluginNameWithoutPrefix = util.removePluginPrefix(pluginNameWithoutNamespace),
+ plugin;
+
+ if (!loadedPlugins[pluginNameWithoutPrefix]) {
+ debug("Load plugin " + pluginNameWithoutPrefix);
+
+ plugin = require(pluginNamespace + util.PLUGIN_NAME_PREFIX + pluginNameWithoutPrefix);
+ // if this plugin has rules, import them
+ if (plugin.rules) {
+ rules.import(plugin.rules, pluginNameWithoutPrefix);
+ }
+
+ loadedPlugins[pluginNameWithoutPrefix] = plugin;
+ }
+ });
+ }
+}
+
+/**
+ * It will calculate the error and warning count for collection of messages per file
+ * @param {Object[]} messages - Collection of messages
+ * @returns {Object} Contains the stats
+ * @private
+ */
+function calculateStatsPerFile(messages) {
+ return messages.reduce(function(stat, message) {
+ if (message.fatal || message.severity === 2) {
+ stat.errorCount++;
+ } else {
+ stat.warningCount++;
+ }
+ return stat;
+ }, {
+ errorCount: 0,
+ warningCount: 0
+ });
+}
+
+/**
+ * It will calculate the error and warning count for collection of results from all files
+ * @param {Object[]} results - Collection of messages from all the files
+ * @returns {Object} Contains the stats
+ * @private
+ */
+function calculateStatsPerRun(results) {
+ return results.reduce(function(stat, result) {
+ stat.errorCount += result.errorCount;
+ stat.warningCount += result.warningCount;
+ return stat;
+ }, {
+ errorCount: 0,
+ warningCount: 0
+ });
+}
+
+/**
+ * Processes an source code using ESLint.
+ * @param {string} text The source code to check.
+ * @param {Object} configHelper The configuration options for ESLint.
+ * @param {string} filename An optional string representing the texts filename.
+ * @param {boolean} fix Indicates if fixes should be processed.
+ * @param {boolean} allowInlineConfig Allow/ignore comments that change config.
+ * @returns {Result} The results for linting on this text.
+ * @private
+ */
+function processText(text, configHelper, filename, fix, allowInlineConfig) {
+
+ // clear all existing settings for a new file
+ eslint.reset();
+
+ var filePath,
+ config,
+ messages,
+ stats,
+ fileExtension = path.extname(filename),
+ processor,
+ fixedResult;
+
+ if (filename) {
+ filePath = path.resolve(filename);
+ }
+
+ filename = filename || "";
+ debug("Linting " + filename);
+ config = configHelper.getConfig(filePath);
+ loadPlugins(config.plugins);
+
+ for (var plugin in loadedPlugins) {
+ if (loadedPlugins[plugin].processors && Object.keys(loadedPlugins[plugin].processors).indexOf(fileExtension) >= 0) {
+ processor = loadedPlugins[plugin].processors[fileExtension];
+ break;
+ }
+ }
+
+ if (processor) {
+ debug("Using processor");
+ var parsedBlocks = processor.preprocess(text, filename);
+ var unprocessedMessages = [];
+ parsedBlocks.forEach(function(block) {
+ unprocessedMessages.push(eslint.verify(block, config, {
+ filename: filename,
+ allowInlineConfig: allowInlineConfig
+ }));
+ });
+
+ // TODO(nzakas): Figure out how fixes might work for processors
+
+ messages = processor.postprocess(unprocessedMessages, filename);
+
+ } else {
+
+ messages = eslint.verify(text, config, {
+ filename: filename,
+ allowInlineConfig: allowInlineConfig
+ });
+
+ if (fix) {
+ debug("Generating fixed text for " + filename);
+ fixedResult = SourceCodeFixer.applyFixes(eslint.getSourceCode(), messages);
+ messages = fixedResult.messages;
+ }
+ }
+
+ stats = calculateStatsPerFile(messages);
+
+ var result = {
+ filePath: filename,
+ messages: messages,
+ errorCount: stats.errorCount,
+ warningCount: stats.warningCount
+ };
+
+ if (fixedResult && fixedResult.fixed) {
+ result.output = fixedResult.output;
+ }
+
+ return result;
+}
+
+/**
+ * Processes an individual file using ESLint. Files used here are known to
+ * exist, so no need to check that here.
+ * @param {string} filename The filename of the file being checked.
+ * @param {Object} configHelper The configuration options for ESLint.
+ * @param {Object} options The CLIEngine options object.
+ * @returns {Result} The results for linting on this file.
+ * @private
+ */
+function processFile(filename, configHelper, options) {
+
+ var text = fs.readFileSync(path.resolve(filename), "utf8"),
+ result = processText(text, configHelper, filename, options.fix, options.allowInlineConfig);
+
+ return result;
+
+}
+
+/**
+ * Returns result with warning by ignore settings
+ * @param {string} filePath File path of checked code
+ * @returns {Result} Result with single warning
+ * @private
+ */
+function createIgnoreResult(filePath) {
+ return {
+ filePath: path.resolve(filePath),
+ messages: [
+ {
+ fatal: false,
+ severity: 1,
+ message: "File ignored because of your .eslintignore file. Use --no-ignore to override."
+ }
+ ],
+ errorCount: 0,
+ warningCount: 1
+ };
+}
+
+
+/**
+ * Checks if the given message is an error message.
+ * @param {object} message The message to check.
+ * @returns {boolean} Whether or not the message is an error message.
+ * @private
+ */
+function isErrorMessage(message) {
+ return message.severity === 2;
+}
+
+/**
+ * create a md5Hash of a given string
+ * @param {string} str the string to calculate the hash for
+ * @returns {string} the calculated hash
+ */
+function md5Hash(str) {
+ return crypto
+ .createHash("md5")
+ .update(str, "utf8")
+ .digest("hex");
+}
+
+/**
+ * return the cacheFile to be used by eslint, based on whether the provided parameter is
+ * a directory or looks like a directory (ends in `path.sep`), in which case the file
+ * name will be the `cacheFile/.cache_hashOfCWD`
+ *
+ * if cacheFile points to a file or looks like a file then in will just use that file
+ *
+ * @param {string} cacheFile The name of file to be used to store the cache
+ * @returns {string} the resolved path to the cache file
+ */
+function getCacheFile(cacheFile) {
+ // make sure the path separators are normalized for the environment/os
+ // keeping the trailing path separator if present
+ cacheFile = path.normalize(cacheFile);
+
+ var resolvedCacheFile = path.resolve(cacheFile);
+ var looksLikeADirectory = cacheFile[cacheFile.length - 1 ] === path.sep;
+
+ /**
+ * return the name for the cache file in case the provided parameter is a directory
+ * @returns {string} the resolved path to the cacheFile
+ */
+ function getCacheFileForDirectory() {
+ return path.join(resolvedCacheFile, ".cache_" + md5Hash(process.cwd()));
+ }
+
+ var fileStats;
+
+ try {
+ fileStats = fs.lstatSync(resolvedCacheFile);
+ } catch (ex) {
+ fileStats = null;
+ }
+
+
+ // in case the file exists we need to verify if the provided path
+ // is a directory or a file. If it is a directory we want to create a file
+ // inside that directory
+ if (fileStats) {
+ // is a directory or is a file, but the original file the user provided
+ // looks like a directory but `path.resolve` removed the `last path.sep`
+ // so we need to still treat this like a directory
+ if (fileStats.isDirectory() || looksLikeADirectory) {
+ return getCacheFileForDirectory();
+ }
+ // is file so just use that file
+ return resolvedCacheFile;
+ }
+
+ // here we known the file or directory doesn't exist,
+ // so we will try to infer if its a directory if it looks like a directory
+ // for the current operating system.
+
+ // if the last character passed is a path separator we assume is a directory
+ if (looksLikeADirectory) {
+ return getCacheFileForDirectory();
+ }
+
+ return resolvedCacheFile;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * Creates a new instance of the core CLI engine.
+ * @param {CLIEngineOptions} options The options for this instance.
+ * @constructor
+ */
+function CLIEngine(options) {
+
+ /**
+ * Stored options for this instance
+ * @type {Object}
+ */
+ this.options = assign(Object.create(defaultOptions), options || {});
+
+
+ var cacheFile = getCacheFile(this.options.cacheLocation || this.options.cacheFile);
+
+ /**
+ * cache used to not operate on files that haven't changed since last successful
+ * execution (e.g. file passed with no errors and no warnings
+ * @type {Object}
+ */
+ this._fileCache = fileEntryCache.create(cacheFile); // eslint-disable-line no-underscore-dangle
+
+ if (!this.options.cache) {
+ this._fileCache.destroy(); // eslint-disable-line no-underscore-dangle
+ }
+
+ // load in additional rules
+ if (this.options.rulePaths) {
+ this.options.rulePaths.forEach(function(rulesdir) {
+ debug("Loading rules from " + rulesdir);
+ rules.load(rulesdir);
+ });
+ }
+
+ Object.keys(this.options.rules || {}).forEach(function(name) {
+ validator.validateRuleOptions(name, this.options.rules[name], "CLI");
+ }.bind(this));
+}
+
+/**
+ * Returns the formatter representing the given format or null if no formatter
+ * with the given name can be found.
+ * @param {string} [format] The name of the format to load or the path to a
+ * custom formatter.
+ * @returns {Function} The formatter function or null if not found.
+ */
+CLIEngine.getFormatter = function(format) {
+
+ var formatterPath;
+
+ // default is stylish
+ format = format || "stylish";
+
+ // only strings are valid formatters
+ if (typeof format === "string") {
+
+ // replace \ with / for Windows compatibility
+ format = format.replace(/\\/g, "/");
+
+ // if there's a slash, then it's a file
+ if (format.indexOf("/") > -1) {
+ formatterPath = path.resolve(process.cwd(), format);
+ } else {
+ formatterPath = "./formatters/" + format;
+ }
+
+ try {
+ return require(formatterPath);
+ } catch (ex) {
+ return null;
+ }
+
+ } else {
+ return null;
+ }
+};
+
+/**
+ * Returns results that only contains errors.
+ * @param {LintResult[]} results The results to filter.
+ * @returns {LintResult[]} The filtered results.
+ */
+CLIEngine.getErrorResults = function(results) {
+ var filtered = [];
+
+ results.forEach(function(result) {
+ var filteredMessages = result.messages.filter(isErrorMessage);
+
+ if (filteredMessages.length > 0) {
+ filtered.push({
+ filePath: result.filePath,
+ messages: filteredMessages
+ });
+ }
+ });
+
+ return filtered;
+};
+
+/**
+ * Outputs fixes from the given results to files.
+ * @param {Object} report The report object created by CLIEngine.
+ * @returns {void}
+ */
+CLIEngine.outputFixes = function(report) {
+ report.results.filter(function(result) {
+ return result.hasOwnProperty("output");
+ }).forEach(function(result) {
+ fs.writeFileSync(result.filePath, result.output);
+ });
+};
+
+CLIEngine.prototype = {
+
+ constructor: CLIEngine,
+
+ /**
+ * Add a plugin by passing it's configuration
+ * @param {string} name Name of the plugin.
+ * @param {Object} pluginobject Plugin configuration object.
+ * @returns {void}
+ */
+ addPlugin: function(name, pluginobject) {
+ var pluginNameWithoutPrefix = util.removePluginPrefix(util.removeNameSpace(name));
+ if (pluginobject.rules) {
+ rules.import(pluginobject.rules, pluginNameWithoutPrefix);
+ }
+ loadedPlugins[pluginNameWithoutPrefix] = pluginobject;
+ },
+
+ /**
+ * Resolves the patterns passed into executeOnFiles() into glob-based patterns
+ * for easier handling.
+ * @param {string[]} patterns The file patterns passed on the command line.
+ * @returns {string[]} The equivalent glob patterns.
+ */
+ resolveFileGlobPatterns: function(patterns) {
+ return globUtil.resolveFileGlobPatterns(patterns, this.options.extensions);
+ },
+
+ /**
+ * Executes the current configuration on an array of file and directory names.
+ * @param {string[]} patterns An array of file and directory names.
+ * @returns {Object} The results for all files that were linted.
+ */
+ executeOnFiles: function(patterns) {
+ var results = [],
+ processed = {},
+ options = this.options,
+ fileCache = this._fileCache, // eslint-disable-line no-underscore-dangle
+ configHelper = new Config(options),
+ stats,
+ startTime,
+ prevConfig; // the previous configuration used
+
+ startTime = Date.now();
+
+ patterns = this.resolveFileGlobPatterns(patterns);
+
+ /**
+ * Calculates the hash of the config file used to validate a given file
+ * @param {string} filename The path of the file to retrieve a config object for to calculate the hash
+ * @returns {string} the hash of the config
+ */
+ function hashOfConfigFor(filename) {
+ var config = configHelper.getConfig(filename);
+
+ if (!prevConfig) {
+ prevConfig = {};
+ }
+
+ // reuse the previously hashed config if the config hasn't changed
+ if (prevConfig.config !== config) {
+ // config changed so we need to calculate the hash of the config
+ // and the hash of the plugins being used
+ prevConfig.config = config;
+
+ var eslintVersion = pkg.version;
+
+ prevConfig.hash = md5Hash(eslintVersion + "_" + stringify(config));
+ }
+
+ return prevConfig.hash;
+ }
+
+ /**
+ * Executes the linter on a file defined by the `filename`. Skips
+ * unsupported file extensions and any files that are already linted.
+ * @param {string} filename The resolved filename of the file to be linted
+ * @returns {void}
+ */
+ function executeOnFile(filename) {
+ var hashOfConfig;
+
+ if (processed[filename]) {
+ return;
+ }
+
+ if (options.cache) {
+ // get the descriptor for this file
+ // with the metadata and the flag that determines if
+ // the file has changed
+ var descriptor = fileCache.getFileDescriptor(filename);
+ var meta = descriptor.meta || {};
+
+ hashOfConfig = hashOfConfigFor(filename);
+
+ var changed = descriptor.changed || meta.hashOfConfig !== hashOfConfig;
+ if (!changed) {
+ debug("Skipping file since hasn't changed: " + filename);
+
+ // Adding the filename to the processed hashmap
+ // so the reporting is not affected (showing a warning about .eslintignore being used
+ // when it is not really used)
+
+ processed[filename] = true;
+
+ // Add the the cached results (always will be 0 error and 0 warnings)
+ // cause we don't save to cache files that failed
+ // to guarantee that next execution will process those files as well
+ results.push(descriptor.meta.results);
+
+ // move to the next file
+ return;
+ }
+ }
+
+ debug("Processing " + filename);
+
+ processed[filename] = true;
+
+ var res = processFile(filename, configHelper, options);
+
+ if (options.cache) {
+ // if a file contains errors or warnings we don't want to
+ // store the file in the cache so we can guarantee that
+ // next execution will also operate on this file
+ if ( res.errorCount > 0 || res.warningCount > 0 ) {
+ debug("File has problems, skipping it: " + filename);
+ // remove the entry from the cache
+ fileCache.removeEntry( filename );
+ } else {
+ // since the file passed we store the result here
+ // TODO: check this as we might not need to store the
+ // successful runs as it will always should be 0 error 0 warnings
+ descriptor.meta.hashOfConfig = hashOfConfig;
+ descriptor.meta.results = res;
+ }
+ }
+
+ results.push(res);
+ }
+
+ // Lint each desired file
+ globUtil.listFilesToProcess(patterns, options).forEach(executeOnFile);
+
+ // only warn for files explicitly passed on the command line
+ if (options.ignore) {
+ patterns.forEach(function(file) {
+ var fullPath = path.resolve(file);
+ if (shell.test("-f", fullPath) && !processed[fullPath]) {
+ results.push(createIgnoreResult(file));
+ }
+ });
+ }
+
+ stats = calculateStatsPerRun(results);
+
+ if (options.cache) {
+ // persist the cache to disk
+ fileCache.reconcile();
+ }
+
+ debug("Linting complete in: " + (Date.now() - startTime) + "ms");
+
+ return {
+ results: results,
+ errorCount: stats.errorCount,
+ warningCount: stats.warningCount
+ };
+ },
+
+ /**
+ * Executes the current configuration on text.
+ * @param {string} text A string of JavaScript code to lint.
+ * @param {string} filename An optional string representing the texts filename.
+ * @returns {Object} The results for the linting.
+ */
+ executeOnText: function(text, filename) {
+
+ var results = [],
+ stats,
+ options = this.options,
+ configHelper = new Config(options),
+ ignoredPaths = IgnoredPaths.load(options),
+ exclude = ignoredPaths.contains.bind(ignoredPaths);
+
+ if (filename && options.ignore && exclude(filename)) {
+ results.push(createIgnoreResult(filename));
+ } else {
+ results.push(processText(text, configHelper, filename, options.fix, options.allowInlineConfig));
+ }
+
+ stats = calculateStatsPerRun(results);
+
+ return {
+ results: results,
+ errorCount: stats.errorCount,
+ warningCount: stats.warningCount
+ };
+ },
+
+ /**
+ * Returns a configuration object for the given file based on the CLI options.
+ * This is the same logic used by the ESLint CLI executable to determine
+ * configuration for each file it processes.
+ * @param {string} filePath The path of the file to retrieve a config object for.
+ * @returns {Object} A configuration object for the file.
+ */
+ getConfigForFile: function(filePath) {
+ var configHelper = new Config(this.options);
+ return configHelper.getConfig(filePath);
+ },
+
+ /**
+ * Checks if a given path is ignored by ESLint.
+ * @param {string} filePath The path of the file to check.
+ * @returns {boolean} Whether or not the given path is ignored.
+ */
+ isPathIgnored: function(filePath) {
+ var ignoredPaths;
+
+ if (this.options.ignore) {
+ ignoredPaths = IgnoredPaths.load(this.options);
+ return ignoredPaths.contains(filePath);
+ }
+
+ return false;
+ },
+
+ getFormatter: CLIEngine.getFormatter
+
+};
+
+module.exports = CLIEngine;
diff --git a/node_modules/eslint/lib/cli.js b/node_modules/eslint/lib/cli.js
new file mode 100644
index 0000000..2201367
--- /dev/null
+++ b/node_modules/eslint/lib/cli.js
@@ -0,0 +1,191 @@
+/**
+ * @fileoverview Main CLI object.
+ * @author Nicholas C. Zakas
+ */
+
+"use strict";
+
+/*
+ * The CLI object should *not* call process.exit() directly. It should only return
+ * exit codes. This allows other programs to use the CLI object and still control
+ * when the program exits.
+ */
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var fs = require("fs"),
+ path = require("path"),
+
+ debug = require("debug"),
+
+ options = require("./options"),
+ CLIEngine = require("./cli-engine"),
+ mkdirp = require("mkdirp"),
+ log = require("./logging");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+debug = debug("eslint:cli");
+
+/**
+ * Translates the CLI options into the options expected by the CLIEngine.
+ * @param {Object} cliOptions The CLI options to translate.
+ * @returns {CLIEngineOptions} The options object for the CLIEngine.
+ * @private
+ */
+function translateOptions(cliOptions) {
+ return {
+ envs: cliOptions.env,
+ extensions: cliOptions.ext,
+ rules: cliOptions.rule,
+ plugins: cliOptions.plugin,
+ globals: cliOptions.global,
+ ignore: cliOptions.ignore,
+ ignorePath: cliOptions.ignorePath,
+ ignorePattern: cliOptions.ignorePattern,
+ configFile: cliOptions.config,
+ rulePaths: cliOptions.rulesdir,
+ useEslintrc: cliOptions.eslintrc,
+ parser: cliOptions.parser,
+ cache: cliOptions.cache,
+ cacheFile: cliOptions.cacheFile,
+ cacheLocation: cliOptions.cacheLocation,
+ fix: cliOptions.fix,
+ allowInlineConfig: cliOptions.inlineConfig
+ };
+}
+
+/**
+ * Outputs the results of the linting.
+ * @param {CLIEngine} engine The CLIEngine to use.
+ * @param {LintResult[]} results The results to print.
+ * @param {string} format The name of the formatter to use or the path to the formatter.
+ * @param {string} outputFile The path for the output file.
+ * @returns {boolean} True if the printing succeeds, false if not.
+ * @private
+ */
+function printResults(engine, results, format, outputFile) {
+ var formatter,
+ output,
+ filePath;
+
+ formatter = engine.getFormatter(format);
+ if (!formatter) {
+ log.error("Could not find formatter '%s'.", format);
+ return false;
+ }
+
+ output = formatter(results);
+
+ if (output) {
+ if (outputFile) {
+ filePath = path.resolve(process.cwd(), outputFile);
+
+ if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
+ log.error("Cannot write to output file path, it is a directory: %s", outputFile);
+ return false;
+ }
+
+ try {
+ mkdirp.sync(path.dirname(filePath));
+ fs.writeFileSync(filePath, output);
+ } catch (ex) {
+ log.error("There was a problem writing the output file:\n%s", ex);
+ return false;
+ }
+ } else {
+ log.info(output);
+ }
+ }
+
+ return true;
+
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * Encapsulates all CLI behavior for eslint. Makes it easier to test as well as
+ * for other Node.js programs to effectively run the CLI.
+ */
+var cli = {
+
+ /**
+ * Executes the CLI based on an array of arguments that is passed in.
+ * @param {string|Array|Object} args The arguments to process.
+ * @param {string} [text] The text to lint (used for TTY).
+ * @returns {int} The exit code for the operation.
+ */
+ execute: function(args, text) {
+
+ var currentOptions,
+ files,
+ report,
+ engine,
+ tooManyWarnings;
+
+ try {
+ currentOptions = options.parse(args);
+ } catch (error) {
+ log.error(error.message);
+ return 1;
+ }
+
+ files = currentOptions._;
+
+ if (currentOptions.version) { // version from package.json
+
+ log.info("v" + require("../package.json").version);
+
+ } else if (currentOptions.help || (!files.length && !text)) {
+
+ log.info(options.generateHelp());
+
+ } else {
+
+ debug("Running on " + (text ? "text" : "files"));
+
+ // disable --fix for piped-in code until we know how to do it correctly
+ if (text && currentOptions.fix) {
+ log.error("The --fix option is not available for piped-in code.");
+ return 1;
+ }
+
+ engine = new CLIEngine(translateOptions(currentOptions));
+
+ report = text ? engine.executeOnText(text, currentOptions.stdinFilename) : engine.executeOnFiles(files);
+ if (currentOptions.fix) {
+ debug("Fix mode enabled - applying fixes");
+ CLIEngine.outputFixes(report);
+ }
+
+ if (currentOptions.quiet) {
+ debug("Quiet mode enabled - filtering out warnings");
+ report.results = CLIEngine.getErrorResults(report.results);
+ }
+
+ if (printResults(engine, report.results, currentOptions.format, currentOptions.outputFile)) {
+ tooManyWarnings = currentOptions.maxWarnings >= 0 && report.warningCount > currentOptions.maxWarnings;
+
+ if (!report.errorCount && tooManyWarnings) {
+ log.error("ESLint found too many warnings (maximum: %s).", currentOptions.maxWarnings);
+ }
+
+ return (report.errorCount || tooManyWarnings) ? 1 : 0;
+ } else {
+ return 1;
+ }
+
+ }
+
+ return 0;
+ }
+};
+
+module.exports = cli;
diff --git a/node_modules/eslint/lib/config.js b/node_modules/eslint/lib/config.js
new file mode 100644
index 0000000..3eb174e
--- /dev/null
+++ b/node_modules/eslint/lib/config.js
@@ -0,0 +1,336 @@
+/**
+ * @fileoverview Responsible for loading config files
+ * @author Seth McLaughlin
+ * @copyright 2014 Nicholas C. Zakas. All rights reserved.
+ * @copyright 2014 Michael McLaughlin. All rights reserved.
+ * @copyright 2013 Seth McLaughlin. All rights reserved.
+ * See LICENSE in root directory for full license.
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var path = require("path"),
+ ConfigOps = require("./config/config-ops"),
+ ConfigFile = require("./config/config-file"),
+ util = require("./util"),
+ FileFinder = require("./file-finder"),
+ debug = require("debug"),
+ userHome = require("user-home"),
+ isResolvable = require("is-resolvable"),
+ pathIsInside = require("path-is-inside");
+
+//------------------------------------------------------------------------------
+// Constants
+//------------------------------------------------------------------------------
+
+var PACKAGE_CONFIG_FILENAME = "package.json",
+ PERSONAL_CONFIG_DIR = userHome || null;
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+var loadedPlugins = Object.create(null);
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+debug = debug("eslint:config");
+
+/**
+ * Check if item is an javascript object
+ * @param {*} item object to check for
+ * @returns {boolean} True if its an object
+ * @private
+ */
+function isObject(item) {
+ return typeof item === "object" && !Array.isArray(item) && item !== null;
+}
+
+/**
+ * Load and parse a JSON config object from a file.
+ * @param {string|Object} configToLoad the path to the JSON config file or the config object itself.
+ * @returns {Object} the parsed config object (empty object if there was a parse error)
+ * @private
+ */
+function loadConfig(configToLoad) {
+ var config = {},
+ filePath = "";
+
+ if (configToLoad) {
+
+ if (isObject(configToLoad)) {
+ config = configToLoad;
+
+ if (config.extends) {
+ config = ConfigFile.applyExtends(config, filePath);
+ }
+ } else {
+ filePath = configToLoad;
+ config = ConfigFile.load(filePath);
+ }
+
+ }
+
+ return config;
+}
+
+/**
+ * Load configuration for all plugins provided.
+ * @param {string[]} pluginNames An array of plugin names which should be loaded.
+ * @returns {Object} all plugin configurations merged together
+ */
+function getPluginsConfig(pluginNames) {
+ var pluginConfig = {};
+
+ pluginNames.forEach(function(pluginName) {
+ var pluginNamespace = util.getNamespace(pluginName),
+ pluginNameWithoutNamespace = util.removeNameSpace(pluginName),
+ pluginNameWithoutPrefix = util.removePluginPrefix(pluginNameWithoutNamespace),
+ plugin = {},
+ rules = {};
+
+ if (!loadedPlugins[pluginNameWithoutPrefix]) {
+ try {
+ plugin = require(pluginNamespace + util.PLUGIN_NAME_PREFIX + pluginNameWithoutPrefix);
+ loadedPlugins[pluginNameWithoutPrefix] = plugin;
+ } catch (err) {
+ debug("Failed to load plugin configuration for " + pluginNameWithoutPrefix + ". Proceeding without it.");
+ plugin = { rulesConfig: {}};
+ }
+ } else {
+ plugin = loadedPlugins[pluginNameWithoutPrefix];
+ }
+
+ if (!plugin.rulesConfig) {
+ plugin.rulesConfig = {};
+ }
+
+ Object.keys(plugin.rulesConfig).forEach(function(item) {
+ rules[pluginNameWithoutPrefix + "/" + item] = plugin.rulesConfig[item];
+ });
+
+ pluginConfig = ConfigOps.merge(pluginConfig, rules);
+ });
+
+ return {rules: pluginConfig};
+}
+
+/**
+ * Get personal config object from ~/.eslintrc.
+ * @returns {Object} the personal config object (empty object if there is no personal config)
+ * @private
+ */
+function getPersonalConfig() {
+ var config = {},
+ filename;
+
+ if (PERSONAL_CONFIG_DIR) {
+ filename = ConfigFile.getFilenameForDirectory(PERSONAL_CONFIG_DIR);
+
+ if (filename) {
+ debug("Using personal config");
+ config = loadConfig(filename);
+ }
+ }
+
+ return config;
+}
+
+/**
+ * Get a local config object.
+ * @param {Object} thisConfig A Config object.
+ * @param {string} directory The directory to start looking in for a local config file.
+ * @returns {Object} The local config object, or an empty object if there is no local config.
+ */
+function getLocalConfig(thisConfig, directory) {
+ var found,
+ i,
+ localConfig,
+ localConfigFile,
+ config = {},
+ localConfigFiles = thisConfig.findLocalConfigFiles(directory),
+ numFiles = localConfigFiles.length,
+ rootPath,
+ projectConfigPath = ConfigFile.getFilenameForDirectory(process.cwd());
+
+ for (i = 0; i < numFiles; i++) {
+
+ localConfigFile = localConfigFiles[i];
+
+ // Don't consider the personal config file in the home directory,
+ // except if the home directory is the same as the current working directory
+ if (path.dirname(localConfigFile) === PERSONAL_CONFIG_DIR && localConfigFile !== projectConfigPath) {
+ continue;
+ }
+
+ // If root flag is set, don't consider file if it is above root
+ if (rootPath && !pathIsInside(path.dirname(localConfigFile), rootPath)) {
+ continue;
+ }
+
+ debug("Loading " + localConfigFile);
+ localConfig = loadConfig(localConfigFile);
+
+ // Don't consider a local config file found if the config is null
+ if (!localConfig) {
+ continue;
+ }
+
+ // Check for root flag
+ if (localConfig.root === true) {
+ rootPath = path.dirname(localConfigFile);
+ }
+
+ found = true;
+ debug("Using " + localConfigFile);
+ config = ConfigOps.merge(localConfig, config);
+ }
+
+ // Use the personal config file if there are no other local config files found.
+ return found ? config : ConfigOps.merge(config, getPersonalConfig());
+}
+
+//------------------------------------------------------------------------------
+// API
+//------------------------------------------------------------------------------
+
+/**
+ * Config
+ * @constructor
+ * @class Config
+ * @param {Object} options Options to be passed in
+ * @param {string} [cwd] current working directory. Defaults to process.cwd()
+ */
+function Config(options) {
+ var useConfig;
+
+ options = options || {};
+
+ this.ignore = options.ignore;
+ this.ignorePath = options.ignorePath;
+ this.cache = {};
+ this.parser = options.parser;
+
+ this.baseConfig = options.baseConfig ? loadConfig(options.baseConfig) : { rules: {} };
+
+ this.useEslintrc = (options.useEslintrc !== false);
+
+ this.env = (options.envs || []).reduce(function(envs, name) {
+ envs[name] = true;
+ return envs;
+ }, {});
+
+ this.globals = (options.globals || []).reduce(function(globals, def) {
+ // Default "foo" to false and handle "foo:false" and "foo:true"
+ var parts = def.split(":");
+ globals[parts[0]] = (parts.length > 1 && parts[1] === "true");
+ return globals;
+ }, {});
+
+ useConfig = options.configFile;
+ this.options = options;
+
+ if (useConfig) {
+ debug("Using command line config " + useConfig);
+ if (isResolvable(useConfig) || isResolvable("eslint-config-" + useConfig) || useConfig.charAt(0) === "@") {
+ this.useSpecificConfig = loadConfig(useConfig);
+ } else {
+ this.useSpecificConfig = loadConfig(path.resolve(process.cwd(), useConfig));
+ }
+ }
+}
+
+/**
+ * Build a config object merging the base config (conf/eslint.json), the
+ * environments config (conf/environments.js) and eventually the user config.
+ * @param {string} filePath a file in whose directory we start looking for a local config
+ * @returns {Object} config object
+ */
+Config.prototype.getConfig = function(filePath) {
+ var config,
+ userConfig,
+ directory = filePath ? path.dirname(filePath) : process.cwd(),
+ pluginConfig;
+
+ debug("Constructing config for " + (filePath ? filePath : "text"));
+
+ config = this.cache[directory];
+
+ if (config) {
+ debug("Using config from cache");
+ return config;
+ }
+
+ // Step 1: Determine user-specified config from .eslintrc and package.json files
+ if (this.useEslintrc) {
+ debug("Using .eslintrc and package.json files");
+ userConfig = getLocalConfig(this, directory);
+ } else {
+ debug("Not using .eslintrc or package.json files");
+ userConfig = {};
+ }
+
+ // Step 2: Create a copy of the baseConfig
+ config = ConfigOps.merge({parser: this.parser}, this.baseConfig);
+
+ // Step 3: Merge in the user-specified configuration from .eslintrc and package.json
+ config = ConfigOps.merge(config, userConfig);
+
+ // Step 4: Merge in command line config file
+ if (this.useSpecificConfig) {
+ debug("Merging command line config file");
+
+ config = ConfigOps.merge(config, this.useSpecificConfig);
+ }
+
+ // Step 5: Merge in command line environments
+ debug("Merging command line environment settings");
+ config = ConfigOps.merge(config, ConfigOps.createEnvironmentConfig(this.env));
+
+ // Step 6: Merge in command line rules
+ if (this.options.rules) {
+ debug("Merging command line rules");
+ config = ConfigOps.merge(config, { rules: this.options.rules });
+ }
+
+ // Step 7: Merge in command line globals
+ config = ConfigOps.merge(config, { globals: this.globals });
+
+ // Step 8: Merge in command line plugins
+ if (this.options.plugins) {
+ debug("Merging command line plugins");
+ pluginConfig = getPluginsConfig(this.options.plugins);
+ config = ConfigOps.merge(config, { plugins: this.options.plugins });
+ }
+
+ // Step 9: Merge in plugin specific rules in reverse
+ if (config.plugins) {
+ pluginConfig = getPluginsConfig(config.plugins);
+ config = ConfigOps.merge(pluginConfig, config);
+ }
+
+ this.cache[directory] = config;
+
+ return config;
+};
+
+/**
+ * Find local config files from directory and parent directories.
+ * @param {string} directory The directory to start searching from.
+ * @returns {string[]} The paths of local config files found.
+ */
+Config.prototype.findLocalConfigFiles = function(directory) {
+
+ if (!this.localConfigFinder) {
+ this.localConfigFinder = new FileFinder(ConfigFile.CONFIG_FILES, PACKAGE_CONFIG_FILENAME);
+ }
+
+ return this.localConfigFinder.findAllInDirectoryAndParents(directory);
+};
+
+module.exports = Config;
diff --git a/node_modules/eslint/lib/config/config-file.js b/node_modules/eslint/lib/config/config-file.js
new file mode 100644
index 0000000..aaffb6f
--- /dev/null
+++ b/node_modules/eslint/lib/config/config-file.js
@@ -0,0 +1,440 @@
+/**
+ * @fileoverview Helper to locate and load configuration files.
+ * @author Nicholas C. Zakas
+ * @copyright 2015 Nicholas C. Zakas. All rights reserved.
+ * See LICENSE file in root directory for full license.
+ */
+/* eslint no-use-before-define: 0 */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var debug = require("debug"),
+ fs = require("fs"),
+ path = require("path"),
+ ConfigOps = require("./config-ops"),
+ validator = require("./config-validator"),
+ stripComments = require("strip-json-comments"),
+ isAbsolutePath = require("path-is-absolute");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+var CONFIG_FILES = [
+ ".eslintrc.js",
+ ".eslintrc.yaml",
+ ".eslintrc.yml",
+ ".eslintrc.json",
+ ".eslintrc"
+];
+
+debug = debug("eslint:config-file");
+
+/**
+ * Convenience wrapper for synchronously reading file contents.
+ * @param {string} filePath The filename to read.
+ * @returns {string} The file contents.
+ * @private
+ */
+function readFile(filePath) {
+ return fs.readFileSync(filePath, "utf8");
+}
+
+/**
+ * Determines if a given string represents a filepath or not using the same
+ * conventions as require(), meaning that the first character must be nonalphanumeric
+ * and not the @ sign which is used for scoped packages to be considered a file path.
+ * @param {string} filePath The string to check.
+ * @returns {boolean} True if it's a filepath, false if not.
+ * @private
+ */
+function isFilePath(filePath) {
+ return isAbsolutePath(filePath) || !/\w|@/.test(filePath.charAt(0));
+}
+
+/**
+ * Loads a YAML configuration from a file.
+ * @param {string} filePath The filename to load.
+ * @returns {Object} The configuration object from the file.
+ * @throws {Error} If the file cannot be read.
+ * @private
+ */
+function loadYAMLConfigFile(filePath) {
+ debug("Loading YAML config file: " + filePath);
+
+ // lazy load YAML to improve performance when not used
+ var yaml = require("js-yaml");
+
+ try {
+ // empty YAML file can be null, so always use
+ return yaml.safeLoad(readFile(filePath)) || {};
+ } catch (e) {
+ debug("Error reading YAML file: " + filePath);
+ e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
+ throw e;
+ }
+}
+
+/**
+ * Loads a JSON configuration from a file.
+ * @param {string} filePath The filename to load.
+ * @returns {Object} The configuration object from the file.
+ * @throws {Error} If the file cannot be read.
+ * @private
+ */
+function loadJSONConfigFile(filePath) {
+ debug("Loading JSON config file: " + filePath);
+
+ try {
+ return JSON.parse(stripComments(readFile(filePath)));
+ } catch (e) {
+ debug("Error reading JSON file: " + filePath);
+ e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
+ throw e;
+ }
+}
+
+/**
+ * Loads a legacy (.eslintrc) configuration from a file.
+ * @param {string} filePath The filename to load.
+ * @returns {Object} The configuration object from the file.
+ * @throws {Error} If the file cannot be read.
+ * @private
+ */
+function loadLegacyConfigFile(filePath) {
+ debug("Loading config file: " + filePath);
+
+ // lazy load YAML to improve performance when not used
+ var yaml = require("js-yaml");
+
+ try {
+ return yaml.safeLoad(stripComments(readFile(filePath))) || {};
+ } catch (e) {
+ debug("Error reading YAML file: " + filePath);
+ e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
+ throw e;
+ }
+}
+
+/**
+ * Loads a JavaScript configuration from a file.
+ * @param {string} filePath The filename to load.
+ * @returns {Object} The configuration object from the file.
+ * @throws {Error} If the file cannot be read.
+ * @private
+ */
+function loadJSConfigFile(filePath) {
+ debug("Loading JS config file: " + filePath);
+ try {
+ return require(filePath);
+ } catch (e) {
+ debug("Error reading JavaScript file: " + filePath);
+ e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
+ throw e;
+ }
+}
+
+/**
+ * Loads a configuration from a package.json file.
+ * @param {string} filePath The filename to load.
+ * @returns {Object} The configuration object from the file.
+ * @throws {Error} If the file cannot be read.
+ * @private
+ */
+function loadPackageJSONConfigFile(filePath) {
+ debug("Loading package.json config file: " + filePath);
+ try {
+ return require(filePath).eslintConfig || null;
+ } catch (e) {
+ debug("Error reading package.json file: " + filePath);
+ e.message = "Cannot read config file: " + filePath + "\nError: " + e.message;
+ throw e;
+ }
+}
+
+/**
+ * Loads a JavaScript configuration from a package.
+ * @param {string} filePath The package name to load.
+ * @returns {Object} The configuration object from the package.
+ * @throws {Error} If the package cannot be read.
+ * @private
+ */
+function loadPackage(filePath) {
+ debug("Loading config package: " + filePath);
+ try {
+ return require(filePath);
+ } catch (e) {
+ debug("Error reading package: " + filePath);
+ e.message = "Cannot read config package: " + filePath + "\nError: " + e.message;
+ throw e;
+ }
+}
+
+/**
+ * Loads a configuration file regardless of the source. Inspects the file path
+ * to determine the correctly way to load the config file.
+ * @param {string} filePath The path to the configuration.
+ * @returns {Object} The configuration information.
+ * @private
+ */
+function loadConfigFile(filePath) {
+ var config;
+
+ if (isFilePath(filePath)) {
+ switch (path.extname(filePath)) {
+ case ".js":
+ config = loadJSConfigFile(filePath);
+ break;
+
+ case ".json":
+ if (path.basename(filePath) === "package.json") {
+ config = loadPackageJSONConfigFile(filePath);
+ if (config === null) {
+ return null;
+ }
+ } else {
+ config = loadJSONConfigFile(filePath);
+ }
+ break;
+
+ case ".yaml":
+ case ".yml":
+ config = loadYAMLConfigFile(filePath);
+ break;
+
+ default:
+ config = loadLegacyConfigFile(filePath);
+ }
+ } else {
+ config = loadPackage(filePath);
+ }
+
+ return ConfigOps.merge(ConfigOps.createEmptyConfig(), config);
+}
+
+/**
+ * Writes a configuration file in JSON format.
+ * @param {Object} config The configuration object to write.
+ * @param {string} filePath The filename to write to.
+ * @returns {void}
+ * @private
+ */
+function writeJSONConfigFile(config, filePath) {
+ debug("Writing JSON config file: " + filePath);
+
+ var content = JSON.stringify(config, null, 4);
+ fs.writeFileSync(filePath, content, "utf8");
+}
+
+/**
+ * Writes a configuration file in YAML format.
+ * @param {Object} config The configuration object to write.
+ * @param {string} filePath The filename to write to.
+ * @returns {void}
+ * @private
+ */
+function writeYAMLConfigFile(config, filePath) {
+ debug("Writing YAML config file: " + filePath);
+
+ // lazy load YAML to improve performance when not used
+ var yaml = require("js-yaml");
+
+ var content = yaml.safeDump(config);
+ fs.writeFileSync(filePath, content, "utf8");
+}
+
+/**
+ * Writes a configuration file in JavaScript format.
+ * @param {Object} config The configuration object to write.
+ * @param {string} filePath The filename to write to.
+ * @returns {void}
+ * @private
+ */
+function writeJSConfigFile(config, filePath) {
+ debug("Writing JS config file: " + filePath);
+
+ var content = "module.exports = " + JSON.stringify(config, null, 4) + ";";
+ fs.writeFileSync(filePath, content, "utf8");
+}
+
+/**
+ * Writes a configuration file.
+ * @param {Object} config The configuration object to write.
+ * @param {string} filePath The filename to write to.
+ * @returns {void}
+ * @throws {Error} When an unknown file type is specified.
+ * @private
+ */
+function write(config, filePath) {
+ switch (path.extname(filePath)) {
+ case ".js":
+ writeJSConfigFile(config, filePath);
+ break;
+
+ case ".json":
+ writeJSONConfigFile(config, filePath);
+ break;
+
+ case ".yaml":
+ case ".yml":
+ writeYAMLConfigFile(config, filePath);
+ break;
+
+ default:
+ throw new Error("Can't write to unknown file type.");
+ }
+}
+
+/**
+ * Applies values from the "extends" field in a configuration file.
+ * @param {Object} config The configuration information.
+ * @param {string} filePath The file path from which the configuration information
+ * was loaded.
+ * @returns {Object} A new configuration object with all of the "extends" fields
+ * loaded and merged.
+ * @private
+ */
+function applyExtends(config, filePath) {
+ var configExtends = config.extends;
+
+ // normalize into an array for easier handling
+ if (!Array.isArray(config.extends)) {
+ configExtends = [config.extends];
+ }
+
+ // Make the last element in an array take the highest precedence
+ config = configExtends.reduceRight(function(previousValue, parentPath) {
+
+ if (parentPath === "eslint:recommended") {
+ // Add an explicit substitution for eslint:recommended to conf/eslint.json
+ // this lets us use the eslint.json file as the recommended rules
+ parentPath = path.resolve(__dirname, "../../conf/eslint.json");
+ } else if (isFilePath(parentPath)) {
+ // If the `extends` path is relative, use the directory of the current configuration
+ // file as the reference point. Otherwise, use as-is.
+ parentPath = (!isAbsolutePath(parentPath) ?
+ path.join(path.dirname(filePath), parentPath) :
+ parentPath
+ );
+ }
+
+ try {
+ debug("Loading " + parentPath);
+ return ConfigOps.merge(load(parentPath), previousValue);
+ } catch (e) {
+ // If the file referenced by `extends` failed to load, add the path to the
+ // configuration file that referenced it to the error message so the user is
+ // able to see where it was referenced from, then re-throw
+ e.message += "\nReferenced from: " + filePath;
+ throw e;
+ }
+
+ }, config);
+
+ return config;
+}
+
+/**
+ * Resolves a configuration file path into the fully-formed path, whether filename
+ * or package name.
+ * @param {string} filePath The filepath to resolve.
+ * @returns {string} A path that can be used directly to load the configuration.
+ * @private
+ */
+function resolve(filePath) {
+
+ if (isFilePath(filePath)) {
+ return path.resolve(filePath);
+ } else {
+
+ // it's a package
+
+ if (filePath.charAt(0) === "@") {
+ // it's a scoped package
+
+ // package name is "eslint-config", or just a username
+ var scopedPackageShortcutRegex = /^(@[^\/]+)(?:\/(?:eslint-config)?)?$/;
+ if (scopedPackageShortcutRegex.test(filePath)) {
+ filePath = filePath.replace(scopedPackageShortcutRegex, "$1/eslint-config");
+ } else if (filePath.split("/")[1].indexOf("eslint-config-") !== 0) {
+ // for scoped packages, insert the eslint-config after the first /
+ filePath = filePath.replace(/^@([^\/]+)\/(.*)$/, "@$1/eslint-config-$2");
+ }
+ } else if (filePath.indexOf("eslint-config-") !== 0) {
+ filePath = "eslint-config-" + filePath;
+ }
+
+ return filePath;
+ }
+
+}
+
+/**
+ * Loads a configuration file from the given file path.
+ * @param {string} filePath The filename or package name to load the configuration
+ * information from.
+ * @returns {Object} The configuration information.
+ * @private
+ */
+function load(filePath) {
+
+ var resolvedPath = resolve(filePath),
+ config = loadConfigFile(resolvedPath);
+
+ if (config) {
+
+ // validate the configuration before continuing
+ validator.validate(config, filePath);
+
+ // If an `extends` property is defined, it represents a configuration file to use as
+ // a "parent". Load the referenced file and merge the configuration recursively.
+ if (config.extends) {
+ config = applyExtends(config, filePath);
+ }
+
+ if (config.env) {
+ // Merge in environment-specific globals and ecmaFeatures.
+ config = ConfigOps.applyEnvironments(config);
+ }
+
+ }
+
+ return config;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+
+ load: load,
+ resolve: resolve,
+ write: write,
+ applyExtends: applyExtends,
+ CONFIG_FILES: CONFIG_FILES,
+
+ /**
+ * Retrieves the configuration filename for a given directory. It loops over all
+ * of the valid configuration filenames in order to find the first one that exists.
+ * @param {string} directory The directory to check for a config file.
+ * @returns {?string} The filename of the configuration file for the directory
+ * or null if there is no configuration file in the directory.
+ */
+ getFilenameForDirectory: function(directory) {
+
+ var filename;
+
+ for (var i = 0, len = CONFIG_FILES.length; i < len; i++) {
+ filename = path.join(directory, CONFIG_FILES[i]);
+ if (fs.existsSync(filename)) {
+ return filename;
+ }
+ }
+
+ return null;
+ }
+};
diff --git a/node_modules/eslint/lib/config/config-initializer.js b/node_modules/eslint/lib/config/config-initializer.js
new file mode 100644
index 0000000..526c56d
--- /dev/null
+++ b/node_modules/eslint/lib/config/config-initializer.js
@@ -0,0 +1,241 @@
+/**
+ * @fileoverview Config initialization wizard.
+ * @author Ilya Volodin
+ * @copyright 2015 Ilya Volodin. All rights reserved.
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var exec = require("child_process").exec,
+ inquirer = require("inquirer"),
+ ConfigFile = require("./config-file");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+/* istanbul ignore next: hard to test fs function */
+/**
+ * Create .eslintrc file in the current working directory
+ * @param {object} config object that contains user's answers
+ * @param {string} format The file format to write to.
+ * @param {function} callback function to call once the file is written.
+ * @returns {void}
+ */
+function writeFile(config, format, callback) {
+
+ // default is .js
+ var extname = ".js";
+ if (format === "YAML") {
+ extname = ".yml";
+ } else if (format === "JSON") {
+ extname = ".json";
+ }
+
+
+ try {
+ ConfigFile.write(config, "./.eslintrc" + extname);
+ console.log("Successfully created .eslintrc" + extname + " file in " + process.cwd());
+ } catch (e) {
+ callback(e);
+ return;
+ }
+
+ // install any external configs as well as any included plugins
+ if (config.extends && config.extends.indexOf("eslint") === -1) {
+ console.log("Installing additional dependencies");
+ exec("npm i eslint-config-" + config.extends + " --save-dev", function(err) {
+
+ if (err) {
+ return callback(err);
+ }
+
+ // TODO: consider supporting more than 1 plugin though it's required yet.
+ exec("npm i eslint-plugin-" + config.plugins[0] + " --save-dev", callback);
+ });
+ return;
+ }
+
+ // install the react plugin if it was explictly chosen
+ if (config.plugins && config.plugins.indexOf("react") >= 0) {
+ console.log("Installing React plugin");
+ exec("npm i eslint-plugin-react --save-dev", callback);
+ return;
+ }
+ callback();
+}
+
+/**
+ * process user's answers and create config object
+ * @param {object} answers answers received from inquirer
+ * @returns {object} config object
+ */
+function processAnswers(answers) {
+ var config = {rules: {}, env: {}, extends: "eslint:recommended"};
+ config.rules.indent = [2, answers.indent];
+ config.rules.quotes = [2, answers.quotes];
+ config.rules["linebreak-style"] = [2, answers.linebreak];
+ config.rules.semi = [2, answers.semi ? "always" : "never"];
+ if (answers.es6) {
+ config.env.es6 = true;
+ }
+ answers.env.forEach(function(env) {
+ config.env[env] = true;
+ });
+ if (answers.jsx) {
+ config.ecmaFeatures = {jsx: true};
+ if (answers.react) {
+ config.plugins = ["react"];
+ config.ecmaFeatures.experimentalObjectRestSpread = true;
+ }
+ }
+ return config;
+}
+
+/**
+ * process user's style guide of choice and return an appropriate config object.
+ * @param {string} guide name of the chosen style guide
+ * @returns {object} config object
+ */
+function getConfigForStyleGuide(guide) {
+ var guides = {
+ google: {extends: "google"},
+ airbnb: {extends: "airbnb", plugins: ["react"]},
+ standard: {extends: "standard", plugins: ["standard"]}
+ };
+ if (!guides[guide]) {
+ throw new Error("You referenced an unsupported guide.");
+ }
+ return guides[guide];
+}
+
+/* istanbul ignore next: no need to test inquirer*/
+/**
+ * Ask use a few questions on command prompt
+ * @param {function} callback callback function when file has been written
+ * @returns {void}
+ */
+function promptUser(callback) {
+ inquirer.prompt([
+ {
+ type: "list",
+ name: "source",
+ message: "How would you like to configure ESLint?",
+ default: "prompt",
+ choices: [{name: "Answer questions about your style", value: "prompt"}, {name: "Use a popular style guide", value: "guide"}]
+ },
+ {
+ type: "list",
+ name: "styleguide",
+ message: "Which style guide do you want to follow?",
+ choices: [{name: "Google", value: "google"}, {name: "AirBnB", value: "airbnb"}, {name: "Standard", value: "standard"}],
+ when: function(answers) {
+ return answers.source === "guide";
+ }
+ },
+ {
+ type: "list",
+ name: "format",
+ message: "What format do you want your config file to be in?",
+ default: "JavaScript",
+ choices: ["JavaScript", "YAML", "JSON"],
+ when: function(answers) {
+ return answers.source === "guide";
+ }
+ }
+ ], function(earlyAnswers) {
+
+ // early exit if you are using a style guide
+ if (earlyAnswers.source === "guide") {
+ writeFile(getConfigForStyleGuide(earlyAnswers.styleguide), earlyAnswers.format, callback);
+ return;
+ }
+
+ // continue with the style questions otherwise...
+ inquirer.prompt([
+ {
+ type: "list",
+ name: "indent",
+ message: "What style of indentation do you use?",
+ default: "tabs",
+ choices: [{name: "Tabs", value: "tab"}, {name: "Spaces", value: 4}]
+ },
+ {
+ type: "list",
+ name: "quotes",
+ message: "What quotes do you use for strings?",
+ default: "double",
+ choices: [{name: "Double", value: "double"}, {name: "Single", value: "single"}]
+ },
+ {
+ type: "list",
+ name: "linebreak",
+ message: "What line endings do you use?",
+ default: "unix",
+ choices: [{name: "Unix", value: "unix"}, {name: "Windows", value: "windows"}]
+ },
+ {
+ type: "confirm",
+ name: "semi",
+ message: "Do you require semicolons?",
+ default: true
+ },
+ {
+ type: "confirm",
+ name: "es6",
+ message: "Are you using ECMAScript 6 features?",
+ default: false
+ },
+ {
+ type: "checkbox",
+ name: "env",
+ message: "Where will your code run?",
+ default: ["browser"],
+ choices: [{name: "Node", value: "node"}, {name: "Browser", value: "browser"}]
+ },
+ {
+ type: "confirm",
+ name: "jsx",
+ message: "Do you use JSX?",
+ default: false
+ },
+ {
+ type: "confirm",
+ name: "react",
+ message: "Do you use React",
+ default: false,
+ when: function(answers) {
+ return answers.jsx;
+ }
+ },
+ {
+ type: "list",
+ name: "format",
+ message: "What format do you want your config file to be in?",
+ default: "JavaScript",
+ choices: ["JavaScript", "YAML", "JSON"]
+ }
+ ], function(answers) {
+ var config = processAnswers(answers);
+ writeFile(config, answers.format, callback);
+ });
+ });
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+var init = {
+ getConfigForStyleGuide: getConfigForStyleGuide,
+ processAnswers: processAnswers,
+ initializeConfig: /* istanbul ignore next */ function(callback) {
+ promptUser(callback);
+ }
+};
+
+module.exports = init;
diff --git a/node_modules/eslint/lib/config/config-ops.js b/node_modules/eslint/lib/config/config-ops.js
new file mode 100644
index 0000000..fa9436b
--- /dev/null
+++ b/node_modules/eslint/lib/config/config-ops.js
@@ -0,0 +1,186 @@
+/**
+ * @fileoverview Config file operations. This file must be usable in the browser,
+ * so no Node-specific code can be here.
+ * @author Nicholas C. Zakas
+ * @copyright 2015 Nicholas C. Zakas. All rights reserved.
+ * See LICENSE file in root directory for full license.
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var debug = require("debug"),
+ environments = require("../../conf/environments"),
+ assign = require("object-assign");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+debug = debug("eslint:config-ops");
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+
+ /**
+ * Creates an empty configuration object suitable for merging as a base.
+ * @returns {Object} A configuration object.
+ */
+ createEmptyConfig: function() {
+ return {
+ globals: {},
+ env: {},
+ rules: {},
+ ecmaFeatures: {}
+ };
+ },
+
+ /**
+ * Creates an environment config based on the specified environments.
+ * @param {Object} env The environment settings.
+ * @returns {Object} A configuration object with the appropriate rules and globals
+ * set.
+ */
+ createEnvironmentConfig: function(env) {
+
+ var envConfig = this.createEmptyConfig();
+
+ if (env) {
+
+ envConfig.env = env;
+
+ Object.keys(env).filter(function(name) {
+ return env[name];
+ }).forEach(function(name) {
+ var environment = environments[name];
+
+ if (environment) {
+ debug("Creating config for environment " + name);
+ if (environment.globals) {
+ assign(envConfig.globals, environment.globals);
+ }
+
+ if (environment.ecmaFeatures) {
+ assign(envConfig.ecmaFeatures, environment.ecmaFeatures);
+ }
+ }
+ });
+ }
+
+ return envConfig;
+ },
+
+ /**
+ * Given a config with environment settings, applies the globals and
+ * ecmaFeatures to the configuration and returns the result.
+ * @param {Object} config The configuration information.
+ * @returns {Object} The updated configuration information.
+ */
+ applyEnvironments: function(config) {
+ if (config.env && typeof config.env === "object") {
+ debug("Apply environment settings to config");
+ return this.merge(this.createEnvironmentConfig(config.env), config);
+ }
+
+ return config;
+ },
+
+ /**
+ * Merges two config objects. This will not only add missing keys, but will also modify values to match.
+ * @param {Object} target config object
+ * @param {Object} src config object. Overrides in this config object will take priority over base.
+ * @param {boolean} [combine] Whether to combine arrays or not
+ * @param {boolean} [isRule] Whether its a rule
+ * @returns {Object} merged config object.
+ */
+ merge: function deepmerge(target, src, combine, isRule) {
+ /*
+ The MIT License (MIT)
+
+ Copyright (c) 2012 Nicholas Fisher
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+ // This code is taken from deepmerge repo (https://github.com/KyleAMathews/deepmerge) and modified to meet our needs.
+ var array = Array.isArray(src) || Array.isArray(target);
+ var dst = array && [] || {};
+
+ combine = !!combine;
+ isRule = !!isRule;
+ if (array) {
+ target = target || [];
+ if (isRule && src.length > 1) {
+ dst = dst.concat(src);
+ } else {
+ dst = dst.concat(target);
+ }
+ if (typeof src !== "object" && !Array.isArray(src)) {
+ src = [src];
+ }
+ Object.keys(src).forEach(function(e, i) {
+ e = src[i];
+ if (typeof dst[i] === "undefined") {
+ dst[i] = e;
+ } else if (typeof e === "object") {
+ if (isRule) {
+ dst[i] = e;
+ } else {
+ dst[i] = deepmerge(target[i], e, combine, isRule);
+ }
+ } else {
+ if (!combine) {
+ dst[i] = e;
+ } else {
+ if (dst.indexOf(e) === -1) {
+ dst.push(e);
+ }
+ }
+ }
+ });
+ } else {
+ if (target && typeof target === "object") {
+ Object.keys(target).forEach(function(key) {
+ dst[key] = target[key];
+ });
+ }
+ Object.keys(src).forEach(function(key) {
+ if (Array.isArray(src[key]) || Array.isArray(target[key])) {
+ dst[key] = deepmerge(target[key], src[key], key === "plugins", isRule);
+ } else if (typeof src[key] !== "object" || !src[key]) {
+ dst[key] = src[key];
+ } else {
+ if (!target[key]) {
+ dst[key] = src[key];
+ } else {
+ dst[key] = deepmerge(target[key], src[key], combine, key === "rules");
+ }
+ }
+ });
+ }
+
+ return dst;
+ }
+
+
+};
diff --git a/node_modules/eslint/lib/config/config-validator.js b/node_modules/eslint/lib/config/config-validator.js
new file mode 100644
index 0000000..89e9a70
--- /dev/null
+++ b/node_modules/eslint/lib/config/config-validator.js
@@ -0,0 +1,163 @@
+/**
+ * @fileoverview Validates configs.
+ * @author Brandon Mills
+ * @copyright 2015 Brandon Mills
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var rules = require("../rules"),
+ environments = require("../../conf/environments"),
+ schemaValidator = require("is-my-json-valid");
+
+var validators = {
+ rules: Object.create(null)
+};
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+/**
+ * Gets a complete options schema for a rule.
+ * @param {string} id The rule's unique name.
+ * @returns {object} JSON Schema for the rule's options.
+ */
+function getRuleOptionsSchema(id) {
+ var rule = rules.get(id),
+ schema = rule && rule.schema;
+
+ if (!schema) {
+ return {
+ "type": "array",
+ "items": [
+ {
+ "enum": [0, 1, 2]
+ }
+ ],
+ "minItems": 1
+ };
+ }
+
+ // Given a tuple of schemas, insert warning level at the beginning
+ if (Array.isArray(schema)) {
+ return {
+ "type": "array",
+ "items": [
+ {
+ "enum": [0, 1, 2]
+ }
+ ].concat(schema),
+ "minItems": 1,
+ "maxItems": schema.length + 1
+ };
+ }
+
+ // Given a full schema, leave it alone
+ return schema;
+}
+
+/**
+ * Validates a rule's options against its schema.
+ * @param {string} id The rule's unique name.
+ * @param {array|number} options The given options for the rule.
+ * @param {string} source The name of the configuration source.
+ * @returns {void}
+ */
+function validateRuleOptions(id, options, source) {
+ var validateRule = validators.rules[id],
+ message;
+
+ if (!validateRule) {
+ validateRule = schemaValidator(getRuleOptionsSchema(id), { verbose: true });
+ validators.rules[id] = validateRule;
+ }
+
+ if (typeof options === "number") {
+ options = [options];
+ }
+
+ validateRule(options);
+
+ if (validateRule.errors) {
+ message = [
+ source, ":\n",
+ "\tConfiguration for rule \"", id, "\" is invalid:\n"
+ ];
+ validateRule.errors.forEach(function(error) {
+ if (error.field === "data[\"0\"]") { // better error for severity
+ message.push(
+ "\tSeverity should be one of the following: 0 = off, 1 = warning, 2 = error (you passed \"", error.value, "\").\n");
+ } else {
+ message.push(
+ "\tValue \"", error.value, "\" ", error.message, ".\n"
+ );
+ }
+ });
+
+ throw new Error(message.join(""));
+ }
+}
+
+/**
+ * Validates an environment object
+ * @param {object} environment The environment config object to validate.
+ * @param {string} source The location to report with any errors.
+ * @returns {void}
+ */
+function validateEnvironment(environment, source) {
+
+ // not having an environment is ok
+ if (!environment) {
+ return;
+ }
+
+ if (Array.isArray(environment)) {
+ throw new Error("Environment must not be an array");
+ }
+
+ if (typeof environment === "object") {
+ Object.keys(environment).forEach(function(env) {
+ if (!environments[env]) {
+ var message = [
+ source, ":\n",
+ "\tEnvironment key \"", env, "\" is unknown\n"
+ ];
+ throw new Error(message.join(""));
+ }
+ });
+ } else {
+ throw new Error("Environment must be an object");
+ }
+}
+
+/**
+ * Validates an entire config object.
+ * @param {object} config The config object to validate.
+ * @param {string} source The location to report with any errors.
+ * @returns {void}
+ */
+function validate(config, source) {
+
+ if (typeof config.rules === "object") {
+ Object.keys(config.rules).forEach(function(id) {
+ validateRuleOptions(id, config.rules[id], source);
+ });
+ }
+
+ validateEnvironment(config.env, source);
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+ getRuleOptionsSchema: getRuleOptionsSchema,
+ validate: validate,
+ validateRuleOptions: validateRuleOptions
+};
diff --git a/node_modules/eslint/lib/eslint.js b/node_modules/eslint/lib/eslint.js
new file mode 100644
index 0000000..12f8c3e
--- /dev/null
+++ b/node_modules/eslint/lib/eslint.js
@@ -0,0 +1,1043 @@
+/**
+ * @fileoverview Main ESLint object.
+ * @author Nicholas C. Zakas
+ * @copyright 2013 Nicholas C. Zakas. All rights reserved.
+ * See LICENSE file in root directory for full license.
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var estraverse = require("./util/estraverse"),
+ escope = require("escope"),
+ environments = require("../conf/environments"),
+ blankScriptAST = require("../conf/blank-script.json"),
+ assign = require("object-assign"),
+ rules = require("./rules"),
+ RuleContext = require("./rule-context"),
+ timing = require("./timing"),
+ SourceCode = require("./util/source-code"),
+ NodeEventGenerator = require("./util/node-event-generator"),
+ CommentEventGenerator = require("./util/comment-event-generator"),
+ EventEmitter = require("events").EventEmitter,
+ ConfigOps = require("./config/config-ops"),
+ validator = require("./config/config-validator"),
+ replacements = require("../conf/replacements.json"),
+ assert = require("assert");
+
+var DEFAULT_PARSER = require("../conf/eslint.json").parser;
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Parses a list of "name:boolean_value" or/and "name" options divided by comma or
+ * whitespace.
+ * @param {string} string The string to parse.
+ * @param {Comment} comment The comment node which has the string.
+ * @returns {Object} Result map object of names and boolean values
+ */
+function parseBooleanConfig(string, comment) {
+ var items = {};
+ // Collapse whitespace around : to make parsing easier
+ string = string.replace(/\s*:\s*/g, ":");
+ // Collapse whitespace around ,
+ string = string.replace(/\s*,\s*/g, ",");
+ string.split(/\s|,+/).forEach(function(name) {
+ if (!name) {
+ return;
+ }
+ var pos = name.indexOf(":"),
+ value;
+ if (pos !== -1) {
+ value = name.substring(pos + 1, name.length);
+ name = name.substring(0, pos);
+ }
+
+ items[name] = {
+ value: (value === "true"),
+ comment: comment
+ };
+
+ });
+ return items;
+}
+
+/**
+ * Parses a JSON-like config.
+ * @param {string} string The string to parse.
+ * @param {Object} location Start line and column of comments for potential error message.
+ * @param {Object[]} messages The messages queue for potential error message.
+ * @returns {Object} Result map object
+ */
+function parseJsonConfig(string, location, messages) {
+ var items = {};
+ string = string.replace(/([a-zA-Z0-9\-\/]+):/g, "\"$1\":").replace(/(\]|[0-9])\s+(?=")/, "$1,");
+ try {
+ items = JSON.parse("{" + string + "}");
+ } catch (ex) {
+
+ messages.push({
+ fatal: true,
+ severity: 2,
+ message: "Failed to parse JSON from '" + string + "': " + ex.message,
+ line: location.start.line,
+ column: location.start.column + 1
+ });
+
+ }
+
+ return items;
+}
+
+/**
+ * Parses a config of values separated by comma.
+ * @param {string} string The string to parse.
+ * @returns {Object} Result map of values and true values
+ */
+function parseListConfig(string) {
+ var items = {};
+ // Collapse whitespace around ,
+ string = string.replace(/\s*,\s*/g, ",");
+ string.split(/,+/).forEach(function(name) {
+ name = name.trim();
+ if (!name) {
+ return;
+ }
+ items[name] = true;
+ });
+ return items;
+}
+
+/**
+ * Ensures that variables representing built-in properties of the Global Object,
+ * and any globals declared by special block comments, are present in the global
+ * scope.
+ * @param {ASTNode} program The top node of the AST.
+ * @param {Scope} globalScope The global scope.
+ * @param {Object} config The existing configuration data.
+ * @returns {void}
+ */
+function addDeclaredGlobals(program, globalScope, config) {
+ var declaredGlobals = {},
+ exportedGlobals = {},
+ explicitGlobals = {},
+ builtin = environments.builtin;
+
+ assign(declaredGlobals, builtin);
+
+ Object.keys(config.env).forEach(function(name) {
+ if (config.env[name]) {
+ var environmentGlobals = environments[name] && environments[name].globals;
+ if (environmentGlobals) {
+ assign(declaredGlobals, environmentGlobals);
+ }
+ }
+ });
+
+ assign(exportedGlobals, config.exported);
+ assign(declaredGlobals, config.globals);
+ assign(explicitGlobals, config.astGlobals);
+
+ Object.keys(declaredGlobals).forEach(function(name) {
+ var variable = globalScope.set.get(name);
+ if (!variable) {
+ variable = new escope.Variable(name, globalScope);
+ variable.eslintExplicitGlobal = false;
+ globalScope.variables.push(variable);
+ globalScope.set.set(name, variable);
+ }
+ variable.writeable = declaredGlobals[name];
+ });
+
+ Object.keys(explicitGlobals).forEach(function(name) {
+ var variable = globalScope.set.get(name);
+ if (!variable) {
+ variable = new escope.Variable(name, globalScope);
+ variable.eslintExplicitGlobal = true;
+ variable.eslintExplicitGlobalComment = explicitGlobals[name].comment;
+ globalScope.variables.push(variable);
+ globalScope.set.set(name, variable);
+ }
+ variable.writeable = explicitGlobals[name].value;
+ });
+
+ // mark all exported variables as such
+ Object.keys(exportedGlobals).forEach(function(name) {
+ var variable = globalScope.set.get(name);
+ if (variable) {
+ variable.eslintUsed = true;
+ }
+ });
+}
+
+/**
+ * Add data to reporting configuration to disable reporting for list of rules
+ * starting from start location
+ * @param {Object[]} reportingConfig Current reporting configuration
+ * @param {Object} start Position to start
+ * @param {string[]} rulesToDisable List of rules
+ * @returns {void}
+ */
+function disableReporting(reportingConfig, start, rulesToDisable) {
+
+ if (rulesToDisable.length) {
+ rulesToDisable.forEach(function(rule) {
+ reportingConfig.push({
+ start: start,
+ end: null,
+ rule: rule
+ });
+ });
+ } else {
+ reportingConfig.push({
+ start: start,
+ end: null,
+ rule: null
+ });
+ }
+}
+
+/**
+ * Add data to reporting configuration to enable reporting for list of rules
+ * starting from start location
+ * @param {Object[]} reportingConfig Current reporting configuration
+ * @param {Object} start Position to start
+ * @param {string[]} rulesToEnable List of rules
+ * @returns {void}
+ */
+function enableReporting(reportingConfig, start, rulesToEnable) {
+ var i;
+
+ if (rulesToEnable.length) {
+ rulesToEnable.forEach(function(rule) {
+ for (i = reportingConfig.length - 1; i >= 0; i--) {
+ if (!reportingConfig[i].end && reportingConfig[i].rule === rule ) {
+ reportingConfig[i].end = start;
+ break;
+ }
+ }
+ });
+ } else {
+ // find all previous disabled locations if they was started as list of rules
+ var prevStart;
+ for (i = reportingConfig.length - 1; i >= 0; i--) {
+ if (prevStart && prevStart !== reportingConfig[i].start) {
+ break;
+ }
+
+ if (!reportingConfig[i].end) {
+ reportingConfig[i].end = start;
+ prevStart = reportingConfig[i].start;
+ }
+ }
+ }
+}
+
+/**
+ * Parses comments in file to extract file-specific config of rules, globals
+ * and environments and merges them with global config; also code blocks
+ * where reporting is disabled or enabled and merges them with reporting config.
+ * @param {string} filename The file being checked.
+ * @param {ASTNode} ast The top node of the AST.
+ * @param {Object} config The existing configuration data.
+ * @param {Object[]} reportingConfig The existing reporting configuration data.
+ * @param {Object[]} messages The messages queue.
+ * @returns {object} Modified config object
+ */
+function modifyConfigsFromComments(filename, ast, config, reportingConfig, messages) {
+
+ var commentConfig = {
+ exported: {},
+ astGlobals: {},
+ rules: {},
+ env: {}
+ };
+ var commentRules = {};
+
+ ast.comments.forEach(function(comment) {
+
+ var value = comment.value.trim();
+ var match = /^(eslint-\w+|eslint-\w+-\w+|eslint|exported|globals?)(\s|$)/.exec(value);
+
+ if (match) {
+ value = value.substring(match.index + match[1].length);
+
+ if (comment.type === "Block") {
+ switch (match[1]) {
+ case "exported":
+ assign(commentConfig.exported, parseBooleanConfig(value, comment));
+ break;
+
+ case "globals":
+ case "global":
+ assign(commentConfig.astGlobals, parseBooleanConfig(value, comment));
+ break;
+
+ case "eslint-env":
+ assign(commentConfig.env, parseListConfig(value));
+ break;
+
+ case "eslint-disable":
+ disableReporting(reportingConfig, comment.loc.start, Object.keys(parseListConfig(value)));
+ break;
+
+ case "eslint-enable":
+ enableReporting(reportingConfig, comment.loc.start, Object.keys(parseListConfig(value)));
+ break;
+
+ case "eslint":
+ var items = parseJsonConfig(value, comment.loc, messages);
+ Object.keys(items).forEach(function(name) {
+ var ruleValue = items[name];
+ validator.validateRuleOptions(name, ruleValue, filename + " line " + comment.loc.start.line);
+ commentRules[name] = ruleValue;
+ });
+ break;
+
+ // no default
+ }
+ } else {
+ // comment.type === "Line"
+ if (match[1] === "eslint-disable-line") {
+ disableReporting(reportingConfig, { "line": comment.loc.start.line, "column": 0 }, Object.keys(parseListConfig(value)));
+ enableReporting(reportingConfig, comment.loc.end, Object.keys(parseListConfig(value)));
+ }
+ }
+ }
+ });
+
+ // apply environment configs
+ Object.keys(commentConfig.env).forEach(function(name) {
+ if (environments[name]) {
+ commentConfig = ConfigOps.merge(commentConfig, environments[name]);
+ }
+ });
+ assign(commentConfig.rules, commentRules);
+
+ return ConfigOps.merge(config, commentConfig);
+}
+
+/**
+ * Check if message of rule with ruleId should be ignored in location
+ * @param {Object[]} reportingConfig Collection of ignore records
+ * @param {string} ruleId Id of rule
+ * @param {Object} location Location of message
+ * @returns {boolean} True if message should be ignored, false otherwise
+ */
+function isDisabledByReportingConfig(reportingConfig, ruleId, location) {
+
+ for (var i = 0, c = reportingConfig.length; i < c; i++) {
+
+ var ignore = reportingConfig[i];
+ if ((!ignore.rule || ignore.rule === ruleId) &&
+ (location.line > ignore.start.line || (location.line === ignore.start.line && location.column >= ignore.start.column)) &&
+ (!ignore.end || (location.line < ignore.end.line || (location.line === ignore.end.line && location.column <= ignore.end.column)))) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Process initial config to make it safe to extend by file comment config
+ * @param {Object} config Initial config
+ * @returns {Object} Processed config
+ */
+function prepareConfig(config) {
+
+ config.globals = config.globals || config.global || {};
+ delete config.global;
+
+ var copiedRules = {},
+ ecmaFeatures = {},
+ preparedConfig;
+
+ if (typeof config.rules === "object") {
+ Object.keys(config.rules).forEach(function(k) {
+ var rule = config.rules[k];
+ if (rule === null) {
+ throw new Error("Invalid config for rule '" + k + "'\.");
+ }
+ if (Array.isArray(rule)) {
+ copiedRules[k] = rule.slice();
+ } else {
+ copiedRules[k] = rule;
+ }
+ });
+ }
+
+ // merge in environment ecmaFeatures
+ if (typeof config.env === "object") {
+ Object.keys(config.env).forEach(function(env) {
+ if (config.env[env] && environments[env] && environments[env].ecmaFeatures) {
+ assign(ecmaFeatures, environments[env].ecmaFeatures);
+ }
+ });
+ }
+
+ preparedConfig = {
+ rules: copiedRules,
+ parser: config.parser || DEFAULT_PARSER,
+ globals: ConfigOps.merge({}, config.globals),
+ env: ConfigOps.merge({}, config.env || {}),
+ settings: ConfigOps.merge({}, config.settings || {}),
+ ecmaFeatures: ConfigOps.merge(ecmaFeatures, config.ecmaFeatures || {})
+ };
+
+ // can't have global return inside of modules
+ if (preparedConfig.ecmaFeatures.modules) {
+ preparedConfig.ecmaFeatures.globalReturn = false;
+ }
+
+ return preparedConfig;
+}
+
+/**
+ * Provide a stub rule with a given message
+ * @param {string} message The message to be displayed for the rule
+ * @returns {Function} Stub rule function
+ */
+function createStubRule(message) {
+
+ /**
+ * Creates a fake rule object
+ * @param {object} context context object for each rule
+ * @returns {object} collection of node to listen on
+ */
+ function createRuleModule(context) {
+ return {
+ Program: function(node) {
+ context.report(node, message);
+ }
+ };
+ }
+
+ if (message) {
+ return createRuleModule;
+ } else {
+ throw new Error("No message passed to stub rule");
+ }
+}
+
+/**
+ * Provide a rule replacement message
+ * @param {string} ruleId Name of the rule
+ * @returns {string} Message detailing rule replacement
+ */
+function getRuleReplacementMessage(ruleId) {
+ if (ruleId in replacements.rules) {
+ var newRules = replacements.rules[ruleId];
+ return "Rule \'" + ruleId + "\' was removed and replaced by: " + newRules.join(", ");
+ }
+}
+
+var eslintEnvPattern = /\/\*\s*eslint-env\s(.+?)\*\//g;
+
+/**
+ * Checks whether or not there is a comment which has "eslint-env *" in a given text.
+ * @param {string} text - A source code text to check.
+ * @returns {object|null} A result of parseListConfig() with "eslint-env *" comment.
+ */
+function findEslintEnv(text) {
+ var match, retv;
+
+ eslintEnvPattern.lastIndex = 0;
+ while ((match = eslintEnvPattern.exec(text))) {
+ retv = assign(retv || {}, parseListConfig(match[1]));
+ }
+
+ return retv;
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+/**
+ * Object that is responsible for verifying JavaScript text
+ * @name eslint
+ */
+module.exports = (function() {
+
+ var api = Object.create(new EventEmitter()),
+ messages = [],
+ currentConfig = null,
+ currentScopes = null,
+ scopeMap = null,
+ scopeManager = null,
+ currentFilename = null,
+ controller = null,
+ reportingConfig = [],
+ sourceCode = null;
+
+ /**
+ * Parses text into an AST. Moved out here because the try-catch prevents
+ * optimization of functions, so it's best to keep the try-catch as isolated
+ * as possible
+ * @param {string} text The text to parse.
+ * @param {Object} config The ESLint configuration object.
+ * @returns {ASTNode} The AST if successful or null if not.
+ * @private
+ */
+ function parse(text, config) {
+
+ var parser;
+
+ try {
+ parser = require(config.parser);
+ } catch (ex) {
+ messages.push({
+ fatal: true,
+ severity: 2,
+ message: ex.message,
+ line: 0,
+ column: 0
+ });
+
+ return null;
+ }
+
+ /*
+ * Check for parsing errors first. If there's a parsing error, nothing
+ * else can happen. However, a parsing error does not throw an error
+ * from this method - it's just considered a fatal error message, a
+ * problem that ESLint identified just like any other.
+ */
+ try {
+ return parser.parse(text, {
+ loc: true,
+ range: true,
+ raw: true,
+ tokens: true,
+ comment: true,
+ attachComment: true,
+ ecmaFeatures: config.ecmaFeatures
+ });
+ } catch (ex) {
+
+ // If the message includes a leading line number, strip it:
+ var message = ex.message.replace(/^line \d+:/i, "").trim();
+
+ messages.push({
+ fatal: true,
+ severity: 2,
+
+ message: "Parsing error: " + message,
+
+ line: ex.lineNumber,
+ column: ex.column + 1
+ });
+
+ return null;
+ }
+ }
+
+ /**
+ * Get the severity level of a rule (0 - none, 1 - warning, 2 - error)
+ * Returns 0 if the rule config is not valid (an Array or a number)
+ * @param {Array|number} ruleConfig rule configuration
+ * @returns {number} 0, 1, or 2, indicating rule severity
+ */
+ function getRuleSeverity(ruleConfig) {
+ if (typeof ruleConfig === "number") {
+ return ruleConfig;
+ } else if (Array.isArray(ruleConfig)) {
+ return ruleConfig[0];
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Get the options for a rule (not including severity), if any
+ * @param {Array|number} ruleConfig rule configuration
+ * @returns {Array} of rule options, empty Array if none
+ */
+ function getRuleOptions(ruleConfig) {
+ if (Array.isArray(ruleConfig)) {
+ return ruleConfig.slice(1);
+ } else {
+ return [];
+ }
+ }
+
+ // set unlimited listeners (see https://github.com/eslint/eslint/issues/524)
+ api.setMaxListeners(0);
+
+ /**
+ * Resets the internal state of the object.
+ * @returns {void}
+ */
+ api.reset = function() {
+ this.removeAllListeners();
+ messages = [];
+ currentConfig = null;
+ currentScopes = null;
+ scopeMap = null;
+ scopeManager = null;
+ controller = null;
+ reportingConfig = [];
+ sourceCode = null;
+ };
+
+ /**
+ * Verifies the text against the rules specified by the second argument.
+ * @param {string|SourceCode} textOrSourceCode The text to parse or a SourceCode object.
+ * @param {Object} config An object whose keys specify the rules to use.
+ * @param {(string|Object)} [filenameOrOptions] The optional filename of the file being checked.
+ * If this is not set, the filename will default to '' in the rule context. If
+ * an object, then it has "filename", "saveState", and "allowInlineConfig" properties.
+ * @param {boolean} [saveState] Indicates if the state from the last run should be saved.
+ * Mostly useful for testing purposes.
+ * @param {boolean} [filenameOrOptions.allowInlineConfig] Allow/disallow inline comments' ability to change config once it is set. Defaults to true if not supplied.
+ * Useful if you want to validate JS without comments overriding rules.
+ * @returns {Object[]} The results as an array of messages or null if no messages.
+ */
+ api.verify = function(textOrSourceCode, config, filenameOrOptions, saveState) {
+
+ var ast,
+ shebang,
+ ecmaFeatures,
+ ecmaVersion,
+ allowInlineConfig,
+ text = (typeof textOrSourceCode === "string") ? textOrSourceCode : null;
+
+ // evaluate arguments
+ if (typeof filenameOrOptions === "object") {
+ currentFilename = filenameOrOptions.filename;
+ allowInlineConfig = filenameOrOptions.allowInlineConfig;
+ saveState = filenameOrOptions.saveState;
+ } else {
+ currentFilename = filenameOrOptions;
+ }
+
+ if (!saveState) {
+ this.reset();
+ }
+
+ // search and apply "eslint-env *".
+ var envInFile = findEslintEnv(text || textOrSourceCode.text);
+ if (envInFile) {
+ if (!config || !config.env) {
+ config = assign({}, config || {}, {env: envInFile});
+ } else {
+ config = assign({}, config);
+ config.env = assign({}, config.env, envInFile);
+ }
+ }
+
+ // process initial config to make it safe to extend
+ config = prepareConfig(config || {});
+
+ // only do this for text
+ if (text !== null) {
+
+ // there's no input, just exit here
+ if (text.trim().length === 0) {
+ sourceCode = new SourceCode(text, blankScriptAST);
+ return messages;
+ }
+
+ ast = parse(text.replace(/^#!([^\r\n]+)/, function(match, captured) {
+ shebang = captured;
+ return "//" + captured;
+ }), config);
+
+ if (ast) {
+ sourceCode = new SourceCode(text, ast);
+ }
+
+ } else {
+ sourceCode = textOrSourceCode;
+ ast = sourceCode.ast;
+ }
+
+ // if espree failed to parse the file, there's no sense in setting up rules
+ if (ast) {
+
+ // parse global comments and modify config
+ if (allowInlineConfig !== false) {
+ config = modifyConfigsFromComments(currentFilename, ast, config, reportingConfig, messages);
+ }
+
+ // enable appropriate rules
+ Object.keys(config.rules).filter(function(key) {
+ return getRuleSeverity(config.rules[key]) > 0;
+ }).forEach(function(key) {
+ var ruleCreator,
+ severity,
+ options,
+ rule;
+
+ ruleCreator = rules.get(key);
+ if (!ruleCreator) {
+ var replacementMsg = getRuleReplacementMessage(key);
+ if (replacementMsg) {
+ ruleCreator = createStubRule(replacementMsg);
+ } else {
+ ruleCreator = createStubRule("Definition for rule '" + key + "' was not found");
+ }
+ rules.define(key, ruleCreator);
+ }
+
+ severity = getRuleSeverity(config.rules[key]);
+ options = getRuleOptions(config.rules[key]);
+
+ try {
+ rule = ruleCreator(new RuleContext(
+ key, api, severity, options,
+ config.settings, config.ecmaFeatures
+ ));
+
+ // add all the node types as listeners
+ Object.keys(rule).forEach(function(nodeType) {
+ api.on(nodeType, timing.enabled
+ ? timing.time(key, rule[nodeType])
+ : rule[nodeType]
+ );
+ });
+ } catch (ex) {
+ ex.message = "Error while loading rule '" + key + "': " + ex.message;
+ throw ex;
+ }
+ });
+
+ // save config so rules can access as necessary
+ currentConfig = config;
+ controller = new estraverse.Controller();
+
+ ecmaFeatures = currentConfig.ecmaFeatures;
+ ecmaVersion = (ecmaFeatures.blockBindings || ecmaFeatures.classes ||
+ ecmaFeatures.modules || ecmaFeatures.defaultParams ||
+ ecmaFeatures.destructuring) ? 6 : 5;
+
+
+ // gather data that may be needed by the rules
+ scopeManager = escope.analyze(ast, {
+ ignoreEval: true,
+ nodejsScope: ecmaFeatures.globalReturn,
+ ecmaVersion: ecmaVersion,
+ sourceType: ecmaFeatures.modules ? "module" : "script"
+ });
+ currentScopes = scopeManager.scopes;
+
+ /*
+ * Index the scopes by the start range of their block for efficient
+ * lookup in getScope.
+ */
+ scopeMap = [];
+ currentScopes.forEach(function(scope, index) {
+ var range = scope.block.range[0];
+
+ // Sometimes two scopes are returned for a given node. This is
+ // handled later in a known way, so just don't overwrite here.
+ if (!scopeMap[range]) {
+ scopeMap[range] = index;
+ }
+ });
+
+ // augment global scope with declared global variables
+ addDeclaredGlobals(ast, currentScopes[0], currentConfig);
+
+ // remove shebang comments
+ if (shebang && ast.comments.length && ast.comments[0].value === shebang) {
+ ast.comments.splice(0, 1);
+
+ if (ast.body.length && ast.body[0].leadingComments && ast.body[0].leadingComments[0].value === shebang) {
+ ast.body[0].leadingComments.splice(0, 1);
+ }
+ }
+
+ var eventGenerator = new NodeEventGenerator(api);
+ eventGenerator = new CommentEventGenerator(eventGenerator, sourceCode);
+
+ /*
+ * Each node has a type property. Whenever a particular type of node is found,
+ * an event is fired. This allows any listeners to automatically be informed
+ * that this type of node has been found and react accordingly.
+ */
+ controller.traverse(ast, {
+ enter: function(node, parent) {
+ node.parent = parent;
+ eventGenerator.enterNode(node);
+ },
+ leave: function(node) {
+ eventGenerator.leaveNode(node);
+ }
+ });
+
+ }
+
+ // sort by line and column
+ messages.sort(function(a, b) {
+ var lineDiff = a.line - b.line;
+
+ if (lineDiff === 0) {
+ return a.column - b.column;
+ } else {
+ return lineDiff;
+ }
+ });
+
+ return messages;
+ };
+
+ /**
+ * Reports a message from one of the rules.
+ * @param {string} ruleId The ID of the rule causing the message.
+ * @param {number} severity The severity level of the rule as configured.
+ * @param {ASTNode} node The AST node that the message relates to.
+ * @param {Object=} location An object containing the error line and column
+ * numbers. If location is not provided the node's start location will
+ * be used.
+ * @param {string} message The actual message.
+ * @param {Object} opts Optional template data which produces a formatted message
+ * with symbols being replaced by this object's values.
+ * @param {Object} fix A fix command description.
+ * @returns {void}
+ */
+ api.report = function(ruleId, severity, node, location, message, opts, fix) {
+ if (node) {
+ assert.strictEqual(typeof node, "object", "Node must be an object");
+ }
+
+ if (typeof location === "string") {
+ assert.ok(node, "Node must be provided when reporting error if location is not provided");
+
+ fix = opts;
+ opts = message;
+ message = location;
+ location = node.loc.start;
+ }
+ // else, assume location was provided, so node may be omitted
+
+ if (isDisabledByReportingConfig(reportingConfig, ruleId, location)) {
+ return;
+ }
+
+ if (opts) {
+ message = message.replace(/\{\{\s*(.+?)\s*\}\}/g, function(fullMatch, term) {
+ if (term in opts) {
+ return opts[term];
+ }
+
+ // Preserve old behavior: If parameter name not provided, don't replace it.
+ return fullMatch;
+ });
+ }
+
+ var problem = {
+ ruleId: ruleId,
+ severity: severity,
+ message: message,
+ line: location.line,
+ column: location.column + 1, // switch to 1-base instead of 0-base
+ nodeType: node && node.type,
+ source: sourceCode.lines[location.line - 1] || ""
+ };
+
+ // ensure there's range and text properties, otherwise it's not a valid fix
+ if (fix && Array.isArray(fix.range) && (typeof fix.text === "string")) {
+ problem.fix = fix;
+ }
+
+ messages.push(problem);
+ };
+
+ /**
+ * Gets the SourceCode object representing the parsed source.
+ * @returns {SourceCode} The SourceCode object.
+ */
+ api.getSourceCode = function() {
+ return sourceCode;
+ };
+
+ // methods that exist on SourceCode object
+ var externalMethods = {
+ getSource: "getText",
+ getSourceLines: "getLines",
+ getAllComments: "getAllComments",
+ getNodeByRangeIndex: "getNodeByRangeIndex",
+ getComments: "getComments",
+ getJSDocComment: "getJSDocComment",
+ getFirstToken: "getFirstToken",
+ getFirstTokens: "getFirstTokens",
+ getLastToken: "getLastToken",
+ getLastTokens: "getLastTokens",
+ getTokenAfter: "getTokenAfter",
+ getTokenBefore: "getTokenBefore",
+ getTokenByRangeStart: "getTokenByRangeStart",
+ getTokens: "getTokens",
+ getTokensAfter: "getTokensAfter",
+ getTokensBefore: "getTokensBefore",
+ getTokensBetween: "getTokensBetween"
+ };
+
+ // copy over methods
+ Object.keys(externalMethods).forEach(function(methodName) {
+ var exMethodName = externalMethods[methodName];
+
+ // All functions expected to have less arguments than 5.
+ api[methodName] = function(a, b, c, d, e) {
+ if (sourceCode) {
+ return sourceCode[exMethodName](a, b, c, d, e);
+ }
+ return null;
+ };
+ });
+
+ /**
+ * Gets nodes that are ancestors of current node.
+ * @returns {ASTNode[]} Array of objects representing ancestors.
+ */
+ api.getAncestors = function() {
+ return controller.parents();
+ };
+
+ /**
+ * Gets the scope for the current node.
+ * @returns {Object} An object representing the current node's scope.
+ */
+ api.getScope = function() {
+ var parents = controller.parents(),
+ scope = currentScopes[0];
+
+ // Don't do this for Program nodes - they have no parents
+ if (parents.length) {
+
+ // if current node introduces a scope, add it to the list
+ var current = controller.current();
+ if (currentConfig.ecmaFeatures.blockBindings) {
+ if (["BlockStatement", "SwitchStatement", "CatchClause", "FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].indexOf(current.type) >= 0) {
+ parents.push(current);
+ }
+ } else {
+ if (["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].indexOf(current.type) >= 0) {
+ parents.push(current);
+ }
+ }
+
+ // Ascend the current node's parents
+ for (var i = parents.length - 1; i >= 0; --i) {
+
+ // Get the innermost scope
+ scope = scopeManager.acquire(parents[i], true);
+ if (scope) {
+ if (scope.type === "function-expression-name") {
+ return scope.childScopes[0];
+ } else {
+ return scope;
+ }
+ }
+
+ }
+
+ }
+
+ return currentScopes[0];
+ };
+
+ /**
+ * Record that a particular variable has been used in code
+ * @param {string} name The name of the variable to mark as used
+ * @returns {boolean} True if the variable was found and marked as used,
+ * false if not.
+ */
+ api.markVariableAsUsed = function(name) {
+ var scope = this.getScope(),
+ specialScope = currentConfig.ecmaFeatures.globalReturn || currentConfig.ecmaFeatures.modules,
+ variables,
+ i,
+ len;
+
+ // Special Node.js scope means we need to start one level deeper
+ if (scope.type === "global" && specialScope) {
+ scope = scope.childScopes[0];
+ }
+
+ do {
+ variables = scope.variables;
+ for (i = 0, len = variables.length; i < len; i++) {
+ if (variables[i].name === name) {
+ variables[i].eslintUsed = true;
+ return true;
+ }
+ }
+ } while ( (scope = scope.upper) );
+
+ return false;
+ };
+
+ /**
+ * Gets the filename for the currently parsed source.
+ * @returns {string} The filename associated with the source being parsed.
+ * Defaults to "" if no filename info is present.
+ */
+ api.getFilename = function() {
+ if (typeof currentFilename === "string") {
+ return currentFilename;
+ } else {
+ return "";
+ }
+ };
+
+ /**
+ * Defines a new linting rule.
+ * @param {string} ruleId A unique rule identifier
+ * @param {Function} ruleModule Function from context to object mapping AST node types to event handlers
+ * @returns {void}
+ */
+ var defineRule = api.defineRule = function(ruleId, ruleModule) {
+ rules.define(ruleId, ruleModule);
+ };
+
+ /**
+ * Defines many new linting rules.
+ * @param {object} rulesToDefine map from unique rule identifier to rule
+ * @returns {void}
+ */
+ api.defineRules = function(rulesToDefine) {
+ Object.getOwnPropertyNames(rulesToDefine).forEach(function(ruleId) {
+ defineRule(ruleId, rulesToDefine[ruleId]);
+ });
+ };
+
+ /**
+ * Gets the default eslint configuration.
+ * @returns {Object} Object mapping rule IDs to their default configurations
+ */
+ api.defaults = function() {
+ return require("../conf/eslint.json");
+ };
+
+ /**
+ * Gets variables that are declared by a specified node.
+ *
+ * The variables are its `defs[].node` or `defs[].parent` is same as the specified node.
+ * Specifically, below:
+ *
+ * - `VariableDeclaration` - variables of its all declarators.
+ * - `VariableDeclarator` - variables.
+ * - `FunctionDeclaration`/`FunctionExpression` - its function name and parameters.
+ * - `ArrowFunctionExpression` - its parameters.
+ * - `ClassDeclaration`/`ClassExpression` - its class name.
+ * - `CatchClause` - variables of its exception.
+ * - `ImportDeclaration` - variables of its all specifiers.
+ * - `ImportSpecifier`/`ImportDefaultSpecifier`/`ImportNamespaceSpecifier` - a variable.
+ * - others - always an empty array.
+ *
+ * @param {ASTNode} node A node to get.
+ * @returns {escope.Variable[]} Variables that are declared by the node.
+ */
+ api.getDeclaredVariables = function(node) {
+ return (scopeManager && scopeManager.getDeclaredVariables(node)) || [];
+ };
+
+ return api;
+
+}());
diff --git a/node_modules/eslint/lib/file-finder.js b/node_modules/eslint/lib/file-finder.js
new file mode 100644
index 0000000..4f64a3f
--- /dev/null
+++ b/node_modules/eslint/lib/file-finder.js
@@ -0,0 +1,186 @@
+/**
+ * @fileoverview Util class to find config files.
+ * @author Aliaksei Shytkin
+ * @copyright 2014 Michael McLaughlin. All rights reserved.
+ * @copyright 2014 Aliaksei Shytkin. All rights reserved.
+ * See LICENSE in root directory for full license.
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+var fs = require("fs"),
+ path = require("path");
+
+//------------------------------------------------------------------------------
+// Helpers
+//------------------------------------------------------------------------------
+
+/**
+ * Get the entries for a directory. Including a try-catch may be detrimental to
+ * function performance, so move it out here a separate function.
+ * @param {string} directory The directory to search in.
+ * @returns {string[]} The entries in the directory or an empty array on error.
+ * @private
+ */
+function getDirectoryEntries(directory) {
+ try {
+ return fs.readdirSync(directory);
+ } catch (ex) {
+ return [];
+ }
+}
+
+//------------------------------------------------------------------------------
+// API
+//------------------------------------------------------------------------------
+
+/**
+ * FileFinder
+ * @constructor
+ * @param {...string} arguments The basename(s) of the file(s) to find.
+ */
+function FileFinder() {
+ this.fileNames = Array.prototype.slice.call(arguments);
+ this.cache = {};
+}
+
+/**
+ * Find one instance of a specified file name in directory or in a parent directory.
+ * Cache the results.
+ * Does not check if a matching directory entry is a file, and intentionally
+ * only searches for the first file name in this.fileNames.
+ * Is currently used by lib/ignored_paths.js to find an .eslintignore file.
+ * @param {string} directory The directory to start the search from.
+ * @returns {string} Path of the file found, or an empty string if not found.
+ */
+FileFinder.prototype.findInDirectoryOrParents = function(directory) {
+ var cache = this.cache,
+ child,
+ dirs,
+ filePath,
+ i,
+ name,
+ names,
+ searched;
+
+ if (!directory) {
+ directory = process.cwd();
+ }
+
+ if (cache.hasOwnProperty(directory)) {
+ return cache[directory];
+ }
+
+ dirs = [];
+ searched = 0;
+ name = this.fileNames[0];
+ names = Array.isArray(name) ? name : [name];
+
+ (function() {
+ while (directory !== child) {
+ dirs[searched++] = directory;
+
+ for (var k = 0, found = false; k < names.length && !found; k++) {
+
+ if (getDirectoryEntries(directory).indexOf(names[k]) !== -1 && fs.statSync(path.resolve(directory, names[k])).isFile()) {
+ filePath = path.resolve(directory, names[k]);
+ return;
+ }
+ }
+
+ child = directory;
+
+ // Assign parent directory to directory.
+ directory = path.dirname(directory);
+ }
+ }());
+
+ for (i = 0; i < searched; i++) {
+ cache[dirs[i]] = filePath;
+ }
+
+ return filePath || String();
+};
+
+/**
+ * Find all instances of files with the specified file names, in directory and
+ * parent directories. Cache the results.
+ * Does not check if a matching directory entry is a file.
+ * Searches for all the file names in this.fileNames.
+ * Is currently used by lib/config.js to find .eslintrc and package.json files.
+ * @param {string} directory The directory to start the search from.
+ * @returns {string[]} The file paths found.
+ */
+FileFinder.prototype.findAllInDirectoryAndParents = function(directory) {
+ var cache = this.cache,
+ child,
+ dirs,
+ name,
+ fileNames,
+ fileNamesCount,
+ filePath,
+ i,
+ j,
+ searched;
+
+ if (!directory) {
+ directory = process.cwd();
+ }
+
+ if (cache.hasOwnProperty(directory)) {
+ return cache[directory];
+ }
+
+ dirs = [];
+ searched = 0;
+ fileNames = this.fileNames;
+ fileNamesCount = fileNames.length;
+
+ do {
+ dirs[searched++] = directory;
+ cache[directory] = [];
+
+ for (i = 0; i < fileNamesCount; i++) {
+ name = fileNames[i];
+
+ // convert to an array for easier handling
+ if (!Array.isArray(name)) {
+ name = [name];
+ }
+
+ for (var k = 0, found = false; k < name.length && !found; k++) {
+
+ if (getDirectoryEntries(directory).indexOf(name[k]) !== -1 && fs.statSync(path.resolve(directory, name[k])).isFile()) {
+ filePath = path.resolve(directory, name[k]);
+ found = true;
+
+ // Add the file path to the cache of each directory searched.
+ for (j = 0; j < searched; j++) {
+ cache[dirs[j]].push(filePath);
+ }
+ }
+ }
+
+ }
+ child = directory;
+
+ // Assign parent directory to directory.
+ directory = path.dirname(directory);
+
+ if (directory === child) {
+ return cache[dirs[0]];
+ }
+ } while (!cache.hasOwnProperty(directory));
+
+ // Add what has been cached previously to the cache of each directory searched.
+ for (i = 0; i < searched; i++) {
+ dirs.push.apply(cache[dirs[i]], cache[directory]);
+ }
+
+ return cache[dirs[0]];
+};
+
+module.exports = FileFinder;
diff --git a/node_modules/eslint/lib/formatters/checkstyle.js b/node_modules/eslint/lib/formatters/checkstyle.js
new file mode 100644
index 0000000..dc40a2a
--- /dev/null
+++ b/node_modules/eslint/lib/formatters/checkstyle.js
@@ -0,0 +1,81 @@
+/**
+ * @fileoverview CheckStyle XML reporter
+ * @author Ian Christian Myers
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helper Functions
+//------------------------------------------------------------------------------
+
+/**
+ * Returns the severity of warning or error
+ * @param {object} message message object to examine
+ * @returns {string} severity level
+ * @private
+ */
+function getMessageType(message) {
+ if (message.fatal || message.severity === 2) {
+ return "error";
+ } else {
+ return "warning";
+ }
+}
+
+/**
+ * Returns the escaped value for a character
+ * @param {string} s string to examine
+ * @returns {string} severity level
+ * @private
+ */
+function xmlEscape(s) {
+ return ("" + s).replace(/[<>&"']/g, function(c) {
+ switch (c) {
+ case "<":
+ return "<";
+ case ">":
+ return ">";
+ case "&":
+ return "&";
+ case "\"":
+ return """;
+ case "'":
+ return "'";
+ // no default
+ }
+ });
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(results) {
+
+ var output = "";
+
+ output += "";
+ output += "";
+
+ results.forEach(function(result) {
+ var messages = result.messages;
+
+ output += "";
+
+ messages.forEach(function(message) {
+ output += "";
+ });
+
+ output += "";
+
+ });
+
+ output += "";
+
+ return output;
+};
diff --git a/node_modules/eslint/lib/formatters/compact.js b/node_modules/eslint/lib/formatters/compact.js
new file mode 100644
index 0000000..f1eb83a
--- /dev/null
+++ b/node_modules/eslint/lib/formatters/compact.js
@@ -0,0 +1,59 @@
+/**
+ * @fileoverview Compact reporter
+ * @author Nicholas C. Zakas
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Helper Functions
+//------------------------------------------------------------------------------
+
+/**
+ * Returns the severity of warning or error
+ * @param {object} message message object to examine
+ * @returns {string} severity level
+ * @private
+ */
+function getMessageType(message) {
+ if (message.fatal || message.severity === 2) {
+ return "Error";
+ } else {
+ return "Warning";
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = function(results) {
+
+ var output = "",
+ total = 0;
+
+ results.forEach(function(result) {
+
+ var messages = result.messages;
+ total += messages.length;
+
+ messages.forEach(function(message) {
+
+ output += result.filePath + ": ";
+ output += "line " + (message.line || 0);
+ output += ", col " + (message.column || 0);
+ output += ", " + getMessageType(message);
+ output += " - " + message.message;
+ output += message.ruleId ? " (" + message.ruleId + ")" : "";
+ output += "\n";
+
+ });
+
+ });
+
+ if (total > 0) {
+ output += "\n" + total + " problem" + (total !== 1 ? "s" : "");
+ }
+
+ return output;
+};
diff --git a/node_modules/eslint/lib/formatters/html-template.html b/node_modules/eslint/lib/formatters/html-template.html
new file mode 100644
index 0000000..f63a0a8
--- /dev/null
+++ b/node_modules/eslint/lib/formatters/html-template.html
@@ -0,0 +1,130 @@
+
+
+ ESLint Report
+
+
+
+
+
ESLint Report
+
+ {{renderText totalErrors totalWarnings}} - Generated on {{date}}
+
+
+
+
+ {{#each results}}
+
+
+ [+] {{this.filePath}}
+ {{renderText this.errorCount this.warningCount}}
+ |
+
+ {{#each this.messages}}
+
+ {{#if this.line}}{{this.line}}{{else}}0{{/if}}:{{#if this.column}}{{this.column}}{{else}}0{{/if}} |
+ {{getSeverity this.severity}}
+ {{this.message}} |
+
+ {{this.ruleId}}
+ |
+
+ {{/each}}
+ {{/each}}
+
+
+
+
+