Skip to content

Commit f75cc8a

Browse files
committed
refactor: drop the enum/string hack for configs, use a class instead
1 parent fdc3833 commit f75cc8a

File tree

2 files changed

+72
-73
lines changed

2 files changed

+72
-73
lines changed

src/configs.ts

Lines changed: 47 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,56 @@
11
import tseslint from 'typescript-eslint'
2-
32
import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'
43

5-
const PlaceholderPrefix =
6-
'PLACEHOLDER_THAT_MUST_BE_WRAPPED_INSIDE_defineConfigWithVueTs_'
7-
8-
// Manually declare all the available configs as enums to make the auto-completion more user-friendly.
9-
// It's also a good way to avoid the placeholder strings to appear in the auto-completion.
10-
export enum VueTsPreset {
11-
all = `${PlaceholderPrefix}all`,
12-
base = `${PlaceholderPrefix}base`,
13-
disableTypeChecked = `${PlaceholderPrefix}disableTypeChecked`,
14-
eslintRecommended = `${PlaceholderPrefix}eslintRecommended`,
15-
recommended = `${PlaceholderPrefix}recommended`,
16-
recommendedTypeChecked = `${PlaceholderPrefix}recommendedTypeChecked`,
17-
recommendedTypeCheckedOnly = `${PlaceholderPrefix}recommendedTypeCheckedOnly`,
18-
strict = `${PlaceholderPrefix}strict`,
19-
strictTypeChecked = `${PlaceholderPrefix}strictTypeChecked`,
20-
strictTypeCheckedOnly = `${PlaceholderPrefix}strictTypeCheckedOnly`,
21-
stylistic = `${PlaceholderPrefix}stylistic`,
22-
stylisticTypeChecked = `${PlaceholderPrefix}stylisticTypeChecked`,
23-
stylisticTypeCheckedOnly = `${PlaceholderPrefix}stylisticTypeCheckedOnly`,
24-
}
25-
26-
// `enum`s are just objects with reverse mapping during runtime.
27-
// We redefine the type here only to make the auto-completion more user-friendly.
28-
export type ExtendableConfigName = keyof typeof VueTsPreset
29-
export const vueTsConfigs = VueTsPreset as {
30-
[key in ExtendableConfigName]: VueTsPreset
31-
}
4+
const CONFIG_NAMES = [
5+
'all',
6+
'base',
7+
'disableTypeChecked',
8+
'eslintRecommended',
9+
'recommended',
10+
'recommendedTypeChecked',
11+
'recommendedTypeCheckedOnly',
12+
'strict',
13+
'strictTypeChecked',
14+
'strictTypeCheckedOnly',
15+
'stylistic',
16+
'stylisticTypeChecked',
17+
'stylisticTypeCheckedOnly',
18+
] as const
19+
export type ExtendableConfigName = (typeof CONFIG_NAMES)[number]
3220

3321
function toArray<T>(value: T | T[]): T[] {
3422
return Array.isArray(value) ? value : [value]
3523
}
3624

37-
export function getConfigForPlaceholder(
38-
placeholder: VueTsPreset,
39-
): FlatConfig.ConfigArray {
40-
return toArray(
41-
tseslint.configs[
42-
placeholder.replace(
43-
new RegExp(`^${PlaceholderPrefix}`),
44-
'',
45-
) as ExtendableConfigName
46-
],
47-
)
48-
.flat()
49-
.map(config =>
50-
config.files && config.files.includes('**/*.ts')
51-
? {
52-
...config,
53-
files: [...config.files, '**/*.vue'],
54-
}
55-
: config,
56-
)
25+
export class TsEslintConfigForVue {
26+
constructor(readonly configName: ExtendableConfigName) {
27+
this.configName = configName
28+
}
29+
30+
needsTypeChecking(): boolean {
31+
if (this.configName === 'disableTypeChecked') {
32+
return false
33+
}
34+
if (this.configName === 'all') {
35+
return true
36+
}
37+
return this.configName.includes('TypeChecked')
38+
}
39+
40+
toConfigArray(): FlatConfig.ConfigArray {
41+
return toArray(tseslint.configs[this.configName])
42+
.flat()
43+
.map(config =>
44+
config.files && config.files.includes('**/*.ts')
45+
? {
46+
...config,
47+
files: [...config.files, '**/*.vue'],
48+
}
49+
: config,
50+
)
51+
}
5752
}
53+
54+
export const vueTsConfigs = Object.fromEntries(
55+
CONFIG_NAMES.map(name => [name, new TsEslintConfigForVue(name)]),
56+
) as Record<ExtendableConfigName, TsEslintConfigForVue>

src/utilities.ts

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1+
import process from 'node:process'
12
import * as tseslint from 'typescript-eslint'
2-
3-
import type { VueTsPreset } from './configs'
4-
import { getConfigForPlaceholder } from './configs'
3+
import { TsEslintConfigForVue } from './configs'
54
import groupVueFiles from './groupVueFiles'
65
import {
76
additionalRulesRequiringParserServices,
@@ -10,17 +9,16 @@ import {
109
createTypeCheckingConfigs,
1110
} from './internals'
1211

12+
import type { ScriptLang } from './internals'
13+
1314
type ConfigArray = ReturnType<typeof tseslint.config>
1415
type Rules = NonNullable<ConfigArray[number]['rules']>
1516

16-
type ConfigOrVueTsPreset = ConfigArray[number] | VueTsPreset
17+
type ConfigObjectOrPlaceholder = ConfigArray[number] | TsEslintConfigForVue
1718
type InfiniteDepthConfigWithVueSupport =
18-
| ConfigOrVueTsPreset
19+
| ConfigObjectOrPlaceholder
1920
| InfiniteDepthConfigWithVueSupport[]
2021

21-
import process from 'node:process'
22-
import type { ScriptLang } from './internals'
23-
2422
export type ProjectOptions = {
2523
scriptLangs?: ScriptLang[]
2624
rootDir?: string
@@ -48,11 +46,14 @@ export function configureVueProject(userOptions: ProjectOptions) {
4846
export function defineConfigWithVueTs(
4947
...configs: InfiniteDepthConfigWithVueSupport[]
5048
): ConfigArray {
51-
// @ts-ignore
52-
const flattenedConfigs: Array<ConfigOrVueTsPreset> = configs.flat(Infinity)
53-
const normalizedConfigs = insertAndReorderConfigs(flattenedConfigs).map(
54-
config =>
55-
typeof config === 'string' ? getConfigForPlaceholder(config) : config,
49+
const flattenedConfigs: Array<ConfigObjectOrPlaceholder> =
50+
// @ts-ignore
51+
configs.flat(Infinity)
52+
53+
const reorderedConfigs = insertAndReorderConfigs(flattenedConfigs)
54+
55+
const normalizedConfigs = reorderedConfigs.map(config =>
56+
config instanceof TsEslintConfigForVue ? config.toConfigArray() : config,
5657
)
5758

5859
return tseslint.config(...normalizedConfigs)
@@ -86,10 +87,10 @@ type ExtractedConfig = {
8687
const userTypeAwareConfigs: ExtractedConfig[] = []
8788

8889
function insertAndReorderConfigs(
89-
configs: Array<ConfigOrVueTsPreset>,
90-
): Array<ConfigOrVueTsPreset> {
90+
configs: Array<ConfigObjectOrPlaceholder>,
91+
): Array<ConfigObjectOrPlaceholder> {
9192
const lastExtendedConfigIndex = configs.findLastIndex(
92-
config => typeof config === 'string',
93+
config => config instanceof TsEslintConfigForVue,
9394
)
9495

9596
if (lastExtendedConfigIndex === -1) {
@@ -99,13 +100,12 @@ function insertAndReorderConfigs(
99100
const vueFiles = groupVueFiles(projectOptions.rootDir!)
100101
const configsWithoutTypeAwareRules = configs.map(extractTypeAwareRules)
101102

102-
const hasTypeAwarePresets = configs.some(
103+
const hasTypeAwareConfigs = configs.some(
103104
config =>
104-
typeof config === 'string' &&
105-
!config.endsWith('disableTypeChecked') &&
106-
(config.includes('TypeChecked') || config.endsWith('all')),
105+
config instanceof TsEslintConfigForVue && config.needsTypeChecking(),
107106
)
108-
const needsTypeAwareLinting = hasTypeAwarePresets || userTypeAwareConfigs.length > 0
107+
const needsTypeAwareLinting =
108+
hasTypeAwareConfigs || userTypeAwareConfigs.length > 0
109109

110110
return [
111111
...configsWithoutTypeAwareRules.slice(0, lastExtendedConfigIndex + 1),
@@ -130,9 +130,9 @@ function insertAndReorderConfigs(
130130
}
131131

132132
function extractTypeAwareRules(
133-
config: ConfigOrVueTsPreset,
134-
): ConfigOrVueTsPreset {
135-
if (typeof config === 'string') {
133+
config: ConfigObjectOrPlaceholder,
134+
): ConfigObjectOrPlaceholder {
135+
if (config instanceof TsEslintConfigForVue) {
136136
return config
137137
}
138138

@@ -148,7 +148,7 @@ function extractTypeAwareRules(
148148
if (typeAwareRuleEntries.length > 0) {
149149
userTypeAwareConfigs.push({
150150
rules: Object.fromEntries(typeAwareRuleEntries),
151-
...(config.files && { files: config.files }),
151+
...(config.files && { files: config.files }),
152152
})
153153
}
154154

0 commit comments

Comments
 (0)