@bfra.me/eslint-config
Version:
Shared ESLint configuration for bfra.me
86 lines (78 loc) • 2.7 kB
text/typescript
import type {Config} from '../config'
import type {Flatten, OptionsFiles, OptionsOverrides} from '../options'
import {GLOB_SRC} from '../globs'
import {anyParser} from '../parsers/any-parser'
import {requireOf} from '../require-of'
import {interopDefault} from '../utils'
import {fallback} from './fallback'
/**
* Configuration options for Next.js ESLint rules.
*
* Extends the base configuration options with file pattern matching
* and rule override capabilities specifically tailored for Next.js projects.
*
* @extends `Flatten<OptionsFiles & OptionsOverrides>`
*/
export interface NextjsOptions extends Flatten<OptionsFiles & OptionsOverrides> {}
function normalizeRules(rules: Record<string, any>): Record<string, any> {
return Object.fromEntries(
Object.entries(rules).map(([key, value]) => [key, typeof value === 'string' ? [value] : value]),
)
}
/**
* Creates ESLint configuration for Next.js projects.
*
* @param options - Configuration options for the Next.js ESLint setup
* @param options.files - File patterns to apply the configuration to (defaults to GLOB_SRC)
* @param options.overrides - Rule overrides to apply on top of the default Next.js rules
* @returns Promise resolving to an array of ESLint configurations including plugin setup and rules
*/
export async function nextjs(options: NextjsOptions = {}): Promise<Config[]> {
const {files = [GLOB_SRC], overrides = {}} = options
return requireOf(
['@next/eslint-plugin-next'],
async (): Promise<Config[]> => {
const pluginNextJs = await interopDefault(import('@next/eslint-plugin-next'))
function getRules(name: keyof typeof pluginNextJs.configs) {
const rules = pluginNextJs.configs?.[name]?.rules
if (!rules) {
throw new Error(
`[@bfra.me/eslint-config] No rules found for @next/eslint-plugin-next config: ${name}`,
)
}
return normalizeRules(rules)
}
return [
{
name: '@bfra.me/nextjs/setup',
plugins: {'@next/next': pluginNextJs},
},
{
name: '@bfra.me/nextjs/rules',
files,
languageOptions: {
parserOptions: {
ecmaFeatures: {jsx: true},
},
sourceType: 'module',
},
rules: {
...getRules('recommended'),
...getRules('core-web-vitals'),
...overrides,
},
settings: {
react: {version: 'detect'},
},
},
]
},
async missingList =>
fallback(missingList, {
files,
languageOptions: {
parser: anyParser,
},
}),
)
}