Skip to content

Commit 04ae3cc

Browse files
committed
fix: respect global ignores when scanning for vue files to lint
fixes #225
1 parent 24f47f3 commit 04ae3cc

File tree

6 files changed

+84
-32
lines changed

6 files changed

+84
-32
lines changed

src/configs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export class TsEslintConfigForVue {
7777
toConfigArray(): FlatConfig.ConfigArray {
7878
return toArray(tseslint.configs[this.configName])
7979
.flat()
80-
.map(config => ({
80+
.map((config: FlatConfig.Config) => ({
8181
...config,
8282
...(config.files && config.files.includes('**/*.ts')
8383
? { files: [...config.files, '**/*.vue'] }

src/createConfig.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ export default function createConfig({
5151
rootDir,
5252
})
5353
return defineConfigWithVueTs(
54-
...configNamesToExtend.map(name => vueTsConfigs[name as ExtendableConfigName]),
54+
...configNamesToExtend.map(
55+
name => vueTsConfigs[name as ExtendableConfigName],
56+
),
5557
)
5658
}

src/fpHelpers.ts

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,55 @@
11
// This file is for some generic helper functions that aren't tied to the main functionality of the project.
22

3-
export function omit<T extends Record<string, any>, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {
3+
export function omit<T extends Record<string, any>, K extends keyof T>(
4+
obj: T,
5+
keys: K[],
6+
): Omit<T, K> {
47
return Object.fromEntries(
5-
Object.entries(obj).filter(([key]) => !keys.includes(key as K))
6-
) as Omit<T, K>;
8+
Object.entries(obj).filter(([key]) => !keys.includes(key as K)),
9+
) as Omit<T, K>
710
}
811

912
// https://dev.to/nexxeln/implementing-the-pipe-operator-in-typescript-30ip
1013
interface Pipe {
11-
<A>(value: A): A;
12-
<A, B>(value: A, fn1: (input: A) => B): B;
13-
<A, B, C>(value: A, fn1: (input: A) => B, fn2: (input: B) => C): C;
14+
<A>(value: A): A
15+
<A, B>(value: A, fn1: (input: A) => B): B
16+
<A, B, C>(value: A, fn1: (input: A) => B, fn2: (input: B) => C): C
1417
<A, B, C, D>(
1518
value: A,
1619
fn1: (input: A) => B,
1720
fn2: (input: B) => C,
18-
fn3: (input: C) => D
19-
): D;
21+
fn3: (input: C) => D,
22+
): D
2023
<A, B, C, D, E>(
2124
value: A,
2225
fn1: (input: A) => B,
2326
fn2: (input: B) => C,
2427
fn3: (input: C) => D,
25-
fn4: (input: D) => E
26-
): E;
28+
fn4: (input: D) => E,
29+
): E
2730
<A, B, C, D, E, F>(
2831
value: A,
2932
fn1: (input: A) => B,
3033
fn2: (input: B) => C,
3134
fn3: (input: C) => D,
3235
fn4: (input: D) => E,
33-
fn5: (input: E) => F
34-
): F;
36+
fn5: (input: E) => F,
37+
): F
38+
<A, B, C, D, E, F, G>(
39+
value: A,
40+
fn1: (input: A) => B,
41+
fn2: (input: B) => C,
42+
fn3: (input: C) => D,
43+
fn4: (input: D) => E,
44+
fn5: (input: E) => F,
45+
fn6: (input: F) => G,
46+
): G
3547
// ... and so on
3648
}
3749

3850
export const pipe: Pipe = (value: any, ...fns: Function[]): unknown => {
39-
return fns.reduce((acc, fn) => fn(acc), value);
40-
};
41-
51+
return fns.reduce((acc, fn) => fn(acc), value)
52+
}
4253

4354
export function partition<T>(
4455
array: T[],

src/groupVueFiles.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,24 @@ type VueFilesByGroup = {
1010
nonTypeCheckable: string[]
1111
}
1212

13-
export default function groupVueFiles(rootDir: string): VueFilesByGroup {
13+
export default function groupVueFiles(
14+
rootDir: string,
15+
globalIgnores: string[],
16+
): VueFilesByGroup {
1417
debug(`Grouping .vue files in ${rootDir}`)
15-
16-
const ignore = ['**/node_modules/**', '**/.git/**']
17-
// FIXME: to get global ignore patterns from user config
18+
19+
const ignore = [
20+
'**/node_modules/**',
21+
'**/.git/**',
22+
23+
// Global ignore patterns from ESLint config are relative to the ESLint base path,
24+
// which is usually the cwd, but could be different if `--config` is provided via CLI.
25+
// This is way too complicated, so we only use process.cwd() as a best-effort guess here.
26+
// Could be improved in the future if needed.
27+
...globalIgnores.map(pattern =>
28+
fg.convertPathToPattern(path.resolve(process.cwd(), pattern)),
29+
),
30+
]
1831
debug(`Ignoring patterns: ${ignore.join(', ')}`)
1932

2033
const { vueFilesWithScriptTs, otherVueFiles } = fg

src/index.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
export {
2-
defineConfigWithVueTs,
3-
configureVueProject,
4-
} from './utilities'
1+
export { defineConfigWithVueTs, configureVueProject } from './utilities'
52
export { vueTsConfigs } from './configs'
63

74
// Compatibility layer for the `createConfig` function in <= 14.2.0

src/utilities.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import process from 'node:process'
22
import tseslint from 'typescript-eslint'
33
import type { TSESLint } from '@typescript-eslint/utils'
4+
import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'
45
import pluginVue from 'eslint-plugin-vue'
56

67
import { TsEslintConfigForVue } from './configs'
@@ -43,16 +44,16 @@ export type ProjectOptions = {
4344
/**
4445
* Whether to override some `no-unsafe-*` rules to avoid false positives on Vue component operations.
4546
* Defaults to `true`.
46-
*
47+
*
4748
* Due to limitations in the integration between Vue and TypeScript-ESLint,
4849
* TypeScript-ESLint cannot get the full type information for `.vue` files
4950
* and will use fallback types that contain some `any`s.
5051
* Therefore, some `no-unsafe-*` rules will error on functions that operate on Vue components,
5152
* such as `createApp`, `createRouter`, `useTemplateRef`, etc.
52-
*
53+
*
5354
* Setting this option to `true` will override those `no-unsafe-*` rules
5455
* to allow these patterns in the project.
55-
*
56+
*
5657
* If you're using a metaframework such as Nuxt or Quasar
5758
* that handles app creation & router configuration for you,
5859
* you might not need to interact with component types directly.
@@ -82,7 +83,8 @@ export function configureVueProject(userOptions: ProjectOptions): void {
8283
projectOptions.tsSyntaxInTemplates = userOptions.tsSyntaxInTemplates
8384
}
8485
if (userOptions.allowComponentTypeUnsafety !== undefined) {
85-
projectOptions.allowComponentTypeUnsafety = userOptions.allowComponentTypeUnsafety
86+
projectOptions.allowComponentTypeUnsafety =
87+
userOptions.allowComponentTypeUnsafety
8688
}
8789
if (userOptions.scriptLangs) {
8890
projectOptions.scriptLangs = userOptions.scriptLangs
@@ -104,6 +106,7 @@ export function defineConfigWithVueTs(
104106
return pipe(
105107
configs,
106108
flattenConfigs,
109+
collectGlobalIgnores,
107110
deduplicateVuePlugin,
108111
insertAndReorderConfigs,
109112
resolveVueTsConfigs,
@@ -165,6 +168,29 @@ function flattenConfigs(
165168
)
166169
}
167170

171+
let globalIgnores: string[] = []
172+
173+
/**
174+
* Fields that are considered metadata and not part of the config object.
175+
*/
176+
const META_FIELDS = new Set(['name', 'basePath'])
177+
178+
function collectGlobalIgnores(configs: RawConfigItem[]): RawConfigItem[] {
179+
configs.forEach(config => {
180+
if (config instanceof TsEslintConfigForVue) return
181+
182+
if (!config.ignores) return
183+
184+
if (Object.keys(config).filter(key => !META_FIELDS.has(key)).length !== 1)
185+
return
186+
187+
// Configs that only contain `ignores` (and possibly `name`/`basePath`) are treated as global ignores
188+
globalIgnores.push(...config.ignores)
189+
})
190+
191+
return configs
192+
}
193+
168194
function resolveVueTsConfigs(configs: RawConfigItem[]): ConfigItem[] {
169195
return configs.flatMap(config =>
170196
config instanceof TsEslintConfigForVue ? config.toConfigArray() : config,
@@ -206,7 +232,7 @@ function insertAndReorderConfigs(configs: RawConfigItem[]): RawConfigItem[] {
206232
return configs
207233
}
208234

209-
const vueFiles = groupVueFiles(projectOptions.rootDir)
235+
const vueFiles = groupVueFiles(projectOptions.rootDir, globalIgnores)
210236
const configsWithoutTypeAwareRules = configs.map(extractTypeAwareRules)
211237

212238
const hasTypeAwareConfigs = configs.some(
@@ -233,7 +259,10 @@ function insertAndReorderConfigs(configs: RawConfigItem[]): RawConfigItem[] {
233259
...(needsTypeAwareLinting
234260
? [
235261
...createSkipTypeCheckingConfigs(vueFiles.nonTypeCheckable),
236-
...createTypeCheckingConfigs(vueFiles.typeCheckable, projectOptions.allowComponentTypeUnsafety),
262+
...createTypeCheckingConfigs(
263+
vueFiles.typeCheckable,
264+
projectOptions.allowComponentTypeUnsafety,
265+
),
237266
]
238267
: []),
239268

@@ -269,7 +298,7 @@ function extractTypeAwareRules(config: RawConfigItem): RawConfigItem {
269298
}
270299

271300
const rulesRequiringTypeInformation = new Set(
272-
Object.entries(tseslint.plugin.rules!)
301+
Object.entries((tseslint.plugin as FlatConfig.Plugin).rules!)
273302
// @ts-expect-error
274303
.filter(([_name, def]) => def?.meta?.docs?.requiresTypeChecking)
275304
.map(([name, _def]) => `@typescript-eslint/${name}`)

0 commit comments

Comments
 (0)