Skip to content

Commit

Permalink
feat: implement the new configuration format and pass all existing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
haoqunjiang committed Dec 26, 2024
1 parent 815cc88 commit 3755a4c
Show file tree
Hide file tree
Showing 11 changed files with 568 additions and 246 deletions.
21 changes: 12 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ Because of the complexity of this config, it is exported as a factory function t

This package exports:

- 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).
- all the [shared configruations from `typescript-eslint`](https://typescript-eslint.io/users/configs), available as named exports (in camelCase, e.g. `recommendedTypeChecked`).
- `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).
- `configs`, contains all the [shared configruations from `typescript-eslint`](https://typescript-eslint.io/users/configs) (in camelCase, e.g. `configs.recommendedTypeChecked`).
- a Vue-specific config factory: `configureVueProject({ supportedScriptLangs, rootDir })`. More info below.

### Minimal Setup
Expand All @@ -40,12 +40,12 @@ This package exports:
import pluginVue from 'eslint-plugin-vue'
import {
defineConfig,
recommended,
configs,
} from '@vue/eslint-config-typescript'

export default defineConfig(
pluginVue.configs['flat/essential'],
recommended,
configs.recommended,
)
```

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

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

// We STRONGLY RECOMMEND you to start with `recommended` or `recommendedTypeChecked`.
// But if you are determined to configure all rules by yourself,
// you can start with `base`, and then turn on/off the rules you need.
configs.base,

configureVueProject({
// Optional: specify the script langs in `.vue` files
// Defaults to `{ ts: true, js: false, tsx: false, jsx: false }`
Expand Down Expand Up @@ -99,8 +104,6 @@ export default [
// and only apply the loosened rules to the files that do need them.
rootDir: import.meta.dirname,
}),

recommended,
)
```

Expand All @@ -118,12 +121,12 @@ Instead, you can start by extending from the `recommendedTypeChecked` configurat
import pluginVue from 'eslint-plugin-vue'
import {
defineConfig,
recommendedTypeChecked,
configs,
} from '@vue/eslint-config-typescript'

export default defineConfig(
pluginVue.configs['flat/essential'],
recommendedTypeChecked
configs.recommendedTypeChecked
)
```

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
}
},
"dependencies": {
"@typescript-eslint/utils": "^8.18.2",
"fast-glob": "^3.3.2",
"typescript-eslint": "^8.18.1",
"vue-eslint-parser": "^9.4.3"
Expand Down
73 changes: 69 additions & 4 deletions pnpm-lock.yaml

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

44 changes: 44 additions & 0 deletions src/configs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import tseslint from 'typescript-eslint'

export type ExtendableConfigName = keyof typeof tseslint.configs
type ConfigArray = (typeof tseslint.configs)[ExtendableConfigName]

// TODO: use enum
export type VueTsPreset =
`PLACEHOLDER_THAT_MUST_BE_WRAPPED_INSIDE_defineConfig_${ExtendableConfigName}`
export type VueTsPresets = Record<ExtendableConfigName, VueTsPreset>

export const configs: VueTsPresets = Object.keys(tseslint.configs).reduce(
(configs, name) => {
configs[name as ExtendableConfigName] =
`PLACEHOLDER_THAT_MUST_BE_WRAPPED_INSIDE_defineConfig_${name as ExtendableConfigName}`
return configs
},
{} as VueTsPresets,
)

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

export function getConfigForPlaceholder(
placeholder: VueTsPreset,
): ConfigArray {
return toArray(
tseslint.configs[
placeholder.replace(
/^PLACEHOLDER_THAT_MUST_BE_WRAPPED_INSIDE_defineConfig_/,
'',
) as ExtendableConfigName
],
)
.flat()
.map(config =>
config.files && config.files.includes('**/*.ts')
? {
...config,
files: [...config.files, '**/*.vue'],
}
: config,
)
}
41 changes: 41 additions & 0 deletions src/createConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// This is a compatibility layer for the `createConfig` function in <= 14.2.0
// Will be removed in 15.0.0

import * as tseslint from 'typescript-eslint'
import { configureVueProject, defineConfig, type ProjectOptions } from './utilities'
import { configs, type ExtendableConfigName } from './configs'
import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'

type ConfigOptions = ProjectOptions & {
extends?: Array<ExtendableConfigName>
}

export default function createConfig({
extends: configNamesToExtend = ['recommended'],
supportedScriptLangs = { ts: true, tsx: false, js: false, jsx: false },
rootDir = process.cwd(),
}: ConfigOptions = {}): FlatConfig.ConfigArray {
// More meaningful error message for the user, in case they didn't know the correct config name.
for (const name of configNamesToExtend) {
if (!tseslint.configs[name]) {
const nameInCamelCase = name.replace(/-([a-z])/g, (_, letter) =>
letter.toUpperCase(),
)

// @ts-expect-error
if (tseslint.configs[nameInCamelCase]) {
throw new Error(
`The config name "${name}" is not supported in "extends". ` +
`Please use "${nameInCamelCase}" instead.`,
)
}

throw new Error(`Unknown config name in "extends": ${name}.`)
}
}

configureVueProject({ supportedScriptLangs, rootDir })
return defineConfig(
...configNamesToExtend.map(name => configs[name as ExtendableConfigName]),
)
}
36 changes: 36 additions & 0 deletions src/groupVueFiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import fs from 'node:fs'
import fg from 'fast-glob'
import path from 'node:path'

type VueFilesByGroup = {
typeCheckable: string[]
nonTypeCheckable: string[]
}

export default function groupVueFiles(rootDir: string): VueFilesByGroup {
const { vueFilesWithScriptTs, otherVueFiles } = fg
.sync(['**/*.vue'], {
cwd: rootDir,
ignore: ['**/node_modules/**'],
})
.reduce(
(acc, file) => {
const absolutePath = path.resolve(rootDir, file)
const contents = fs.readFileSync(absolutePath, 'utf8')
// contents matches the <script lang="ts"> (there can be anything but `>` between `script` and `lang`)
if (/<script[^>]*\blang\s*=\s*"ts"[^>]*>/i.test(contents)) {
acc.vueFilesWithScriptTs.push(file)
} else {
acc.otherVueFiles.push(file)
}
return acc
},
{ vueFilesWithScriptTs: [] as string[], otherVueFiles: [] as string[] },
)

return {
// Only `.vue` files with `<script lang="ts">` or `<script setup lang="ts">` can be type-checked.
typeCheckable: vueFilesWithScriptTs,
nonTypeCheckable: otherVueFiles,
}
}
Loading

0 comments on commit 3755a4c

Please sign in to comment.