Skip to content

Commit 89d8422

Browse files
authored
Closes #50 - Add support for checking package.json property order (#51)
* Closes #50 - Add support for checking package.json property order Add a new rule, prefer-property-order, that allows users to specify an array of properties in the order they desire. Fixed a bug that caused alphabetical-sort to quietly go out of bounds * Remove accidental console.log statement
1 parent 66b748c commit 89d8422

File tree

9 files changed

+218
-4
lines changed

9 files changed

+218
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1111

1212
### Removed
1313

14+
## [2.8.0] - 2017-08-16
15+
### Added
16+
- New rule: [prefer-property-order](https://github.com/tclindner/npm-package-json-lint/wiki/prefer-property-order)
17+
1418
## [2.7.1] - 2017-08-15
1519
### Fixed
1620
- [Issue #48](https://github.com/tclindner/npm-package-json-lint/issues/48) [valid-values-license](https://github.com/tclindner/npm-package-json-lint/wiki/valid-values-license)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "npm-package-json-lint",
3-
"version": "2.7.1",
3+
"version": "2.8.0",
44
"description": "CLI app for linting package.json files.",
55
"keywords": [
66
"lint",

src/Config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class Config {
2323
'no-restricted-dependencies',
2424
'no-restricted-devDependencies',
2525
'no-restricted-pre-release-dependencies',
26-
'no-restricted-pre-release-devDependencies'
26+
'no-restricted-pre-release-devDependencies',
27+
'prefer-property-order'
2728
];
2829

2930
this.passedConfigParam = passedConfigParam;

src/rules/prefer-property-order.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
3+
const isInPreferredOrder = require('./../validators/property-order').isInPreferredOrder;
4+
const LintIssue = require('./../LintIssue');
5+
const lintId = 'prefer-property-order';
6+
const nodeName = '';
7+
const message = 'Your package.json properties are not in the desired order.';
8+
const ruleType = 'property-order';
9+
10+
const lint = function(packageJsonData, lintType, preferredOrder) {
11+
const result = isInPreferredOrder(packageJsonData, preferredOrder);
12+
13+
if (!result.status) {
14+
let helpTip = '';
15+
16+
if (result.data.actualNode === null) {
17+
helpTip = `Please add ${result.data.desiredNode} at the end of the file.`;
18+
} else {
19+
helpTip = `Please move ${result.data.actualNode} after ${result.data.desiredNode}.`;
20+
}
21+
22+
return new LintIssue(lintId, lintType, nodeName, `${message} ${helpTip}`);
23+
}
24+
25+
return true;
26+
};
27+
28+
module.exports.lint = lint;
29+
module.exports.ruleType = ruleType;

src/validators/alphabetical-sort.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const isInAlphabeticalOrder = function(packageJsonData, nodeName) {
2727
const nodeKeysOriginal = Object.keys(packageJsonData[nodeName]);
2828
const nodeKeysSorted = Object.keys(packageJsonData[nodeName]).sort();
2929

30-
for (let keyIndex = 0;keyIndex <= nodeKeysOriginal.length;keyIndex += increment) {
30+
for (let keyIndex = 0;keyIndex < nodeKeysOriginal.length;keyIndex += increment) {
3131
if (nodeKeysOriginal[keyIndex] !== nodeKeysSorted[keyIndex]) {
3232
isValid = false;
3333
data = {

src/validators/property-order.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
3+
const increment = 1;
4+
5+
/**
6+
* Determines whether an array is in the specified order
7+
* @param {Object} packageJsonData Valid JSON
8+
* @param {Array} preferredNodeOrder Preferred order of nodes
9+
* @return {Object} Object containing the status and the node that is out of order, if applicable
10+
*/
11+
const isInPreferredOrder = function(packageJsonData, preferredNodeOrder) {
12+
let isValid = true;
13+
let data = {
14+
actualNode: null,
15+
desiredNode: null
16+
};
17+
const actualNodeList = Object.keys(packageJsonData);
18+
19+
for (let keyIndex = 0;keyIndex < preferredNodeOrder.length;keyIndex += increment) {
20+
if (typeof actualNodeList[keyIndex] === 'undefined') {
21+
isValid = false;
22+
data = {
23+
actualNode: null,
24+
desiredNode: preferredNodeOrder[keyIndex]
25+
};
26+
break;
27+
} else if (actualNodeList[keyIndex] !== preferredNodeOrder[keyIndex]) {
28+
isValid = false;
29+
data = {
30+
actualNode: actualNodeList[keyIndex],
31+
desiredNode: preferredNodeOrder[keyIndex]
32+
};
33+
break;
34+
}
35+
}
36+
37+
return {
38+
status: isValid,
39+
data
40+
};
41+
};
42+
43+
module.exports.isInPreferredOrder = isInPreferredOrder;

tests/unit/Config.test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,8 @@ describe('Config Unit Tests', function() {
387387
'require-version': 'warning',
388388
'valid-values-author': ['error', ['Thomas', 'Lindner', 'Thomas Lindner']],
389389
'valid-values-private': ['warning', [true, false]],
390-
'valid-values-license': ['error', ['private', 'unlicensed']]
390+
'valid-values-license': ['error', ['private', 'unlicensed']],
391+
'prefer-property-order': ['error', ['name', 'version']]
391392
};
392393
const config = new Config(rcFileObj);
393394

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use strict';
2+
3+
const chai = require('chai');
4+
const lint = require('./../../../src/rules/prefer-property-order').lint;
5+
6+
const should = chai.should();
7+
8+
describe('prefer-property-order Unit Tests', function() {
9+
context('when the properties in the package.json file are in the desired order', function() {
10+
it('true should be returned', function() {
11+
const packageJsonData = {
12+
name: 'awesome-module',
13+
version: '1.0.0',
14+
description: 'description'
15+
};
16+
const preferredOrder = [
17+
'name',
18+
'version',
19+
'description'
20+
];
21+
const response = lint(packageJsonData, 'error', preferredOrder);
22+
23+
response.should.be.true;
24+
});
25+
});
26+
27+
context('when the actual node list does not have the same number of nodes as the desired list', function() {
28+
it('LintIssue object should be returned', function() {
29+
const packageJsonData = {
30+
name: 'awesome-module',
31+
version: '1.0.0'
32+
};
33+
const preferredOrder = [
34+
'name',
35+
'version',
36+
'description'
37+
];
38+
const response = lint(packageJsonData, 'error', preferredOrder);
39+
40+
response.lintId.should.equal('prefer-property-order');
41+
response.lintType.should.equal('error');
42+
response.node.should.equal('');
43+
response.lintMessage.should.equal('Your package.json properties are not in the desired order. Please add description at the end of the file.');
44+
});
45+
});
46+
47+
context('when the actual node list is in a different order than desired', function() {
48+
it('LintIssue object should be returned', function() {
49+
const packageJsonData = {
50+
name: 'awesome-module',
51+
description: 'description',
52+
version: '1.0.0'
53+
};
54+
const preferredOrder = [
55+
'name',
56+
'version',
57+
'description'
58+
];
59+
const response = lint(packageJsonData, 'error', preferredOrder);
60+
61+
response.lintId.should.equal('prefer-property-order');
62+
response.lintType.should.equal('error');
63+
response.node.should.equal('');
64+
response.lintMessage.should.equal('Your package.json properties are not in the desired order. Please move description after version.');
65+
});
66+
});
67+
});
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
'use strict';
2+
3+
const chai = require('chai');
4+
const propertyOrder = require('./../../../src/validators/property-order');
5+
6+
const should = chai.should();
7+
8+
describe('property-order Unit Tests', function() {
9+
describe('isInPreferredOrder method', function() {
10+
context('when the properties in the package.json file are in the desired order', function() {
11+
it('true should be returned', function() {
12+
const packageJson = {
13+
name: 'awesome-module',
14+
version: '1.0.0',
15+
description: 'description'
16+
};
17+
const preferredOrder = [
18+
'name',
19+
'version',
20+
'description'
21+
];
22+
const response = propertyOrder.isInPreferredOrder(packageJson, preferredOrder);
23+
24+
response.status.should.be.true;
25+
(response.data.actualNode === null).should.be.true;
26+
(response.data.desiredNode === null).should.be.true;
27+
});
28+
});
29+
30+
context('when the actual node list does not have the same number of nodes as the desired list', function() {
31+
it('false should be returned', function() {
32+
const packageJson = {
33+
name: 'awesome-module',
34+
version: '1.0.0'
35+
};
36+
const preferredOrder = [
37+
'name',
38+
'version',
39+
'description'
40+
];
41+
const response = propertyOrder.isInPreferredOrder(packageJson, preferredOrder);
42+
43+
response.status.should.be.false;
44+
(response.data.actualNode === null).should.be.true;
45+
response.data.desiredNode.should.equal('description');
46+
});
47+
});
48+
49+
context('when the actual node list is in a different order than desired', function() {
50+
it('false should be returned', function() {
51+
const packageJson = {
52+
name: 'awesome-module',
53+
description: 'description',
54+
version: '1.0.0'
55+
};
56+
const preferredOrder = [
57+
'name',
58+
'version',
59+
'description'
60+
];
61+
const response = propertyOrder.isInPreferredOrder(packageJson, preferredOrder);
62+
63+
response.status.should.be.false;
64+
response.data.actualNode.should.equal('description');
65+
response.data.desiredNode.should.equal('version');
66+
});
67+
});
68+
});
69+
});

0 commit comments

Comments
 (0)