From b34d9ff9f2c1ab45ff4a5e840f802b16be111da2 Mon Sep 17 00:00:00 2001 From: Eugene Tihonov Date: Wed, 28 Mar 2018 20:54:53 +0500 Subject: [PATCH] Add autofixer for order rule (#908) --- CHANGELOG.md | 2 + docs/rules/order.md | 4 +- src/rules/order.js | 247 ++++++++++++++++++-- tests/src/rules/order.js | 490 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 717 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 068bffea2..a88cc01aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com). ## [Unreleased] +- Autofixer for [`order`] rule ([#711], thanks [@tihonove]) ## [2.9.0] - 2018-02-21 ### Added @@ -679,6 +680,7 @@ for info on changes for earlier releases. [@mplewis]: https://github.com/mplewis [@rosswarren]: https://github.com/rosswarren [@alexgorbatchev]: https://github.com/alexgorbatchev +[@tihonove]: https://github.com/tihonove [@robertrossmann]: https://github.com/robertrossmann [@isiahmeadows]: https://github.com/isiahmeadows [@graingert]: https://github.com/graingert diff --git a/docs/rules/order.md b/docs/rules/order.md index f51152cf5..45bde6acc 100644 --- a/docs/rules/order.md +++ b/docs/rules/order.md @@ -1,6 +1,8 @@ # import/order: Enforce a convention in module import order -Enforce a convention in the order of `require()` / `import` statements. The order is as shown in the following example: +Enforce a convention in the order of `require()` / `import` statements. ++(fixable) The `--fix` option on the [command line] automatically fixes problems reported by this rule. +The order is as shown in the following example: ```js // 1. node "builtin" modules diff --git a/src/rules/order.js b/src/rules/order.js index cdda60358..81babd7fd 100644 --- a/src/rules/order.js +++ b/src/rules/order.js @@ -6,7 +6,7 @@ import docsUrl from '../docsUrl' const defaultGroups = ['builtin', 'external', 'parent', 'sibling', 'index'] -// REPORTING +// REPORTING AND FIXING function reverse(array) { return array.map(function (v) { @@ -18,6 +18,60 @@ function reverse(array) { }).reverse() } +function getTokensOrCommentsAfter(sourceCode, node, count) { + let currentNodeOrToken = node + const result = [] + for (let i = 0; i < count; i++) { + currentNodeOrToken = sourceCode.getTokenOrCommentAfter(currentNodeOrToken) + if (currentNodeOrToken == null) { + break + } + result.push(currentNodeOrToken) + } + return result +} + +function getTokensOrCommentsBefore(sourceCode, node, count) { + let currentNodeOrToken = node + const result = [] + for (let i = 0; i < count; i++) { + currentNodeOrToken = sourceCode.getTokenOrCommentBefore(currentNodeOrToken) + if (currentNodeOrToken == null) { + break + } + result.push(currentNodeOrToken) + } + return result.reverse() +} + +function takeTokensAfterWhile(sourceCode, node, condition) { + const tokens = getTokensOrCommentsAfter(sourceCode, node, 100) + const result = [] + for (let i = 0; i < tokens.length; i++) { + if (condition(tokens[i])) { + result.push(tokens[i]) + } + else { + break + } + } + return result +} + +function takeTokensBeforeWhile(sourceCode, node, condition) { + const tokens = getTokensOrCommentsBefore(sourceCode, node, 100) + const result = [] + for (let i = tokens.length - 1; i >= 0; i--) { + if (condition(tokens[i])) { + result.push(tokens[i]) + } + else { + break + } + } + return result.reverse() +} + function findOutOfOrder(imported) { if (imported.length === 0) { return [] @@ -32,13 +86,141 @@ function findOutOfOrder(imported) { }) } +function findRootNode(node) { + let parent = node + while (parent.parent != null && parent.parent.body == null) { + parent = parent.parent + } + return parent +} + +function findEndOfLineWithComments(sourceCode, node) { + const tokensToEndOfLine = takeTokensAfterWhile(sourceCode, node, commentOnSameLineAs(node)) + let endOfTokens = tokensToEndOfLine.length > 0 + ? tokensToEndOfLine[tokensToEndOfLine.length - 1].end + : node.end + let result = endOfTokens + for (let i = endOfTokens; i < sourceCode.text.length; i++) { + if (sourceCode.text[i] === '\n') { + result = i + 1 + break + } + if (sourceCode.text[i] !== ' ' && sourceCode.text[i] !== '\t' && sourceCode.text[i] !== '\r') { + break + } + result = i + 1 + } + return result +} + +function commentOnSameLineAs(node) { + return token => (token.type === 'Block' || token.type === 'Line') && + token.loc.start.line === token.loc.end.line && + token.loc.end.line === node.loc.end.line +} + +function findStartOfLineWithComments(sourceCode, node) { + const tokensToEndOfLine = takeTokensBeforeWhile(sourceCode, node, commentOnSameLineAs(node)) + let startOfTokens = tokensToEndOfLine.length > 0 ? tokensToEndOfLine[0].start : node.start + let result = startOfTokens + for (let i = startOfTokens - 1; i > 0; i--) { + if (sourceCode.text[i] !== ' ' && sourceCode.text[i] !== '\t') { + break + } + result = i + } + return result +} + +function isPlainRequireModule(node) { + if (node.type !== 'VariableDeclaration') { + return false + } + if (node.declarations.length !== 1) { + return false + } + const decl = node.declarations[0] + const result = (decl.id != null && decl.id.type === 'Identifier') && + decl.init != null && + decl.init.type === 'CallExpression' && + decl.init.callee != null && + decl.init.callee.name === 'require' && + decl.init.arguments != null && + decl.init.arguments.length === 1 && + decl.init.arguments[0].type === 'Literal' + return result +} + +function isPlainImportModule(node) { + return node.type === 'ImportDeclaration' && node.specifiers != null && node.specifiers.length > 0 +} + +function canCrossNodeWhileReorder(node) { + return isPlainRequireModule(node) || isPlainImportModule(node) +} + +function canReorderItems(firstNode, secondNode) { + const parent = firstNode.parent + const firstIndex = parent.body.indexOf(firstNode) + const secondIndex = parent.body.indexOf(secondNode) + const nodesBetween = parent.body.slice(firstIndex, secondIndex + 1) + for (var nodeBetween of nodesBetween) { + if (!canCrossNodeWhileReorder(nodeBetween)) { + return false + } + } + return true +} + +function fixOutOfOrder(context, firstNode, secondNode, order) { + const sourceCode = context.getSourceCode() + + const firstRoot = findRootNode(firstNode.node) + let firstRootStart = findStartOfLineWithComments(sourceCode, firstRoot) + const firstRootEnd = findEndOfLineWithComments(sourceCode, firstRoot) + + const secondRoot = findRootNode(secondNode.node) + let secondRootStart = findStartOfLineWithComments(sourceCode, secondRoot) + let secondRootEnd = findEndOfLineWithComments(sourceCode, secondRoot) + const canFix = canReorderItems(firstRoot, secondRoot) + + let newCode = sourceCode.text.substring(secondRootStart, secondRootEnd) + if (newCode[newCode.length - 1] !== '\n') { + newCode = newCode + '\n' + } + + const message = '`' + secondNode.name + '` import should occur ' + order + + ' import of `' + firstNode.name + '`' + + if (order === 'before') { + context.report({ + node: secondNode.node, + message: message, + fix: canFix && (fixer => + fixer.replaceTextRange( + [firstRootStart, secondRootEnd], + newCode + sourceCode.text.substring(firstRootStart, secondRootStart) + )), + }) + } else if (order === 'after') { + context.report({ + node: secondNode.node, + message: message, + fix: canFix && (fixer => + fixer.replaceTextRange( + [secondRootStart, firstRootEnd], + sourceCode.text.substring(secondRootEnd, firstRootEnd) + newCode + )), + }) + } +} + function reportOutOfOrder(context, imported, outOfOrder, order) { outOfOrder.forEach(function (imp) { const found = imported.find(function hasHigherRank(importedItem) { return importedItem.rank > imp.rank }) - context.report(imp.node, '`' + imp.name + '` import should occur ' + order + - ' import of `' + found.name + '`') + fixOutOfOrder(context, found, imp, order) }) } @@ -109,6 +291,32 @@ function convertGroupsToRanks(groups) { }, rankObject) } +function fixNewLineAfterImport(context, previousImport) { + const prevRoot = findRootNode(previousImport.node) + const tokensToEndOfLine = takeTokensAfterWhile( + context.getSourceCode(), prevRoot, commentOnSameLineAs(prevRoot)) + + let endOfLine = prevRoot.end + if (tokensToEndOfLine.length > 0) { + endOfLine = tokensToEndOfLine[tokensToEndOfLine.length - 1].end + } + return (fixer) => fixer.insertTextAfterRange([prevRoot.start, endOfLine], '\n') +} + +function removeNewLineAfterImport(context, currentImport, previousImport) { + const sourceCode = context.getSourceCode() + const prevRoot = findRootNode(previousImport.node) + const currRoot = findRootNode(currentImport.node) + const rangeToRemove = [ + findEndOfLineWithComments(sourceCode, prevRoot), + findStartOfLineWithComments(sourceCode, currRoot), + ] + if (/^\s*$/.test(sourceCode.text.substring(rangeToRemove[0], rangeToRemove[1]))) { + return (fixer) => fixer.removeRange(rangeToRemove) + } + return undefined +} + function makeNewlinesBetweenReport (context, imported, newlinesBetweenImports) { const getNumberOfEmptyLinesBetween = (currentImport, previousImport) => { const linesBetweenImports = context.getSourceCode().lines.slice( @@ -125,23 +333,27 @@ function makeNewlinesBetweenReport (context, imported, newlinesBetweenImports) { if (newlinesBetweenImports === 'always' || newlinesBetweenImports === 'always-and-inside-groups') { - if (currentImport.rank !== previousImport.rank && emptyLinesBetween === 0) - { - context.report( - previousImport.node, 'There should be at least one empty line between import groups' - ) + if (currentImport.rank !== previousImport.rank && emptyLinesBetween === 0) { + context.report({ + node: previousImport.node, + message: 'There should be at least one empty line between import groups', + fix: fixNewLineAfterImport(context, previousImport, currentImport), + }) } else if (currentImport.rank === previousImport.rank && emptyLinesBetween > 0 - && newlinesBetweenImports !== 'always-and-inside-groups') - { - context.report( - previousImport.node, 'There should be no empty line within import group' - ) - } - } else { - if (emptyLinesBetween > 0) { - context.report(previousImport.node, 'There should be no empty line between import groups') + && newlinesBetweenImports !== 'always-and-inside-groups') { + context.report({ + node: previousImport.node, + message: 'There should be no empty line within import group', + fix: removeNewLineAfterImport(context, currentImport, previousImport), + }) } + } else if (emptyLinesBetween > 0) { + context.report({ + node: previousImport.node, + message: 'There should be no empty line between import groups', + fix: removeNewLineAfterImport(context, currentImport, previousImport), + }) } previousImport = currentImport @@ -154,6 +366,7 @@ module.exports = { url: docsUrl('order'), }, + fixable: 'code', schema: [ { type: 'object', diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index c87508573..fb3b78844 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -5,6 +5,10 @@ import { RuleTester } from 'eslint' const ruleTester = new RuleTester() , rule = require('rules/order') +function withoutAutofixOutput(test) { + return Object.assign({}, test, { output: test.code }) +} + ruleTester.run('order', rule, { valid: [ // Default order using require @@ -410,6 +414,135 @@ ruleTester.run('order', rule, { var async = require('async'); var fs = require('fs'); `, + output: ` + var fs = require('fs'); + var async = require('async'); + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + }), + // fix order with spaces on the end of line + test({ + code: ` + var async = require('async'); + var fs = require('fs');${' '} + `, + output: ` + var fs = require('fs');${' '} + var async = require('async'); + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + }), + // fix order with comment on the end of line + test({ + code: ` + var async = require('async'); + var fs = require('fs'); /* comment */ + `, + output: ` + var fs = require('fs'); /* comment */ + var async = require('async'); + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + }), + // fix order with comments at the end and start of line + test({ + code: ` + /* comment1 */ var async = require('async'); /* comment2 */ + /* comment3 */ var fs = require('fs'); /* comment4 */ + `, + output: ` + /* comment3 */ var fs = require('fs'); /* comment4 */ + /* comment1 */ var async = require('async'); /* comment2 */ + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + }), + // fix order with few comments at the end and start of line + test({ + code: ` + /* comment0 */ /* comment1 */ var async = require('async'); /* comment2 */ + /* comment3 */ var fs = require('fs'); /* comment4 */ + `, + output: ` + /* comment3 */ var fs = require('fs'); /* comment4 */ + /* comment0 */ /* comment1 */ var async = require('async'); /* comment2 */ + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + }), + // fix order with windows end of lines + test({ + code: + `/* comment0 */ /* comment1 */ var async = require('async'); /* comment2 */` + `\r\n` + + `/* comment3 */ var fs = require('fs'); /* comment4 */` + `\r\n` + , + output: + `/* comment3 */ var fs = require('fs'); /* comment4 */` + `\r\n` + + `/* comment0 */ /* comment1 */ var async = require('async'); /* comment2 */` + `\r\n` + , + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + }), + // fix order with multilines comments at the end and start of line + test({ + code: ` + /* multiline1 + comment1 */ var async = require('async'); /* multiline2 + comment2 */ var fs = require('fs'); /* multiline3 + comment3 */ + `, + output: ` + /* multiline1 + comment1 */ var fs = require('fs');` + ' ' + ` + var async = require('async'); /* multiline2 + comment2 *//* multiline3 + comment3 */ + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + }), + // fix order of multile import + test({ + code: ` + var async = require('async'); + var fs = + require('fs'); + `, + output: ` + var fs = + require('fs'); + var async = require('async'); + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + }), + // fix order at the end of file + test({ + code: ` + var async = require('async'); + var fs = require('fs');`, + output: ` + var fs = require('fs'); + var async = require('async');` + '\n', errors: [{ ruleId: 'order', message: '`fs` import should occur before import of `async`', @@ -421,6 +554,10 @@ ruleTester.run('order', rule, { import async from 'async'; import fs from 'fs'; `, + output: ` + import fs from 'fs'; + import async from 'async'; + `, errors: [{ ruleId: 'order', message: '`fs` import should occur before import of `async`', @@ -432,6 +569,10 @@ ruleTester.run('order', rule, { var async = require('async'); import fs from 'fs'; `, + output: ` + import fs from 'fs'; + var async = require('async'); + `, errors: [{ ruleId: 'order', message: '`fs` import should occur before import of `async`', @@ -443,6 +584,10 @@ ruleTester.run('order', rule, { var parent = require('../parent'); var async = require('async'); `, + output: ` + var async = require('async'); + var parent = require('../parent'); + `, errors: [{ ruleId: 'order', message: '`async` import should occur before import of `../parent`', @@ -454,6 +599,10 @@ ruleTester.run('order', rule, { var sibling = require('./sibling'); var parent = require('../parent'); `, + output: ` + var parent = require('../parent'); + var sibling = require('./sibling'); + `, errors: [{ ruleId: 'order', message: '`../parent` import should occur before import of `./sibling`', @@ -465,6 +614,10 @@ ruleTester.run('order', rule, { var index = require('./'); var sibling = require('./sibling'); `, + output: ` + var sibling = require('./sibling'); + var index = require('./'); + `, errors: [{ ruleId: 'order', message: '`./sibling` import should occur before import of `./`', @@ -495,6 +648,14 @@ ruleTester.run('order', rule, { var foo = require('foo'); var bar = require('bar'); `, + output: ` + var fs = require('fs'); + var path = require('path'); + var _ = require('lodash'); + var foo = require('foo'); + var bar = require('bar'); + var index = require('./'); + `, errors: [{ ruleId: 'order', message: '`./` import should occur after import of `bar`', @@ -506,6 +667,10 @@ ruleTester.run('order', rule, { var fs = require('fs'); var index = require('./'); `, + output: ` + var index = require('./'); + var fs = require('fs'); + `, options: [{groups: ['index', 'sibling', 'parent', 'external', 'builtin']}], errors: [{ ruleId: 'order', @@ -513,7 +678,7 @@ ruleTester.run('order', rule, { }], }), // member expression of require - test({ + test(withoutAutofixOutput({ code: ` var foo = require('./foo').bar; var fs = require('fs'); @@ -522,9 +687,9 @@ ruleTester.run('order', rule, { ruleId: 'order', message: '`fs` import should occur before import of `./foo`', }], - }), + })), // nested member expression of require - test({ + test(withoutAutofixOutput({ code: ` var foo = require('./foo').bar.bar.bar; var fs = require('fs'); @@ -533,7 +698,33 @@ ruleTester.run('order', rule, { ruleId: 'order', message: '`fs` import should occur before import of `./foo`', }], - }), + })), + // fix near nested member expression of require with newlines + test(withoutAutofixOutput({ + code: ` + var foo = require('./foo').bar + .bar + .bar; + var fs = require('fs'); + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `./foo`', + }], + })), + // fix nested member expression of require with newlines + test(withoutAutofixOutput({ + code: ` + var foo = require('./foo'); + var fs = require('fs').bar + .bar + .bar; + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `./foo`', + }], + })), // Grouping import types test({ code: ` @@ -542,6 +733,12 @@ ruleTester.run('order', rule, { var sibling = require('./foo'); var path = require('path'); `, + output: ` + var fs = require('fs'); + var index = require('./'); + var path = require('path'); + var sibling = require('./foo'); + `, options: [{groups: [ ['builtin', 'index'], ['sibling', 'parent', 'external'], @@ -557,6 +754,10 @@ ruleTester.run('order', rule, { var path = require('path'); var async = require('async'); `, + output: ` + var async = require('async'); + var path = require('path'); + `, options: [{groups: [ 'index', ['sibling', 'parent', 'external', 'internal'], @@ -639,6 +840,15 @@ ruleTester.run('order', rule, { import sibling, {foo3} from './foo'; var index = require('./'); `, + output: ` + import async, {foo1} from 'async'; + import relParent2, {foo2} from '../foo/bar'; + import sibling, {foo3} from './foo'; + var fs = require('fs'); + var relParent1 = require('../foo'); + var relParent3 = require('../'); + var index = require('./'); + `, errors: [{ ruleId: 'order', message: '`./foo` import should occur before import of `fs`', @@ -650,6 +860,11 @@ ruleTester.run('order', rule, { import async, {foo1} from 'async'; import relParent2, {foo2} from '../foo/bar'; `, + output: ` + import async, {foo1} from 'async'; + import relParent2, {foo2} from '../foo/bar'; + var fs = require('fs'); + `, errors: [{ ruleId: 'order', message: '`fs` import should occur after import of `../foo/bar`', @@ -668,6 +883,15 @@ ruleTester.run('order', rule, { var relParent3 = require('../'); var async = require('async'); `, + output: ` + var fs = require('fs'); + var index = require('./'); + var path = require('path'); + var sibling = require('./foo'); + var relParent1 = require('../foo'); + var relParent3 = require('../'); + var async = require('async'); + `, options: [ { groups: [ @@ -689,7 +913,58 @@ ruleTester.run('order', rule, { }, ], }), - // // Option newlines-between: 'always' - should report lack of newline between groups + // Fix newlines-between with comments after + test({ + code: ` + var fs = require('fs'); /* comment */ + + var index = require('./'); + `, + output: ` + var fs = require('fs'); /* comment */ + var index = require('./'); + `, + options: [ + { + groups: [['builtin'], ['index']], + 'newlines-between': 'never', + }, + ], + errors: [ + { + line: 2, + message: 'There should be no empty line between import groups', + }, + ], + }), + // Cannot fix newlines-between with multiline comment after + test({ + code: ` + var fs = require('fs'); /* multiline + comment */ + + var index = require('./'); + `, + output: ` + var fs = require('fs'); /* multiline + comment */ + + var index = require('./'); + `, + options: [ + { + groups: [['builtin'], ['index']], + 'newlines-between': 'never', + }, + ], + errors: [ + { + line: 2, + message: 'There should be no empty line between import groups', + }, + ], + }), + // Option newlines-between: 'always' - should report lack of newline between groups test({ code: ` var fs = require('fs'); @@ -700,6 +975,17 @@ ruleTester.run('order', rule, { var relParent3 = require('../'); var async = require('async'); `, + output: ` + var fs = require('fs'); + var index = require('./'); + var path = require('path'); + + var sibling = require('./foo'); + + var relParent1 = require('../foo'); + var relParent3 = require('../'); + var async = require('async'); + `, options: [ { groups: [ @@ -721,7 +1007,7 @@ ruleTester.run('order', rule, { }, ], }), - //Option newlines-between: 'always' should report unnecessary empty lines space between import groups + // Option newlines-between: 'always' should report unnecessary empty lines space between import groups test({ code: ` var fs = require('fs'); @@ -733,11 +1019,19 @@ ruleTester.run('order', rule, { var async = require('async'); `, + output: ` + var fs = require('fs'); + var path = require('path'); + var index = require('./'); + + var sibling = require('./foo'); + var async = require('async'); + `, options: [ { groups: [ ['builtin', 'index'], - ['sibling', 'parent', 'external'] + ['sibling', 'parent', 'external'], ], 'newlines-between': 'always', }, @@ -753,7 +1047,7 @@ ruleTester.run('order', rule, { }, ], }), - // Option newlines-between: 'never' should report unnecessary empty lines when using not assigned imports + // Option newlines-between: 'never' cannot fix if there are other statements between imports test({ code: ` import path from 'path'; @@ -762,6 +1056,13 @@ ruleTester.run('order', rule, { import 'something-else'; import _ from 'lodash'; `, + output: ` + import path from 'path'; + import 'loud-rejection'; + + import 'something-else'; + import _ from 'lodash'; + `, options: [{ 'newlines-between': 'never' }], errors: [ { @@ -778,6 +1079,72 @@ ruleTester.run('order', rule, { import 'something-else'; import _ from 'lodash'; `, + output: ` + import path from 'path'; + + import 'loud-rejection'; + import 'something-else'; + import _ from 'lodash'; + `, + options: [{ 'newlines-between': 'always' }], + errors: [ + { + line: 2, + message: 'There should be at least one empty line between import groups', + }, + ], + }), + // fix missing empty lines with single line comment after + test({ + code: ` + import path from 'path'; // comment + import _ from 'lodash'; + `, + output: ` + import path from 'path'; // comment + + import _ from 'lodash'; + `, + options: [{ 'newlines-between': 'always' }], + errors: [ + { + line: 2, + message: 'There should be at least one empty line between import groups', + }, + ], + }), + // fix missing empty lines with few line block comment after + test({ + code: ` + import path from 'path'; /* comment */ /* comment */ + import _ from 'lodash'; + `, + output: ` + import path from 'path'; /* comment */ /* comment */ + + import _ from 'lodash'; + `, + options: [{ 'newlines-between': 'always' }], + errors: [ + { + line: 2, + message: 'There should be at least one empty line between import groups', + }, + ], + }), + // fix missing empty lines with single line block comment after + test({ + code: ` + import path from 'path'; /* 1 + 2 */ + import _ from 'lodash'; + `, + output: ` + import path from 'path'; + /* 1 + 2 */ + import _ from 'lodash'; + `, options: [{ 'newlines-between': 'always' }], errors: [ { @@ -786,5 +1153,112 @@ ruleTester.run('order', rule, { }, ], }), + + // reorder fix cannot cross non import or require + test(withoutAutofixOutput({ + code: ` + var async = require('async'); + fn_call(); + var fs = require('fs'); + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + })), + // reorder cannot cross non plain requires + test(withoutAutofixOutput({ + code: ` + var async = require('async'); + var a = require('./value.js')(a); + var fs = require('fs'); + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + })), + // reorder fixes cannot be applied to non plain requires #1 + test(withoutAutofixOutput({ + code: ` + var async = require('async'); + var fs = require('fs')(a); + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + })), + // reorder fixes cannot be applied to non plain requires #2 + test(withoutAutofixOutput({ + code: ` + var async = require('async')(a); + var fs = require('fs'); + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + })), + // cannot require in case of not assignement require + test(withoutAutofixOutput({ + code: ` + var async = require('async'); + require('./aa'); + var fs = require('fs'); + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + })), + // reorder cannot cross function call (import statement) + test(withoutAutofixOutput({ + code: ` + import async from 'async'; + fn_call(); + import fs from 'fs'; + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + })), + // reorder cannot cross variable assignemet (import statement) + test(withoutAutofixOutput({ + code: ` + import async from 'async'; + var a = 1; + import fs from 'fs'; + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + })), + // reorder cannot cross non plain requires (import statement) + test(withoutAutofixOutput({ + code: ` + import async from 'async'; + var a = require('./value.js')(a); + import fs from 'fs'; + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + })), + // cannot reorder in case of not assignement import + test(withoutAutofixOutput({ + code: ` + import async from 'async'; + import './aa'; + import fs from 'fs'; + `, + errors: [{ + ruleId: 'order', + message: '`fs` import should occur before import of `async`', + }], + })), ], })