diff --git a/rules/sort-modules.ts b/rules/sort-modules.ts index f39ad5f2f..f797d0a5e 100644 --- a/rules/sort-modules.ts +++ b/rules/sort-modules.ts @@ -48,6 +48,7 @@ import { createEslintRule } from '../utils/create-eslint-rule' import { reportAllErrors } from '../utils/report-all-errors' import { shouldPartition } from '../utils/should-partition' import { getGroupIndex } from '../utils/get-group-index' +import { assertIsNever } from '../utils/assert-is-never' import { computeGroup } from '../utils/compute-group' import { rangeToDiff } from '../utils/range-to-diff' import { getSettings } from '../utils/get-settings' @@ -193,6 +194,16 @@ function analyzeModule({ >[][] = [[]] for (let node of module.body) { switch (node.type) { + case AST_NODE_TYPES.TSNamespaceExportDeclaration: + case AST_NODE_TYPES.ExportAllDeclaration: + case AST_NODE_TYPES.ImportDeclaration: + case AST_NODE_TYPES.DebuggerStatement: + case AST_NODE_TYPES.ContinueStatement: + case AST_NODE_TYPES.ReturnStatement: + case AST_NODE_TYPES.EmptyStatement: + case AST_NODE_TYPES.BreakStatement: + case AST_NODE_TYPES.WithStatement: + continue case AST_NODE_TYPES.ExportDefaultDeclaration: case AST_NODE_TYPES.ExportNamedDeclaration: case AST_NODE_TYPES.TSInterfaceDeclaration: @@ -200,15 +211,30 @@ function analyzeModule({ case AST_NODE_TYPES.FunctionDeclaration: case AST_NODE_TYPES.TSModuleDeclaration: break + case AST_NODE_TYPES.TSImportEqualsDeclaration: case AST_NODE_TYPES.VariableDeclaration: case AST_NODE_TYPES.ExpressionStatement: + case AST_NODE_TYPES.TSExportAssignment: + case AST_NODE_TYPES.DoWhileStatement: + case AST_NODE_TYPES.LabeledStatement: + case AST_NODE_TYPES.SwitchStatement: + case AST_NODE_TYPES.WhileStatement: + case AST_NODE_TYPES.ForInStatement: + case AST_NODE_TYPES.ForOfStatement: + case AST_NODE_TYPES.ThrowStatement: + case AST_NODE_TYPES.BlockStatement: + case AST_NODE_TYPES.ForStatement: + case AST_NODE_TYPES.TryStatement: + case AST_NODE_TYPES.IfStatement: sortingNodeGroupsWithoutOverloadSignature.push([]) continue case AST_NODE_TYPES.TSDeclareFunction: case AST_NODE_TYPES.TSEnumDeclaration: case AST_NODE_TYPES.ClassDeclaration: break + /* v8 ignore next 2 -- @preserve Exhaustive guard. */ default: + assertIsNever(node) continue } diff --git a/test/rules/sort-modules.test.ts b/test/rules/sort-modules.test.ts index 86856faa5..1aa3b9a32 100644 --- a/test/rules/sort-modules.test.ts +++ b/test/rules/sort-modules.test.ts @@ -285,6 +285,234 @@ describe('sort-modules', () => { }) }) + it('creates partitions at if statements', async () => { + await valid({ + code: dedent` + enum B { b = 'b' } + if (B.b === 'b') {} + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('creates partitions at while statements', async () => { + await valid({ + code: dedent` + enum B { b = 'b' } + while (B.b === 'b') {} + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('creates partitions at for-in loops', async () => { + await valid({ + code: dedent` + enum B { b = 'b' } + for (const key in B) {} + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('creates partitions at try/catch statements', async () => { + await valid({ + code: dedent` + enum B { b = 'b' } + try { B.b } catch {} + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('creates partitions at for loops', async () => { + await valid({ + code: dedent` + enum B { b = 'b' } + for (let i = 0; i < Object.keys(B).length; i++) {} + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('creates partitions at for-of loops', async () => { + await valid({ + code: dedent` + enum B { b = 'b' } + for (const value of Object.values(B)) {} + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('creates partitions at do-while loops', async () => { + await valid({ + code: dedent` + enum B { b = 'b' } + do { void B.b } while (false) + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('creates partitions at switch statements', async () => { + await valid({ + code: dedent` + enum B { b = 'b' } + switch (B.b) { case 'b': break } + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('creates partitions at throw statements', async () => { + await valid({ + code: dedent` + enum B { b = 'b' } + throw new Error(B.b) + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('creates partitions at block statements', async () => { + await valid({ + code: dedent` + enum B { b = 'b' } + { const x = B.b } + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('creates partitions at labeled statements', async () => { + await valid({ + code: dedent` + enum B { b = 'b' } + label: { const x = B.b } + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('creates partitions at TypeScript import-equals declarations', async () => { + await valid({ + code: dedent` + enum B { b = 'b' } + import foo = B + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('creates partitions at export assignments', async () => { + await valid({ + code: dedent` + enum B { b = 'b' } + export = B + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('sorts across empty statements', async () => { + await invalid({ + errors: [ + { + messageId: 'unexpectedModulesOrder', + data: { right: 'A', left: 'B' }, + }, + ], + code: dedent` + enum B { b = 'b' } + ; + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('sorts across debugger statements', async () => { + await invalid({ + errors: [ + { + messageId: 'unexpectedModulesOrder', + data: { right: 'A', left: 'B' }, + }, + ], + code: dedent` + enum B { b = 'b' } + debugger + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('sorts across import declarations', async () => { + await invalid({ + errors: [ + { + messageId: 'unexpectedModulesOrder', + data: { right: 'A', left: 'B' }, + }, + ], + code: dedent` + enum B { b = 'b' } + import 'x' + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('sorts across export-all declarations', async () => { + await invalid({ + errors: [ + { + messageId: 'unexpectedModulesOrder', + data: { right: 'A', left: 'B' }, + }, + ], + code: dedent` + enum B { b = 'b' } + export * from 'x' + enum A { a = 'a' } + `, + options: [options], + }) + }) + + it('sorts across namespace export declarations', async () => { + await invalid({ + errors: [ + { + messageId: 'unexpectedModulesOrder', + data: { right: 'A', left: 'B' }, + }, + ], + code: dedent` + enum B { b = 'b' } + export as namespace Foo + enum A { a = 'a' } + `, + options: [options], + }) + }) + it('accepts complex predefined group configurations', async () => { await valid({ options: [