@ntnyq/eslint-config
Version: 
An opinionated ESLint config preset of ntnyq
1,849 lines (1,820 loc) • 96.5 kB
JavaScript
import { FlatConfigComposer } from "eslint-flat-config-utils";
import { mergeProcessors, mergeProcessors as mergeProcessors$1, processorPassThrough } from "eslint-merge-processors";
import { configs as configsTypeScript, parser as parserTypeScript, plugin as pluginTypeScript } from "typescript-eslint";
import * as parserVue from "vue-eslint-parser";
import * as parserToml from "toml-eslint-parser";
import * as parserYaml from "yaml-eslint-parser";
import * as parserPlain from "eslint-parser-plain";
import * as parserJsonc from "jsonc-eslint-parser";
import * as pluginRegexp from "eslint-plugin-regexp";
import pluginNode from "eslint-plugin-n";
import pluginVue from "eslint-plugin-vue";
import pluginYml from "eslint-plugin-yml";
import pluginSvgo from "eslint-plugin-svgo";
import pluginToml from "eslint-plugin-toml";
import pluginMarkdown from "@eslint/markdown";
import pluginAntfu from "eslint-plugin-antfu";
import pluginJsdoc from "eslint-plugin-jsdoc";
import pluginJsonc from "eslint-plugin-jsonc";
import pluginNtnyq from "eslint-plugin-ntnyq";
import pluginPinia from "eslint-plugin-pinia";
import pluginDepend from "eslint-plugin-depend";
import pluginUnoCSS from "@unocss/eslint-plugin";
import pluginVitest from "@vitest/eslint-plugin";
import pluginUnicorn from "eslint-plugin-unicorn";
import pluginImportX from "eslint-plugin-import-x";
import pluginPrettier from "eslint-plugin-prettier";
import pluginDeMorgan from "eslint-plugin-de-morgan";
import pluginNoOnlyTests from "eslint-plugin-no-only-tests";
import pluginGitHubAction from "eslint-plugin-github-action";
import pluginPerfectionist from "eslint-plugin-perfectionist";
import pluginComments from "@eslint-community/eslint-plugin-eslint-comments";
import processorVueBlocks from "eslint-processor-vue-blocks";
import { resolve } from "node:path";
import process from "node:process";
import { isPackageExists } from "local-pkg";
import { builtinCommands, defineCommand } from "eslint-plugin-command/commands";
import createCommandConfig from "eslint-plugin-command/config";
import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript";
import globals from "globals";
import createGitIgnoreConfig from "eslint-config-flat-gitignore";
import jsConfig from "@eslint/js";
//#region src/globs.ts
/**
* @file globs constants
*/
const GLOB_SRC_EXT = "?([cm])[jt]s?(x)";
const GLOB_SRC = `**/*.${GLOB_SRC_EXT}`;
const GLOB_JS = "**/*.?([cm])js";
const GLOB_JSX = `${GLOB_JS}x`;
const GLOB_JSX_ONLY = "**/*.jsx";
const GLOB_TS = "**/*.?([cm])ts";
const GLOB_TSX = `${GLOB_TS}x`;
const GLOB_TSX_ONLY = "**/*.tsx";
const GLOB_DTS = "**/*.d.?([cm])ts";
const GLOB_TYPES = [
	GLOB_DTS,
	`**/types/${GLOB_TS}`,
	`**/types.ts`
];
const GLOB_TYPE_TEST = [`**/*.test-d.${GLOB_SRC_EXT}`, `**/*.spec-d.${GLOB_SRC_EXT}`];
const GLOB_TEST = [
	`**/*.test.${GLOB_SRC_EXT}`,
	`**/*.spec.${GLOB_SRC_EXT}`,
	`**/*.bench.${GLOB_SRC_EXT}`,
	`**/*.benchmark.${GLOB_SRC_EXT}`,
	...GLOB_TYPE_TEST
];
const GLOB_STYLE = "**/*.{c,le,sc}ss";
const GLOB_CSS = "**/*.css";
const GLOB_LESS = "**/*.less";
const GLOB_SCSS = "**/*.scss";
const GLOB_POSTCSS = "**/*.{p,post}css";
const GLOB_JSON = "**/*.json";
const GLOB_JSON5 = "**/*.json5";
const GLOB_JSONC = "**/*.jsonc";
const GLOB_PACKAGE_JSON = "**/package.json";
const GLOB_JSON_SCHEMA = ["**/*.schema.json", "**/schemas/*.json"];
const GLOB_TSCONFIG_JSON = ["**/tsconfig.json", "**/tsconfig.*.json"];
const GLOB_YAML = "**/*.y?(a)ml";
const GLOB_PNPM_WORKSPACE_YAML = "**/pnpm-workspace.yaml";
const GLOB_SVG = "**/*.svg";
const GLOB_VUE = "**/*.vue";
const GLOB_SVELTE = "**/*.svelte?(.{js,ts})";
const GLOB_TOML = "**/*.toml";
const GLOB_HTML = "**/*.htm?(l)";
const GLOB_ASTRO = "**/*.astro";
const GLOB_ASTRO_TS = "**/*.astro/*.ts";
const GLOB_MARKDOWN = "**/*.md";
const GLOB_MARKDOWN_CODE = `${GLOB_MARKDOWN}/${GLOB_SRC}`;
const GLOB_MARKDOWN_NESTED = `${GLOB_MARKDOWN}/*.md`;
const GLOB_ALL_SRC = [
	GLOB_SRC,
	GLOB_STYLE,
	GLOB_JSON,
	GLOB_JSON5,
	GLOB_VUE,
	GLOB_YAML,
	GLOB_TOML,
	GLOB_HTML,
	GLOB_MARKDOWN
];
const GLOB_PINIA_STORE = `**/store?(s)/*.${GLOB_SRC_EXT}`;
const GLOB_GITHUB_ACTION = "**/.github/workflows/*.y?(a)ml";
const GLOB_NODE_MODULES = "**/node_modules/**";
const GLOB_DIST = "**/dist/**";
const GLOB_LOCKFILE = [
	"**/package-lock.json",
	"**/yarn.lock",
	"**/pnpm-lock.yaml",
	"**/bun.lock?(b)",
	"**/deno.lock"
];
const GLOB_EXCLUDE = [
	GLOB_NODE_MODULES,
	GLOB_DIST,
	...GLOB_LOCKFILE,
	"**/.pnpm-store/**",
	"!.github",
	"!.vitepress",
	"!.vuepress",
	"!.vscode",
	"**/CHANGELOG*.md",
	"**/*.min.*",
	"**/LICENSE*",
	"**/__snapshots__",
	"**/auto-import?(s).d.ts",
	"**/components.d.ts",
	"**/typed-router.d.ts",
	"**/uni-pages.d.ts",
	"**/coverage",
	"**/fixtures",
	"**/output",
	"**/public",
	"**/static",
	"**/?(.)temp",
	"**/?(.)cache",
	"**/.eslintcache",
	"**/.stylelintcache",
	"**/.eslint-config-inspector",
	"**/.vite-inspect",
	"**/.nuxt",
	"**/.wxt",
	"**/.output",
	"**/.tsup",
	"**/.nitro",
	"**/.vercel",
	"**/.wrangler",
	"**/.changeset",
	"**/.npmrc",
	"**/.yarnrc",
	"**/.husky",
	"**/src-tauri/gen",
	"**/src-tauri/target"
];
//#endregion
//#region src/configs/vue.ts
const sharedRules = {
	...pluginVue.configs.base.rules,
	...pluginVue.configs.essential.rules,
	...pluginVue.configs["strongly-recommended"].rules,
	...pluginVue.configs.recommended.rules
};
const disabledRules = {
	"vue/multi-word-component-names": "off",
	"vue/no-setup-props-reactivity-loss": "off",
	"vue/no-v-html": "off",
	"vue/no-v-text-v-html-on-component": "off",
	"vue/require-default-prop": "off",
	"vue/require-prop-types": "off"
};
const extensionRules = {
	"vue/array-bracket-spacing": ["error", "never"],
	"vue/arrow-spacing": ["error", {
		after: true,
		before: true
	}],
	"vue/block-spacing": ["error", "always"],
	"vue/brace-style": [
		"error",
		"stroustrup",
		{ allowSingleLine: true }
	],
	"vue/comma-dangle": ["error", "always-multiline"],
	"vue/comma-spacing": ["error", {
		after: true,
		before: false
	}],
	"vue/comma-style": ["error", "last"],
	"vue/dot-location": ["error", "property"],
	"vue/dot-notation": ["error", { allowKeywords: true }],
	"vue/eqeqeq": ["error", "smart"],
	"vue/key-spacing": ["error", {
		afterColon: true,
		beforeColon: false
	}],
	"vue/keyword-spacing": ["error", {
		after: true,
		before: true
	}],
	"vue/no-constant-condition": "error",
	"vue/no-empty-pattern": "error",
	"vue/no-extra-parens": ["error", "functions"],
	"vue/no-loss-of-precision": "error",
	"vue/no-negated-condition": "error",
	"vue/no-restricted-syntax": [
		"error",
		"DebuggerStatement",
		"LabeledStatement",
		"WithStatement"
	],
	"vue/no-sparse-arrays": "error",
	"vue/object-curly-newline": ["error", {
		consistent: true,
		multiline: true
	}],
	"vue/object-curly-spacing": ["error", "always"],
	"vue/object-property-newline": ["error", { allowMultiplePropertiesPerLine: true }],
	"vue/object-shorthand": [
		"error",
		"always",
		{
			avoidQuotes: true,
			ignoreConstructors: false
		}
	],
	"vue/operator-linebreak": ["error", "before"],
	"vue/prefer-template": "error",
	"vue/quote-props": ["error", "consistent-as-needed"],
	"vue/space-in-parens": ["error", "never"],
	"vue/space-infix-ops": "error",
	"vue/space-unary-ops": ["error", {
		nonwords: false,
		words: true
	}],
	"vue/template-curly-spacing": "error"
};
const unCategorizedRules = {
	"vue/block-order": ["error", { order: [
		"script",
		"template",
		"style"
	] }],
	"vue/block-tag-newline": ["error", {
		multiline: "always",
		singleline: "always"
	}],
	"vue/component-api-style": ["error", ["script-setup", "composition"]],
	"vue/component-name-in-template-casing": [
		"error",
		"PascalCase",
		{
			ignores: ["slot", "component"],
			registeredComponentsOnly: false
		}
	],
	"vue/component-options-name-casing": ["error", "PascalCase"],
	"vue/custom-event-name-casing": ["error", "camelCase"],
	"vue/define-emits-declaration": ["error", "type-literal"],
	"vue/define-macros-order": ["error", {
		defineExposeLast: true,
		order: [
			"defineProps",
			"defineEmits",
			"defineOptions",
			"defineSlots",
			"defineModel"
		]
	}],
	"vue/define-props-declaration": ["error", "type-based"],
	"vue/define-props-destructuring": ["error", { destructure: "never" }],
	"vue/enforce-style-attribute": ["error", { allow: ["scoped", "plain"] }],
	"vue/html-button-has-type": ["error", {
		button: true,
		reset: true,
		submit: true
	}],
	"vue/html-comment-content-newline": [
		"error",
		{
			multiline: "always",
			singleline: "ignore"
		},
		{ exceptions: ["*"] }
	],
	"vue/html-comment-content-spacing": [
		"error",
		"always",
		{ exceptions: ["-"] }
	],
	"vue/html-comment-indent": ["error", 2],
	"vue/next-tick-style": ["error", "promise"],
	"vue/no-deprecated-delete-set": "error",
	"vue/no-deprecated-model-definition": ["error", { allowVue3Compat: true }],
	"vue/no-duplicate-attr-inheritance": ["error", { checkMultiRootNodes: true }],
	"vue/no-empty-component-block": "error",
	"vue/no-irregular-whitespace": "error",
	"vue/no-multiple-objects-in-class": "error",
	"vue/no-negated-v-if-condition": "error",
	"vue/no-ref-object-reactivity-loss": "error",
	"vue/no-required-prop-with-default": ["error", { autofix: true }],
	"vue/no-restricted-v-bind": ["error", "/^v-/"],
	"vue/no-static-inline-styles": ["error", { allowBinding: true }],
	"vue/no-unused-emit-declarations": "error",
	"vue/no-use-v-else-with-v-for": "error",
	"vue/no-useless-v-bind": "error",
	"vue/no-v-text": "error",
	"vue/padding-line-between-blocks": "error",
	"vue/prefer-define-options": "error",
	"vue/prefer-prop-type-boolean-first": "error",
	"vue/prefer-separate-static-class": "error",
	"vue/prefer-true-attribute-shorthand": ["error", "always"],
	"vue/prefer-use-template-ref": "error",
	"vue/require-macro-variable-name": ["error", {
		defineEmits: "emits",
		defineProps: "props",
		defineSlots: "slots",
		useAttrs: "attrs",
		useSlots: "slots"
	}],
	"vue/require-typed-object-prop": "error",
	"vue/slot-name-casing": ["error", "kebab-case"],
	"vue/v-for-delimiter-style": ["error", "in"],
	"vue/valid-define-options": "error"
};
/**
* Config for vue files
*
* @see {@link https://github.com/vuejs/eslint-plugin-vue}
*
* @param options - {@link ConfigVueOptions}
* @returns ESLint configs
*/
const configVue = (options = {}) => {
	const { files = [GLOB_VUE], ecmaVersion = "latest", extraFileExtensions = [] } = options;
	const sfcBlocks = options.sfcBlocks === true ? {} : options.sfcBlocks ?? {};
	function getVueProcessor() {
		const processorVueSFC = pluginVue.processors[".vue"];
		if (!sfcBlocks) return processorVueSFC;
		return mergeProcessors$1([processorVueSFC, processorVueBlocks({
			...sfcBlocks,
			blocks: {
				styles: true,
				...sfcBlocks.blocks
			}
		})]);
	}
	return [{
		name: "ntnyq/vue/setup",
		plugins: {
			"@typescript-eslint": pluginTypeScript,
			vue: pluginVue
		}
	}, {
		name: "ntnyq/vue/rules",
		files,
		processor: getVueProcessor(),
		languageOptions: {
			parser: parserVue,
			parserOptions: {
				ecmaVersion,
				extraFileExtensions,
				parser: parserTypeScript,
				sourceType: "module",
				ecmaFeatures: { jsx: true }
			}
		},
		rules: {
			...sharedRules,
			"vue/attributes-order": ["error", {
				alphabetical: false,
				order: [
					"EVENTS",
					"TWO_WAY_BINDING",
					"OTHER_DIRECTIVES",
					"LIST_RENDERING",
					"CONDITIONALS",
					"CONTENT",
					"SLOT",
					"UNIQUE",
					"DEFINITION",
					"ATTR_DYNAMIC",
					"RENDER_MODIFIERS",
					"GLOBAL",
					"ATTR_STATIC",
					"ATTR_SHORTHAND_BOOL"
				]
			}],
			"vue/html-self-closing": ["error", {
				math: "always",
				svg: "always",
				html: {
					component: "always",
					normal: "always",
					void: "always"
				}
			}],
			"vue/max-attributes-per-line": ["error", {
				multiline: 1,
				singleline: 1
			}],
			"vue/order-in-components": ["error", { order: [
				"el",
				"name",
				"key",
				"parent",
				"functional",
				["provide", "inject"],
				["delimiters", "comments"],
				[
					"components",
					"directives",
					"filters"
				],
				"extends",
				"mixins",
				"layout",
				"middleware",
				"validate",
				"scrollToTop",
				"transition",
				"loading",
				"inheritAttrs",
				"model",
				["props", "propsData"],
				"slots",
				"expose",
				"emits",
				"setup",
				"asyncData",
				"computed",
				"data",
				"fetch",
				"head",
				"methods",
				["template", "render"],
				"watch",
				"watchQuery",
				"LIFECYCLE_HOOKS",
				"renderError",
				"ROUTER_GUARDS"
			] }],
			"vue/prop-name-casing": ["error", "camelCase"],
			"vue/this-in-template": ["error", "never"],
			"vue/v-bind-style": [
				"error",
				"shorthand",
				{ sameNameShorthand: "always" }
			],
			...disabledRules,
			...extensionRules,
			...unCategorizedRules,
			...options.overrides
		}
	}];
};
//#endregion
//#region src/configs/yml.ts
/**
* @see {@link https://github.com/ota-meshi/eslint-plugin-yml/blob/master/src/configs/base.ts}
*/
const disabledCoreRules$1 = {
	"no-irregular-whitespace": "off",
	"no-unused-vars": "off",
	"spaced-comment": "off"
};
/**
* Config for yml, yaml files
*
* @see {@link https://ota-meshi.github.io/eslint-plugin-yml}
*
* @param options - {@link ConfigYmlOptions}
* @returns ESLint configs
*/
const configYml = (options = {}) => {
	const { files = [GLOB_YAML] } = options;
	return [{
		name: "ntnyq/yml",
		files,
		plugins: { yml: pluginYml },
		languageOptions: { parser: parserYaml },
		rules: {
			"yml/no-empty-mapping-value": "off",
			"yml/block-mapping": "error",
			"yml/block-mapping-question-indicator-newline": "error",
			"yml/block-sequence": "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",
			"yml/key-spacing": "error",
			"yml/no-empty-document": "error",
			"yml/no-empty-key": "error",
			"yml/no-empty-sequence-entry": "error",
			"yml/no-irregular-whitespace": "error",
			"yml/no-tab-indent": "error",
			"yml/plain-scalar": "error",
			"yml/quotes": ["error", {
				avoidEscape: false,
				prefer: "single"
			}],
			"yml/spaced-comment": "error",
			"yml/vue-custom-block/no-parsing-error": "error",
			...disabledCoreRules$1,
			...options.prettier ? {
				"yml/block-mapping-colon-indicator-newline": "off",
				"yml/block-mapping-question-indicator-newline": "off",
				"yml/block-sequence-hyphen-indicator-newline": "off",
				"yml/flow-mapping-curly-newline": "off",
				"yml/flow-mapping-curly-spacing": "off",
				"yml/flow-sequence-bracket-newline": "off",
				"yml/flow-sequence-bracket-spacing": "off",
				"yml/indent": "off",
				"yml/key-spacing": "off",
				"yml/no-multiple-empty-lines": "off",
				"yml/no-trailing-zeros": "off",
				"yml/quotes": "off"
			} : {},
			...options.overrides
		}
	}];
};
//#endregion
//#region src/utils/env.ts
const hasPinia = () => isPackageExists("pinia");
const hasVitest = () => isPackageExists("vitest");
const hasTypeScript = () => isPackageExists("typescript");
const hasShadcnVue = () => (isPackageExists("radix-vue") || isPackageExists("reka-ui")) && isPackageExists("class-variance-authority") && isPackageExists("clsx");
const hasUnoCSS = () => isPackageExists("unocss") || isPackageExists("@unocss/postcss") || isPackageExists("@unocss/webpack") || isPackageExists("@unocss/nuxt");
const hasVue = () => isPackageExists("vue") || isPackageExists("nuxt") || isPackageExists("vitepress") || isPackageExists("vuepress") || isPackageExists("@slidev/cli") || isPackageExists("vue", { paths: [resolve(process.cwd(), "playground"), resolve(process.cwd(), "docs")] });
//#endregion
//#region src/utils/resolveSubOptions.ts
function resolveSubOptions(options, key) {
	return typeof options[key] === "boolean" ? {} : options[key] || {};
}
//#endregion
//#region src/utils/getOverrides.ts
function getOverrides(options, key) {
	const subOptions = resolveSubOptions(options, key);
	return "overrides" in subOptions && subOptions.overrides ? subOptions.overrides : {};
}
//#endregion
//#region src/utils/combineConfigs.ts
async function combineConfigs(...configs) {
	return (await Promise.all(configs)).flat();
}
//#endregion
//#region src/utils/isInGitHooksOrRunBySpecifyPackages.ts
const CHECKED_RUNNER_PACKAGES = [
	"nano-staged",
	"lint-staged",
	"lefthook",
	"tsx"
];
function isInGitHooksOrRunBySpecifyPackages() {
	return !!(process.env.GIT_PARAMS || process.env.VSCODE_GIT_COMMAND || CHECKED_RUNNER_PACKAGES.some((packageName) => process.env.npm_lifecycle_script?.startsWith(packageName)));
}
//#endregion
//#region src/utils/ensurePackages.ts
const isCwdInScope = isPackageExists("@ntnyq/eslint-config");
function isPackageInScope(name) {
	return isPackageExists(name, { paths: [import.meta.dirname] });
}
async function ensurePackages(packages) {
	if (process.env.CI || !process.stdout.isTTY || isInGitHooksOrRunBySpecifyPackages() || !isCwdInScope) return;
	const nonExistingPackages = packages.filter((pkg) => !!pkg && !isPackageInScope(pkg));
	if (nonExistingPackages.length === 0) return;
	const { confirm } = await import("@clack/prompts");
	if (await confirm({ message: `${nonExistingPackages.length === 1 ? "Package is" : "Packages are"} required for this config: ${nonExistingPackages.join(", ")}. Do you want to install them?` })) try {
		const { installPackage } = await import("@antfu/install-pkg");
		await installPackage(nonExistingPackages, { dev: true });
	} catch (err) {
		console.log(err);
	}
}
//#endregion
//#region src/utils/interopDefault.ts
/**
* Interop default export from a module
* @param mod - The module
* @returns The default export
*/
async function interopDefault(mod) {
	const resolved = await mod;
	return resolved.default || resolved;
}
//#endregion
//#region src/utils/mergePrettierOptions.ts
function mergePrettierOptions(options = {}, overrides = {}) {
	return {
		...options,
		...overrides,
		plugins: [...options.plugins || [], ...overrides.plugins || []]
	};
}
//#endregion
//#region src/configs/html.ts
/**
* Config for html files
*
* @see {@link https://github.com/yeonjuan/html-eslint}
*
* @param options - {@link ConfigHtmlOptions}
* @returns ESLint configs
*/
const configHtml = async (options = {}) => {
	await ensurePackages(["@html-eslint/parser", "@html-eslint/eslint-plugin"]);
	const [parserHtml, pluginHtml] = await Promise.all([interopDefault(import("@html-eslint/parser")), interopDefault(import("@html-eslint/eslint-plugin"))]);
	const { files = [GLOB_HTML] } = options;
	return [{
		name: "ntnyq/html",
		files,
		plugins: { "@html-eslint": pluginHtml },
		languageOptions: { parser: parserHtml },
		rules: {
			"@html-eslint/attrs-newline": "error",
			"@html-eslint/element-newline": ["error", { inline: [`$inline`] }],
			"@html-eslint/indent": "error",
			"@html-eslint/no-duplicate-attrs": "error",
			"@html-eslint/no-duplicate-id": "error",
			"@html-eslint/no-extra-spacing-attrs": "error",
			"@html-eslint/no-multiple-h1": "error",
			"@html-eslint/no-obsolete-tags": "error",
			"@html-eslint/quotes": "error",
			"@html-eslint/require-closing-tags": "error",
			"@html-eslint/require-doctype": "error",
			"@html-eslint/require-img-alt": "error",
			"@html-eslint/require-lang": "error",
			"@html-eslint/require-li-container": "error",
			"@html-eslint/require-title": "error",
			"@html-eslint/use-baseline": "error",
			...options.overrides
		}
	}];
};
//#endregion
//#region src/configs/node.ts
/**
* Config for common files
*
* @see {@link https://github.com/eslint-community/eslint-plugin-n}
*
* @param options - {@link ConfigNodeOptions}
* @returns ESLint configs
*/
const configNode = (options = {}) => [{
	name: "ntnyq/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/prefer-global/buffer": ["error", "never"],
		"node/prefer-global/process": ["error", "never"],
		"node/process-exit-as-throw": "error",
		...options.overrides
	}
}];
//#endregion
//#region src/configs/pnpm.ts
/**
* Config for pnpm package manager
*
* @see {@link https://github.com/antfu/pnpm-workspace-utils/tree/main/packages/eslint-plugin-pnpm}
*
* @param options - {@link ConfigPnpmOptions}
* @returns ESLint configs
*/
const configPnpm = async (options = {}) => {
	await ensurePackages(["eslint-plugin-pnpm"]);
	const pluginPnpm = await interopDefault(import("eslint-plugin-pnpm"));
	const { filesJson = [GLOB_PACKAGE_JSON], filesYaml = [GLOB_PNPM_WORKSPACE_YAML] } = options;
	return [{
		name: "ntnyq/pnpm/package-json",
		files: filesJson,
		plugins: { pnpm: pluginPnpm },
		languageOptions: { parser: parserJsonc },
		rules: {
			"pnpm/json-enforce-catalog": ["error", { autofix: true }],
			"pnpm/json-valid-catalog": "error",
			...options.overridesJsonRules
		}
	}, {
		name: "ntnyq/pnpm/pnpm-workspace-yaml",
		files: filesYaml,
		plugins: { pnpm: pluginPnpm },
		languageOptions: { parser: parserYaml },
		rules: {
			"pnpm/yaml-no-duplicate-catalog-item": "error",
			"pnpm/yaml-no-unused-catalog-item": "error",
			...options.overridesYamlRules
		}
	}];
};
//#endregion
//#region src/configs/sort.ts
/**
* Config for sort keys and values
*
* @param options - {@link ConfigSortOptions}
* @returns ESLint configs
*/
const configSort = (options = {}) => {
	const configs = [];
	const { additionalJsonFiles = [], additionalYamlFiles = [], i18nLocale: enableSortI18nLocale = true, jsonSchema: enableSortJsonSchema = true, packageJson: enableSortPackageJson = true, pnpmWorkspace: enableSortPnpmWorkspace = true, tsconfig: enableSortTsconfig = true } = options;
	if (enableSortTsconfig) configs.push({
		name: "ntnyq/sort/tsconfig",
		files: [...GLOB_TSCONFIG_JSON],
		rules: { "jsonc/sort-keys": [
			"error",
			{
				pathPattern: "^$",
				order: [
					"extends",
					"compilerOptions",
					"references",
					"files",
					"include",
					"exclude",
					"vueCompilerOptions",
					{ order: { type: "asc" } }
				]
			},
			{
				pathPattern: "^compilerOptions$",
				order: [
					"incremental",
					"composite",
					"tsBuildInfoFile",
					"disableSourceOfProjectReferenceRedirect",
					"disableSolutionSearching",
					"disableReferencedProjectLoad",
					"target",
					"lib",
					"jsx",
					"experimentalDecorators",
					"emitDecoratorMetadata",
					"jsxFactory",
					"jsxFragmentFactory",
					"jsxImportSource",
					"reactNamespace",
					"noLib",
					"useDefineForClassFields",
					"moduleDetection",
					"module",
					"rootDir",
					"moduleResolution",
					"baseUrl",
					"paths",
					"rootDirs",
					"typeRoots",
					"types",
					"allowUmdGlobalAccess",
					"moduleSuffixes",
					"allowImportingTsExtensions",
					"resolvePackageJsonExports",
					"resolvePackageJsonImports",
					"customConditions",
					"resolveJsonModule",
					"allowArbitraryExtensions",
					"noResolve",
					"erasableSyntaxOnly",
					"libReplacement",
					"allowJs",
					"checkJs",
					"maxNodeModuleJsDepth",
					"declaration",
					"declarationMap",
					"emitDeclarationOnly",
					"sourceMap",
					"inlineSourceMap",
					"outFile",
					"outDir",
					"removeComments",
					"noEmit",
					"importHelpers",
					"importsNotUsedAsValues",
					"downlevelIteration",
					"sourceRoot",
					"mapRoot",
					"inlineSources",
					"emitBOM",
					"newLine",
					"stripInternal",
					"noEmitHelpers",
					"noEmitOnError",
					"preserveConstEnums",
					"declarationDir",
					"preserveValueImports",
					"isolatedDeclarations",
					"isolatedModules",
					"verbatimModuleSyntax",
					"allowSyntheticDefaultImports",
					"esModuleInterop",
					"preserveSymlinks",
					"forceConsistentCasingInFileNames",
					"strict",
					"strictBindCallApply",
					"strictFunctionTypes",
					"strictNullChecks",
					"strictPropertyInitialization",
					"allowUnreachableCode",
					"allowUnusedLabels",
					"alwaysStrict",
					"exactOptionalPropertyTypes",
					"noFallthroughCasesInSwitch",
					"noImplicitAny",
					"noImplicitOverride",
					"noImplicitReturns",
					"noImplicitThis",
					"noPropertyAccessFromIndexSignature",
					"noUncheckedIndexedAccess",
					"noUnusedLocals",
					"noUnusedParameters",
					"useUnknownInCatchVariables",
					"skipDefaultLibCheck",
					"skipLibCheck",
					{ order: { type: "asc" } }
				]
			},
			{
				order: { type: "asc" },
				pathPattern: "^vueCompilerOptions$"
			}
		] }
	});
	if (enableSortPackageJson) configs.push({
		name: "ntnyq/sort/package-json",
		files: [GLOB_PACKAGE_JSON],
		rules: {
			"jsonc/sort-array-values": ["error", {
				order: { type: "asc" },
				pathPattern: "^(?:files|keywords|activationEvents|contributes.*)$"
			}],
			"jsonc/sort-keys": [
				"error",
				{
					pathPattern: "^$",
					order: [
						"publisher",
						"name",
						"displayName",
						"preview",
						"type",
						"config",
						"version",
						"private",
						"packageManager",
						"workspaces",
						"description",
						"keywords",
						"license",
						"licenses",
						"author",
						"contributors",
						"maintainers",
						"homepage",
						"repository",
						"bugs",
						"funding",
						"exports",
						"imports",
						"main",
						"module",
						"unpkg",
						"jsdelivr",
						"types",
						"typesVersions",
						"bin",
						"icon",
						"files",
						"directories",
						"publishConfig",
						"sideEffects",
						"scripts",
						"peerDependencies",
						"peerDependenciesMeta",
						"bundledDependencies",
						"bundleDependencies",
						"dependencies",
						"optionalDependencies",
						"devDependencies",
						"activationEvents",
						"contributes",
						"categories",
						"galleryBanner",
						"badges",
						"markdown",
						"qna",
						"sponsor",
						"extensionPack",
						"extensionDependencies",
						"extensionKind",
						"pricing",
						"capabilities",
						"engines",
						"pnpm",
						"overrides",
						"resolutions",
						"husky",
						"prettier",
						"nano-staged",
						"lint-staged",
						"eslintConfig",
						{ order: { type: "asc" } }
					]
				},
				{
					order: { type: "asc" },
					pathPattern: "^(?:dev|peer|optional|bundled)?[Dd]ependencies(Meta)?$"
				},
				{
					order: { type: "asc" },
					pathPattern: "^(?:resolutions|overrides|pnpm.overrides)$"
				},
				{
					pathPattern: "^exports.*$",
					order: [
						"./package.json",
						"types",
						"import",
						"require",
						"default",
						{ order: { type: "asc" } }
					]
				},
				{
					order: { type: "asc" },
					pathPattern: "^contributes.*$"
				},
				(
				/**
				* pnpm publish config
				* @see {@link https://pnpm.io/package_json#publishconfig}
				*/
				{
					pathPattern: "^publishConfig.*$",
					order: [
						"./package.json",
						"types",
						"import",
						"require",
						"default",
						{ order: { type: "asc" } }
					]
				}),
				{
					order: { type: "asc" },
					pathPattern: "^scripts$"
				},
				{
					pathPattern: "^(?:gitHooks|husky|simple-git-hooks)$",
					order: [
						"pre-commit",
						"prepare-commit-msg",
						"commit-msg",
						"post-commit",
						"pre-rebase",
						"post-rewrite",
						"post-checkout",
						"post-merge",
						"pre-push",
						"pre-auto-gc",
						{ order: { type: "asc" } }
					]
				}
			]
		}
	});
	if (enableSortI18nLocale) configs.push({
		name: "ntnyq/sort/i18n-locale/json",
		files: ["**/{i18n,langs,locales}/*.json"],
		rules: { "jsonc/sort-keys": ["error", {
			order: { type: "asc" },
			pathPattern: ".*"
		}] }
	}, {
		name: "ntnyq/sort/i18n-locale/yaml",
		files: ["**/{i18n,langs,locales}/*.y?(a)ml"],
		rules: { "yml/sort-keys": ["error", {
			order: { type: "asc" },
			pathPattern: ".*"
		}] }
	});
	/**
	* @see {@link https://json-schema.org/draft-07/schema}
	*/
	if (enableSortJsonSchema) configs.push({
		name: "ntnyq/sort/json-schema",
		files: [...GLOB_JSON_SCHEMA],
		ignores: [GLOB_PACKAGE_JSON],
		rules: {
			"jsonc/sort-array-values": ["error", {
				order: { type: "asc" },
				pathPattern: "^(?:required)$"
			}],
			"jsonc/sort-keys": [
				"error",
				{
					pathPattern: "^$",
					order: [
						"$schema",
						"$comment",
						"$id",
						"$ref",
						"title",
						"description",
						"version",
						"type",
						"definitions",
						"properties",
						"required",
						"additionalProperties",
						{ order: { type: "asc" } }
					]
				},
				{
					order: { type: "asc" },
					pathPattern: "^(?:definitions|properties)$"
				}
			]
		}
	});
	if (enableSortPnpmWorkspace) configs.push({
		name: "ntnyq/sort/pnpm-workspace",
		files: [GLOB_PNPM_WORKSPACE_YAML],
		rules: {
			"yml/sort-keys": [
				"error",
				{
					pathPattern: "^$",
					order: [
						"packages",
						"catalog",
						"catalogs",
						"allowedDeprecatedVersions",
						"overrides",
						"onlyBuiltDependencies",
						"patchedDependencies",
						"peerDependencyRules",
						"allowNonAppliedPatches",
						"auditConfig",
						"configDependencies",
						"executionEnv",
						"ignoredBuiltDependencies",
						"ignoredOptionalDependencies",
						"neverBuiltDependencies",
						"onlyBuiltDependenciesFile",
						"packageExtensions",
						"requiredScripts",
						"supportedArchitectures",
						"updateConfig",
						{ order: { type: "asc" } }
					]
				},
				{
					order: { type: "asc" },
					pathPattern: "^(?:catalog|overrides|patchedDependencies|peerDependencyRules)$"
				},
				{
					allowLineSeparatedGroups: true,
					order: { type: "asc" },
					pathPattern: "^catalogs$"
				}
			],
			"yml/sort-sequence-values": ["error", {
				order: [".", { order: { type: "asc" } }],
				pathPattern: "^(?:packages|onlyBuiltDependencies|peerDependencyRules.ignoreMissing)$"
			}]
		}
	});
	if (additionalJsonFiles.length) configs.push({
		name: "ntnyq/sort/additional-json",
		files: additionalJsonFiles,
		rules: { "jsonc/sort-keys": ["error", {
			pathPattern: ".*",
			order: ["$schema", { order: { type: "asc" } }]
		}] }
	});
	if (additionalYamlFiles.length) configs.push({
		name: "ntnyq/sort/additional-yaml",
		files: additionalYamlFiles,
		rules: { "yml/sort-keys": ["error", {
			order: { type: "asc" },
			pathPattern: ".*"
		}] }
	});
	return configs;
};
//#endregion
//#region src/configs/svgo.ts
/**
* Config for svg files
*
* @see {@link https://github.com/ntnyq/eslint-plugin-svgo}
*
* @param options - {@link ConfigSVGOOptions}
* @returns ESLint configs
*/
const configSVGO = (options = {}) => {
	const { files = [GLOB_SVG], ignores = [] } = options;
	return [{
		name: "ntnyq/svgo",
		files,
		ignores,
		plugins: { svgo: pluginSvgo },
		languageOptions: { parser: parserPlain },
		rules: {
			"svgo/svgo": ["error", {
				plugins: ["preset-default"],
				js2svg: {
					indent: 2,
					pretty: true
				}
			}],
			...options.overrides
		}
	}];
};
//#endregion
//#region src/configs/test.ts
/**
* Config for test files
*
* @see {@link https://github.com/vitest-dev/eslint-plugin-vitest}
*
* @param options - {@link ConfigTestOptions}
* @returns ESLint configs
*/
const configTest = (options = {}) => {
	const { files = [...GLOB_TEST], vitest: enableVitest = hasVitest() } = options;
	const configs = [{
		name: "ntnyq/test/setup",
		plugins: { "no-only-tests": pluginNoOnlyTests }
	}, {
		name: "ntnyq/test/base",
		files,
		rules: {
			"max-lines-per-function": "off",
			"no-unused-expressions": "off",
			"no-only-tests/no-only-tests": "error",
			...options.overrides
		}
	}];
	if (enableVitest) configs.push({
		name: "ntnyq/test/vitest",
		files,
		plugins: { vitest: pluginVitest },
		settings: {},
		rules: {
			...pluginVitest.configs.recommended.rules,
			"vitest/expect-expect": ["error", { assertFunctionNames: [
				"expect",
				"assert",
				"expectTypeOf",
				"assertType"
			] }],
			...options.overridesVitestRules
		}
	});
	return configs;
};
//#endregion
//#region src/configs/toml.ts
/**
* Config for toml files
*
* @see {@link https://ota-meshi.github.io/eslint-plugin-toml}
*
* @param options - {@link ConfigTomlOptions}
* @returns ESLint configs
*/
const configToml = (options = {}) => {
	const { files = [GLOB_TOML] } = options;
	return [{
		name: "ntnyq/toml",
		files,
		plugins: { toml: pluginToml },
		languageOptions: { parser: parserToml },
		rules: {
			"toml/array-bracket-newline": "error",
			"toml/array-bracket-spacing": ["error", "never"],
			"toml/array-element-newline": ["error", "never"],
			"toml/comma-style": "error",
			"toml/indent": ["error", 2],
			"toml/inline-table-curly-spacing": "error",
			"toml/key-spacing": "error",
			"toml/keys-order": "error",
			"toml/no-space-dots": "error",
			"toml/no-unreadable-number-separator": "error",
			"toml/padding-line-between-pairs": "error",
			"toml/padding-line-between-tables": "error",
			"toml/precision-of-fractional-seconds": "error",
			"toml/precision-of-integer": "error",
			"toml/quoted-keys": "error",
			"toml/spaced-comment": "error",
			"toml/table-bracket-spacing": "error",
			"toml/tables-order": "error",
			"toml/vue-custom-block/no-parsing-error": "error",
			...options.overrides
		}
	}];
};
//#endregion
//#region src/configs/antfu.ts
/**
* Config for common files
*
* @see {@link https://github.com/antfu/eslint-plugin-antfu}
*
* @param options - {@link ConfigAntfuOptions}
* @returns ESLint configs
*/
const configAntfu = (options = {}) => [{
	name: "ntnyq/antfu",
	plugins: { antfu: pluginAntfu },
	rules: {
		"antfu/import-dedupe": "error",
		"antfu/indent-unindent": "error",
		"antfu/no-import-dist": "error",
		"antfu/no-import-node-modules-by-path": "error",
		...options.overrides
	}
}];
//#endregion
//#region src/configs/astro.ts
/**
* Config for astro files
*
* @see {@link https://github.com/ota-meshi/eslint-plugin-astro}
*
* @param options - {@link ConfigAstroOptions}
* @returns ESLint configs
*/
const configAstro = async (options = {}) => {
	await ensurePackages(["astro-eslint-parser", "eslint-plugin-astro"]);
	const [parserAstro, pluginAstro] = await Promise.all([interopDefault(import("astro-eslint-parser")), interopDefault(import("eslint-plugin-astro"))]);
	const { files = [GLOB_ASTRO], extraFileExtensions = [] } = options;
	return [{
		name: "ntnyq/astro",
		files,
		plugins: { astro: pluginAstro },
		processor: pluginAstro.processors["client-side-ts"],
		languageOptions: {
			parser: parserAstro,
			sourceType: "module",
			globals: { ...pluginAstro.environments.astro.globals },
			parserOptions: {
				extraFileExtensions,
				parser: parserTypeScript
			}
		},
		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-unused-define-vars-in-style": "error",
			"astro/valid-compile": "error",
			...options.overrides
		}
	}];
};
//#endregion
//#region src/configs/jsdoc.ts
const SPECIAL_CHAR = {
	emptyString: "",
	singleSpace: " "
};
/**
* JavaScript specific rules
*/
const javscriptRules = {
	"jsdoc/no-types": "off",
	"jsdoc/no-undefined-types": "error",
	"jsdoc/require-param-type": "error",
	"jsdoc/require-property-type": "error",
	"jsdoc/require-returns-type": "error"
};
/**
* TypeScript specific rules
*/
const typescriptRules = {
	"jsdoc/no-undefined-types": "off",
	"jsdoc/require-param-type": "off",
	"jsdoc/require-property-type": "off",
	"jsdoc/require-returns-type": "off",
	"jsdoc/no-types": "error"
};
/**
* Config for jsdoc
*
* @see {@link https://github.com/gajus/eslint-plugin-jsdoc}
*
* @param options - {@link ConfigJsdocOptions}
* @returns ESLint configs
*/
const configJsdoc = (options = {}) => [{
	name: "ntnyq/jsdoc",
	plugins: { jsdoc: pluginJsdoc },
	rules: {
		"jsdoc/prefer-import-tag": "off",
		"jsdoc/require-tags": "off",
		"jsdoc/tag-lines": "off",
		"jsdoc/text-escaping": "off",
		"jsdoc/check-access": "warn",
		"jsdoc/implements-on-classes": "warn",
		"jsdoc/require-param-name": "warn",
		"jsdoc/require-property": "warn",
		"jsdoc/require-property-description": "warn",
		"jsdoc/require-property-name": "warn",
		"jsdoc/require-returns-check": "warn",
		"jsdoc/require-returns-description": "warn",
		"jsdoc/require-template-description": "warn",
		"jsdoc/require-yields-check": "warn",
		"jsdoc/require-next-description": "warn",
		"jsdoc/require-next-type": "warn",
		"jsdoc/require-throws-description": "warn",
		"jsdoc/require-throws-type": "warn",
		"jsdoc/require-yields-description": "warn",
		"jsdoc/require-yields-type": "warn",
		"jsdoc/ts-method-signature-style": "warn",
		"jsdoc/ts-no-empty-object-type": "warn",
		"jsdoc/ts-no-unnecessary-template-expression": "warn",
		"jsdoc/check-alignment": "error",
		"jsdoc/check-line-alignment": "error",
		"jsdoc/check-param-names": "error",
		"jsdoc/check-property-names": "error",
		"jsdoc/check-tag-names": ["error", { definedTags: [
			"vite-ignore",
			"unocss-include",
			"pg",
			"perfectionist-group",
			"regex101",
			"compatibility",
			"category",
			"experimental",
			"internal"
		] }],
		"jsdoc/check-types": "error",
		"jsdoc/empty-tags": "error",
		"jsdoc/multiline-blocks": "error",
		"jsdoc/no-bad-blocks": ["error", { ignore: [
			"ts-check",
			"ts-expect-error",
			"ts-ignore",
			"ts-nocheck",
			"vite-ignore"
		] }],
		"jsdoc/no-blank-block-descriptions": "error",
		"jsdoc/no-blank-blocks": "error",
		"jsdoc/no-defaults": "error",
		"jsdoc/no-multi-asterisks": "error",
		"jsdoc/reject-any-type": "error",
		"jsdoc/reject-function-type": "error",
		"jsdoc/require-asterisk-prefix": "error",
		"jsdoc/require-hyphen-before-param-description": "error",
		"jsdoc/type-formatting": ["error", {
			arrayBrackets: "square",
			enableFixer: true,
			genericDot: false,
			keyValuePostColonSpacing: SPECIAL_CHAR.singleSpace,
			keyValuePostKeySpacing: SPECIAL_CHAR.emptyString,
			keyValuePostOptionalSpacing: SPECIAL_CHAR.emptyString,
			keyValuePostVariadicSpacing: SPECIAL_CHAR.emptyString,
			objectFieldIndent: SPECIAL_CHAR.emptyString,
			objectFieldQuote: null,
			objectFieldSeparator: "comma",
			stringQuotes: "single",
			typeBracketSpacing: SPECIAL_CHAR.emptyString,
			unionSpacing: SPECIAL_CHAR.singleSpace
		}],
		...options.typescript ? typescriptRules : javscriptRules,
		...options.overrides
	}
}];
//#endregion
//#region src/configs/jsonc.ts
/**
* @see {@link https://github.com/ota-meshi/eslint-plugin-jsonc/blob/master/lib/configs/base.ts}
*/
const disabledCoreRules = {
	"no-unused-expressions": "off",
	"no-unused-vars": "off",
	strict: "off"
};
/**
* Config for json, jsonc and json5 files
*
* @see {@link https://ota-meshi.github.io/eslint-plugin-jsonc}
*
* @param options - {@link ConfigJsoncOptions}
* @returns ESLint configs
*/
const configJsonc = (options = {}) => {
	const { files = [
		GLOB_JSON,
		GLOB_JSON5,
		GLOB_JSONC
	] } = options;
	return [{
		name: "ntnyq/jsonc",
		files,
		plugins: { jsonc: pluginJsonc },
		languageOptions: { parser: parserJsonc },
		rules: {
			"jsonc/array-bracket-spacing": ["error", "never"],
			"jsonc/comma-dangle": ["error", "never"],
			"jsonc/comma-style": ["error", "last"],
			"jsonc/indent": ["error", 2],
			"jsonc/key-spacing": ["error", {
				afterColon: true,
				beforeColon: false
			}],
			"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/object-curly-newline": ["error", {
				consistent: true,
				multiline: true
			}],
			"jsonc/object-curly-spacing": ["error", "always"],
			"jsonc/object-property-newline": ["error", { allowMultiplePropertiesPerLine: true }],
			"jsonc/quote-props": "error",
			"jsonc/quotes": "error",
			"jsonc/space-unary-ops": "error",
			"jsonc/valid-json-number": "error",
			"jsonc/vue-custom-block/no-parsing-error": "error",
			...disabledCoreRules,
			...options.prettier ? {
				"jsonc/array-bracket-newline": "off",
				"jsonc/array-bracket-spacing": "off",
				"jsonc/array-element-newline": "off",
				"jsonc/comma-dangle": "off",
				"jsonc/comma-style": "off",
				"jsonc/indent": "off",
				"jsonc/key-spacing": "off",
				"jsonc/no-floating-decimal": "off",
				"jsonc/object-curly-newline": "off",
				"jsonc/object-curly-spacing": "off",
				"jsonc/object-property-newline": "off",
				"jsonc/quote-props": "off",
				"jsonc/quotes": "off",
				"jsonc/space-unary-ops": "off"
			} : {},
			...options.overrides
		}
	}];
};
//#endregion
//#region src/configs/ntnyq.ts
/**
* Config for common files
*
* @see {@link https://github.com/ntnyq/eslint-plugin-ntnyq}
*
* @param options - {@link ConfigNtnyqOptions}
* @returns ESLint configs
*/
const configNtnyq = (options = {}) => [{
	name: "ntnyq/ntnyq",
	plugins: { ntnyq: pluginNtnyq },
	rules: {
		"ntnyq/no-duplicate-exports": "error",
		"ntnyq/prefer-newline-after-file-header": "error",
		...options.overrides
	}
}];
//#endregion
//#region src/configs/pinia.ts
/**
* Config for pinia store
*
* @see {@link https://github.com/lisilinhart/eslint-plugin-pinia}
*
* @param options - {@link ConfigPiniaOptions}
* @returns ESLint configs
*/
const configPinia = (options = {}) => {
	const { files = [GLOB_PINIA_STORE] } = options;
	return [{
		name: "ntnyq/pinia",
		files,
		plugins: { pinia: pluginPinia },
		rules: {
			"pinia/never-export-initialized-store": "error",
			"pinia/no-duplicate-store-ids": "error",
			"pinia/no-return-global-properties": "error",
			"pinia/no-store-to-refs-in-store": "error",
			"pinia/prefer-single-store-per-file": "error",
			"pinia/prefer-use-store-naming-convention": ["error", {
				checkStoreNameMismatch: true,
				storeSuffix: "Store"
			}],
			"pinia/require-setup-store-properties-export": "error",
			...options.overrides
		}
	}];
};
//#endregion
//#region src/configs/depend.ts
/**
* Config for optimisations dependency
*
* @see {@link https://github.com/es-tooling/eslint-plugin-depend}
* @see {@link https://github.com/es-tooling/module-replacements}
*
* @param options - {@link ConfigDependOptions}
* @returns ESLint configs
*/
const configDepend = (options = {}) => {
	const { files = [GLOB_SRC], allowed = [], modules = [], packageJson: enableCheckPackageJson = true, presets = [
		"native",
		"microutilities",
		"preferred"
	] } = options;
	const rules = {
		"depend/ban-dependencies": ["error", {
			allowed,
			modules,
			presets
		}],
		...options.overrides
	};
	const configs = [{
		name: "ntnyq/depend",
		files,
		plugins: { depend: pluginDepend },
		rules
	}];
	if (enableCheckPackageJson) configs.push({
		name: "ntnyq/depend/package-json",
		files: [GLOB_PACKAGE_JSON],
		plugins: { depend: pluginDepend },
		languageOptions: { parser: parserJsonc },
		rules
	});
	return configs;
};
//#endregion
//#region src/constants/prettier.ts
/**
* Options from `@ntnyq/prettier-config`
*
* @see {@link https://github.com/ntnyq/configs/blob/main/packages/prettier-config/index.js}
*/
const PRETTIER_DEFAULT_OPTIONS = {
	arrowParens: "avoid",
	bracketSameLine: false,
	bracketSpacing: true,
	embeddedLanguageFormatting: "auto",
	endOfLine: "lf",
	experimentalOperatorPosition: "start",
	experimentalTernaries: false,
	htmlWhitespaceSensitivity: "css",
	insertPragma: false,
	jsxSingleQuote: true,
	objectWrap: "preserve",
	printWidth: 80,
	proseWrap: "preserve",
	quoteProps: "as-needed",
	rangeEnd: Number.POSITIVE_INFINITY,
	rangeStart: 0,
	requirePragma: false,
	semi: false,
	singleAttributePerLine: true,
	singleQuote: true,
	tabWidth: 2,
	trailingComma: "all",
	useTabs: false,
	vueIndentScriptAndStyle: false
};
//#endregion
//#region src/constants/perfectionist.ts
/**
* Shared perfectionist plugin settings
*/
const pluginSettings = {
	fallbackSort: {
		order: "asc",
		type: "alphabetical"
	},
	ignoreCase: true,
	order: "asc",
	partitionByNewLine: false,
	specialCharacters: "keep",
	type: "alphabetical"
};
/**
* Shared perfectionist rule options for some rules
*/
const partialRuleOptions = {
	newlinesBetween: "ignore",
	partitionByComment: ["@pg", "@perfectionist-group"]
};
/**
* Shared option `groups` for rule `sort-objects`
*
* @see {@link https://perfectionist.dev/rules/sort-objects}
*/
const sortObjectsGroups = [
	"property",
	"multiline-property",
	"method",
	"multiline-method",
	"unknown"
];
/**
* Shared option `groups` for rules
* - `sort-interfaces`
* - `sort-object-types`
*
* @see {@link https://perfectionist.dev/rules/sort-interfaces}
* @see {@link https://perfectionist.dev/rules/sort-object-types}
*/
const sortInterfacesOrObjectTypesGroups = [
	"required-property",
	"optional-property",
	"required-method",
	"optional-method",
	"required-multiline-property",
	"optional-multiline-property",
	"required-multiline-method",
	"optional-multiline-method",
	"unknown",
	"index-signature",
	"multiline-index-signature"
];
/**
* Shared option `groups` for rules:
* - `sort-intersection-types`
* - `sort-union-types`
*
* Philosophy: keep simple thing first except null & undefined
*
* @see {@link https://perfectionist.dev/rules/sort-intersection-types}
* @see {@link https://perfectionist.dev/rules/sort-union-types}
*/
const sortIntersectionTypesOrUnionTypesGroups = [
	"literal",
	"keyword",
	"named",
	"intersection",
	"conditional",
	"function",
	"import",
	"object",
	"operator",
	"tuple",
	"union",
	"nullish"
];
/**
* Shared option `groups` for rule `sort-imports`
*
* @see {@link https://perfectionist.dev/rules/sort-imports}
*/
const sortImportsTypes = [
	"side-effect-style",
	"value-style",
	"value-builtin",
	"value-external",
	"value-subpath",
	"value-internal",
	"value-parent",
	"value-sibling",
	"value-index",
	"ts-equals-import",
	"side-effect",
	"type-builtin",
	"type-external",
	"type-subpath",
	"type-internal",
	"type-parent",
	"type-sibling",
	"type-index",
	"unknown"
];
/**
* Shared option `groups` for rule `sort-exports`
*
* @see {@link https://perfectionist.dev/rules/sort-exports}
*/
const sortExportsGroups = [
	"value-export",
	"type-export",
	"unknown"
];
/**
* Shared option `groups` for rule `sort-named-exports`
*
* @see {@link https://perfectionist.dev/rules/sort-named-exports}
*/
const sortNamedExportsGroups = [
	"value-export",
	"type-export",
	"unknown"
];
/**
* Shared option `groups` for rule `sort-named-imports`
*
* @see {@link https://perfectionist.dev/rules/sort-named-imports}
*/
const sortNamedImportsGroups = [
	"value-import",
	"type-import",
	"unknown"
];
/**
* Shared option `groups` for rule `sort-classes`
*
* // TODO: implement this
*
* @see {@link https://perfectionist.dev/rules/sort-classes}
*/
const sortClassesGroups = ["unknown"];
/**
* Shared constants about eslint-plugin-perfectionist
*/
const PERFECTIONIST = Object.freeze({
	partialRuleOptions,
	pluginSettings,
	sortClassesGroups,
	sortExportsGroups,
	sortImportsTypes,
	sortInterfacesOrObjectTypesGroups,
	sortIntersectionTypesOrUnionTypesGroups,
	sortNamedExportsGroups,
	sortNamedImportsGroups,
	sortObjectsGroups
});
//#endregion
//#region src/configs/format.ts
/**
* Config to use a formatter
*
* @see {@link https://github.com/antfu/eslint-plugin-format}
*
* @param options - {@link ConfigFormatOptions}
* @returns ESLint configs
*/
const configFormat = async (options = {}) => {
	await ensurePackages(["eslint-plugin-format"]);
	const pluginFormat = await interopDefault(import("eslint-plugin-format"));
	const { css: enableCSS = true, html: enableHTML = true, prettierOptions = {} } = options;
	const sharedPrettierOptions = {
		...PRETTIER_DEFAULT_OPTIONS,
		...prettierOptions
	};
	const configs = [{
		name: "ntnyq/format/setup",
		plugins: { format: pluginFormat }
	}];
	if (enableCSS) configs.push({
		name: "ntnyq/format/css",
		files: [GLOB_CSS, GLOB_POSTCSS],
		languageOptions: { parser: parserPlain },
		rules: { "format/prettier": ["error", mergePrettierOptions(sharedPrettierOptions, { parser: "css" })] }
	}, {
		name: "ntnyq/format/scss",
		files: [GLOB_SCSS],
		languageOptions: { parser: parserPlain },
		rules: { "format/prettier": ["error", mergePrettierOptions(sharedPrettierOptions, { parser: "scss" })] }
	}, {
		name: "ntnyq/format/less",
		files: [GLOB_LESS],
		languageOptions: { parser: parserPlain },
		rules: { "format/prettier": ["error", mergePrettierOptions(sharedPrettierOptions, { parser: "less" })] }
	});
	if (enableHTML) configs.push({
		name: "ntnyq/format/html",
		files: [GLOB_HTML],
		languageOptions: { parser: parserPlain },
		rules: { "format/prettier": ["error", mergePrettierOptions(sharedPrettierOptions, { parser: "html" })] }
	});
	return configs;
};
//#endregion
//#region src/configs/regexp.ts
/**
* Config for regexp
*
* @see {@link https://github.com/ota-meshi/eslint-plugin-regexp}
*
* @param options - {@link ConfigRegexpOptions}
* @returns ESLint configs
*/
const configRegexp = (options = {}) => {
	const recommendedConfig = pluginRegexp.configs["flat/recommended"];
	const recommendedRules$1 = { ...recommendedConfig.rules };
	if (options.severity === "warn") {
		for (const key in recommendedRules$1) if (recommendedRules$1[key] === "error") recommendedRules$1[key] = "warn";
	}
	return [{
		...recommendedC