Repository: tclindner/npm-package-json-lint
Pull request #87 (2018-07-11 20:04:05)

add dependency rules for preventing use of certain ranges
** Description of change **
This commit adds six new `dependency` rules based on the existing absolute, caret, and tilde rules. We are contemplating using this linter on my team at Spotify, but would need the ability to prevent caret version in dependencies, for example.

** Checklist **

  - [x] Unit tests have been added
  - [ ] Specific notes for documentation, if applicable
    - I think that this one is essentially covered, in that it's 6 new rules that must be added to the Wiki should this be merged and released.



Patch:
diff --git a/src/rules/no-absolute-version-dependencies.js b/src/rules/no-absolute-version-dependencies.js
new file mode 100644
index 0000000..446a18c
--- /dev/null
+++ b/src/rules/no-absolute-version-dependencies.js
@@ -0,0 +1,20 @@
+'use strict';
+
+const isVersionAbsolute = require('./../validators/dependency-audit').isVersionAbsolute;
+const LintIssue = require('./../LintIssue');
+const lintId = 'no-absolute-version-dependencies';
+const nodeName = 'dependencies';
+const message = 'You are using an invalid version range. Please do not use absolute versions.';
+const ruleType = 'standard';
+
+const lint = function(packageJsonData, severity) {
+
+  if (packageJsonData.hasOwnProperty(nodeName) && isVersionAbsolute(packageJsonData, nodeName)) {
+    return new LintIssue(lintId, severity, nodeName, message);
+  }
+
+  return true;
+};
+
+module.exports.lint = lint;
+module.exports.ruleType = ruleType;
diff --git a/src/rules/no-absolute-version-devDependencies.js b/src/rules/no-absolute-version-devDependencies.js
new file mode 100644
index 0000000..9ceb95b
--- /dev/null
+++ b/src/rules/no-absolute-version-devDependencies.js
@@ -0,0 +1,20 @@
+'use strict';
+
+const isVersionAbsolute = require('./../validators/dependency-audit').isVersionAbsolute;
+const LintIssue = require('./../LintIssue');
+const lintId = 'no-absolute-version-devDependencies';
+const nodeName = 'devDependencies';
+const message = 'You are using an invalid version range. Please do not use absolute versions.';
+const ruleType = 'standard';
+
+const lint = function(packageJsonData, severity) {
+
+  if (packageJsonData.hasOwnProperty(nodeName) && isVersionAbsolute(packageJsonData, nodeName)) {
+    return new LintIssue(lintId, severity, nodeName, message);
+  }
+
+  return true;
+};
+
+module.exports.lint = lint;
+module.exports.ruleType = ruleType;
diff --git a/src/rules/no-caret-version-dependencies.js b/src/rules/no-caret-version-dependencies.js
new file mode 100644
index 0000000..826ac2f
--- /dev/null
+++ b/src/rules/no-caret-version-dependencies.js
@@ -0,0 +1,21 @@
+'use strict';
+
+const areVersRangesValid = require('./../validators/dependency-audit').areVersRangesValid;
+const LintIssue = require('./../LintIssue');
+const lintId = 'no-caret-version-dependencies';
+const nodeName = 'dependencies';
+const message = 'You are using an invalid version range. Please do not use ^.';
+const ruleType = 'standard';
+
+const lint = function(packageJsonData, severity) {
+  const rangeSpecifier = '^';
+
+  if (packageJsonData.hasOwnProperty(nodeName) && areVersRangesValid(packageJsonData, nodeName, rangeSpecifier)) {
+    return new LintIssue(lintId, severity, nodeName, message);
+  }
+
+  return true;
+};
+
+module.exports.lint = lint;
+module.exports.ruleType = ruleType;
diff --git a/src/rules/no-caret-version-devDependencies.js b/src/rules/no-caret-version-devDependencies.js
new file mode 100644
index 0000000..04a6e88
--- /dev/null
+++ b/src/rules/no-caret-version-devDependencies.js
@@ -0,0 +1,21 @@
+'use strict';
+
+const areVersRangesValid = require('./../validators/dependency-audit').areVersRangesValid;
+const LintIssue = require('./../LintIssue');
+const lintId = 'no-caret-version-devDependencies';
+const nodeName = 'devDependencies';
+const message = 'You are using an invalid version range. Please do not use ^.';
+const ruleType = 'standard';
+
+const lint = function(packageJsonData, severity) {
+  const rangeSpecifier = '^';
+
+  if (packageJsonData.hasOwnProperty(nodeName) && areVersRangesValid(packageJsonData, nodeName, rangeSpecifier)) {
+    return new LintIssue(lintId, severity, nodeName, message);
+  }
+
+  return true;
+};
+
+module.exports.lint = lint;
+module.exports.ruleType = ruleType;
diff --git a/src/rules/no-tilde-version-dependencies.js b/src/rules/no-tilde-version-dependencies.js
new file mode 100644
index 0000000..90f5f67
--- /dev/null
+++ b/src/rules/no-tilde-version-dependencies.js
@@ -0,0 +1,21 @@
+'use strict';
+
+const areVersRangesValid = require('./../validators/dependency-audit').areVersRangesValid;
+const LintIssue = require('./../LintIssue');
+const lintId = 'no-tilde-version-dependencies';
+const nodeName = 'dependencies';
+const message = 'You are using an invalid version range. Please do not use ~.';
+const ruleType = 'standard';
+
+const lint = function(packageJsonData, severity) {
+  const rangeSpecifier = '~';
+
+  if (packageJsonData.hasOwnProperty(nodeName) && areVersRangesValid(packageJsonData, nodeName, rangeSpecifier)) {
+    return new LintIssue(lintId, severity, nodeName, message);
+  }
+
+  return true;
+};
+
+module.exports.lint = lint;
+module.exports.ruleType = ruleType;
diff --git a/src/rules/no-tilde-version-devDependencies.js b/src/rules/no-tilde-version-devDependencies.js
new file mode 100644
index 0000000..978ac02
--- /dev/null
+++ b/src/rules/no-tilde-version-devDependencies.js
@@ -0,0 +1,21 @@
+'use strict';
+
+const areVersRangesValid = require('./../validators/dependency-audit').areVersRangesValid;
+const LintIssue = require('./../LintIssue');
+const lintId = 'no-tilde-version-devDependencies';
+const nodeName = 'devDependencies';
+const message = 'You are using an invalid version range. Please do not use ~.';
+const ruleType = 'standard';
+
+const lint = function(packageJsonData, severity) {
+  const rangeSpecifier = '~';
+
+  if (packageJsonData.hasOwnProperty(nodeName) && areVersRangesValid(packageJsonData, nodeName, rangeSpecifier)) {
+    return new LintIssue(lintId, severity, nodeName, message);
+  }
+
+  return true;
+};
+
+module.exports.lint = lint;
+module.exports.ruleType = ruleType;


Test patch:
diff --git a/tests/unit/rules/no-absolute-version-dependencies.test.js b/tests/unit/rules/no-absolute-version-dependencies.test.js
new file mode 100644
index 0000000..7dafa12
--- /dev/null
+++ b/tests/unit/rules/no-absolute-version-dependencies.test.js
@@ -0,0 +1,70 @@
+'use strict';
+
+const chai = require('chai');
+const ruleModule = require('./../../../src/rules/no-absolute-version-dependencies');
+const lint = ruleModule.lint;
+const ruleType = ruleModule.ruleType;
+
+const should = chai.should();
+
+describe('no-absolute-version-dependencies Unit Tests', function() {
+  context('a rule type value should be exported', function() {
+    it('it should equal "standard"', function() {
+      ruleType.should.equal('standard');
+    });
+  });
+
+  context('when package.json has node with an invalid value', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        dependencies: {
+          'npm-package-json-lint': '1.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.lintId.should.equal('no-absolute-version-dependencies');
+      response.severity.should.equal('error');
+      response.node.should.equal('dependencies');
+      response.lintMessage.should.equal('You are using an invalid version range. Please do not use absolute versions.');
+    });
+  });
+
+  context('when package.json has node with an invalid value (= prefixed)', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        dependencies: {
+          'npm-package-json-lint': '=1.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.lintId.should.equal('no-absolute-version-dependencies');
+      response.severity.should.equal('error');
+      response.node.should.equal('dependencies');
+      response.lintMessage.should.equal('You are using an invalid version range. Please do not use absolute versions.');
+    });
+  });
+
+  context('when package.json has node with a valid value', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        dependencies: {
+          'gulp-npm-package-json-lint': '~1.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/no-absolute-version-devDependencies.test.js b/tests/unit/rules/no-absolute-version-devDependencies.test.js
new file mode 100644
index 0000000..815e6c0
--- /dev/null
+++ b/tests/unit/rules/no-absolute-version-devDependencies.test.js
@@ -0,0 +1,70 @@
+'use strict';
+
+const chai = require('chai');
+const ruleModule = require('./../../../src/rules/no-absolute-version-devDependencies');
+const lint = ruleModule.lint;
+const ruleType = ruleModule.ruleType;
+
+const should = chai.should();
+
+describe('no-absolute-version-devDependencies Unit Tests', function() {
+  context('a rule type value should be exported', function() {
+    it('it should equal "standard"', function() {
+      ruleType.should.equal('standard');
+    });
+  });
+
+  context('when package.json has node with an invalid value', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        devDependencies: {
+          'npm-package-json-lint': '1.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.lintId.should.equal('no-absolute-version-devDependencies');
+      response.severity.should.equal('error');
+      response.node.should.equal('devDependencies');
+      response.lintMessage.should.equal('You are using an invalid version range. Please do not use absolute versions.');
+    });
+  });
+
+  context('when package.json has node with an invalid value (= prefixed)', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        devDependencies: {
+          'npm-package-json-lint': '=1.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.lintId.should.equal('no-absolute-version-devDependencies');
+      response.severity.should.equal('error');
+      response.node.should.equal('devDependencies');
+      response.lintMessage.should.equal('You are using an invalid version range. Please do not use absolute versions.');
+    });
+  });
+
+  context('when package.json has node with a valid value', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        devDependencies: {
+          'gulp-npm-package-json-lint': '~1.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/no-caret-version-dependencies.test.js b/tests/unit/rules/no-caret-version-dependencies.test.js
new file mode 100644
index 0000000..f0b1eb4
--- /dev/null
+++ b/tests/unit/rules/no-caret-version-dependencies.test.js
@@ -0,0 +1,54 @@
+'use strict';
+
+const chai = require('chai');
+const ruleModule = require('./../../../src/rules/no-caret-version-dependencies');
+const lint = ruleModule.lint;
+const ruleType = ruleModule.ruleType;
+
+const should = chai.should();
+
+describe('no-caret-version-dependencies Unit Tests', function() {
+  context('a rule type value should be exported', function() {
+    it('it should equal "standard"', function() {
+      ruleType.should.equal('standard');
+    });
+  });
+
+  context('when package.json has node with an invalid value', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        dependencies: {
+          'npm-package-json-lint': '^1.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.lintId.should.equal('no-caret-version-dependencies');
+      response.severity.should.equal('error');
+      response.node.should.equal('dependencies');
+      response.lintMessage.should.equal('You are using an invalid version range. Please do not use ^.');
+    });
+  });
+
+  context('when package.json has node with a valid value', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        dependencies: {
+          'gulp-npm-package-json-lint': '~1.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/no-caret-version-devDependencies.test.js b/tests/unit/rules/no-caret-version-devDependencies.test.js
new file mode 100644
index 0000000..e8f7756
--- /dev/null
+++ b/tests/unit/rules/no-caret-version-devDependencies.test.js
@@ -0,0 +1,54 @@
+'use strict';
+
+const chai = require('chai');
+const ruleModule = require('./../../../src/rules/no-caret-version-devDependencies');
+const lint = ruleModule.lint;
+const ruleType = ruleModule.ruleType;
+
+const should = chai.should();
+
+describe('no-caret-version-devDependencies Unit Tests', function() {
+  context('a rule type value should be exported', function() {
+    it('it should equal "standard"', function() {
+      ruleType.should.equal('standard');
+    });
+  });
+
+  context('when package.json has node with an invalid value', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        devDependencies: {
+          'npm-package-json-lint': '^1.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.lintId.should.equal('no-caret-version-devDependencies');
+      response.severity.should.equal('error');
+      response.node.should.equal('devDependencies');
+      response.lintMessage.should.equal('You are using an invalid version range. Please do not use ^.');
+    });
+  });
+
+  context('when package.json has node with a valid value', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        devDependencies: {
+          'gulp-npm-package-json-lint': '~1.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/no-tilde-version-dependencies.test.js b/tests/unit/rules/no-tilde-version-dependencies.test.js
new file mode 100644
index 0000000..798a19e
--- /dev/null
+++ b/tests/unit/rules/no-tilde-version-dependencies.test.js
@@ -0,0 +1,54 @@
+'use strict';
+
+const chai = require('chai');
+const ruleModule = require('./../../../src/rules/no-tilde-version-dependencies');
+const lint = ruleModule.lint;
+const ruleType = ruleModule.ruleType;
+
+const should = chai.should();
+
+describe('no-tilde-version-dependencies Unit Tests', function() {
+  context('a rule type value should be exported', function() {
+    it('it should equal "standard"', function() {
+      ruleType.should.equal('standard');
+    });
+  });
+
+  context('when package.json has node with an invalid value', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        dependencies: {
+          'npm-package-json-lint': '~1.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.lintId.should.equal('no-tilde-version-dependencies');
+      response.severity.should.equal('error');
+      response.node.should.equal('dependencies');
+      response.lintMessage.should.equal('You are using an invalid version range. Please do not use ~.');
+    });
+  });
+
+  context('when package.json has node with a valid value', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        dependencies: {
+          'gulp-npm-package-json-lint': '^1.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/no-tilde-version-devDependencies.test.js b/tests/unit/rules/no-tilde-version-devDependencies.test.js
new file mode 100644
index 0000000..2feaf9f
--- /dev/null
+++ b/tests/unit/rules/no-tilde-version-devDependencies.test.js
@@ -0,0 +1,54 @@
+'use strict';
+
+const chai = require('chai');
+const ruleModule = require('./../../../src/rules/no-tilde-version-devDependencies');
+const lint = ruleModule.lint;
+const ruleType = ruleModule.ruleType;
+
+const should = chai.should();
+
+describe('no-tilde-version-devDependencies Unit Tests', function() {
+  context('a rule type value should be exported', function() {
+    it('it should equal "standard"', function() {
+      ruleType.should.equal('standard');
+    });
+  });
+
+  context('when package.json has node with an invalid value', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        devDependencies: {
+          'npm-package-json-lint': '~1.0.0'
+        }
+      };
+      const response = lint(packageJsonData, 'error');
+
+      response.lintId.should.equal('no-tilde-version-devDependencies');
+      response.severity.should.equal('error');
+      response.node.should.equal('devDependencies');
+      response.lintMessage.should.equal('You are using an invalid version range. Please do not use ~.');
+    });
+  });
+
+  context('when package.json has node with a valid value', function() {
+    it('LintIssue object should be returned', function() {
+      const packageJsonData = {
+        devDependencies: {
+          'gulp-npm-package-json-lint': '^1.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;
+    });
+  });
+});