UNPKG

@rebeccastevens/eslint-config

Version:
1,303 lines (1,293 loc) 164 kB
import * as path from 'node:path'; import path__default from 'node:path'; import { isPackageExists } from 'local-pkg'; import fs from 'node:fs/promises'; import globals from 'globals'; import semver from 'semver'; async function interopDefault(value) { const resolved = await value; // eslint-disable-next-line ts/no-unsafe-return, ts/no-explicit-any, ts/no-unsafe-member-access return resolved.default ?? resolved; } const parserPlain = { meta: { name: "parser-plain", }, parseForESLint: (code) => ({ ast: { body: [], comments: [], loc: { end: code.length, start: 0 }, range: [0, code.length], tokens: [], type: "Program", }, scopeManager: null, services: { isPlain: true }, visitorKeys: { Program: [], }, }), }; async function loadPackages(packageIds) { const missing = packageIds.filter((id) => !isPackageExists(id)); if (missing.length > 0) { await installPackages(missing); } // eslint-disable-next-line ts/no-explicit-any, ts/no-unsafe-return return Promise.all(packageIds.map((id) => interopDefault(import(__rewriteRelativeImportExtension(id))))); } const mut_installPackagesToLoad = new Set(); let mut_installPackagesAction = null; let mut_installPackagesActionResolver = null; let mut_installPackagesTimeout = null; /* eslint-disable functional/no-loop-statements */ async function installPackages(packages) { for (const p of packages) { mut_installPackagesToLoad.add(p); } if (mut_installPackagesTimeout !== null) { clearTimeout(mut_installPackagesTimeout); } mut_installPackagesTimeout = setTimeout(() => { const allPackages = [...mut_installPackagesToLoad.values()]; mut_installPackagesTimeout = null; mut_installPackagesToLoad.clear(); mut_installPackagesAction = null; mut_installPackagesActionResolver(allPackages); mut_installPackagesActionResolver = null; }, 100); if (mut_installPackagesAction === null) { mut_installPackagesAction = new Promise((resolve) => { mut_installPackagesActionResolver = resolve; }).then(async (allPackages) => { const allPackagesString = allPackages.join(", "); if (Boolean(process.env["CI"]) || !process.stdout.isTTY) { throw new Error(`Missing packages: ${allPackagesString}`); } const prompt = await import('@clack/prompts'); const result = await prompt.confirm({ message: allPackages.length === 1 ? `${allPackages[0]} is required for this config. Do you want to install it?` : `Packages are required for this config: ${allPackagesString}.\nDo you want to install them?`, }); if (result !== false) { await import('@antfu/install-pkg').then(({ installPackage }) => installPackage(allPackages, { dev: true })); } }); } return mut_installPackagesAction; } /* eslint-enable functional/no-loop-statements */ async function comments() { const [pluginComments] = (await loadPackages(["eslint-plugin-eslint-comments"])); return [ { name: "rs:comments", plugins: { comments: pluginComments, }, rules: { "comments/no-aggregating-enable": "error", "comments/no-duplicate-disable": "error", "comments/no-unlimited-disable": "error", "comments/no-unused-enable": "error", "comments/disable-enable-pair": ["error", { allowWholeFile: true }], }, }, ]; } 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_ROOT_JS = "./?(.)*.?([cm])js"; const GLOB_ROOT_JSX = "./?(.)*.?([cm])jsx"; const GLOB_ROOT_TS = "./?(.)*.?([cm])ts"; const GLOB_ROOT_TSX = "./?(.)*.?([cm])tsx"; const GLOB_ROOT_DTS = "./?(.)*.d.?([cm])ts"; const GLOB_TYPINGS = "typings/**/?(.)*.?([cm])ts"; const GLOB_MJS = "**/?(.)*.mjs"; const GLOB_MTS = "**/?(.)*.mts"; const GLOB_CJS = "**/?(.)*.cjs"; const GLOB_CTS = "**/?(.)*.cts"; // cspell:disable-next-line const GLOB_STYLE = "**/?(.)*.{c,le,sc,pc,postc}ss"; const GLOB_CSS = "**/?(.)*.css"; const GLOB_POSTCSS = "**/?(.)*.{p,post}css"; const GLOB_LESS = "**/?(.)*.less"; const GLOB_SCSS = "**/?(.)*.scss"; const GLOB_JSON = "**/?(.)*.json"; const GLOB_JSON5 = "**/?(.)*.json5"; const GLOB_JSONC = "**/?(.)*.jsonc"; const GLOB_MARKDOWN = "**/?(.)*.md"; const GLOB_MARKDOWN_IN_MARKDOWN = "**/?(.)*.md/?(.)*.md"; const GLOB_VUE = "**/?(.)*.vue"; const GLOB_YAML = "**/?(.)*.y?(a)ml"; const GLOB_TOML = "**/?(.)*.toml"; const GLOB_HTML = "**/?(.)*.htm?(l)"; const GLOB_GRAPHQL = "**/?(.)*.{g,graph}ql"; const GLOB_MARKDOWN_CODE = `${GLOB_MARKDOWN}/${GLOB_SRC}`; const GLOB_TESTS = [ `**/__tests__/**/?(.)*.${GLOB_SRC_EXT}`, `**/?(.)*.spec.${GLOB_SRC_EXT}`, `**/?(.)*.test.${GLOB_SRC_EXT}`, `**/?(.)*.bench.${GLOB_SRC_EXT}`, `**/?(.)*.benchmark.${GLOB_SRC_EXT}`, ]; const GLOB_ALL_SRC = [ GLOB_SRC, GLOB_STYLE, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_MARKDOWN, GLOB_VUE, GLOB_YAML, GLOB_HTML, GLOB_TOML, GLOB_GRAPHQL, ]; const GLOB_EXCLUDE = [ "**/node_modules", "**/dist", "**/lib", "**/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", "**/.yarn", "**/CHANGELOG*.md", "**/?(.)*.min.*", "**/LICENSE*", "**/__snapshots__", "**/auto-import.ts", "**/auto-import.d.ts", "**/auto-imports.ts", "**/auto-imports.d.ts", "**/components.ts", "**/components.d.ts", "**/typegen.ts", "**/typegen.d.ts", ]; async function formatters(opts, stylistic) { const options = opts === true ? { js: true, ts: true, dts: false, json: true, yaml: true, css: true, graphql: true, html: true, markdown: true, slidev: isPackageExists("@slidev/cli"), tailwind: isPackageExists("tailwindcss"), } : opts; if (options.slidev !== false && options.slidev !== undefined && options.markdown !== true) { throw new Error("`slidev` option only works when `markdown` is enabled with `prettier`"); } const { indent, printWidth, quotes, semi } = stylistic; const prettierOptions = Object.assign({ endOfLine: "lf", printWidth: printWidth ?? 120, semi: semi ?? true, singleQuote: quotes === "single", tabWidth: typeof indent === "number" ? indent : 2, trailingComma: "all", useTabs: indent === "tab", }, options.prettierOptions ?? {}); const packages = loadPackages([ "eslint-plugin-format", "eslint-config-prettier", "sort-package-json", "eslint-formatting-reporter", "prettier", ]); const prettierPluginTailwindcssPromise = (((options.js !== undefined && options.js) || (options.ts !== undefined && options.ts)) && options.tailwind !== undefined && options.tailwind ? loadPackages(["prettier-plugin-tailwindcss"]) : [undefined]); const [[pluginFormat, configPrettier, sortPackageJson, formattingReporter], [prettierPluginTailwindcss]] = await Promise.all([packages, prettierPluginTailwindcssPromise]); const turnOffRulesForPrettier = { ...Object.fromEntries(Object.entries(configPrettier.rules ?? {}).filter(([, value]) => value === "off")), // curly: "off", "no-unexpected-multiline": "off", // "style/lines-around-comment": "off", "style/max-len": "off", "style/no-confusing-arrow": "off", "style/no-mixed-operators": "off", "style/no-tabs": "off", "style/quotes": "off", // "style/js/lines-around-comment": "off", // "style/js/max-len": "off", "style/js/no-confusing-arrow": "off", "style/js/no-mixed-operators": "off", "style/js/no-tabs": "off", "style/js/quotes": "off", // "style/ts/lines-around-comment": "off", "style/ts/quotes": "off", "ts/lines-around-comment": "off", "ts/quotes": "off", // "babel/quotes": "off", // "unicorn/template-indent": "off", "vue/html-self-closing": "off", "vue/max-len": "off", // other "no-irregular-whitespace": "off", "yaml/block-sequence-hyphen-indicator-newline": "off", }; const mut_configs = [ { name: "rs:formatters:setup", plugins: { format: pluginFormat, }, }, ]; if (options.js !== undefined && options.js) { mut_configs.push({ name: "rs:formatter:javascript", files: [GLOB_JS, GLOB_JSX], rules: { ...turnOffRulesForPrettier, "format/prettier": [ "error", { ...prettierOptions, parser: "babel", ...(options.tailwind !== undefined && options.tailwind ? { plugins: [ ...(prettierOptions.plugins ?? []), ...(prettierPluginTailwindcss === undefined ? [] : ["prettier-plugin-tailwindcss"]), ], } : {}), }, ], }, }); } if (options.ts !== undefined && options.ts) { mut_configs.push({ name: "rs:formatter:typescript", files: [GLOB_TS, GLOB_TSX], ignores: options.dts === true ? [] : [GLOB_DTS], rules: { ...turnOffRulesForPrettier, "format/prettier": [ "error", { ...prettierOptions, parser: "typescript", ...(options.tailwind !== undefined && options.tailwind ? { plugins: [ ...(prettierOptions.plugins ?? []), ...(prettierPluginTailwindcss === undefined ? [] : ["prettier-plugin-tailwindcss"]), ], } : {}), }, ], }, }); } if (options.yaml !== undefined && options.yaml) { mut_configs.push({ name: "rs:formatter:yaml", files: [GLOB_YAML], languageOptions: { parser: parserPlain, }, rules: { ...turnOffRulesForPrettier, "format/prettier": [ "error", { ...prettierOptions, parser: "yaml", }, ], }, }); } if (options.json !== undefined && options.json) { mut_configs.push({ name: "rs:formatter:json", files: [GLOB_JSON], ignores: ["**/package.json"], languageOptions: { parser: parserPlain, }, rules: { ...turnOffRulesForPrettier, "format/prettier": [ "error", { ...prettierOptions, parser: "json", }, ], }, }, { name: "rs:formatter:jsonc", files: [GLOB_JSONC], languageOptions: { parser: parserPlain, }, rules: { ...turnOffRulesForPrettier, "format/prettier": [ "error", { ...prettierOptions, parser: "jsonc", }, ], }, }, { name: "rs:formatter:json5", files: [GLOB_JSON5], languageOptions: { parser: parserPlain, }, rules: { ...turnOffRulesForPrettier, "format/prettier": [ "error", { ...prettierOptions, parser: "json5", }, ], }, }, { name: "rs:formatter:packagejson", files: ["**/package.json"], languageOptions: { parser: parserPlain, }, plugins: { "package-json": { meta: { name: "rs:formatter:packagejson", }, rules: { sort: { meta: { type: "layout", fixable: "whitespace", messages: formattingReporter.messages, schema: [ { type: "object", properties: { parser: { type: "string", required: true, }, }, additionalProperties: true, }, ], }, create(context) { return { Program() { if (!("text" in context.sourceCode)) { return; } const sourceCode = context.sourceCode.text; try { const formatted = sortPackageJson(sourceCode); formattingReporter.reportDifferences(context, sourceCode, formatted); } catch (error) { console.error(error); context.report({ loc: { start: { line: 1, column: 0 }, end: { line: 1, column: 0 }, }, message: "Failed to format package.json", }); } }, }; }, }, }, }, }, rules: { "package-json/sort": "error", }, }); } if (options.css !== undefined && options.css) { mut_configs.push({ name: "rs:formatter:css", files: [GLOB_CSS, GLOB_POSTCSS], languageOptions: { parser: parserPlain, }, rules: { ...turnOffRulesForPrettier, "format/prettier": [ "error", { ...prettierOptions, parser: "css", }, ], }, }, { name: "rs:formatter:scss", files: [GLOB_SCSS], languageOptions: { parser: parserPlain, }, rules: { ...turnOffRulesForPrettier, "format/prettier": [ "error", { ...prettierOptions, parser: "scss", }, ], }, }, { name: "rs:formatter:less", files: [GLOB_LESS], languageOptions: { parser: parserPlain, }, rules: { ...turnOffRulesForPrettier, "format/prettier": [ "error", { ...prettierOptions, parser: "less", }, ], }, }); } if (options.html !== undefined && options.html) { mut_configs.push({ name: "rs:formatter:html", files: ["**/*.html"], languageOptions: { parser: parserPlain, }, rules: { ...turnOffRulesForPrettier, "format/prettier": [ "error", { ...prettierOptions, parser: "html", }, ], }, }); } if (options.markdown !== undefined && options.markdown) { const GLOB_SLIDEV = options.slidev === undefined || options.slidev === false ? [] : options.slidev === true ? ["**/slides.md"] : (options.slidev.files ?? []); mut_configs.push({ name: "rs:formatter:markdown", files: [GLOB_MARKDOWN], ignores: GLOB_SLIDEV, languageOptions: { parser: parserPlain, }, rules: { ...turnOffRulesForPrettier, "format/prettier": [ "error", { ...prettierOptions, embeddedLanguageFormatting: "off", parser: "markdown", }, ], }, }); if (options.slidev !== undefined && options.slidev !== false) { mut_configs.push({ name: "rs:formatter:slidev", files: GLOB_SLIDEV, languageOptions: { parser: parserPlain, }, rules: { ...turnOffRulesForPrettier, "format/prettier": [ "error", { ...prettierOptions, embeddedLanguageFormatting: "off", parser: "slidev", plugins: ["prettier-plugin-slidev"], }, ], }, }); } } if (options.graphql !== undefined && options.graphql) { mut_configs.push({ files: [GLOB_GRAPHQL], languageOptions: { parser: parserPlain, }, name: "rs:formatter:graphql", rules: { ...turnOffRulesForPrettier, "format/prettier": [ "error", { ...prettierOptions, parser: "graphql", }, ], }, }); } return mut_configs; } async function functional(options) { const { mode, overrides, stylistic, filesTypeAware, functionalEnforcement, ignoreNamePattern, // ignoreTypePattern, } = options; const [pluginFunctional] = (await loadPackages(["eslint-plugin-functional"])); const strictRules = { "functional/functional-parameters": "error", "functional/no-throw-statements": "error", "functional/no-try-statements": "error", "functional/no-let": "error", "functional/no-class-inheritance": "error", "functional/no-this-expressions": "error", "functional/no-loop-statements": "error", "functional/immutable-data": "error", "functional/prefer-immutable-types": "error", "functional/type-declaration-immutability": "error", "functional/no-mixed-types": "error", "functional/no-conditional-statements": [ "error", { ignoreCodePattern: ["import.meta.vitest"], }, ], "functional/no-expression-statements": "error", "functional/no-return-void": "error", "functional/prefer-property-signatures": stylistic === false ? "off" : "error", "functional/prefer-tacit": stylistic === false ? "off" : "warn", "functional/readonly-type": stylistic === false ? "off" : "error", }; const recommendedRules = { "functional/functional-parameters": [ "error", { allowRestParameter: true, enforceParameterCount: false, }, ], "functional/no-let": [ "error", { allowInForLoopInit: true, ignoreIdentifierPattern: ignoreNamePattern, }, ], "functional/no-loop-statements": "error", "functional/no-throw-statements": [ "error", { allowToRejectPromises: true, }, ], "functional/immutable-data": [ "error", { ignoreAccessorPattern: ["**.mut_*.**"], ignoreClasses: "fieldsOnly", ignoreImmediateMutation: true, ignoreNonConstDeclarations: true, }, ], "functional/no-conditional-statements": [ "error", { allowReturningBranches: true, ignoreCodePattern: ["import.meta.vitest"], }, ], "functional/no-expression-statements": [ "error", { ignoreCodePattern: "^assert", ignoreSelfReturning: true, ignoreVoid: true, }, ], "functional/no-return-void": mode === "library" ? "error" : "off", "ts/prefer-readonly-parameter-types": "off", "functional/prefer-immutable-types": [ mode === "library" ? "warn" : "off", { enforcement: "None", overrides: [ { specifiers: [ { from: "file", }, { from: "lib", }, ], options: { ignoreInferredTypes: true, ignoreNamePattern, parameters: { enforcement: "ReadonlyShallow" }, }, }, ], suggestions: { ReadonlyShallow: [ [ { pattern: "^([_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*\\[\\])$", replace: "readonly $1", message: "Prepend with readonly.", }, { pattern: "^(Array|Map|Set)<(.+)>$", replace: "Readonly$1<$2>", message: "Use Readonly$1 instead of $1.", }, ], [ { pattern: "^(.+)$", replace: "Readonly<$1>", message: "Surround with Readonly.", }, ], ], ReadonlyDeep: [ [ { pattern: "^(?:Readonly<(.+)>|(.+))$", replace: "ReadonlyDeep<$1$2>", message: "Surround with ReadonlyDeep.", }, ], ], Immutable: [ [ { pattern: "^(?:Readonly(?:Deep)?<(.+)>|(.+))$", replace: "Immutable<$1$2>", message: "Surround with Immutable.", }, ], ], }, }, ], "functional/type-declaration-immutability": [ "error", { rules: [ { identifiers: "I?Immutable.+", immutability: "Immutable", comparator: "AtLeast", }, { identifiers: "I?ReadonlyDeep.+", immutability: "ReadonlyDeep", comparator: "AtLeast", }, { identifiers: "I?Readonly.+", immutability: "ReadonlyShallow", comparator: "AtLeast", fixer: [ { pattern: "^([_$a-zA-Z\\xA0-\\uFFFF][_$a-zA-Z0-9\\xA0-\\uFFFF]*\\[\\])$", replace: "readonly $1", }, { pattern: "^(Array|Map|Set)<(.+)>$", replace: "Readonly$1<$2>", }, { pattern: "^(.+)$", replace: "Readonly<$1>", }, ], }, { identifiers: "I?Mutable.+", immutability: "Mutable", comparator: "AtMost", fixer: [ { pattern: "^Readonly(Array|Map|Set)<(.+)>$", replace: "$1<$2>", }, { pattern: "^Readonly<(.+)>$", replace: "$1", }, ], }, ], }, ], "functional/prefer-property-signatures": stylistic === false ? "off" : "error", "functional/prefer-tacit": stylistic === false ? "off" : "warn", "functional/readonly-type": stylistic === false ? "off" : "error", }; const liteRules = { ...recommendedRules, "functional/no-conditional-statements": "off", "functional/no-expression-statements": "off", "functional/no-return-void": "off", "functional/prefer-immutable-types": [ mode === "library" ? "warn" : "off", { ...recommendedRules["functional/prefer-immutable-types"][1], overrides: [ { ...recommendedRules["functional/prefer-immutable-types"][1].overrides[0], specifiers: [ { from: "file", }, ], }, ], }, ], }; const noneLibraryRules = { "functional/prefer-immutable-types": liteRules["functional/prefer-immutable-types"], "functional/type-declaration-immutability": ["warn", liteRules["functional/type-declaration-immutability"][1]], }; return [ { name: "rs:functional", plugins: { functional: pluginFunctional, }, settings: { immutability: { overrides: [ { type: "^Readonly<.+>$", to: "ReadonlyShallow" }, { type: "^ReadonlyDeep<.+>$", to: "ReadonlyDeep" }, { type: "^Immutable<.+>$", to: "Immutable" }, ], }, }, rules: { ...pluginFunctional.configs.off.rules, ...(functionalEnforcement === "none" ? mode === "library" ? noneLibraryRules : {} : functionalEnforcement === "lite" ? liteRules : functionalEnforcement === "strict" ? strictRules : recommendedRules), ...overrides, }, }, { name: "rs:functional:disable-type-aware", ignores: filesTypeAware, rules: pluginFunctional.configs.disableTypeChecked.rules, }, ]; } async function ignores(options) { const { ignoreFiles, ignores: ignoresOptions, projectRoot } = options; const includeIgnoreFile = ignoreFiles.length === 0 ? undefined : await loadPackages(["@eslint/compat"]).then(([p]) => p.includeIgnoreFile); const [extend, files] = Array.isArray(ignoresOptions) ? [true, ignoresOptions] : [ignoresOptions.extend, ignoresOptions.files]; const ignoreConfig = { ignores: extend ? [...GLOB_EXCLUDE, ...files] : [...files], }; const ignoreFileConfigs = await Promise.all(ignoreFiles.map((file) => { const filePath = path__default.resolve(projectRoot, file); return fs .access(filePath) .then(() => includeIgnoreFile(filePath)) .catch(() => { console.warn(`Ignore file "${filePath}" not found.`); return null; }); })); return [ignoreConfig, ...ignoreFileConfigs.filter((v) => v !== null)]; } async function imports(options) { const { stylistic, parserOptions, typescript } = options; const [pluginImport] = (await loadPackages(typescript ? [ "eslint-plugin-import-x", "eslint-import-resolver-typescript", // make sure it exists - we only implicitly use it ] : ["eslint-plugin-import-x"])); return [ { name: "rs:imports", plugins: { import: pluginImport, }, settings: { "import-x/external-module-folders": ["node_modules", "node_modules/@types"], "import-x/internal-regex": "^(?:#|(?:@|~)\\/).*", "import-x/extensions": [".ts", ".tsx", ".js", ".jsx"], "import-x/parsers": { ...(typescript ? { "@typescript-eslint/parser": [".ts", ".tsx", ".cts", ".mts"], } : undefined), }, "import-x/resolver": { node: { extensions: [".ts", ".tsx", ".js", ".jsx"], }, ...(typescript ? { typescript: { alwaysTryTypes: true, projectService: parserOptions.projectService, }, } : undefined), }, }, rules: { // "import/consistent-type-specifier-style": ["error", "prefer-inline"], -- using ts/consistent-type-imports instead "import/default": "error", // "import/dynamic-import-chunkname": "off", "import/export": "error", // "import/exports-last": "off", // "import/extensions": "off", "import/first": "error", // "import/group-exports": "off", // "import/max-dependencies": [ // "off", // { // max: 10, // }, // ], "import/named": "error", "import/namespace": [ "error", { allowComputed: true, }, ], "import/no-absolute-path": "error", "import/no-amd": "error", // "import/no-anonymous-default-export": "off", // "import/no-commonjs": "off", // "import/no-cycle": "off", // "import/no-default-export": "off", // "import/no-deprecated": "warn", "import/no-duplicates": ["error", { "prefer-inline": true }], // "import/no-dynamic-require": "off", "import/no-empty-named-blocks": "error", // "import/no-internal-modules": "off", "import/no-mutable-exports": "error", // "import/no-named-as-default": "off", // "import/no-named-as-default-member": "off", "import/no-named-default": "error", // "import/no-named-export": "off", // "import/no-namespace": "off", // "import/no-nodejs-modules": "off", // "import/no-relative-parent-imports": "off", // "import/no-restricted-paths": "off", "import/no-self-import": "error", "import/no-unassigned-import": "error", // "import/no-unused-modules": "off", // "import/no-unresolved": "off", "import/no-useless-path-segments": [ "error", { commonjs: true, noUselessIndex: true, }, ], "import/no-webpack-loader-syntax": "error", // "import/prefer-default-export": "off", // "import/unambiguous": "off", "import/newline-after-import": [stylistic === false ? "off" : "error", { count: 1 }], "import/order": [ stylistic === false ? "off" : "error", { alphabetize: { caseInsensitive: false, order: "asc", }, groups: ["builtin", "external", "internal", "parent", "sibling", "index"], "newlines-between": "always", }, ], }, }, { files: [GLOB_MJS, GLOB_MTS], rules: { "import/no-commonjs": "error", "import/no-dynamic-require": "error", }, }, ...(typescript ? [ { files: [GLOB_TS, GLOB_TSX, GLOB_DTS], rules: { "import/no-unresolved": "off", "import/named": "off", "import/default": "off", "import/namespace": "off", "ts/no-import-type-side-effects": "error", "ts/consistent-type-imports": [ stylistic === false ? "off" : "error", { prefer: "type-imports", fixStyle: "inline-type-imports", disallowTypeAnnotations: false, }, ], }, }, ] : []), ...(options.mode === "library" ? [ { files: [GLOB_SRC], rules: { // Use non-sloppy imports. See: https://jsr.io/docs/publishing-packages#relative-imports "import/extensions": [ "error", "always", { checkTypeImports: true, ignorePackages: true, }, ], "import/no-useless-path-segments": ["error", { noUselessIndex: false }], "import/no-extraneous-dependencies": [ "error", { bundledDependencies: true, devDependencies: [ `?(.)*.${GLOB_SRC_EXT}`, `?(.)config?(s)/**`, "**/?(.)script?(s)/**", "**/spec/**", "**/test?(s)/**", "**/__mocks__/**", "**/__tests__/**", `**/*{.,_}{test,spec}.${GLOB_SRC_EXT}`, `**/Gruntfile{,.${GLOB_SRC_EXT}`, `**/gulpfile.${GLOB_SRC_EXT}`, `**/gulpfile.*.${GLOB_SRC_EXT}`, `**/jest.config.${GLOB_SRC_EXT}`, `**/jest.setup.${GLOB_SRC_EXT}`, `**/karma.conf.${GLOB_SRC_EXT}`, `**/postcss.config.${GLOB_SRC_EXT}`, `**/protractor.conf.${GLOB_SRC_EXT}`, `**/protractor.conf.*.${GLOB_SRC_EXT}`, `**/rollup.config.${GLOB_SRC_EXT}`, `**/rollup.config.*.${GLOB_SRC_EXT}`, `**/tailwind.config.${GLOB_SRC_EXT}`, `**/tailwind.config.*.${GLOB_SRC_EXT}`, `**/vite.config.${GLOB_SRC_EXT}`, `**/vitest.config.${GLOB_SRC_EXT}`, `**/vue.config.${GLOB_SRC_EXT}`, `**/webpack.config.${GLOB_SRC_EXT}`, `**/webpack.config.*.${GLOB_SRC_EXT}`, ], optionalDependencies: true, peerDependencies: true, }, ], }, }, ] : options.mode === "application" ? [ { files: [GLOB_SRC], rules: { "import/no-unassigned-import": [ "error", { allow: ["**/*.?(s)css"], }, ], }, }, ] : []), ]; } function inEditor() { return [ { name: "rs:in-editor", rules: { "no-console": "off", "no-debugger": "off", "prefer-const": "off", "sonar/no-all-duplicated-branches": "off", "sonar/no-collapsible-if": "off", "sonar/no-collection-size-mischeck": "off", "sonar/no-duplicated-branches": "off", "sonar/no-element-overwrite": "off", "sonar/no-empty-collection": "off", "sonar/no-extra-arguments": "off", "sonar/no-gratuitous-expressions": "off", "sonar/no-identical-conditions": "off", "sonar/no-identical-expressions": "off", "sonar/no-identical-functions": "off", "sonar/no-ignored-return": "off", "sonar/no-inverted-boolean-check": "off", "sonar/no-nested-switch": "off", "sonar/no-nested-template-literals": "off", "sonar/no-one-iteration-loop": "off", "sonar/no-redundant-boolean": "off", "sonar/no-redundant-jump": "off", "sonar/no-same-line-conditional": "off", "sonar/no-unused-collection": "off", "sonar/no-use-of-empty-return-value": "off", "sonar/no-useless-catch": "off", "sonar/non-existent-operator": "off", "sonar/prefer-immediate-return": "off", "sonar/prefer-object-literal": "off", "sonar/prefer-single-boolean-return": "off", "sonar/prefer-while": "off", "test/no-only-tests": "off", "unicorn/no-lonely-if": "off", }, }, ]; } const useNumberIsFinite = "Please use Number.isFinite instead"; const useNumberIsNan = "Please use Number.isNaN instead"; const useObjectDefineProperty = "Please use Object.defineProperty instead."; function javascript(options) { const { functionalEnforcement, overrides } = options; return [ { name: "rs:javascript", languageOptions: { ecmaVersion: "latest", globals: { ...globals.browser, ...globals.es2021, ...globals.node, document: "readonly", navigator: "readonly", window: "readonly", }, parserOptions: { ecmaFeatures: { jsx: true }, ecmaVersion: "latest", sourceType: "module", }, sourceType: "module", }, linterOptions: { reportUnusedDisableDirectives: true, }, rules: { "accessor-pairs": "error", "array-callback-return": "error", "arrow-body-style": ["error", "as-needed"], "block-scoped-var": "error", "class-methods-use-this": "error", "consistent-return": ["error", { treatUndefinedAsUnspecified: false }], "constructor-super": "error", curly: ["error", "all"], "default-case-last": "error", "dot-notation": ["error", { allowKeywords: true }], eqeqeq: ["error", "always", { null: "always" }], "for-direction": "error", "func-name-matching": "error", "func-names": ["error", "as-needed"], "func-style": ["error", "declaration", { allowArrowFunctions: true }], "getter-return": "error", "grouped-accessor-pairs": "error", "guard-for-in": "error", "logical-assignment-operators": "error", "max-depth": ["error", 10], "new-cap": [ "error", { capIsNew: false, newIsCap: true, properties: true, }, ], "no-alert": "error", "no-array-constructor": "error", "no-async-promise-executor": "error", "no-await-in-loop": "error", "no-caller": "error", "no-case-declarations": "error", "no-class-assign": "error", "no-compare-neg-zero": "error", "no-cond-assign": ["error", "always"], "no-console": ["error", { allow: ["warn", "error"] }], "no-const-assign": "error", "no-constant-binary-expression": "error", // "no-constant-condition": "error", "no-constructor-return": "error", "no-control-regex": "error", "no-debugger": "error", "no-delete-var": "error", "no-dupe-args": "error", "no-dupe-class-members": "error", "no-dupe-keys": "error", "no-duplicate-case": "error", "no-else-return": ["error", { allowElseIf: false }], "no-empty-pattern": "error", "no-empty": ["error", { allowEmptyCatch: true }], "no-eq-null": "error", "no-eval": "error", "no-ex-assign": "error", "no-extend-native": "error", "no-extra-bind": "error", "no-extra-boolean-cast": "error", "no-extra-label": "error", "no-fallthrough": "error", "no-func-assign": "error", "no-global-assign": "error", "no-implicit-coercion": "error", "no-implicit-globals": "error", "no-implied-eval": "error", "no-import-assign": "error", "no-inner-declarations": ["error", "functions"], "no-invalid-this": "error", "no-irregular-whitespace": "error", "no-iterator": "error", "no-label-var": "error", "no-labels": ["error", { allowLoop: true, allowSwitch: true }], "no-lone-blocks": "error", "no-lonely-if": "error", "no-loop-func": "error", "no-loss-of-precision": "error", "no-misleading-character-class": "error", "no-multi-assign": "error", "no-multi-str": "error", "no-new-func": "error", "no-object-constructor": "error", "no-new-native-nonconstructor": "error", "no-new-wrappers": "error", "no-new": "error", "no-obj-calls": "error", "no-octal-escape": "error", "no-octal": "error", "no-promise-executor-return": [ "error", { allowVoid: true, }, ], "no-proto": "error", "no-prototype-builtins": "error", "no-redeclare": ["error", { builtinGlobals: false }], "no-regex-spaces": "error", "no-restricted-globals": [ "error", { name: "event", message: "Use local parameter instead." }, { name: "global", message: "Use `globalThis` instead." }, { name: "self", message: "Use `globalThis` instead." }, { name: "window", message: "Use `globalThis` instead." }, ], "no-restricted-properties": [ "error", { property: "__proto__", message: "Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.", }, { property: "__lookupGetter__", message: "Use `Object.getOwnPropertyDescriptor` instead.", }, { property: "__lookupSetter__", message: "Use `Object.getOwnPropertyDescriptor` instead.", }, { object: "arguments", property: "callee", message: "arguments.callee is deprecated", }, { object: "global", property: "isFinite", message: useNumberIsFinite, }, { object: "self", property: "isFinite", message: useNumberIsFinite, }, { object: "window", property: "isFinite", message: useNumberIsFinite, }, { object: "global", property: "isNaN", message: useNumberIsNan, }, { object: "self",