Skip to content

Commit

Permalink
feat: add read-tsconfig option to sort-imports rule
Browse files Browse the repository at this point in the history
  • Loading branch information
azat-io committed May 31, 2023
1 parent bd0016b commit 84cfc3d
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 5 deletions.
4 changes: 4 additions & 0 deletions docs/rules/sort-imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ If you use [one of the configs](/configs/) exported by this plugin, you get the
- `always` - at least one new line between each group will be enforced, and new lines inside a group will be forbidden.
- `never` - no new lines are allowed in the entire import section.

### `read-tsconfig`

- `boolean` (default: `false`) - read `tsconfig.json` and use `paths` as internal imports.

## ⚙️ Usage

### Legacy config
Expand Down
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ let createConfigWithOptions = (options: {
],
'newlines-between': 'always',
'internal-pattern': ['./**'],
'read-tsconfig': false,
},
],
[sortInterfacesName]: ['error'],
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"@typescript-eslint/types": "^5.59.8",
"@typescript-eslint/utils": "^5.59.8",
"is-core-module": "^2.12.1",
"json5": "^2.2.3",
"minimatch": "^9.0.1",
"natural-compare-lite": "^1.4.0"
},
Expand Down
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 24 additions & 4 deletions rules/sort-imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { minimatch } from 'minimatch'

import { createEslintRule } from '../utils/create-eslint-rule'
import { getNodeRange } from '../utils/get-node-range'
import { readTSConfig } from '../utils/read-ts-config'
import { rangeToDiff } from '../utils/range-to-diff'
import { SortType, SortOrder } from '../typings'
import { complete } from '../utils/complete'
Expand Down Expand Up @@ -47,6 +48,7 @@ type Options = [
'newlines-between': NewlinesBetweenValue
'internal-pattern': string[]
groups: (Group[] | Group)[]
'read-tsconfig': boolean
}>,
]

Expand Down Expand Up @@ -99,6 +101,10 @@ export default createEslintRule<Options, MESSAGE_ID>({
],
default: NewlinesBetweenValue.always,
},
'read-tsconfig': {
type: 'boolean',
default: false,
},
},
additionalProperties: false,
},
Expand All @@ -123,10 +129,23 @@ export default createEslintRule<Options, MESSAGE_ID>({
'newlines-between': NewlinesBetweenValue.always,
'internal-pattern': ['~/**'],
type: SortType.alphabetical,
'read-tsconfig': false,
order: SortOrder.asc,
groups: ['unknown'],
})

let tsPaths: string[] = []

if (options['read-tsconfig']) {
let { compilerOptions } = readTSConfig()

if (compilerOptions?.paths) {
Object.keys(compilerOptions?.paths).forEach(path => {
tsPaths.push(path)
})
}
}

let source = context.getSourceCode()

let nodes: SortingNodeWithGroup[] = []
Expand Down Expand Up @@ -156,10 +175,11 @@ export default createEslintRule<Options, MESSAGE_ID>({
}

let isInternal = (nodeElement: TSESTree.ImportDeclaration) =>
options['internal-pattern'].length &&
options['internal-pattern'].some(pattern =>
minimatch(nodeElement.source.value, pattern),
)
(options['internal-pattern'].length &&
options['internal-pattern'].some(pattern =>
minimatch(nodeElement.source.value, pattern),
)) ||
tsPaths.some(pattern => minimatch(nodeElement.source.value, pattern))

if (node.importKind === 'type') {
if (node.type === AST_NODE_TYPES.ImportDeclaration) {
Expand Down
76 changes: 75 additions & 1 deletion test/sort-imports.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ESLintUtils } from '@typescript-eslint/utils'
import { describe, it } from 'vitest'
import { describe, it, vi } from 'vitest'
import { dedent } from 'ts-dedent'

import rule, { NewlinesBetweenValue, RULE_NAME } from '../rules/sort-imports'
Expand Down Expand Up @@ -1940,4 +1940,78 @@ describe(RULE_NAME, () => {
})
})
})

it(`${RULE_NAME}: allow to use paths from tsconfig.json`, () => {
vi.mock('../utils/read-ts-config', () => ({
readTSConfig: () => ({
compilerOptions: {
moduleResolution: 'bundler',
verbatimModuleSyntax: true,
resolveJsonModule: true,
lib: ['ESNext', 'DOM'],
esModuleInterop: true,
skipLibCheck: true,
module: 'esnext',
target: 'es2020',
paths: {
'@/components/*': './src/components/*',
},
},
}),
}))

ruleTester.run(RULE_NAME, rule, {
valid: [],
invalid: [
{
code: dedent`
import { MikuruAsahina } from '@/components/mikuru'
import { HaruhiSuzumiya } from '@melancholy/haruhi-suzumiya'
import { YukiNagato } from '~/data/yuki'
`,
output: dedent`
import { HaruhiSuzumiya } from '@melancholy/haruhi-suzumiya'
import { MikuruAsahina } from '@/components/mikuru'
import { YukiNagato } from '~/data/yuki'
`,
options: [
{
type: SortType['line-length'],
order: SortOrder.desc,
'newlines-between': NewlinesBetweenValue.always,
'internal-pattern': ['~/**'],
groups: [
'type',
['builtin', 'external'],
'internal-type',
'internal',
['parent-type', 'sibling-type', 'index-type'],
['parent', 'sibling', 'index'],
'object',
'unknown',
],
'read-tsconfig': true,
},
],
errors: [
{
messageId: 'unexpectedImportsOrder',
data: {
first: '@/components/mikuru',
second: '@melancholy/haruhi-suzumiya',
},
},
{
messageId: 'missedSpacingBetweenImports',
data: {
first: '@melancholy/haruhi-suzumiya',
second: '~/data/yuki',
},
},
],
},
],
})
})
})
8 changes: 8 additions & 0 deletions utils/read-ts-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import json5 from 'json5'
import path from 'path'
import fs from 'fs'

export let readTSConfig = () =>
json5.parse(
fs.readFileSync(path.resolve(process.cwd(), 'tsconfig.json'), 'utf8'),
)

0 comments on commit 84cfc3d

Please sign in to comment.