UNPKG

@storm-software/eslint

Version:

⚡ A package containing the base ESLint configuration used by Storm Software across many projects.

1,710 lines (1,696 loc) 141 kB
import { ensurePackages, interopDefault, isInEditorEnv, isPackageInScope, parserPlain, renameRules } from "./chunk-SZFP4QOA.js"; import { getTsConfigPath } from "./chunk-D3EN5HD2.js"; import { banner_plugin_default } from "./chunk-TBQNKXDY.js"; import "./chunk-XNCBYAYL.js"; import { GLOB_ASTRO, GLOB_ASTRO_TS, GLOB_CSS, GLOB_EXCLUDE, GLOB_GRAPHQL, GLOB_HTML, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSX, GLOB_LESS, GLOB_MARKDOWN, GLOB_MARKDOWN_IN_MARKDOWN, GLOB_MDX, GLOB_POSTCSS, GLOB_SCSS, GLOB_SRC, GLOB_SRC_EXT, GLOB_SVG, GLOB_TESTS, GLOB_TOML, GLOB_TS, GLOB_TSX, GLOB_XML, GLOB_YAML } from "./chunk-SRHSKJB5.js"; import { findWorkspaceRoot } from "./chunk-7JRBTALJ.js"; import { joinPaths } from "./chunk-G7QVU75O.js"; import { __name } from "./chunk-SHUYVCID.js"; // src/preset.ts import { FlatConfigComposer } from "eslint-flat-config-utils"; import { isPackageExists as isPackageExists2 } from "local-pkg"; // src/configs/astro.ts async function astro(options = {}) { const { files = [ GLOB_ASTRO ], overrides = {}, stylistic: stylistic2 = true } = options; const [pluginAstro, parserAstro, parserTs] = await Promise.all([ interopDefault(import("eslint-plugin-astro")), interopDefault(import("astro-eslint-parser")), interopDefault(import("@typescript-eslint/parser")) ]); return [ { name: "storm/astro/setup", plugins: { astro: pluginAstro } }, { files, languageOptions: { globals: pluginAstro.environments.astro.globals, parser: parserAstro, parserOptions: { extraFileExtensions: [ ".astro" ], parser: parserTs }, sourceType: "module" }, name: "storm/astro/rules", processor: "astro/client-side-ts", rules: { // use recommended rules "astro/missing-client-only-directive-value": "error", "astro/no-conflict-set-directives": "error", "astro/no-deprecated-astro-canonicalurl": "error", "astro/no-deprecated-astro-fetchcontent": "error", "astro/no-deprecated-astro-resolve": "error", "astro/no-deprecated-getentrybyslug": "error", "astro/no-set-html-directive": "off", "astro/no-unused-define-vars-in-style": "error", "astro/semi": "off", "astro/valid-compile": "error", ...stylistic2 ? { "style/indent": "off", "style/jsx-closing-tag-location": "off", "style/jsx-one-expression-per-line": "off", "style/no-multiple-empty-lines": "off" } : {}, ...overrides } } ]; } __name(astro, "astro"); // src/configs/cspell.ts import cspellConfig from "@cspell/eslint-plugin/recommended"; async function cspell(options = {}) { const { configFile = "./.vscode/cspell.json", overrides = {} } = options; return [ { name: "storm/cspell/rules", ...cspellConfig, rules: { ...cspellConfig.rules, "@cspell/spellchecker": [ "warn", { configFile: joinPaths(findWorkspaceRoot(), configFile), generateSuggestions: true, numSuggestions: 10, autoFix: true } ], ...overrides } } ]; } __name(cspell, "cspell"); // src/configs/disables.ts async function disables() { return [ { files: [ `**/scripts/${GLOB_SRC}` ], name: "storm/disables/scripts", rules: { "no-console": "off", "ts/explicit-function-return-type": "off" } }, { files: [ `**/cli/${GLOB_SRC}`, `**/cli.${GLOB_SRC_EXT}` ], name: "storm/disables/cli", rules: { "no-console": "off" } }, { files: [ "**/*.d.?([cm])ts" ], name: "storm/disables/dts", rules: { "eslint-comments/no-unlimited-disable": "off", "import/no-duplicates": "off", "no-restricted-syntax": "off", "unused-imports/no-unused-vars": "off" } }, { files: [ "**/*.js", "**/*.cjs" ], name: "storm/disables/cjs", rules: { "ts/no-require-imports": "off" } }, { files: [ `**/*.config.${GLOB_SRC_EXT}`, `**/*.config.*.${GLOB_SRC_EXT}` ], name: "storm/disables/config-files", rules: { "no-console": "off", "ts/explicit-function-return-type": "off" } } ]; } __name(disables, "disables"); // src/configs/formatters.ts import defu from "defu"; // src/configs/stylistic.ts var StylisticConfigDefaults = { indent: 2, jsx: true, quotes: "double", semi: true }; async function stylistic(options = {}) { const { indent = 2, jsx: jsx2 = true, overrides = {}, quotes = "double", semi = true, lineEndings = "unix" } = { ...StylisticConfigDefaults, ...options }; const pluginStylistic = await interopDefault(import("@stylistic/eslint-plugin")); const config2 = pluginStylistic.configs.customize({ indent, jsx: jsx2, pluginName: "style", quotes, semi }); return [ { name: "storm/stylistic/rules", plugins: { style: pluginStylistic }, rules: { ...config2.rules, "style/lines-around-comment": "off", "style/linebreak-style": [ "error", lineEndings ], "style/comma-dangle": [ "error", "never" ], "style/comma-style": [ "error", "last" ], "style/quotes": [ "error", quotes ], "style/semi": [ "error", semi ? "always" : "never" ], "style/indent": [ "error", indent, { SwitchCase: 1 } ], "style/operator-linebreak": [ "error", "after", { overrides: { "=": "none", "?": "before", ":": "before" } } ], "style/jsx-indent": [ "error", indent ], "style/jsx-quotes": [ "error", quotes === "single" ? "prefer-single" : "prefer-double" ], "style/brace-style": [ "error", "1tbs", { allowSingleLine: false } ], ...overrides } } ]; } __name(stylistic, "stylistic"); // src/configs/formatters.ts function mergePrettierOptions(options, overrides = {}) { return { ...options, ...overrides, plugins: [ ...overrides.plugins || [], ...options.plugins || [] ] }; } __name(mergePrettierOptions, "mergePrettierOptions"); async function formatters(options = {}, stylistic2 = {}) { if (options === true) { const isPrettierPluginXmlInScope = isPackageInScope("@prettier/plugin-xml"); options = { astro: isPackageInScope("prettier-plugin-astro"), css: true, graphql: true, html: true, markdown: true, svg: isPrettierPluginXmlInScope, xml: isPrettierPluginXmlInScope }; } await ensurePackages([ "eslint-plugin-format", options.astro ? "prettier-plugin-astro" : void 0, options.xml || options.svg ? "@prettier/plugin-xml" : void 0 ]); const { indent = 2, quotes = "double", semi = true } = { ...StylisticConfigDefaults, ...stylistic2 }; const prettierOptions = defu({ proseWrap: "always", quoteProps: "preserve", bracketSameLine: true, bracketSpacing: true, arrowParens: "avoid", endOfLine: "lf", printWidth: 120, semi, singleQuote: quotes === "single", tabWidth: typeof indent === "number" ? indent : 2, trailingComma: "none", useTabs: indent === "tab" }, options.prettierOptions ?? {}); const prettierXmlOptions = { xmlQuoteAttributes: "double", xmlSelfClosingSpace: true, xmlSortAttributesByKey: false, xmlWhitespaceSensitivity: "ignore" }; const dprintOptions = defu({ indentWidth: typeof indent === "number" ? indent : 2, quoteStyle: quotes === "single" ? "preferSingle" : "preferDouble", useTabs: indent === "tab" }, options.dprintOptions ?? {}); const pluginFormat = await interopDefault(import("eslint-plugin-format")); const configs3 = [ { name: "storm/formatter/setup", plugins: { format: pluginFormat } } ]; if (options.css) { configs3.push({ files: [ GLOB_CSS, GLOB_POSTCSS ], languageOptions: { parser: parserPlain }, name: "storm/formatter/css", rules: { "format/prettier": [ "error", mergePrettierOptions(prettierOptions, { parser: "css" }) ] } }, { files: [ GLOB_SCSS ], languageOptions: { parser: parserPlain }, name: "storm/formatter/scss", rules: { "format/prettier": [ "error", mergePrettierOptions(prettierOptions, { parser: "scss" }) ] } }, { files: [ GLOB_LESS ], languageOptions: { parser: parserPlain }, name: "storm/formatter/less", rules: { "format/prettier": [ "error", mergePrettierOptions(prettierOptions, { parser: "less" }) ] } }); } if (options.html) { configs3.push({ files: [ GLOB_HTML ], languageOptions: { parser: parserPlain }, name: "storm/formatter/html", rules: { "format/prettier": [ "error", mergePrettierOptions(prettierOptions, { parser: "html" }) ] } }); } if (options.xml) { configs3.push({ files: [ GLOB_XML ], languageOptions: { parser: parserPlain }, name: "storm/formatter/xml", rules: { "format/prettier": [ "error", mergePrettierOptions({ ...prettierXmlOptions, ...prettierOptions }, { parser: "xml", plugins: [ "@prettier/plugin-xml" ] }) ] } }); } if (options.svg) { configs3.push({ files: [ GLOB_SVG ], languageOptions: { parser: parserPlain }, name: "storm/formatter/svg", rules: { "format/prettier": [ "error", mergePrettierOptions({ ...prettierXmlOptions, ...prettierOptions }, { parser: "xml", plugins: [ "@prettier/plugin-xml" ] }) ] } }); } if (options.markdown) { const formater = options.markdown === true ? "prettier" : options.markdown; configs3.push({ files: [ GLOB_MARKDOWN ], languageOptions: { parser: parserPlain }, name: "storm/formatter/markdown", rules: { [`format/${formater}`]: [ "error", formater === "prettier" ? mergePrettierOptions(prettierOptions, { embeddedLanguageFormatting: "off", parser: "markdown" }) : { ...dprintOptions, language: "markdown" } ] } }); } if (options.astro) { configs3.push({ files: [ GLOB_ASTRO ], languageOptions: { parser: parserPlain }, name: "storm/formatter/astro", rules: { "format/prettier": [ "error", mergePrettierOptions(prettierOptions, { parser: "astro", plugins: [ "prettier-plugin-astro" ] }) ] } }); configs3.push({ files: [ GLOB_ASTRO, GLOB_ASTRO_TS ], name: "storm/formatter/astro/disables", rules: { "style/arrow-parens": "off", "style/block-spacing": "off", "style/comma-dangle": "off", "style/indent": "off", "style/no-multi-spaces": "off", "style/quotes": "off", "style/semi": "off" } }); } if (options.graphql) { configs3.push({ files: [ GLOB_GRAPHQL ], languageOptions: { parser: parserPlain }, name: "storm/formatter/graphql", rules: { "format/prettier": [ "error", mergePrettierOptions(prettierOptions, { parser: "graphql" }) ] } }); } return configs3; } __name(formatters, "formatters"); // src/configs/graphql.ts async function graphql(options = {}) { const { relay = true, operations = true, schema = true, overrides = {} } = options; await ensurePackages([ "@graphql-eslint/eslint-plugin", "eslint-plugin-relay" ]); const [pluginGraphQL, pluginRelay] = await Promise.all([ interopDefault(import("@graphql-eslint/eslint-plugin")), interopDefault(import("eslint-plugin-relay")) ]); return [ { name: "storm/graphql/setup", files: [ "**/*.graphql", "**/*.gql" ], languageOptions: { parser: pluginGraphQL.parser }, plugins: { "@graphql-eslint": pluginGraphQL } }, { name: "storm/graphql/rules", plugins: { graphql: pluginGraphQL }, rules: { ...schema ? { "@graphql-eslint/description-style": "error", "@graphql-eslint/known-argument-names": "error", "@graphql-eslint/known-directives": "error", "@graphql-eslint/known-type-names": "error", "@graphql-eslint/lone-schema-definition": "error", "@graphql-eslint/naming-convention": [ "error", { types: "PascalCase", FieldDefinition: "camelCase", InputValueDefinition: "camelCase", Argument: "camelCase", DirectiveDefinition: "camelCase", EnumValueDefinition: "UPPER_CASE", "FieldDefinition[parent.name.value=Query]": { forbiddenPrefixes: [ "query", "get" ], forbiddenSuffixes: [ "Query" ] }, "FieldDefinition[parent.name.value=Mutation]": { forbiddenPrefixes: [ "mutation" ], forbiddenSuffixes: [ "Mutation" ] }, "FieldDefinition[parent.name.value=Subscription]": { forbiddenPrefixes: [ "subscription" ], forbiddenSuffixes: [ "Subscription" ] }, "EnumTypeDefinition,EnumTypeExtension": { forbiddenPrefixes: [ "Enum" ], forbiddenSuffixes: [ "Enum" ] }, "InterfaceTypeDefinition,InterfaceTypeExtension": { forbiddenPrefixes: [ "Interface" ], forbiddenSuffixes: [ "Interface" ] }, "UnionTypeDefinition,UnionTypeExtension": { forbiddenPrefixes: [ "Union" ], forbiddenSuffixes: [ "Union" ] }, "ObjectTypeDefinition,ObjectTypeExtension": { forbiddenPrefixes: [ "Type" ], forbiddenSuffixes: [ "Type" ] } } ], "@graphql-eslint/no-hashtag-description": "error", "@graphql-eslint/no-typename-prefix": "error", "@graphql-eslint/no-unreachable-types": "error", "@graphql-eslint/possible-type-extension": "error", "@graphql-eslint/provided-required-arguments": "error", "@graphql-eslint/require-deprecation-reason": "error", "@graphql-eslint/require-description": [ "error", { types: true, DirectiveDefinition: true, rootField: true } ], "@graphql-eslint/strict-id-in-types": "error", "@graphql-eslint/unique-directive-names": "error", "@graphql-eslint/unique-directive-names-per-location": "error", "@graphql-eslint/unique-enum-value-names": "error", "@graphql-eslint/unique-field-definition-names": "error", "@graphql-eslint/unique-operation-types": "error", "@graphql-eslint/unique-type-names": "error" } : {}, ...operations ? { "@graphql-eslint/executable-definitions": "error", "@graphql-eslint/fields-on-correct-type": "error", "@graphql-eslint/fragments-on-composite-type": "error", "@graphql-eslint/known-argument-names": "error", "@graphql-eslint/known-directives": "error", "@graphql-eslint/known-fragment-names": "error", "@graphql-eslint/known-type-names": "error", "@graphql-eslint/lone-anonymous-operation": "error", "@graphql-eslint/naming-convention": [ "error", { VariableDefinition: "camelCase", OperationDefinition: { style: "PascalCase", forbiddenPrefixes: [ "Query", "Mutation", "Subscription", "Get" ], forbiddenSuffixes: [ "Query", "Mutation", "Subscription" ] }, FragmentDefinition: { style: "PascalCase", forbiddenPrefixes: [ "Fragment" ], forbiddenSuffixes: [ "Fragment" ] } } ], "@graphql-eslint/no-anonymous-operations": "error", "@graphql-eslint/no-deprecated": "error", "@graphql-eslint/no-duplicate-fields": "error", "@graphql-eslint/no-fragment-cycles": "error", "@graphql-eslint/no-undefined-variables": "error", "@graphql-eslint/no-unused-fragments": "error", "@graphql-eslint/no-unused-variables": "error", "@graphql-eslint/one-field-subscriptions": "error", "@graphql-eslint/overlapping-fields-can-be-merged": "error", "@graphql-eslint/possible-fragment-spread": "error", "@graphql-eslint/provided-required-arguments": "error", "@graphql-eslint/require-selections": "error", "@graphql-eslint/scalar-leafs": "error", "@graphql-eslint/selection-set-depth": [ "error", { maxDepth: 7 } ], "@graphql-eslint/unique-argument-names": "error", "@graphql-eslint/unique-directive-names-per-location": "error", "@graphql-eslint/unique-fragment-name": "error", "@graphql-eslint/unique-input-field-names": "error", "@graphql-eslint/unique-operation-name": "error", "@graphql-eslint/unique-variable-names": "error", "@graphql-eslint/value-literals-of-correct-type": "error", "@graphql-eslint/variables-are-input-types": "error", "@graphql-eslint/variables-in-allowed-position": "error" } : {}, ...overrides } }, relay ? { name: "storm/graphql/relay", plugins: { relay: pluginRelay }, rules: { // errors "relay/graphql-syntax": "error", "relay/graphql-naming": "error", // warnings "relay/compat-uses-vars": "warn", "relay/generated-typescript-types": "warn", "relay/no-future-added-value": "warn", "relay/unused-fields": "warn", "relay/must-colocate-fragment-spreads": "warn", "relay/function-required-argument": "warn", "relay/hook-required-argument": "warn", // @graphql-eslint rules "@graphql-eslint/relay-arguments": "error", "@graphql-eslint/relay-connection-types": "error", "@graphql-eslint/relay-edge-types": "error", "@graphql-eslint/relay-page-info": "error" } } : {} ]; } __name(graphql, "graphql"); // src/configs/ignores.ts async function ignores(userIgnores = []) { return [ { ignores: [ ...GLOB_EXCLUDE, ...userIgnores ], name: "storm/ignores" } ]; } __name(ignores, "ignores"); // src/plugins.ts import { default as default2 } from "@cspell/eslint-plugin"; import { default as default3 } from "@nx/eslint-plugin/nx.js"; import * as pluginImport from "eslint-plugin-import-x"; import { default as default4 } from "eslint-plugin-n"; import { default as default5 } from "eslint-plugin-no-secrets"; import { default as default6 } from "eslint-plugin-perfectionist"; // ../../node_modules/.pnpm/eslint-plugin-pnpm@0.3.0_patch_hash=72dcde755c336eeca3e6170de1106fd85ecb66171e060788f80_c181f521aad000c211a6aa41fe87e6e0/node_modules/eslint-plugin-pnpm/dist/index.mjs import * as jsoncParser from "jsonc-eslint-parser"; import * as yamlParser from "yaml-eslint-parser"; import fs, { existsSync, readFileSync } from "node:fs"; import process from "node:process"; import { findUpSync } from "find-up-simple"; import { parsePnpmWorkspaceYaml } from "pnpm-workspace-yaml"; import { basename, normalize, resolve, dirname } from "pathe"; import { globSync } from "tinyglobby"; var name = "eslint-plugin-pnpm"; var version = "0.3.0"; var blobUrl = "https://github.com/antfu/eslint-plugin-pnpm/blob/main/src/rules/"; function RuleCreator(urlCreator) { return /* @__PURE__ */ __name(function createNamedRule({ name: name2, meta, ...rule }) { return createRule({ meta: { ...meta, docs: { ...meta.docs, url: urlCreator(name2) } }, ...rule }); }, "createNamedRule"); } __name(RuleCreator, "RuleCreator"); function createRule({ create, defaultOptions, meta }) { return { create: /* @__PURE__ */ __name((context) => { const optionsWithDefault = context.options.map((options, index) => { return { ...defaultOptions[index] || {}, ...options || {} }; }); return create(context, optionsWithDefault); }, "create"), defaultOptions, meta }; } __name(createRule, "createRule"); var createEslintRule = RuleCreator( (ruleName) => `${blobUrl}${ruleName}.test.ts` ); function getPackageJsonRootNode(context) { if (!context.filename.endsWith("package.json")) return; const ast = context.sourceCode.ast; const root = ast.body[0]; if (root.expression.type === "JSONObjectExpression") return root.expression; } __name(getPackageJsonRootNode, "getPackageJsonRootNode"); function* iterateDependencies(context, fields) { const root = getPackageJsonRootNode(context); if (!root) return; for (const fieldName of fields) { const path = fieldName.split("."); let node2 = root; for (let i = 0; i < path.length; i++) { const item = node2.properties.find((property) => property.key.type === "JSONLiteral" && property.key.value === path[i]); if (!item?.value || item.value.type !== "JSONObjectExpression") { node2 = void 0; break; } node2 = item.value; } if (!node2 || node2 === root) continue; for (const property of node2.properties) { if (property.value.type !== "JSONLiteral" || property.key.type !== "JSONLiteral") continue; if (typeof property.value.value !== "string") continue; const packageName = String(property.key.value); const specifier = String(property.value.value); yield { packageName, specifier, property }; } } } __name(iterateDependencies, "iterateDependencies"); function readPnpmWorkspace() { const filepath = findUpSync("pnpm-workspace.yaml", { cwd: process.cwd() }); if (!filepath) throw new Error("pnpm-workspace.yaml not found"); const content = fs.readFileSync(filepath, "utf-8"); const workspace2 = parsePnpmWorkspaceYaml(content); let queueTimer; const queue = []; const write = /* @__PURE__ */ __name(() => { fs.writeFileSync(filepath, workspace2.toString()); }, "write"); const hasQueue = /* @__PURE__ */ __name(() => queueTimer != null, "hasQueue"); const queueChange = /* @__PURE__ */ __name((fn, order) => { if (order === "pre") queue.unshift(fn); else queue.push(fn); if (queueTimer != null) clearTimeout(queueTimer); queueTimer = setTimeout(() => { queueTimer = void 0; const clone = [...queue]; queue.length = 0; for (const fn2 of clone) fn2(workspace2); if (workspace2.hasChanged()) write(); }, 1e3); }, "queueChange"); return { filepath, ...workspace2, hasQueue, queueChange }; } __name(readPnpmWorkspace, "readPnpmWorkspace"); var WORKSPACE_CACHE_TIME = 1e4; var workspaceLastRead; var workspace; function getPnpmWorkspace() { if (workspaceLastRead && workspace && !workspace.hasQueue() && Date.now() - workspaceLastRead > WORKSPACE_CACHE_TIME) { workspace = void 0; } if (!workspace) { workspace = readPnpmWorkspace(); workspaceLastRead = Date.now(); } return workspace; } __name(getPnpmWorkspace, "getPnpmWorkspace"); var RULE_NAME$4 = "json-enforce-catalog"; var DEFAULT_FIELDS$1 = [ "dependencies", "devDependencies" ]; var IGNORED_DEPENDENCIES = [ "typescript" ]; var enforceCatalog = createEslintRule({ name: RULE_NAME$4, meta: { type: "layout", docs: { description: 'Enforce using "catalog:" in `package.json`' }, fixable: "code", schema: [ { type: "object", properties: { allowedProtocols: { type: "array", description: "Allowed protocols in specifier to not be converted to catalog", items: { type: "string" } }, autofix: { type: "boolean", description: "Whether to autofix the linting error", default: true }, defaultCatalog: { type: "string", description: "Default catalog to use when moving version to catalog with autofix" }, reuseExistingCatalog: { type: "boolean", description: "Whether to reuse existing catalog when moving version to catalog with autofix", default: true }, conflicts: { type: "string", description: "Strategy to handle conflicts when adding packages to catalogs", enum: ["new-catalog", "overrides", "error"], default: "new-catalog" }, fields: { type: "array", description: "Fields to check for catalog", items: { type: "string" }, default: DEFAULT_FIELDS$1 }, ignore: { type: "array", description: "A list of dependencies to ignore", items: { type: "string" }, default: IGNORED_DEPENDENCIES } }, additionalProperties: false } ], messages: { expectCatalog: 'Expect to use catalog instead of plain specifier, got "{{specifier}}" for package "{{packageName}}".' } }, defaultOptions: [{}], create(context, [options]) { const { allowedProtocols = ["workspace", "link", "file"], defaultCatalog = "default", autofix = true, reuseExistingCatalog = true, conflicts = "new-catalog", fields = DEFAULT_FIELDS$1, ignore = IGNORED_DEPENDENCIES } = options || {}; for (const { packageName, specifier, property } of iterateDependencies(context, fields)) { if (ignore?.some((i) => i === packageName)) continue; if (specifier.startsWith("catalog:")) continue; if (allowedProtocols?.some((p) => specifier.startsWith(p))) continue; const workspace2 = getPnpmWorkspace(); if (!workspace2) return {}; let targetCatalog = reuseExistingCatalog ? workspace2.getPackageCatalogs(packageName)[0] || defaultCatalog : defaultCatalog; const resolvedConflicts = workspace2.hasSpecifierConflicts( targetCatalog, packageName, specifier ); let shouldFix = autofix; if (conflicts === "error") { if (resolvedConflicts.conflicts) { shouldFix = false; } } if (conflicts === "new-catalog" && resolvedConflicts.conflicts) { targetCatalog = resolvedConflicts.newCatalogName; } context.report({ node: property.value, messageId: "expectCatalog", data: { specifier, packageName }, fix: shouldFix ? (fixer) => { workspace2.queueChange(() => { workspace2.setPackage(targetCatalog, packageName, specifier); }); return fixer.replaceText( property.value, targetCatalog === "default" ? JSON.stringify("catalog:") : JSON.stringify(`catalog:${targetCatalog}`) ); } : void 0 }); } return {}; } }); var RULE_NAME$3 = "json-prefer-workspace-settings"; var preferWorkspaceSettings = createEslintRule({ name: RULE_NAME$3, meta: { type: "layout", docs: { description: "Prefer having pnpm settings in `pnpm-workspace.yaml` instead of `package.json`. This would requires pnpm v10.6+, see https://github.com/orgs/pnpm/discussions/9037." }, fixable: "code", schema: [ { type: "object", properties: { autofix: { type: "boolean", description: "Whether to autofix the linting error", default: true } }, additionalProperties: false } ], messages: { unexpectedPnpmSettings: "Unexpected pnpm settings in package.json, should move to pnpm-workspace.yaml" } }, defaultOptions: [{}], create(context, [options = {}]) { const { autofix = true } = options || {}; const root = getPackageJsonRootNode(context); if (!root) return {}; const pnpmNode = root.properties.find((property) => property.key.type === "JSONLiteral" && property.key.value === "pnpm"); if (!pnpmNode) return {}; const workspace2 = getPnpmWorkspace(); if (!workspace2) return {}; context.report({ node: pnpmNode, messageId: "unexpectedPnpmSettings", fix: autofix ? (fixer) => { const json = JSON.parse(context.sourceCode.text); const pnpmSettings = json.pnpm; const flatValueParis = []; function traverse(value, paths) { if (typeof value === "object" && value !== null && !Array.isArray(value)) { for (const key in value) { traverse(value[key], [...paths, key]); } } else { flatValueParis.push([paths, value]); } } __name(traverse, "traverse"); traverse(pnpmSettings, []); workspace2.queueChange(() => { for (const [paths, value] of flatValueParis) { workspace2.setPath(paths, value); } }); let start = pnpmNode.range[0]; let end = pnpmNode.range[1]; const before = context.sourceCode.getTokenBefore(pnpmNode); if (before) start = before.range[1]; const after = context.sourceCode.getTokenAfter(pnpmNode); if (after?.type === "Punctuator" && after.value === ",") end = after.range[1]; return fixer.removeRange([start, end]); } : void 0 }); return {}; } }); var RULE_NAME$2 = "json-valid-catalog"; var DEFAULT_FIELDS = [ "dependencies", "devDependencies", "optionalDependencies", "peerDependencies", "resolutions", "overrides", "pnpm.overrides" ]; var validCatalog = createEslintRule({ name: RULE_NAME$2, meta: { type: "layout", docs: { description: "Enforce using valid catalog in `package.json`" }, fixable: "code", schema: [ { type: "object", properties: { autoInsert: { type: "boolean", description: "Whether to auto insert to catalog if missing", default: true }, autoInsertDefaultSpecifier: { type: "string", description: "Default specifier to use when auto inserting to catalog", default: "^0.0.0" }, autofix: { type: "boolean", description: "Whether to autofix the linting error", default: true }, enforceNoConflict: { type: "boolean", description: "Whether to enforce no conflicts when adding packages to catalogs (will create version-specific catalogs)", default: true }, fields: { type: "array", description: "Fields to check for catalog", default: DEFAULT_FIELDS } }, additionalProperties: false } ], messages: { invalidCatalog: 'Catalog "{{specifier}}" for package "{{packageName}}" is not defined in `pnpm-workspace.yaml`.' } }, defaultOptions: [{}], create(context, [options = {}]) { const { autoInsert = true, autofix = true, autoInsertDefaultSpecifier = "^0.0.0", fields = DEFAULT_FIELDS } = options || {}; for (const { packageName, specifier, property } of iterateDependencies(context, fields)) { if (!specifier.startsWith("catalog:")) continue; const workspace2 = getPnpmWorkspace(); if (!workspace2) return {}; const currentCatalog = specifier.replace(/^catalog:/, "").trim() || "default"; const existingCatalogs = workspace2.getPackageCatalogs(packageName); if (!existingCatalogs.includes(currentCatalog)) { context.report({ node: property.value, messageId: "invalidCatalog", data: { specifier, packageName }, fix: !autofix || !autoInsert && !existingCatalogs.length ? void 0 : (fixer) => { let catalog = existingCatalogs[0]; if (!catalog && autoInsert) { catalog = currentCatalog; workspace2.queueChange(() => { workspace2.setPackage(catalog, packageName, autoInsertDefaultSpecifier); }, "pre"); } return fixer.replaceText( property.value, catalog === "default" ? JSON.stringify("catalog:") : JSON.stringify(`catalog:${catalog}`) ); } }); } } return {}; } }); var rules$2 = { "json-enforce-catalog": enforceCatalog, "json-valid-catalog": validCatalog, "json-prefer-workspace-settings": preferWorkspaceSettings }; var RULE_NAME$1 = "yaml-no-duplicate-catalog-item"; var noDuplicateCatalogItem = createEslintRule({ name: RULE_NAME$1, meta: { type: "problem", docs: { description: "Disallow unused catalogs in `pnpm-workspace.yaml`" }, fixable: "code", schema: [ { type: "object", properties: { allow: { type: "array", items: { type: "string" } } }, additionalProperties: false } ], messages: { duplicateCatalogItem: 'Catalog item "{{name}}" is already defined in the "{{existingCatalog}}" catalog. You may want to remove one of them.' } }, defaultOptions: [{}], create(context, [options = {}]) { if (basename(context.filename) !== "pnpm-workspace.yaml") return {}; const workspace2 = getPnpmWorkspace(); if (!workspace2 || normalize(workspace2.filepath) !== normalize(context.filename)) return {}; if (workspace2.hasChanged() || workspace2.hasQueue()) return {}; const { allow = [] } = options; workspace2.setContent(context.sourceCode.text); const json = workspace2.toJSON() || {}; const exists = /* @__PURE__ */ new Map(); const catalogs = { ...json.catalogs, default: json.catalog }; const doc = workspace2.getDocument(); for (const [catalog, object] of Object.entries(catalogs)) { if (!object) continue; for (const key of Object.keys(object)) { if (allow.includes(key)) continue; if (exists.has(key)) { const existingCatalog = exists.get(key); const node2 = doc.getIn(catalog === "default" ? ["catalog", key] : ["catalogs", catalog, key], true); const start = context.sourceCode.getLocFromIndex(node2.range[0]); const end = context.sourceCode.getLocFromIndex(node2.range[1]); context.report({ loc: { start, end }, messageId: "duplicateCatalogItem", data: { name: key, currentCatalog: catalog, existingCatalog } }); } else { exists.set(key, catalog); } } } return {}; } }); var RULE_NAME = "yaml-no-unused-catalog-item"; var noUnusedCatalogItem = createEslintRule({ name: RULE_NAME, meta: { type: "problem", docs: { description: "Disallow unused catalogs in `pnpm-workspace.yaml`" }, fixable: "code", schema: [], messages: { unusedCatalogItem: 'Catalog item "{{catalogItem}}" is not used in any package.json.' } }, defaultOptions: [], create(context) { if (basename(context.filename) !== "pnpm-workspace.yaml") return {}; const workspace2 = getPnpmWorkspace(); if (!workspace2 || normalize(workspace2.filepath) !== normalize(context.filename)) return {}; if (workspace2.hasChanged() || workspace2.hasQueue()) return {}; workspace2.setContent(context.sourceCode.text); const parsed = workspace2.toJSON() || {}; const root = resolve(dirname(context.filename)); const entries = /* @__PURE__ */ new Map(); const doc = workspace2.getDocument(); const catalogs = { default: doc.getIn(["catalog"]) }; for (const item of doc.getIn(["catalogs"])?.items || []) catalogs[String(item.key)] = item.value; for (const [catalog, map] of Object.entries(catalogs)) { if (!map) continue; for (const item of map.items) { entries.set(`${String(item.key)}:${catalog}`, item); } } if (entries.size === 0) return {}; const dirs = parsed.packages ? globSync(parsed.packages, { cwd: root, dot: false, ignore: [ "**/node_modules/**", "**/dist/**", "**/build/**", "**/dist/**", "**/dist/**" ], absolute: true, expandDirectories: false, onlyDirectories: true }) : []; dirs.push(root); const packages = dirs.map((dir) => resolve(dir, "package.json")).filter((x) => existsSync(x)).sort(); const FIELDS = [ "dependencies", "devDependencies", "peerDependencies", "optionalDependencies", "overrides", "resolutions", "pnpm.overrides" ]; for (const path of packages) { const pkg = JSON.parse(readFileSync(path, "utf-8")); for (const field of FIELDS) { const map = getObjectPath(pkg, field.split(".")); if (!map) continue; for (const [name2, value] of Object.entries(map)) { if (!value.startsWith("catalog:")) continue; const catalog = value.slice(8) || "default"; const key = `${name2}:${catalog}`; entries.delete(key); } } } if (entries.size > 0) { for (const [key, value] of Array.from(entries.entries()).sort((a, b) => a[0].localeCompare(b[0]))) { const start = context.sourceCode.getLocFromIndex(value.key.range[0]); const end = context.sourceCode.getLocFromIndex(value.value.range.at(-1)); context.report({ loc: { start, end }, messageId: "unusedCatalogItem", data: { catalogItem: key } }); } } return {}; } }); function getObjectPath(obj, path) { let current = obj; for (const key of path) { current = current[key]; if (!current) return void 0; } return current; } __name(getObjectPath, "getObjectPath"); var rules$1 = { "yaml-no-unused-catalog-item": noUnusedCatalogItem, "yaml-no-duplicate-catalog-item": noDuplicateCatalogItem }; var rules = { ...rules$2, ...rules$1 }; var plugin = { meta: { name, version }, rules }; var configsJson = [ { name: "pnpm/package.json", files: [ "package.json", "**/package.json" ], languageOptions: { parser: jsoncParser }, plugins: { pnpm: plugin }, rules: { "pnpm/json-enforce-catalog": "error", "pnpm/json-valid-catalog": "error", "pnpm/json-prefer-workspace-settings": "error" } } ]; var configsYaml = [ { name: "pnpm/pnpm-workspace-yaml", files: ["pnpm-workspace.yaml"], languageOptions: { parser: yamlParser }, plugins: { pnpm: plugin }, rules: { "pnpm/yaml-no-unused-catalog-item": "error", "pnpm/yaml-no-duplicate-catalog-item": "error" } } ]; var configs = { recommended: [ ...configsJson // Yaml support is still experimental // ...configsYaml, ], json: configsJson, yaml: configsYaml }; plugin.configs = configs; // src/plugins.ts import { default as default7 } from "eslint-plugin-prettier"; import { default as default8 } from "eslint-plugin-unicorn"; import { default as default9 } from "eslint-plugin-unused-imports"; // src/configs/imports.ts async function imports(options = {}) { const { stylistic: stylistic2 = true } = options; return [ { name: "storm/imports/rules", plugins: { import: pluginImport }, rules: { "import/consistent-type-specifier-style": [ "error", "prefer-top-level" ], "import/first": "off", "import/no-duplicates": "error", "import/no-mutable-exports": "error", "import/no-named-default": "error", "import/no-self-import": "error", "import/no-webpack-loader-syntax": "error", ...stylistic2 ? { "import/newline-after-import": [ "error", { count: 1 } ] } : {} } } ]; } __name(imports, "imports"); // src/configs/javascript.ts import defu2 from "defu"; import globalsLib from "globals"; async function javascript(options = {}) { const { lineEndings = "unix", overrides = {}, repositoryName, globals = {} } = options; return [ { name: "storm/javascript/setup", languageOptions: { ecmaVersion: 2022, globals: defu2(globals, { ...globalsLib.browser, ...globalsLib.es2021, ...globalsLib.node, document: "readonly", navigator: "readonly", window: "readonly" }), parserOptions: { ecmaFeatures: { jsx: true }, ecmaVersion: 2022 } }, linterOptions: { reportUnusedDisableDirectives: true } }, { // Banner ...banner_plugin_default.configs?.["recommended"], name: "storm/javascript/banner", plugins: { banner: banner_plugin_default }, rules: { "banner/banner": [ "error", { commentType: "block", numNewlines: 2, repositoryName, lineEndings } ] } }, { name: "storm/javascript/rules", plugins: { "unused-imports": default9 }, rules: { // disallow use of console "no-console": "error", // Disallows expressions where the operation doesn't affect the value // https://eslint.org/docs/rules/no-constant-binary-expression // TODO: semver-major, enable "no-constant-binary-expression": "off", // disallow use of constant expressions in conditions "no-constant-condition": "warn", // disallow control characters in regular expressions "no-control-regex": "error", // disallow use of debugger "no-debugger": "warn", // disallow duplicate arguments in functions "no-dupe-args": "error", // Disallow duplicate conditions in if-else-if chains // https://eslint.org/docs/rules/no-dupe-else-if "no-dupe-else-if": "error", // disallow duplicate keys when creating object literals "no-dupe-keys": "error", // disallow a duplicate case label. "no-duplicate-case": "error", // disallow empty statements "no-empty": "error", // disallow the use of empty character classes in regular expressions "no-empty-character-class": "error", // disallow assigning to the exception in a catch block "no-ex-assign": "error", // disallow double-negation boolean casts in a boolean context // https://eslint.org/docs/rules/no-extra-boolean-cast "no-extra-boolean-cast": "error", // disallow unnecessary parentheses // https://eslint.org/docs/rules/no-extra-parens "no-extra-parens": [ "off", "all", { conditionalAssign: true, nestedBinaryExpressions: false, returnAssign: false, ignoreJSX: "all", enforceForArrowConditionals: false } ], // disallow unnecessary semicolons "no-extra-semi": "error", // disallow overwriting functions written as function declarations "no-func-assign": "off", // https://eslint.org/docs/rules/no-import-assign "no-import-assign": "error", // disallow function or variable declarations in nested blocks "no-inner-declarations": "warn", // disallow invalid regular expression strings in the RegExp constructor "no-invalid-regexp": "error", // disallow irregular whitespace outside of strings and comments "no-irregular-whitespace": "error", // Disallow Number Literals That Lose Precision // https://eslint.org/docs/rules/no-loss-of-precision "no-loss-of-precision": "error", // Disallow characters which are made with multiple code points in character class syntax // https://eslint.org/docs/rules/no-misleading-character-class "no-misleading-character-class": "error", // disallow the use of object properties of the global object (Math and JSON) as functions "no-obj-calls": "error", // Disallow new operators with global non-constructor functions // https://eslint.org/docs/latest/rules/no-new-native-nonconstructor // TODO: semver-major, enable "no-new-native-nonconstructor": "off", // Disallow returning values from Promise executor functions // https://eslint.org/docs/rules/no-promise-executor-return "no-promise-executor-return": "error", // disallow use of Object.prototypes builtins directly // https://eslint.org/docs/rules/no-prototype-builtins "no-prototype-builtins": "error", // disallow multiple spaces in a regular expression literal "no-regex-spaces": "error", // Disallow returning values from setters // https://eslint.org/docs/rules/no-setter-return "no-setter-return": "error", // disallow sparse arrays "no-sparse-arrays": "error", // Disallow template literal placeholder syntax in regular strings // https://eslint.org/docs/rules/no-template-curly-in-string "no-template-curly-in-string": "error", // Avoid code that looks like two expressions but is actually one // https://eslint.org/docs/rules/no-unexpected-multiline "no-unexpected-multiline": "error", // disallow unreachable statements after a return, throw, continue, or break statement "no-unreachable": "error", // Disallow loops with a body that allows only one iteration // https://eslint.org/docs/rules/no-unreachable-loop "no-unreachable-loop": [ "error", { ignore: [] // WhileStatement, DoWhileStatement, ForStatement, ForInStatement, ForOfStatement } ], // disallow return/throw/break/continue inside finally blocks // https://eslint.org/docs/rules/no-unsafe-finally "no-unsafe-finally": "error", // disallow negating the left operand of relational operators // https://eslint.org/docs/rules/no-unsafe-negation "no-unsafe-negation": "error", // disallow use of optional chaining in contexts where the undefined value is not allowed // https://eslint.org/docs/rules/no-unsafe-optional-chaining "no-unsafe-optional-chaining": [ "error", { disallowArithmeti