minor adjustment to readme
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / sort-imports.js
1 /**
2  * @fileoverview Rule to require sorting of import declarations
3  * @author Christian Schuller
4  */
5
6 "use strict";
7
8 //------------------------------------------------------------------------------
9 // Rule Definition
10 //------------------------------------------------------------------------------
11
12 module.exports = {
13     meta: {
14         type: "suggestion",
15
16         docs: {
17             description: "enforce sorted import declarations within modules",
18             category: "ECMAScript 6",
19             recommended: false,
20             url: "https://eslint.org/docs/rules/sort-imports"
21         },
22
23         schema: [
24             {
25                 type: "object",
26                 properties: {
27                     ignoreCase: {
28                         type: "boolean",
29                         default: false
30                     },
31                     memberSyntaxSortOrder: {
32                         type: "array",
33                         items: {
34                             enum: ["none", "all", "multiple", "single"]
35                         },
36                         uniqueItems: true,
37                         minItems: 4,
38                         maxItems: 4
39                     },
40                     ignoreDeclarationSort: {
41                         type: "boolean",
42                         default: false
43                     },
44                     ignoreMemberSort: {
45                         type: "boolean",
46                         default: false
47                     }
48                 },
49                 additionalProperties: false
50             }
51         ],
52
53         fixable: "code"
54     },
55
56     create(context) {
57
58         const configuration = context.options[0] || {},
59             ignoreCase = configuration.ignoreCase || false,
60             ignoreDeclarationSort = configuration.ignoreDeclarationSort || false,
61             ignoreMemberSort = configuration.ignoreMemberSort || false,
62             memberSyntaxSortOrder = configuration.memberSyntaxSortOrder || ["none", "all", "multiple", "single"],
63             sourceCode = context.getSourceCode();
64         let previousDeclaration = null;
65
66         /**
67          * Gets the used member syntax style.
68          *
69          * import "my-module.js" --> none
70          * import * as myModule from "my-module.js" --> all
71          * import {myMember} from "my-module.js" --> single
72          * import {foo, bar} from  "my-module.js" --> multiple
73          * @param {ASTNode} node the ImportDeclaration node.
74          * @returns {string} used member parameter style, ["all", "multiple", "single"]
75          */
76         function usedMemberSyntax(node) {
77             if (node.specifiers.length === 0) {
78                 return "none";
79             }
80             if (node.specifiers[0].type === "ImportNamespaceSpecifier") {
81                 return "all";
82             }
83             if (node.specifiers.length === 1) {
84                 return "single";
85             }
86             return "multiple";
87
88         }
89
90         /**
91          * Gets the group by member parameter index for given declaration.
92          * @param {ASTNode} node the ImportDeclaration node.
93          * @returns {number} the declaration group by member index.
94          */
95         function getMemberParameterGroupIndex(node) {
96             return memberSyntaxSortOrder.indexOf(usedMemberSyntax(node));
97         }
98
99         /**
100          * Gets the local name of the first imported module.
101          * @param {ASTNode} node the ImportDeclaration node.
102          * @returns {?string} the local name of the first imported module.
103          */
104         function getFirstLocalMemberName(node) {
105             if (node.specifiers[0]) {
106                 return node.specifiers[0].local.name;
107             }
108             return null;
109
110         }
111
112         return {
113             ImportDeclaration(node) {
114                 if (!ignoreDeclarationSort) {
115                     if (previousDeclaration) {
116                         const currentMemberSyntaxGroupIndex = getMemberParameterGroupIndex(node),
117                             previousMemberSyntaxGroupIndex = getMemberParameterGroupIndex(previousDeclaration);
118                         let currentLocalMemberName = getFirstLocalMemberName(node),
119                             previousLocalMemberName = getFirstLocalMemberName(previousDeclaration);
120
121                         if (ignoreCase) {
122                             previousLocalMemberName = previousLocalMemberName && previousLocalMemberName.toLowerCase();
123                             currentLocalMemberName = currentLocalMemberName && currentLocalMemberName.toLowerCase();
124                         }
125
126                         /*
127                          * When the current declaration uses a different member syntax,
128                          * then check if the ordering is correct.
129                          * Otherwise, make a default string compare (like rule sort-vars to be consistent) of the first used local member name.
130                          */
131                         if (currentMemberSyntaxGroupIndex !== previousMemberSyntaxGroupIndex) {
132                             if (currentMemberSyntaxGroupIndex < previousMemberSyntaxGroupIndex) {
133                                 context.report({
134                                     node,
135                                     message: "Expected '{{syntaxA}}' syntax before '{{syntaxB}}' syntax.",
136                                     data: {
137                                         syntaxA: memberSyntaxSortOrder[currentMemberSyntaxGroupIndex],
138                                         syntaxB: memberSyntaxSortOrder[previousMemberSyntaxGroupIndex]
139                                     }
140                                 });
141                             }
142                         } else {
143                             if (previousLocalMemberName &&
144                                 currentLocalMemberName &&
145                                 currentLocalMemberName < previousLocalMemberName
146                             ) {
147                                 context.report({
148                                     node,
149                                     message: "Imports should be sorted alphabetically."
150                                 });
151                             }
152                         }
153                     }
154
155                     previousDeclaration = node;
156                 }
157
158                 if (!ignoreMemberSort) {
159                     const importSpecifiers = node.specifiers.filter(specifier => specifier.type === "ImportSpecifier");
160                     const getSortableName = ignoreCase ? specifier => specifier.local.name.toLowerCase() : specifier => specifier.local.name;
161                     const firstUnsortedIndex = importSpecifiers.map(getSortableName).findIndex((name, index, array) => array[index - 1] > name);
162
163                     if (firstUnsortedIndex !== -1) {
164                         context.report({
165                             node: importSpecifiers[firstUnsortedIndex],
166                             message: "Member '{{memberName}}' of the import declaration should be sorted alphabetically.",
167                             data: { memberName: importSpecifiers[firstUnsortedIndex].local.name },
168                             fix(fixer) {
169                                 if (importSpecifiers.some(specifier =>
170                                     sourceCode.getCommentsBefore(specifier).length || sourceCode.getCommentsAfter(specifier).length)) {
171
172                                     // If there are comments in the ImportSpecifier list, don't rearrange the specifiers.
173                                     return null;
174                                 }
175
176                                 return fixer.replaceTextRange(
177                                     [importSpecifiers[0].range[0], importSpecifiers[importSpecifiers.length - 1].range[1]],
178                                     importSpecifiers
179
180                                         // Clone the importSpecifiers array to avoid mutating it
181                                         .slice()
182
183                                         // Sort the array into the desired order
184                                         .sort((specifierA, specifierB) => {
185                                             const aName = getSortableName(specifierA);
186                                             const bName = getSortableName(specifierB);
187
188                                             return aName > bName ? 1 : -1;
189                                         })
190
191                                         // Build a string out of the sorted list of import specifiers and the text between the originals
192                                         .reduce((sourceText, specifier, index) => {
193                                             const textAfterSpecifier = index === importSpecifiers.length - 1
194                                                 ? ""
195                                                 : sourceCode.getText().slice(importSpecifiers[index].range[1], importSpecifiers[index + 1].range[0]);
196
197                                             return sourceText + sourceCode.getText(specifier) + textAfterSpecifier;
198                                         }, "")
199                                 );
200                             }
201                         });
202                     }
203                 }
204             }
205         };
206     }
207 };