import { test, getTSParsers, getNonDefaultParsers } from '../utils';
import { RuleTester } from 'eslint';
import eslintPkg from 'eslint/package.json';
import semver from 'semver';
import flatMap from 'array.prototype.flatmap';
const ruleTester = new RuleTester();
const rule = require('rules/order');
function withoutAutofixOutput(test) {
return Object.assign({}, test, { output: test.code });
}
ruleTester.run('order', rule, {
valid: [
test({
code: `
var fs = require('fs');
var async = require('async');
var relParent1 = require('../foo');
var relParent2 = require('../foo/bar');
var relParent3 = require('../');
var relParent4 = require('..');
var sibling = require('./foo');
var index = require('./');`,
}),
test({
code: `
import fs from 'fs';
import async, {foo1} from 'async';
import relParent1 from '../foo';
import relParent2, {foo2} from '../foo/bar';
import relParent3 from '../';
import sibling, {foo3} from './foo';
import index from './';`,
}),
test({
code: `
var fs = require('fs');
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var async = require('async');`,
}),
test({
code: `
var index = require('./');
var sibling = require('./foo');
var relParent3 = require('../');
var relParent2 = require('../foo/bar');
var relParent1 = require('../foo');
var async = require('async');
var fs = require('fs');
`,
options: [{ groups: ['index', 'sibling', 'parent', 'external', 'builtin'] }],
}),
test({
code: `
var path = require('path');
var _ = require('lodash');
var async = require('async');
var fs = require('f' + 's');`,
}),
test({
code: `
var path = require('path');
var result = add(1, 2);
var _ = require('lodash');`,
}),
test({
code: `
var index = require('./');
function foo() {
var fs = require('fs');
}
() => require('fs');
if (a) {
require('fs');
}`,
}),
test({
code: `
const foo = [
require('./foo'),
require('fs'),
]`,
}),
test({
code: "const foo = `${require('./a')} ${require('fs')}`",
}),
test({
code: `
var unknown1 = require('/unknown1');
var fs = require('fs');
var unknown2 = require('/unknown2');
var async = require('async');
var unknown3 = require('/unknown3');
var foo = require('../foo');
var unknown4 = require('/unknown4');
var bar = require('../foo/bar');
var unknown5 = require('/unknown5');
var parent = require('../');
var unknown6 = require('/unknown6');
var foo = require('./foo');
var unknown7 = require('/unknown7');
var index = require('./');
var unknown8 = require('/unknown8');
` }),
test({
code: `
require('./foo');
require('fs');
var path = require('path');
` }),
test({
code: `
import './foo';
import 'fs';
import path from 'path';
` }),
test({
code: `
function add(a, b) {
return a + b;
}
var foo;
` }),
test({
code: `
var fs = require('fs');
var index = require('./');
var path = require('path');
var sibling = require('./foo');
var relParent3 = require('../');
var async = require('async');
var relParent1 = require('../foo');
`,
options: [{ groups: [
['builtin', 'index'],
['sibling', 'parent', 'external'],
] }],
}),
test({
code: `
var index = require('./');
var path = require('path');
`,
options: [{ groups: [
'index',
['sibling', 'parent', 'external'],
] }],
}),
test({
code: `
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('./');
`,
}),
...flatMap(getTSParsers(), parser => [
test({
code: `
import async, {foo1} from 'async';
import relParent2, {foo2} from '../foo/bar';
import sibling, {foo3} from './foo';
var fs = require('fs');
var util = require("util");
var relParent1 = require('../foo');
var relParent3 = require('../');
var index = require('./');
`,
parser,
}),
test({
code: `
export import CreateSomething = _CreateSomething;
`,
parser,
}),
]),
test({
code: `
import fs from 'fs';
import { Input } from '-/components/Input';
import { Button } from '-/components/Button';
import { add } from './helper';`,
options: [{
groups: ['builtin', 'external', 'unknown', 'parent', 'sibling', 'index'],
}],
}),
test({
code: `
import { Input } from '-/components/Input';
import { Button } from '-/components/Button';
import fs from 'fs';
import { add } from './helper';`,
options: [{
groups: [ 'unknown', 'builtin', 'external', 'parent', 'sibling', 'index' ],
}],
}),
test({
code: `
import fs from 'fs';
import { Input } from '-/components/Input';
import { Button } from '-/components/Button';
import p from '..';
import q from '../';
import { add } from './helper';
import i from '.';
import j from './';`,
options: [
{
'newlines-between': 'always',
groups: ['builtin', 'external', 'unknown', 'parent', 'sibling', 'index'],
},
],
}),
test({
code: `
import fs from 'fs';
import _ from 'lodash';
import { Input } from '~/components/Input';
import { Button } from '#/components/Button';
import { add } from './helper';`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external', position: 'after' },
{ pattern: '#/**', group: 'external', position: 'after' },
],
}],
}),
test({
code: `
import fs from 'fs';
import { Input } from '~/components/Input';
import async from 'async';
import { Button } from '#/components/Button';
import _ from 'lodash';
import { add } from './helper';`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external' },
{ pattern: '#/**', group: 'external' },
],
}],
}),
test({
code: `
import fs from 'fs';
import { Input } from '~/components/Input';
import { Button } from '#/components/Button';
import _ from 'lodash';
import { add } from './helper';`,
options: [{
'newlines-between': 'always',
pathGroups: [
{ pattern: '~/**', group: 'external', position: 'before' },
{ pattern: '#/**', group: 'external', position: 'before' },
],
}],
}),
test({
code: `
import fs from 'fs';
import _ from 'lodash';
import { Input } from '~/components/Input';
import { Button } from '!/components/Button';
import { add } from './helper';`,
options: [{
'newlines-between': 'always',
pathGroups: [
{ pattern: '~/**', group: 'external', position: 'after' },
{ pattern: '!/**', patternOptions: { nonegate: true }, group: 'external', position: 'after' },
],
}],
}),
test({
code: `
import fs from 'fs';
import { Input } from '@app/components/Input';
import { Button } from '@app2/components/Button';
import _ from 'lodash';
import { add } from './helper';`,
options: [{
'newlines-between': 'always',
pathGroupsExcludedImportTypes: ['builtin'],
pathGroups: [
{ pattern: '@app/**', group: 'external', position: 'before' },
{ pattern: '@app2/**', group: 'external', position: 'before' },
],
}],
}),
test({
code: `
import fs from 'fs';
import external from 'external';
import externalTooPlease from './make-me-external';
import sibling from './sibling';`,
options: [{
'newlines-between': 'always',
pathGroupsExcludedImportTypes: [],
pathGroups: [
{ pattern: './make-me-external', group: 'external' },
],
groups: [['builtin', 'external'], 'internal', 'parent', 'sibling', 'index'],
}],
}),
test({
code: `
import _ from 'lodash';
import m from '@test-scope/some-module';
import bar from './bar';
`,
options: [{
'newlines-between': 'always',
}],
settings: {
'import/resolver': 'webpack',
'import/external-module-folders': ['node_modules', 'symlinked-module'],
},
}),
test({
code: `
import _ from 'lodash';
import m from '@test-scope/some-module';
import bar from './bar';
`,
options: [{
'newlines-between': 'always',
}],
settings: {
'import/resolver': 'node',
'import/external-module-folders': ['node_modules', 'symlinked-module'],
},
}),
test({
code: `
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: [
['builtin', 'index'],
['sibling'],
['parent', 'external'],
],
'newlines-between': 'always',
},
],
}),
test({
code: `
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: [
['builtin', 'index'],
['sibling'],
['parent', 'external'],
],
'newlines-between': 'never',
},
],
}),
test({
code: `
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: [
['builtin', 'index'],
['sibling'],
['parent', 'external'],
],
'newlines-between': 'ignore',
},
],
}),
test({
code: `
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: [
['builtin', 'index'],
['sibling'],
['parent', 'external'],
],
},
],
}),
test({
code: `
import path from 'path';
import {
I,
Want,
Couple,
Imports,
Here
} from 'bar';
import external from 'external'
`,
options: [{ 'newlines-between': 'always' }],
}),
test({
code: `
import path from 'path';
import net
from 'net';
import external from 'external'
`,
options: [{ 'newlines-between': 'always' }],
}),
test({
code: `
import foo
from '../../../../this/will/be/very/long/path/and/therefore/this/import/has/to/be/in/two/lines';
import bar
from './sibling';
`,
options: [{ 'newlines-between': 'always' }],
}),
test({
code: `
import path from 'path';
import 'loud-rejection';
import 'something-else';
import _ from 'lodash';
`,
options: [{ 'newlines-between': 'always' }],
}),
test({
code: `
import path from 'path';
import 'loud-rejection';
import 'something-else';
import _ from 'lodash';
`,
options: [{ 'newlines-between': 'never' }],
}),
test({
code: `
var path = require('path');
require('loud-rejection');
require('something-else');
var _ = require('lodash');
`,
options: [{ 'newlines-between': 'always' }],
}),
test({
code: `
var path = require('path');
require('loud-rejection');
require('something-else');
var _ = require('lodash');
`,
options: [{ 'newlines-between': 'never' }],
}),
test({
code: `
var some = require('asdas');
var config = {
port: 4444,
runner: {
server_path: require('runner-binary').path,
cli_args: {
'webdriver.chrome.driver': require('browser-binary').path
}
}
}
`,
options: [{ 'newlines-between': 'never' }],
}),
test({
code: `
var some = require('asdas');
var config = {
port: 4444,
runner: {
server_path: require('runner-binary').path,
cli_args: {
'webdriver.chrome.driver': require('browser-binary').path
}
}
}
`,
options: [{ 'newlines-between': 'always' }],
}),
test({
code: `
var fs = require('fs');
var path = require('path');
var util = require('util');
var async = require('async');
var relParent1 = require('../foo');
var relParent2 = require('../');
var relParent3 = require('../bar');
var sibling = require('./foo');
var sibling2 = require('./bar');
var sibling3 = require('./foobar');
`,
options: [
{
'newlines-between': 'always-and-inside-groups',
},
],
}),
test({
code: `
import a from 'foo';
import b from 'bar';
import index from './';
`,
options: [{
groups: ['external', 'index'],
alphabetize: { order: 'ignore' },
}],
}),
test({
code: `
import c from 'Bar';
import b from 'bar';
import a from 'foo';
import index from './';
`,
options: [{
groups: ['external', 'index'],
alphabetize: { order: 'asc' },
}],
}),
test({
code: `
import a from 'foo';
import b from 'bar';
import c from 'Bar';
import index from './';
`,
options: [{
groups: ['external', 'index'],
alphabetize: { order: 'desc' },
}],
}),
test({
code: `
import b from 'Bar';
import c from 'bar';
import a from 'foo';
import index from './';
`,
options: [{
groups: ['external', 'index'],
alphabetize: { order: 'asc' },
'newlines-between': 'always',
}],
}),
test({
code: `
import { hello } from './hello';
import { int } from './int';
const blah = require('./blah');
const { cello } = require('./cello');
`,
options: [
{
alphabetize: {
order: 'asc',
},
},
],
}),
test({
code: `
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
`,
options: [
{
alphabetize: {
order: 'asc',
},
},
],
}),
test({
code: `
import { UserInputError } from 'apollo-server-express';
import { new as assertNewEmail } from '~/Assertions/Email';
`,
options: [{
alphabetize: {
caseInsensitive: true,
order: 'asc',
},
pathGroups: [
{ pattern: '~/*', group: 'internal' },
],
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
],
'newlines-between': 'always',
}],
}),
test({
code: `
import { ReactElement, ReactNode } from 'react';
import { util } from 'Internal/lib';
import { parent } from '../parent';
import { sibling } from './sibling';
`,
options: [{
alphabetize: {
caseInsensitive: true,
order: 'asc',
},
pathGroups: [
{ pattern: 'Internal/**/*', group: 'internal' },
],
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
],
'newlines-between': 'always',
pathGroupsExcludedImportTypes: [],
}],
}),
...flatMap(getTSParsers, parser => [
test({
code: `
import blah = require('./blah');
import { hello } from './hello';`,
parser,
options: [
{
alphabetize: {
order: 'asc',
},
},
],
}),
test({
code: `
import blah = require('./blah');
import log = console.log;`,
parser,
options: [
{
alphabetize: {
order: 'asc',
},
},
],
}),
test({
code: `
import debug = console.debug;
import log = console.log;`,
parser,
options: [
{
alphabetize: {
order: 'asc',
},
},
],
}),
test({
code: `
import log = console.log;
import debug = console.debug;`,
parser,
options: [
{
alphabetize: {
order: 'asc',
},
},
],
}),
test({
code: `
import { a } from "./a";
export namespace SomeNamespace {
export import a2 = a;
}
`,
parser,
options: [
{
groups: ['external', 'index'],
alphabetize: { order: 'asc' },
},
],
}),
]),
],
invalid: [
test({
code: `
var async = require('async');
var fs = require('fs');
`,
output: `
var fs = require('fs');
var async = require('async');
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
}),
test({
code: `
var async = require('async');
var fs = require('fs');${' '}
`,
output: `
var fs = require('fs');${' '}
var async = require('async');
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
}),
test({
code: `
var async = require('async');
var fs = require('fs'); /* comment */
`,
output: `
var fs = require('fs'); /* comment */
var async = require('async');
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
}),
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: [{
message: '`fs` import should occur before import of `async`',
}],
}),
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: [{
message: '`fs` import should occur before import of `async`',
}],
}),
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: [{
message: '`fs` import should occur before import of `async`',
}],
}),
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: [{
message: '`fs` import should occur before import of `async`',
}],
}),
test({
code: `
var {b} = require('async');
var {a} = require('fs');
`,
output: `
var {a} = require('fs');
var {b} = require('async');
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
}),
test({
code: `
var async = require('async');
var fs =
require('fs');
`,
output: `
var fs =
require('fs');
var async = require('async');
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
}),
test({
code: `
var async = require('async');
var fs = require('fs');`,
output: `
var fs = require('fs');
var async = require('async');` + '\n',
errors: [{
message: '`fs` import should occur before import of `async`',
}],
}),
test({
code: `
import async from 'async';
import fs from 'fs';
`,
output: `
import fs from 'fs';
import async from 'async';
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
}),
test({
code: `
var async = require('async');
import fs from 'fs';
`,
output: `
import fs from 'fs';
var async = require('async');
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
}),
test({
code: `
var parent = require('../parent');
var async = require('async');
`,
output: `
var async = require('async');
var parent = require('../parent');
`,
errors: [{
message: '`async` import should occur before import of `../parent`',
}],
}),
test({
code: `
var sibling = require('./sibling');
var parent = require('../parent');
`,
output: `
var parent = require('../parent');
var sibling = require('./sibling');
`,
errors: [{
message: '`../parent` import should occur before import of `./sibling`',
}],
}),
test({
code: `
var index = require('./');
var sibling = require('./sibling');
`,
output: `
var sibling = require('./sibling');
var index = require('./');
`,
errors: [{
message: '`./sibling` import should occur before import of `./`',
}],
}),
...semver.satisfies(eslintPkg.version, '< 3.0.0') ? [] : [
test({
code: `
var sibling = require('./sibling');
var async = require('async');
var fs = require('fs');
`,
output: `
var async = require('async');
var sibling = require('./sibling');
var fs = require('fs');
`,
errors: [{
message: '`async` import should occur before import of `./sibling`',
}, {
message: '`fs` import should occur before import of `./sibling`',
}],
}),
],
test({
code: `
var index = require('./');
var fs = require('fs');
var path = require('path');
var _ = require('lodash');
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: [{
message: '`./` import should occur after import of `bar`',
}],
}),
test({
code: `
var fs = require('fs');
var index = require('./');
`,
output: `
var index = require('./');
var fs = require('fs');
`,
options: [{ groups: ['index', 'sibling', 'parent', 'external', 'builtin'] }],
errors: [{
message: '`./` import should occur before import of `fs`',
}],
}),
test(withoutAutofixOutput({
code: `
var foo = require('./foo').bar;
var fs = require('fs');
`,
errors: [{
message: '`fs` import should occur before import of `./foo`',
}],
})),
test(withoutAutofixOutput({
code: `
var foo = require('./foo').bar.bar.bar;
var fs = require('fs');
`,
errors: [{
message: '`fs` import should occur before import of `./foo`',
}],
})),
test(withoutAutofixOutput({
code: `
var foo = require('./foo').bar
.bar
.bar;
var fs = require('fs');
`,
errors: [{
message: '`fs` import should occur before import of `./foo`',
}],
})),
test(withoutAutofixOutput({
code: `
var foo = require('./foo');
var fs = require('fs').bar
.bar
.bar;
`,
errors: [{
message: '`fs` import should occur before import of `./foo`',
}],
})),
test({
code: `
var fs = require('fs');
var index = require('./');
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'],
] }],
errors: [{
message: '`path` import should occur before import of `./foo`',
}],
}),
test({
code: `
var path = require('path');
var async = require('async');
`,
output: `
var async = require('async');
var path = require('path');
`,
options: [{ groups: [
'index',
['sibling', 'parent', 'external', 'internal'],
] }],
errors: [{
message: '`async` import should occur before import of `path`',
}],
}),
test({
code: `
var async = require('async');
var index = require('./');
`,
options: [{ groups: [
'index',
['sibling', 'parent', 'UNKNOWN', 'internal'],
] }],
errors: [{
message: 'Incorrect configuration of the rule: Unknown type `"UNKNOWN"`',
}],
}),
test({
code: `
var async = require('async');
var index = require('./');
`,
options: [{ groups: [
'index',
['sibling', 'parent', ['builtin'], 'internal'],
] }],
errors: [{
message: 'Incorrect configuration of the rule: Unknown type `["builtin"]`',
}],
}),
test({
code: `
var async = require('async');
var index = require('./');
`,
options: [{ groups: [
'index',
['sibling', 'parent', 2, 'internal'],
] }],
errors: [{
message: 'Incorrect configuration of the rule: Unknown type `2`',
}],
}),
test({
code: `
var async = require('async');
var index = require('./');
`,
options: [{ groups: [
'index',
['sibling', 'parent', 'parent', 'internal'],
] }],
errors: [{
message: 'Incorrect configuration of the rule: `parent` is duplicated',
}],
}),
test({
code: `
import async, {foo1} from 'async';
import relParent2, {foo2} from '../foo/bar';
var fs = require('fs');
var relParent1 = require('../foo');
var relParent3 = require('../');
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: [{
message: '`./foo` import should occur before import of `fs`',
}],
}),
test({
code: `
var fs = require('fs');
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: [{
message: '`fs` import should occur after import of `../foo/bar`',
}],
}),
...flatMap(getTSParsers(), parser => [
test({
code: `
var fs = require('fs');
import async, {foo1} from 'async';
import bar = require("../foo/bar");
`,
output: `
import async, {foo1} from 'async';
import bar = require("../foo/bar");
var fs = require('fs');
`,
parser,
errors: [{
message: '`fs` import should occur after import of `../foo/bar`',
}],
}),
test({
code: `
var async = require('async');
var fs = require('fs');
`,
output: `
var fs = require('fs');
var async = require('async');
`,
parser,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
}),
test({
code: `
import sync = require('sync');
import async, {foo1} from 'async';
import index from './';
`,
output: `
import async, {foo1} from 'async';
import sync = require('sync');
import index from './';
`,
options: [{
groups: ['external', 'index'],
alphabetize: { order: 'asc' },
}],
parser,
errors: [{
message: '`async` import should occur before import of `sync`',
}],
}),
test({
code: `
import log = console.log;
import blah = require('./blah');`,
parser,
errors: [{
message: '`./blah` import should occur before import of `console.log`',
}],
}),
]),
test({
code: `
import { Button } from '-/components/Button';
import { add } from './helper';
import fs from 'fs';
`,
output: `
import fs from 'fs';
import { Button } from '-/components/Button';
import { add } from './helper';
`,
options: [
{
groups: ['builtin', 'external', 'unknown', 'parent', 'sibling', 'index'],
},
],
errors: [
{
line: 4,
message: '`fs` import should occur before import of `-/components/Button`',
},
],
}),
test({
code: `
import fs from 'fs';
import { Button } from '-/components/Button';
import { LinkButton } from '-/components/Link';
import { add } from './helper';
`,
output: `
import fs from 'fs';
import { Button } from '-/components/Button';
import { LinkButton } from '-/components/Link';
import { add } from './helper';
`,
options: [
{
groups: ['builtin', 'external', 'unknown', 'parent', 'sibling', 'index'],
'newlines-between': 'always',
},
],
errors: [
{
line: 2,
message: 'There should be at least one empty line between import groups',
},
{
line: 4,
message: 'There should be at least one empty line between import groups',
},
],
}),
test({
code: `
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');
`,
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: [
['builtin', 'index'],
['sibling'],
['parent', 'external'],
],
'newlines-between': 'never',
},
],
errors: [
{
line: 4,
message: 'There should be no empty line between import groups',
},
{
line: 6,
message: 'There should be no empty line between import groups',
},
],
}),
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',
},
],
}),
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',
},
],
}),
test({
code: `
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');
`,
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: [
['builtin', 'index'],
['sibling'],
['parent', 'external'],
],
'newlines-between': 'always',
},
],
errors: [
{
line: 4,
message: 'There should be at least one empty line between import groups',
},
{
line: 5,
message: 'There should be at least one empty line between import groups',
},
],
}),
test({
code: `
var fs = require('fs');
var path = require('path');
var index = require('./');
var sibling = require('./foo');
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'],
],
'newlines-between': 'always',
},
],
errors: [
{
line: 2,
message: 'There should be no empty line within import group',
},
{
line: 7,
message: 'There should be no empty line within import group',
},
],
}),
test({
code: `
import path from 'path';
import 'loud-rejection';
import 'something-else';
import _ from 'lodash';
`,
output: `
import path from 'path';
import 'loud-rejection';
import 'something-else';
import _ from 'lodash';
`,
options: [{ 'newlines-between': 'never', warnOnUnassignedImports: false }],
errors: [
{
line: 2,
message: 'There should be no empty line between import groups',
},
],
}),
test({
code: `
import path from 'path';
import 'loud-rejection';
import 'something-else';
import _ from 'lodash';
`,
output: `
import path from 'path';
import 'loud-rejection';
import 'something-else';
import _ from 'lodash';
`,
options: [{ 'newlines-between': 'never', warnOnUnassignedImports: true }],
errors: [
{
line: 3,
message: 'There should be no empty line between import groups',
},
],
}),
test({
code: `
import path from 'path';
export const abc = 123;
import 'something-else';
import _ from 'lodash';
`,
output: `
import path from 'path';
export const abc = 123;
import 'something-else';
import _ from 'lodash';
`,
options: [{ 'newlines-between': 'never' }],
errors: [
{
line: 2,
message: 'There should be no empty line between import groups',
},
],
}),
test({
code: `
import path from 'path';
import 'loud-rejection';
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',
},
],
}),
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',
},
],
}),
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',
},
],
}),
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: [
{
line: 2,
message: 'There should be at least one empty line between import groups',
},
],
}),
test({
code: `
const local = require('./local');
fn_call();
const global1 = require('global1');
const global2 = require('global2');
fn_call();
`,
output: `
const local = require('./local');
fn_call();
const global1 = require('global1');
const global2 = require('global2');
fn_call();
`,
errors: [{
message: '`./local` import should occur after import of `global2`',
}],
}),
test({
code: `
const local = require('./local');
fn_call();
const global1 = require('global1');
const global2 = require('global2');
fn_call();
`,
output: `
const local = require('./local');
fn_call();
const global1 = require('global1');
const global2 = require('global2');
fn_call();
`,
errors: [{
message: '`./local` import should occur after import of `global2`',
}],
}),
test({
code: `
const local1 = require('./local1');
const local2 = require('./local2');
const local3 = require('./local3');
const local4 = require('./local4');
fn_call();
const global1 = require('global1');
const global2 = require('global2');
const global3 = require('global3');
const global4 = require('global4');
const global5 = require('global5');
fn_call();
`,
output: `
const local1 = require('./local1');
const local2 = require('./local2');
const local3 = require('./local3');
const local4 = require('./local4');
fn_call();
const global1 = require('global1');
const global2 = require('global2');
const global3 = require('global3');
const global4 = require('global4');
const global5 = require('global5');
fn_call();
`,
errors: [
'`./local1` import should occur after import of `global5`',
'`./local2` import should occur after import of `global5`',
'`./local3` import should occur after import of `global5`',
'`./local4` import should occur after import of `global5`',
],
}),
test(withoutAutofixOutput({
code: `
const local = require('./local');
const global1 = require('global1');
const global2 = require('global2');
fn_call();
const global3 = require('global3');
fn_call();
`,
errors: [{
message: '`./local` import should occur after import of `global3`',
}],
})),
test({
code: `
const local1 = require('./local1');
const global1 = require('global1');
const global2 = require('global2');
fn_call();
const local2 = require('./local2');
const global3 = require('global3');
const global4 = require('global4');
fn_call();
`,
output: `
const local1 = require('./local1');
const global1 = require('global1');
const global2 = require('global2');
fn_call();
const global3 = require('global3');
const global4 = require('global4');
const local2 = require('./local2');
fn_call();
`,
errors: [
'`./local1` import should occur after import of `global4`',
'`./local2` import should occur after import of `global4`',
],
}),
test({
code: `
import fs from 'fs';
import _ from 'lodash';
import { add } from './helper';
import { Input } from '~/components/Input';
`,
output: `
import fs from 'fs';
import _ from 'lodash';
import { Input } from '~/components/Input';
import { add } from './helper';
`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external', position: 'after' },
],
}],
errors: [{
message: '`~/components/Input` import should occur before import of `./helper`',
}],
}),
test({
code: `
import fs from 'fs';
import _ from 'lodash';
import { add } from './helper';
import { Input } from '~/components/Input';
import async from 'async';
`,
output: `
import fs from 'fs';
import _ from 'lodash';
import { Input } from '~/components/Input';
import async from 'async';
import { add } from './helper';
`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external' },
],
}],
errors: [{
message: '`./helper` import should occur after import of `async`',
}],
}),
test({
code: `
import fs from 'fs';
import _ from 'lodash';
import { add } from './helper';
import { Input } from '~/components/Input';
`,
output: `
import fs from 'fs';
import { Input } from '~/components/Input';
import _ from 'lodash';
import { add } from './helper';
`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external', position: 'before' },
],
}],
errors: [{
message: '`~/components/Input` import should occur before import of `lodash`',
}],
}),
test({
code: `
import fs from 'fs';
import { Import } from '$/components/Import';
import _ from 'lodash';
import { Output } from '~/components/Output';
import { Input } from '#/components/Input';
import { add } from './helper';
import { Export } from '-/components/Export';
`,
output: `
import fs from 'fs';
import { Export } from '-/components/Export';
import { Import } from '$/components/Import';
import _ from 'lodash';
import { Output } from '~/components/Output';
import { Input } from '#/components/Input';
import { add } from './helper';
`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external', position: 'after' },
{ pattern: '#/**', group: 'external', position: 'after' },
{ pattern: '-/**', group: 'external', position: 'before' },
{ pattern: '$/**', group: 'external', position: 'before' },
],
}],
errors: [
{
message: '`-/components/Export` import should occur before import of `$/components/Import`',
},
],
}),
test({
code: `
import fs from 'fs';
import { Export } from '-/components/Export';
import { Import } from '$/components/Import';
import _ from 'lodash';
import { Input } from '#/components/Input';
import { add } from './helper';
import { Output } from '~/components/Output';
`,
output: `
import fs from 'fs';
import { Export } from '-/components/Export';
import { Import } from '$/components/Import';
import _ from 'lodash';
import { Output } from '~/components/Output';
import { Input } from '#/components/Input';
import { add } from './helper';
`,
options: [{
pathGroups: [
{ pattern: '~/**', group: 'external', position: 'after' },
{ pattern: '#/**', group: 'external', position: 'after' },
{ pattern: '-/**', group: 'external', position: 'before' },
{ pattern: '$/**', group: 'external', position: 'before' },
],
}],
errors: [
{
message: '`~/components/Output` import should occur before import of `#/components/Input`',
},
],
}),
test(withoutAutofixOutput({
code: `
var async = require('async');
fn_call();
var fs = require('fs');
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
})),
test({
code: `
const env = require('./config');
Object.keys(env);
const http = require('http');
const express = require('express');
http.createServer(express());
`,
output: `
const env = require('./config');
Object.keys(env);
const http = require('http');
const express = require('express');
http.createServer(express());
`,
errors: [{
message: '`./config` import should occur after import of `express`',
}],
}),
test(withoutAutofixOutput({
code: `
var async = require('async');
var a = require('./value.js')(a);
var fs = require('fs');
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
})),
test(withoutAutofixOutput({
code: `
var async = require('async');
var fs = require('fs')(a);
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
})),
test(withoutAutofixOutput({
code: `
var async = require('async')(a);
var fs = require('fs');
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
})),
test(withoutAutofixOutput({
code: `
var async = require('async');
require('./aa');
var fs = require('fs');
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
})),
test(withoutAutofixOutput({
code: `
import async from 'async';
fn_call();
import fs from 'fs';
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
})),
test(withoutAutofixOutput({
code: `
import async from 'async';
var a = 1;
import fs from 'fs';
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
})),
test(withoutAutofixOutput({
code: `
import async from 'async';
var a = require('./value.js')(a);
import fs from 'fs';
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
})),
test(withoutAutofixOutput({
code: `
import async from 'async';
import './aa';
import fs from 'fs';
`,
errors: [{
message: '`fs` import should occur before import of `async`',
}],
})),
test({
code: `
import b from 'bar';
import c from 'Bar';
import a from 'foo';
import index from './';
`,
output: `
import c from 'Bar';
import b from 'bar';
import a from 'foo';
import index from './';
`,
options: [{
groups: ['external', 'index'],
alphabetize: { order: 'asc' },
}],
errors: [{
message: '`Bar` import should occur before import of `bar`',
}],
}),
test({
code: `
import a from 'foo';
import c from 'Bar';
import b from 'bar';
import index from './';
`,
output: `
import a from 'foo';
import b from 'bar';
import c from 'Bar';
import index from './';
`,
options: [{
groups: ['external', 'index'],
alphabetize: { order: 'desc' },
}],
errors: [{
message: '`bar` import should occur before import of `Bar`',
}],
}),
test({
code: `
import b from 'foo';
import a from 'Bar';
import index from './';
`,
output: `
import a from 'Bar';
import b from 'foo';
import index from './';
`,
options: [{
groups: ['external', 'index'],
alphabetize: { order: 'asc', caseInsensitive: true },
}],
errors: [{
message: '`Bar` import should occur before import of `foo`',
}],
}),
test({
code: `
import a from 'Bar';
import b from 'foo';
import index from './';
`,
output: `
import b from 'foo';
import a from 'Bar';
import index from './';
`,
options: [{
groups: ['external', 'index'],
alphabetize: { order: 'desc', caseInsensitive: true },
}],
errors: [{
message: '`foo` import should occur before import of `Bar`',
}],
}),
test({
code: `
import a from '../a';
import p from '..';
`,
output: `
import p from '..';
import a from '../a';
`,
options: [{
groups: ['external', 'index'],
alphabetize: { order: 'asc' },
}],
errors: [{
message: '`..` import should occur before import of `../a`',
}],
}),
...semver.satisfies(eslintPkg.version, '< 3.0.0') ? [] : [
test({
code: `
const { cello } = require('./cello');
import { int } from './int';
const blah = require('./blah');
import { hello } from './hello';
`,
output: `
import { int } from './int';
const { cello } = require('./cello');
const blah = require('./blah');
import { hello } from './hello';
`,
errors: [{
message: '`./int` import should occur before import of `./cello`',
}, {
message: '`./hello` import should occur before import of `./cello`',
}],
}),
],
].filter((t) => !!t),
});
context('TypeScript', function () {
getNonDefaultParsers()
.filter((parser) => parser !== require.resolve('typescript-eslint-parser'))
.forEach((parser) => {
const parserConfig = {
parser,
settings: {
'import/parsers': { [parser]: ['.ts'] },
'import/resolver': { 'eslint-import-resolver-typescript': true },
},
};
ruleTester.run('order', rule, {
valid: [
test({
code: `
import c from 'Bar';
import type { C } from 'Bar';
import b from 'bar';
import a from 'foo';
import type { A } from 'foo';
import index from './';
`,
...parserConfig,
options: [
{
groups: ['external', 'index'],
alphabetize: { order: 'asc' },
},
],
}),
test({
code: `
import a from 'foo';
import type { A } from 'foo';
import b from 'bar';
import c from 'Bar';
import type { C } from 'Bar';
import index from './';
`,
...parserConfig,
options: [
{
groups: ['external', 'index'],
alphabetize: { order: 'desc' },
},
],
}),
test({
code: `
import c from 'Bar';
import b from 'bar';
import a from 'foo';
import index from './';
import type { C } from 'Bar';
import type { A } from 'foo';
`,
...parserConfig,
options: [
{
groups: ['external', 'index', 'type'],
alphabetize: { order: 'asc' },
},
],
}),
test({
code: `
import c from 'Bar';
import a from 'foo';
import b from 'dirA/bar';
import index from './';
import type { C } from 'dirA/Bar';
import type { A } from 'foo';
`,
...parserConfig,
options: [
{
alphabetize: { order: 'asc' },
groups: ['external', 'internal', 'index', 'type'],
pathGroups: [
{
pattern: 'dirA/**',
group: 'internal',
},
],
'newlines-between': 'always',
pathGroupsExcludedImportTypes: ['type'],
},
],
}),
test({
code: `
import c from 'Bar';
import type { A } from 'foo';
import a from 'foo';
import type { C } from 'dirA/Bar';
import b from 'dirA/bar';
import index from './';
`,
...parserConfig,
options: [
{
alphabetize: { order: 'asc' },
groups: ['external', 'internal', 'index'],
pathGroups: [
{
pattern: 'dirA/**',
group: 'internal',
},
],
'newlines-between': 'always',
pathGroupsExcludedImportTypes: [],
},
],
}),
test({
code: `
import a from 'foo';
import b from 'bar';
import c from 'Bar';
import index from './';
import type { A } from 'foo';
import type { C } from 'Bar';
`,
...parserConfig,
options: [
{
groups: ['external', 'index', 'type'],
alphabetize: { order: 'desc' },
},
],
}),
test({
code: `
import { Partner } from '@models/partner/partner';
import { PartnerId } from '@models/partner/partner-id';
`,
...parserConfig,
options: [
{
alphabetize: { order: 'asc' },
},
],
}),
test({
code: `
import { serialize, parse, mapFieldErrors } from '@vtaits/form-schema';
import type { GetFieldSchema } from '@vtaits/form-schema';
import { useMemo, useCallback } from 'react';
import type { ReactElement, ReactNode } from 'react';
import { Form } from 'react-final-form';
import type { FormProps as FinalFormProps } from 'react-final-form';
`,
...parserConfig,
options: [
{
alphabetize: { order: 'asc' },
},
],
}),
test({
code: `
import type { CopyOptions } from 'fs';
import type { ParsedPath } from 'path';
declare module 'my-module' {
import type { CopyOptions } from 'fs';
import type { ParsedPath } from 'path';
}
`,
...parserConfig,
options: [
{
alphabetize: { order: 'asc' },
},
],
}),
],
invalid: [
test({
code: `
import b from 'bar';
import c from 'Bar';
import type { C } from 'Bar';
import a from 'foo';
import type { A } from 'foo';
import index from './';
`,
output: `
import c from 'Bar';
import type { C } from 'Bar';
import b from 'bar';
import a from 'foo';
import type { A } from 'foo';
import index from './';
`,
...parserConfig,
options: [
{
groups: ['external', 'index'],
alphabetize: { order: 'asc' },
},
],
errors: [
{
message: semver.satisfies(eslintPkg.version, '< 3')
? '`bar` import should occur after import of `Bar`'
: /(`bar` import should occur after import of `Bar`)|(`Bar` import should occur before import of `bar`)/,
},
],
}),
test({
code: `
import a from 'foo';
import type { A } from 'foo';
import c from 'Bar';
import type { C } from 'Bar';
import b from 'bar';
import index from './';
`,
output: `
import a from 'foo';
import type { A } from 'foo';
import b from 'bar';
import c from 'Bar';
import type { C } from 'Bar';
import index from './';
`,
...parserConfig,
options: [
{
groups: ['external', 'index'],
alphabetize: { order: 'desc' },
},
],
errors: [
{
message: semver.satisfies(eslintPkg.version, '< 3')
? '`bar` import should occur before import of `Bar`'
: /(`bar` import should occur before import of `Bar`)|(`Bar` import should occur after import of `bar`)/,
},
],
}),
test({
code: `
import b from 'bar';
import c from 'Bar';
import a from 'foo';
import index from './';
import type { A } from 'foo';
import type { C } from 'Bar';
`,
output: `
import c from 'Bar';
import b from 'bar';
import a from 'foo';
import index from './';
import type { C } from 'Bar';
import type { A } from 'foo';
`,
...parserConfig,
options: [
{
groups: ['external', 'index', 'type'],
alphabetize: { order: 'asc' },
},
],
errors: semver.satisfies(eslintPkg.version, '< 3') ? [
{ message: '`Bar` import should occur before import of `bar`' },
{ message: '`Bar` import should occur before import of `foo`' },
] : [
{ message: /(`Bar` import should occur before import of `bar`)|(`bar` import should occur after import of `Bar`)/ },
{ message: /(`Bar` import should occur before import of `foo`)|(`foo` import should occur after import of `Bar`)/ },
],
}),
test({
code: `
import a from 'foo';
import c from 'Bar';
import b from 'bar';
import index from './';
import type { C } from 'Bar';
import type { A } from 'foo';
`,
output: `
import a from 'foo';
import b from 'bar';
import c from 'Bar';
import index from './';
import type { A } from 'foo';
import type { C } from 'Bar';
`,
...parserConfig,
options: [
{
groups: ['external', 'index', 'type'],
alphabetize: { order: 'desc' },
},
],
errors: semver.satisfies(eslintPkg.version, '< 3') ? [
{ message: '`bar` import should occur before import of `Bar`' },
{ message: '`foo` import should occur before import of `Bar`' },
] : [
{ message: /(`bar` import should occur before import of `Bar`)|(`Bar` import should occur after import of `bar`)/ },
{ message: /(`foo` import should occur before import of `Bar`)|(`Bar` import should occur after import of `foo`)/ },
],
}),
test({
code: `
import './local1';
import global from 'global1';
import local from './local2';
import 'global2';
`,
output: `
import './local1';
import global from 'global1';
import local from './local2';
import 'global2';
`,
errors: [
{
message: '`global1` import should occur before import of `./local1`',
},
{
message: '`global2` import should occur before import of `./local1`',
},
],
options: [{ warnOnUnassignedImports: true }],
}),
test({
code: `
import local from './local';
import 'global1';
import global2 from 'global2';
import global3 from 'global3';
`,
output: `
import local from './local';
import 'global1';
import global2 from 'global2';
import global3 from 'global3';
`,
errors: [{
message: '`./local` import should occur after import of `global3`',
}],
options: [{ warnOnUnassignedImports: true }],
}),
test({
code: `
import type { ParsedPath } from 'path';
import type { CopyOptions } from 'fs';
declare module 'my-module' {
import type { ParsedPath } from 'path';
import type { CopyOptions } from 'fs';
}
`,
output: `
import type { CopyOptions } from 'fs';
import type { ParsedPath } from 'path';
declare module 'my-module' {
import type { CopyOptions } from 'fs';
import type { ParsedPath } from 'path';
}
`,
errors: [{
message: '`fs` import should occur before import of `path`',
},{
message: '`fs` import should occur before import of `path`',
}],
...parserConfig,
options: [
{
alphabetize: { order: 'asc' },
},
],
}),
],
});
});
});