@sankeyangshu/eslint-config
Version:
ESLint config for @sankeyangshu.
1,783 lines (1,757 loc) • 90.7 kB
JavaScript
import { FlatConfigComposer } from "eslint-flat-config-utils";
import { mergeProcessors, processorPassThrough } from "eslint-merge-processors";
import { configs as configsTypeScript, parser as parserTypeScript, plugin as pluginTypeScript } from "typescript-eslint";
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 pluginYml from "eslint-plugin-yml";
import pluginToml from "eslint-plugin-toml";
import pluginMarkdown from "@eslint/markdown";
import pluginJsdoc from "eslint-plugin-jsdoc";
import pluginJsonc from "eslint-plugin-jsonc";
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 pluginNoOnlyTests from "eslint-plugin-no-only-tests";
import pluginPerfectionist from "eslint-plugin-perfectionist";
import pluginUnusedImports from "eslint-plugin-unused-imports";
import pluginComments from "@eslint-community/eslint-plugin-eslint-comments";
import { resolve } from "node:path";
import process from "node:process";
import { isPackageExists } from "local-pkg";
import { builtinCommands } from "eslint-plugin-command/commands";
import createCommand 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/constants/globs.ts
const GLOB_SRC_EXT = "?([cm])[jt]s?(x)";
const GLOB_SRC = "**/*.?([cm])[jt]s?(x)";
const GLOB_JS = "**/*.?([cm])js";
const GLOB_JSX = "**/*.?([cm])jsx";
const GLOB_TS = "**/*.?([cm])ts";
const GLOB_TSX = "**/*.?([cm])tsx";
const GLOB_DTS = "**/*.d.?([cm])ts";
const GLOB_VUE = "**/*.vue";
const GLOB_ASTRO = "**/*.astro";
const GLOB_ASTRO_TS = "**/*.astro/*.ts";
const GLOB_SVELTE = "**/*.svelte";
const GLOB_HTML = "**/*.htm?(l)";
const GLOB_STYLE = "**/*.{c,le,sc}ss";
const GLOB_CSS = "**/*.css";
const GLOB_POSTCSS = "**/*.{p,post}css";
const GLOB_LESS = "**/*.less";
const GLOB_SCSS = "**/*.scss";
const GLOB_SVG = "**/*.svg";
const GLOB_JSON = "**/*.json";
const GLOB_JSON5 = "**/*.json5";
const GLOB_JSONC = "**/*.jsonc";
const GLOB_MARKDOWN = "**/*.md";
const GLOB_MARKDOWN_CODE = `${GLOB_MARKDOWN}/${GLOB_SRC}`;
const GLOB_MARKDOWN_NESTED = "**/*.md/*.md";
const GLOB_YAML = "**/*.y?(a)ml";
const GLOB_PNPM_WORKSPACE_YAML = "**/pnpm-workspace.yaml";
const GLOB_TOML = "**/*.toml";
const GLOB_PRETTIER_LINT = [GLOB_SRC, GLOB_VUE];
const GLOB_PACKAGE_JSON = "**/package.json";
const GLOB_TS_CONFIG = "**/tsconfig.json";
const GLOB_TS_OTHER_CONFIG = "**/tsconfig.*.json";
const GLOB_JSON_SCHEMA = ["**/*.schema.json", "**/schemas/*.json"];
const GLOB_TESTS = [
`**/__tests__/**/*.${GLOB_SRC_EXT}`,
`**/*.spec.${GLOB_SRC_EXT}`,
`**/*.test.${GLOB_SRC_EXT}`,
`**/*.bench.${GLOB_SRC_EXT}`,
`**/*.benchmark.${GLOB_SRC_EXT}`,
`**/*.test-d.${GLOB_SRC_EXT}`,
`**/*.spec-d.${GLOB_SRC_EXT}`
];
const GLOB_EXCLUDE = [
"**/node_modules",
"**/dist",
"**/package-lock.json",
"**/yarn.lock",
"**/pnpm-lock.yaml",
"**/bun.lockb",
"**/output",
"**/coverage",
"**/temp",
"**/.temp",
"**/tmp",
"**/.tmp",
"**/.history",
"**/.vitepress/cache",
"**/.nuxt",
"**/.next",
"**/.vercel",
"**/.changeset",
"**/.idea",
"**/.cache",
"**/.output",
"**/.vite-inspect",
"**/CHANGELOG*.md",
"**/*.min.*",
"**/LICENSE*",
"**/__snapshots__",
"**/auto-import?(s).d.ts",
"**/components.d.ts",
"**/.github/workflows/*.yml"
];
const GLOB_TYPES = [
GLOB_DTS,
`**/types/${GLOB_TS}`,
`**/types.ts`
];
//#endregion
//#region src/constants/prettier.ts
/**
* Default prettier rules
*/
const PRETTIER_DEFAULT_OPTIONS = {
arrowParens: "always",
bracketSameLine: false,
bracketSpacing: true,
embeddedLanguageFormatting: "auto",
endOfLine: "lf",
experimentalOperatorPosition: "end",
experimentalTernaries: false,
htmlWhitespaceSensitivity: "css",
insertPragma: false,
jsxSingleQuote: true,
objectWrap: "preserve",
printWidth: 120,
proseWrap: "preserve",
quoteProps: "as-needed",
rangeEnd: Number.POSITIVE_INFINITY,
rangeStart: 0,
requirePragma: false,
semi: true,
singleAttributePerLine: true,
singleQuote: true,
tabWidth: 2,
trailingComma: "es5",
useTabs: false,
vueIndentScriptAndStyle: false
};
//#endregion
//#region src/constants/perfectionist.ts
/**
* Shared perfectionist plugin settings
* @descCN 共享的 perfectionist 插件设置
*/
const pluginSettings = {
fallbackSort: {
order: "asc",
type: "alphabetical"
},
ignoreCase: true,
order: "asc",
partitionByNewLine: false,
specialCharacters: "keep",
type: "alphabetical"
};
/**
* Shared perfectionist rule options for some rules
* @descCN 部分规则的共享 perfectionist 规则选项
*/
const partialRuleOptions = {
newlinesBetween: "ignore",
partitionByComment: ["@pg", "@perfectionist-group"]
};
/**
* Shared option `groups` for rule `sort-objects`
* @descCN 规则 `sort-objects` 的共享 `groups` 选项
*
* @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`
* @descCN 以下规则的共享 `groups` 选项:
* - `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
* @descCN 设计理念:简单类型优先,除了 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`
* @descCN 规则 `sort-imports` 的共享 `groups` 选项
*
* @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`
* @descCN 规则 `sort-exports` 的共享 `groups` 选项
*
* @see {@link https://perfectionist.dev/rules/sort-exports}
*/
const sortExportsGroups = [
"value-export",
"type-export",
"unknown"
];
/**
* Shared option `groups` for rule `sort-named-exports`
* @descCN 规则 `sort-named-exports` 的共享 `groups` 选项
*
* @see {@link https://perfectionist.dev/rules/sort-named-exports}
*/
const sortNamedExportsGroups = [
"value-export",
"type-export",
"unknown"
];
/**
* Shared option `groups` for rule `sort-named-imports`
* @descCN 规则 `sort-named-imports` 的共享 `groups` 选项
*
* @see {@link https://perfectionist.dev/rules/sort-named-imports}
*/
const sortNamedImportsGroups = [
"value-import",
"type-import",
"unknown"
];
/**
* Shared option `groups` for rule `sort-classes`
* @descCN 规则 `sort-classes` 的共享 `groups` 选项
*
* @see {@link https://perfectionist.dev/rules/sort-classes}
*/
const sortClassesGroups = ["unknown"];
/**
* Shared constants about eslint-plugin-perfectionist
* @descCN 关于 eslint-plugin-perfectionist 的共享常量
*/
const PERFECTIONIST = Object.freeze({
partialRuleOptions,
pluginSettings,
sortClassesGroups,
sortExportsGroups,
sortImportsTypes,
sortInterfacesOrObjectTypesGroups,
sortIntersectionTypesOrUnionTypesGroups,
sortNamedExportsGroups,
sortNamedImportsGroups,
sortObjectsGroups
});
//#endregion
//#region src/utils/env.ts
const hasVitest = () => isPackageExists("vitest");
const hasTypeScript = () => isPackageExists("typescript");
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) {
const resolved = await Promise.all(configs);
return resolved.flat();
}
//#endregion
//#region src/utils/ensurePackages.ts
/**
* Prompts user to install required packages if they are not installed.
*
* @example
* ensurePackages(['eslint', 'prettier']);
*
* @param packages List of package names to check.
*/
async function ensurePackages(packages) {
if (process.env.CI || process.stdout.isTTY === false) return;
const nonExistingPackages = packages.filter((i) => !isPackageExists(i));
if (nonExistingPackages.length === 0) return;
const { confirm } = await import("@clack/prompts");
const result = await confirm({ message: `${nonExistingPackages.length === 1 ? "Package is" : "Packages are"} required for this config: ${nonExistingPackages.join(", ")}. Do you want to install them?` });
if (result) {
const { installPackage } = await import("@antfu/install-pkg");
await installPackage(nonExistingPackages, { dev: true });
}
}
//#endregion
//#region src/utils/interopDefault.ts
/**
* Loads a module and returns the default export if it exists, otherwise returns the module itself.
* Useful for loading both CommonJS and ESM modules.
*
* @example
* const express = await interopDefault(import('express'));
* const app = express();
*
* @example
* const express = await interopDefault(import('express'));
* const { default: app } = express;
*/
async function interopDefault(mod) {
const resolved = await mod;
return resolved.default || resolved;
}
//#endregion
//#region src/utils/mergePrettierOptions.ts
function mergePrettierOptions(options = {}, overrides = {}) {
const result = {
...options,
...overrides,
plugins: [...options.plugins || [], ...overrides.plugins || []]
};
return result;
}
//#endregion
//#region src/configs/vue.ts
const disabledRules$1 = {
"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-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: [
"template",
"script",
"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-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"
};
/**
* Create a basic configuration for Vue.
*
* @see {@link https://github.com/vuejs/eslint-plugin-vue}
*
* @param options - {@link ConfigVueOptions}
* @returns A list of flat config items.
*/
async function createVueConfig(options = {}) {
await ensurePackages(["eslint-plugin-vue", "vue-eslint-parser"]);
const [pluginVue, parserVue, processorVueBlocks] = await Promise.all([
interopDefault(import("eslint-plugin-vue")),
interopDefault(import("vue-eslint-parser")),
interopDefault(import("eslint-processor-vue-blocks"))
]);
const { files = [GLOB_VUE], extraFileExtensions = [] } = options;
const sfcBlocks = options.sfcBlocks === true ? {} : options.sfcBlocks ?? {};
function getVueProcessor() {
const processorVueSFC = pluginVue.processors[".vue"];
if (!sfcBlocks) return processorVueSFC;
return mergeProcessors([processorVueSFC, processorVueBlocks({
...sfcBlocks,
blocks: {
styles: true,
...sfcBlocks.blocks
}
})]);
}
const sharedRules = {
...pluginVue.configs.base.rules,
...pluginVue.configs.essential.rules,
...pluginVue.configs["strongly-recommended"].rules,
...pluginVue.configs.recommended.rules
};
return [{
name: "sankeyangshu/vue/setup",
plugins: {
"@typescript-eslint": pluginTypeScript,
vue: pluginVue
}
}, {
name: "sankeyangshu/vue/rules",
files,
processor: getVueProcessor(),
languageOptions: {
parser: parserVue,
parserOptions: {
ecmaVersion: "latest",
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"],
...disabledRules$1,
...extensionRules,
...unCategorizedRules,
...options.overrides
}
}];
}
//#endregion
//#region src/configs/node.ts
/**
* Create a basic configuration for Node.
*
* @see {@link https://github.com/eslint-community/eslint-plugin-n}
*
* @param options - {@link ConfigNodeOptions}
* @returns A list of flat config items.
*/
function createNodeConfig(options = {}) {
return [{
name: "sankeyangshu/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
*/
async function createPnpmConfig(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: "sankeyangshu/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: "sankeyangshu/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
/**
* Creates a basic configuration for Perfectionist.
*
* @param options - {@link ConfigSortOptions}
* @returns A list of flat config items.
*/
function createSortConfig(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: "sankeyangshu/sort/tsconfig",
files: [GLOB_TS_CONFIG, GLOB_TS_OTHER_CONFIG],
rules: { "jsonc/sort-keys": [
"error",
{
order: [
"extends",
"compilerOptions",
"references",
"files",
"include",
"exclude"
],
pathPattern: "^$"
},
{
order: [
"incremental",
"composite",
"tsBuildInfoFile",
"disableSourceOfProjectReferenceRedirect",
"disableSolutionSearching",
"disableReferencedProjectLoad",
"target",
"jsx",
"jsxFactory",
"jsxFragmentFactory",
"jsxImportSource",
"lib",
"moduleDetection",
"noLib",
"reactNamespace",
"useDefineForClassFields",
"emitDecoratorMetadata",
"experimentalDecorators",
"baseUrl",
"rootDir",
"rootDirs",
"customConditions",
"module",
"moduleResolution",
"moduleSuffixes",
"noResolve",
"paths",
"resolveJsonModule",
"resolvePackageJsonExports",
"resolvePackageJsonImports",
"typeRoots",
"types",
"allowArbitraryExtensions",
"allowImportingTsExtensions",
"allowUmdGlobalAccess",
"allowJs",
"checkJs",
"maxNodeModuleJsDepth",
"strict",
"strictBindCallApply",
"strictFunctionTypes",
"strictNullChecks",
"strictPropertyInitialization",
"allowUnreachableCode",
"allowUnusedLabels",
"alwaysStrict",
"exactOptionalPropertyTypes",
"noFallthroughCasesInSwitch",
"noImplicitAny",
"noImplicitOverride",
"noImplicitReturns",
"noImplicitThis",
"noPropertyAccessFromIndexSignature",
"noUncheckedIndexedAccess",
"noUnusedLocals",
"noUnusedParameters",
"useUnknownInCatchVariables",
"declaration",
"declarationDir",
"declarationMap",
"downlevelIteration",
"emitBOM",
"emitDeclarationOnly",
"importHelpers",
"importsNotUsedAsValues",
"inlineSourceMap",
"inlineSources",
"mapRoot",
"newLine",
"noEmit",
"noEmitHelpers",
"noEmitOnError",
"outDir",
"outFile",
"preserveConstEnums",
"preserveValueImports",
"removeComments",
"sourceMap",
"sourceRoot",
"stripInternal",
"allowSyntheticDefaultImports",
"esModuleInterop",
"forceConsistentCasingInFileNames",
"isolatedDeclarations",
"isolatedModules",
"preserveSymlinks",
"verbatimModuleSyntax",
"skipDefaultLibCheck",
"skipLibCheck"
],
pathPattern: "^compilerOptions$"
}
] }
});
if (enableSortPackageJson) configs.push({
name: "sankeyangshu/sort/package-json",
files: [GLOB_PACKAGE_JSON],
rules: {
"jsonc/sort-array-values": ["error", {
order: { type: "asc" },
pathPattern: "^files$"
}],
"jsonc/sort-keys": [
"error",
{
order: [
"name",
"version",
"type",
"private",
"packageManager",
"description",
"author",
"license",
"homepage",
"repository",
"bugs",
"keywords",
"contributors",
"funding",
"main",
"module",
"types",
"files",
"engines",
"exports",
"typesVersions",
"sideEffects",
"unpkg",
"jsdelivr",
"browser",
"bin",
"man",
"directories",
"publishConfig",
"scripts",
"peerDependencies",
"peerDependenciesMeta",
"optionalDependencies",
"dependencies",
"devDependencies",
"pnpm",
"config",
"overrides",
"husky",
"simple-git-hooks",
"lint-staged",
"eslintConfig",
"prettier"
],
pathPattern: "^$"
},
{
order: { type: "asc" },
pathPattern: "^(?:dev|peer|optional|bundled)?[Dd]ependencies(Meta)?$"
},
{
order: { type: "asc" },
pathPattern: "^(?:resolutions|overrides|pnpm.overrides)$"
},
{
order: [
"types",
"import",
"require",
"default"
],
pathPattern: "^exports.*$"
},
{
order: [
"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)$"
}
]
}
});
if (enableSortI18nLocale) configs.push({
name: "sankeyangshu/sort/i18n-locale/json",
files: ["**/{i18n,langs,locales}/*.json"],
rules: { "jsonc/sort-keys": ["error", {
order: { type: "asc" },
pathPattern: ".*"
}] }
}, {
name: "sankeyangshu/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: "sankeyangshu/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: "sankeyangshu/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: "sankeyangshu/sort/additional-json",
files: additionalJsonFiles,
rules: { "jsonc/sort-keys": ["error", {
pathPattern: ".*",
order: ["$schema", { order: { type: "asc" } }]
}] }
});
if (additionalYamlFiles.length) configs.push({
name: "sankeyangshu/sort/additional-yaml",
files: additionalYamlFiles,
rules: { "yml/sort-keys": ["error", {
order: { type: "asc" },
pathPattern: ".*"
}] }
});
return configs;
}
//#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
*/
function createTestConfig(options = {}) {
const { files = [...GLOB_TESTS], vitest: enableVitest = hasVitest() } = options;
const configs = [{
name: "sankeyangshu/test/setup",
plugins: { "no-only-tests": pluginNoOnlyTests }
}, {
name: "sankeyangshu/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: "sankeyangshu/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
*/
function createTomlConfig(options = {}) {
const { files = [GLOB_TOML] } = options;
return [{
name: "sankeyangshu/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/yaml.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
*/
function createYamlConfig(options = {}) {
const { files = [GLOB_YAML] } = options;
return [{
name: "sankeyangshu/yaml/setup",
plugins: { yml: pluginYml }
}, {
name: "sankeyangshu/yaml/rules",
files,
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/configs/astro.ts
/**
* Creates a basic configuration for Astro.
*
* @see {@link https://github.com/ota-meshi/eslint-plugin-astro}
*
* @param options - {@link ConfigAstroOptions}
* @returns A list of flat config items.
*/
async function createAstroConfig(options = {}) {
await ensurePackages(["eslint-plugin-astro", "astro-eslint-parser"]);
const [parserAstro, pluginAstro] = await Promise.all([interopDefault(import("astro-eslint-parser")), interopDefault(import("eslint-plugin-astro"))]);
const { files = [GLOB_ASTRO], extraFileExtensions = [] } = options;
return [{
name: "sankeyangshu/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
/**
* JavaScript specific rules
*/
const javascriptRules = {
"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"
};
/**
* Creates a basic ESLint configuration for the JSDoc plugin.
*
* @see {@link https://github.com/gajus/eslint-plugin-jsdoc}
*
* @param options - {@link ConfigJsdocOptions}
* @returns ESLint configs
*/
function createJsdocConfig(options = {}) {
return [{
name: "sankeyangshu/jsdoc",
plugins: { jsdoc: pluginJsdoc },
rules: {
"jsdoc/check-access": "warn",
"jsdoc/check-param-names": "warn",
"jsdoc/check-property-names": "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-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-yields-check": "warn",
...options.typescript ? typescriptRules : javascriptRules,
...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"
};
/**
* Create a configuration for jsonc.
*
* @see {@link https://ota-meshi.github.io/eslint-plugin-jsonc}
*
* @param options - {@link ConfigJsoncOptions}
* @returns ESLint configs
*/
function createJsoncConfig(options = {}) {
const { files = [
GLOB_JSON,
GLOB_JSON5,
GLOB_JSONC
] } = options;
return [{
name: "sankeyangshu/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/react.ts
const ReactRefreshAllowConstantExportPackages = ["vite"];
const RemixPackages = [
"@remix-run/node",
"@remix-run/react",
"@remix-run/serve",
"@remix-run/dev"
];
const ReactRouterPackages = [
"@react-router/node",
"@react-router/react",
"@react-router/serve",
"@react-router/dev"
];
const NextJsPackages = ["next"];
const typeAwareRules$1 = { "react/no-leaked-conditional-rendering": "warn" };
/**
* Creates a basic configuration for React using ESLint plugins.
* @see {@link https://github.com/Rel1cx/eslint-react}
*
* @param options - {@link ConfigReactOptions}
* @returns A list of flat config items.
*/
async function createReactConfig(options = {}) {
await ensurePackages([
"@eslint-react/eslint-plugin",
"eslint-plugin-react-hooks",
"eslint-plugin-react-refresh"
]);
const [pluginReact, pluginReactHooks, pluginReactRefresh] = await Promise.all([
interopDefault(import("@eslint-react/eslint-plugin")),
interopDefault(import("eslint-plugin-react-hooks")),
interopDefault(import("eslint-plugin-react-refresh"))
]);
const { files = [GLOB_SRC], filesTypeAware = [GLOB_TS, GLOB_TSX], ignoresTypeAware = [`${GLOB_MARKDOWN}/**`, GLOB_ASTRO_TS], overrides = {}, tsconfigPath } = options;
const enableTypeAwareLint = !!tsconfigPath;
const isAllowConstantExport = ReactRefreshAllowConstantExportPackages.some((i) => isPackageExists(i));
const isUsingRemix = RemixPackages.some((i) => isPackageExists(i));
const isUsingReactRouter = ReactRouterPackages.some((i) => isPackageExists(i));
const isUsingNext = NextJsPackages.some((i) => isPackageExists(i));
const plugins = pluginReact.configs.all.plugins;
return [
{
name: "sankeyangshu/react/setup",
plugins: {
react: plugins["@eslint-react"],
"react-dom": plugins["@eslint-react/dom"],
"react-hooks": pluginReactHooks,
"react-hooks-extra": plugins["@eslint-react/hooks-extra"],
"react-naming-convention": plugins["@eslint-react/naming-convention"],
"react-refresh": pluginReactRefresh,
"react-web-api": plugins["@eslint-react/web-api"]
}
},
{
name: "sankeyangshu/react/rules",
files,
languageOptions: {
parserOptions: { ecmaFeatures: { jsx: true } },
sourceType: "module"
},
rules: {
"react/jsx-no-duplicate-props": "warn",
"react/jsx-uses-vars": "warn",
"react/no-access-state-in-setstate": "error",
"react/no-array-index-key": "warn",
"react/no-children-count": "warn",
"react/no-children-for-each": "warn",
"react/no-children-map": "warn",
"react/no-children-only": "warn",
"react/no-children-to-array": "warn",
"react/no-clone-element": "warn",
"react/no-comment-textnodes": "warn",
"react/no-component-will-mount": "error",
"react/no-component-will-receive-props": "error",
"react/no-component-will-update": "error",
"react/no-context-provider": "warn",
"react/no-create-ref": "error",
"react/no-default-props": "error",
"react/no-direct-mutation-state": "error",
"react/no-duplicate-key": "warn",
"react/no-forward-ref": "warn",
"react/no-implicit-key": "warn",
"react/no-missing-key": "error",
"react/no-nested-component-definitions": "error",
"react/no-prop-types": "error",
"react/no-redundant-should-component-update": "error",
"react/no-set-state-in-component-did-mount": "warn",
"react/no-set-state-in-component-did-update": "warn",
"react/no-set-state-in-component-will-update": "warn",
"react/no-string-refs": "error",
"react/no-unsafe-component-will-mount": "warn",
"react/no-unsafe-component-will-receive-props": "warn",
"react/no-unsafe-component-will-update": "warn",
"react/no-unstable-context-value": "warn",
"react/no-unstable-default-props": "warn",
"react/no-unused-class-component-members": "warn",
"react/no-unused-state": "warn",
"react/no-use-context": "warn",
"react/no-useless-forward-ref": "warn",
"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"
] : []]
}],
"react/react-in-jsx-scope": "off",
"react/jsx-uses-react": "off",
...overrides
}
},
...enableTypeAwareLint ? [{
files: filesTypeAware,
ignores: ignoresTypeAware,
name: "sankeyangshu/react/type-aware-rules",
rules: {
...typeAwareRules$1,
...options.overridesTypeAwareRules ?? {}
}
}] : []
];
}
//#endregion
//#region src/configs/solid.ts
/**
* Creates a basic configuration for Solid.
*
* @see {@link https://github.com/solidjs-community/eslint-plugin-solid}
*
* @param options - {@link ConfigSolidOptions}
* @returns A list of flat config items.
*/
async function createSolidConfig(options = {}) {
await ensurePackages(["eslint-plugin-solid"]);
const pluginSolid = await interopDefault(import("eslint-plugin-solid"));
const { files = [GLOB_JSX, GLOB_TSX], typescript: enableTypeScript } = options;
return [{
name: "sankeyangshu/solid",
files,
plugins: { solid: pluginSolid },
languageOptions: {
parser: parserTypeScript,
parserOptions: { ecmaFeatures: { jsx: true } },
sourceType: "module"
},
rules: {
"solid/components-return-once": "warn",
"solid/event-handlers": ["error", {
ignoreCase: false,
warnOnSpread: false
}],
"solid/imports": "error",
"solid/jsx-no-duplicate-props": "error",
"solid/jsx-no-script-url": "error",
"solid/jsx-no-undef": "error",
"solid/jsx-uses-vars": "error",
"solid/no-destructure": "error",
"solid/no-innerhtml": ["error", { allowStatic: true }],
"solid/no-react-deps": "error",
"solid/no-react-specific-props": "error",
"solid/no-unknown-namespaces": "error",
"solid/prefer-for": "error",
"solid/reactivity": "warn",
"solid/self-closing-comp": "error",
"solid/style-prop": ["error", { styleProps: ["style", "css"] }],
...enableTypeScript ? {
"solid/jsx-no-undef": ["error", { typescriptEnabled: true }],
"solid/no-unknown-namespaces": "off"
} : {},
...options.overrides
}
}];
}
//#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
*/
function createRegexpConfig(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 [{
...recommendedConfig,
name: "sankeyangshu/regexp",
rules: {
...recommendedRules$1,
...options.overrides
}
}];
}
//#endregion
//#region src/configs/svelte.ts
/**
* Create a basic configuration for Svelte.
*
* @see {@link https://github.com/ota-meshi/eslint-plugin-svelte}
*
* @param options - {@link ConfigSvelteOptions}
* @returns A list of flat config items.
*/
async function createSvelteConfig(options = {}) {
await ensurePackages(["svelte-eslint-parser", "eslint-plugin-svelte"]);
const [parserSvelte, pluginSvelte] = await Promise.all([interopDefault(import("svelte-eslint-parser")), interopDefault(import("eslint-plugin-svelte"))]);
const { files = [GLOB_SVELTE], extraFileExtensions = [] } = options;
return [{
name: "sankeyangshu/svelte",
files,
plugins: { svelte: pluginSvelte },
processor: pluginSvelte.processors[".svelte"],
languageOptions: {
parser: parserSvelte,
sourceType: "module",
parserOptions: {
extraFileExtensions,
parser: parserTypeScript
}
},
rules: {
"import-x/no-mutable-exports": "off",
"no-undef": "off",
"no-unused-vars": ["error", {
args: "none",
caughtErrors: "none",
ignoreRestSiblings: true,
vars: "all",
varsIgnorePattern: "^(\\$\\$Props$|\\$\\$Events$|\\$\\$Slots$)"
}],
"svelte/comment-directive": "error",
"svelte/derived-has-same-inputs-outputs": "error",
"svelte/html-closing-bracket-spacing": "error",
"svelte/html-quotes": ["error", {
prefer: "double",
dynamic: {
avoidInvalidUnquotedInHTML: false,
quoted: true
}
}],
"svelte/indent": ["error", {
alignAttributesVertically: true,
indent: 2,
indentScript: false
}],
"svelte/mustache-spacing": ["error", {
attributesAndProps: "never",
directiveExpressions: "always",
textExpressions: "always",
tags: {
closingBrace: "always",
openingBrace: "always"
}
}],
"svelte/no-at-debug-tags": "error",
"svelte/no-at-html-tags": "error",
"svelte/no-dupe-else-if-blocks": "error",
"svelte/no-dupe-style-properties": "error",
"svelte/no-dupe-use-directives": "error",
"svelte/no-dynamic-slot-name": "error",
"svelte/no-export-load-in-svelte-module-in-kit-pages": "error",
"svelte/no-inner-declarations": "error",
"svelte/no-not-function-handler": "error",
"svelte/no-object-in-text-mustaches": "error",
"svelte/no-reactive-functions": "error",
"svelte/no-reactive-literals": "error",
"svelte/no-shorthand-style-property-overrides": "error",
"svelte/no-spaces-around-equal-signs-in-attribute": "error",
"svelte/no-trailing-spaces": "error",
"svelte/no-unknown-style-directive-property": "error",
"svelte/no-unused-svelte-ignore": "error",
"svelte/no-useless-mustaches": "error",
"svelte/require-store-callbacks-use-set-param": "error",
"svelte/spaced-html-comment": "error",
"svelte/system": "error",
"svelte/valid-each-key": "error",
...options.overrides
}
}];
}
//#endregion
//#region src/configs/unocss.ts
/**
* Creates a basic configuration for UnoCSS.
*
* @see {@link https://github.com/unocss/unocss/tree/main/packages-integrations/eslint-plugin}
*
* @param options - {@link ConfigUnoCSSOptions}
* @returns A list of flat config items.
*/
async function createUnoCssConfig(options = {}) {
await ensurePackages(["@unocss/eslint-plugin"]);
const pluginUnoCSS = await interopDefault(import("@unocss/eslint-plugin"));
const { attributify = false } = options;
return [{
name: "sankeyangshu/unocss",
plugins: { unocss: pluginUnoCSS },
rules: {
"unocss/order": "warn",
...attributify ? { "unocss/order-attributify": "warn" } : {},
...options.overrides
}
}];
}
//#e