Skip to content

Commit 3755a4c

Browse files
committed
feat: implement the new configuration format and pass all existing tests
1 parent 815cc88 commit 3755a4c

File tree

11 files changed

+568
-246
lines changed

11 files changed

+568
-246
lines changed

README.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ Because of the complexity of this config, it is exported as a factory function t
2929

3030
This package exports:
3131

32-
- a utility function: `defineConfig`, whose type signature is the same as the [`config` function from `typescript-eslint`](https://typescript-eslint.io/packages/typescript-eslint#config).
33-
- all the [shared configruations from `typescript-eslint`](https://typescript-eslint.io/users/configs), available as named exports (in camelCase, e.g. `recommendedTypeChecked`).
32+
- `defineConfig`, a utility function whose type signature is the same as the [`config` function from `typescript-eslint`](https://typescript-eslint.io/packages/typescript-eslint#config).
33+
- `configs`, contains all the [shared configruations from `typescript-eslint`](https://typescript-eslint.io/users/configs) (in camelCase, e.g. `configs.recommendedTypeChecked`).
3434
- a Vue-specific config factory: `configureVueProject({ supportedScriptLangs, rootDir })`. More info below.
3535

3636
### Minimal Setup
@@ -40,12 +40,12 @@ This package exports:
4040
import pluginVue from 'eslint-plugin-vue'
4141
import {
4242
defineConfig,
43-
recommended,
43+
configs,
4444
} from '@vue/eslint-config-typescript'
4545

4646
export default defineConfig(
4747
pluginVue.configs['flat/essential'],
48-
recommended,
48+
configs.recommended,
4949
)
5050
```
5151

@@ -60,13 +60,18 @@ All the `<script>` blocks in `.vue` files *MUST* be written in TypeScript (shoul
6060
import pluginVue from 'eslint-plugin-vue'
6161
import {
6262
defineConfig,
63+
configs,
6364
configureVueProject,
64-
recommended,
6565
} from '@vue/eslint-config-typescript'
6666

6767
export default [
6868
...pluginVue.configs["flat/essential"],
6969

70+
// We STRONGLY RECOMMEND you to start with `recommended` or `recommendedTypeChecked`.
71+
// But if you are determined to configure all rules by yourself,
72+
// you can start with `base`, and then turn on/off the rules you need.
73+
configs.base,
74+
7075
configureVueProject({
7176
// Optional: specify the script langs in `.vue` files
7277
// Defaults to `{ ts: true, js: false, tsx: false, jsx: false }`
@@ -99,8 +104,6 @@ export default [
99104
// and only apply the loosened rules to the files that do need them.
100105
rootDir: import.meta.dirname,
101106
}),
102-
103-
recommended,
104107
)
105108
```
106109

@@ -118,12 +121,12 @@ Instead, you can start by extending from the `recommendedTypeChecked` configurat
118121
import pluginVue from 'eslint-plugin-vue'
119122
import {
120123
defineConfig,
121-
recommendedTypeChecked,
124+
configs,
122125
} from '@vue/eslint-config-typescript'
123126

124127
export default defineConfig(
125128
pluginVue.configs['flat/essential'],
126-
recommendedTypeChecked
129+
configs.recommendedTypeChecked
127130
)
128131
```
129132

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
}
7171
},
7272
"dependencies": {
73+
"@typescript-eslint/utils": "^8.18.2",
7374
"fast-glob": "^3.3.2",
7475
"typescript-eslint": "^8.18.1",
7576
"vue-eslint-parser": "^9.4.3"

pnpm-lock.yaml

Lines changed: 69 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/configs.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import tseslint from 'typescript-eslint'
2+
3+
export type ExtendableConfigName = keyof typeof tseslint.configs
4+
type ConfigArray = (typeof tseslint.configs)[ExtendableConfigName]
5+
6+
// TODO: use enum
7+
export type VueTsPreset =
8+
`PLACEHOLDER_THAT_MUST_BE_WRAPPED_INSIDE_defineConfig_${ExtendableConfigName}`
9+
export type VueTsPresets = Record<ExtendableConfigName, VueTsPreset>
10+
11+
export const configs: VueTsPresets = Object.keys(tseslint.configs).reduce(
12+
(configs, name) => {
13+
configs[name as ExtendableConfigName] =
14+
`PLACEHOLDER_THAT_MUST_BE_WRAPPED_INSIDE_defineConfig_${name as ExtendableConfigName}`
15+
return configs
16+
},
17+
{} as VueTsPresets,
18+
)
19+
20+
function toArray<T>(value: T | T[]): T[] {
21+
return Array.isArray(value) ? value : [value]
22+
}
23+
24+
export function getConfigForPlaceholder(
25+
placeholder: VueTsPreset,
26+
): ConfigArray {
27+
return toArray(
28+
tseslint.configs[
29+
placeholder.replace(
30+
/^PLACEHOLDER_THAT_MUST_BE_WRAPPED_INSIDE_defineConfig_/,
31+
'',
32+
) as ExtendableConfigName
33+
],
34+
)
35+
.flat()
36+
.map(config =>
37+
config.files && config.files.includes('**/*.ts')
38+
? {
39+
...config,
40+
files: [...config.files, '**/*.vue'],
41+
}
42+
: config,
43+
)
44+
}

src/createConfig.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// This is a compatibility layer for the `createConfig` function in <= 14.2.0
2+
// Will be removed in 15.0.0
3+
4+
import * as tseslint from 'typescript-eslint'
5+
import { configureVueProject, defineConfig, type ProjectOptions } from './utilities'
6+
import { configs, type ExtendableConfigName } from './configs'
7+
import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'
8+
9+
type ConfigOptions = ProjectOptions & {
10+
extends?: Array<ExtendableConfigName>
11+
}
12+
13+
export default function createConfig({
14+
extends: configNamesToExtend = ['recommended'],
15+
supportedScriptLangs = { ts: true, tsx: false, js: false, jsx: false },
16+
rootDir = process.cwd(),
17+
}: ConfigOptions = {}): FlatConfig.ConfigArray {
18+
// More meaningful error message for the user, in case they didn't know the correct config name.
19+
for (const name of configNamesToExtend) {
20+
if (!tseslint.configs[name]) {
21+
const nameInCamelCase = name.replace(/-([a-z])/g, (_, letter) =>
22+
letter.toUpperCase(),
23+
)
24+
25+
// @ts-expect-error
26+
if (tseslint.configs[nameInCamelCase]) {
27+
throw new Error(
28+
`The config name "${name}" is not supported in "extends". ` +
29+
`Please use "${nameInCamelCase}" instead.`,
30+
)
31+
}
32+
33+
throw new Error(`Unknown config name in "extends": ${name}.`)
34+
}
35+
}
36+
37+
configureVueProject({ supportedScriptLangs, rootDir })
38+
return defineConfig(
39+
...configNamesToExtend.map(name => configs[name as ExtendableConfigName]),
40+
)
41+
}

src/groupVueFiles.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import fs from 'node:fs'
2+
import fg from 'fast-glob'
3+
import path from 'node:path'
4+
5+
type VueFilesByGroup = {
6+
typeCheckable: string[]
7+
nonTypeCheckable: string[]
8+
}
9+
10+
export default function groupVueFiles(rootDir: string): VueFilesByGroup {
11+
const { vueFilesWithScriptTs, otherVueFiles } = fg
12+
.sync(['**/*.vue'], {
13+
cwd: rootDir,
14+
ignore: ['**/node_modules/**'],
15+
})
16+
.reduce(
17+
(acc, file) => {
18+
const absolutePath = path.resolve(rootDir, file)
19+
const contents = fs.readFileSync(absolutePath, 'utf8')
20+
// contents matches the <script lang="ts"> (there can be anything but `>` between `script` and `lang`)
21+
if (/<script[^>]*\blang\s*=\s*"ts"[^>]*>/i.test(contents)) {
22+
acc.vueFilesWithScriptTs.push(file)
23+
} else {
24+
acc.otherVueFiles.push(file)
25+
}
26+
return acc
27+
},
28+
{ vueFilesWithScriptTs: [] as string[], otherVueFiles: [] as string[] },
29+
)
30+
31+
return {
32+
// Only `.vue` files with `<script lang="ts">` or `<script setup lang="ts">` can be type-checked.
33+
typeCheckable: vueFilesWithScriptTs,
34+
nonTypeCheckable: otherVueFiles,
35+
}
36+
}

0 commit comments

Comments
 (0)