Skip to content

Commit

Permalink
Merge pull request #450 from ryan-williams-nourish/feature/ignore-rul…
Browse files Browse the repository at this point in the history
…es-for-file

Feature/ignore rules for file
  • Loading branch information
rrd108 authored Nov 21, 2024
2 parents 20ca8db + 8e805b6 commit 7d685d4
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 3 deletions.
8 changes: 7 additions & 1 deletion docs/get-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,16 @@ If you want to store your flags in a configuration file, you can create a `.conf
```json
{
"apply": "vue-strong,rrd",
"level": "error"
"level": "error",
"fileIgnoreRules": {
"src/main.ts": "tooManyProps,computedSideEffects",
"src/router/index.ts": "noConsole,multiAttributeElements"
}
}
```

Note that fileIgnoreRules is only available in the json file configuration. This field allows you to ignore specific rules for specific files.

You can override the limits for multiple rules using our new `override` field in the config file:

```json
Expand Down
10 changes: 8 additions & 2 deletions src/analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import getProjectRoot from './helpers/getProjectRoot'
import { groupRulesByRuleset } from './helpers/groupRulesByRuleset'
import hasServerDir from './helpers/hasServerDir'
import { isNuxtProject, isVueProject } from './helpers/projectTypeChecker'
import { checkFileIgnoreRules } from './helpers/setupFileIgnoreList'
import { RULESETS } from './rules/rules'
import { checkRules } from './rulesCheck'
import { reportRules } from './rulesReport'
Expand All @@ -23,6 +24,7 @@ let filesCount = 0
let linesCount = 0
let _apply: string[] = []
let _override: OverrideConfig = {} as OverrideConfig
let _fileIgnoreRules: { [key: string]: string } = {}

// Directories to skip during analysis
const skipDirs = ['cache', 'coverage', 'dist', '.git', 'node_modules', '.nuxt', '.output', 'vendor']
Expand All @@ -43,7 +45,9 @@ const checkFile = async (fileName: string, filePath: string) => {
descriptor.script = { content } as SFCScriptBlock
}

checkRules(descriptor, filePath, _apply, _override)
const apply = checkFileIgnoreRules(filePath, _fileIgnoreRules, _apply)

checkRules(descriptor, filePath, apply, _override)
return `Analyzing ${filePath}...`
}
}
Expand Down Expand Up @@ -82,7 +86,7 @@ const walkAsync = async (dir: string) => {
return overviewMessages
}

export const analyze = async ({ dir, apply = [], ignore = [], exclude = '', groupBy = 'rule', level = 'all', sortBy = 'desc' }: AnalyzeParams): Promise<AnalyzeOutput> => {
export const analyze = async ({ dir, apply = [], ignore = [], exclude = '', groupBy = 'rule', level = 'all', sortBy = 'desc', fileIgnoreRules = {} }: AnalyzeParams): Promise<AnalyzeOutput> => {
filesCount = 0
linesCount = 0
const projectRoot = await getProjectRoot(dir)
Expand All @@ -96,6 +100,7 @@ export const analyze = async ({ dir, apply = [], ignore = [], exclude = '', grou
groupBy = groupBy || config.group
level = level || config.level
sortBy = sortBy || config.sort
_fileIgnoreRules = { ...config.fileIgnoreRules, ...fileIgnoreRules }

_override = config.override

Expand Down Expand Up @@ -143,6 +148,7 @@ export const analyze = async ({ dir, apply = [], ignore = [], exclude = '', grou
info: `${applyingMessage}
Ignoring ${ignoredRulesets.length} rulesets: ${ignoreRulesetsOutput}
Ignoring ${ignoredRules.length} individual rules: ${ignoredRulesOutput}
Ignoring file-specific rules: ${Object.entries(_fileIgnoreRules).map(([file, rules]) => `${file}: ${rules}`).join(', ')}
Excluding ${exclude || '-'}
Output level <bg_info>${level}</bg_info>
Grouping by <bg_info>${groupBy}</bg_info>
Expand Down
5 changes: 5 additions & 0 deletions src/helpers/getConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const getConfig = async (projectRoot: string): Promise<Config> => {
const defaultConfig: Config = {
apply: Object.values(RULESETS).join(','),
ignore: '',
fileIgnoreRules: {},
exclude: '',
group: 'rule',
level: 'all',
Expand Down Expand Up @@ -54,6 +55,10 @@ export const getConfig = async (projectRoot: string): Promise<Config> => {
...defaultConfig.override,
...configFileContent?.override,
},
fileIgnoreRules: {
...defaultConfig.fileIgnoreRules,
...configFileContent?.fileIgnoreRules,
},
}
}
catch {
Expand Down
42 changes: 42 additions & 0 deletions src/helpers/setupFileIgnoreList.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type minimatch from 'minimatch'
import { describe, expect, it, vi } from 'vitest'
import { checkFileIgnoreRules } from './setupFileIgnoreList'

// Use `importActual` to partially mock `minimatch`
vi.mock('minimatch', async () => {
const actual = await vi.importActual<typeof minimatch>('minimatch')
return {
...actual,
default: (filePath: string, pattern: string) => filePath === pattern,
}
})

describe('checkFileIgnoreRules', () => {
it('should return apply unchanged if no matching rules in fileIgnoreRules (Scenario 1)', () => {
const filePath = 'test/file/path'
const fileIgnoreRules = {} // No ignore rules
const _apply = ['rule1', 'rule3']
const result = checkFileIgnoreRules(filePath, fileIgnoreRules, _apply)
expect(result).toEqual(_apply) // Should return unchanged
})

it('should remove matching rules from apply if they exist in fileIgnoreRules (Scenario 2)', () => {
const filePath = 'test/file/path'
const fileIgnoreRules = {
'test/file/path': 'rule1, rule3', // Matching rules
}
const _apply = ['rule1', 'rule2', 'rule3']
const result = checkFileIgnoreRules(filePath, fileIgnoreRules, _apply)
expect(result).toEqual(['rule2']) // Should remove 'rule1' and 'rule3'
})

it('should remove rules from apply if fileIgnoreRules has rulesets containing those rules (Scenario 3)', () => {
const filePath = 'test/file/path'
const fileIgnoreRules = {
'test/file/path': 'vue-caution', // ruleSet1 contains rule1 and rule2
}
const _apply = ['rule1', 'rule2', 'elementSelectorsWithScoped', 'implicitParentChildCommunication']
const result = checkFileIgnoreRules(filePath, fileIgnoreRules, _apply)
expect(result).toEqual(['rule1', 'rule2']) // Should remove 'elementSelectorsWithScoped' and 'implicitParentChildCommunication'
})
})
28 changes: 28 additions & 0 deletions src/helpers/setupFileIgnoreList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { RuleSetType } from '../rules/rules'
import { minimatch } from 'minimatch'
import { RULES, RULESETS } from '../rules/rules'

export const checkFileIgnoreRules = (filePath: string, fileIgnoreRules: { [key: string]: string }, _apply: string[]) => {
let apply = [..._apply] // Here we get a list of rules only - no rule sets

for (const [pattern, rules] of Object.entries(fileIgnoreRules)) {
if (minimatch(filePath, pattern, { matchBase: true })) {
const ignoreRules = rules.split(',').map(rule => rule.trim())
// Expand ignoreRules by replacing rulesets with individual rules
const expandedIgnoreRules = ignoreRules.flatMap((rule) => {
if (RULESETS.includes(rule as RuleSetType)) {
return RULES[rule as RuleSetType]
}
else {
return rule
}
})

// Remove duplicates by converting to a Set and back to an array
const uniqueIgnoreRules = Array.from(new Set(expandedIgnoreRules))
apply = apply.filter(rule => !uniqueIgnoreRules.includes(rule))
}
}

return apply
}
1 change: 1 addition & 0 deletions src/types/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export interface Config {
path?: string
apply: string
ignore: string
fileIgnoreRules?: Record<string, string>
exclude: string
group: string
level: string
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface AnalyzeParams {
groupBy: GroupBy
level: OutputLevel
sortBy: SortBy
fileIgnoreRules?: { [key: string]: string }
}

export interface Offense {
Expand Down

0 comments on commit 7d685d4

Please sign in to comment.