@bfra.me/eslint-config
Version:
Shared ESLint configuration for bfra.me
2,493 lines • 79.2 kB
JavaScript
import "./chunk-EGRHWZRV.js";
// src/define-config.ts
import { isPackageExists as isPackageExists3 } from "local-pkg";
// ../es/src/env/ci.ts
import isCI from "is-in-ci";
function isInCI() {
return isCI;
}
// ../es/src/env/git.ts
import process2 from "process";
// ../es/src/env/helpers.ts
import process from "process";
function hasNonEmptyEnv(key) {
const value = process.env[key];
return typeof value === "string" && value.trim().length > 0;
}
// ../es/src/env/git.ts
function isInGitLifecycle() {
const lifecycleScript = process2.env.npm_lifecycle_script;
const isLintStaged = typeof lifecycleScript === "string" && (lifecycleScript.startsWith("lint-staged") || lifecycleScript.startsWith("nano-staged"));
return hasNonEmptyEnv("GIT_PARAMS") || hasNonEmptyEnv("VSCODE_GIT_COMMAND") || isLintStaged;
}
// ../es/src/env/editor.ts
function isInEditorEnv() {
if (isInCI()) return false;
if (isInGitLifecycle()) return false;
return hasNonEmptyEnv("VSCODE_PID") || hasNonEmptyEnv("VSCODE_CWD") || hasNonEmptyEnv("JETBRAINS_IDE") || hasNonEmptyEnv("VIM") || hasNonEmptyEnv("NVIM");
}
// ../es/src/env/runtime.ts
import process3 from "process";
// src/compose-config.ts
import { composer } from "eslint-flat-config-utils";
var composeConfig = (...configs) => composer(...configs);
// src/globs.ts
var GLOB_SRC_EXT = "?([cm])[jt]s?(x)";
var GLOB_SRC = "**/*.?([cm])[jt]s?(x)";
var GLOB_JS = "**/*.?([cm])js";
var GLOB_JSX = "**/*.?([cm])jsx";
var GLOB_TS = "**/*.?([cm])ts";
var GLOB_TSX = "**/*.?([cm])tsx";
var GLOB_JSON = "**/*.json";
var GLOB_JSON5 = "**/*.json5";
var GLOB_JSONC = "**/*.jsonc";
var GLOB_JSON_FILES = ["*.json", "*.json5", "*.jsonc"].flatMap((p) => [p, `**/${p}`]);
var GLOB_TOML = "**/*.toml";
var GLOB_TOML_FILES = ["*.toml"].flatMap((p) => [p, `**/${p}`]);
var GLOB_YAML = "**/*.y?(a)ml";
var GLOB_YAML_FILES = ["*.yaml", "*.yml"].flatMap((p) => [p, `**/${p}`]);
var GLOB_MARKDOWN = "**/*.md";
var GLOB_MARKDOWN_FILES = ["*.markdown", "*.md"].flatMap((p) => [p, `**/${p}`]);
var GLOB_MARKDOWN_CODE = `${GLOB_MARKDOWN}/${GLOB_SRC}`;
var GLOB_CODE_IN_MARKDOWN_FILES = GLOB_MARKDOWN_FILES.flatMap((p) => [
`${p}/*.js`,
`${p}/*.jsx`,
`${p}/*.cjs`,
`${p}/*.mjs`,
`${p}/*.ts`,
`${p}/*.tsx`,
`${p}/*.cts`,
`${p}/*.mts`,
`${p}/*.json`,
`${p}/*.json5`,
`${p}/*.jsonc`,
`${p}/*.yaml`,
`${p}/*.yml`,
`${p}/*.toml`
]);
var GLOB_EXT_IN_MARKDOWN_FILES = [
...GLOB_CODE_IN_MARKDOWN_FILES,
...GLOB_MARKDOWN_FILES.flatMap((p) => [
`${p}/*.json`,
`${p}/*.json5`,
`${p}/*.jsonc`,
`${p}/*.toml`,
`${p}/*.yml`,
`${p}/*.yaml`,
`${p}/*.vue`,
`${p}/*.svelte`,
`${p}/*.astro`
])
];
var GLOB_MARKDOWN_IN_MARKDOWN = "**/*.md/*.md";
var GLOB_ASTRO = "**/*.astro";
var GLOB_ASTRO_TS = `${GLOB_ASTRO}/*.ts`;
var GLOB_PACKAGE_JSON_FILES = ["package.json", "package.json5", "package.jsonc"].flatMap(
(file) => [file, `**/${file}`]
);
var GLOB_RENOVATE_CONFIG = [
"**/renovate.json",
"**/renovate.json5",
"**/.renovaterc",
"**/.renovaterc.json",
"**/.renovaterc.json5"
];
var GLOB_TS_CONFIG = ["**/[t]sconfig.json", "**/[jt]sconfig.*.json"];
var GLOB_TESTS = [
`**/__tests__/**/*.${GLOB_SRC_EXT}`,
`**/*.spec.${GLOB_SRC_EXT}`,
`**/*.test.${GLOB_SRC_EXT}`,
`**/*.bench.${GLOB_SRC_EXT}`,
`**/*.benchmark.${GLOB_SRC_EXT}`
];
var GLOB_EXCLUDE = [
"**/node_modules",
"**/dist",
"**/coverage",
"**/out",
"**/temp",
"**/.idea",
"**/.next",
"**/.nuxt",
"**/.output",
"**/.svelte-kit",
"**/.tsup",
"**/.vercel",
"**/.vitepress/cache",
"**/.vite-inspect",
"**/.yarn",
"**/__snapshots__",
"**/test/fixtures",
"**/auto-import?(s).d.ts",
"**/.changeset/*.md",
"**/CHANGELOG*.md",
"**/changelog*.md",
"**/components.d.ts",
"**/devcontainer-lock.json",
"**/LICENSE*",
"**/license*",
"**/*.min.*",
"**/package-lock.json",
"**/pnpm-lock.yaml",
"**/typed-router.d.ts",
"**/yarn.lock",
"**/bun.lockb"
];
// src/parsers/any-parser.ts
import { fileURLToPath } from "url";
// package.json
var version = "0.52.1";
// src/parsers/any-parser.ts
var lineBreakPattern = /\r\n|[\n\r\u2028\u2029]/u;
var meta = {
name: fileURLToPath(import.meta.url),
version
};
function parseForESLint(text) {
const lines = text.split(lineBreakPattern);
return {
ast: {
body: [],
comments: [],
loc: {
end: {
column: lines.at(-1)?.length ?? 0,
line: lines.length
},
start: {
column: 0,
line: 1
}
},
range: [0, text.length],
sourceType: "module",
tokens: [],
type: "Program"
}
};
}
var anyParser = { meta, parseForESLint };
// src/utils.ts
import { fileURLToPath as fileURLToPath3 } from "url";
import { isPackageExists } from "local-pkg";
// ../es/src/module/interop.ts
import { fileURLToPath as fileURLToPath2 } from "url";
async function interopDefault(m) {
const resolved = await m;
return typeof resolved === "object" && resolved !== null && "default" in resolved ? interopDefault(resolved.default) : resolved;
}
// src/utils.ts
var scopeUrl = fileURLToPath3(new URL(".", import.meta.url));
function isPackageInScope2(name) {
return isPackageExists(name, { paths: [scopeUrl] });
}
// src/require-of.ts
var packageExistsCache = /* @__PURE__ */ new Map();
var has = (name) => {
if (!packageExistsCache.has(name)) {
packageExistsCache.set(name, isPackageInScope2(name));
}
return packageExistsCache.get(name);
};
async function requireOf(names, getConfig, fallback2) {
const missingList = names.filter((n) => n !== "" && !has(n));
if (missingList.length) {
return fallback2(missingList);
}
return getConfig();
}
// src/configs/fallback.ts
async function fallback(missingList = [], options) {
const rules = await interopDefault(import("./missing-module-for-config-XLDTAVYJ.js"));
const pluginName = `@bfra.me${missingList.length > 0 ? `/missing-modules-${missingList.map((m) => m.replaceAll(/[^a-z0-9]/gi, "-").toLowerCase()).join("-")}` : ""}`;
return [
{
plugins: {
[pluginName]: {
rules: {
"missing-module-for-config": rules
}
}
},
rules: {
[`${pluginName}/missing-module-for-config`]: ["error", missingList]
},
...options ?? {}
}
];
}
// src/configs/astro.ts
async function astro(options = {}) {
const { files = [GLOB_ASTRO], overrides = {}, stylistic: stylistic2 = true } = options;
const includeStylistic = typeof stylistic2 === "boolean" ? stylistic2 : true;
return requireOf(
["eslint-plugin-astro", "astro-eslint-parser"],
async () => {
const [pluginAstro, parserAstro, parserTs] = await Promise.all([
interopDefault(import("eslint-plugin-astro")),
interopDefault(import("astro-eslint-parser")),
interopDefault(import("typescript-eslint")).then(({ parser }) => parser)
]);
return [
{
name: "@bfra.me/astro/setup",
plugins: { astro: pluginAstro }
},
{
name: "@bfra.me/astro/rules",
files,
languageOptions: {
globals: pluginAstro.environments.astro.globals,
parser: parserAstro,
parserOptions: {
extraFileExtensions: [".astro"],
parser: parserTs
},
sourceType: "module"
},
processor: "astro/client-side-ts",
rules: {
"astro/missing-client-only-directive-value": "error",
"astro/no-conflict-set-directives": "error",
"astro/no-deprecated-astro-canonicalurl": "error",
"astro/no-deprecated-astro-fetchcontent": "error",
"astro/no-deprecated-astro-resolve": "error",
"astro/no-deprecated-getentrybyslug": "error",
"astro/no-set-html-directive": "off",
"astro/no-unused-define-vars-in-style": "error",
"astro/semi": "off",
"astro/valid-compile": "error",
...includeStylistic ? {
"@stylistic/indent": "off",
"@stylistic/jsx-closing-tag-location": "off",
"@stylistic/jsx-one-expression-per-line": "off",
"@stylistic/no-multiple-empty-lines": "off"
} : {},
...overrides
}
}
];
},
async (missingList) => fallback(missingList, {
files,
languageOptions: {
parser: anyParser
}
})
);
}
// src/configs/command.ts
async function command() {
const config2 = await interopDefault(import("eslint-plugin-command/config"));
return [
{
...config2(),
name: "@bfra.me/command"
}
];
}
// src/configs/epilogue.ts
async function epilogue() {
return [
{
name: "@bfra.me/epilogue/cli",
files: [`**/cli/${GLOB_SRC}`, `**/cli.${GLOB_SRC_EXT}`],
rules: {
"@typescript-eslint/explicit-function-return-type": "off",
"no-console": "off"
}
},
{
name: "@bfra.me/epilogue/configs",
files: [`**/*.config.${GLOB_SRC_EXT}`, `**/*.config.*.${GLOB_SRC_EXT}`],
rules: {
"@typescript-eslint/explicit-function-return-type": "off",
"no-console": "off"
}
},
{
name: "@bfra.me/epilogue/scripts",
files: [`**/scripts/${GLOB_SRC}`],
rules: {
"@typescript-eslint/explicit-function-return-type": "off",
"no-console": "off"
}
},
{
name: "@bfra.me/epilogue/commonjs",
files: ["**/*.js", "**/*.cjs"],
rules: {
"@typescript-eslint/no-require-imports": "off"
}
},
{
name: "@bfra.me/epilogue/dts",
files: ["**/*.d.?([cm])ts"],
rules: {
"eslint-comments/no-unlimited-disable": "off",
"import-x/no-duplicates": "off",
"no-restricted-syntax": "off",
"unused-imports/no-unused-vars": "off"
}
},
{
name: "@bfra.me/epilogue"
}
];
}
// src/plugins.ts
import { default as default2 } from "@eslint-community/eslint-plugin-eslint-comments";
import { default as default3 } from "eslint-plugin-import-x";
// src/configs/eslint-comments.ts
function eslintComments() {
return [
{
name: "@bfra.me/eslint-comments/rules",
plugins: { "eslint-comments": default2 },
rules: {
"eslint-comments/disable-enable-pair": ["error", { allowWholeFile: true }],
"eslint-comments/no-aggregating-enable": "error",
"eslint-comments/no-duplicate-disable": "error",
"eslint-comments/no-unlimited-disable": "error",
"eslint-comments/no-unused-disable": "error",
"eslint-comments/no-unused-enable": "error"
}
}
];
}
// src/configs/ignores.ts
async function ignores(userIgnores = []) {
let ignores2 = [...GLOB_EXCLUDE];
if (typeof userIgnores === "function") {
ignores2 = userIgnores(ignores2);
} else {
ignores2.push(...userIgnores);
}
return [
{
name: "@bfra.me/ignores",
ignores: ignores2
}
];
}
async function gitignore(gitignoreOptions = { strict: false }) {
return interopDefault(import("eslint-config-flat-gitignore")).then((ignore) => [
ignore({
name: "@bfra.me/gitignore",
...gitignoreOptions
})
]);
}
// src/configs/imports.ts
function imports(options = {}) {
const { overrides = {}, stylistic: stylistic2 = true } = options;
const includeStylistic = typeof stylistic2 === "boolean" ? stylistic2 : true;
return [
{
name: "@bfra.me/imports",
plugins: {
"import-x": default3
},
rules: {
"import-x/no-named-default": "error",
"import-x/first": "error",
"import-x/no-duplicates": "error",
"import-x/no-mutable-exports": "error",
"import-x/no-self-import": "error",
"import-x/no-useless-path-segments": "error",
"import-x/no-webpack-loader-syntax": "error",
...includeStylistic ? {
"import-x/newline-after-import": ["warn", { count: 1 }]
} : {},
...overrides
}
}
];
}
// src/configs/javascript.ts
import globals from "globals";
async function javascript(options = {}) {
const { isInEditor = false, overrides = {} } = options;
const pluginUnusedImports = await interopDefault(import("eslint-plugin-unused-imports"));
return [
{
name: "@bfra.me/javascript/options",
languageOptions: {
ecmaVersion: "latest",
globals: {
...globals.browser,
...globals.es2021,
...globals.node,
document: "readonly",
navigator: "readonly",
window: "readonly"
},
parserOptions: {
ecmaFeatures: {
jsx: true
},
ecmaVersion: "latest",
sourceType: "module"
},
sourceType: "module"
},
linterOptions: {
reportUnusedDisableDirectives: true
}
},
{
name: "@bfra.me/javascript/rules",
plugins: {
"unused-imports": pluginUnusedImports
},
rules: {
"no-shadow-restricted-names": "error",
"no-useless-rename": "error",
"accessor-pairs": ["error", { enforceForClassMembers: true, setWithoutGet: true }],
"array-callback-return": "error",
"block-scoped-var": "error",
"constructor-super": "error",
"default-case-last": "error",
"dot-notation": "error",
eqeqeq: ["error", "smart"],
"new-cap": ["error", { capIsNew: false, newIsCap: true, properties: true }],
"no-alert": "error",
"no-array-constructor": "error",
"no-async-promise-executor": "error",
"no-caller": "error",
"no-case-declarations": "error",
"no-class-assign": "error",
"no-compare-neg-zero": "error",
"no-cond-assign": ["error", "always"],
"no-console": ["warn", { allow: ["warn", "error"] }],
"no-const-assign": "error",
"no-control-regex": "error",
"no-debugger": "error",
"no-delete-var": "error",
"no-dupe-args": "error",
"no-dupe-class-members": "error",
"no-dupe-keys": "error",
"no-duplicate-case": "error",
"no-duplicate-imports": ["error", { allowSeparateTypeImports: true }],
"no-empty": ["error", { allowEmptyCatch: true }],
"no-empty-character-class": "error",
"no-empty-pattern": "error",
"no-eval": "error",
"no-ex-assign": "error",
"no-extend-native": "error",
"no-extra-bind": "error",
"no-extra-boolean-cast": "error",
"no-fallthrough": ["warn", { commentPattern: String.raw`break[\s\w]*omitted` }],
"no-func-assign": "error",
"no-global-assign": "error",
"no-implied-eval": "error",
"no-import-assign": "error",
"no-inner-declarations": "error",
"no-invalid-regexp": "error",
"no-irregular-whitespace": "error",
"no-iterator": "error",
"no-labels": ["error", { allowLoop: false, allowSwitch: false }],
"no-lone-blocks": "error",
"no-lonely-if": "error",
"no-loss-of-precision": "error",
"no-misleading-character-class": "error",
"no-multi-str": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-native-nonconstructor": "error",
"no-new-wrappers": "error",
"no-obj-calls": "error",
"no-octal": "error",
"no-octal-escape": "error",
"no-proto": "error",
"no-prototype-builtins": "error",
"no-redeclare": ["error", { builtinGlobals: false }],
"no-regex-spaces": "error",
"no-restricted-globals": [
"error",
{ name: "global", message: "Use `globalThis` instead." },
{ name: "self", message: "Use `globalThis` instead." }
],
"no-restricted-properties": [
"error",
{
message: "Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.",
property: "__proto__"
},
{ message: "Use `Object.defineProperty` instead.", property: "__defineGetter__" },
{ message: "Use `Object.defineProperty` instead.", property: "__defineSetter__" },
{
message: "Use `Object.getOwnPropertyDescriptor` instead.",
property: "__lookupGetter__"
},
{
message: "Use `Object.getOwnPropertyDescriptor` instead.",
property: "__lookupSetter__"
}
],
"no-restricted-syntax": [
"error",
"TSEnumDeclaration[const=true]",
"TSExportAssignment",
"ForInStatement",
"LabeledStatement",
"WithStatement"
],
"no-self-assign": ["error", { props: true }],
"no-self-compare": "error",
"no-sequences": "error",
"no-sparse-arrays": "error",
"no-template-curly-in-string": "error",
"no-this-before-super": "error",
"no-throw-literal": "error",
"no-undef": "error",
"no-undef-init": "error",
"no-unexpected-multiline": "error",
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": ["error", { defaultAssignment: false }],
"no-unreachable": "error",
"no-unreachable-loop": "error",
"no-unsafe-finally": "error",
"no-unused-expressions": [
"error",
{
allowShortCircuit: true,
allowTaggedTemplates: true,
allowTernary: true
}
],
"no-unused-vars": [
"error",
{
args: "none",
caughtErrors: "none",
ignoreRestSiblings: true,
vars: "all"
}
],
"no-use-before-define": ["error", { classes: false, functions: false, variables: true }],
"no-useless-backreference": "error",
"no-useless-call": "error",
"no-useless-catch": "error",
"no-useless-computed-key": "error",
"no-useless-constructor": "error",
"no-useless-return": "error",
"no-var": "error",
"no-void": "error",
"no-with": "error",
"object-shorthand": ["error", "always", { avoidQuotes: true, ignoreConstructors: false }],
"one-var": ["error", { initialized: "never" }],
"prefer-arrow-callback": [
"error",
{
allowNamedFunctions: false,
allowUnboundThis: true
}
],
"prefer-const": [
"error",
{
destructuring: "all",
ignoreReadBeforeAssign: true
}
],
"prefer-exponentiation-operator": "error",
"prefer-promise-reject-errors": "error",
"prefer-regex-literals": ["error", { disallowRedundantWrapping: true }],
"prefer-rest-params": "error",
"prefer-spread": "error",
"prefer-template": "error",
"symbol-description": "error",
"unicode-bom": ["error", "never"],
"unused-imports/no-unused-imports": isInEditor ? "off" : "error",
"unused-imports/no-unused-vars": [
"error",
{
args: "after-used",
argsIgnorePattern: "^_",
ignoreRestSiblings: true,
vars: "all",
varsIgnorePattern: "^_"
}
],
"use-isnan": ["error", { enforceForIndexOf: true, enforceForSwitchCase: true }],
"valid-typeof": ["error", { requireStringLiterals: true }],
"vars-on-top": "error",
...overrides
}
}
];
}
// src/configs/jsdoc.ts
async function jsdoc(options = {}) {
const { stylistic: stylistic2 = true } = options;
const includeStylistic = typeof stylistic2 === "boolean" ? stylistic2 : true;
return [
{
name: "@bfra.me/jsdoc",
plugins: { jsdoc: await interopDefault(import("eslint-plugin-jsdoc")) },
rules: {
"jsdoc/check-param-names": "warn",
"jsdoc/check-property-names": "warn",
"jsdoc/require-param-name": "warn",
"jsdoc/require-property-name": "warn",
"jsdoc/check-access": "warn",
"jsdoc/check-types": "warn",
"jsdoc/empty-tags": "warn",
"jsdoc/implements-on-classes": "warn",
"jsdoc/no-defaults": "warn",
"jsdoc/no-multi-asterisks": "warn",
"jsdoc/require-property": "warn",
"jsdoc/require-property-description": "warn",
"jsdoc/require-returns-check": "warn",
"jsdoc/require-returns-description": "warn",
"jsdoc/require-yields-check": "warn",
...includeStylistic ? {
"jsdoc/check-alignment": "warn",
"jsdoc/multiline-blocks": "warn"
} : {}
}
}
];
}
// src/configs/json-schema.ts
async function jsonSchema(name, files) {
const pluginJsonSchemaValidator = await interopDefault(
import("eslint-plugin-json-schema-validator")
);
return [
...pluginJsonSchemaValidator.configs["flat/base"].map((config2) => {
if (config2.files == null) {
return { ...config2, files };
}
if (config2.files.flat().some((file) => files.some((suffix) => file.endsWith(suffix)))) {
return config2;
}
return {};
}).filter((o) => Object.keys(o).length > 0).map((config2, index) => ({
...config2,
name: config2.plugins ? `@bfra.me/${name}/json-schema/plugins` : `@bfra.me/${name}/json-schema/${(config2.name ?? "") || `unnamed${index}`}`
})),
{
name: `@bfra.me/${name}/json-schema`,
files,
rules: {
"json-schema-validator/no-invalid": "off"
}
}
];
}
// src/configs/jsonc.ts
async function jsonc(options = {}) {
const { files = GLOB_JSON_FILES, overrides = {}, stylistic: stylistic2 = true } = options;
const stylisticConfig = typeof stylistic2 === "boolean" ? {} : stylistic2;
const indent = typeof stylisticConfig.indent === "number" ? stylisticConfig.indent : 2;
const includeStylistic = typeof stylistic2 === "boolean" ? stylistic2 : true;
const pluginJsonc = await interopDefault(import("eslint-plugin-jsonc"));
const baseConfigs = pluginJsonc.configs.base ?? pluginJsonc.configs["flat/base"] ?? [];
return [
...baseConfigs,
...await jsonSchema("jsonc", files),
{
name: "@bfra.me/jsonc",
files,
language: "jsonc/x",
rules: {
"jsonc/no-bigint-literals": "error",
"jsonc/no-binary-expression": "error",
"jsonc/no-binary-numeric-literals": "error",
"jsonc/no-dupe-keys": "error",
"jsonc/no-escape-sequence-in-identifier": "error",
"jsonc/no-floating-decimal": "error",
"jsonc/no-hexadecimal-numeric-literals": "error",
"jsonc/no-infinity": "error",
"jsonc/no-multi-str": "error",
"jsonc/no-nan": "error",
"jsonc/no-number-props": "error",
"jsonc/no-numeric-separators": "error",
"jsonc/no-octal": "error",
"jsonc/no-octal-escape": "error",
"jsonc/no-octal-numeric-literals": "error",
"jsonc/no-parenthesized": "error",
"jsonc/no-plus-sign": "error",
"jsonc/no-regexp-literals": "error",
"jsonc/no-sparse-arrays": "error",
"jsonc/no-template-literals": "error",
"jsonc/no-undefined-value": "error",
"jsonc/no-unicode-codepoint-escapes": "error",
"jsonc/no-useless-escape": "error",
"jsonc/space-unary-ops": "error",
"jsonc/valid-json-number": "error",
"jsonc/vue-custom-block/no-parsing-error": "error",
...includeStylistic ? {
"jsonc/array-bracket-spacing": ["error", "never"],
"jsonc/comma-dangle": ["error", "never"],
"jsonc/comma-style": ["error", "last"],
"jsonc/indent": ["error", indent],
"jsonc/key-spacing": ["error", { afterColon: true, beforeColon: false }],
"jsonc/object-curly-newline": ["error", { consistent: true, multiline: true }],
"jsonc/object-curly-spacing": ["error", "never"],
"jsonc/object-property-newline": ["error", { allowAllPropertiesOnSameLine: true }],
"jsonc/quote-props": "error",
"jsonc/quotes": "error"
} : {},
...overrides
}
}
];
}
// src/configs/jsx.ts
async function jsx(options = {}) {
const { a11y } = options;
const baseConfig = {
name: "@bfra.me/jsx",
files: [GLOB_JSX, GLOB_TSX],
languageOptions: {
parserOptions: {
ecmaFeatures: {
jsx: true
}
}
},
plugins: {},
rules: {}
};
if (a11y === false || a11y === void 0) {
return [baseConfig];
}
return requireOf(
["eslint-plugin-jsx-a11y"],
async () => {
const jsxA11yPlugin = await interopDefault(import("eslint-plugin-jsx-a11y"));
const a11yConfig = jsxA11yPlugin.flatConfigs.recommended;
const a11yRules = {
...a11yConfig.rules ?? {},
...typeof a11y === "object" && a11y.overrides ? a11y.overrides : {}
};
return [
{
...baseConfig,
...a11yConfig,
name: baseConfig.name,
files: baseConfig.files,
languageOptions: {
...baseConfig.languageOptions,
...a11yConfig.languageOptions
},
plugins: {
...baseConfig.plugins,
"jsx-a11y": jsxA11yPlugin
},
rules: {
...baseConfig.rules,
...a11yRules
}
}
];
},
async (missingList) => fallback(missingList, {
name: baseConfig.name,
files: baseConfig.files,
languageOptions: baseConfig.languageOptions
})
);
}
// src/configs/markdown.ts
import { mergeProcessors, processorPassThrough } from "eslint-merge-processors";
// src/parsers/plain-parser.ts
import { fileURLToPath as fileURLToPath4 } from "url";
var plainParser = {
meta: {
name: fileURLToPath4(import.meta.url),
version
},
parseForESLint: (code) => ({
ast: {
body: [],
comments: [],
loc: { end: code.length, start: 0 },
range: [0, code.length],
tokens: [],
type: "Program"
},
scopeManager: null,
services: { isPlain: true },
visitorKeys: {
Program: []
}
})
};
// src/configs/markdown.ts
async function markdown(options = {}) {
const {
codeBlocks = true,
files = [GLOB_MARKDOWN],
frontmatter = "yaml",
language = "gfm",
overrides = {}
} = options;
const markdown2 = await interopDefault(import("@eslint/markdown"));
const configs = [
{
name: "@bfra.me/markdown/plugin",
plugins: {
markdown: markdown2
}
},
{
name: "@bfra.me/markdown/processor",
files,
ignores: [GLOB_MARKDOWN_IN_MARKDOWN],
language: `markdown/${language}`,
processor: mergeProcessors([markdown2.processors.markdown, processorPassThrough]),
...frontmatter !== false && {
languageOptions: { frontmatter }
}
},
{
name: "@bfra.me/markdown/parser",
files,
languageOptions: {
parser: plainParser
}
},
// Disable rules incompatible with Markdown processor's virtual files
// The @eslint/markdown processor creates virtual files that lack complete ESLint SourceCode API
// (getAllComments, getTokenBefore, etc.) and TypeScript parser services (esTreeNodeToTSNodeMap).
// This causes failures in plugins that depend on these features.
{
name: "@bfra.me/markdown/disabled",
files,
rules: {
"jsdoc/check-property-names": "off",
"jsdoc/require-property-name": "off",
"unicorn/filename-case": "off",
"command/command": "off",
"jsdoc/check-access": "off",
"jsdoc/check-alignment": "off",
"jsdoc/check-types": "off",
"jsdoc/empty-tags": "off",
"jsdoc/multiline-blocks": "off",
"jsdoc/no-multi-asterisks": "off",
"jsdoc/require-property": "off",
"jsdoc/require-property-description": "off",
"no-irregular-whitespace": "off",
"perfectionist/sort-exports": "off",
"perfectionist/sort-imports": "off",
"regexp/no-legacy-features": "off",
"regexp/no-missing-g-flag": "off",
"regexp/no-useless-dollar-replacements": "off",
"regexp/no-useless-flag": "off"
}
},
{
name: "@bfra.me/markdown/overrides",
files,
rules: {
"markdown/fenced-code-language": "warn",
"markdown/heading-increment": "error",
"markdown/no-duplicate-definitions": "error",
"markdown/no-empty-definitions": "error",
"markdown/no-empty-images": "error",
"markdown/no-empty-links": "error",
"markdown/no-invalid-label-refs": "error",
"markdown/no-missing-atx-heading-space": "error",
"markdown/no-missing-label-refs": (
// Disable label warnings for admonitions if using GFM
language === "gfm" ? [
"error",
{
// @keep-sorted
allowLabels: ["!CAUTION", "!IMPORTANT", "!NOTE", "!TIP", "!WARNING"]
}
] : "error"
),
"markdown/no-missing-link-fragments": "error",
"markdown/no-multiple-h1": "error",
"markdown/no-reference-like-urls": "error",
"markdown/no-reversed-media-syntax": "error",
"markdown/no-space-in-emphasis": "error",
"markdown/no-unused-definitions": "error",
"markdown/require-alt-text": "error",
"markdown/table-column-count": "error",
...overrides
}
}
];
if (codeBlocks === false) {
return configs;
}
const {
javascript: javascript2 = true,
json = true,
jsx: jsx2 = true,
typescript: typescript2 = true,
yaml: yaml2 = true
} = codeBlocks === true ? {} : codeBlocks;
if (typescript2 || javascript2 || jsx2) {
const tselint = await interopDefault(import("typescript-eslint"));
const tsJsFiles = [];
if (typescript2) {
tsJsFiles.push(...GLOB_MARKDOWN_FILES.map((p) => `${p}/${GLOB_TS}`));
}
if (typescript2 || jsx2) {
tsJsFiles.push(...GLOB_MARKDOWN_FILES.map((p) => `${p}/${GLOB_TSX}`));
}
if (javascript2) {
tsJsFiles.push(...GLOB_MARKDOWN_FILES.map((p) => `${p}/${GLOB_JS}`));
}
if (javascript2 || jsx2) {
tsJsFiles.push(...GLOB_MARKDOWN_FILES.map((p) => `${p}/${GLOB_JSX}`));
}
configs.push({
name: "@bfra.me/markdown/code-blocks/typescript-javascript",
files: tsJsFiles,
languageOptions: {
parser: tselint.parser,
parserOptions: {
ecmaFeatures: {
impliedStrict: true,
jsx: jsx2 || javascript2
},
ecmaVersion: "latest",
// Type-aware rules disabled: documentation snippets lack tsconfig context
project: null,
sourceType: "module"
}
},
rules: {
// Only disable non-type-aware rules we want to skip for markdown code blocks
"@typescript-eslint/no-namespace": "off",
"@stylistic/comma-dangle": "off",
"@stylistic/eol-last": "off",
"@stylistic/padding-line-between-statements": "off",
"@typescript-eslint/consistent-type-imports": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-redeclare": "off",
"@typescript-eslint/no-require-imports": "off",
"@typescript-eslint/no-unused-expressions": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-var-requires": "off",
"import-x/newline-after-import": "off",
"jsdoc/require-returns-check": "off",
"no-alert": "off",
"no-console": "off",
"no-labels": "off",
"no-lone-blocks": "off",
"no-restricted-imports": "off",
"no-restricted-syntax": "off",
"no-undef": "off",
"no-unused-expressions": "off",
"no-unused-labels": "off",
"no-unused-vars": "off",
"node/prefer-global/process": "off",
"unicode-bom": "off",
"unused-imports/no-unused-imports": "off",
"unused-imports/no-unused-vars": "off"
}
});
}
if (json) {
configs.push({
name: "@bfra.me/markdown/code-blocks/json",
files: GLOB_MARKDOWN_FILES.flatMap((p) => [
`${p}/${GLOB_JSON}`,
`${p}/${GLOB_JSON5}`,
`${p}/${GLOB_JSONC}`
]),
language: "jsonc/x",
rules: {
// Examples may show JSON with trailing commas or explanatory comments
"jsonc/comma-dangle": "off",
"jsonc/no-comments": "off"
}
});
}
if (yaml2) {
const pluginYaml = await interopDefault(import("eslint-plugin-yml"));
const standardConfigs = pluginYaml.configs.standard ?? pluginYaml.configs["flat/standard"];
const normalizedConfigs = Array.isArray(standardConfigs) ? standardConfigs : [standardConfigs];
const languageSetup = normalizedConfigs.find(
(c) => c != null && "language" in c && typeof c.language === "string"
);
const parserSetup = normalizedConfigs.find(
(c) => c != null && "languageOptions" in c && c.languageOptions != null
);
configs.push({
name: "@bfra.me/markdown/code-blocks/yaml",
files: GLOB_MARKDOWN_FILES.map((p) => `${p}/${GLOB_YAML}`),
...languageSetup ? { language: languageSetup.language } : {},
...parserSetup ? { languageOptions: parserSetup.languageOptions } : {},
...languageSetup && "plugins" in languageSetup ? { plugins: languageSetup.plugins } : {},
rules: {
// Examples may show incomplete YAML mappings to focus on specific concepts
"yml/no-empty-mapping-value": "off"
}
});
}
return configs;
}
// src/configs/nextjs.ts
function normalizeRules(rules) {
return Object.fromEntries(
Object.entries(rules).map(([key, value]) => [key, typeof value === "string" ? [value] : value])
);
}
async function nextjs(options = {}) {
const { files = [GLOB_SRC], overrides = {} } = options;
return requireOf(
["@next/eslint-plugin-next"],
async () => {
const pluginNextJs = await interopDefault(import("@next/eslint-plugin-next"));
function getRules(name) {
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
}
})
);
}
// src/configs/node.ts
async function node() {
const pluginNode = await interopDefault(import("eslint-plugin-n"));
return [
{
name: "@bfra.me/node",
plugins: {
node: pluginNode
},
rules: {
"node/handle-callback-err": ["error", "^(err|error)$"],
"node/no-deprecated-api": "error",
"node/no-exports-assign": "error",
"node/no-new-require": "error",
"node/no-path-concat": "error",
"node/no-unsupported-features/es-builtins": "error",
"node/prefer-global/buffer": ["error", "never"],
"node/prefer-global/process": ["error", "never"],
"node/process-exit-as-throw": "error"
}
}
];
}
// src/configs/package-json.ts
async function packageJson(options = {}) {
const { files = GLOB_PACKAGE_JSON_FILES } = options;
return requireOf(
["eslint-plugin-node-dependencies"],
async () => {
const pluginNodeDependencies = await interopDefault(import("eslint-plugin-node-dependencies"));
return [
...pluginNodeDependencies.configs["flat/recommended"].map(
(config2, index) => ({
...config2,
name: config2.plugins ? `@bfra.me/package-json/plugins` : `@bfra.me/${(config2.name ?? "") || `package-json/unnamed${index}`}`,
files
})
),
...await jsonSchema("package-json", files)
];
},
async (missingList) => fallback(missingList, {
name: "@bfra.me/package-json/fallback",
files,
languageOptions: { parser: anyParser }
})
);
}
// src/configs/perfectionist.ts
async function perfectionist(options = {}) {
const {
isInEditor = false,
overrides = {},
sortExports = true,
sortImports = true,
sortNamedExports = true,
sortNamedImports = true
} = options;
const pluginPerfectionist = await interopDefault(import("eslint-plugin-perfectionist"));
return [
{
name: "@bfra.me/perfectionist",
plugins: {
perfectionist: pluginPerfectionist
},
rules: {
...sortNamedExports && {
"perfectionist/sort-named-exports": [
isInEditor ? "warn" : "error",
{
groups: ["value-export", "type-export"],
type: "natural"
}
]
},
...sortNamedImports && {
"perfectionist/sort-named-imports": [
isInEditor ? "warn" : "error",
{
groups: ["value-import", "type-import"],
type: "natural"
}
]
},
...sortExports && {
"perfectionist/sort-exports": [isInEditor ? "warn" : "error", { type: "natural" }]
},
...sortImports && {
"perfectionist/sort-imports": [
isInEditor ? "warn" : "error",
{
customGroups: [
{
elementNamePattern: "^[~#]/.*",
groupName: "internal"
},
{
elementNamePattern: "^[~#]/.*",
groupName: "internal-type",
selector: "type"
}
],
groups: [
"type-import",
"type-builtin",
"type-external",
["type-parent", "type-sibling", "type-index"],
"value-builtin",
"value-external",
["internal", "internal-type"],
["value-parent", "value-sibling", "value-index"],
"side-effect",
"style"
],
newlinesBetween: "ignore",
type: "natural"
}
]
},
...overrides
}
}
];
}
// src/configs/pnpm.ts
async function pnpm() {
return requireOf(
["eslint-plugin-pnpm"],
async () => {
const [pluginPnpm, pluginYaml] = await Promise.all([
interopDefault(import("eslint-plugin-pnpm")),
interopDefault(import("eslint-plugin-yml"))
]);
return [
{
name: "@bfra.me/pnpm/package-json",
files: ["package.json", "**/package.json"],
language: "jsonc/x",
plugins: {
pnpm: pluginPnpm
},
rules: {
"pnpm/json-enforce-catalog": "error",
"pnpm/json-prefer-workspace-settings": "error",
"pnpm/json-valid-catalog": "error"
}
},
{
name: "@bfra.me/pnpm/pnpm-workspace-yaml",
files: ["pnpm-workspace.yaml"],
language: "yaml",
plugins: {
pnpm: pluginPnpm,
yml: pluginYaml
},
rules: {
"pnpm/yaml-no-duplicate-catalog-item": "error",
"pnpm/yaml-no-unused-catalog-item": "error"
}
}
];
},
fallback
);
}
// src/configs/prettier.ts
import process4 from "process";
function getConfigRules(configs) {
if (Array.isArray(configs)) {
const configWithRules = [...configs].reverse().find(
(config2) => typeof config2 === "object" && config2 !== null && "rules" in config2
);
return configWithRules?.rules;
}
if (typeof configs === "object" && configs !== null && "rules" in configs) {
return configs.rules;
}
return void 0;
}
async function prettier(options = {}) {
const { isInEditor, overrides } = options;
return requireOf(
["eslint-config-prettier", "eslint-plugin-prettier", "prettier"],
async () => {
process4.env.ESLINT_CONFIG_PRETTIER_NO_DEPRECATED ??= "true";
const [configPrettier, pluginPrettier, pluginJsonc, pluginYaml] = await Promise.all([
interopDefault(import("eslint-config-prettier")),
interopDefault(import("eslint-plugin-prettier")),
interopDefault(import("eslint-plugin-jsonc")).catch(() => void 0),
interopDefault(import("eslint-plugin-yml")).catch(() => void 0)
]);
const jsoncPrettierRules = getConfigRules(pluginJsonc?.configs.prettier);
const yamlPrettierRules = getConfigRules(pluginYaml?.configs.prettier);
return [
{
name: "@bfra.me/prettier",
plugins: {
prettier: pluginPrettier
},
rules: {
"prettier/prettier": isInEditor ? "warn" : "error",
...configPrettier.rules,
...jsoncPrettierRules ?? {},
"toml/array-bracket-newline": "off",
"toml/array-bracket-spacing": "off",
"toml/array-element-newline": "off",
"toml/indent": "off",
"toml/inline-table-curly-spacing": "off",
"toml/key-spacing": "off",
"toml/table-bracket-spacing": "off",
...yamlPrettierRules ?? {},
...overrides
}
},
{
name: "@bfra.me/prettier/markdown",
files: GLOB_MARKDOWN_FILES,
rules: {
"prettier/prettier": [
"error",
{
embeddedLanguageFormatting: "off",
parser: "markdown"
}
]
}
},
{
name: "@bfra.me/prettier/toml",
files: GLOB_TOML_FILES,
rules: {
// TODO: Detect if the TOML plugin for Prettier is installed
// and if so, use the Prettier rules
"prettier/prettier": "off"
}
},
{
name: "@bfra.me/prettier/overrides",
files: GLOB_EXT_IN_MARKDOWN_FILES,
rules: {
"prettier/prettier": "off"
}
}
];
},
fallback
);
}
// src/configs/react.ts
import { isPackageExists as isPackageExists2 } from "local-pkg";
var ReactRefreshAllowConstantExportPackages = ["vite"];
var RemixPackages = ["@remix-run/node", "@remix-run/react", "@remix-run/serve", "@remix-run/dev"];
var ReactRouterPackages = [
"@react-router/node",
"@react-router/react",
"@react-router/serve",
"@react-router/dev"
];
var NextJsPackages = ["next"];
var ReactTypeAwareRules = {
"@eslint-react/no-implicit-key": "warn",
"@eslint-react/no-leaked-conditional-rendering": "warn"
};
async function react(options = {}) {
const {
files = [GLOB_SRC],
overrides = {},
tsconfigPath,
typeAware = {
files: [GLOB_TS, GLOB_TSX],
ignores: [`${GLOB_MARKDOWN}/**`, GLOB_ASTRO_TS]
}
} = options;
const isTypeAware = typeof tsconfigPath === "string" && tsconfigPath.trim().length > 0;
return requireOf(
["@eslint-react/eslint-plugin", "eslint-plugin-react-refresh"],
async () => {
const [pluginReact, pluginReactRefresh] = await Promise.all([
interopDefault(import("@eslint-react/eslint-plugin")),
import("eslint-plugin-react-refresh").then((m) => m.reactRefresh)
]);
const plugins = pluginReact.configs.all.plugins;
const isAllowConstantExport = ReactRefreshAllowConstantExportPackages.some(
(i) => isPackageExists2(i)
);
const isUsingRemix = RemixPackages.some((i) => isPackageExists2(i));
const isUsingReactRouter = ReactRouterPackages.some((i) => isPackageExists2(i));
const isUsingNext = NextJsPackages.some((i) => isPackageExists2(i));
return [
{
name: "@bfra.me/react/setup",
plugins: {
...plugins,
"react-refresh": pluginReactRefresh.plugin
}
},
{
name: "@bfra.me/react/rules",
files,
languageOptions: {
parserOptions: {
ecmaFeatures: {
jsx: true
}
},
sourceType: "module"
},
rules: {
...pluginReact.configs.recommended.rules,
// preconfigured rules from eslint-plugin-react-refresh https://github.com/ArnaudBarre/eslint-plugin-react-refresh/tree/main/src
"react-refresh/only-export-components": [
"warn",
{
allowConstantExport: isAllowConstantExport,
allowExportNames: [
...isUsingNext ? [
"dynamic",
"dynamicParams",
"revalidate",
"fetchCache",
"runtime",
"preferredRegion",
"maxDuration",
"config",
"generateStaticParams",
"metadata",
"generateMetadata",
"viewport",
"generateViewport"
] : [],
...isUsingRemix || isUsingReactRouter ? [
"meta",
"links",
"headers",
"loader",
"action",
"clientLoader",
"clientAction",
"handle",
"shouldRevalidate"
] : []
]
}
],
...overrides
}
},
...isTypeAware ? [
{
name: "@bfra.me/react/type-aware-rules",
files: typeAware.files,
ignores: typeAware.ignores,
rules: {
...ReactTypeAwareRules
}
}
] : []
];
},
async (missingList) => fallback(missingList, { files, languageOptions: { parser: anyParser } })
);
}
// src/configs/regexp.ts
async function regexp(options = {}) {
const { configs } = await interopDefault(import("eslint-plugin-regexp"));
const config2 = configs["flat/recommended"];
return [
{
...config2,
name: "@bfra.me/regexp",
rules: {
...config2.rules,
...options.overrides
}
}
];
}
// src/configs/sort.ts
import { sortOrder } from "sort-package-json";
function sortPackageJson() {
return [
{
name: "@bfra.me/sort/package-json",
files: ["**/package.json"],
rules: {
"jsonc/sort-array-values": [
"error",
{
order: { type: "asc" },
pathPattern: "^files$"
}
],
"jsonc/sort-keys": [
"error",
{
order: sortOrder,
pathPattern: "^$"
},
{
order: { type: "asc" },
pathPattern: "^(?:dev|peer|optional|bundled)?[Dd]ependencies(Meta)?$"
},
{
order: { type: "asc" },
pathPattern: "^(?:resolutions|overrides|pnpm.overrides)$"
},
{
order: { type: "asc" },
pathPattern: String.raw`^workspaces\.catalog$`
},
{
order: { type: "asc" },
pathPattern: String.raw`^workspaces\.catalogs\.[^.]+$`
},
{
order: ["types", "import", "require", "default"],
pathPattern: "^exports.*$"
},
{
order: [
// client hooks only
"pre-commit",
"prepare-commit-msg",
"commit-msg",
"post-commit",
"pre-rebase",
"post-rewrite",
"post-checkout",
"post-merge",
"pre-push",
"pre-auto-gc"
],
pathPattern: "^(?:gitHooks|husky|simple-git-hooks)$"
}
]
}
}
];
}
async function sortRenovateConfig() {
return [
{
name: "@bfra.me/sort/renovate-config",
files: GLOB_RENOVATE_CONFIG,
rules: {
"jsonc/sort-array-values": [
"error",
{
// Don't sort 'extends' or 'postUpgradeTasks.commands' arrays, as order matters
order: { type: "asc" },
pathPattern: "^(?!extends$|postUpgradeTasks.commands$).*"
}
],
"jsonc/sort-keys": [
"error",
{
// Based on the order defined here:
// https://github.com/sanity-io/renovate-config/blob/8c1fdebe125f16087924216f97838e93824109d1/scripts/update-sorting.js#L30
order: [
"$schema",
"description",
"extends",
"onboardingConfigFileName",
"lockFileMaintenance",
"packageRules",
"postUpgradeTasks"
],
pathPattern: "^$"
}
]
}
},
...await jsonSchema("renovate-config", GLOB_RENOVATE_CONFIG)
];
}
async function sortTsconfig() {
return [
{
name: "@bfra.me/sort/tsconfig",
files: GLOB_TS_CONFIG,
rules: {
"jsonc/sort-keys": [
"error",
{
order: ["extends", "compilerOptions", "references", "files", "include", "exclude"],
pathPattern: "^$"
},
{
order: [
/* Projects */
"incremental",
"composite",
"tsBuildInfoFile",
"disableSourceOfProjectReferenceRedirect",
"disableSolutionSearching",
"disableReferencedProjectLoad",
/* Language and Environment */
"target",
"jsx",
"jsxFactory",
"jsxFragmentFactory",
"jsxImportSource",
"lib",
"moduleDetection",
"noLib",
"reactNamespace",
"useDefineForClassFields",
"emitDecoratorMetadata",
"experimentalDecorators",
"libReplacement",
/* Modules */
"baseUrl",
"rootDir",
"rootDirs",
"customConditions",
"module",
"moduleResolution",
"moduleSuffixes",
"noResolve",
"paths",
"resolveJsonModule",
"resolvePackageJsonExports",
"resolvePackageJsonImports",
"typeRoots",
"types",
"allowArbitraryExtensions",
"allowImportingTsExtensions",
"allowUmdGlobalAccess",
/* JavaScript Support */
"allowJs",
"checkJs",
"maxNodeModuleJsDepth",
/* Type Checking */
"strict",
"strictBindCallApply",
"strictFunctionTypes",
"strictNullChecks",
"strictPropertyInitialization",
"allowUnreachableCode",
"allowUnusedLabels",
"alwaysStrict",
"exactOptionalPropertyTypes",
"noFallthroughCasesInSwitch",
"noImplicitAny",
"noImplicitOverride",
"noImplicitReturns",
"noImplicitThis",
"noPropertyAccessFromIndexSignature",
"noUncheckedIndexedAccess",
"noUnusedLocals",
"noUnusedParameters",
"useUnknownInCatchVariables",
/* Emit */
"declaration",
"declarationDir",
"declarationMap",
"downlevelIteration",
"emitBOM",
"emitDeclarationOnly",
"importHelpers",
"importsNotUsedAsValues",
"inlineSourceMap",
"inlineSources",
"mapRoot",
"newLine",
"noEmit",
"noEmitHelpers",
"noEmitOnError",
"outDir",
"outFile",
"preserveConstEnums",
"preserveValueImports",
"removeComments",
"sourceMap",
"sourceRoot",
"stripInternal",
/* Interop Constraints */
"allowSyntheticDefaultImports",
"esModuleInterop",
"forceConsistentCasingInFileNames",
"isolatedDeclarations",
"isolatedModules",
"preserveSymlinks",
"verbatimModuleSyntax",
"erasableSyntaxOnly",
/* Completeness */
"skipDefaultLibCheck",
"skipLibCheck"
],
pathPattern: "^compilerOptions$"
}
]
}
},
...await jsonSchema("tsconfig", GLOB_TS_CONFIG)
];
}
// src/configs/stylistic.ts
var StylisticConfigDefaults = {
indent: 2,
jsx: true,
quotes: "single",
semi: false
};
async function stylistic(options = {}) {
const { indent, jsx: jsx2, overrides = {}, quotes, semi } = { ...StylisticConfigDefaults, ...options };
const pluginStylistic = await interopDefault(import("@stylistic/eslint-plugin"));
const config2 = pluginStylistic.configs.customize({ indent, jsx: jsx2, quotes, semi });
return [
{
name: "@bfra.me/stylistic",
plugins: { "@stylistic": pluginStylistic },
rules: {
...config2.rules,
"@stylistic/arrow-parens": ["error", "as-needed"],
"@stylistic/brace-style": ["error", "1tbs", { allowSingleLine: true }],
"@stylistic/generator-star-spacing": ["error", { after: true, before: false }],
"@stylistic/member-delimiter-style": [
"error",
{
multiline: { delimiter: "none", requireLast: false },
singleline: { delimiter: "semi", requireLast: false }
}
],
"@stylistic/object-curly-spacing": ["error", "never"],
"@stylistic/operator-linebreak": [
"error",
"after",
{ overrides: { ":": "before", "?": "before", "|": "before" } }
],
"@stylistic/quote-props": ["error", "as-needed"],
"@stylistic/quotes": [
"error",
quotes,
{ allowTemplateLiterals: "always", avoidEscape: true }
],
"@stylistic/yield-star-spacing": ["error", { after: true, before: false }],
...overrides
}
}
];
}
// src/configs/toml.ts
async function toml(options = {}) {
const { files = GLOB_TOML_FILES, overrides = {}, stylistic: stylistic2 = true } = options;
const stylisticConfig = typeof stylistic2 === "boolean" ? {} : stylistic2;
const indent = typeof stylisticConfig.indent === "number" ? stylisticConfig.indent : 2;
const includeStylistic = typeof stylistic2 === "boolean" ? stylistic2 : true;
const pluginToml = await interopDefault(import("eslint-plugin-toml"));
return [
...pluginToml.configs.standard.map((config2, index) => ({
...config2,
name: config2.plugins ? `@bfra.me/toml/plugins` : `@bfra.me/${(config2.name ?? "") || `toml/unnamed${index}`}`
})),
...await jsonSchema("toml", files),
{
name: "@bfra.me/toml",
files,
rules: {
"@stylistic/spaced-comment": "off",
"toml/comma-style": "error",
"toml/keys-order": "error",
"toml/no-space-dots": "error",
"toml/no-unreadable-number-separator": "error",
"toml/precision-of-fractional-seconds": "error",
"toml/precision-of-integer": "error",
"toml/tables-order": "error",
"toml/vue-custom-block/no-parsing-error": "error",
...includeStylistic ? {
"toml/array-bracket-newline": "error",
"toml/array-bracket-spacing": "error",
"toml/array-element-newline": "error",
"toml/indent": ["error", indent],
"toml/inline-table-curly-spacing": "error",
"toml/key-spacing": "error",
"toml/padding-line-between-pairs": "error",
"toml/padding-line-between-tables": "error",
"toml/quoted-keys": "error",
"toml/spaced-comment": "error",
"toml/table-bracket-spacing": "error"
} : {},
...overrides
}
}
];
}
// src/configs/typescript.ts
import process5 from "process";
var TypeAwareRules = {
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/dot-notation": ["error", { allowKeywords: true }],
"@typescript-eslint/naming-convention": [
"error",
{
format: ["camelCase", "PascalCase", "UPPER_CASE"],
selector: "variableLike"
},
{
format: ["PascalCase"],
selector: "typeLike"
},
{
filter: {
match: false,
regex: "(Component|Icon)$"
},
format: ["camelCase"],
leadingUnderscore: "allow",
selector: "parameter"
},
{
format: ["PascalCase"],
selector: "class"
},
{
custom: {
match: false,
regex: "^I[A-Z]"
},
format: ["PascalCase"],
selector: "interface"
}
],
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-for-in-array": "error",
"@typescript-eslint/no-implied-eval": "error",
"@typescript-eslint/no-misused-promises": "error",
"@typescript-eslint/no-unnecessary-qualifier": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-unsafe-argument": "error",
"@typescript-eslint/no-unsafe-assignment": "error",
"@typescript-eslint/no-unsafe-call": "error",
"@typescript-eslint/no-unsafe-member-access": "error",
"@typescript-eslint/no-unsafe-return": "error",
"@typescript-eslint/prefer-includes": "error",
"@typescript-eslint/prefer-readonly": "error",
"@typescript-eslint/prefer-readonly-parameter-types": "off",
"@typescript-eslint/prefer-string-starts-ends-with": "error",
"@typescript-eslint/promise-function-async": "error",
"@typescript-eslint/require-array-sort-compare": "error",
"@typescript-eslint/restrict-plus-operands": "error",
"@typescript-eslint/restrict-template-expressions": "error",
"@typescript-eslint/return-await": ["error", "in-try-catch"],
"@typescript-eslint/strict-boolean-expressions": [
"error",
{ allowNullableBoolean: true, allowNullableObject: true }
],
"@typescript-eslint/switch-exhaustiveness-check": "error",
"@typescript-eslint/unbound-method": "error",
"dot-notation": "off",
"no-implied-eval": "off"
};
async function typescript(options = {}) {
const {
erasableSyntaxOnly = false,
overrides = {},
parserOptions = {},
typeAware = { overrides: {} }
} = options;
const files = options.files ?? [GLOB_TS, GLOB_TSX];
const typeAwareFiles = typeAware.files ?? [GLOB_TS, GLOB_TSX];
const typeAwareIgnores = typeAware.ignores ?? [`${GLOB_MARKDOWN}/**`, GLOB_ASTRO_TS];
const tsconfigPath = typeof options.tsconfigPath === "string" && options.tsconfigPath.trim().length > 0 ? options.tsconfigPath : void 0;
const isTypeAware = typeof tsconfigPath === "string";
const tselint = await interopDefault(import("typescript-eslint"));
const generateTsConfig = (kind, files2, ignores2) => ({
name: `@bfra.me/typescript/${kind === "type-aware" ? "type-aware-" : ""}parser`,
files: files2,
...ignores2 ? { ignores: ignores2 } : {},
languageOptions: {
parser: tselint.parser,
parserOptions: {
sourceType: "module",
...kind === "type-aware" ? {
projectService: {
allowDefaultProject: ["./*.js"],
defaultProject: tsconfigPath
},
tsconfigRootDir: process5.cwd()
} : {},
...parserOptions
}
}
});
return [
{
name: "@bfra.me/typescript/plugins",
plugins: { "@typescript-eslint": tselint.plugin }
},
...isTypeAware ? [
generateTsConfig("default", files),
generateTsConfig("type-aware", typeAwareFiles, typeAwareIgnores)
] : [generateTsConfig("default", files)],
{
name: "@bfra.me/typescript/rules",
files,
rules: {
...tselint.configs.eslintRecommended.rules,
...tselint.configs.strict.map((config2) => config2.rules).reduce((acc, rules) => ({ ...acc, ...rules }), {}),
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/array-type": "error",
"@typescript-eslint/ban-ts-comment": [
"error",
{ "ts-expect-error": "allow-with-description" }
],
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/consistent-type-definitions": ["error", "interface"],
"@typescript-eslint/consistent-type-imports": [
"error",
{ disallowTypeAnnotations: false, fixStyle: "inline-type-imports" }
],
"@typescript-eslint/explicit-function-return-type": [
"error",
{
allowExpressions: true,
allowFunctionsWithoutTypeParameters: true,
allowHigherOrderFunctions: true,
allowIIFEs: true
}
],
"@typescript-eslint/explicit-member-accessibility": [
"error",
{
accessibility: "no-public"
}
],
"@typescript-eslint/method-signature-style": ["error", "property"],
"@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-dupe-class-members": "error",
"@typescript-eslint/no-dynamic-delete": "off",
"@typescript-eslint/no-empty-object-type": ["error", { allowInterfaces: "always" }],
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-extraneous-class": "error",
"@typescript-eslint/no-import-type-side-effects": "error",
"@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/no-invalid-this": "error",
"@typescript-eslint/no-invalid-void-type": "off",
"@typescript-eslint/no-misused-new": "error",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/no-redeclare": ["error", { builtinGlobals: false }],
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/no-unused-expressions": [
"error",
{
allowShortCircuit: true,
allowTaggedTemplates: true,
allowTernary: true
}
],
// This is reported by `unused-imports/no-unused-vars`
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-use-before-define": [
"error",
{ classes: false, functions: false, variables: true }
],
"@typescript-eslint/no-useless-constructor": "off",
"@typescript-eslint/no-wrapper-object-types": "error",
"@typescript-eslint/prefer-for-of": "warn",
"@typescript-eslint/prefer-function-type": "warn",
"@typescript-eslint/triple-slash-reference": "off",
"@typescript-eslint/unified-signatures": "off",
"no-dupe-class-members": "off",
"no-invalid-this": "off",
"no-redeclare": "off",
"no-use-before-define": "off",
"no-useless-constructor": "off",
...overrides
}
},
...isTypeAware ? [
{
name: "@bfra.me/typescript/type-aware-rules",
files: typeAwareFiles,
ignores: typeAwareIgnores,
rules: { ...TypeAwareRules, ...typeAware?.overrides }
}
] : [],
...erasableSyntaxOnly ? [
{
name: "@bfra.me/typescript/erasable-syntax-only",
plugins: {
"erasable-syntax-only": await interopDefault(
import("eslint-plugin-erasable-syntax-only")
)
},
rules: {
"erasable-syntax-only/namespaces": "error",
"erasable-syntax-only/enums": "error",
"erasable-syntax-only/import-aliases": "error",
"erasable-syntax-only/parameter-properties": "error"
}
}
] : []
];
}
// src/configs/unicorn.ts
async function unicorn(options = {}) {
const { files = [GLOB_SRC], overrides = {} } = options;
const pluginUnicorn = await interopDefault(import("eslint-plugin-unicorn"));
return [
{
name: "@bfra.me/unicorn",
files,
plugins: {
unicorn: pluginUnicorn
},
rules: {
"unicorn/catch-error-name": "error",
"unicorn/filename-case": [
"error",
{
cases: { kebabCase: true, pascalCase: true },
checkDirectories: false,
ignore: [String.raw`^[A-Z]+\..*$`, String.raw`import_map\.json`]
}
],
"unicorn/consistent-empty-array-spread": "error",
"unicorn/consistent-existence-index-check": "error",
"unicorn/consistent-function-scoping": ["error", { checkArrowFunctions: false }],
"unicorn/error-message": "error",
"unicorn/escape-case": "error",
"unicorn/new-for-builtins": "error",
"unicorn/no-array-method-this-argument": "error",
"unicorn/no-array-push-push": "error",
"unicorn/no-await-in-promise-methods": "error",
"unicorn/no-console-spaces": "error",
"unicorn/no-for-loop": "error",
"unicorn/no-hex-escape": "error",
"unicorn/no-instanceof-array": "error",
"unicorn/no-invalid-remove-event-listener": "error",
"unicorn/no-lonely-if": "error",
"unicorn/no-negated-condition": "error",
"unicorn/no-negation-in-equality-check": "error",
"unicorn/no-new-array": "error",
"unicorn/no-new-buffer": "error",
"unicorn/no-single-promise-in-promise-methods": "error",
"unicorn/no-static-only-class": "error",
"unicorn/no-typeof-undefined": "error",
"unicorn/no-unnecessary-await": "error",
"unicorn/no-zero-fractions": "error",
"unicorn/number-literal-case": "error",
"unicorn/prefer-add-event-listener": "error",
"unicorn/prefer-array-find": "error",
"unicorn/prefer-array-flat-map": "error",
"unicorn/prefer-array-index-of": "error",
"unicorn/prefer-array-some": "error",
"unicorn/prefer-at": "error",
"unicorn/prefer-blob-reading-methods": "error",
"unicorn/prefer-date-now": "error",
"unicorn/prefer-dom-node-append": "error",
"unicorn/prefer-dom-node-dataset": "error",
"unicorn/prefer-dom-node-remove": "error",
"unicorn/prefer-dom-node-text-content": "error",
"unicorn/prefer-includes": "error",
"unicorn/prefer-keyboard-event-key": "error",
"unicorn/prefer-math-min-max": "error",
"unicorn/prefer-math-trunc": "error",
"unicorn/prefer-modern-dom-apis": "error",
"unicorn/prefer-modern-math-apis": "error",
"unicorn/prefer-negative-index": "error",
"unicorn/prefer-node-protocol": "error",
"unicorn/prefer-number-properties": "error",
"unicorn/prefer-optional-catch-binding": "error",
"unicorn/prefer-prototype-methods": "error",
"unicorn/prefer-query-selector": "error",
"unicorn/prefer-reflect-apply": "error",
"unicorn/prefer-regexp-test": "error",
"unicorn/prefer-string-raw": "error",
"unicorn/prefer-string-replace-all": "error",
"unicorn/prefer-string-slice": "error",
"unicorn/prefer-string-starts-ends-with": "error",
"unicorn/prefer-string-trim-start-end": "error",
"unicorn/prefer-type-error": "error",
"unicorn/throw-new-error": "error",
...overrides
}
}
];
}
// src/configs/vitest.ts
async function vitest(options = {}) {
const { files = GLOB_TESTS, isInEditor = false, overrides = {}, tsconfigPath } = options;
const isTypeAware = typeof tsconfigPath === "string" && tsconfigPath.trim().length > 0;
return requireOf(
["@vitest/eslint-plugin"],
async () => {
const vitest2 = await interopDefault(import("@vitest/eslint-plugin"));
return [
{
name: "@bfra.me/vitest/plugins",
plugins: {
vitest: vitest2
},
...isTypeAware ? {
settings: {
vitest: {
typecheck: true
}
}
} : {}
},
{
...vitest2.configs?.env ?? {},
name: "@bfra.me/vitest",
files,
rules: {
...vitest2.configs?.recommended.rules ?? {},
"vitest/consistent-test-it": ["error", { fn: "it", withinDescribe: "it" }],
"vitest/no-focused-tests": isInEditor ? "off" : ["error", { fixable: true }],
"vitest/no-import-node-test": "error",
"vitest/no-standalone-expect": ["error", { additionalTestBlockFunctions: [] }],
"vitest/prefer-hooks-in-order": "error",
"vitest/prefer-lowercase-title": "error",
// @ts-expect-error - @vitest/eslint-plugin types are incorrect
"vitest/valid-title": ["error", { allowArguments: true }],
// Disabled rules
...{
"@typescript-eslint/explicit-function-return-type": "off",
...isTypeAware ? {
"@typescript-eslint/unbound-method": "off",
"vitest/unbound-method": "error"
} : {},
"no-unused-expressions": "off",
"node/prefer-global/process": "off"
},
...overrides
}
}
];
},
async (missingList) => fallback(missingList, {
files,
languageOptions: {
parser: anyParser
}
})
);
}
// src/configs/yaml.ts
async function yaml(options = {}) {
const { files = GLOB_YAML_FILES, overrides = {}, stylistic: stylistic2 = true } = options;
const stylisticConfig = typeof stylistic2 === "boolean" ? {} : stylistic2;
const indent = typeof stylisticConfig.indent === "number" ? stylisticConfig.indent : 2;
const quotes = typeof stylisticConfig.quotes === "string" ? stylisticConfig.quotes : "single";
const includeStylistic = typeof stylistic2 === "boolean" ? stylistic2 : true;
const pluginYaml = await interopDefault(import("eslint-plugin-yml"));
return [
...pluginYaml.configs["flat/standard"].map((config2, index) => ({
...config2,
name: config2.plugins ? `@bfra.me/yaml/plugins` : `@bfra.me/${(config2.name ?? "") || `yaml/unnamed${index}`}`
})),
...await jsonSchema("yaml", files),
{
name: "@bfra.me/yaml",
files,
rules: {
"@stylistic/spaced-comment": "off",
"yml/block-mapping": "error",
"yml/block-sequence": "error",
"yml/no-empty-key": "error",
"yml/no-empty-mapping-value": "off",
"yml/no-empty-sequence-entry": "error",
"yml/no-irregular-whitespace": "error",
"yml/plain-scalar": "error",
"yml/vue-custom-block/no-parsing-error": "error",
...includeStylistic ? {
"yml/block-mapping-question-indicator-newline": "error",
"yml/block-sequence-hyphen-indicator-newline": "error",
"yml/flow-mapping-curly-newline": "error",
"yml/flow-mapping-curly-spacing": "error",
"yml/flow-sequence-bracket-newline": "error",
"yml/flow-sequence-bracket-spacing": "error",
"yml/indent": ["error", indent],
"yml/key-spacing": "error",
"yml/no-tab-indent": "error",
"yml/quotes": [
"error",
{ avoidEscape: true, prefer: quotes === "backtick" ? "single" : quotes }
],
"yml/spaced-comment": "error"
} : {},
...overrides
}
}
];
}
// src/define-config.ts
var AllowedConfigPropertiesForOptions = [
"name",
"ignores",
"language",
"languageOptions",
"linterOptions",
"plugins",
"processor",
"rules",
"settings"
];
async function defineConfig(options = {}, ...userConfigs) {
const {
astro: enableAstro = false,
gitignore: enableGitignore = true,
ignores: userIgnores = [],
imports: enableImports = true,
jsx: enableJsx = true,
nextjs: enableNextjs = false,
packageJson: enablePackageJson = false,
perfectionist: enablePerfectionist = true,
pnpm: enableCatalogs = false,
prettier: enablePrettier = isPackageExists3("prettier"),
react: enableReact = false,
regexp: enableRegexp = true,
typescript: enableTypeScript = isPackageExists3("typescript"),
unicorn: enableUnicorn = true
} = options;
let isInEditor = options.isInEditor;
if (isInEditor == null) {
isInEditor = isInEditorEnv();
if (isInEditor) {
console.log(
"[@bfra.me/eslint-config] Editor specific config is enabled. Some rules may be disabled."
);
}
}
const stylisticOptions = options.stylistic === false ? false : typeof options.stylistic === "object" ? options.stylistic : {};
if (stylisticOptions && !("jsx" in stylisticOptions)) {
stylisticOptions.jsx = typeof enableJsx === "object" ? true : enableJsx;
}
const configs = [];
if (enableGitignore) {
configs.push(gitignore(enableGitignore === true ? { strict: false } : enableGitignore));
}
configs.push(
ignores(userIgnores),
javascript({ isInEditor, overrides: getOverrides(options, "javascript") }),
eslintComments(),
node(),
jsdoc({ stylistic: stylisticOptions }),
imports(),
command()
);
if (enablePackageJson) {
configs.push(packageJson(resolveSubOptions(options, "packageJson")));
}
if (enablePerfectionist) {
configs.push(
perfectionist({
isInEditor,
overrides: getOverrides(options, "perfectionist"),
...resolveSubOptions(options, "perfectionist")
})
);
}
if (enableImports) {
configs.push(
imports({
stylistic: stylisticOptions,
...enableImports === true ? {} : enableImports
})
);
}
if (enableUnicorn) {
const unicornOptions = resolveSubOptions(options, "unicorn");
configs.push(
unicorn({
...unicornOptions,
files: unicornOptions.files ?? (enableTypeScript ? void 0 : [GLOB_JS, GLOB_JSX]),
overrides: getOverrides(options, "unicorn")
})
);
}
if (enableJsx) {
configs.push(jsx(enableJsx === true ? {} : enableJsx));
}
const typescriptOptions = resolveSubOptions(options, "typescript");
const tsconfigPath = "tsconfigPath" in typescriptOptions ? typescriptOptions.tsconfigPath : void 0;
if (enableTypeScript) {
configs.push(
typescript({
...typescriptOptions,
overrides: getOverrides(options, "typescript")
})
);
}
if (stylisticOptions) {
configs.push(
stylistic({
...stylisticOptions,
overrides: getOverrides(options, "stylistic")
})
);
}
if (enableRegexp) {
configs.push(regexp({ overrides: getOverrides(options, "regexp") }));
}
if (options.vitest) {
configs.push(
vitest({
isInEditor,
overrides: getOverrides(options, "vitest"),
tsconfigPath
})
);
}
if (enableReact) {
configs.push(
react({
...typescriptOptions,
overrides: getOverrides(options, "react"),
tsconfigPath
})
);
}
if (enableNextjs) {
configs.push(
nextjs({
overrides: getOverrides(options, "nextjs")
})
);
}
if (enableAstro) {
configs.push(
astro({
...resolveSubOptions(options, "astro"),
overrides: getOverrides(options, "astro"),
stylistic: stylisticOptions
})
);
}
if (options.jsonc ?? true) {
configs.push(
jsonc({ overrides: getOverrides(options, "jsonc"), stylistic: stylisticOptions }),
sortPackageJson(),
sortRenovateConfig(),
sortTsconfig()
);
}
if (enableCatalogs) {
configs.push(pnpm());
}
if (options.toml ?? true) {
configs.push(
toml({
overrides: getOverrides(options, "toml"),
stylistic: stylisticOptions
})
);
}
if (options.yaml ?? true) {
configs.push(
yaml({
overrides: getOverrides(options, "yaml"),
stylistic: stylisticOptions
})
);
}
if (options.markdown ?? true) {
configs.push(
markdown({
...resolveSubOptions(options, "markdown"),
overrides: getOverrides(options, "markdown")
})
);
}
if (enablePrettier) {
configs.push(
prettier({
isInEditor,
overrides: getOverrides(options, "prettier")
})
);
}
configs.push(epilogue());
const optionsConfig = AllowedConfigPropertiesForOptions.reduce(
(config2, key) => ({
...config2,
...key in options ? { [key]: options[key] } : {}
}),
{}
);
if (Object.keys(optionsConfig).length) {
configs.push([optionsConfig]);
}
return composeConfig(...configs, ...userConfigs);
}
function resolveSubOptions(options, key) {
return typeof options[key] === "boolean" ? {} : options[key] ?? {};
}
function getOverrides(options, key) {
const sub = resolveSubOptions(options, key);
return "overrides" in sub ? sub.overrides : {};
}
// src/index.ts
var config = defineConfig();
var index_default = config;
export {
GLOB_ASTRO,
GLOB_ASTRO_TS,
GLOB_CODE_IN_MARKDOWN_FILES,
GLOB_EXCLUDE,
GLOB_EXT_IN_MARKDOWN_FILES,
GLOB_JS,
GLOB_JSON,
GLOB_JSON5,
GLOB_JSONC,
GLOB_JSON_FILES,
GLOB_JSX,
GLOB_MARKDOWN,
GLOB_MARKDOWN_CODE,
GLOB_MARKDOWN_FILES,
GLOB_MARKDOWN_IN_MARKDOWN,
GLOB_PACKAGE_JSON_FILES,
GLOB_RENOVATE_CONFIG,
GLOB_SRC,
GLOB_SRC_EXT,
GLOB_TESTS,
GLOB_TOML,
GLOB_TOML_FILES,
GLOB_TS,
GLOB_TSX,
GLOB_TS_CONFIG,
GLOB_YAML,
GLOB_YAML_FILES,
StylisticConfigDefaults,
astro,
command,
composeConfig,
config,
index_default as default,
defineConfig,
epilogue,
eslintComments,
fallback,
gitignore,
ignores,
imports,
javascript,
jsdoc,
jsonc,
jsx,
markdown,
nextjs,
node,
packageJson,
perfectionist,
pnpm,
prettier,
react,
regexp,
sortPackageJson,
sortRenovateConfig,
sortTsconfig,
stylistic,
toml,
typescript,
unicorn,
vitest,
yaml
};
//# sourceMappingURL=index.js.map