eslint-config-xaxa
Version:
The ultimate ESLint config - successor to Airbnb Config. Built on Anthony Fu's ESLint config, Airbnb, ESLint Stylistic, Perfectionist, React, TypeScript, Astro, JSDocs, Prettier, Node.js, Unicorns, Promises, and more.
332 lines (297 loc) • 9.08 kB
text/typescript
/* eslint complexity: ["error", 100] */
import type {
Awaitable,
OptionsFormatters,
OptionsOverrides,
StylisticConfig,
} from '@antfu/eslint-config';
import {
astro as antfuAstro,
defaultPluginRenaming as antfuPluginRenaming,
comments,
disables,
formatters,
ignores,
interopDefault,
isInEditorEnv,
// jsdoc, // TODO: add jsdoc + jsdoc examples support
jsonc,
jsx,
markdown,
pnpm,
react,
regexp,
resolveSubOptions,
sortPackageJson,
sortTsconfig,
toml,
typescript,
yaml,
} from '@antfu/eslint-config';
import js from '@eslint/js';
import { FlatConfigComposer } from 'eslint-flat-config-utils';
import { isPackageExists } from 'local-pkg';
import type { OptionsXaxa, UserConfig } from './types.ts';
import * as config from './configs/index.ts';
import { isPnpm, isUnoCSS } from './utils.ts';
const renaming = antfuPluginRenaming as any;
export * from './configs/index.ts';
export * from './plugins.ts';
export * from './types.ts';
export * from './vscode.ts';
export const defaultPluginRenaming = { ...renaming, '@next/next': 'nextjs' } as any;
export {
FlatConfigComposer,
interopDefault,
isInEditorEnv,
isPackageExists,
isPnpm,
isUnoCSS,
js,
antfuAstro as astro,
comments,
disables,
formatters,
ignores,
// jsdoc, // TODO: add jsdoc + jsdoc examples support
jsonc,
jsx,
markdown,
pnpm,
react,
regexp,
resolveSubOptions,
sortPackageJson,
sortTsconfig,
toml,
typescript,
yaml,
};
/**
* Construct an array of ESLint flat config items.
*
* @param {OptionsXaxa} options - The options for generating the ESLint configurations.
* @param {Awaitable<TypedFlatConfigItem | TypedFlatConfigItem[]>[]} userConfigs - The user configurations to be merged with the generated configurations.
* @returns {Promise<TypedFlatConfigItem[]>} The merged ESLint configurations.
*/
export default function xaxa(
options?: OptionsXaxa,
...userConfigs: Awaitable<UserConfig>[] | UserConfig[]
): FlatConfigComposer {
// console.log('current dir', process.cwd(), fileURLToPath(import.meta.url));
const settings = { ...options } as OptionsXaxa;
const isTypescript = isPackageExists('typescript');
const isReact = isPackageExists('preact') || isPackageExists('react');
const {
astro: enableAstro = false,
autoRenamePlugins = true,
componentExts = [],
eslintJsRecommended = true,
gitignore: enableGitignore = true,
ignores: userIgnores = [],
jsonc: enableJsonc = true,
// jsdoc: enableJsdoc = true,
jsx: enableJsx = isReact,
markdown: enableMarkdown = true,
nextjs: enableNextjs = false,
node: enableNode = true,
perfectionist: enablePerfectionist = true,
pnpm: enableCatalogs = isPnpm(settings.cwd),
promise: enablePromise = true,
react: enableReact = isReact,
regexp: enableRegexp = true,
semi: enableSemi = true,
tailwind: enableTailwind = false,
toml: enableToml = true,
type: packageType = 'lib',
typescript: enableTypeScript = isTypescript,
unicorn: enableUnicorn = true,
unocss: enableUnoCSS = isUnoCSS(settings.cwd),
wgw: enableWgw = true,
yaml: enableYaml = true,
...opts
} = { ...settings } as OptionsXaxa;
if (('files' in settings) && ('name' in settings) && ('plugins' in settings)) {
throw new Error(
'[eslint-xaxa-config] The first argument should not contain the "files" property as the "options" are supposed to be global.',
);
}
const disableFixForRules = opts.disableFixForRules || [
'unused-imports/no-unused-imports',
'test/no-only-tests',
'prefer-const',
];
let isInEditor = opts.isInEditor;
if (isInEditor == null) {
isInEditor = isInEditorEnv();
if (isInEditor) {
console.log(`[eslint-config-xaxa] Detected running in editor, fix disabled for: ${disableFixForRules.join(', ')}`.trim());
}
}
// eslint-disable-next-line no-nested-ternary
const stylisticOptions = (opts.stylistic === false
? false
: typeof opts.stylistic === 'object'
? opts.stylistic
: {}
) as (StylisticConfig & OptionsOverrides);
stylisticOptions.jsx = enableJsx;
stylisticOptions.semi = enableSemi;
const configs: any = [];
if (enableGitignore) {
if (typeof enableGitignore !== 'boolean') {
configs.push(
interopDefault(import('eslint-config-flat-gitignore')).then((r) => [r({
name: 'antfu/gitignore',
...enableGitignore,
})]),
);
} else {
configs.push(
interopDefault(import('eslint-config-flat-gitignore')).then((r) => [r({
name: 'antfu/gitignore',
strict: false,
})]),
);
}
}
const typescriptOptions = resolveSubOptions(opts as any, 'typescript');
const tsconfigPath = 'tsconfigPath' in typescriptOptions
? typescriptOptions.tsconfigPath
// eslint-disable-next-line no-undefined
: undefined;
// Base configs
configs.push(
ignores(userIgnores), // user ignores
comments(),
eslintJsRecommended && { name: '@eslint/js/recommended', ...js.configs.recommended },
config.airbnb({
typescript: isTypescript,
...(opts.airbnb || {}),
}),
enableNode && config.node(enableNode === true ? {} : enableNode),
enablePromise && config.promise(enablePromise === true ? {} : enablePromise),
enablePerfectionist && config.perfectionist(
enablePerfectionist === true ? {} : enablePerfectionist,
),
enableUnicorn && config.unicorn(enableUnicorn === true ? {} : enableUnicorn),
enableRegexp && regexp(enableRegexp === true ? {} : enableRegexp),
enableJsx && jsx(),
enableTailwind && config.tailwindcss(enableTailwind === true ? {} : enableTailwind),
enableUnoCSS && config.unocss(enableUnoCSS === true ? {} : enableUnoCSS),
enableTypeScript && typescript({
...typescriptOptions,
componentExts,
type: packageType,
}),
enableNextjs && config.nextjs(enableNextjs === true ? {} : enableNextjs),
enableReact && react({
...typescriptOptions,
overrides: enableReact === true ? {} : enableReact,
tsconfigPath,
} as any), // ? NOTE: buggy even in antfu source... it's okay
enableMarkdown && markdown({
componentExts,
overrides: enableMarkdown === true ? {} : enableMarkdown,
}),
// enableJsdoc && jsdoc({}),
);
if (stylisticOptions) {
configs.push(config.stylistic(stylisticOptions));
}
if (enableCatalogs) {
configs.push(
pnpm(),
);
}
if (enableJsonc) {
configs.push(
jsonc({
overrides: enableJsonc === true ? { 'style/comma-dangle': ['error', 'never'] } : enableJsonc,
stylistic: stylisticOptions,
}),
sortPackageJson(),
sortTsconfig(),
);
}
if (enableYaml) {
configs.push(yaml({
overrides: enableYaml === true ? {} : enableYaml,
stylistic: stylisticOptions,
}));
}
if (enableToml) {
configs.push(toml({
overrides: enableToml === true ? {} : enableToml,
stylistic: stylisticOptions,
}));
}
configs.push(
// enableAstro
// ? [
// pluginAstro.configs['flat/recommended'].flat(),
// {
// // files: [GLOB_ASTRO],
// name: 'xaxa/astro/overrides',
// rules: {
// 'astro/sort-attributes': ['error', {
// ignoreCase: true,
// order: 'asc',
// type: 'alphabetical',
// }],
// },
// },
// ].flat()
// : {},
enableAstro && antfuAstro({
overrides: enableAstro === true ? {} : enableAstro,
stylistic: stylisticOptions,
}),
enableWgw && config.wgw({
isAstro: Boolean(enableAstro),
isInEditor,
isTypescript,
overrides: enableWgw === true ? {} : enableWgw,
}),
);
configs.push(
disables(),
);
if (enableMarkdown) {
opts.formatters = (opts.formatters || {}) as OptionsFormatters;
opts.formatters = { markdown: 'prettier', ...opts.formatters } as OptionsFormatters;
}
// if (enableAstro) {
// opts.formatters = (opts.formatters || {}) as OptionsFormatters;
// opts.formatters = { astro: 'prettier', ...opts.formatters } as OptionsFormatters;
// }
if (opts.formatters) {
configs.push(formatters(
opts.formatters,
typeof stylisticOptions === 'boolean' ? {} : stylisticOptions,
));
}
let composer = new FlatConfigComposer();
composer = composer
.append(
...configs.flat().filter(Boolean),
...userConfigs.flat().filter(Boolean) as any, // ? NOTE: bruh... it's okay
);
if (autoRenamePlugins) {
composer = composer
.renamePlugins(defaultPluginRenaming);
}
if (isInEditor) {
composer = composer
.disableRulesFix(disableFixForRules, {
builtinRules: () => {
const imported = import(['eslint', 'use-at-your-own-risk'].join('/'));
return imported.then((r) => r.builtinRules);
},
});
}
return composer;
}
export {
xaxa,
};