Skip to content

Commit 54b8a42

Browse files
authored
feat(sort-imports): allow to find alternate tsconfig file
1 parent 6ed64b2 commit 54b8a42

File tree

6 files changed

+364
-185
lines changed

6 files changed

+364
-185
lines changed

docs/content/rules/sort-imports.mdx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,32 @@ Specifies a maximum line length for sorting imports. When the line length exceed
267267

268268
This option is only available when the type is set to `'line-length'`.
269269

270-
### tsconfigRootDir
270+
### tsconfig
271+
272+
<sub>
273+
type: `{ rootDir: string; filename?: string }`
274+
</sub>
275+
<sub>default: undefined</sub>
276+
277+
- `rootDir` — Specifies the directory of the root `tsconfig.json` file (ex: `.`). This is used in [`groups`](#groups) for:
278+
- Marking aliased imports as `internal`.
279+
- Marking imports matching [tsconfig paths](https://www.typescriptlang.org/tsconfig/#paths) as `tsconfig-path`.
280+
- `filename` — Specifies the `tsconfig` filename to search for (by default: `tsconfig.json`).
281+
282+
If this option is not set, the rule will not search for a `tsconfig.json` file.
283+
284+
### [DEPRECATED] tsconfigRootDir
271285

272286
<sub>default: `undefined`</sub>
273287

288+
Use the [tsconfig](#tsconfig) option with the `rootDir` attribute.
289+
274290
Specifies the directory of the root `tsconfig.json` file (ex: `.`). This is used in [`groups`](#groups) for:
275291
- Marking aliased imports as `internal`.
276292
- Marking imports matching [tsconfig paths](https://www.typescriptlang.org/tsconfig/#paths) as `tsconfig-path`.
277293

294+
If this option is not set, the rule will not search for a `tsconfig.json` file.
295+
278296
### groups
279297

280298
<sub>

rules/sort-imports.ts

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -62,47 +62,50 @@ import { complete } from '../utils/complete'
6262
*/
6363
let cachedGroupsByModifiersAndSelectors = new Map<string, string[]>()
6464

65-
let defaultGroups = [
66-
'type-import',
67-
['value-builtin', 'value-external'],
68-
'type-internal',
69-
'value-internal',
70-
['type-parent', 'type-sibling', 'type-index'],
71-
['value-parent', 'value-sibling', 'value-index'],
72-
'ts-equals-import',
73-
'unknown',
74-
]
75-
7665
export type MESSAGE_ID =
7766
| 'unexpectedImportsDependencyOrder'
7867
| 'missedSpacingBetweenImports'
7968
| 'unexpectedImportsGroupOrder'
8069
| 'extraSpacingBetweenImports'
8170
| 'unexpectedImportsOrder'
8271

72+
let defaultOptions: Required<
73+
Omit<Options[0], 'tsconfigRootDir' | 'maxLineLength' | 'tsconfig'>
74+
> &
75+
Pick<Options[0], 'tsconfigRootDir' | 'maxLineLength' | 'tsconfig'> = {
76+
groups: [
77+
'type-import',
78+
['value-builtin', 'value-external'],
79+
'type-internal',
80+
'value-internal',
81+
['type-parent', 'type-sibling', 'type-index'],
82+
['value-parent', 'value-sibling', 'value-index'],
83+
'ts-equals-import',
84+
'unknown',
85+
],
86+
internalPattern: ['^~/.+', '^@/.+'],
87+
fallbackSort: { type: 'unsorted' },
88+
partitionByComment: false,
89+
partitionByNewLine: false,
90+
newlinesBetween: 'always',
91+
specialCharacters: 'keep',
92+
sortSideEffects: false,
93+
type: 'alphabetical',
94+
environment: 'node',
95+
customGroups: [],
96+
ignoreCase: true,
97+
locales: 'en-US',
98+
alphabet: '',
99+
order: 'asc',
100+
}
101+
83102
export default createEslintRule<Options, MESSAGE_ID>({
84103
create: context => {
85104
let settings = getSettings(context.settings)
86105

87106
let userOptions = context.options.at(0)
88107
let options = getOptionsWithCleanGroups(
89-
complete(userOptions, settings, {
90-
internalPattern: ['^~/.+', '^@/.+'],
91-
fallbackSort: { type: 'unsorted' },
92-
partitionByComment: false,
93-
partitionByNewLine: false,
94-
newlinesBetween: 'always',
95-
specialCharacters: 'keep',
96-
sortSideEffects: false,
97-
groups: defaultGroups,
98-
type: 'alphabetical',
99-
environment: 'node',
100-
customGroups: [],
101-
ignoreCase: true,
102-
locales: 'en-US',
103-
alphabet: '',
104-
order: 'asc',
105-
} as const),
108+
complete(userOptions, settings, defaultOptions),
106109
)
107110

108111
validateGeneratedGroupsConfiguration({
@@ -122,9 +125,12 @@ export default createEslintRule<Options, MESSAGE_ID>({
122125
validateNewlinesAndPartitionConfiguration(options)
123126
validateSideEffectsConfiguration(options)
124127

125-
let tsConfigOutput = options.tsconfigRootDir
128+
let tsconfigRootDirectory =
129+
options.tsconfig?.rootDir ?? options.tsconfigRootDir
130+
let tsConfigOutput = tsconfigRootDirectory
126131
? readClosestTsConfigByPath({
127-
tsconfigRootDir: options.tsconfigRootDir,
132+
tsconfigFilename: options.tsconfig?.filename ?? 'tsconfig.json',
133+
tsconfigRootDir: tsconfigRootDirectory,
128134
filePath: context.physicalFilename,
129135
contextCwd: context.cwd,
130136
})
@@ -439,6 +445,21 @@ export default createEslintRule<Options, MESSAGE_ID>({
439445
buildCustomGroupsArrayJsonSchema({ singleCustomGroupJsonSchema }),
440446
],
441447
},
448+
tsconfig: {
449+
properties: {
450+
rootDir: {
451+
description: 'Specifies the tsConfig root directory.',
452+
type: 'string',
453+
},
454+
filename: {
455+
description: 'Specifies the tsConfig filename.',
456+
type: 'string',
457+
},
458+
},
459+
additionalProperties: false,
460+
required: ['rootDir'],
461+
type: 'object',
462+
},
442463
maxLineLength: {
443464
description: 'Specifies the maximum line length.',
444465
exclusiveMinimum: true,
@@ -486,24 +507,7 @@ export default createEslintRule<Options, MESSAGE_ID>({
486507
type: 'suggestion',
487508
fixable: 'code',
488509
},
489-
defaultOptions: [
490-
{
491-
customGroups: { value: {}, type: {} },
492-
internalPattern: ['^~/.+', '^@/.+'],
493-
partitionByComment: false,
494-
partitionByNewLine: false,
495-
specialCharacters: 'keep',
496-
newlinesBetween: 'always',
497-
sortSideEffects: false,
498-
groups: defaultGroups,
499-
type: 'alphabetical',
500-
environment: 'node',
501-
ignoreCase: true,
502-
locales: 'en-US',
503-
alphabet: '',
504-
order: 'asc',
505-
},
506-
],
510+
defaultOptions: [defaultOptions],
507511
name: 'sort-imports',
508512
})
509513

@@ -594,7 +598,7 @@ let computeGroupExceptUnknown = ({
594598
}: {
595599
options: Omit<
596600
Required<Options[0]>,
597-
'tsconfigRootDir' | 'maxLineLength' | 'customGroups'
601+
'tsconfigRootDir' | 'maxLineLength' | 'customGroups' | 'tsconfig'
598602
>
599603
customGroups: DeprecatedCustomGroupsOption | CustomGroupsOption | undefined
600604
selectors?: Selector[]

rules/sort-imports/read-closest-ts-config-by-path.ts

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,33 @@ export interface ReadClosestTsConfigByPathValue {
1515
cache: ts.ModuleResolutionCache
1616
}
1717

18-
interface ReadClosestTsConfigByPathParameters {
19-
tsconfigRootDir: string
20-
contextCwd: string
21-
filePath: string
22-
}
23-
2418
export let directoryCacheByPath = new Map<string, string>()
2519
export let contentCacheByPath = new Map<
2620
string,
2721
ReadClosestTsConfigByPathValue
2822
>()
2923

30-
export let readClosestTsConfigByPath = (
31-
input: ReadClosestTsConfigByPathParameters,
32-
): ReadClosestTsConfigByPathValue | null => {
24+
export let readClosestTsConfigByPath = ({
25+
tsconfigFilename,
26+
tsconfigRootDir,
27+
contextCwd,
28+
filePath,
29+
}: {
30+
tsconfigFilename: string
31+
tsconfigRootDir: string
32+
contextCwd: string
33+
filePath: string
34+
}): ReadClosestTsConfigByPathValue | null => {
3335
let typescriptImport = getTypescriptImport()
3436
if (!typescriptImport) {
3537
return null
3638
}
3739

38-
let directory = path.dirname(input.filePath)
40+
let directory = path.dirname(filePath)
3941
let checkedDirectories = [directory]
4042

4143
do {
42-
let tsconfigPath = path.join(directory, 'tsconfig.json')
44+
let tsconfigPath = path.join(directory, tsconfigFilename)
4345
let cachedDirectory = directoryCacheByPath.get(directory)
4446
if (!cachedDirectory && fs.existsSync(tsconfigPath)) {
4547
cachedDirectory = tsconfigPath
@@ -49,22 +51,15 @@ export let readClosestTsConfigByPath = (
4951
for (let checkedDirectory of checkedDirectories) {
5052
directoryCacheByPath.set(checkedDirectory, cachedDirectory)
5153
}
52-
return getCompilerOptions(
53-
typescriptImport,
54-
input.contextCwd,
55-
cachedDirectory,
56-
)
54+
return getCompilerOptions(typescriptImport, contextCwd, cachedDirectory)
5755
}
5856

5957
directory = path.dirname(directory)
6058
checkedDirectories.push(directory)
61-
} while (
62-
directory.length > 1 &&
63-
directory.length >= input.tsconfigRootDir.length
64-
)
59+
} while (directory.length > 1 && directory.length >= tsconfigRootDir.length)
6560

6661
throw new Error(
67-
`Couldn't find any tsconfig.json relative to '${input.filePath}' within '${input.tsconfigRootDir}'.`,
62+
`Couldn't find any ${tsconfigFilename} relative to '${filePath}' within '${tsconfigRootDir}'.`,
6863
)
6964
}
7065

rules/sort-imports/types.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,27 @@ export type Options = Partial<{
2828
type?: DeprecatedCustomGroupsOption
2929
}
3030
| CustomGroupsOption<SingleCustomGroup>
31+
tsconfig:
32+
| {
33+
filename?: string
34+
rootDir: string
35+
}
36+
| undefined
3137
partitionByComment: PartitionByCommentOption
3238
specialCharacters: SpecialCharactersOption
3339
locales: NonNullable<Intl.LocalesArgument>
3440
newlinesBetween: NewlinesBetweenOption
41+
/**
42+
* @deprecated for `tsconfig`
43+
*/
44+
tsconfigRootDir: undefined | string
45+
maxLineLength: undefined | number
3546
fallbackSort: FallbackSortOption
3647
internalPattern: RegexOption[]
3748
groups: GroupsOptions<Group>
3849
environment: 'node' | 'bun'
3950
partitionByNewLine: boolean
4051
sortSideEffects: boolean
41-
tsconfigRootDir?: string
42-
maxLineLength?: number
4352
ignoreCase: boolean
4453
order: OrderOption
4554
type: TypeOption

0 commit comments

Comments
 (0)