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 = =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