From da9c4d70fef17df379be142d06ce248ae7e25236 Mon Sep 17 00:00:00 2001 From: Daniel Bannert Date: Mon, 17 Aug 2026 13:27:47 +0200 Subject: [PATCH 1/2] fix(lint-staged-config): stop mutating the exported extension defaults `defineConfig` seeded its defaults with the exported `eslintExtensions`, `typescriptExtensions` and `stylesheetsExtensions` tuples by reference, then pushed "md" onto the ESLint one when no markdownlint CLI was present. The push mutated the exported constant, so a second `defineConfig()` call in the same process saw "md" already there and appended it again. The defaults are now copied per call. The existing test did not catch this because it built its expectation from the same mutated array, comparing the damage against itself. It now spells out the appended "md" explicitly. Each option was also read as `config.eslint as EslintConfig` at every use, which covered up that the value is `EslintConfig | false | undefined`. With the option unset the assertion produced a property read on `undefined` and a TypeError rather than the intended "extensions option is required" error. Each option is now narrowed once into a local. `getNearestConfigPath` asserted `startsWith("/")` results into the `` `/${string}` `` template literal type at four call sites. That check is now an `isAbsolutePath` predicate, so the narrowing happens in the type system. Two assertions remain, each documented: `Join` cannot be derived from `path.join`, and the package root is an ancestor of `cwd` so it cannot be proven to be the same `A`. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014FP75FgnREe4L45kZtsa9a --- .../__tests__/index.test.ts | 5 +- .../src/eslint/create-eslint-commands.ts | 14 ++-- packages/lint-staged-config/src/index.ts | 73 ++++++++++--------- .../src/utils/get-nearest-config-path.ts | 23 +++++- 4 files changed, 70 insertions(+), 45 deletions(-) diff --git a/packages/lint-staged-config/__tests__/index.test.ts b/packages/lint-staged-config/__tests__/index.test.ts index 87416731e..47cd349dc 100644 --- a/packages/lint-staged-config/__tests__/index.test.ts +++ b/packages/lint-staged-config/__tests__/index.test.ts @@ -45,9 +45,12 @@ describe(defineConfig, () => { }); findPackageManagerSyncMock.mockReturnValue({ packageManager: "npm" }); + // "md" is appended because neither markdownlint CLI is installed in this fixture. The + // expectation used to read `eslintExtensions` directly and passed only because the + // implementation mutated that exported array in place. expect(defineConfig()).toStrictEqual({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- expect.any returns any - [`**/*.{${eslintExtensions.join(",")}}`]: expect.any(Function), + [`**/*.{${[...eslintExtensions, "md"].join(",")}}`]: expect.any(Function), }); }); diff --git a/packages/lint-staged-config/src/eslint/create-eslint-commands.ts b/packages/lint-staged-config/src/eslint/create-eslint-commands.ts index a8dd09200..836452fdf 100644 --- a/packages/lint-staged-config/src/eslint/create-eslint-commands.ts +++ b/packages/lint-staged-config/src/eslint/create-eslint-commands.ts @@ -3,7 +3,7 @@ import { hasPackageJsonAnyDependency } from "@visulima/package"; import { ESLint } from "eslint"; import type { EslintConfig } from "../types"; -import getNearestConfigPath from "../utils/get-nearest-config-path"; +import getNearestConfigPath, { isAbsolutePath } from "../utils/get-nearest-config-path"; import groupFilePathsByDirectoryName from "./group-file-paths-by-directory-name"; import removeIgnoredFiles from "./remove-ignored-files"; @@ -109,11 +109,13 @@ const createEslintCommands = async ( } try { - config = getNearestConfigPath( - configName, - // eslint-disable-next-line no-template-curly-in-string - filePaths[0] as "/${string}", - ); + const [firstPath] = filePaths; + + if (firstPath === undefined || !isAbsolutePath(firstPath)) { + return; + } + + config = getNearestConfigPath(configName, firstPath); } catch { // Ignore } diff --git a/packages/lint-staged-config/src/index.ts b/packages/lint-staged-config/src/index.ts index be64dacb0..fb9c0a3c6 100644 --- a/packages/lint-staged-config/src/index.ts +++ b/packages/lint-staged-config/src/index.ts @@ -5,7 +5,7 @@ import type { Configuration } from "lint-staged"; import type { EslintConfig } from "./types"; import concatFiles from "./utils/concat-files"; -import getNearestConfigPath from "./utils/get-nearest-config-path"; +import getNearestConfigPath, { isAbsolutePath } from "./utils/get-nearest-config-path"; interface StylesheetsConfig { extensions?: (typeof stylesheetsExtensions)[number][]; @@ -34,18 +34,17 @@ export const defineConfig = ( } = {}, // eslint-disable-next-line sonarjs/cognitive-complexity ): Configuration => { + // Copies, not the exported tuples themselves: the ESLint branch below pushes "md" onto this + // array, which would otherwise mutate the shared constant for every subsequent call. + const defaultEslint: EslintConfig = { extensions: [...eslintExtensions] }; + const defaultStylesheets: StylesheetsConfig = { extensions: [...stylesheetsExtensions] }; + const defaultTypescript: TypescriptConfig = { exclude: [], extensions: [...typescriptExtensions] }; + const config = { debug: false, - eslint: { - extensions: eslintExtensions, - }, - stylesheets: { - extensions: stylesheetsExtensions, - }, - typescript: { - exclude: [], - extensions: typescriptExtensions, - }, + eslint: defaultEslint, + stylesheets: defaultStylesheets, + typescript: defaultTypescript, ...options, }; const cwd = config.cwd ?? process.cwd(); @@ -69,23 +68,29 @@ export const defineConfig = ( const hasPrettier = hasPackageJsonAnyDependency(packageJson, ["prettier"]); + // `Extract` here selects lint-staged's object task-map from its config union; it is a union + // filter rather than a dictionary contract, which is what the rule is guarding against. + // eslint-disable-next-line @typescript-eslint/no-restricted-types -- see comment above let loadedPlugins: Extract> = {}; if (config.eslint !== false && hasPackageJsonAnyDependency(packageJson, ["eslint"])) { - if (!Array.isArray((config.eslint as EslintConfig).extensions) || ((config.eslint as EslintConfig).extensions as string[]).length === 0) { + const eslintConfig: EslintConfig = config.eslint; + const { extensions } = eslintConfig; + + if (!Array.isArray(extensions) || extensions.length === 0) { throw new Error("The `extensions` option is required for the ESLint configuration."); } if (!hasMarkdownCli && !hasMarkdownCli2) { - ((config.eslint as EslintConfig).extensions as string[]).push("md"); + extensions.push("md"); } - loadedPlugins[`**/*.{${((config.eslint as EslintConfig).extensions as string[]).join(",")}}`] = async (filenames: ReadonlyArray) => { + loadedPlugins[`**/*.{${extensions.join(",")}}`] = async (filenames: ReadonlyArray) => { const { default: createEslintCommands } = await import("./eslint/create-eslint-commands"); return [ ...(hasPrettier ? [`${packageManager} exec prettier --write ${concatFiles(filenames)}`] : []), - ...(await createEslintCommands(packageManager, packageJson, config.eslint as EslintConfig, filenames)), + ...(await createEslintCommands(packageManager, packageJson, eslintConfig, filenames)), ]; }; } @@ -113,47 +118,45 @@ export const defineConfig = ( } if (config.stylesheets !== false && hasPackageJsonAnyDependency(packageJson, ["stylelint"])) { - if ( - !Array.isArray((config.stylesheets as StylesheetsConfig).extensions) || - ((config.stylesheets as StylesheetsConfig).extensions as string[]).length === 0 - ) { + const stylesheetsConfig: StylesheetsConfig = config.stylesheets; + const { extensions } = stylesheetsConfig; + + if (!Array.isArray(extensions) || extensions.length === 0) { throw new Error("The `extensions` option is required for the Stylesheets configuration."); } - loadedPlugins[`**/*.{${((config.stylesheets as StylesheetsConfig).extensions as string[]).join(",")}}`] = (filenames: ReadonlyArray) => [ + loadedPlugins[`**/*.{${extensions.join(",")}}`] = (filenames: ReadonlyArray) => [ ...(hasPrettier ? [`${packageManager} exec prettier --ignore-unknown --write ${concatFiles(filenames)}`] : []), `${packageManager} exec stylelint --fix`, ]; } if (config.typescript !== false && hasPackageJsonAnyDependency(packageJson, ["typescript"])) { - if ( - !Array.isArray((config.typescript as TypescriptConfig).extensions) || - ((config.typescript as TypescriptConfig).extensions as string[]).length === 0 - ) { + const typescriptConfig: TypescriptConfig = config.typescript; + const { extensions } = typescriptConfig; + + if (!Array.isArray(extensions) || extensions.length === 0) { throw new Error("The `extensions` option is required for the TypeScript configuration."); } - loadedPlugins[`**/*.{${((config.typescript as TypescriptConfig).extensions as string[]).join(",")}}`] = ( + loadedPlugins[`**/*.{${extensions.join(",")}}`] = ( filenames: ReadonlyArray, ): string[] => { const commands = new Set(); filenames.forEach((filePath) => { - if (typeof (config.typescript as TypescriptConfig).exclude === "object" && Array.isArray((config.typescript as TypescriptConfig).exclude)) { - const isExcluded = ((config.typescript as TypescriptConfig).exclude as string[]).some((value) => filePath.includes(value)); + const { exclude } = typescriptConfig; - if (isExcluded) { - return; - } + if (Array.isArray(exclude) && exclude.some((value) => filePath.includes(value))) { + return; + } + + if (!isAbsolutePath(filePath)) { + return; } try { - const tsconfigPath = getNearestConfigPath( - "tsconfig.json", - // eslint-disable-next-line no-template-curly-in-string - filePath as "/${string}", - ) as string; + const tsconfigPath = getNearestConfigPath("tsconfig.json", filePath); commands.add(`${packageManager} exec tsc --noEmit --project ${tsconfigPath}`); } catch (error) { diff --git a/packages/lint-staged-config/src/utils/get-nearest-config-path.ts b/packages/lint-staged-config/src/utils/get-nearest-config-path.ts index 8ed156b0f..0836d89e1 100644 --- a/packages/lint-staged-config/src/utils/get-nearest-config-path.ts +++ b/packages/lint-staged-config/src/utils/get-nearest-config-path.ts @@ -11,11 +11,21 @@ const packageDirectorySync = (cwd?: string) => { return filePath && dirname(filePath); }; +/** + * Narrows a string to an absolute path. + * + * `startsWith` does not narrow a `string` to the `` `/${string}` `` template literal type on its + * own, so the check is expressed as a predicate rather than asserted away at each call site. + * @param value Any path-like string. + * @returns True when the path is absolute. + */ +const isAbsolutePath = (value: string): value is AbsolutePath => value.startsWith("/"); + const getNearestPackageRootPath = (cwd?: string): AbsolutePath => { const packageDirectoryPath = packageDirectorySync(cwd ?? process.cwd()); - if (packageDirectoryPath?.startsWith("/")) { - return packageDirectoryPath as AbsolutePath; + if (packageDirectoryPath !== undefined && isAbsolutePath(packageDirectoryPath)) { + return packageDirectoryPath; } throw new Error(`Cannot determine the nearest root of the package for the file: ${cwd ?? "unknown"}!`); @@ -24,7 +34,9 @@ const getNearestPackageRootPath = (cwd?: string): AbsolutePath => { const joinPaths = >(paths: T): Join => { const joined = join(...paths); - if (joined.startsWith("/")) { + if (isAbsolutePath(joined)) { + // `Join` collapses to a template literal type the checker cannot derive from `path.join`. + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- see comment above return joined as Join; } @@ -33,6 +45,10 @@ const joinPaths = >(paths: T): Join => { const getNearestConfigPath = (fileName: N, cwd?: A): ConfigPath => { const packageRootPath = getNearestPackageRootPath(cwd); + + // The package root is an ancestor of `cwd`, so it cannot be proven to be the same `A`. The + // generic overstates what this function knows; narrowing it would change the public signature. + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- see comment above const configPath = joinPaths<[A, N]>([packageRootPath as A, fileName]); if (isAccessibleSync(configPath)) { @@ -42,4 +58,5 @@ const getNearestConfigPath = Date: Mon, 17 Aug 2026 13:35:56 +0200 Subject: [PATCH 2/2] fix(deps): sync the lockfile with the released eslint-config bump The @anolilab/eslint-config 29.0.0 release repointed lint-staged-config's manifest at the new version but left pnpm-lock.yaml recording 28.1.3. Every job on main therefore dies at `pnpm install --frozen-lockfile`: [ERR_PNPM_OUTDATED_LOCKFILE] Cannot install with "frozen-lockfile" because pnpm-lock.yaml is not up to date with /packages/lint-staged-config/package.json - @anolilab/eslint-config (lockfile: 28.1.3, manifest: 29.0.0) Regenerate it. Verified with the same frozen install CI runs. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_014FP75FgnREe4L45kZtsa9a --- pnpm-lock.yaml | 191 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 2 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 61a7a9fa3..6df0176be 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1178,8 +1178,8 @@ importers: version: 5.8.0 devDependencies: '@anolilab/eslint-config': - specifier: 28.1.3 - version: 28.1.3(c3bc49b65831bdab74607c7c3b1cf763) + specifier: 29.0.0 + version: 29.0.0(c3bc49b65831bdab74607c7c3b1cf763) '@anolilab/prettier-config': specifier: 10.0.3 version: 10.0.3(prettier@3.9.6) @@ -1730,6 +1730,94 @@ packages: typescript: optional: true + '@anolilab/eslint-config@29.0.0': + resolution: {integrity: sha512-lTAErWRWF1VXMf4F4vYgB+6HfcPnMoOP7Dl3fwyOBu/YU3uJ/Il8HK1Qjv+p228NlUitStPvBrbI4gWGQVCytw==} + engines: {node: '>=22.12.0 <=25.*'} + peerDependencies: + '@eslint-react/eslint-plugin': 5.18.3 + '@eslint/css': 1.4.0 + '@ospm/eslint-plugin-react-unhookify': ^1.0.2 + '@tanstack/eslint-plugin-query': 5.101.4 + '@tanstack/eslint-plugin-router': 1.162.0 + '@unocss/eslint-plugin': 66.7.5 + astro-eslint-parser: 3.0.0 + eslint: '>=9.39.5' + eslint-plugin-astro: 3.1.0 + eslint-plugin-format: '>=2.0.1' + eslint-plugin-jsx-a11y: ^6.10.2 + eslint-plugin-oxlint: 1.77.0 + eslint-plugin-playwright: ^0.16.0 || ^0.18.0 || ^2.0.0 + eslint-plugin-react: ^7.37.5 + eslint-plugin-react-compiler: ^19.1.0-rc.2 + eslint-plugin-react-hooks: 7.1.1 + eslint-plugin-react-perf: ^3.3.3 + eslint-plugin-react-refresh: 0.5.3 + eslint-plugin-react-you-might-not-need-an-effect: 1.0.1 + eslint-plugin-storybook: 10.5.3 + eslint-plugin-tailwindcss: 4.2.0 + eslint-plugin-testing-library: 7.16.2 + eslint-plugin-tsdoc: 0.5.2 + eslint-plugin-validate-jsx-nesting: ^0.1.1 + eslint-plugin-you-dont-need-lodash-underscore: ^6.14.0 + eslint-plugin-zod: 4.9.0 + tailwind-csstree: 0.3.3 + typescript: '*' + peerDependenciesMeta: + '@eslint-react/eslint-plugin': + optional: true + '@eslint/css': + optional: true + '@ospm/eslint-plugin-react-unhookify': + optional: true + '@tanstack/eslint-plugin-query': + optional: true + '@tanstack/eslint-plugin-router': + optional: true + '@unocss/eslint-plugin': + optional: true + astro-eslint-parser: + optional: true + eslint-plugin-astro: + optional: true + eslint-plugin-format: + optional: true + eslint-plugin-jsx-a11y: + optional: true + eslint-plugin-oxlint: + optional: true + eslint-plugin-playwright: + optional: true + eslint-plugin-react: + optional: true + eslint-plugin-react-compiler: + optional: true + eslint-plugin-react-hooks: + optional: true + eslint-plugin-react-perf: + optional: true + eslint-plugin-react-refresh: + optional: true + eslint-plugin-react-you-might-not-need-an-effect: + optional: true + eslint-plugin-storybook: + optional: true + eslint-plugin-tailwindcss: + optional: true + eslint-plugin-testing-library: + optional: true + eslint-plugin-tsdoc: + optional: true + eslint-plugin-validate-jsx-nesting: + optional: true + eslint-plugin-you-dont-need-lodash-underscore: + optional: true + eslint-plugin-zod: + optional: true + tailwind-csstree: + optional: true + typescript: + optional: true + '@anolilab/multi-semantic-release@4.4.6': resolution: {integrity: sha512-GZ1M+t20K+7bkmQNoCXpBzoo94smfSG7kxEDfoGkqgSjdGqLnFm8R11hDmtKzWBN9XihNcdLnDtlB9I0ebmGtw==} engines: {node: ^22.14.0 || >=24.10.0} @@ -12670,6 +12758,105 @@ snapshots: - vitest - yaml + '@anolilab/eslint-config@29.0.0(c3bc49b65831bdab74607c7c3b1cf763)': + dependencies: + '@e18e/eslint-plugin': 0.8.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(oxlint@1.77.0) + '@eslint-community/eslint-plugin-eslint-comments': 4.7.2(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + '@eslint/compat': 2.1.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + '@eslint/js': 10.0.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + '@eslint/markdown': 8.0.3(supports-color@10.2.2) + '@html-eslint/eslint-plugin': 0.64.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + '@html-eslint/parser': 0.64.0 + '@stylistic/eslint-plugin': 5.10.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + '@stylistic/eslint-plugin-ts': 4.4.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/parser': 8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/types': 8.66.0 + '@visulima/fs': 5.0.5(ini@7.0.0)(json5@2.2.3)(jsonc-parser@3.3.1)(yaml@2.9.0) + '@visulima/package': 5.0.5(ini@7.0.0)(jsonc-parser@3.3.1) + '@visulima/tsconfig': 3.2.0(ini@7.0.0)(json5@2.2.3)(yaml@2.9.0) + '@vitest/eslint-plugin': 1.6.26(@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)(vitest@4.1.10) + confusing-browser-globals: 1.0.11 + eslint: 10.8.0(jiti@2.7.0)(supports-color@10.2.2) + eslint-config-flat-gitignore: 2.3.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-config-prettier: 10.1.8(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-flat-config-utils: 3.2.0 + eslint-import-resolver-node: 0.4.0(supports-color@10.2.2) + eslint-import-resolver-typescript: 4.4.5(eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-node@0.4.0(supports-color@10.2.2))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) + eslint-merge-processors: 2.0.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-antfu: 3.2.3(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-compat: 7.0.2(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-erasable-syntax-only: 0.4.2(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-es-x: 10.0.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-html: 8.1.4 + eslint-plugin-import-x: 4.17.1(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-import-resolver-node@0.4.0(supports-color@10.2.2))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-jsdoc: 63.3.3(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-jsonc: 3.4.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-n: 18.2.2(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(ts-declaration-location@1.0.7(typescript@6.0.3))(typescript@6.0.3) + eslint-plugin-no-for-of-array: 0.1.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript-eslint@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(typescript@6.0.3) + eslint-plugin-no-only-tests: 3.4.0 + eslint-plugin-no-secrets: 2.3.3(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-no-unsanitized: 4.1.5(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-perfectionist: 5.10.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-pnpm: 1.7.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-promise: 7.3.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-regexp: 3.1.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-security: 4.0.1 + eslint-plugin-simple-import-sort: 14.0.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-sonarjs: 4.2.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-toml: 1.5.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-unicorn: 72.0.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-unused-imports: 4.4.1(@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-yml: 3.8.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + globals: 17.9.0 + jsonc-eslint-parser: 3.3.0 + parse-gitignore: 2.0.0 + semver: 7.8.5 + toml-eslint-parser: 1.0.3 + typescript-eslint: 8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + yaml-eslint-parser: 2.1.0 + optionalDependencies: + '@eslint-react/eslint-plugin': 5.18.3(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@eslint/css': 1.4.0 + '@ospm/eslint-plugin-react-unhookify': 1.0.2(@types/react@19.2.17)(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(react-dom@19.2.7(react@19.2.8))(react@19.2.8) + '@tanstack/eslint-plugin-query': 5.101.4(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@tanstack/eslint-plugin-router': 1.162.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@unocss/eslint-plugin': 66.7.5(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + astro-eslint-parser: 3.0.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(supports-color@10.2.2) + eslint-plugin-astro: 3.1.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint-plugin-jsx-a11y@6.10.2(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)))(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript-eslint@8.66.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)) + eslint-plugin-format: 2.0.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-oxlint: 1.77.0(oxlint@1.77.0) + eslint-plugin-playwright: 2.11.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-react: 7.37.5(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-react-compiler: 19.1.0-rc.2(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-react-hooks: 7.1.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-react-perf: 3.3.3(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-react-refresh: 0.5.3(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-react-you-might-not-need-an-effect: 1.0.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-storybook: 10.5.3(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(storybook@10.5.7(@types/react@19.2.17)(prettier@3.9.6)(react-dom@19.2.7(react@19.2.8))(react@19.2.8))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-tailwindcss: 4.2.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(tailwindcss@4.3.1)(typescript@6.0.3) + eslint-plugin-testing-library: 7.16.2(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-tsdoc: 0.5.2(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint-plugin-validate-jsx-nesting: 0.1.1(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2)) + eslint-plugin-you-dont-need-lodash-underscore: 6.14.0 + eslint-plugin-zod: 4.9.0(eslint@10.8.0(jiti@2.7.0)(supports-color@10.2.2))(oxlint@1.77.0)(supports-color@10.2.2)(typescript@6.0.3)(zod@4.4.3) + tailwind-csstree: 0.3.3(@eslint/css@1.4.0) + typescript: 6.0.3 + transitivePeerDependencies: + - '@eslint/json' + - '@typescript-eslint/eslint-plugin' + - '@typescript-eslint/utils' + - eslint-plugin-import + - ini + - json5 + - jsonc-parser + - oxlint + - smol-toml + - supports-color + - ts-declaration-location + - vitest + - yaml + '@anolilab/multi-semantic-release@4.4.6(ini@7.0.0)(json5@2.2.3)(jsonc-parser@3.3.1)(semantic-release@25.0.9(supports-color@7.2.0)(typescript@6.0.3))(supports-color@7.2.0)(typescript@6.0.3)': dependencies: '@semrel-extra/topo': 1.14.1