UNPKG

6.11 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to require sorting of import declarations
3 * @author Christian Schuller
4 * @copyright 2015 Christian Schuller. All rights reserved.
5 * See LICENSE file in root directory for full license.
6 */
7
8"use strict";
9
10//------------------------------------------------------------------------------
11// Rule Definition
12//------------------------------------------------------------------------------
13
14module.exports = function(context) {
15
16 var configuration = context.options[0] || {},
17 ignoreCase = configuration.ignoreCase || false,
18 ignoreMemberSort = configuration.ignoreMemberSort || false,
19 memberSyntaxSortOrder = configuration.memberSyntaxSortOrder || ["none", "all", "multiple", "single"],
20 previousDeclaration = null;
21
22 /**
23 * Gets the used member syntax style.
24 *
25 * import "my-module.js" --> none
26 * import * as myModule from "my-module.js" --> all
27 * import {myMember} from "my-module.js" --> single
28 * import {foo, bar} from "my-module.js" --> multiple
29 *
30 * @param {ASTNode} node - the ImportDeclaration node.
31 * @returns {string} used member parameter style, ["all", "multiple", "single"]
32 */
33 function usedMemberSyntax(node) {
34 if (node.specifiers.length === 0) {
35 return "none";
36 } else if (node.specifiers[0].type === "ImportNamespaceSpecifier") {
37 return "all";
38 } else if (node.specifiers.length === 1) {
39 return "single";
40 } else {
41 return "multiple";
42 }
43 }
44
45 /**
46 * Gets the group by member parameter index for given declaration.
47 * @param {ASTNode} node - the ImportDeclaration node.
48 * @returns {number} the declaration group by member index.
49 */
50 function getMemberParameterGroupIndex(node) {
51 return memberSyntaxSortOrder.indexOf(usedMemberSyntax(node));
52 }
53
54 /**
55 * Gets the local name of the first imported module.
56 * @param {ASTNode} node - the ImportDeclaration node.
57 * @returns {?string} the local name of the first imported module.
58 */
59 function getFirstLocalMemberName(node) {
60 if (node.specifiers[0]) {
61 return node.specifiers[0].local.name;
62 } else {
63 return null;
64 }
65 }
66
67 return {
68 "ImportDeclaration": function(node) {
69 if (previousDeclaration) {
70 var currentLocalMemberName = getFirstLocalMemberName(node),
71 currentMemberSyntaxGroupIndex = getMemberParameterGroupIndex(node),
72 previousLocalMemberName = getFirstLocalMemberName(previousDeclaration),
73 previousMemberSyntaxGroupIndex = getMemberParameterGroupIndex(previousDeclaration);
74
75 if (ignoreCase) {
76 previousLocalMemberName = previousLocalMemberName.toLowerCase();
77 currentLocalMemberName = currentLocalMemberName.toLowerCase();
78 }
79
80 // When the current declaration uses a different member syntax,
81 // then check if the ordering is correct.
82 // Otherwise, make a default string compare (like rule sort-vars to be consistent) of the first used local member name.
83 if (currentMemberSyntaxGroupIndex !== previousMemberSyntaxGroupIndex) {
84 if (currentMemberSyntaxGroupIndex < previousMemberSyntaxGroupIndex) {
85 context.report({
86 node: node,
87 message: "Expected '{{syntaxA}}' syntax before '{{syntaxB}}' syntax.",
88 data: {
89 syntaxA: memberSyntaxSortOrder[currentMemberSyntaxGroupIndex],
90 syntaxB: memberSyntaxSortOrder[previousMemberSyntaxGroupIndex]
91 }
92 });
93 }
94 } else {
95 if (currentLocalMemberName < previousLocalMemberName) {
96 context.report({
97 node: node,
98 message: "Imports should be sorted alphabetically."
99 });
100 }
101 }
102 }
103
104 // Multiple members of an import declaration should also be sorted alphabetically.
105 if (!ignoreMemberSort && node.specifiers.length > 1) {
106 node.specifiers.reduce(function(previousSpecifier, currentSpecifier) {
107 var currentSpecifierName = currentSpecifier.local.name,
108 previousSpecifierName = previousSpecifier.local.name;
109
110 if (ignoreCase) {
111 currentSpecifierName = currentSpecifierName.toLowerCase();
112 previousSpecifierName = previousSpecifierName.toLowerCase();
113 }
114
115 if (currentSpecifierName < previousSpecifierName) {
116 context.report({
117 node: currentSpecifier,
118 message: "Member '{{memberName}}' of the import declaration should be sorted alphabetically.",
119 data: {
120 memberName: currentSpecifier.local.name
121 }
122 });
123 }
124
125 return currentSpecifier;
126 }, node.specifiers[0]);
127 }
128
129 previousDeclaration = node;
130 }
131 };
132};
133
134module.exports.schema = [
135 {
136 "type": "object",
137 "properties": {
138 "ignoreCase": {
139 "type": "boolean"
140 },
141 "memberSyntaxSortOrder": {
142 "type": "array",
143 "items": {
144 "enum": ["none", "all", "multiple", "single"]
145 },
146 "uniqueItems": true,
147 "minItems": 4,
148 "maxItems": 4
149 },
150 "ignoreMemberSort": {
151 "type": "boolean"
152 }
153 },
154 "additionalProperties": false
155 }
156];