Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions rules/sort-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -193,22 +194,47 @@ 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:
case AST_NODE_TYPES.TSTypeAliasDeclaration:
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)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewers

I'm not doing throw new UnreachableCaseError to preserve current behavior in case custom parsers return other types.

continue
}

Expand Down
228 changes: 228 additions & 0 deletions test/rules/sort-modules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
})
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

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],
})
})
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could these sorts across ... cases be turned into invalid(...) tests with B ... skipped node ... A?

Right now they use already sorted input, so they still pass even if those skipped nodes accidentally become partition boundaries. Coverage is green, but I don't think those tests actually pin the behavior down yet.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@azat-io

Good point, there are indeed useless as of now!

4379a9c (this PR) 🤦


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: [
Expand Down
Loading