summaryrefslogtreecommitdiff
path: root/tools/node_modules/eslint/lib/rules/sort-imports.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/node_modules/eslint/lib/rules/sort-imports.js')
-rw-r--r--tools/node_modules/eslint/lib/rules/sort-imports.js196
1 files changed, 196 insertions, 0 deletions
diff --git a/tools/node_modules/eslint/lib/rules/sort-imports.js b/tools/node_modules/eslint/lib/rules/sort-imports.js
new file mode 100644
index 0000000000..2bd415e974
--- /dev/null
+++ b/tools/node_modules/eslint/lib/rules/sort-imports.js
@@ -0,0 +1,196 @@
+/**
+ * @fileoverview Rule to require sorting of import declarations
+ * @author Christian Schuller
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+ meta: {
+ docs: {
+ description: "enforce sorted import declarations within modules",
+ category: "ECMAScript 6",
+ recommended: false
+ },
+
+ schema: [
+ {
+ type: "object",
+ properties: {
+ ignoreCase: {
+ type: "boolean"
+ },
+ memberSyntaxSortOrder: {
+ type: "array",
+ items: {
+ enum: ["none", "all", "multiple", "single"]
+ },
+ uniqueItems: true,
+ minItems: 4,
+ maxItems: 4
+ },
+ ignoreMemberSort: {
+ type: "boolean"
+ }
+ },
+ additionalProperties: false
+ }
+ ],
+
+ fixable: "code"
+ },
+
+ create(context) {
+
+ const configuration = context.options[0] || {},
+ ignoreCase = configuration.ignoreCase || false,
+ ignoreMemberSort = configuration.ignoreMemberSort || false,
+ memberSyntaxSortOrder = configuration.memberSyntaxSortOrder || ["none", "all", "multiple", "single"],
+ sourceCode = context.getSourceCode();
+ let previousDeclaration = null;
+
+ /**
+ * Gets the used member syntax style.
+ *
+ * import "my-module.js" --> none
+ * import * as myModule from "my-module.js" --> all
+ * import {myMember} from "my-module.js" --> single
+ * import {foo, bar} from "my-module.js" --> multiple
+ *
+ * @param {ASTNode} node - the ImportDeclaration node.
+ * @returns {string} used member parameter style, ["all", "multiple", "single"]
+ */
+ function usedMemberSyntax(node) {
+ if (node.specifiers.length === 0) {
+ return "none";
+ }
+ if (node.specifiers[0].type === "ImportNamespaceSpecifier") {
+ return "all";
+ }
+ if (node.specifiers.length === 1) {
+ return "single";
+ }
+ return "multiple";
+
+ }
+
+ /**
+ * Gets the group by member parameter index for given declaration.
+ * @param {ASTNode} node - the ImportDeclaration node.
+ * @returns {number} the declaration group by member index.
+ */
+ function getMemberParameterGroupIndex(node) {
+ return memberSyntaxSortOrder.indexOf(usedMemberSyntax(node));
+ }
+
+ /**
+ * Gets the local name of the first imported module.
+ * @param {ASTNode} node - the ImportDeclaration node.
+ * @returns {?string} the local name of the first imported module.
+ */
+ function getFirstLocalMemberName(node) {
+ if (node.specifiers[0]) {
+ return node.specifiers[0].local.name;
+ }
+ return null;
+
+ }
+
+ return {
+ ImportDeclaration(node) {
+ if (previousDeclaration) {
+ const currentMemberSyntaxGroupIndex = getMemberParameterGroupIndex(node),
+ previousMemberSyntaxGroupIndex = getMemberParameterGroupIndex(previousDeclaration);
+ let currentLocalMemberName = getFirstLocalMemberName(node),
+ previousLocalMemberName = getFirstLocalMemberName(previousDeclaration);
+
+ if (ignoreCase) {
+ previousLocalMemberName = previousLocalMemberName && previousLocalMemberName.toLowerCase();
+ currentLocalMemberName = currentLocalMemberName && currentLocalMemberName.toLowerCase();
+ }
+
+ /*
+ * When the current declaration uses a different member syntax,
+ * then check if the ordering is correct.
+ * Otherwise, make a default string compare (like rule sort-vars to be consistent) of the first used local member name.
+ */
+ if (currentMemberSyntaxGroupIndex !== previousMemberSyntaxGroupIndex) {
+ if (currentMemberSyntaxGroupIndex < previousMemberSyntaxGroupIndex) {
+ context.report({
+ node,
+ message: "Expected '{{syntaxA}}' syntax before '{{syntaxB}}' syntax.",
+ data: {
+ syntaxA: memberSyntaxSortOrder[currentMemberSyntaxGroupIndex],
+ syntaxB: memberSyntaxSortOrder[previousMemberSyntaxGroupIndex]
+ }
+ });
+ }
+ } else {
+ if (previousLocalMemberName &&
+ currentLocalMemberName &&
+ currentLocalMemberName < previousLocalMemberName
+ ) {
+ context.report({
+ node,
+ message: "Imports should be sorted alphabetically."
+ });
+ }
+ }
+ }
+
+ if (!ignoreMemberSort) {
+ const importSpecifiers = node.specifiers.filter(specifier => specifier.type === "ImportSpecifier");
+ const getSortableName = ignoreCase ? specifier => specifier.local.name.toLowerCase() : specifier => specifier.local.name;
+ const firstUnsortedIndex = importSpecifiers.map(getSortableName).findIndex((name, index, array) => array[index - 1] > name);
+
+ if (firstUnsortedIndex !== -1) {
+ context.report({
+ node: importSpecifiers[firstUnsortedIndex],
+ message: "Member '{{memberName}}' of the import declaration should be sorted alphabetically.",
+ data: { memberName: importSpecifiers[firstUnsortedIndex].local.name },
+ fix(fixer) {
+ if (importSpecifiers.some(specifier =>
+ sourceCode.getCommentsBefore(specifier).length || sourceCode.getCommentsAfter(specifier).length)) {
+
+ // If there are comments in the ImportSpecifier list, don't rearrange the specifiers.
+ return null;
+ }
+
+ return fixer.replaceTextRange(
+ [importSpecifiers[0].range[0], importSpecifiers[importSpecifiers.length - 1].range[1]],
+ importSpecifiers
+
+ // Clone the importSpecifiers array to avoid mutating it
+ .slice()
+
+ // Sort the array into the desired order
+ .sort((specifierA, specifierB) => {
+ const aName = getSortableName(specifierA);
+ const bName = getSortableName(specifierB);
+
+ return aName > bName ? 1 : -1;
+ })
+
+ // Build a string out of the sorted list of import specifiers and the text between the originals
+ .reduce((sourceText, specifier, index) => {
+ const textAfterSpecifier = index === importSpecifiers.length - 1
+ ? ""
+ : sourceCode.getText().slice(importSpecifiers[index].range[1], importSpecifiers[index + 1].range[0]);
+
+ return sourceText + sourceCode.getText(specifier) + textAfterSpecifier;
+ }, "")
+ );
+ }
+ });
+ }
+ }
+
+ previousDeclaration = node;
+ }
+ };
+ }
+};