Skip to content
This repository has been archived by the owner on Mar 23, 2024. It is now read-only.

Commit

Permalink
New rule: require-imports-alphabetized
Browse files Browse the repository at this point in the history
Requires imports to be alphabetised

Fixes #2049
Closes gh-2091
  • Loading branch information
rheh authored and markelog committed Feb 1, 2016
1 parent 9c45089 commit 17daa29
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/config/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,7 @@ Configuration.prototype.registerDefaultRules = function() {
this.registerRule(require('../rules/require-enhanced-object-literals'));
this.registerRule(require('../rules/require-array-destructuring'));
this.registerRule(require('../rules/disallow-var'));
this.registerRule(require('../rules/require-imports-alphabetized'));
/* ES6 only (end) */

this.registerRule(require('../rules/require-curly-braces'));
Expand Down
94 changes: 94 additions & 0 deletions lib/rules/require-imports-alphabetized.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* Requires imports to be alphabetised
*
* Types: `Boolean`
*
* Values: `true` to require imports to be ordered (A-Z)
*
* #### Example
*
* ```js
* "requireImportAlphabetized": true
* ```
*
* ##### Valid
*
* ```js
* import a from 'a';
* import c from 'c';
* import z from 'z';
* ```
*
* ##### Invalid
*
* ```js
* import a from 'a';
* import z from 'z';
* import c from 'c';
* ```
*/

var assert = require('assert');

module.exports = function() {
};

module.exports.prototype = {

configure: function(option) {
assert(
option === true,
this.getOptionName() + ' option requires true value'
);
},

getOptionName: function() {
return 'requireImportAlphabetized';
},

check: function(file, errors) {

var previous;
var current;

var createSpecHash = function(specifier) {

var imported = '';
var local = '';

if (specifier.imported && specifier.imported.name) {
imported = specifier.imported.name;
}

if (specifier.local && specifier.local.name) {
local = specifier.local.name;
}

return imported === local ? imported : imported + local;

};

file.iterateNodesByType(
'ImportDeclaration',
function(node) {

current = '';

for (var i = 0; i < node.specifiers.length; i++) {
current += createSpecHash(node.specifiers[i]);
}

if (node.source && node.source.value) {
current += node.source.value;
}

if (previous && previous > current) {
errors.add('imports must be alphabetized',
node.loc.start.line, node.loc.start.column);
}

previous = current;
}
);
}
};
92 changes: 92 additions & 0 deletions test/specs/rules/require-imports-alphabetized.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
var Checker = require('../../../lib/checker');
var expect = require('chai').expect;

describe('rules/require-imports-aplhabetized', function() {
var checker;

beforeEach(function() {
checker = new Checker();
checker.registerDefaultRules();
checker.configure({ requireImportAlphabetized: true });
});

it('should report no errors when there are no imports', function() {
var code = '// no imports here;\n' +
'var looping = function(n) {\n' +
'var a = 0, b = 1, f = 1;\n' +
'for(var i = 2; i <= n; i++) {\n' +
'f = a + b;\n' +
'a = b;\n' +
'b = f;\n' +
'}\n' +
'return f; \n' +
'};';

expect(checker.checkString(code)).to.have.no.errors();
});

it('should report no errors for ordered imports', function() {
var code = 'import A from \'A\';\nimport C from \'C\';\nimport Z from \'Z\';';
expect(checker.checkString(code)).to.have.no.errors();
});

it('should report no errors for ordered import multiline', function() {
var code = 'import A\n from\n \'A\';\nimport C\n from \'C\';\nimport Z\n from \'Z\';';
expect(checker.checkString(code)).to.have.no.errors();
});

it('should report one error for un-ordered imports', function() {
var code = 'import A from \'A\';\nimport Z from \'Z\';\nimport B from \'B\';';
expect(checker.checkString(code)).to.have.one.error();
});

it('should report no errors for ordered named imports', function() {
var code = 'import { A as myNamed1, named2 } from \'src/mylib\';\n' +
'import { B as myNamed1, named2 } from \'src/mylib\';\n' +
'import { C as myNamed1, named2 } from \'src/mylib\';';

expect(checker.checkString(code)).to.have.no.errors();
});

it('should report one error for un-ordered named imports', function() {
var code = 'import { Z as myNamed1, named2 } from \'src/mylib\';\n' +
'import { B as myNamed1, named2 } from \'src/mylib\';\n' +
'import { C as myNamed1, named2 } from \'src/mylib\';';

expect(checker.checkString(code)).to.have.one.error();
});

it('should report no errors for ordered module as an object import', function() {
var code = 'import * from \'src/A\';\n' +
'import * from \'src/B\';\n' +
'import * from \'src/C\';';

expect(checker.checkString(code)).to.have.no.error();
});

it('should report one error for un-ordered module as an object import', function() {
var code = 'import * from \'src/A\';\n' +
'import * from \'src/Z\';\n' +
'import * from \'src/C\';';

expect(checker.checkString(code)).to.have.one.error();
});

it('should report no errors for ordered module import', function() {
var code = 'import \'src/A\';\n' +
'import \'src/B\';\n' +
'import \'src/C\';';

expect(checker.checkString(code)).to.have.no.error();
});

it('should report one error for un-ordered module import', function() {
var code = 'import \'src/Z\';\n import \'src/B\';\nimport \'src/C\';';
expect(checker.checkString(code)).to.have.one.error();
});

it('should report multiple errors for un-ordered module import', function() {
var code = 'import \'src/C\';\n import \'src/B\';\nimport \'src/A\';';
expect(checker.checkString(code)).to.have.validation.errors.from('requireImportAlphabetized');
});
});

0 comments on commit 17daa29

Please sign in to comment.