Repository: tclindner/npm-package-json-lint
Pull request #35 (2017-04-22 15:07:34)

Feature add alphabetical dep rules
# Conflicts:
#	CHANGELOG.md


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 514cafa..ef56af2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 ### Removed
 
 ## [2.3.0] - 2017-04-22
+### Added
+- New rule: [prefer-alphabetical-dependencies](https://github.com/tclindner/npm-package-json-lint/wiki/prefer-alphabetical-dependencies)
+- New rule: [prefer-alphabetical-devDependencies](https://github.com/tclindner/npm-package-json-lint/wiki/prefer-alphabetical-devDependencies)
+- New rule: [prefer-alphabetical-bundledDependencies](https://github.com/tclindner/npm-package-json-lint/wiki/prefer-alphabetical-bundledDependencies)
+- New rule: [prefer-alphabetical-optionalDependencies](https://github.com/tclindner/npm-package-json-lint/wiki/prefer-alphabetical-optionalDependencies)
+- New rule: [prefer-alphabetical-peerDependencies](https://github.com/tclindner/npm-package-json-lint/wiki/prefer-alphabetical-peerDependencies)
+
 ### Removed
 - grunt tasks, tests, and project linting files from published node module
 
diff --git a/src/rules/prefer-alphabetical-bundledDependencies.js b/src/rules/prefer-alphabetical-bundledDependencies.js
new file mode 100644
index 0000000..953d5cc
--- /dev/null
+++ b/src/rules/prefer-alphabetical-bundledDependencies.js
@@ -0,0 +1,19 @@
+'use strict';
+
+const isInAlphabeticalOrder = require('./../validators/alphabetical-sort').isInAlphabeticalOrder;
+const LintIssue = require('./../LintIssue');
+const lintId = 'prefer-alphabetical-bundledDependencies';
+const nodeName = 'bundledDependencies';
+const message = 'Your bundledDependencies are not in alphabetical order. Please update the order.';
+const ruleType = 'bundledDependencies-alphabetical-order';
+
+const lint = function(packageJsonData, lintType) {
+  if (!isInAlphabeticalOrder(packageJsonData, nodeName)) {
+    return new LintIssue(lintId, lintType, nodeName, message);
+  }
+
+  return true;
+};
+
+module.exports.lint = lint;
+module.exports.ruleType = ruleType;
diff --git a/src/rules/prefer-alphabetical-dependencies.js b/src/rules/prefer-alphabetical-dependencies.js
new file mode 100644
index 0000000..802d4c1
--- /dev/null
+++ b/src/rules/prefer-alphabetical-dependencies.js
@@ -0,0 +1,19 @@
+'use strict';
+
+const isInAlphabeticalOrder = require('./../validators/alphabetical-sort').isInAlphabeticalOrder;
+const LintIssue = require('./../LintIssue');
+const lintId = 'prefer-alphabetical-dependencies';
+const nodeName = 'dependencies';
+const message = 'Your dependencies are not in alphabetical order. Please update the order.';
+const ruleType = 'dependencies-alphabetical-order';
+
+const lint = function(packageJsonData, lintType) {
+  if (!isInAlphabeticalOrder(packageJsonData, nodeName)) {
+    return new LintIssue(lintId, lintType, nodeName, message);
+  }
+
+  return true;
+};
+
+module.exports.lint = lint;
+module.exports.ruleType = ruleType;
diff --git a/src/rules/prefer-alphabetical-devDependencies.js b/src/rules/prefer-alphabetical-devDependencies.js
new file mode 100644
index 0000000..0d3ac6d
--- /dev/null
+++ b/src/rules/prefer-alphabetical-devDependencies.js
@@ -0,0 +1,19 @@
+'use strict';
+
+const isInAlphabeticalOrder = require('./../validators/alphabetical-sort').isInAlphabeticalOrder;
+const LintIssue = require('./../LintIssue');
+const lintId = 'prefer-alphabetical-devDependencies';
+const nodeName = 'devDependencies';
+const message = 'Your devDependencies are not in alphabetical order. Please update the order.';
+const ruleType = 'devDependencies-alphabetical-order';
+
+const lint = function(packageJsonData, lintType) {
+  if (!isInAlphabeticalOrder(packageJsonData, nodeName)) {
+    return new LintIssue(lintId, lintType, nodeName, message);
+  }
+
+  return true;
+};
+
+module.exports.lint = lint;
+module.exports.ruleType = ruleType;
diff --git a/src/rules/prefer-alphabetical-optionalDependencies.js b/src/rules/prefer-alphabetical-optionalDependencies.js
new file mode 100644
index 0000000..08c8e78
--- /dev/null
+++ b/src/rules/prefer-alphabetical-optionalDependencies.js
@@ -0,0 +1,19 @@
+'use strict';
+
+const isInAlphabeticalOrder = require('./../validators/alphabetical-sort').isInAlphabeticalOrder;
+const LintIssue = require('./../LintIssue');
+const lintId = 'prefer-alphabetical-optionalDependencies';
+const nodeName = 'optionalDependencies';
+const message = 'Your optionalDependencies are not in alphabetical order. Please update the order.';
+const ruleType = 'optionalDependencies-alphabetical-order';
+
+const lint = function(packageJsonData, lintType) {
+  if (!isInAlphabeticalOrder(packageJsonData, nodeName)) {
+    return new LintIssue(lintId, lintType, nodeName, message);
+  }
+
+  return true;
+};
+
+module.exports.lint = lint;
+module.exports.ruleType = ruleType;
diff --git a/src/rules/prefer-alphabetical-peerDependencies.js b/src/rules/prefer-alphabetical-peerDependencies.js
new file mode 100644
index 0000000..f2bf216
--- /dev/null
+++ b/src/rules/prefer-alphabetical-peerDependencies.js
@@ -0,0 +1,19 @@
+'use strict';
+
+const isInAlphabeticalOrder = require('./../validators/alphabetical-sort').isInAlphabeticalOrder;
+const LintIssue = require('./../LintIssue');
+const lintId = 'prefer-alphabetical-peerDependencies';
+const nodeName = 'peerDependencies';
+const message = 'Your peerDependencies are not in alphabetical order. Please update the order.';
+const ruleType = 'peerDependencies-alphabetical-order';
+
+const lint = function(packageJsonData, lintType) {
+  if (!isInAlphabeticalOrder(packageJsonData, nodeName)) {
+    return new LintIssue(lintId, lintType, nodeName, message);
+  }
+
+  return true;
+};
+
+module.exports.lint = lint;
+module.exports.ruleType = ruleType;
diff --git a/src/validators/alphabetical-sort.js b/src/validators/alphabetical-sort.js
new file mode 100644
index 0000000..794107d
--- /dev/null
+++ b/src/validators/alphabetical-sort.js
@@ -0,0 +1,33 @@
+'use strict';
+
+// jscs:disable requireSpacesInForStatement, requireSpacesInForStatement
+
+/**
+ * Determines whether an array is in alphabetical order
+ * @param  {object} packageJsonData Valid JSON
+ * @param  {string} nodeName        Name of a node in the package.json file
+ * @return {boolean}                True if the node is in alphabetical order or is missing. False if it is not.
+ */
+const isInAlphabeticalOrder = function(packageJsonData, nodeName) {
+  if (!packageJsonData.hasOwnProperty(nodeName)) {
+    return true;
+  }
+
+  let isValid = true;
+  const increment = 1;
+  const nodeKeysOriginal = Object.keys(packageJsonData[nodeName]);
+  let nodeKeysSorted = Object.keys(packageJsonData[nodeName]);
+
+  nodeKeysSorted = nodeKeysSorted.sort();
+
+  for (let keyIndex = 0;keyIndex <= nodeKeysOriginal.length;keyIndex += increment) {
+    if (nodeKeysOriginal[keyIndex] !== nodeKeysSorted[keyIndex]) {
+      isValid = false;
+      break;
+    }
+  }
+
+  return isValid;
+};
+
+module.exports.isInAlphabeticalOrder = isInAlphabeticalOrder;


Test patch:
diff --git a/tests/unit/rules/prefer-alphabetical-bundledDependencies.js b/tests/unit/rules/prefer-alphabetical-bundledDependencies.js
new file mode 100644
index 0000000..5a34759
--- /dev/null
+++ b/tests/unit/rules/prefer-alphabetical-bundledDependencies.js
@@ -0,0 +1,52 @@
+'use strict';
+
+/* eslint max-nested-callbacks: "off" */
+// jscs:disable disallowQuotedKeysInObjects
+
+const should = require('should');
+const requireHelper = require('../../require_helper');
+const lint = requireHelper('rules/prefer-alphabetical-bundledDependencies').lint;
+
+describe('prefer-alphabetical-bundledDependencies Unit Tests', function() {
+  context('when package.json has node with an invalid order', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        bundledDependencies: {
+          'semver': '^5.3.0',
+          'chalk': '^1.1.3',
+          'user-home': '^2.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.lintId.should.equal('prefer-alphabetical-bundledDependencies');
+      response.lintType.should.equal('error');
+      response.node.should.equal('bundledDependencies');
+      response.lintMessage.should.equal('Your bundledDependencies are not in alphabetical order. Please update the order.');
+    });
+  });
+
+  context('when package.json has node with a valid order', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        bundledDependencies: {
+          'chalk': '^1.1.3',
+          'semver': '^5.3.0',
+          'user-home': '^2.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.should.be.true();
+    });
+  });
+
+  context('when package.json does not have node', function() {
+    it('true should be returned', function() {
+      const packageJsonData = {};
+      const response = lint(packageJsonData, 'error');
+
+      response.should.be.true();
+    });
+  });
+});
diff --git a/tests/unit/rules/prefer-alphabetical-dependencies.js b/tests/unit/rules/prefer-alphabetical-dependencies.js
new file mode 100644
index 0000000..2a0b37d
--- /dev/null
+++ b/tests/unit/rules/prefer-alphabetical-dependencies.js
@@ -0,0 +1,52 @@
+'use strict';
+
+/* eslint max-nested-callbacks: "off" */
+// jscs:disable disallowQuotedKeysInObjects
+
+const should = require('should');
+const requireHelper = require('../../require_helper');
+const lint = requireHelper('rules/prefer-alphabetical-dependencies').lint;
+
+describe('prefer-alphabetical-dependencies Unit Tests', function() {
+  context('when package.json has node with an invalid order', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        dependencies: {
+          'semver': '^5.3.0',
+          'chalk': '^1.1.3',
+          'user-home': '^2.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.lintId.should.equal('prefer-alphabetical-dependencies');
+      response.lintType.should.equal('error');
+      response.node.should.equal('dependencies');
+      response.lintMessage.should.equal('Your dependencies are not in alphabetical order. Please update the order.');
+    });
+  });
+
+  context('when package.json has node with a valid order', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        dependencies: {
+          'chalk': '^1.1.3',
+          'semver': '^5.3.0',
+          'user-home': '^2.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.should.be.true();
+    });
+  });
+
+  context('when package.json does not have node', function() {
+    it('true should be returned', function() {
+      const packageJsonData = {};
+      const response = lint(packageJsonData, 'error');
+
+      response.should.be.true();
+    });
+  });
+});
diff --git a/tests/unit/rules/prefer-alphabetical-devDependencies.js b/tests/unit/rules/prefer-alphabetical-devDependencies.js
new file mode 100644
index 0000000..08f9560
--- /dev/null
+++ b/tests/unit/rules/prefer-alphabetical-devDependencies.js
@@ -0,0 +1,52 @@
+'use strict';
+
+/* eslint max-nested-callbacks: "off" */
+// jscs:disable disallowQuotedKeysInObjects
+
+const should = require('should');
+const requireHelper = require('../../require_helper');
+const lint = requireHelper('rules/prefer-alphabetical-devDependencies').lint;
+
+describe('prefer-alphabetical-devDependencies Unit Tests', function() {
+  context('when package.json has node with an invalid order', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        devDependencies: {
+          'semver': '^5.3.0',
+          'chalk': '^1.1.3',
+          'user-home': '^2.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.lintId.should.equal('prefer-alphabetical-devDependencies');
+      response.lintType.should.equal('error');
+      response.node.should.equal('devDependencies');
+      response.lintMessage.should.equal('Your devDependencies are not in alphabetical order. Please update the order.');
+    });
+  });
+
+  context('when package.json has node with a valid order', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        devDependencies: {
+          'chalk': '^1.1.3',
+          'semver': '^5.3.0',
+          'user-home': '^2.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.should.be.true();
+    });
+  });
+
+  context('when package.json does not have node', function() {
+    it('true should be returned', function() {
+      const packageJsonData = {};
+      const response = lint(packageJsonData, 'error');
+
+      response.should.be.true();
+    });
+  });
+});
diff --git a/tests/unit/rules/prefer-alphabetical-optionalDependencies.js b/tests/unit/rules/prefer-alphabetical-optionalDependencies.js
new file mode 100644
index 0000000..f7a591b
--- /dev/null
+++ b/tests/unit/rules/prefer-alphabetical-optionalDependencies.js
@@ -0,0 +1,52 @@
+'use strict';
+
+/* eslint max-nested-callbacks: "off" */
+// jscs:disable disallowQuotedKeysInObjects
+
+const should = require('should');
+const requireHelper = require('../../require_helper');
+const lint = requireHelper('rules/prefer-alphabetical-optionalDependencies').lint;
+
+describe('prefer-alphabetical-optionalDependencies Unit Tests', function() {
+  context('when package.json has node with an invalid order', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        optionalDependencies: {
+          'semver': '^5.3.0',
+          'chalk': '^1.1.3',
+          'user-home': '^2.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.lintId.should.equal('prefer-alphabetical-optionalDependencies');
+      response.lintType.should.equal('error');
+      response.node.should.equal('optionalDependencies');
+      response.lintMessage.should.equal('Your optionalDependencies are not in alphabetical order. Please update the order.');
+    });
+  });
+
+  context('when package.json has node with a valid order', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        optionalDependencies: {
+          'chalk': '^1.1.3',
+          'semver': '^5.3.0',
+          'user-home': '^2.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.should.be.true();
+    });
+  });
+
+  context('when package.json does not have node', function() {
+    it('true should be returned', function() {
+      const packageJsonData = {};
+      const response = lint(packageJsonData, 'error');
+
+      response.should.be.true();
+    });
+  });
+});
diff --git a/tests/unit/rules/prefer-alphabetical-peerDependencies.js b/tests/unit/rules/prefer-alphabetical-peerDependencies.js
new file mode 100644
index 0000000..3164326
--- /dev/null
+++ b/tests/unit/rules/prefer-alphabetical-peerDependencies.js
@@ -0,0 +1,52 @@
+'use strict';
+
+/* eslint max-nested-callbacks: "off" */
+// jscs:disable disallowQuotedKeysInObjects
+
+const should = require('should');
+const requireHelper = require('../../require_helper');
+const lint = requireHelper('rules/prefer-alphabetical-peerDependencies').lint;
+
+describe('prefer-alphabetical-peerDependencies Unit Tests', function() {
+  context('when package.json has node with an invalid order', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        peerDependencies: {
+          'semver': '^5.3.0',
+          'chalk': '^1.1.3',
+          'user-home': '^2.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.lintId.should.equal('prefer-alphabetical-peerDependencies');
+      response.lintType.should.equal('error');
+      response.node.should.equal('peerDependencies');
+      response.lintMessage.should.equal('Your peerDependencies are not in alphabetical order. Please update the order.');
+    });
+  });
+
+  context('when package.json has node with a valid order', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        peerDependencies: {
+          'chalk': '^1.1.3',
+          'semver': '^5.3.0',
+          'user-home': '^2.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.should.be.true();
+    });
+  });
+
+  context('when package.json does not have node', function() {
+    it('true should be returned', function() {
+      const packageJsonData = {};
+      const response = lint(packageJsonData, 'error');
+
+      response.should.be.true();
+    });
+  });
+});
diff --git a/tests/unit/validators/alphabetical-sort.js b/tests/unit/validators/alphabetical-sort.js
new file mode 100644
index 0000000..7cc6422
--- /dev/null
+++ b/tests/unit/validators/alphabetical-sort.js
@@ -0,0 +1,53 @@
+'use strict';
+
+/* eslint max-nested-callbacks: "off" */
+// jscs:disable disallowQuotedKeysInObjects
+
+const should = require('should');
+const requireHelper = require('../../require_helper');
+const alphabeticalSort = requireHelper('validators/alphabetical-sort');
+
+describe('alphabetical-sort Unit Tests', function() {
+  describe('isInAlphabeticalOrder method', function() {
+    context('when the node does not exist in the package.json file', function() {
+      it('true should be returned', function() {
+        const packageJson = {
+          name: 'awesome-module'
+        };
+        const response = alphabeticalSort.isInAlphabeticalOrder(packageJson, 'devDependencies');
+
+        response.should.be.true();
+      });
+    });
+
+    context('when the node exists in the package.json file and dependencies are in alpahbetical order', function() {
+      it('true should be returned', function() {
+        const packageJson = {
+          devDependencies: {
+            'chalk': '^1.1.3',
+            'semver': '^5.3.0',
+            'user-home': '^2.0.0'
+          }
+        };
+        const response = alphabeticalSort.isInAlphabeticalOrder(packageJson, 'devDependencies');
+
+        response.should.be.true();
+      });
+    });
+
+    context('when the node exists in the package.json file and dependencies are not in alpahbetical order', function() {
+      it('false should be returned', function() {
+        const packageJson = {
+          devDependencies: {
+            'semver': '^5.3.0',
+            'chalk': '^1.1.3',
+            'user-home': '^2.0.0'
+          }
+        };
+        const response = alphabeticalSort.isInAlphabeticalOrder(packageJson, 'devDependencies');
+
+        response.should.be.false();
+      });
+    });
+  });
+});