UNPKG

@sankeyangshu/eslint-config

Version:
1,783 lines (1,757 loc) 90.7 kB
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