├── .editorconfig
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ └── crash-report.md
└── workflows
│ └── build_test_publish.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .yarn
└── releases
│ └── yarn-4.6.0.cjs
├── .yarnrc.yml
├── LICENSE
├── README.md
├── build.js
├── eslint.config.js
├── package.json
├── src
├── index.cjs
├── index.js
├── messages.js
├── rule.js
└── util
│ ├── ast.js
│ └── react.js
├── test
├── chaining-state.test.js
├── config.test.js
├── deriving-state.test.js
├── empty-effect.test.js
├── initializing-state.test.js
├── parent-child-coupling.test.js
├── real-world.test.js
├── resetting-state-from-props.test.js
├── rule-tester.js
├── syntax.test.js
└── using-state-as-event-handler.test.js
├── types
└── index.d.ts
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 |
7 | [*.{js,json,yml}]
8 | charset = utf-8
9 | indent_style = space
10 | indent_size = 2
11 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | /.yarn/** linguist-vendored
2 | /.yarn/releases/* binary
3 | /.yarn/plugins/**/* binary
4 | /.pnp.* binary linguist-generated
5 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/crash-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Crash report
3 | about: Report a crash in the plugin
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Issue
11 |
12 | ### Stack Trace
13 |
14 | The crash's stack trace.
15 |
16 | ## Context
17 |
18 | ### Versions
19 |
20 | **Plugin:**
21 | **ESLint:**
22 | **Node:**
23 |
24 | ### ESLint Config
25 |
26 | Your `.eslintrc` or `eslint.config.js`.
27 |
28 | ### Source Code
29 |
30 | If possible, please provide your entire React component that the plugin crashed on, not just the `useEffect`.
31 |
--------------------------------------------------------------------------------
/.github/workflows/build_test_publish.yml:
--------------------------------------------------------------------------------
1 | name: Build, test, publish
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | env:
9 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
10 |
11 | jobs:
12 | build_and_test:
13 | runs-on: ubuntu-latest
14 | outputs:
15 | current_version: ${{ steps.version_check.outputs.current_version }}
16 | previous_version: ${{ steps.version_check.outputs.previous_version }}
17 | steps:
18 | - name: Checkout code
19 | uses: actions/checkout@v4
20 |
21 | - name: Setup Node.js
22 | uses: actions/setup-node@v4
23 | with:
24 | node-version: '23.9.0'
25 | cache: 'yarn'
26 |
27 | - name: Install dependencies
28 | run: yarn install --immutable
29 |
30 | - name: Lint
31 | run: yarn lint
32 |
33 | - name: Build
34 | run: yarn build
35 |
36 | - name: Test
37 | run: yarn test
38 |
39 | - name: Upload dist artifact
40 | uses: actions/upload-artifact@v4
41 | with:
42 | name: dist
43 | path: dist/
44 |
45 | - name: Check version
46 | id: version_check
47 | run: |
48 | sudo apt-get install -y jq
49 | echo "current_version=$(jq -r .version package.json)" >> $GITHUB_OUTPUT
50 | echo "previous_version=$(yarn npm info $(jq -r .name package.json) -f version --json | jq -r .version)" >> $GITHUB_OUTPUT
51 |
52 | publish:
53 | runs-on: ubuntu-latest
54 | needs: build_and_test
55 | if: ${{ needs.build_and_test.outputs.current_version != needs.build_and_test.outputs.previous_version }}
56 | steps:
57 | - name: Checkout code
58 | uses: actions/checkout@v4
59 |
60 | - name: Setup Node.js
61 | uses: actions/setup-node@v4
62 | with:
63 | node-version: '23.9.0'
64 | cache: 'yarn'
65 |
66 | - name: Download dist artifact
67 | uses: actions/download-artifact@v4
68 | with:
69 | name: dist
70 | path: dist/
71 |
72 | - name: Publish to npm
73 | run: yarn npm publish --access public
74 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 |
3 | .yarn/*
4 | !.yarn/patches
5 | !.yarn/plugins
6 | !.yarn/releases
7 | !.yarn/sdks
8 | !.yarn/versions
9 |
10 | # Swap the comments on the following lines if you wish to use zero-installs
11 | # In that case, don't forget to run `yarn config set enableGlobalCache false`!
12 | # Documentation here: https://yarnpkg.com/features/caching#zero-installs
13 |
14 | #!.yarn/cache
15 | .pnp.*
16 | node_modules
17 | .aider*
18 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | README.md
2 | .github
3 | .yarn
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
3 | yarnPath: .yarn/releases/yarn-4.6.0.cjs
4 |
5 | npmAuthToken: "${NPM_TOKEN}"
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Nick van Dyke
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the “Software”), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ESLint - React - You Might Not Need An Effect
2 |
3 | ESLint plugin to catch [unnecessary React `useEffect`s](https://react.dev/learn/you-might-not-need-an-effect) to make your code easier to follow, faster to run, and less error-prone. Highly recommended for new React developers as you learn its mental model, and even experienced developers may be surprised.
4 |
5 | ## 🚀 Setup
6 |
7 | This plugin requires ESLint >= v7.0.0 and Node >= 14.
8 |
9 | ### Installation
10 |
11 | **NPM**:
12 |
13 | ```bash
14 | npm install --save-dev eslint-plugin-react-you-might-not-need-an-effect
15 | ```
16 |
17 | **Yarn**:
18 |
19 | ```bash
20 | yarn add -D eslint-plugin-react-you-might-not-need-an-effect
21 | ```
22 |
23 | ### Configuration
24 |
25 | Add the plugin's recommended config to your ESLint configuration file.
26 |
27 | #### Legacy config (`.eslintrc`)
28 |
29 | ```js
30 | {
31 | "extends": [
32 | "plugin:react-you-might-not-need-an-effect/legacy-recommended"
33 | ],
34 | }
35 | ```
36 |
37 | #### Flat config (`eslint.config.js`)
38 |
39 | ```js
40 | import reactYouMightNotNeedAnEffect from "eslint-plugin-react-you-might-not-need-an-effect";
41 |
42 | export default [
43 | reactYouMightNotNeedAnEffect.configs.recommended
44 | ];
45 | ```
46 |
47 | ### Recommended
48 |
49 | The plugin will have more information to act upon when you pass the correct dependencies to your effect — [`react-hooks/exhaustive-deps`](https://www.npmjs.com/package/eslint-plugin-react-hooks).
50 |
51 | ## 🔎 Rule: `you-might-not-need-an-effect`
52 |
53 | Determines when an effect is likely unnecessary, such as when it:
54 |
55 | - Stores derived state
56 | - Chains state updates
57 | - Initializes state
58 | - Resets all state when props change
59 | - Couples parent and child state or behavior
60 |
61 | When possible, also suggests the more idiomatic pattern.
62 |
63 | While the effect may be unnecessary, we cannot reliably determine that when it:
64 |
65 | - Uses external state
66 | - Calls external functions
67 | - Uses internal state to handle events
68 |
69 | ## ⚠️ Limitations
70 |
71 | This plugin aims to minimize false positives and accepts that some false negatives are inevitable — see the [tests](./test) for (in)valid examples. But the ways to (mis)use an effect are practically endless; if you encounter unexpected behavior or edge cases in real-world usage, please [open an issue](https://github.com/NickvanDyke/eslint-plugin-react-you-might-not-need-an-effect/issues/new) with details about your scenario. Your feedback helps improve the plugin for everyone!
72 |
73 | ## 📖 Learn More
74 |
75 | - https://react.dev/reference/react/useEffect
76 | - https://react.dev/learn/you-might-not-need-an-effect
77 | - https://react.dev/learn/synchronizing-with-effects
78 | - https://react.dev/learn/separating-events-from-effects
79 | - https://react.dev/learn/lifecycle-of-reactive-effects
80 |
--------------------------------------------------------------------------------
/build.js:
--------------------------------------------------------------------------------
1 | import { build } from "esbuild";
2 |
3 | build({
4 | entryPoints: ["src/index.cjs"],
5 | bundle: true,
6 | sourcemap: true,
7 | format: "cjs",
8 | outfile: "dist/index.cjs",
9 | platform: "node",
10 | external: ["eslint"],
11 | });
12 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import eslint from "@eslint/js";
2 | import eslintPlugin from "eslint-plugin-eslint-plugin";
3 | import nodePlugin from "eslint-plugin-n";
4 | import globals from "globals";
5 |
6 | export default [
7 | eslint.configs.recommended,
8 | eslintPlugin.configs["flat/recommended"],
9 | nodePlugin.configs["flat/recommended"],
10 | {
11 | ignores: ["dist/**", "node_modules/**", ".yarn/**"],
12 | },
13 | {
14 | languageOptions: {
15 | globals: {
16 | ...globals.node,
17 | },
18 | },
19 | },
20 | {
21 | files: ["test/**"],
22 | languageOptions: {
23 | globals: {
24 | ...globals.mocha,
25 | },
26 | },
27 | },
28 | ];
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eslint-plugin-react-you-might-not-need-an-effect",
3 | "version": "0.1.4",
4 | "description": "ESLint rule to warn against unnecessary React useEffect hooks.",
5 | "author": "Nick van Dyke",
6 | "license": "MIT",
7 | "type": "module",
8 | "module": "./src/index.js",
9 | "main": "./dist/index.cjs",
10 | "types": "./types/index.d.ts",
11 | "files": [
12 | "src",
13 | "types",
14 | "dist"
15 | ],
16 | "exports": {
17 | "types": "./types/index.d.ts",
18 | "import": "./src/index.js",
19 | "require": "./dist/index.cjs"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "https://github.com/NickvanDyke/eslint-plugin-react-you-might-not-need-an-effect.git"
24 | },
25 | "scripts": {
26 | "build": "node build.js",
27 | "lint": "eslint",
28 | "test": "mocha"
29 | },
30 | "keywords": [
31 | "eslint",
32 | "eslintplugin",
33 | "eslint-plugin",
34 | "react"
35 | ],
36 | "engines": {
37 | "node": ">=14.0.0"
38 | },
39 | "dependencies": {
40 | "eslint-utils": "^3.0.0",
41 | "globals": "^16.2.0"
42 | },
43 | "devDependencies": {
44 | "@eslint/js": "^9.28.0",
45 | "esbuild": "^0.25.3",
46 | "eslint": "^9.20.1",
47 | "eslint-plugin-eslint-plugin": "^6.4.0",
48 | "eslint-plugin-n": "^17.17.0",
49 | "lint-staged": "^16.1.0",
50 | "mocha": "11.1.0",
51 | "prettier": "^3.5.3",
52 | "simple-git-hooks": "^2.13.0"
53 | },
54 | "peerDependencies": {
55 | "eslint": ">=7.0.0"
56 | },
57 | "packageManager": "yarn@4.6.0",
58 | "simple-git-hooks": {
59 | "pre-commit": "yarn lint-staged"
60 | },
61 | "lint-staged": {
62 | "*.js": [
63 | "prettier --write"
64 | ]
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/index.cjs:
--------------------------------------------------------------------------------
1 | // `build.js` will bundle everything into CJS.
2 | // Would be nice to have it use `index.js` directly, but then `esbuild`
3 | // seems unable to structure the CJS export the way ESLint expects.
4 | // Seems we have to unwrap the default export ourselves for that.
5 | module.exports = require("./index.js").default;
6 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { name as ruleName, rule } from "./rule.js";
2 | import globals from "globals";
3 |
4 | const pluginName = "react-you-might-not-need-an-effect";
5 |
6 | const plugin = {
7 | meta: {
8 | name: pluginName,
9 | },
10 | configs: {},
11 | rules: {
12 | [ruleName]: rule,
13 | },
14 | };
15 |
16 | Object.assign(plugin.configs, {
17 | // flat config format
18 | recommended: {
19 | files: ["**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}"],
20 | plugins: {
21 | // Object.assign above so we can reference `plugin` here
22 | [pluginName]: plugin,
23 | },
24 | rules: {
25 | [pluginName + "/" + ruleName]: "warn",
26 | },
27 | languageOptions: {
28 | globals: {
29 | // NOTE: Required so we can resolve global references to their upstream global variables
30 | ...globals.browser,
31 | },
32 | parserOptions: {
33 | ecmaFeatures: {
34 | jsx: true,
35 | },
36 | },
37 | },
38 | },
39 | // eslintrc format
40 | "legacy-recommended": {
41 | plugins: [pluginName],
42 | rules: {
43 | [pluginName + "/" + ruleName]: "warn",
44 | },
45 | globals: {
46 | // NOTE: Required so we can resolve global references to their upstream global variables
47 | ...globals.browser,
48 | },
49 | parserOptions: {
50 | ecmaFeatures: {
51 | jsx: true,
52 | },
53 | },
54 | },
55 | });
56 |
57 | export default plugin;
58 |
--------------------------------------------------------------------------------
/src/messages.js:
--------------------------------------------------------------------------------
1 | export const messageIds = {
2 | avoidEmptyEffect: "avoidEmptyEffect",
3 | avoidDerivedState: "avoidDerivedState",
4 | avoidInitializingState: "avoidInitializingState",
5 | avoidChainingState: "avoidChainingState",
6 | avoidParentChildCoupling: "avoidParentChildCoupling",
7 | avoidResettingStateFromProps: "avoidResettingStateFromProps",
8 | // TODO: This would be nice, but I'm not sure it can be done accurately
9 | // Maybe we can accurately warn about this when the state being reacted to is one of our own `useState`s?
10 | // Because if we have a setter then we have a callback.
11 | // But, I think that would also warn about valid uses that synchronize internal state to external state.
12 | // avoidEventHandler: "avoidEventHandler",
13 | // TODO: Possible to detect when `useSyncExternalStore` should be preferred?
14 | };
15 |
16 | // TODO: Could include more info in messages, like the relevant node
17 | export const messages = {
18 | [messageIds.avoidEmptyEffect]: "This effect is empty and could be removed.",
19 | [messageIds.avoidDerivedState]:
20 | 'Avoid storing derived state. Compute "{{state}}" directly during render, optionally with `useMemo` if it\'s expensive.',
21 | [messageIds.avoidInitializingState]:
22 | 'Avoid initializing state in an effect. Instead, pass "{{state}}"\'s initial value to its `useState`.',
23 | [messageIds.avoidChainingState]:
24 | "Avoid chaining state changes. When possible, update all relevant state simultaneously.",
25 | [messageIds.avoidParentChildCoupling]:
26 | "Avoid coupling parent behavior or state to a child component. Instead, lift shared logic or state up to the parent.",
27 | [messageIds.avoidResettingStateFromProps]:
28 | 'Avoid resetting state from props. If "{{prop}}" is a key, pass it as `key` instead so React will reset the component.',
29 | // [messages.avoidEventHandler]:
30 | // "Avoid using state as an event handler. Instead, call the event handler directly.",
31 | };
32 |
--------------------------------------------------------------------------------
/src/rule.js:
--------------------------------------------------------------------------------
1 | import { messageIds, messages } from "./messages.js";
2 | import { getCallExpr, getDownstreamRefs } from "./util/ast.js";
3 | import {
4 | findPropUsedToResetAllState,
5 | isUseEffect,
6 | getUseStateNode,
7 | getEffectFnRefs,
8 | getDependenciesRefs,
9 | isStateSetter,
10 | isPropCallback,
11 | isDirectCall,
12 | getUpstreamReactVariables,
13 | isState,
14 | isProp,
15 | isHOCProp,
16 | } from "./util/react.js";
17 |
18 | export const name = "you-might-not-need-an-effect";
19 |
20 | export const rule = {
21 | meta: {
22 | type: "suggestion",
23 | docs: {
24 | description: "Catch unnecessary React useEffect hooks.",
25 | url: "https://react.dev/learn/you-might-not-need-an-effect",
26 | },
27 | schema: [],
28 | messages: messages,
29 | },
30 | create: (context) => ({
31 | CallExpression: (node) => {
32 | if (!isUseEffect(node)) {
33 | return;
34 | }
35 |
36 | const effectFnRefs = getEffectFnRefs(context, node);
37 | const depsRefs = getDependenciesRefs(context, node);
38 |
39 | if (!effectFnRefs || !depsRefs) {
40 | return;
41 | } else if (effectFnRefs.length === 0) {
42 | // Hopefully it's obvious the effect can be removed.
43 | // More a follow-up for once they fix/remove other issues.
44 | context.report({
45 | node,
46 | messageId: messageIds.avoidEmptyEffect,
47 | });
48 | return;
49 | }
50 |
51 | const propUsedToResetAllState = findPropUsedToResetAllState(
52 | context,
53 | effectFnRefs,
54 | depsRefs,
55 | node,
56 | );
57 | if (propUsedToResetAllState) {
58 | const propName = propUsedToResetAllState.identifier.name;
59 | context.report({
60 | node: node,
61 | messageId: messageIds.avoidResettingStateFromProps,
62 | data: { prop: propName },
63 | });
64 | // Don't flag anything else -- confusing, and this should be fixed first.
65 | return;
66 | }
67 |
68 | effectFnRefs
69 | .filter(
70 | (ref) =>
71 | isStateSetter(context, ref) ||
72 | (isPropCallback(context, ref) &&
73 | // Don't analyze HOC prop callbacks -- we don't have control over them to lift state or logic
74 | !isHOCProp(ref.resolved)),
75 | )
76 | // Non-direct calls are likely inside a callback passed to an external system like `window.addEventListener`,
77 | // or a Promise chain that (probably) retrieves external data.
78 | // Note we'll still analyze derived setters because isStateSetter considers that.
79 | // Heuristic inspired by https://eslint-react.xyz/docs/rules/hooks-extra-no-direct-set-state-in-use-effect
80 | .filter((ref) => isDirectCall(ref.identifier))
81 | .forEach((ref) => {
82 | const callExpr = getCallExpr(ref);
83 |
84 | if (isStateSetter(context, ref)) {
85 | const useStateNode = getUseStateNode(context, ref);
86 | const stateName = (
87 | useStateNode.id.elements[0] ?? useStateNode.id.elements[1]
88 | )?.name;
89 |
90 | if (depsRefs.length === 0) {
91 | context.report({
92 | node: callExpr,
93 | messageId: messageIds.avoidInitializingState,
94 | data: { state: stateName },
95 | });
96 | }
97 |
98 | // TODO: Make more readable
99 | const isArgsInternal = callExpr.arguments
100 | .flatMap((arg) => getDownstreamRefs(context, arg))
101 | .flatMap((ref) =>
102 | getUpstreamReactVariables(context, ref.identifier),
103 | )
104 | .notEmptyEvery(
105 | (variable) =>
106 | isState(variable) ||
107 | (isProp(variable) && !isHOCProp(variable)),
108 | );
109 | const isArgsExternal = callExpr.arguments
110 | .flatMap((arg) => getDownstreamRefs(context, arg))
111 | .flatMap((ref) =>
112 | getUpstreamReactVariables(context, ref.identifier),
113 | )
114 | .some(
115 | (variable) =>
116 | (!isState(variable) && !isProp(variable)) ||
117 | isHOCProp(variable),
118 | );
119 | const isDepsInternal = depsRefs
120 | .flatMap((ref) =>
121 | getUpstreamReactVariables(context, ref.identifier),
122 | )
123 | .notEmptyEvery(
124 | (variable) =>
125 | isState(variable) ||
126 | (isProp(variable) && !isHOCProp(variable)),
127 | );
128 |
129 | if (isArgsInternal) {
130 | // TODO: Can also warn if this is the only call to the setter,
131 | // even if the arg is external (and not retrieved in the effect)...
132 | // Does it matter whether the args are in the deps array?
133 | // I guess so, to differentiate between derived and chain state updates?
134 | // What about internal vs in deps? Changes behavior, but meaningfully?
135 | context.report({
136 | node: callExpr,
137 | messageId: messageIds.avoidDerivedState,
138 | data: { state: stateName },
139 | });
140 | }
141 |
142 | if (!isArgsInternal && !isArgsExternal && isDepsInternal) {
143 | context.report({
144 | node: callExpr,
145 | messageId: messageIds.avoidChainingState,
146 | });
147 | }
148 | } else if (isPropCallback(context, ref)) {
149 | // I'm pretty sure we can flag this regardless of the arguments, including none...
150 | //
151 | // Because we are either:
152 | // 1. Passing live state updates to the parent
153 | // 2. Using state as an event handler to pass final state to the parent
154 | //
155 | // Both are bad. However I'm not yet sure how we could differentiate #2 to give a better warning.
156 | //
157 | // TODO: Can we thus safely assume that state is used as an event handler when the ref is a prop?
158 | // Normally we can't warn about that because we don't know what the event handler does externally.
159 | // But when it's a prop, it's internal.
160 | // I guess it could still be valid when the dep is external state? Or in that case,
161 | // the issue is the state should be lifted to the parent?
162 | context.report({
163 | node: callExpr,
164 | messageId: messageIds.avoidParentChildCoupling,
165 | });
166 | }
167 | });
168 | },
169 | }),
170 | };
171 |
--------------------------------------------------------------------------------
/src/util/ast.js:
--------------------------------------------------------------------------------
1 | import { findVariable } from "eslint-utils";
2 |
3 | export const traverse = (context, node, visit, visited = new Set()) => {
4 | if (visited.has(node)) {
5 | return;
6 | }
7 |
8 | visited.add(node);
9 | visit(node);
10 |
11 | (context.sourceCode.visitorKeys[node.type] || [])
12 | .map((key) => node[key])
13 | // Some `visitorKeys` are optional, e.g. `IfStatement.alternate`.
14 | .filter(Boolean)
15 | // Can be an array, like `CallExpression.arguments`
16 | .flatMap((child) => (Array.isArray(child) ? child : [child]))
17 | // Can rarely be `null`, e.g. `ArrayPattern.elements[1]` when an element is skipped - `const [a, , b] = arr`
18 | .filter(Boolean)
19 | // Check it's a valid AST node
20 | .filter((child) => typeof child.type === "string")
21 | .forEach((child) => traverse(context, child, visit, visited));
22 | };
23 |
24 | const getDownstreamIdentifiers = (context, rootNode) => {
25 | const identifiers = [];
26 | traverse(context, rootNode, (node) => {
27 | if (node.type === "Identifier") {
28 | identifiers.push(node);
29 | }
30 | });
31 | return identifiers;
32 | };
33 |
34 | export const getUpstreamVariables = (
35 | context,
36 | node,
37 | filter,
38 | visited = new Set(),
39 | ) => {
40 | if (visited.has(node)) {
41 | return [];
42 | }
43 |
44 | visited.add(node);
45 |
46 | const variable = findVariable(context.sourceCode.getScope(node), node);
47 | if (!variable) {
48 | // I think this only happens when:
49 | // 1. There's genuinely no variable, i.e. `node` is a literal
50 | // 2. Import statement is missing
51 | // 3. ESLint globals are misconfigured
52 | return [];
53 | }
54 |
55 | const upstreamVariables = variable.defs
56 | .filter((def) => !!def.node.init)
57 | .filter((def) => filter(def.node))
58 | .flatMap((def) => getDownstreamIdentifiers(context, def.node.init))
59 | .flatMap((identifier) =>
60 | getUpstreamVariables(context, identifier, filter, visited),
61 | );
62 |
63 | // Ultimately return only leaf variables
64 | return upstreamVariables.length === 0 ? [variable] : upstreamVariables;
65 | };
66 |
67 | export const getDownstreamRefs = (context, node) =>
68 | getDownstreamIdentifiers(context, node)
69 | .map((identifier) => getRef(context, identifier))
70 | .filter(Boolean);
71 |
72 | const getRef = (context, identifier) =>
73 | findVariable(
74 | context.sourceCode.getScope(identifier),
75 | identifier,
76 | )?.references.find((ref) => ref.identifier === identifier);
77 |
78 | export const getCallExpr = (ref, current = ref.identifier.parent) => {
79 | if (current.type === "CallExpression") {
80 | // We've reached the top - confirm that the ref is the (eventual) callee, as opposed to an argument.
81 | let node = ref.identifier;
82 | while (node.parent.type === "MemberExpression") {
83 | node = node.parent;
84 | }
85 |
86 | if (current.callee === node) {
87 | return current;
88 | }
89 | }
90 |
91 | if (current.type === "MemberExpression") {
92 | return getCallExpr(ref, current.parent);
93 | }
94 |
95 | return undefined;
96 | };
97 |
98 | export const isIIFE = (node) =>
99 | node.type === "CallExpression" &&
100 | (node.callee.type === "ArrowFunctionExpression" ||
101 | node.callee.type === "FunctionExpression");
102 |
--------------------------------------------------------------------------------
/src/util/react.js:
--------------------------------------------------------------------------------
1 | import {
2 | traverse,
3 | getUpstreamVariables,
4 | getDownstreamRefs,
5 | getCallExpr,
6 | isIIFE,
7 | } from "./ast.js";
8 |
9 | export const isReactFunctionalComponent = (node) =>
10 | (node.type === "FunctionDeclaration" ||
11 | (node.type === "VariableDeclarator" &&
12 | (node.init.type === "ArrowFunctionExpression" ||
13 | node.init.type === "CallExpression"))) &&
14 | node.id.type === "Identifier" &&
15 | node.id.name[0].toUpperCase() === node.id.name[0];
16 |
17 | // NOTE: Returns false for known pure HOCs -- `memo` and `forwardRef`.
18 | // TODO: Will not detect when they define the component normally and then export it wrapped in the HOC.
19 | // e.g. `const MyComponent = (props) => {...}; export default memo(MyComponent);`
20 | export const isReactFunctionalHOC = (node) =>
21 | node.type === "VariableDeclarator" &&
22 | node.init &&
23 | node.init.type === "CallExpression" &&
24 | node.init.callee.type === "Identifier" &&
25 | !["memo", "forwardRef"].includes(node.init.callee.name) &&
26 | node.init.arguments.length > 0 &&
27 | (node.init.arguments[0].type === "ArrowFunctionExpression" ||
28 | node.init.arguments[0].type === "FunctionExpression") &&
29 | node.id.type === "Identifier" &&
30 | node.id.name[0].toUpperCase() === node.id.name[0];
31 |
32 | export const isCustomHook = (node) =>
33 | (node.type === "FunctionDeclaration" ||
34 | (node.type === "VariableDeclarator" &&
35 | node.init &&
36 | (node.init.type === "ArrowFunctionExpression" ||
37 | node.init.type === "FunctionExpression"))) &&
38 | node.id.type === "Identifier" &&
39 | node.id.name.startsWith("use") &&
40 | node.id.name[3] === node.id.name[3].toUpperCase();
41 |
42 | export const isUseState = (node) =>
43 | node.type === "VariableDeclarator" &&
44 | node.init &&
45 | node.init.type === "CallExpression" &&
46 | node.init.callee.name === "useState" &&
47 | node.id.type === "ArrayPattern" &&
48 | // Not sure its usecase, but may just have the setter
49 | (node.id.elements.length === 1 || node.id.elements.length === 2) &&
50 | node.id.elements.every((el) => {
51 | // Apparently skipping the state element is a valid use.
52 | // I suppose technically the state can still be read via setter callback.
53 | return !el || el.type === "Identifier";
54 | });
55 |
56 | export const isUseEffect = (node) =>
57 | node.type === "CallExpression" &&
58 | ((node.callee.type === "Identifier" &&
59 | (node.callee.name === "useEffect" ||
60 | node.callee.name === "useLayoutEffect")) ||
61 | (node.callee.type === "MemberExpression" &&
62 | node.callee.object.name === "React" &&
63 | (node.callee.property.name === "useEffect" ||
64 | node.callee.property.name === "useLayoutEffect")));
65 |
66 | export const getEffectFn = (node) => {
67 | if (!isUseEffect(node) || node.arguments.length < 1) {
68 | return undefined;
69 | }
70 |
71 | const effectFn = node.arguments[0];
72 | if (
73 | effectFn.type !== "ArrowFunctionExpression" &&
74 | effectFn.type !== "FunctionExpression"
75 | ) {
76 | return undefined;
77 | }
78 |
79 | return effectFn;
80 | };
81 |
82 | // NOTE: When `MemberExpression` (even nested ones), a `Reference` is only the root object, not the function.
83 | export const getEffectFnRefs = (context, node) => {
84 | const effectFn = getEffectFn(node);
85 | if (!effectFn) {
86 | return null;
87 | }
88 |
89 | return getDownstreamRefs(context, effectFn);
90 | };
91 |
92 | export function getDependenciesRefs(context, node) {
93 | if (!isUseEffect(node) || node.arguments.length < 2) {
94 | return undefined;
95 | }
96 |
97 | const depsArr = node.arguments[1];
98 | if (depsArr.type !== "ArrayExpression") {
99 | return undefined;
100 | }
101 |
102 | return getDownstreamRefs(context, depsArr);
103 | }
104 |
105 | export const isFnRef = (ref) => getCallExpr(ref) !== undefined;
106 |
107 | // NOTE: These return true for state with CallExpressions, like `list.concat()`.
108 | // Arguably preferable, as mutating the state is functionally the same as calling the setter.
109 | // (Even though that is not recommended and should be prevented by a different rule).
110 | // And in the case of a prop, we can't differentiate state mutations from callbacks anyway.
111 | export const isStateSetter = (context, ref) =>
112 | isFnRef(ref) &&
113 | getUpstreamReactVariables(context, ref.identifier).notEmptyEvery((variable) =>
114 | isState(variable),
115 | );
116 | export const isPropCallback = (context, ref) =>
117 | isFnRef(ref) &&
118 | getUpstreamReactVariables(context, ref.identifier).notEmptyEvery((variable) =>
119 | isProp(variable),
120 | );
121 |
122 | // NOTE: Global variables (like `JSON` in `JSON.stringify()`) have an empty `defs`; fortunately `[].some() === false`.
123 | // Also, I'm not sure so far when `defs.length > 1`... haven't seen it with shadowed variables or even redeclared variables with `var`.
124 | export const isState = (variable) =>
125 | variable.defs.some((def) => isUseState(def.node));
126 | export const isProp = (variable) =>
127 | variable.defs.some(
128 | (def) =>
129 | def.type === "Parameter" &&
130 | (isReactFunctionalComponent(getDeclNode(def.node)) ||
131 | isCustomHook(getDeclNode(def.node))),
132 | );
133 | export const isHOCProp = (variable) =>
134 | variable.defs.some(
135 | (def) =>
136 | def.type === "Parameter" && isReactFunctionalHOC(getDeclNode(def.node)),
137 | );
138 |
139 | const getDeclNode = (node) =>
140 | node.type === "ArrowFunctionExpression"
141 | ? node.parent.type === "CallExpression"
142 | ? node.parent.parent
143 | : node.parent
144 | : node;
145 |
146 | export const getUseStateNode = (context, ref) => {
147 | return getUpstreamReactVariables(context, ref.identifier)
148 | .find((variable) => isState(variable))
149 | ?.defs.find((def) => isUseState(def.node))?.node;
150 | };
151 |
152 | // Returns true if the node is called directly inside a `useEffect`.
153 | // Note IIFEs do not break the "direct" chain because they're invoked immediately, as opposed to being a callback.
154 | export const isDirectCall = (node) => {
155 | if (!node) {
156 | return false;
157 | } else if (
158 | (node.type === "ArrowFunctionExpression" ||
159 | node.type === "FunctionExpression") &&
160 | !isIIFE(node.parent)
161 | ) {
162 | return isUseEffect(node.parent);
163 | } else {
164 | return isDirectCall(node.parent);
165 | }
166 | };
167 |
168 | export const findPropUsedToResetAllState = (
169 | context,
170 | effectFnRefs,
171 | depsRefs,
172 | useEffectNode,
173 | ) => {
174 | const stateSetterRefs = effectFnRefs.filter((ref) =>
175 | isStateSetter(context, ref),
176 | );
177 |
178 | const isAllStateReset =
179 | stateSetterRefs.length > 0 &&
180 | stateSetterRefs.every((ref) => isSetStateToInitialValue(context, ref)) &&
181 | stateSetterRefs.length ===
182 | countUseStates(context, findContainingNode(useEffectNode));
183 |
184 | return isAllStateReset
185 | ? depsRefs.find((ref) => isProp(ref.resolved))
186 | : undefined;
187 | };
188 |
189 | const isSetStateToInitialValue = (context, setterRef) => {
190 | const setStateToValue = getCallExpr(setterRef).arguments[0];
191 | const stateInitialValue = getUseStateNode(context, setterRef).init
192 | .arguments[0];
193 |
194 | // `useState()` (with no args) defaults to `undefined`,
195 | // so ommitting the arg is equivalent to passing `undefined`.
196 | // Technically this would false positive if they shadowed
197 | // `undefined` in only one of the scopes (only possible via `var`),
198 | // but I hope no one would do that.
199 | const isUndefined = (node) => node === undefined || node.name === "undefined";
200 | if (isUndefined(setStateToValue) && isUndefined(stateInitialValue)) {
201 | return true;
202 | }
203 |
204 | // `sourceCode.getText()` returns the entire file when passed null/undefined - let's short circuit that
205 | if (setStateToValue === null && stateInitialValue === null) {
206 | return true;
207 | } else if (
208 | (setStateToValue && !stateInitialValue) ||
209 | (!setStateToValue && stateInitialValue)
210 | ) {
211 | return false;
212 | }
213 |
214 | return (
215 | context.sourceCode.getText(setStateToValue) ===
216 | context.sourceCode.getText(stateInitialValue)
217 | );
218 | };
219 |
220 | const countUseStates = (context, componentNode) => {
221 | let count = 0;
222 |
223 | traverse(context, componentNode, (node) => {
224 | if (isUseState(node)) {
225 | count++;
226 | }
227 | });
228 |
229 | return count;
230 | };
231 |
232 | // Returns the component or custom hook that contains the `useEffect` node.
233 | const findContainingNode = (node) => {
234 | if (!node) {
235 | return undefined;
236 | } else if (
237 | isReactFunctionalComponent(node) ||
238 | isReactFunctionalHOC(node) ||
239 | isCustomHook(node)
240 | ) {
241 | return node;
242 | } else {
243 | return findContainingNode(node.parent);
244 | }
245 | };
246 |
247 | export const getUpstreamReactVariables = (context, node) =>
248 | getUpstreamVariables(
249 | context,
250 | node,
251 | // Stop at the *usage* of `useState` - don't go up to the `useState` variable.
252 | // Not needed for props - they don't go "too far".
253 | // We could remove this and check for the `useState` variable instead,
254 | // but then all our tests need to import it so we can traverse up to it.
255 | // And would need to change `getUseStateNode()` too?
256 | // TODO: Could probably organize these filters better.
257 | (node) => !isUseState(node),
258 | ).filter(
259 | (variable) =>
260 | isProp(variable) ||
261 | variable.defs.every((def) => def.type !== "Parameter"),
262 | );
263 |
264 | Array.prototype.notEmptyEvery = function (predicate) {
265 | return this.length > 0 && this.every(predicate);
266 | };
267 |
--------------------------------------------------------------------------------
/test/chaining-state.test.js:
--------------------------------------------------------------------------------
1 | import { MyRuleTester, js } from "./rule-tester.js";
2 | import { messageIds } from "../src/messages.js";
3 |
4 | new MyRuleTester().run("/chaining-state", {
5 | invalid: [
6 | {
7 | // React docs recommend to first update state in render instead of an effect.
8 | // But then continue on to say that usually you can avoid the sync entirely by
9 | // more wisely choosing your state. So we'll just always warn about chained state.
10 | name: "Syncing prop changes to internal state",
11 | code: js`
12 | function List({ items }) {
13 | const [selection, setSelection] = useState();
14 |
15 | useEffect(() => {
16 | setSelection(null);
17 | }, [items]);
18 |
19 | return (
20 |
21 | {items.map((item) => (
22 |
setSelection(item)}>
23 | {item.name}
24 |
25 | ))}
26 |
27 | )
28 | }
29 | `,
30 | errors: [
31 | {
32 | messageId: messageIds.avoidChainingState,
33 | },
34 | ],
35 | },
36 | {
37 | name: "Conditionally setting state from internal state",
38 | code: js`
39 | function Form() {
40 | const [error, setError] = useState();
41 | const [result, setResult] = useState();
42 |
43 | useEffect(() => {
44 | if (result.data) {
45 | setError(null);
46 | }
47 | }, [result]);
48 | }
49 | `,
50 | errors: [
51 | {
52 | messageId: messageIds.avoidChainingState,
53 | },
54 | ],
55 | },
56 | {
57 | name: "In an otherwise valid effect",
58 | code: js`
59 | function MyComponent() {
60 | const [state, setState] = useState();
61 | const [otherState, setOtherState] = useState('Meow');
62 |
63 | useEffect(() => {
64 | console.log('Meow');
65 | setState('Hello World');
66 | }, [otherState]);
67 | }
68 | `,
69 | errors: [
70 | {
71 | messageId: messageIds.avoidChainingState,
72 | data: { state: "state" },
73 | },
74 | ],
75 | },
76 | ],
77 | });
78 |
--------------------------------------------------------------------------------
/test/config.test.js:
--------------------------------------------------------------------------------
1 | import { ESLint } from "eslint";
2 | import plugin from "../src/index.js";
3 | import assert from "assert";
4 | import { js } from "./rule-tester.js";
5 | import { LegacyESLint } from "eslint/use-at-your-own-risk";
6 |
7 | describe("Recommended config", () => {
8 | const code = js`
9 | import { useState, useEffect } from "react";
10 |
11 | const MyComponent = () => {
12 | const [state, setState] = useState(0);
13 | const [otherState, setOtherState] = useState(0);
14 |
15 | useEffect(() => {
16 | setState(otherState * 2);
17 | }, [state]);
18 | };
19 | `;
20 |
21 | const testCases = [
22 | {
23 | name: "Flat",
24 | eslint: new ESLint({
25 | // Use `overrideConfig` so it ignores the project's config
26 | overrideConfig: [plugin.configs.recommended],
27 | }),
28 | },
29 | {
30 | name: "Legacy",
31 | eslint: new LegacyESLint({
32 | overrideConfig: {
33 | extends: [
34 | "plugin:react-you-might-not-need-an-effect/legacy-recommended",
35 | ],
36 | parserOptions: {
37 | // To support the syntax in the code under test
38 | ecmaVersion: 2020,
39 | sourceType: "module",
40 | },
41 | },
42 | }),
43 | },
44 | ];
45 |
46 | testCases.forEach(({ name, eslint }) => {
47 | it(name, async () => {
48 | const results = await eslint.lintText(code);
49 |
50 | assert.strictEqual(results.length, 1);
51 | assert.ok(results[0].messages);
52 | assert.ok(
53 | results[0].messages.some(
54 | (message) =>
55 | message.ruleId ===
56 | "react-you-might-not-need-an-effect/you-might-not-need-an-effect",
57 | ),
58 | );
59 | });
60 | });
61 | });
62 |
--------------------------------------------------------------------------------
/test/deriving-state.test.js:
--------------------------------------------------------------------------------
1 | import { MyRuleTester, js } from "./rule-tester.js";
2 | import { messageIds } from "../src/messages.js";
3 |
4 | // TODO: All these need the state setter in the deps
5 | new MyRuleTester().run("/deriving-state", {
6 | valid: [
7 | {
8 | name: "Compute in render from internal state",
9 | code: js`
10 | function Form() {
11 | const [firstName, setFirstName] = useState('Taylor');
12 | const [lastName, setLastName] = useState('Swift');
13 |
14 | const fullName = firstName + ' ' + lastName;
15 | }
16 | `,
17 | },
18 | {
19 | name: "Compute in render from props",
20 | code: js`
21 | function Form({ firstName, lastName }) {
22 | const fullName = firstName + ' ' + lastName;
23 | }
24 | `,
25 | },
26 | {
27 | name: "From external state change",
28 | code: js`
29 | function Feed() {
30 | const { data: posts } = useQuery('/posts');
31 | const [scrollPosition, setScrollPosition] = useState(0);
32 |
33 | useEffect(() => {
34 | setScrollPosition(0);
35 | }, [posts]);
36 | }
37 | `,
38 | },
39 | {
40 | name: "From external state change, with multiple setter calls",
41 | code: js`
42 | function Feed() {
43 | const { data: posts } = useQuery('/posts');
44 | const [selectedPost, setSelectedPost] = useState();
45 |
46 | useEffect(() => {
47 | setSelectedPost(posts[0]);
48 | }, [posts]);
49 |
50 | return (
51 |
52 | {posts.map((post) => (
53 |
setSelectedPost(post)}>
54 | {post.title}
55 |
56 | ))}
57 |
58 | )
59 | }
60 | `,
61 | },
62 | {
63 | name: "Fetch external state on mount",
64 | code: js`
65 | function Todos() {
66 | const [todos, setTodos] = useState([]);
67 |
68 | useEffect(() => {
69 | fetch('/todos').then((todos) => {
70 | setTodos(todos);
71 | });
72 | }, []);
73 | }
74 | `,
75 | },
76 | {
77 | name: "Sync external state",
78 | // Technically we could trigger the network call in `input.onChange`,
79 | // but the use of an effect to sync state is arguably more readable and a valid use.
80 | // Especially when we already store the input's controlled state.
81 | code: js`
82 | function Search() {
83 | const [query, setQuery] = useState();
84 | const [results, setResults] = useState();
85 |
86 | useEffect(() => {
87 | fetch('/search').then((data) => {
88 | setResults(data);
89 | });
90 | }, [query]);
91 |
92 | return (
93 |
94 |
setQuery(e.target.value)}
99 | />
100 |
101 | {results.map((result) => (
102 | - {result.title}
103 | ))}
104 |
105 |
106 | )
107 | }
108 | `,
109 | },
110 | {
111 | name: "Subscribe to external state",
112 | code: js`
113 | import { subscribeToStatus } from 'library';
114 |
115 | function Status({ topic }) {
116 | const [status, setStatus] = useState();
117 |
118 | useEffect(() => {
119 | const unsubscribe = subscribeToStatus(topic, (status) => {
120 | setStatus(status);
121 | });
122 |
123 | return () => unsubscribe();
124 | }, [topic]);
125 |
126 | return {status}
;
127 | }
128 | `,
129 | },
130 | {
131 | name: "From derived external state with multiple calls to setter",
132 | code: js`
133 | function Form() {
134 | const name = useQuery('/name');
135 | const [fullName, setFullName] = useState('');
136 |
137 | useEffect(() => {
138 | const prefixedName = 'Dr. ' + name;
139 | setFullName(prefixedName)
140 | }, [name]);
141 |
142 | return (
143 | setFullName(e.target.value)}
148 | />
149 | )
150 | }
151 | `,
152 | },
153 | {
154 | name: "From external state via member function",
155 | code: js`
156 | function Counter() {
157 | const countGetter = useSomeAPI();
158 | const [count, setCount] = useState(0);
159 |
160 | useEffect(() => {
161 | const newCount = countGetter.getCount();
162 | setCount(newCount);
163 | }, [countGetter, setCount]);
164 | }
165 | `,
166 | },
167 | {
168 | name: "Via pure local function",
169 | code: js`
170 | function DoubleCounter() {
171 | const [count, setCount] = useState(0);
172 | const [doubleCount, setDoubleCount] = useState(0);
173 |
174 | function calculateDoubleCount(count) {
175 | return count * 2;
176 | }
177 |
178 | useEffect(() => {
179 | setDoubleCount(calculateDoubleCount(count));
180 | }, [count]);
181 | }
182 | `,
183 | },
184 | {
185 | name: "Via unpure local function",
186 | code: js`
187 | function Counter() {
188 | const [count, setCount] = useState(0);
189 |
190 | function calculate(count) {
191 | return count * fetch('/multipler');
192 | }
193 |
194 | useEffect(() => {
195 | setCount(calculate(count));
196 | }, [count]);
197 | }
198 | `,
199 | },
200 | {
201 | name: "From props via unpure derived setter",
202 | code: js`
203 | function DoubleCounter({ count }) {
204 | const [doubleCount, setDoubleCount] = useState(0);
205 |
206 | const derivedSetter = (count) => {
207 | const multipler = fetch('/multipler');
208 | setDoubleCount(count);
209 | }
210 |
211 | useEffect(() => {
212 | derivedSetter(count);
213 | }, [count]);
214 | }
215 | `,
216 | },
217 | {
218 | name: "Via pure global function",
219 | code: js`
220 | function Counter({ count }) {
221 | const [countJson, setCountJson] = useState();
222 |
223 | useEffect(() => {
224 | setCountJson(JSON.stringify(count));
225 | }, [count]);
226 | }
227 | `,
228 | },
229 | {
230 | name: "Via unpure global function",
231 | code: js`
232 | function Counter({ count }) {
233 | const [multipliedCount, setMultipliedCount] = useState();
234 |
235 | useEffect(() => {
236 | const multipler = fetch('/multipler');
237 | setMultipliedCount(count * multipler);
238 | }, [count]);
239 | }
240 | `,
241 | },
242 | {
243 | name: "From internal and external state",
244 | code: js`
245 | import { getPrefixFor } from 'library';
246 | import { useState } from 'react';
247 |
248 | function Component() {
249 | const [name, setName] = useState();
250 | const [prefixedName, setPrefixedName] = useState();
251 | const prefix = getPrefixFor(name);
252 |
253 | useEffect(() => {
254 | setPrefixedName(prefix + name);
255 | }, [name, prefix])
256 | }
257 | `,
258 | },
259 | {
260 | name: "From derived internal and external state",
261 | code: js`
262 | import { getPrefixFor } from 'library';
263 | import { useState } from 'react';
264 |
265 | function Component() {
266 | const [name, setName] = useState();
267 | const [prefixedName, setPrefixedName] = useState();
268 | const prefix = getPrefixFor(name);
269 | const newValue = prefix + name;
270 |
271 | useEffect(() => {
272 | setPrefixedName(newValue);
273 | }, [newValue])
274 | }
275 | `,
276 | },
277 | {
278 | name: "From external state via useCallback derived setter",
279 | code: js`
280 | import { getPrefixFor } from 'library';
281 | import { useState } from 'react';
282 |
283 | function Component() {
284 | const [name, setName] = useState();
285 | const [prefixedName, setPrefixedName] = useState();
286 | const prefix = getPrefixFor(name);
287 |
288 | const derivedSetter = useCallback((name) => {
289 | setPrefixedName(prefix + name);
290 | }, [prefix]);
291 |
292 | useEffect(() => {
293 | derivedSetter(name);
294 | }, [name, derivedSetter])
295 | }
296 | `,
297 | },
298 | ],
299 | invalid: [
300 | {
301 | name: "From internal state",
302 | code: js`
303 | function Form() {
304 | const [firstName, setFirstName] = useState('Taylor');
305 | const [lastName, setLastName] = useState('Swift');
306 |
307 | const [fullName, setFullName] = useState('');
308 | useEffect(() => setFullName(firstName + ' ' + lastName), [firstName, lastName]);
309 | }
310 | `,
311 | errors: [
312 | {
313 | messageId: messageIds.avoidDerivedState,
314 | data: { state: "fullName" },
315 | },
316 | ],
317 | },
318 | {
319 | name: "From derived internal state",
320 | code: js`
321 | function Form() {
322 | const [firstName, setFirstName] = useState('Taylor');
323 | const [lastName, setLastName] = useState('Swift');
324 | const [fullName, setFullName] = useState('');
325 |
326 | useEffect(() => {
327 | const name = firstName + ' ' + lastName;
328 | setFullName(name)
329 | }, [firstName, lastName]);
330 | }
331 | `,
332 | errors: [
333 | {
334 | messageId: messageIds.avoidDerivedState,
335 | data: { state: "fullName" },
336 | },
337 | ],
338 | },
339 | {
340 | name: "From derived internal state outside effect",
341 | code: js`
342 | function Form() {
343 | const [firstName, setFirstName] = useState('Taylor');
344 | const [lastName, setLastName] = useState('Swift');
345 | const [fullName, setFullName] = useState('');
346 | const name = firstName + ' ' + lastName;
347 |
348 | useEffect(() => {
349 | setFullName(name)
350 | }, [name]);
351 | }
352 | `,
353 | errors: [
354 | {
355 | messageId: messageIds.avoidDerivedState,
356 | data: { state: "fullName" },
357 | },
358 | ],
359 | },
360 | {
361 | name: "From props",
362 | code: js`
363 | function Form({ firstName, lastName }) {
364 | const [fullName, setFullName] = useState('');
365 |
366 | useEffect(() => {
367 | setFullName(firstName + ' ' + lastName);
368 | }, [firstName, lastName]);
369 | }
370 | `,
371 | errors: [
372 | {
373 | messageId: messageIds.avoidDerivedState,
374 | data: { state: "fullName" },
375 | },
376 | ],
377 | },
378 | {
379 | name: "From derived prop",
380 | code: js`
381 | function Form({ firstName, lastName }) {
382 | const [fullName, setFullName] = useState('');
383 | const prefixedName = 'Dr. ' + firstName;
384 |
385 | useEffect(() => {
386 | setFullName(prefixedName + ' ' + lastName);
387 | }, [prefixedName, lastName]);
388 | }
389 | `,
390 | errors: [
391 | {
392 | messageId: messageIds.avoidDerivedState,
393 | data: { state: "fullName" },
394 | },
395 | ],
396 | },
397 | {
398 | name: "From props via member function",
399 | code: js`
400 | function DoubleList({ list }) {
401 | const [doubleList, setDoubleList] = useState([]);
402 |
403 | useEffect(() => {
404 | setDoubleList(list.concat(list));
405 | }, [list]);
406 | }
407 | `,
408 | errors: [
409 | {
410 | messageId: messageIds.avoidDerivedState,
411 | data: { state: "doubleList" },
412 | },
413 | {
414 | // We consider `list.concat` to essentially be a prop callback
415 | messageId: messageIds.avoidParentChildCoupling,
416 | },
417 | ],
418 | },
419 | {
420 | name: "From internal state via member function",
421 | code: js`
422 | function DoubleList() {
423 | const [list, setList] = useState([]);
424 | const [doubleList, setDoubleList] = useState([]);
425 |
426 | useEffect(() => {
427 | setDoubleList(list.concat(list));
428 | }, [list]);
429 | }
430 | `,
431 | errors: [
432 | {
433 | messageId: messageIds.avoidDerivedState,
434 | data: { state: "doubleList" },
435 | },
436 | {
437 | // We consider `list.concat` to essentially be a state setter call
438 | messageId: messageIds.avoidDerivedState,
439 | data: { state: "list" },
440 | },
441 | ],
442 | },
443 | {
444 | name: "Mutate internal state",
445 | code: js`
446 | function DoubleList() {
447 | const [list, setList] = useState([]);
448 | const [doubleList, setDoubleList] = useState([]);
449 |
450 | useEffect(() => {
451 | doubleList.push(...list);
452 | }, [list]);
453 | }
454 | `,
455 | errors: [
456 | {
457 | // We consider `doubleList.push` to essentially be a state setter call
458 | messageId: messageIds.avoidDerivedState,
459 | data: { state: "doubleList" },
460 | },
461 | ],
462 | },
463 | {
464 | name: "From external state with single setter call",
465 | todo: true,
466 | code: js`
467 | function Feed() {
468 | const { data: posts } = useQuery('/posts');
469 | const [selectedPost, setSelectedPost] = useState();
470 |
471 | useEffect(() => {
472 | // This is the only place that modifies the state,
473 | // thus they will always be in sync and it could be computed during render
474 | // Difficult bit is that a single state setter call is legit when the
475 | // external state is initialized inside the effect (i.e. retrieved from external system)
476 | // Hopefully 'isDirectCall' will mostly catch that now.
477 | setSelectedPost(posts[0]);
478 | }, [posts]);
479 | }
480 | `,
481 | errors: [
482 | {
483 | messageId: messageIds.avoidDerivedState,
484 | data: { state: "selectedPost" },
485 | },
486 | ],
487 | },
488 | {
489 | name: "From derived external state with single setter call",
490 | todo: true,
491 | code: js`
492 | function Form() {
493 | const name = useQuery('/name');
494 | const [fullName, setFullName] = useState('');
495 |
496 | useEffect(() => {
497 | const prefixedName = 'Dr. ' + name;
498 | setFullName(prefixedName)
499 | }, [name]);
500 | }
501 | `,
502 | errors: [
503 | {
504 | messageId: messageIds.avoidDerivedState,
505 | data: { state: "fullName" },
506 | },
507 | ],
508 | },
509 | {
510 | name: "From HOC prop with single setter call",
511 | todo: true,
512 | code: js`
513 | import { withRouter } from 'react-router-dom';
514 |
515 | const MyComponent = withRouter(({ history }) => {
516 | const [location, setLocation] = useState();
517 |
518 | useEffect(() => {
519 | setLocation(history.location);
520 | }, [history.location]);
521 | });
522 | `,
523 | errors: [
524 | {
525 | messageId: messageIds.avoidDerivedState,
526 | data: { state: "fullName" },
527 | },
528 | ],
529 | },
530 | {
531 | name: "From props via callback setter",
532 | code: js`
533 | import { useState, useEffect } from 'react';
534 |
535 | function CountAccumulator({ count }) {
536 | const [total, setTotal] = useState(count);
537 |
538 | useEffect(() => {
539 | setTotal((prev) => prev + count);
540 | }, [count]);
541 | }
542 | `,
543 | errors: [
544 | {
545 | messageId: messageIds.avoidDerivedState,
546 | data: { state: "total" },
547 | },
548 | ],
549 | },
550 | {
551 | name: "From props via pure derived setter",
552 | code: js`
553 | function DoubleCounter({ count }) {
554 | const [doubleCount, setDoubleCount] = useState(0);
555 |
556 | const derivedSetter = (count) => setDoubleCount(count * 2);
557 |
558 | useEffect(() => {
559 | derivedSetter(count);
560 | }, [count]);
561 | }
562 | `,
563 | errors: [
564 | {
565 | messageId: messageIds.avoidDerivedState,
566 | data: { state: "doubleCount" },
567 | },
568 | ],
569 | },
570 | {
571 | name: "From internal state via useCallback derived setter",
572 | todo: true,
573 | code: js`
574 | import { getPrefixFor } from 'library';
575 | import { useState } from 'react';
576 |
577 | function Component() {
578 | const [name, setName] = useState();
579 | const [prefixedName, setPrefixedName] = useState();
580 | const prefix = 'Dr. ';
581 |
582 | const derivedSetter = useCallback((name) => {
583 | setPrefixedName(prefix + name);
584 | }, [prefix]);
585 |
586 | useEffect(() => {
587 | derivedSetter(name);
588 | }, [name, derivedSetter]);
589 | }
590 | `,
591 | errors: [
592 | {
593 | messageId: messageIds.avoidDerivedState,
594 | data: { state: "prefixedName" },
595 | },
596 | ],
597 | },
598 | {
599 | name: "Partially update complex state from props",
600 | code: js`
601 | function Form({ firstName, lastName }) {
602 | const [formData, setFormData] = useState({
603 | title: 'Dr.',
604 | fullName: '',
605 | });
606 |
607 | useEffect(() => {
608 | setFormData({
609 | ...formData,
610 | fullName: firstName + ' ' + lastName,
611 | });
612 | }, [firstName, lastName, formData]);
613 | }
614 | `,
615 | errors: [
616 | {
617 | messageId: messageIds.avoidDerivedState,
618 | data: { state: "formData" },
619 | },
620 | ],
621 | },
622 | {
623 | name: "Partially update complex state from props via callback setter",
624 | code: js`
625 | function Form({ firstName, lastName }) {
626 | const [formData, setFormData] = useState({
627 | title: 'Dr.',
628 | fullName: '',
629 | });
630 |
631 | useEffect(() => {
632 | setFormData((prev) => ({
633 | ...prev,
634 | fullName: firstName + ' ' + lastName,
635 | }));
636 | }, [firstName, lastName]);
637 | }
638 | `,
639 | errors: [
640 | {
641 | messageId: messageIds.avoidDerivedState,
642 | data: { state: "formData" },
643 | },
644 | ],
645 | },
646 | {
647 | name: "Partially update complex state from props via derived setter",
648 | code: js`
649 | function Form({ firstName, lastName }) {
650 | const [formData, setFormData] = useState({
651 | title: 'Dr.',
652 | fullName: '',
653 | });
654 |
655 | const setFullName = (fullName) => setFormData({ ...formData, fullName });
656 |
657 | useEffect(() => {
658 | setFormData({
659 | ...formData,
660 | fullName: firstName + ' ' + lastName,
661 | });
662 | }, [firstName, lastName, formData]);
663 | }
664 | `,
665 | errors: [
666 | {
667 | messageId: messageIds.avoidDerivedState,
668 | data: { state: "formData" },
669 | },
670 | ],
671 | },
672 | {
673 | name: "Derived state in larger, otherwise legit effect",
674 | code: js`
675 | function Form() {
676 | const [firstName, setFirstName] = useState('Taylor');
677 | const [lastName, setLastName] = useState('Swift');
678 | const [fullName, setFullName] = useState('');
679 |
680 | useEffect(() => {
681 | console.log(name);
682 |
683 | setFullName(firstName + ' ' + lastName);
684 | }, [firstName, lastName]);
685 | }
686 | `,
687 | errors: [
688 | {
689 | messageId: messageIds.avoidDerivedState,
690 | data: { state: "fullName" },
691 | },
692 | ],
693 | },
694 | ],
695 | });
696 |
--------------------------------------------------------------------------------
/test/empty-effect.test.js:
--------------------------------------------------------------------------------
1 | import { MyRuleTester, js } from "./rule-tester.js";
2 | import { messageIds } from "../src/messages.js";
3 |
4 | new MyRuleTester().run("/empty-effect", {
5 | valid: [
6 | {
7 | name: "Valid effect",
8 | code: js`
9 | function Component() {
10 | useEffect(() => {
11 | console.log("Meow");
12 | }, []);
13 | }
14 | `,
15 | },
16 | ],
17 | invalid: [
18 | {
19 | name: "Empty effect",
20 | code: js`
21 | function Component() {
22 | useEffect(() => {}, []);
23 | }
24 | `,
25 | errors: [
26 | {
27 | messageId: messageIds.avoidEmptyEffect,
28 | },
29 | ],
30 | },
31 | ],
32 | });
33 |
--------------------------------------------------------------------------------
/test/initializing-state.test.js:
--------------------------------------------------------------------------------
1 | import { MyRuleTester, js } from "./rule-tester.js";
2 | import { messageIds } from "../src/messages.js";
3 |
4 | new MyRuleTester().run("/initializing-state", {
5 | valid: [
6 | {
7 | name: "To external data",
8 | code: js`
9 | function MyComponent() {
10 | const [state, setState] = useState();
11 |
12 | useEffect(() => {
13 | fetch("https://api.example.com/data")
14 | .then(response => response.json())
15 | .then(data => setState(data));
16 | }, []);
17 | }
18 | `,
19 | },
20 | ],
21 | invalid: [
22 | {
23 | name: "To literal",
24 | code: js`
25 | function MyComponent() {
26 | const [state, setState] = useState();
27 |
28 | useEffect(() => {
29 | setState("Hello");
30 | }, []);
31 |
32 | return {state}
;
33 | }
34 | `,
35 | errors: [
36 | {
37 | messageId: messageIds.avoidInitializingState,
38 | data: { state: "state" },
39 | },
40 | ],
41 | },
42 | {
43 | name: "To internal data",
44 | code: js`
45 | function MyComponent() {
46 | const [state, setState] = useState();
47 | const [otherState, setOtherState] = useState('Meow');
48 |
49 | useEffect(() => {
50 | setState(otherState);
51 | }, []);
52 | }
53 | `,
54 | errors: [
55 | {
56 | messageId: messageIds.avoidInitializingState,
57 | data: { state: "state" },
58 | },
59 | {
60 | messageId: messageIds.avoidDerivedState,
61 | },
62 | ],
63 | },
64 | {
65 | name: "In an otherwise valid effect",
66 | code: js`
67 | function MyComponent() {
68 | const [state, setState] = useState();
69 |
70 | useEffect(() => {
71 | console.log('Meow');
72 | setState('Hello World');
73 | }, []);
74 | }
75 | `,
76 | errors: [
77 | {
78 | messageId: messageIds.avoidInitializingState,
79 | data: { state: "state" },
80 | },
81 | ],
82 | },
83 | ],
84 | });
85 |
--------------------------------------------------------------------------------
/test/parent-child-coupling.test.js:
--------------------------------------------------------------------------------
1 | import { MyRuleTester, js } from "./rule-tester.js";
2 | import { messageIds } from "../src/messages.js";
3 |
4 | new MyRuleTester().run("/parent-child-coupling", {
5 | valid: [
6 | {
7 | name: "Prop from library HOC used internally",
8 | code: js`
9 | import { withRouter } from 'react-router-dom';
10 |
11 | const MyComponent = withRouter(({ history }) => {
12 | const [option, setOption] = useState();
13 |
14 | useEffect(() => {
15 | history.push('/options/' + option);
16 | }, [option]);
17 | });
18 | `,
19 | },
20 | {
21 | name: "Prop from library HOC used externally",
22 | code: js`
23 | import { withRouter } from 'react-router-dom';
24 |
25 | const MyComponent = withRouter(({ history }) => {
26 | const data = useSomeAPI();
27 |
28 | useEffect(() => {
29 | if (data.error) {
30 | history.push('/error-page');
31 | }
32 | }, [data]);
33 | });
34 | `,
35 | },
36 | ],
37 | invalid: [
38 | {
39 | // Valid wrt this flag.
40 | // Verifies `setSelection` is not considered a prop because it's initialized with a prop.
41 | name: "Using prop in state initializer",
42 | code: js`
43 | function List({ items }) {
44 | const [selection, setSelection] = useState(items[0]);
45 |
46 | useEffect(() => {
47 | setSelection(null);
48 | }, [items]);
49 | }
50 | `,
51 | errors: [
52 | {
53 | messageId: messageIds.avoidChainingState,
54 | },
55 | ],
56 | },
57 | {
58 | name: "Pass internal live state",
59 | code: js`
60 | const Child = ({ onFetched }) => {
61 | const [data, setData] = useState();
62 |
63 | useEffect(() => {
64 | onFetched(data);
65 | }, [onFetched, data]);
66 | }
67 | `,
68 | errors: [
69 | {
70 | messageId: messageIds.avoidParentChildCoupling,
71 | },
72 | ],
73 | },
74 | {
75 | name: "Pass derived internal live state",
76 | code: js`
77 | const Child = ({ onFetched }) => {
78 | const [data, setData] = useState();
79 |
80 | useEffect(() => {
81 | const firstElement = data[0];
82 | onFetched(firstElement);
83 | }, [onFetched, data]);
84 | }
85 | `,
86 | errors: [
87 | {
88 | messageId: messageIds.avoidParentChildCoupling,
89 | },
90 | ],
91 | },
92 | {
93 | name: "Pass internal live state via derived prop",
94 | code: js`
95 | const Child = ({ onFetched }) => {
96 | const [data, setData] = useState();
97 | // No idea why someone would do this, but hey we can catch it
98 | const onFetchedWrapper = onFetched
99 |
100 | useEffect(() => {
101 | onFetchedWrapper(data);
102 | }, [onFetched, data]);
103 | }
104 | `,
105 | errors: [
106 | {
107 | messageId: messageIds.avoidParentChildCoupling,
108 | },
109 | ],
110 | },
111 | {
112 | name: "No-arg prop callback in response to internal state change",
113 | code: js`
114 | function Form({ onClose }) {
115 | const [name, setName] = useState();
116 | const [isOpen, setIsOpen] = useState(true);
117 |
118 | useEffect(() => {
119 | onClose();
120 | }, [isOpen]);
121 |
122 | return (
123 |
124 | )
125 | }
126 | `,
127 | errors: [
128 | {
129 | messageId: messageIds.avoidParentChildCoupling,
130 | },
131 | ],
132 | },
133 | {
134 | name: "Pass live external state",
135 | code: js`
136 | const Child = ({ onFetched }) => {
137 | const data = useSomeAPI();
138 |
139 | useEffect(() => {
140 | onFetched(data);
141 | }, [onFetched, data]);
142 | }
143 | `,
144 | errors: [
145 | {
146 | messageId: messageIds.avoidParentChildCoupling,
147 | },
148 | ],
149 | },
150 | {
151 | name: "Pass derived live external state",
152 | code: js`
153 | const Child = ({ onFetched }) => {
154 | const data = useSomeAPI();
155 | const firstElement = data[0];
156 |
157 | useEffect(() => {
158 | onFetched(firstElement);
159 | }, [onFetched, firstElement]);
160 | }
161 | `,
162 | errors: [
163 | {
164 | messageId: messageIds.avoidParentChildCoupling,
165 | },
166 | ],
167 | },
168 | {
169 | name: "Pass final external state",
170 | code: js`
171 | function Form({ onSubmit }) {
172 | const [name, setName] = useState();
173 | const [dataToSubmit, setDataToSubmit] = useState();
174 |
175 | useEffect(() => {
176 | onSubmit(dataToSubmit);
177 | }, [dataToSubmit]);
178 |
179 | return (
180 |
181 | setName(e.target.value)}
185 | />
186 |
187 |
188 | )
189 | }
190 | `,
191 | errors: [
192 | {
193 | // TODO: Ideally we catch using state as an event handler,
194 | // but not sure how to differentiate that
195 | messageId: messageIds.avoidParentChildCoupling,
196 | },
197 | ],
198 | },
199 | {
200 | name: "Call prop in response to prop change",
201 | code: js`
202 | function Form({ isOpen, events }) {
203 |
204 | useEffect(() => {
205 | if (!isOpen) {
206 | // NOTE: Also verifies that we consider 'events' in 'events.onClose' to be a fn ref
207 | // (It's a MemberExpression under a CallExpression)
208 | events.onClose();
209 | }
210 | }, [isOpen]);
211 | }
212 | `,
213 | errors: [
214 | {
215 | messageId: messageIds.avoidParentChildCoupling,
216 | },
217 | ],
218 | },
219 | {
220 | name: "Derive state from prop function",
221 | code: js`
222 | function FilteredPosts({ posts }) {
223 | const [filteredPosts, setFilteredPosts] = useState([]);
224 |
225 | useEffect(() => {
226 | // Resulting AST node looks like:
227 | // {
228 | // "type": "ArrayPattern",
229 | // "elements": [
230 | // null, <-- Must handle this!
231 | // {
232 | // "type": "Identifier",
233 | // "name": "second"
234 | // }
235 | // ]
236 | // }
237 | setFilteredPosts(
238 | posts.filter((post) => post.body !== "")
239 | );
240 | }, [posts]);
241 | }
242 | `,
243 | errors: [
244 | {
245 | messageId: messageIds.avoidDerivedState,
246 | },
247 | {
248 | messageId: messageIds.avoidParentChildCoupling,
249 | },
250 | ],
251 | },
252 | ],
253 | });
254 |
--------------------------------------------------------------------------------
/test/real-world.test.js:
--------------------------------------------------------------------------------
1 | import { messageIds } from "../src/messages.js";
2 | import { MyRuleTester, js } from "./rule-tester.js";
3 |
4 | // Uses taken from the real world, as opposed to contrived examples
5 | new MyRuleTester().run("/real-world", {
6 | valid: [
7 | {
8 | name: "Managing a timer",
9 | code: js`
10 | function Timer() {
11 | const [seconds, setSeconds] = useState(0);
12 |
13 | useEffect(() => {
14 | const interval = setInterval(() => {
15 | setSeconds((s) => s + 1);
16 | }, 1000);
17 |
18 | return () => {
19 | clearInterval(interval);
20 | }
21 | }, []);
22 |
23 | return {seconds}
;
24 | }
25 | `,
26 | },
27 | {
28 | // https://github.com/NickvanDyke/eslint-plugin-react-you-might-not-need-an-effect/issues/11
29 | name: "Debouncing",
30 | code: js`
31 | function useDebouncedState(value, delay) {
32 | const [state, setState] = useState(value);
33 | const [debouncedState, setDebouncedState] = useState(value);
34 |
35 | useEffect(() => {
36 | const timeout = setTimeout(() => {
37 | setDebouncedState(state);
38 | }, delay);
39 |
40 | return () => {
41 | clearTimeout(timeout);
42 | };
43 | }, [delay, state]);
44 |
45 | return [state, debouncedState, setState];
46 | }
47 | `,
48 | },
49 | {
50 | name: "Listening for window events",
51 | code: js`
52 | function WindowSize() {
53 | const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });
54 |
55 | useEffect(() => {
56 | const handleResize = () => {
57 | setSize({ width: window.innerWidth, height: window.innerHeight });
58 | };
59 |
60 | window.addEventListener('resize', handleResize);
61 |
62 | return () => {
63 | window.removeEventListener('resize', handleResize);
64 | };
65 | }, []);
66 |
67 | return {size.width} x {size.height}
;
68 | }
69 | `,
70 | },
71 | {
72 | name: "Play/pausing DOM video",
73 | // Could technically play/pause the video in the `onClick` handler,
74 | // but the use of an effect to sync state is arguably more readable and a valid use.
75 | code: js`
76 | function VideoPlayer() {
77 | const [isPlaying, setIsPlaying] = useState(false);
78 | const videoRef = useRef();
79 |
80 | useEffect(() => {
81 | if (isPlaying) {
82 | videoRef.current.play();
83 | } else {
84 | videoRef.current.pause();
85 | }
86 | }, [isPlaying]);
87 |
88 | return
89 |
90 |
92 | }
93 | `,
94 | },
95 | {
96 | name: "Saving to LocalStorage",
97 | code: js`
98 | function Notes() {
99 | const [notes, setNotes] = useState(() => {
100 | const savedNotes = localStorage.getItem('notes');
101 | return savedNotes ? JSON.parse(savedNotes) : [];
102 | });
103 |
104 | useEffect(() => {
105 | localStorage.setItem('notes', JSON.stringify(notes));
106 | }, [notes]);
107 |
108 | return setNotes(e.target.value)}
112 | />
113 | }
114 | `,
115 | },
116 | {
117 | name: "Logging/Analytics",
118 | code: js`
119 | function Nav() {
120 | const [page, setPage] = useState('home');
121 |
122 | useEffect(() => {
123 | console.log("page viewed", page);
124 | }, [page]);
125 |
126 | return (
127 |
128 |
129 |
130 |
{page}
131 |
132 | )
133 | }
134 | `,
135 | },
136 | {
137 | // This might be a code smell, but people do it
138 | name: "JSON.stringifying in deps",
139 | code: js`
140 | function Feed() {
141 | const [posts, setPosts] = useState([]);
142 | const [scrollPosition, setScrollPosition] = useState(0);
143 |
144 | useEffect(() => {
145 | setScrollPosition(0);
146 | // We can't be sure JSON.stringify is pure, so we can't warn about this.
147 | // TODO: Technically we could check against known pure functions.
148 | }, [JSON.stringify(posts)]);
149 | }
150 | `,
151 | },
152 | {
153 | // Taken from https://github.com/linhnguyen-gt/react-native-phone-number-input/blob/b5e6dc652fa8a03609efb72607dc6866f5556ca3/src/countryPickerModal/CountryPicker.tsx
154 | name: "CountryPicker",
155 | code: js`
156 | function CountryPicker({ withEmoji }) {
157 | const { translation, getCountries } = useContext();
158 |
159 | const [state, setState] = useState({
160 | countries: [],
161 | selectedCountry: null,
162 | });
163 | const setCountries = (countries) => setState({ ...state, countries });
164 |
165 | useEffect(() => {
166 | let cancel = false;
167 | getCountries(translation)
168 | .then((countries) => (cancel ? null : setCountries(countries)))
169 | .catch(console.warn);
170 |
171 | return () => {
172 | cancel = true;
173 | };
174 | },
175 | // Important to the test: Leads us to find useStates to check their initializers
176 | [translation, withEmoji]
177 | );
178 | }
179 | `,
180 | },
181 | {
182 | // https://github.com/NickvanDyke/eslint-plugin-react-you-might-not-need-an-effect/issues/7
183 | name: "Klarna",
184 | code: js`
185 | function Klarna({ klarnaAppId }) {
186 | const [countryCode] = useState(qs.parse('countryCode=meow'));
187 | const [result, setResult] = useState();
188 | const klarnaEnabled = useSelector('idk') && shouldKlarnaBeEnabled(countryCode);
189 | const currentLocale = getCurrentLocale(useGetCurrentLanguage());
190 |
191 | const loadSignInWithKlarna = (klarnaAppId, klarnaEnvironment, countryCode, currentLocale) => {
192 | const klarnaResult = doSomething();
193 | setResult(klarnaResult);
194 | };
195 |
196 | useEffect(() => {
197 | if (klarnaEnabled) {
198 | return loadSignInWithKlarna(
199 | klarnaAppId,
200 | klarnaEnvironment,
201 | countryCode?.toUpperCase(),
202 | currentLocale,
203 | );
204 | }
205 | }, [
206 | countryCode,
207 | klarnaAppId,
208 | klarnaEnabled,
209 | klarnaEnvironment,
210 | currentLocale,
211 | ]);
212 | }
213 | `,
214 | },
215 | {
216 | // https://github.com/NickvanDyke/eslint-plugin-react-you-might-not-need-an-effect/issues/10
217 | name: "navigation.setOptions",
218 | code: js`
219 | import { useNavigation } from '@react-navigation/native';
220 | import { useState, useLayoutEffect } from 'react';
221 |
222 | function ProfileScreen({ route }) {
223 | const navigation = useNavigation();
224 | const [value, onChangeText] = React.useState(route.params.title);
225 |
226 | React.useLayoutEffect(() => {
227 | navigation.setOptions({
228 | title: value === '' ? 'No title' : value,
229 | });
230 | }, [navigation, route]);
231 | }
232 | `,
233 | },
234 | {
235 | // https://github.com/NickvanDyke/eslint-plugin-react-you-might-not-need-an-effect/issues/9#issuecomment-2913950378
236 | name: "Keyboard state listener",
237 | code: js`
238 | import { useEffect, useState } from 'react';
239 | import keyboardReducer from './reducers';
240 |
241 | let globalKeyboardState = {
242 | recentlyUsed: []
243 | };
244 |
245 | export const keyboardStateListeners = new Set();
246 |
247 | const setKeyboardState = (action) => {
248 | globalKeyboardState = keyboardReducer(globalKeyboardState, action);
249 | keyboardStateListeners.forEach((listener) => listener(globalKeyboardState));
250 | };
251 |
252 | export const useKeyboardStore = () => {
253 | const [keyboardState, setState] = useState(globalKeyboardState);
254 |
255 | useEffect(() => {
256 | const listener = () => setState(globalKeyboardState);
257 | keyboardStateListeners.add(listener);
258 | return () => {
259 | keyboardStateListeners.delete(listener);
260 | };
261 | }, [keyboardState]);
262 |
263 | return { keyboardState, setKeyboardState };
264 | };
265 |
266 | useKeyboardStore.setKeyboardState = setKeyboardState;
267 | `,
268 | },
269 | ],
270 | invalid: [
271 | {
272 | // https://github.com/NickvanDyke/eslint-plugin-react-you-might-not-need-an-effect/issues/8
273 | name: "Meow",
274 | code: js`
275 | const ExternalAssetItemRow = memo(
276 | ({
277 | id,
278 | title,
279 | exportIdentifier,
280 | localId,
281 | hasUpdate,
282 | isViewOnly,
283 | getMenuOptions,
284 | onUpdate,
285 | onDragStart,
286 | Icon,
287 | exitMode,
288 | }) => {
289 | const [shouldUpdate, setShouldUpdate] = useState(hasUpdate);
290 |
291 | useEffect(() => {
292 | setShouldUpdate(hasUpdate);
293 | }, [hasUpdate]);
294 |
295 | const onClickUpdate = useCallback(
296 | (event) => {
297 | event.stopPropagation();
298 |
299 | if (isViewOnly) return;
300 |
301 | setShouldUpdate(false);
302 | },
303 | [onUpdate, exportIdentifier, title, isViewOnly],
304 | );
305 |
306 | const handleDragStart = useCallback(
307 | (event) => {
308 | exitMode();
309 | onDragStart(event, exportIdentifier);
310 | },
311 | [onDragStart, exportIdentifier],
312 | );
313 |
314 | const getMenu = useCallback(
315 | (id) => getMenuOptions(id, exportIdentifier, title, localId),
316 | [getMenuOptions, exportIdentifier, title, localId],
317 | );
318 |
319 | return (
320 |
326 |
327 | )
328 | },
329 | );
330 | `,
331 | errors: [
332 | {
333 | // TODO: Because the initial state is internal, derived state would be a better flag.
334 | messageId: messageIds.avoidResettingStateFromProps,
335 | },
336 | ],
337 | },
338 | ],
339 | });
340 |
--------------------------------------------------------------------------------
/test/resetting-state-from-props.test.js:
--------------------------------------------------------------------------------
1 | import { MyRuleTester, js } from "./rule-tester.js";
2 | import { messageIds } from "../src/messages.js";
3 |
4 | new MyRuleTester().run("/resetting-state-from-props", {
5 | invalid: [
6 | {
7 | // Valid wrt this flag
8 | name: "Set state when a prop changes, but not to its default value",
9 | code: js`
10 | function List({ items }) {
11 | const [selection, setSelection] = useState();
12 |
13 | useEffect(() => {
14 | setSelection(items[0]);
15 | }, [items]);
16 | }
17 | `,
18 | errors: [
19 | {
20 | messageId: messageIds.avoidDerivedState,
21 | },
22 | ],
23 | },
24 | {
25 | // Valid wrt this flag
26 | name: "Reset some state when a prop changes",
27 | code: js`
28 | function ProfilePage({ userId }) {
29 | const [user, setUser] = useState(null);
30 | const [comment, setComment] = useState('type something');
31 | const [catName, setCatName] = useState('Sparky');
32 |
33 | useEffect(() => {
34 | setUser(null);
35 | setComment('meow')
36 | }, [userId]);
37 | }
38 | `,
39 | errors: [
40 | {
41 | messageId: messageIds.avoidChainingState,
42 | },
43 | {
44 | messageId: messageIds.avoidChainingState,
45 | },
46 | ],
47 | },
48 | {
49 | name: "Reset all state when a prop changes",
50 | code: js`
51 | function ProfilePage({ userId }) {
52 | const [user, setUser] = useState(null);
53 | const [comment, setComment] = useState('type something');
54 |
55 | useEffect(() => {
56 | setUser(null);
57 | setComment('type something');
58 | }, [userId]);
59 | }
60 | `,
61 | errors: [
62 | {
63 | messageId: messageIds.avoidResettingStateFromProps,
64 | data: { prop: "userId" },
65 | },
66 | ],
67 | },
68 | {
69 | name: "Reset all state to shared var when a prop changes",
70 | code: js`
71 | function ProfilePage({ userId }) {
72 | const initialState = 'meow meow'
73 | const [user, setUser] = useState(null);
74 | const [comment, setComment] = useState(initialState);
75 |
76 | useEffect(() => {
77 | setUser(null);
78 | setComment(initialState);
79 | }, [userId]);
80 | }
81 | `,
82 | errors: [
83 | {
84 | messageId: messageIds.avoidResettingStateFromProps,
85 | data: { prop: "userId" },
86 | },
87 | ],
88 | },
89 | {
90 | name: "Reset all state when a prop member changes",
91 | code: js`
92 | function ProfilePage({ user }) {
93 | const [comment, setComment] = useState('type something');
94 |
95 | useEffect(() => {
96 | setComment('type something');
97 | }, [user.id]);
98 | }
99 | `,
100 | errors: [
101 | {
102 | messageId: messageIds.avoidResettingStateFromProps,
103 | // TODO: Ideally would be "user.id"
104 | data: { prop: "user" },
105 | },
106 | ],
107 | },
108 | {
109 | name: "Reset all state when one of two props change",
110 | code: js`
111 | function ProfilePage({ userId, friends }) {
112 | const [comment, setComment] = useState('type something');
113 |
114 | useEffect(() => {
115 | setComment('type something');
116 | }, [userId, friends]);
117 | }
118 | `,
119 | errors: [
120 | {
121 | messageId: messageIds.avoidResettingStateFromProps,
122 | data: { prop: "userId" },
123 | },
124 | ],
125 | },
126 | {
127 | // These are equivalent because state initializes to `undefined` when it has no argument
128 | name: "Undefined state initializer compared to state setter with literal undefined",
129 | code: js`
130 | function List({ items }) {
131 | const [selectedItem, setSelectedItem] = useState();
132 |
133 | useEffect(() => {
134 | setSelectedItem(undefined);
135 | }, [items]);
136 | }
137 | `,
138 | errors: [
139 | {
140 | messageId: messageIds.avoidResettingStateFromProps,
141 | },
142 | ],
143 | },
144 | {
145 | // Valid wrt this flag - undefined !== null
146 | name: "Undefined state initializer compared to state setter with literal null",
147 | code: js`
148 | function List({ items }) {
149 | const [selectedItem, setSelectedItem] = useState();
150 |
151 | useEffect(() => {
152 | setSelectedItem(null);
153 | }, [items]);
154 | }
155 | `,
156 | errors: [
157 | {
158 | messageId: messageIds.avoidChainingState,
159 | },
160 | ],
161 | },
162 | ],
163 | });
164 |
--------------------------------------------------------------------------------
/test/rule-tester.js:
--------------------------------------------------------------------------------
1 | import assert from "assert";
2 | import { RuleTester } from "eslint";
3 | import plugin from "../src/index.js";
4 |
5 | // For syntax highlighting inside code under test
6 | export const js = String.raw;
7 |
8 | export class MyRuleTester extends RuleTester {
9 | constructor(options) {
10 | super({
11 | ...options,
12 | languageOptions: plugin.configs.recommended.languageOptions,
13 | });
14 | }
15 |
16 | run(variation, tests) {
17 | const originalStrictEqual = assert.strictEqual;
18 | try {
19 | assert.strictEqual = (actual, expected, ...rest) => {
20 | if (typeof actual === "string" && typeof expected === "string") {
21 | return originalStrictEqual(
22 | normalizeWhitespace(actual),
23 | normalizeWhitespace(expected),
24 | ...rest,
25 | );
26 | }
27 | return originalStrictEqual(actual, expected, ...rest);
28 | };
29 |
30 | const { valid = [], invalid = [] } = tests;
31 |
32 | [...valid, ...invalid]
33 | .filter((test) => test.todo)
34 | .forEach((test) => {
35 | it.skip(test.name);
36 | });
37 |
38 | const filteredTests = {
39 | valid: valid.filter((test) => !test.todo),
40 | invalid: invalid.filter((test) => !test.todo),
41 | };
42 |
43 | super.run(
44 | plugin.meta.name + variation,
45 | plugin.rules["you-might-not-need-an-effect"],
46 | filteredTests,
47 | );
48 | } finally {
49 | // Restore the original strictEqual function to avoid unintended effects
50 | assert.strictEqual = originalStrictEqual;
51 | }
52 | }
53 | }
54 |
55 | const normalizeWhitespace = (str) =>
56 | typeof str === "string" ? str.replace(/\s+/g, " ").trim() : str;
57 |
--------------------------------------------------------------------------------
/test/syntax.test.js:
--------------------------------------------------------------------------------
1 | import { MyRuleTester, js } from "./rule-tester.js";
2 | import { messageIds } from "../src/messages.js";
3 |
4 | // TODO: Should maybe do away with this... it helps writing but not readable
5 | const code = ({
6 | componentDeclaration = js`const DoubleCounter = () =>`,
7 | effectBody = js`setDoubleCount(count * 2)`,
8 | effectDeps = js`[count]`,
9 | }) => js`
10 | ${componentDeclaration} {
11 | const [count, setCount] = useState(0);
12 | const [doubleCount, setDoubleCount] = useState(0);
13 |
14 | useEffect(() => ${effectBody}, ${effectDeps});
15 |
16 | return (
17 |
18 |
Count: {count}
19 |
Double Count: {doubleCount}
20 |
21 | );
22 | }
23 | `;
24 |
25 | // Syntax variations that are semantically equivalent
26 | // TODO: Could dynamically generate variations: https://mochajs.org/#dynamically-generating-tests
27 | // Could be overkill; they shouldn't affect each other (supposedly, but I guess that's the point of tests!)
28 | new MyRuleTester().run("/syntax", {
29 | valid: [
30 | {
31 | name: "Two components with overlapping names",
32 | // Not a super realistic example
33 | code: js`
34 | function ComponentOne() {
35 | const [data, setData] = useState();
36 | }
37 |
38 | function ComponentTwo() {
39 | const setData = (data) => {
40 | console.log(data);
41 | }
42 |
43 | useEffect(() => {
44 | setData('hello');
45 | }, []);
46 | }
47 | `,
48 | },
49 | {
50 | // TODO: We don't follow functions passed directly to the effect right now
51 | name: "Passing non-anonymous function to effect",
52 | code: js`
53 | function Form({ onClose }) {
54 | const [name, setName] = useState();
55 | const [isOpen, setIsOpen] = useState(true);
56 |
57 | useEffect(onClose, [isOpen]);
58 | }
59 | `,
60 | },
61 | {
62 | name: "Variable name shadows state name",
63 | code: js`
64 | import { getCountries } from 'library';
65 |
66 | function CountrySelect({ translation }) {
67 | const [countries, setCountries] = useState();
68 |
69 | useEffect(() => {
70 | // Verify that the shadowing variable is not considered a state ref
71 | const countries = getCountries(translation);
72 | setCountries(countries);
73 | },
74 | // Important to the test: Leads us to check useState initializers,
75 | // so we can verify that we don't try to find a useState for the shadowing variable
76 | [translation]
77 | );
78 | }
79 | `,
80 | },
81 | {
82 | name: "Reacting to external state changes with member access in deps",
83 | code: js`
84 | function Feed() {
85 | const { data } = useQuery('/posts');
86 | const [scrollPosition, setScrollPosition] = useState(0);
87 |
88 | useEffect(() => {
89 | setScrollPosition(0);
90 | }, [data.posts]);
91 | }
92 | `,
93 | },
94 | {
95 | name: "Destructured array skips element in arrow function params",
96 | code: js`
97 | function FilteredPosts() {
98 | const posts = useSomeAPI();
99 | const [filteredPosts, setFilteredPosts] = useState([]);
100 |
101 | useEffect(() => {
102 | // Resulting AST node looks like:
103 | // {
104 | // "type": "ArrayPattern",
105 | // "elements": [
106 | // null, <-- Must handle this!
107 | // {
108 | // "type": "Identifier",
109 | // "name": "second"
110 | // }
111 | // ]
112 | // }
113 | setFilteredPosts(
114 | posts.filter(([, value]) => value !== "")
115 | );
116 | }, [posts]);
117 | }
118 | `,
119 | },
120 | {
121 | // https://github.com/NickvanDyke/eslint-plugin-react-you-might-not-need-an-effect/issues/16
122 | name: "External IIFE",
123 | code: js`
124 | import { useEffect, useState } from 'react';
125 |
126 | export const App = () => {
127 | const [response, setResponse] = useState(null);
128 |
129 | const fetchYesNoApi = () => {
130 | return (async () => {
131 | try {
132 | const response = await fetch('https://yesno.wtf/api');
133 | if (!response.ok) {
134 | throw new Error('Network error');
135 | }
136 | const data = await response.json();
137 | setResponse(data);
138 | } catch (err) {
139 | console.error(err);
140 | }
141 | })();
142 | };
143 |
144 | useEffect(() => {
145 | (async () => {
146 | await fetchYesNoApi();
147 | })();
148 | }, []);
149 |
150 | return (
151 | {response}
152 | );
153 | };
154 | `,
155 | },
156 | {
157 | name: "Internal IIFE inside callback",
158 | code: js`
159 | import { useEffect, useState } from 'react';
160 |
161 | export const MyComponent = () => {
162 | const [state, setState] = useState();
163 |
164 | useEffect(() => {
165 | window.addEventListener('load', () => {
166 | (async () => {
167 | setState('Loaded');
168 | })();
169 | });
170 | }, []);
171 | };
172 | `,
173 | },
174 | {
175 | name: "Internal callback inside IIFE",
176 | code: js`
177 | import { useEffect, useState } from 'react';
178 |
179 | export const MyComponent = () => {
180 | const [state, setState] = useState();
181 |
182 | useEffect(() => {
183 | (async () => {
184 | window.addEventListener('load', () => {
185 | setState('Loaded');
186 | });
187 | })();
188 | }, []);
189 | };
190 | `,
191 | },
192 | ],
193 | invalid: [
194 | {
195 | name: "Function component",
196 | code: code({
197 | componentDeclaration: js`function DoubleCounter()`,
198 | }),
199 | errors: 1,
200 | },
201 | {
202 | name: "Arrow function component",
203 | code: code({
204 | componentDeclaration: js`const DoubleCounter = () =>`,
205 | }),
206 | errors: 1,
207 | },
208 | {
209 | name: "Memoized component, with props",
210 | code: js`
211 | const DoubleCounter = memo(({ count }) => {
212 | const [doubleCount, setDoubleCount] = useState(0);
213 |
214 | useEffect(() => setDoubleCount(count), [count]);
215 | });
216 | `,
217 | errors: [
218 | {
219 | messageId: messageIds.avoidDerivedState,
220 | data: { state: "doubleCount" },
221 | },
222 | ],
223 | },
224 | {
225 | name: "Effect one-liner body",
226 | code: code({
227 | componentDeclaration: js`const AvoidDuplicateTest = () =>`,
228 | effectBody: js`setDoubleCount(count * 2)`,
229 | }),
230 | errors: 1,
231 | },
232 | {
233 | name: "Effect single-statement body",
234 | code: code({
235 | effectBody: js`{ setDoubleCount(count * 2); }`,
236 | }),
237 | errors: 1,
238 | },
239 | {
240 | name: "Effect multi-statement body",
241 | code: code({
242 | effectBody: js`{ setDoubleCount(count * 2); setDoubleCount(count * 2); }`,
243 | }),
244 | errors: 2,
245 | },
246 | {
247 | name: "React.useEffect",
248 | code: js`
249 | function DoubleCounter() {
250 | const [count, setCount] = useState(0);
251 | const [doubleCount, setDoubleCount] = useState(0);
252 |
253 | React.useEffect(() => {
254 | setDoubleCount(count * 2);
255 | }, [count]);
256 | }
257 | `,
258 | errors: 1,
259 | },
260 | {
261 | name: "useLayoutEffect",
262 | code: js`
263 | function DoubleCounter() {
264 | const [count, setCount] = useState(0);
265 | const [doubleCount, setDoubleCount] = useState(0);
266 |
267 | useLayoutEffect(() => {
268 | setDoubleCount(count * 2);
269 | }, [count]);
270 | }
271 | `,
272 | errors: 1,
273 | },
274 | {
275 | name: "Non-destructured props",
276 | code: code({
277 | componentDeclaration: js`function DoubleCounter(props)`,
278 | effectBody: js`setDoubleCount(props.count * 2)`,
279 | effectDeps: js`[props.count]`,
280 | }),
281 | errors: 1,
282 | },
283 | {
284 | name: "Destructured props",
285 | code: code({
286 | componentDeclaration: js`function DoubleCounter({ propCount })`,
287 | effectBody: js`setDoubleCount(propCount * 2)`,
288 | effectDeps: js`[propCount]`,
289 | }),
290 | errors: 1,
291 | },
292 | {
293 | name: "Renamed destructured props",
294 | code: code({
295 | componentDeclaration: js`function DoubleCounter({ count: countProp })`,
296 | effectBody: js`setDoubleCount(countProp * 2)`,
297 | effectDeps: js`[countProp]`,
298 | }),
299 | errors: 1,
300 | },
301 | {
302 | name: "Doubly deep MemberExpression in effect",
303 | code: code({
304 | componentDeclaration: js`function DoubleCounter(props)`,
305 | effectBody: js`setDoubleCount(props.nested.count * 2)`,
306 | effectDeps: js`[props.nested.count]`,
307 | }),
308 | errors: 1,
309 | },
310 | {
311 | name: "Objects stored in state",
312 | code: js`
313 | function DoubleCounter() {
314 | const [count, setCount] = useState({ value: 0 });
315 | const [doubleCount, setDoubleCount] = useState({ value: 0 });
316 |
317 | useEffect(() => {
318 | setDoubleCount({ value: count.value * 2 });
319 | }, [count]);
320 | }
321 | `,
322 | errors: 1,
323 | },
324 | {
325 | name: "Optional chaining and nullish coalescing",
326 | code: js`
327 | function DoubleCounter({ count }) {
328 | const [doubleCount, setDoubleCount] = useState(0);
329 |
330 | useEffect(() => {
331 | setDoubleCount((count?.value ?? 1) * 2);
332 | }, [count?.value]);
333 | }
334 | `,
335 | errors: 1,
336 | },
337 | {
338 | // `exhaustive-deps` doesn't enforce member access in the deps
339 | name: "Member access in effect body but not in deps",
340 | code: code({
341 | componentDeclaration: js`function DoubleCounter(props)`,
342 | effectBody: js`setDoubleCount(props.count * 2)`,
343 | effectDeps: js`[props]`,
344 | }),
345 | errors: 1,
346 | },
347 | {
348 | name: "Doubly nested scopes in effect body",
349 | code: code({
350 | effectBody: js`
351 | {
352 | if (count > 10) {
353 | if (count > 100) {
354 | setDoubleCount(count * 4);
355 | } else {
356 | setDoubleCount(count * 2);
357 | }
358 | } else {
359 | setDoubleCount(count);
360 | }
361 | }
362 | `,
363 | }),
364 | errors: 3,
365 | },
366 | {
367 | name: "Destructured array skips element in variable declaration",
368 | code: js`
369 | function SecondPost({ posts }) {
370 | const [secondPost, setSecondPost] = useState();
371 |
372 | useEffect(() => {
373 | const [, second] = posts;
374 | setSecondPost(second);
375 | }, [posts]);
376 | }
377 | `,
378 | errors: 1,
379 | },
380 | {
381 | name: "Value-less useState",
382 | code: js`
383 | import { useState } from 'react';
384 |
385 | function AttemptCounter() {
386 | const [, setAttempts] = useState(0);
387 | const [count, setCount] = useState(0);
388 |
389 | useEffect(() => {
390 | setAttempts((prev) => {
391 | return prev + count;
392 | });
393 | }, [count]);
394 | }
395 | `,
396 | errors: [
397 | {
398 | messageId: messageIds.avoidDerivedState,
399 | data: { state: "setAttempts" },
400 | },
401 | ],
402 | },
403 | {
404 | name: "Setter-less useState",
405 | code: js`
406 | function AttemptCounter() {
407 | const [attempts, setAttempts] = useState(0);
408 | const [count] = useState(0);
409 |
410 | useEffect(() => {
411 | setAttempts(count);
412 | }, [count]);
413 | }
414 | `,
415 | errors: [
416 | {
417 | messageId: messageIds.avoidDerivedState,
418 | data: { state: "attempts" },
419 | },
420 | ],
421 | },
422 | {
423 | name: "Custom hook with state",
424 | code: js`
425 | function useCustomHook() {
426 | const [count, setCount] = useState(0);
427 | const [doubleCount, setDoubleCount] = useState(0);
428 |
429 | useEffect(() => {
430 | setDoubleCount(count * 2);
431 | }, [count]);
432 |
433 | return state;
434 | }
435 |
436 | function Component() {
437 | const customState = useCustomHook();
438 | }
439 | `,
440 | errors: [
441 | {
442 | messageId: messageIds.avoidDerivedState,
443 | data: { state: "doubleCount" },
444 | },
445 | ],
446 | },
447 | {
448 | name: "FunctionDeclaration custom hook with props",
449 | code: js`
450 | function useCustomHook(prop) {
451 | const [state, setState] = useState(0);
452 |
453 | useEffect(() => {
454 | setState(prop);
455 | }, [prop]);
456 |
457 | return state;
458 | }
459 | `,
460 | errors: [
461 | {
462 | messageId: messageIds.avoidDerivedState,
463 | data: { state: "state" },
464 | },
465 | ],
466 | },
467 | {
468 | name: "VariableDeclarator custom hook with object props",
469 | code: js`
470 | const useCustomHook = ({ prop }) => {
471 | const [state, setState] = useState(0);
472 |
473 | useEffect(() => {
474 | setState(prop);
475 | }, [prop]);
476 |
477 | return state;
478 | }
479 | `,
480 | errors: [
481 | {
482 | messageId: messageIds.avoidDerivedState,
483 | data: { state: "state" },
484 | },
485 | ],
486 | },
487 | {
488 | // Verifies that we don't check for upstream state and props in isolation
489 | name: "Derive from both state and props",
490 | code: js`
491 | function Component({ prop }) {
492 | const [state, setState] = useState(0);
493 | const [derived, setDerived] = useState(0);
494 | const combined = state + prop;
495 |
496 | useEffect(() => {
497 | setDerived(combined);
498 | }, [combined]);
499 | }
500 | `,
501 | errors: [
502 | {
503 | messageId: messageIds.avoidDerivedState,
504 | data: { state: "derived" },
505 | },
506 | ],
507 | },
508 | {
509 | // Effects shouldn't be called conditionally, but good to be prepared
510 | name: "Conditional useEffect",
511 | code: js`
512 | function ConditionalEffect({ key }) {
513 | const [state, setState] = useState(0);
514 |
515 | if (condition) {
516 | useEffect(() => {
517 | setState(0);
518 | }, [key]);
519 | }
520 | }
521 | `,
522 | errors: [
523 | {
524 | messageId: messageIds.avoidResettingStateFromProps,
525 | data: { prop: "key" },
526 | },
527 | ],
528 | },
529 | {
530 | // https://github.com/NickvanDyke/eslint-plugin-react-you-might-not-need-an-effect/issues/16
531 | name: "Internal IIFE",
532 | code: js`
533 | import { useEffect, useState } from 'react';
534 |
535 | export const App = () => {
536 | const [data, setData] = useState(null);
537 |
538 | const iife = () => {
539 | return (async () => {
540 | setData('Meow');
541 | })();
542 | };
543 |
544 | useEffect(() => {
545 | (async () => {
546 | await iife();
547 | })();
548 | }, []);
549 | };
550 | `,
551 | errors: [
552 | {
553 | messageId: messageIds.avoidInitializingState,
554 | },
555 | ],
556 | },
557 | ],
558 | });
559 |
--------------------------------------------------------------------------------
/test/using-state-as-event-handler.test.js:
--------------------------------------------------------------------------------
1 | import { MyRuleTester, js } from "./rule-tester.js";
2 | import { messageIds } from "../src/messages.js";
3 |
4 | new MyRuleTester().run("/using-state-as-event-handler", {
5 | invalid: [
6 | {
7 | // TODO: How to detect this though? Not sure it's discernable from legit synchronization effects.
8 | // Maybe when the setter is only called in this one place? Meaning we could instead inline the effect.
9 | name: "Using state to handle an event",
10 | todo: true,
11 | code: js`
12 | function Form() {
13 | const [name, setName] = useState();
14 | const [dataToSubmit, setDataToSubmit] = useState();
15 |
16 | useEffect(() => {
17 | submitData(dataToSubmit);
18 | }, [dataToSubmit]);
19 |
20 | return (
21 |
22 | setName(e.target.value)}
26 | />
27 |
28 |
29 | )
30 | }
31 | `,
32 | errors: [
33 | {
34 | messageId: messageIds.avoidEventHandler,
35 | },
36 | ],
37 | },
38 | ],
39 | });
40 |
--------------------------------------------------------------------------------
/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import type { ESLint, Linter } from "eslint";
2 |
3 | declare const plugin: ESLint.Plugin & {
4 | configs: {
5 | recommended: Linter.Config;
6 | "legacy-recommended": Linter.LegacyConfig;
7 | };
8 | };
9 | export default plugin;
10 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # This file is generated by running "yarn install" inside your project.
2 | # Manual changes might be lost - proceed with caution!
3 |
4 | __metadata:
5 | version: 8
6 | cacheKey: 10c0
7 |
8 | "@esbuild/aix-ppc64@npm:0.25.3":
9 | version: 0.25.3
10 | resolution: "@esbuild/aix-ppc64@npm:0.25.3"
11 | conditions: os=aix & cpu=ppc64
12 | languageName: node
13 | linkType: hard
14 |
15 | "@esbuild/android-arm64@npm:0.25.3":
16 | version: 0.25.3
17 | resolution: "@esbuild/android-arm64@npm:0.25.3"
18 | conditions: os=android & cpu=arm64
19 | languageName: node
20 | linkType: hard
21 |
22 | "@esbuild/android-arm@npm:0.25.3":
23 | version: 0.25.3
24 | resolution: "@esbuild/android-arm@npm:0.25.3"
25 | conditions: os=android & cpu=arm
26 | languageName: node
27 | linkType: hard
28 |
29 | "@esbuild/android-x64@npm:0.25.3":
30 | version: 0.25.3
31 | resolution: "@esbuild/android-x64@npm:0.25.3"
32 | conditions: os=android & cpu=x64
33 | languageName: node
34 | linkType: hard
35 |
36 | "@esbuild/darwin-arm64@npm:0.25.3":
37 | version: 0.25.3
38 | resolution: "@esbuild/darwin-arm64@npm:0.25.3"
39 | conditions: os=darwin & cpu=arm64
40 | languageName: node
41 | linkType: hard
42 |
43 | "@esbuild/darwin-x64@npm:0.25.3":
44 | version: 0.25.3
45 | resolution: "@esbuild/darwin-x64@npm:0.25.3"
46 | conditions: os=darwin & cpu=x64
47 | languageName: node
48 | linkType: hard
49 |
50 | "@esbuild/freebsd-arm64@npm:0.25.3":
51 | version: 0.25.3
52 | resolution: "@esbuild/freebsd-arm64@npm:0.25.3"
53 | conditions: os=freebsd & cpu=arm64
54 | languageName: node
55 | linkType: hard
56 |
57 | "@esbuild/freebsd-x64@npm:0.25.3":
58 | version: 0.25.3
59 | resolution: "@esbuild/freebsd-x64@npm:0.25.3"
60 | conditions: os=freebsd & cpu=x64
61 | languageName: node
62 | linkType: hard
63 |
64 | "@esbuild/linux-arm64@npm:0.25.3":
65 | version: 0.25.3
66 | resolution: "@esbuild/linux-arm64@npm:0.25.3"
67 | conditions: os=linux & cpu=arm64
68 | languageName: node
69 | linkType: hard
70 |
71 | "@esbuild/linux-arm@npm:0.25.3":
72 | version: 0.25.3
73 | resolution: "@esbuild/linux-arm@npm:0.25.3"
74 | conditions: os=linux & cpu=arm
75 | languageName: node
76 | linkType: hard
77 |
78 | "@esbuild/linux-ia32@npm:0.25.3":
79 | version: 0.25.3
80 | resolution: "@esbuild/linux-ia32@npm:0.25.3"
81 | conditions: os=linux & cpu=ia32
82 | languageName: node
83 | linkType: hard
84 |
85 | "@esbuild/linux-loong64@npm:0.25.3":
86 | version: 0.25.3
87 | resolution: "@esbuild/linux-loong64@npm:0.25.3"
88 | conditions: os=linux & cpu=loong64
89 | languageName: node
90 | linkType: hard
91 |
92 | "@esbuild/linux-mips64el@npm:0.25.3":
93 | version: 0.25.3
94 | resolution: "@esbuild/linux-mips64el@npm:0.25.3"
95 | conditions: os=linux & cpu=mips64el
96 | languageName: node
97 | linkType: hard
98 |
99 | "@esbuild/linux-ppc64@npm:0.25.3":
100 | version: 0.25.3
101 | resolution: "@esbuild/linux-ppc64@npm:0.25.3"
102 | conditions: os=linux & cpu=ppc64
103 | languageName: node
104 | linkType: hard
105 |
106 | "@esbuild/linux-riscv64@npm:0.25.3":
107 | version: 0.25.3
108 | resolution: "@esbuild/linux-riscv64@npm:0.25.3"
109 | conditions: os=linux & cpu=riscv64
110 | languageName: node
111 | linkType: hard
112 |
113 | "@esbuild/linux-s390x@npm:0.25.3":
114 | version: 0.25.3
115 | resolution: "@esbuild/linux-s390x@npm:0.25.3"
116 | conditions: os=linux & cpu=s390x
117 | languageName: node
118 | linkType: hard
119 |
120 | "@esbuild/linux-x64@npm:0.25.3":
121 | version: 0.25.3
122 | resolution: "@esbuild/linux-x64@npm:0.25.3"
123 | conditions: os=linux & cpu=x64
124 | languageName: node
125 | linkType: hard
126 |
127 | "@esbuild/netbsd-arm64@npm:0.25.3":
128 | version: 0.25.3
129 | resolution: "@esbuild/netbsd-arm64@npm:0.25.3"
130 | conditions: os=netbsd & cpu=arm64
131 | languageName: node
132 | linkType: hard
133 |
134 | "@esbuild/netbsd-x64@npm:0.25.3":
135 | version: 0.25.3
136 | resolution: "@esbuild/netbsd-x64@npm:0.25.3"
137 | conditions: os=netbsd & cpu=x64
138 | languageName: node
139 | linkType: hard
140 |
141 | "@esbuild/openbsd-arm64@npm:0.25.3":
142 | version: 0.25.3
143 | resolution: "@esbuild/openbsd-arm64@npm:0.25.3"
144 | conditions: os=openbsd & cpu=arm64
145 | languageName: node
146 | linkType: hard
147 |
148 | "@esbuild/openbsd-x64@npm:0.25.3":
149 | version: 0.25.3
150 | resolution: "@esbuild/openbsd-x64@npm:0.25.3"
151 | conditions: os=openbsd & cpu=x64
152 | languageName: node
153 | linkType: hard
154 |
155 | "@esbuild/sunos-x64@npm:0.25.3":
156 | version: 0.25.3
157 | resolution: "@esbuild/sunos-x64@npm:0.25.3"
158 | conditions: os=sunos & cpu=x64
159 | languageName: node
160 | linkType: hard
161 |
162 | "@esbuild/win32-arm64@npm:0.25.3":
163 | version: 0.25.3
164 | resolution: "@esbuild/win32-arm64@npm:0.25.3"
165 | conditions: os=win32 & cpu=arm64
166 | languageName: node
167 | linkType: hard
168 |
169 | "@esbuild/win32-ia32@npm:0.25.3":
170 | version: 0.25.3
171 | resolution: "@esbuild/win32-ia32@npm:0.25.3"
172 | conditions: os=win32 & cpu=ia32
173 | languageName: node
174 | linkType: hard
175 |
176 | "@esbuild/win32-x64@npm:0.25.3":
177 | version: 0.25.3
178 | resolution: "@esbuild/win32-x64@npm:0.25.3"
179 | conditions: os=win32 & cpu=x64
180 | languageName: node
181 | linkType: hard
182 |
183 | "@eslint-community/eslint-utils@npm:^4.1.2, @eslint-community/eslint-utils@npm:^4.5.0":
184 | version: 4.7.0
185 | resolution: "@eslint-community/eslint-utils@npm:4.7.0"
186 | dependencies:
187 | eslint-visitor-keys: "npm:^3.4.3"
188 | peerDependencies:
189 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
190 | checksum: 10c0/c0f4f2bd73b7b7a9de74b716a664873d08ab71ab439e51befe77d61915af41a81ecec93b408778b3a7856185244c34c2c8ee28912072ec14def84ba2dec70adf
191 | languageName: node
192 | linkType: hard
193 |
194 | "@eslint-community/eslint-utils@npm:^4.2.0":
195 | version: 4.4.1
196 | resolution: "@eslint-community/eslint-utils@npm:4.4.1"
197 | dependencies:
198 | eslint-visitor-keys: "npm:^3.4.3"
199 | peerDependencies:
200 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
201 | checksum: 10c0/2aa0ac2fc50ff3f234408b10900ed4f1a0b19352f21346ad4cc3d83a1271481bdda11097baa45d484dd564c895e0762a27a8240be7a256b3ad47129e96528252
202 | languageName: node
203 | linkType: hard
204 |
205 | "@eslint-community/eslint-utils@npm:^4.4.0":
206 | version: 4.6.1
207 | resolution: "@eslint-community/eslint-utils@npm:4.6.1"
208 | dependencies:
209 | eslint-visitor-keys: "npm:^3.4.3"
210 | peerDependencies:
211 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
212 | checksum: 10c0/cdeb6f8fc33a83726357d7f736075cdbd6e79dc7ac4b00b15680f1111d0f33bda583e7fafa5937245a058cc66302dc47568bba57b251302dc74964d8e87f56d7
213 | languageName: node
214 | linkType: hard
215 |
216 | "@eslint-community/regexpp@npm:^4.11.0, @eslint-community/regexpp@npm:^4.12.1":
217 | version: 4.12.1
218 | resolution: "@eslint-community/regexpp@npm:4.12.1"
219 | checksum: 10c0/a03d98c246bcb9109aec2c08e4d10c8d010256538dcb3f56610191607214523d4fb1b00aa81df830b6dffb74c5fa0be03642513a289c567949d3e550ca11cdf6
220 | languageName: node
221 | linkType: hard
222 |
223 | "@eslint/config-array@npm:^0.19.0":
224 | version: 0.19.2
225 | resolution: "@eslint/config-array@npm:0.19.2"
226 | dependencies:
227 | "@eslint/object-schema": "npm:^2.1.6"
228 | debug: "npm:^4.3.1"
229 | minimatch: "npm:^3.1.2"
230 | checksum: 10c0/dd68da9abb32d336233ac4fe0db1e15a0a8d794b6e69abb9e57545d746a97f6f542496ff9db0d7e27fab1438546250d810d90b1904ac67677215b8d8e7573f3d
231 | languageName: node
232 | linkType: hard
233 |
234 | "@eslint/core@npm:^0.10.0":
235 | version: 0.10.0
236 | resolution: "@eslint/core@npm:0.10.0"
237 | dependencies:
238 | "@types/json-schema": "npm:^7.0.15"
239 | checksum: 10c0/074018075079b3ed1f14fab9d116f11a8824cdfae3e822badf7ad546962fafe717a31e61459bad8cc59cf7070dc413ea9064ddb75c114f05b05921029cde0a64
240 | languageName: node
241 | linkType: hard
242 |
243 | "@eslint/core@npm:^0.11.0":
244 | version: 0.11.0
245 | resolution: "@eslint/core@npm:0.11.0"
246 | dependencies:
247 | "@types/json-schema": "npm:^7.0.15"
248 | checksum: 10c0/1e0671d035c908175f445864a7864cf6c6a8b67a5dfba8c47b2ac91e2d3ed36e8c1f2fd81d98a73264f8677055559699d4adb0f97d86588e616fc0dc9a4b86c9
249 | languageName: node
250 | linkType: hard
251 |
252 | "@eslint/eslintrc@npm:^3.2.0":
253 | version: 3.2.0
254 | resolution: "@eslint/eslintrc@npm:3.2.0"
255 | dependencies:
256 | ajv: "npm:^6.12.4"
257 | debug: "npm:^4.3.2"
258 | espree: "npm:^10.0.1"
259 | globals: "npm:^14.0.0"
260 | ignore: "npm:^5.2.0"
261 | import-fresh: "npm:^3.2.1"
262 | js-yaml: "npm:^4.1.0"
263 | minimatch: "npm:^3.1.2"
264 | strip-json-comments: "npm:^3.1.1"
265 | checksum: 10c0/43867a07ff9884d895d9855edba41acf325ef7664a8df41d957135a81a477ff4df4196f5f74dc3382627e5cc8b7ad6b815c2cea1b58f04a75aced7c43414ab8b
266 | languageName: node
267 | linkType: hard
268 |
269 | "@eslint/js@npm:9.20.0":
270 | version: 9.20.0
271 | resolution: "@eslint/js@npm:9.20.0"
272 | checksum: 10c0/10e7b5b9e628b5192e8fc6b0ecd27cf48322947e83e999ff60f9f9e44ac8d499138bcb9383cbfa6e51e780d53b4e76ccc2d1753b108b7173b8404fd484d37328
273 | languageName: node
274 | linkType: hard
275 |
276 | "@eslint/js@npm:^9.28.0":
277 | version: 9.28.0
278 | resolution: "@eslint/js@npm:9.28.0"
279 | checksum: 10c0/5a6759542490dd9f778993edfbc8d2f55168fd0f7336ceed20fe3870c65499d72fc0bca8d1ae00ea246b0923ea4cba2e0758a8a5507a3506ddcf41c92282abb8
280 | languageName: node
281 | linkType: hard
282 |
283 | "@eslint/object-schema@npm:^2.1.6":
284 | version: 2.1.6
285 | resolution: "@eslint/object-schema@npm:2.1.6"
286 | checksum: 10c0/b8cdb7edea5bc5f6a96173f8d768d3554a628327af536da2fc6967a93b040f2557114d98dbcdbf389d5a7b290985ad6a9ce5babc547f36fc1fde42e674d11a56
287 | languageName: node
288 | linkType: hard
289 |
290 | "@eslint/plugin-kit@npm:^0.2.5":
291 | version: 0.2.5
292 | resolution: "@eslint/plugin-kit@npm:0.2.5"
293 | dependencies:
294 | "@eslint/core": "npm:^0.10.0"
295 | levn: "npm:^0.4.1"
296 | checksum: 10c0/ba9832b8409af618cf61791805fe201dd62f3c82c783adfcec0f5cd391e68b40beaecb47b9a3209e926dbcab65135f410cae405b69a559197795793399f61176
297 | languageName: node
298 | linkType: hard
299 |
300 | "@humanfs/core@npm:^0.19.1":
301 | version: 0.19.1
302 | resolution: "@humanfs/core@npm:0.19.1"
303 | checksum: 10c0/aa4e0152171c07879b458d0e8a704b8c3a89a8c0541726c6b65b81e84fd8b7564b5d6c633feadc6598307d34564bd53294b533491424e8e313d7ab6c7bc5dc67
304 | languageName: node
305 | linkType: hard
306 |
307 | "@humanfs/node@npm:^0.16.6":
308 | version: 0.16.6
309 | resolution: "@humanfs/node@npm:0.16.6"
310 | dependencies:
311 | "@humanfs/core": "npm:^0.19.1"
312 | "@humanwhocodes/retry": "npm:^0.3.0"
313 | checksum: 10c0/8356359c9f60108ec204cbd249ecd0356667359b2524886b357617c4a7c3b6aace0fd5a369f63747b926a762a88f8a25bc066fa1778508d110195ce7686243e1
314 | languageName: node
315 | linkType: hard
316 |
317 | "@humanwhocodes/module-importer@npm:^1.0.1":
318 | version: 1.0.1
319 | resolution: "@humanwhocodes/module-importer@npm:1.0.1"
320 | checksum: 10c0/909b69c3b86d482c26b3359db16e46a32e0fb30bd306a3c176b8313b9e7313dba0f37f519de6aa8b0a1921349e505f259d19475e123182416a506d7f87e7f529
321 | languageName: node
322 | linkType: hard
323 |
324 | "@humanwhocodes/retry@npm:^0.3.0":
325 | version: 0.3.1
326 | resolution: "@humanwhocodes/retry@npm:0.3.1"
327 | checksum: 10c0/f0da1282dfb45e8120480b9e2e275e2ac9bbe1cf016d046fdad8e27cc1285c45bb9e711681237944445157b430093412b4446c1ab3fc4bb037861b5904101d3b
328 | languageName: node
329 | linkType: hard
330 |
331 | "@humanwhocodes/retry@npm:^0.4.1":
332 | version: 0.4.1
333 | resolution: "@humanwhocodes/retry@npm:0.4.1"
334 | checksum: 10c0/be7bb6841c4c01d0b767d9bb1ec1c9359ee61421ce8ba66c249d035c5acdfd080f32d55a5c9e859cdd7868788b8935774f65b2caf24ec0b7bd7bf333791f063b
335 | languageName: node
336 | linkType: hard
337 |
338 | "@isaacs/cliui@npm:^8.0.2":
339 | version: 8.0.2
340 | resolution: "@isaacs/cliui@npm:8.0.2"
341 | dependencies:
342 | string-width: "npm:^5.1.2"
343 | string-width-cjs: "npm:string-width@^4.2.0"
344 | strip-ansi: "npm:^7.0.1"
345 | strip-ansi-cjs: "npm:strip-ansi@^6.0.1"
346 | wrap-ansi: "npm:^8.1.0"
347 | wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0"
348 | checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e
349 | languageName: node
350 | linkType: hard
351 |
352 | "@isaacs/fs-minipass@npm:^4.0.0":
353 | version: 4.0.1
354 | resolution: "@isaacs/fs-minipass@npm:4.0.1"
355 | dependencies:
356 | minipass: "npm:^7.0.4"
357 | checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2
358 | languageName: node
359 | linkType: hard
360 |
361 | "@npmcli/agent@npm:^3.0.0":
362 | version: 3.0.0
363 | resolution: "@npmcli/agent@npm:3.0.0"
364 | dependencies:
365 | agent-base: "npm:^7.1.0"
366 | http-proxy-agent: "npm:^7.0.0"
367 | https-proxy-agent: "npm:^7.0.1"
368 | lru-cache: "npm:^10.0.1"
369 | socks-proxy-agent: "npm:^8.0.3"
370 | checksum: 10c0/efe37b982f30740ee77696a80c196912c274ecd2cb243bc6ae7053a50c733ce0f6c09fda085145f33ecf453be19654acca74b69e81eaad4c90f00ccffe2f9271
371 | languageName: node
372 | linkType: hard
373 |
374 | "@npmcli/fs@npm:^4.0.0":
375 | version: 4.0.0
376 | resolution: "@npmcli/fs@npm:4.0.0"
377 | dependencies:
378 | semver: "npm:^7.3.5"
379 | checksum: 10c0/c90935d5ce670c87b6b14fab04a965a3b8137e585f8b2a6257263bd7f97756dd736cb165bb470e5156a9e718ecd99413dccc54b1138c1a46d6ec7cf325982fe5
380 | languageName: node
381 | linkType: hard
382 |
383 | "@pkgjs/parseargs@npm:^0.11.0":
384 | version: 0.11.0
385 | resolution: "@pkgjs/parseargs@npm:0.11.0"
386 | checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd
387 | languageName: node
388 | linkType: hard
389 |
390 | "@types/estree@npm:^1.0.6":
391 | version: 1.0.6
392 | resolution: "@types/estree@npm:1.0.6"
393 | checksum: 10c0/cdfd751f6f9065442cd40957c07fd80361c962869aa853c1c2fd03e101af8b9389d8ff4955a43a6fcfa223dd387a089937f95be0f3eec21ca527039fd2d9859a
394 | languageName: node
395 | linkType: hard
396 |
397 | "@types/json-schema@npm:^7.0.15":
398 | version: 7.0.15
399 | resolution: "@types/json-schema@npm:7.0.15"
400 | checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db
401 | languageName: node
402 | linkType: hard
403 |
404 | "abbrev@npm:^3.0.0":
405 | version: 3.0.1
406 | resolution: "abbrev@npm:3.0.1"
407 | checksum: 10c0/21ba8f574ea57a3106d6d35623f2c4a9111d9ee3e9a5be47baed46ec2457d2eac46e07a5c4a60186f88cb98abbe3e24f2d4cca70bc2b12f1692523e2209a9ccf
408 | languageName: node
409 | linkType: hard
410 |
411 | "acorn-jsx@npm:^5.3.2":
412 | version: 5.3.2
413 | resolution: "acorn-jsx@npm:5.3.2"
414 | peerDependencies:
415 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
416 | checksum: 10c0/4c54868fbef3b8d58927d5e33f0a4de35f59012fe7b12cf9dfbb345fb8f46607709e1c4431be869a23fb63c151033d84c4198fa9f79385cec34fcb1dd53974c1
417 | languageName: node
418 | linkType: hard
419 |
420 | "acorn@npm:^8.14.0":
421 | version: 8.14.0
422 | resolution: "acorn@npm:8.14.0"
423 | bin:
424 | acorn: bin/acorn
425 | checksum: 10c0/6d4ee461a7734b2f48836ee0fbb752903606e576cc100eb49340295129ca0b452f3ba91ddd4424a1d4406a98adfb2ebb6bd0ff4c49d7a0930c10e462719bbfd7
426 | languageName: node
427 | linkType: hard
428 |
429 | "agent-base@npm:^7.1.0, agent-base@npm:^7.1.2":
430 | version: 7.1.3
431 | resolution: "agent-base@npm:7.1.3"
432 | checksum: 10c0/6192b580c5b1d8fb399b9c62bf8343d76654c2dd62afcb9a52b2cf44a8b6ace1e3b704d3fe3547d91555c857d3df02603341ff2cb961b9cfe2b12f9f3c38ee11
433 | languageName: node
434 | linkType: hard
435 |
436 | "ajv@npm:^6.12.4":
437 | version: 6.12.6
438 | resolution: "ajv@npm:6.12.6"
439 | dependencies:
440 | fast-deep-equal: "npm:^3.1.1"
441 | fast-json-stable-stringify: "npm:^2.0.0"
442 | json-schema-traverse: "npm:^0.4.1"
443 | uri-js: "npm:^4.2.2"
444 | checksum: 10c0/41e23642cbe545889245b9d2a45854ebba51cda6c778ebced9649420d9205f2efb39cb43dbc41e358409223b1ea43303ae4839db682c848b891e4811da1a5a71
445 | languageName: node
446 | linkType: hard
447 |
448 | "ansi-colors@npm:^4.1.3":
449 | version: 4.1.3
450 | resolution: "ansi-colors@npm:4.1.3"
451 | checksum: 10c0/ec87a2f59902f74e61eada7f6e6fe20094a628dab765cfdbd03c3477599368768cffccdb5d3bb19a1b6c99126783a143b1fee31aab729b31ffe5836c7e5e28b9
452 | languageName: node
453 | linkType: hard
454 |
455 | "ansi-escapes@npm:^7.0.0":
456 | version: 7.0.0
457 | resolution: "ansi-escapes@npm:7.0.0"
458 | dependencies:
459 | environment: "npm:^1.0.0"
460 | checksum: 10c0/86e51e36fabef18c9c004af0a280573e828900641cea35134a124d2715e0c5a473494ab4ce396614505da77638ae290ff72dd8002d9747d2ee53f5d6bbe336be
461 | languageName: node
462 | linkType: hard
463 |
464 | "ansi-regex@npm:^5.0.1":
465 | version: 5.0.1
466 | resolution: "ansi-regex@npm:5.0.1"
467 | checksum: 10c0/9a64bb8627b434ba9327b60c027742e5d17ac69277960d041898596271d992d4d52ba7267a63ca10232e29f6107fc8a835f6ce8d719b88c5f8493f8254813737
468 | languageName: node
469 | linkType: hard
470 |
471 | "ansi-regex@npm:^6.0.1":
472 | version: 6.1.0
473 | resolution: "ansi-regex@npm:6.1.0"
474 | checksum: 10c0/a91daeddd54746338478eef88af3439a7edf30f8e23196e2d6ed182da9add559c601266dbef01c2efa46a958ad6f1f8b176799657616c702b5b02e799e7fd8dc
475 | languageName: node
476 | linkType: hard
477 |
478 | "ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0":
479 | version: 4.3.0
480 | resolution: "ansi-styles@npm:4.3.0"
481 | dependencies:
482 | color-convert: "npm:^2.0.1"
483 | checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041
484 | languageName: node
485 | linkType: hard
486 |
487 | "ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1":
488 | version: 6.2.1
489 | resolution: "ansi-styles@npm:6.2.1"
490 | checksum: 10c0/5d1ec38c123984bcedd996eac680d548f31828bd679a66db2bdf11844634dde55fec3efa9c6bb1d89056a5e79c1ac540c4c784d592ea1d25028a92227d2f2d5c
491 | languageName: node
492 | linkType: hard
493 |
494 | "anymatch@npm:~3.1.2":
495 | version: 3.1.3
496 | resolution: "anymatch@npm:3.1.3"
497 | dependencies:
498 | normalize-path: "npm:^3.0.0"
499 | picomatch: "npm:^2.0.4"
500 | checksum: 10c0/57b06ae984bc32a0d22592c87384cd88fe4511b1dd7581497831c56d41939c8a001b28e7b853e1450f2bf61992dfcaa8ae2d0d161a0a90c4fb631ef07098fbac
501 | languageName: node
502 | linkType: hard
503 |
504 | "argparse@npm:^2.0.1":
505 | version: 2.0.1
506 | resolution: "argparse@npm:2.0.1"
507 | checksum: 10c0/c5640c2d89045371c7cedd6a70212a04e360fd34d6edeae32f6952c63949e3525ea77dbec0289d8213a99bbaeab5abfa860b5c12cf88a2e6cf8106e90dd27a7e
508 | languageName: node
509 | linkType: hard
510 |
511 | "balanced-match@npm:^1.0.0":
512 | version: 1.0.2
513 | resolution: "balanced-match@npm:1.0.2"
514 | checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee
515 | languageName: node
516 | linkType: hard
517 |
518 | "binary-extensions@npm:^2.0.0":
519 | version: 2.3.0
520 | resolution: "binary-extensions@npm:2.3.0"
521 | checksum: 10c0/75a59cafc10fb12a11d510e77110c6c7ae3f4ca22463d52487709ca7f18f69d886aa387557cc9864fbdb10153d0bdb4caacabf11541f55e89ed6e18d12ece2b5
522 | languageName: node
523 | linkType: hard
524 |
525 | "brace-expansion@npm:^1.1.7":
526 | version: 1.1.11
527 | resolution: "brace-expansion@npm:1.1.11"
528 | dependencies:
529 | balanced-match: "npm:^1.0.0"
530 | concat-map: "npm:0.0.1"
531 | checksum: 10c0/695a56cd058096a7cb71fb09d9d6a7070113c7be516699ed361317aca2ec169f618e28b8af352e02ab4233fb54eb0168460a40dc320bab0034b36ab59aaad668
532 | languageName: node
533 | linkType: hard
534 |
535 | "brace-expansion@npm:^2.0.1":
536 | version: 2.0.1
537 | resolution: "brace-expansion@npm:2.0.1"
538 | dependencies:
539 | balanced-match: "npm:^1.0.0"
540 | checksum: 10c0/b358f2fe060e2d7a87aa015979ecea07f3c37d4018f8d6deb5bd4c229ad3a0384fe6029bb76cd8be63c81e516ee52d1a0673edbe2023d53a5191732ae3c3e49f
541 | languageName: node
542 | linkType: hard
543 |
544 | "braces@npm:^3.0.3, braces@npm:~3.0.2":
545 | version: 3.0.3
546 | resolution: "braces@npm:3.0.3"
547 | dependencies:
548 | fill-range: "npm:^7.1.1"
549 | checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04
550 | languageName: node
551 | linkType: hard
552 |
553 | "browser-stdout@npm:^1.3.1":
554 | version: 1.3.1
555 | resolution: "browser-stdout@npm:1.3.1"
556 | checksum: 10c0/c40e482fd82be872b6ea7b9f7591beafbf6f5ba522fe3dade98ba1573a1c29a11101564993e4eb44e5488be8f44510af072df9a9637c739217eb155ceb639205
557 | languageName: node
558 | linkType: hard
559 |
560 | "cacache@npm:^19.0.1":
561 | version: 19.0.1
562 | resolution: "cacache@npm:19.0.1"
563 | dependencies:
564 | "@npmcli/fs": "npm:^4.0.0"
565 | fs-minipass: "npm:^3.0.0"
566 | glob: "npm:^10.2.2"
567 | lru-cache: "npm:^10.0.1"
568 | minipass: "npm:^7.0.3"
569 | minipass-collect: "npm:^2.0.1"
570 | minipass-flush: "npm:^1.0.5"
571 | minipass-pipeline: "npm:^1.2.4"
572 | p-map: "npm:^7.0.2"
573 | ssri: "npm:^12.0.0"
574 | tar: "npm:^7.4.3"
575 | unique-filename: "npm:^4.0.0"
576 | checksum: 10c0/01f2134e1bd7d3ab68be851df96c8d63b492b1853b67f2eecb2c37bb682d37cb70bb858a16f2f0554d3c0071be6dfe21456a1ff6fa4b7eed996570d6a25ffe9c
577 | languageName: node
578 | linkType: hard
579 |
580 | "callsites@npm:^3.0.0":
581 | version: 3.1.0
582 | resolution: "callsites@npm:3.1.0"
583 | checksum: 10c0/fff92277400eb06c3079f9e74f3af120db9f8ea03bad0e84d9aede54bbe2d44a56cccb5f6cf12211f93f52306df87077ecec5b712794c5a9b5dac6d615a3f301
584 | languageName: node
585 | linkType: hard
586 |
587 | "camelcase@npm:^6.0.0":
588 | version: 6.3.0
589 | resolution: "camelcase@npm:6.3.0"
590 | checksum: 10c0/0d701658219bd3116d12da3eab31acddb3f9440790c0792e0d398f0a520a6a4058018e546862b6fba89d7ae990efaeb97da71e1913e9ebf5a8b5621a3d55c710
591 | languageName: node
592 | linkType: hard
593 |
594 | "chalk@npm:^4.0.0, chalk@npm:^4.1.0":
595 | version: 4.1.2
596 | resolution: "chalk@npm:4.1.2"
597 | dependencies:
598 | ansi-styles: "npm:^4.1.0"
599 | supports-color: "npm:^7.1.0"
600 | checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880
601 | languageName: node
602 | linkType: hard
603 |
604 | "chalk@npm:^5.4.1":
605 | version: 5.4.1
606 | resolution: "chalk@npm:5.4.1"
607 | checksum: 10c0/b23e88132c702f4855ca6d25cb5538b1114343e41472d5263ee8a37cccfccd9c4216d111e1097c6a27830407a1dc81fecdf2a56f2c63033d4dbbd88c10b0dcef
608 | languageName: node
609 | linkType: hard
610 |
611 | "chokidar@npm:^3.5.3":
612 | version: 3.6.0
613 | resolution: "chokidar@npm:3.6.0"
614 | dependencies:
615 | anymatch: "npm:~3.1.2"
616 | braces: "npm:~3.0.2"
617 | fsevents: "npm:~2.3.2"
618 | glob-parent: "npm:~5.1.2"
619 | is-binary-path: "npm:~2.1.0"
620 | is-glob: "npm:~4.0.1"
621 | normalize-path: "npm:~3.0.0"
622 | readdirp: "npm:~3.6.0"
623 | dependenciesMeta:
624 | fsevents:
625 | optional: true
626 | checksum: 10c0/8361dcd013f2ddbe260eacb1f3cb2f2c6f2b0ad118708a343a5ed8158941a39cb8fb1d272e0f389712e74ee90ce8ba864eece9e0e62b9705cb468a2f6d917462
627 | languageName: node
628 | linkType: hard
629 |
630 | "chownr@npm:^3.0.0":
631 | version: 3.0.0
632 | resolution: "chownr@npm:3.0.0"
633 | checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10
634 | languageName: node
635 | linkType: hard
636 |
637 | "cli-cursor@npm:^5.0.0":
638 | version: 5.0.0
639 | resolution: "cli-cursor@npm:5.0.0"
640 | dependencies:
641 | restore-cursor: "npm:^5.0.0"
642 | checksum: 10c0/7ec62f69b79f6734ab209a3e4dbdc8af7422d44d360a7cb1efa8a0887bbe466a6e625650c466fe4359aee44dbe2dc0b6994b583d40a05d0808a5cb193641d220
643 | languageName: node
644 | linkType: hard
645 |
646 | "cli-truncate@npm:^4.0.0":
647 | version: 4.0.0
648 | resolution: "cli-truncate@npm:4.0.0"
649 | dependencies:
650 | slice-ansi: "npm:^5.0.0"
651 | string-width: "npm:^7.0.0"
652 | checksum: 10c0/d7f0b73e3d9b88cb496e6c086df7410b541b56a43d18ade6a573c9c18bd001b1c3fba1ad578f741a4218fdc794d042385f8ac02c25e1c295a2d8b9f3cb86eb4c
653 | languageName: node
654 | linkType: hard
655 |
656 | "cliui@npm:^8.0.1":
657 | version: 8.0.1
658 | resolution: "cliui@npm:8.0.1"
659 | dependencies:
660 | string-width: "npm:^4.2.0"
661 | strip-ansi: "npm:^6.0.1"
662 | wrap-ansi: "npm:^7.0.0"
663 | checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5
664 | languageName: node
665 | linkType: hard
666 |
667 | "color-convert@npm:^2.0.1":
668 | version: 2.0.1
669 | resolution: "color-convert@npm:2.0.1"
670 | dependencies:
671 | color-name: "npm:~1.1.4"
672 | checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7
673 | languageName: node
674 | linkType: hard
675 |
676 | "color-name@npm:~1.1.4":
677 | version: 1.1.4
678 | resolution: "color-name@npm:1.1.4"
679 | checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95
680 | languageName: node
681 | linkType: hard
682 |
683 | "colorette@npm:^2.0.20":
684 | version: 2.0.20
685 | resolution: "colorette@npm:2.0.20"
686 | checksum: 10c0/e94116ff33b0ff56f3b83b9ace895e5bf87c2a7a47b3401b8c3f3226e050d5ef76cf4072fb3325f9dc24d1698f9b730baf4e05eeaf861d74a1883073f4c98a40
687 | languageName: node
688 | linkType: hard
689 |
690 | "commander@npm:^14.0.0":
691 | version: 14.0.0
692 | resolution: "commander@npm:14.0.0"
693 | checksum: 10c0/73c4babfa558077868d84522b11ef56834165d472b9e86a634cd4c3ae7fc72d59af6377d8878e06bd570fe8f3161eced3cbe383c38f7093272bb65bd242b595b
694 | languageName: node
695 | linkType: hard
696 |
697 | "concat-map@npm:0.0.1":
698 | version: 0.0.1
699 | resolution: "concat-map@npm:0.0.1"
700 | checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f
701 | languageName: node
702 | linkType: hard
703 |
704 | "cross-spawn@npm:^7.0.6":
705 | version: 7.0.6
706 | resolution: "cross-spawn@npm:7.0.6"
707 | dependencies:
708 | path-key: "npm:^3.1.0"
709 | shebang-command: "npm:^2.0.0"
710 | which: "npm:^2.0.1"
711 | checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1
712 | languageName: node
713 | linkType: hard
714 |
715 | "debug@npm:4, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5":
716 | version: 4.4.0
717 | resolution: "debug@npm:4.4.0"
718 | dependencies:
719 | ms: "npm:^2.1.3"
720 | peerDependenciesMeta:
721 | supports-color:
722 | optional: true
723 | checksum: 10c0/db94f1a182bf886f57b4755f85b3a74c39b5114b9377b7ab375dc2cfa3454f09490cc6c30f829df3fc8042bc8b8995f6567ce5cd96f3bc3688bd24027197d9de
724 | languageName: node
725 | linkType: hard
726 |
727 | "debug@npm:^4.4.1":
728 | version: 4.4.1
729 | resolution: "debug@npm:4.4.1"
730 | dependencies:
731 | ms: "npm:^2.1.3"
732 | peerDependenciesMeta:
733 | supports-color:
734 | optional: true
735 | checksum: 10c0/d2b44bc1afd912b49bb7ebb0d50a860dc93a4dd7d946e8de94abc957bb63726b7dd5aa48c18c2386c379ec024c46692e15ed3ed97d481729f929201e671fcd55
736 | languageName: node
737 | linkType: hard
738 |
739 | "decamelize@npm:^4.0.0":
740 | version: 4.0.0
741 | resolution: "decamelize@npm:4.0.0"
742 | checksum: 10c0/e06da03fc05333e8cd2778c1487da67ffbea5b84e03ca80449519b8fa61f888714bbc6f459ea963d5641b4aa98832130eb5cd193d90ae9f0a27eee14be8e278d
743 | languageName: node
744 | linkType: hard
745 |
746 | "deep-is@npm:^0.1.3":
747 | version: 0.1.4
748 | resolution: "deep-is@npm:0.1.4"
749 | checksum: 10c0/7f0ee496e0dff14a573dc6127f14c95061b448b87b995fc96c017ce0a1e66af1675e73f1d6064407975bc4ea6ab679497a29fff7b5b9c4e99cb10797c1ad0b4c
750 | languageName: node
751 | linkType: hard
752 |
753 | "diff@npm:^5.2.0":
754 | version: 5.2.0
755 | resolution: "diff@npm:5.2.0"
756 | checksum: 10c0/aed0941f206fe261ecb258dc8d0ceea8abbde3ace5827518ff8d302f0fc9cc81ce116c4d8f379151171336caf0516b79e01abdc1ed1201b6440d895a66689eb4
757 | languageName: node
758 | linkType: hard
759 |
760 | "eastasianwidth@npm:^0.2.0":
761 | version: 0.2.0
762 | resolution: "eastasianwidth@npm:0.2.0"
763 | checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39
764 | languageName: node
765 | linkType: hard
766 |
767 | "emoji-regex@npm:^10.3.0":
768 | version: 10.4.0
769 | resolution: "emoji-regex@npm:10.4.0"
770 | checksum: 10c0/a3fcedfc58bfcce21a05a5f36a529d81e88d602100145fcca3dc6f795e3c8acc4fc18fe773fbf9b6d6e9371205edb3afa2668ec3473fa2aa7fd47d2a9d46482d
771 | languageName: node
772 | linkType: hard
773 |
774 | "emoji-regex@npm:^8.0.0":
775 | version: 8.0.0
776 | resolution: "emoji-regex@npm:8.0.0"
777 | checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010
778 | languageName: node
779 | linkType: hard
780 |
781 | "emoji-regex@npm:^9.2.2":
782 | version: 9.2.2
783 | resolution: "emoji-regex@npm:9.2.2"
784 | checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639
785 | languageName: node
786 | linkType: hard
787 |
788 | "encoding@npm:^0.1.13":
789 | version: 0.1.13
790 | resolution: "encoding@npm:0.1.13"
791 | dependencies:
792 | iconv-lite: "npm:^0.6.2"
793 | checksum: 10c0/36d938712ff00fe1f4bac88b43bcffb5930c1efa57bbcdca9d67e1d9d6c57cfb1200fb01efe0f3109b2ce99b231f90779532814a81370a1bd3274a0f58585039
794 | languageName: node
795 | linkType: hard
796 |
797 | "enhanced-resolve@npm:^5.17.1":
798 | version: 5.18.1
799 | resolution: "enhanced-resolve@npm:5.18.1"
800 | dependencies:
801 | graceful-fs: "npm:^4.2.4"
802 | tapable: "npm:^2.2.0"
803 | checksum: 10c0/4cffd9b125225184e2abed9fdf0ed3dbd2224c873b165d0838fd066cde32e0918626cba2f1f4bf6860762f13a7e2364fd89a82b99566be2873d813573ac71846
804 | languageName: node
805 | linkType: hard
806 |
807 | "env-paths@npm:^2.2.0":
808 | version: 2.2.1
809 | resolution: "env-paths@npm:2.2.1"
810 | checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4
811 | languageName: node
812 | linkType: hard
813 |
814 | "environment@npm:^1.0.0":
815 | version: 1.1.0
816 | resolution: "environment@npm:1.1.0"
817 | checksum: 10c0/fb26434b0b581ab397039e51ff3c92b34924a98b2039dcb47e41b7bca577b9dbf134a8eadb364415c74464b682e2d3afe1a4c0eb9873dc44ea814c5d3103331d
818 | languageName: node
819 | linkType: hard
820 |
821 | "err-code@npm:^2.0.2":
822 | version: 2.0.3
823 | resolution: "err-code@npm:2.0.3"
824 | checksum: 10c0/b642f7b4dd4a376e954947550a3065a9ece6733ab8e51ad80db727aaae0817c2e99b02a97a3d6cecc648a97848305e728289cf312d09af395403a90c9d4d8a66
825 | languageName: node
826 | linkType: hard
827 |
828 | "esbuild@npm:^0.25.3":
829 | version: 0.25.3
830 | resolution: "esbuild@npm:0.25.3"
831 | dependencies:
832 | "@esbuild/aix-ppc64": "npm:0.25.3"
833 | "@esbuild/android-arm": "npm:0.25.3"
834 | "@esbuild/android-arm64": "npm:0.25.3"
835 | "@esbuild/android-x64": "npm:0.25.3"
836 | "@esbuild/darwin-arm64": "npm:0.25.3"
837 | "@esbuild/darwin-x64": "npm:0.25.3"
838 | "@esbuild/freebsd-arm64": "npm:0.25.3"
839 | "@esbuild/freebsd-x64": "npm:0.25.3"
840 | "@esbuild/linux-arm": "npm:0.25.3"
841 | "@esbuild/linux-arm64": "npm:0.25.3"
842 | "@esbuild/linux-ia32": "npm:0.25.3"
843 | "@esbuild/linux-loong64": "npm:0.25.3"
844 | "@esbuild/linux-mips64el": "npm:0.25.3"
845 | "@esbuild/linux-ppc64": "npm:0.25.3"
846 | "@esbuild/linux-riscv64": "npm:0.25.3"
847 | "@esbuild/linux-s390x": "npm:0.25.3"
848 | "@esbuild/linux-x64": "npm:0.25.3"
849 | "@esbuild/netbsd-arm64": "npm:0.25.3"
850 | "@esbuild/netbsd-x64": "npm:0.25.3"
851 | "@esbuild/openbsd-arm64": "npm:0.25.3"
852 | "@esbuild/openbsd-x64": "npm:0.25.3"
853 | "@esbuild/sunos-x64": "npm:0.25.3"
854 | "@esbuild/win32-arm64": "npm:0.25.3"
855 | "@esbuild/win32-ia32": "npm:0.25.3"
856 | "@esbuild/win32-x64": "npm:0.25.3"
857 | dependenciesMeta:
858 | "@esbuild/aix-ppc64":
859 | optional: true
860 | "@esbuild/android-arm":
861 | optional: true
862 | "@esbuild/android-arm64":
863 | optional: true
864 | "@esbuild/android-x64":
865 | optional: true
866 | "@esbuild/darwin-arm64":
867 | optional: true
868 | "@esbuild/darwin-x64":
869 | optional: true
870 | "@esbuild/freebsd-arm64":
871 | optional: true
872 | "@esbuild/freebsd-x64":
873 | optional: true
874 | "@esbuild/linux-arm":
875 | optional: true
876 | "@esbuild/linux-arm64":
877 | optional: true
878 | "@esbuild/linux-ia32":
879 | optional: true
880 | "@esbuild/linux-loong64":
881 | optional: true
882 | "@esbuild/linux-mips64el":
883 | optional: true
884 | "@esbuild/linux-ppc64":
885 | optional: true
886 | "@esbuild/linux-riscv64":
887 | optional: true
888 | "@esbuild/linux-s390x":
889 | optional: true
890 | "@esbuild/linux-x64":
891 | optional: true
892 | "@esbuild/netbsd-arm64":
893 | optional: true
894 | "@esbuild/netbsd-x64":
895 | optional: true
896 | "@esbuild/openbsd-arm64":
897 | optional: true
898 | "@esbuild/openbsd-x64":
899 | optional: true
900 | "@esbuild/sunos-x64":
901 | optional: true
902 | "@esbuild/win32-arm64":
903 | optional: true
904 | "@esbuild/win32-ia32":
905 | optional: true
906 | "@esbuild/win32-x64":
907 | optional: true
908 | bin:
909 | esbuild: bin/esbuild
910 | checksum: 10c0/127aff654310ede4e2eb232a7b1d8823f5b5d69222caf17aa7f172574a5b6b75f71ce78c6d8a40030421d7c75b784dc640de0fb1b87b7ea77ab2a1c832fa8df8
911 | languageName: node
912 | linkType: hard
913 |
914 | "escalade@npm:^3.1.1":
915 | version: 3.2.0
916 | resolution: "escalade@npm:3.2.0"
917 | checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65
918 | languageName: node
919 | linkType: hard
920 |
921 | "escape-string-regexp@npm:^4.0.0":
922 | version: 4.0.0
923 | resolution: "escape-string-regexp@npm:4.0.0"
924 | checksum: 10c0/9497d4dd307d845bd7f75180d8188bb17ea8c151c1edbf6b6717c100e104d629dc2dfb687686181b0f4b7d732c7dfdc4d5e7a8ff72de1b0ca283a75bbb3a9cd9
925 | languageName: node
926 | linkType: hard
927 |
928 | "eslint-compat-utils@npm:^0.5.1":
929 | version: 0.5.1
930 | resolution: "eslint-compat-utils@npm:0.5.1"
931 | dependencies:
932 | semver: "npm:^7.5.4"
933 | peerDependencies:
934 | eslint: ">=6.0.0"
935 | checksum: 10c0/325e815205fab70ebcd379f6d4b5d44c7d791bb8dfe0c9888233f30ebabd9418422595b53a781b946c768d9244d858540e5e6129a6b3dd6d606f467d599edc6c
936 | languageName: node
937 | linkType: hard
938 |
939 | "eslint-plugin-es-x@npm:^7.8.0":
940 | version: 7.8.0
941 | resolution: "eslint-plugin-es-x@npm:7.8.0"
942 | dependencies:
943 | "@eslint-community/eslint-utils": "npm:^4.1.2"
944 | "@eslint-community/regexpp": "npm:^4.11.0"
945 | eslint-compat-utils: "npm:^0.5.1"
946 | peerDependencies:
947 | eslint: ">=8"
948 | checksum: 10c0/002fda8c029bc5da41e24e7ac11654062831d675fc4f5f20d0de460e24bf1e05cd559000678ef3e46c48641190f4fc07ae3d57aa5e8b085ef5f67e5f63742614
949 | languageName: node
950 | linkType: hard
951 |
952 | "eslint-plugin-eslint-plugin@npm:^6.4.0":
953 | version: 6.4.0
954 | resolution: "eslint-plugin-eslint-plugin@npm:6.4.0"
955 | dependencies:
956 | "@eslint-community/eslint-utils": "npm:^4.4.0"
957 | estraverse: "npm:^5.3.0"
958 | peerDependencies:
959 | eslint: ">=8.23.0"
960 | checksum: 10c0/c34a976d70e3d8ef91f6adb7ff6a89d149531c8624da07b2ef2696c44d4a0184c4c5baf8a63e18f53fd13468b2b491c72bcbb1832178e96a18bce54ad385dca5
961 | languageName: node
962 | linkType: hard
963 |
964 | "eslint-plugin-n@npm:^17.17.0":
965 | version: 17.17.0
966 | resolution: "eslint-plugin-n@npm:17.17.0"
967 | dependencies:
968 | "@eslint-community/eslint-utils": "npm:^4.5.0"
969 | enhanced-resolve: "npm:^5.17.1"
970 | eslint-plugin-es-x: "npm:^7.8.0"
971 | get-tsconfig: "npm:^4.8.1"
972 | globals: "npm:^15.11.0"
973 | ignore: "npm:^5.3.2"
974 | minimatch: "npm:^9.0.5"
975 | semver: "npm:^7.6.3"
976 | peerDependencies:
977 | eslint: ">=8.23.0"
978 | checksum: 10c0/ac6b2e2bbdc8f49a84be1bf1add8a412093a56fe95e8820610ecd5185fa00a348197a06fe3fe36080c09dc5d5a8f0f4f543cb3cf193265ace3fd071a79a07e88
979 | languageName: node
980 | linkType: hard
981 |
982 | "eslint-plugin-react-you-might-not-need-an-effect@workspace:.":
983 | version: 0.0.0-use.local
984 | resolution: "eslint-plugin-react-you-might-not-need-an-effect@workspace:."
985 | dependencies:
986 | "@eslint/js": "npm:^9.28.0"
987 | esbuild: "npm:^0.25.3"
988 | eslint: "npm:^9.20.1"
989 | eslint-plugin-eslint-plugin: "npm:^6.4.0"
990 | eslint-plugin-n: "npm:^17.17.0"
991 | eslint-utils: "npm:^3.0.0"
992 | globals: "npm:^16.2.0"
993 | lint-staged: "npm:^16.1.0"
994 | mocha: "npm:11.1.0"
995 | prettier: "npm:^3.5.3"
996 | simple-git-hooks: "npm:^2.13.0"
997 | peerDependencies:
998 | eslint: ">=7.0.0"
999 | languageName: unknown
1000 | linkType: soft
1001 |
1002 | "eslint-scope@npm:^8.2.0":
1003 | version: 8.2.0
1004 | resolution: "eslint-scope@npm:8.2.0"
1005 | dependencies:
1006 | esrecurse: "npm:^4.3.0"
1007 | estraverse: "npm:^5.2.0"
1008 | checksum: 10c0/8d2d58e2136d548ac7e0099b1a90d9fab56f990d86eb518de1247a7066d38c908be2f3df477a79cf60d70b30ba18735d6c6e70e9914dca2ee515a729975d70d6
1009 | languageName: node
1010 | linkType: hard
1011 |
1012 | "eslint-utils@npm:^3.0.0":
1013 | version: 3.0.0
1014 | resolution: "eslint-utils@npm:3.0.0"
1015 | dependencies:
1016 | eslint-visitor-keys: "npm:^2.0.0"
1017 | peerDependencies:
1018 | eslint: ">=5"
1019 | checksum: 10c0/45aa2b63667a8d9b474c98c28af908d0a592bed1a4568f3145cd49fb5d9510f545327ec95561625290313fe126e6d7bdfe3fdbdb6f432689fab6b9497d3bfb52
1020 | languageName: node
1021 | linkType: hard
1022 |
1023 | "eslint-visitor-keys@npm:^2.0.0":
1024 | version: 2.1.0
1025 | resolution: "eslint-visitor-keys@npm:2.1.0"
1026 | checksum: 10c0/9f0e3a2db751d84067d15977ac4b4472efd6b303e369e6ff241a99feac04da758f46d5add022c33d06b53596038dbae4b4aceb27c7e68b8dfc1055b35e495787
1027 | languageName: node
1028 | linkType: hard
1029 |
1030 | "eslint-visitor-keys@npm:^3.4.3":
1031 | version: 3.4.3
1032 | resolution: "eslint-visitor-keys@npm:3.4.3"
1033 | checksum: 10c0/92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820
1034 | languageName: node
1035 | linkType: hard
1036 |
1037 | "eslint-visitor-keys@npm:^4.2.0":
1038 | version: 4.2.0
1039 | resolution: "eslint-visitor-keys@npm:4.2.0"
1040 | checksum: 10c0/2ed81c663b147ca6f578312919483eb040295bbab759e5a371953456c636c5b49a559883e2677112453728d66293c0a4c90ab11cab3428cf02a0236d2e738269
1041 | languageName: node
1042 | linkType: hard
1043 |
1044 | "eslint@npm:^9.20.1":
1045 | version: 9.20.1
1046 | resolution: "eslint@npm:9.20.1"
1047 | dependencies:
1048 | "@eslint-community/eslint-utils": "npm:^4.2.0"
1049 | "@eslint-community/regexpp": "npm:^4.12.1"
1050 | "@eslint/config-array": "npm:^0.19.0"
1051 | "@eslint/core": "npm:^0.11.0"
1052 | "@eslint/eslintrc": "npm:^3.2.0"
1053 | "@eslint/js": "npm:9.20.0"
1054 | "@eslint/plugin-kit": "npm:^0.2.5"
1055 | "@humanfs/node": "npm:^0.16.6"
1056 | "@humanwhocodes/module-importer": "npm:^1.0.1"
1057 | "@humanwhocodes/retry": "npm:^0.4.1"
1058 | "@types/estree": "npm:^1.0.6"
1059 | "@types/json-schema": "npm:^7.0.15"
1060 | ajv: "npm:^6.12.4"
1061 | chalk: "npm:^4.0.0"
1062 | cross-spawn: "npm:^7.0.6"
1063 | debug: "npm:^4.3.2"
1064 | escape-string-regexp: "npm:^4.0.0"
1065 | eslint-scope: "npm:^8.2.0"
1066 | eslint-visitor-keys: "npm:^4.2.0"
1067 | espree: "npm:^10.3.0"
1068 | esquery: "npm:^1.5.0"
1069 | esutils: "npm:^2.0.2"
1070 | fast-deep-equal: "npm:^3.1.3"
1071 | file-entry-cache: "npm:^8.0.0"
1072 | find-up: "npm:^5.0.0"
1073 | glob-parent: "npm:^6.0.2"
1074 | ignore: "npm:^5.2.0"
1075 | imurmurhash: "npm:^0.1.4"
1076 | is-glob: "npm:^4.0.0"
1077 | json-stable-stringify-without-jsonify: "npm:^1.0.1"
1078 | lodash.merge: "npm:^4.6.2"
1079 | minimatch: "npm:^3.1.2"
1080 | natural-compare: "npm:^1.4.0"
1081 | optionator: "npm:^0.9.3"
1082 | peerDependencies:
1083 | jiti: "*"
1084 | peerDependenciesMeta:
1085 | jiti:
1086 | optional: true
1087 | bin:
1088 | eslint: bin/eslint.js
1089 | checksum: 10c0/056789dd5a00897730376f8c0a191e22840e97b7276916068ec096341cb2ec3a918c8bd474bf94ccd7b457ad9fbc16e5c521a993c7cc6ebcf241933e2fd378b0
1090 | languageName: node
1091 | linkType: hard
1092 |
1093 | "espree@npm:^10.0.1, espree@npm:^10.3.0":
1094 | version: 10.3.0
1095 | resolution: "espree@npm:10.3.0"
1096 | dependencies:
1097 | acorn: "npm:^8.14.0"
1098 | acorn-jsx: "npm:^5.3.2"
1099 | eslint-visitor-keys: "npm:^4.2.0"
1100 | checksum: 10c0/272beeaca70d0a1a047d61baff64db04664a33d7cfb5d144f84bc8a5c6194c6c8ebe9cc594093ca53add88baa23e59b01e69e8a0160ab32eac570482e165c462
1101 | languageName: node
1102 | linkType: hard
1103 |
1104 | "esquery@npm:^1.5.0":
1105 | version: 1.6.0
1106 | resolution: "esquery@npm:1.6.0"
1107 | dependencies:
1108 | estraverse: "npm:^5.1.0"
1109 | checksum: 10c0/cb9065ec605f9da7a76ca6dadb0619dfb611e37a81e318732977d90fab50a256b95fee2d925fba7c2f3f0523aa16f91587246693bc09bc34d5a59575fe6e93d2
1110 | languageName: node
1111 | linkType: hard
1112 |
1113 | "esrecurse@npm:^4.3.0":
1114 | version: 4.3.0
1115 | resolution: "esrecurse@npm:4.3.0"
1116 | dependencies:
1117 | estraverse: "npm:^5.2.0"
1118 | checksum: 10c0/81a37116d1408ded88ada45b9fb16dbd26fba3aadc369ce50fcaf82a0bac12772ebd7b24cd7b91fc66786bf2c1ac7b5f196bc990a473efff972f5cb338877cf5
1119 | languageName: node
1120 | linkType: hard
1121 |
1122 | "estraverse@npm:^5.1.0, estraverse@npm:^5.2.0, estraverse@npm:^5.3.0":
1123 | version: 5.3.0
1124 | resolution: "estraverse@npm:5.3.0"
1125 | checksum: 10c0/1ff9447b96263dec95d6d67431c5e0771eb9776427421260a3e2f0fdd5d6bd4f8e37a7338f5ad2880c9f143450c9b1e4fc2069060724570a49cf9cf0312bd107
1126 | languageName: node
1127 | linkType: hard
1128 |
1129 | "esutils@npm:^2.0.2":
1130 | version: 2.0.3
1131 | resolution: "esutils@npm:2.0.3"
1132 | checksum: 10c0/9a2fe69a41bfdade834ba7c42de4723c97ec776e40656919c62cbd13607c45e127a003f05f724a1ea55e5029a4cf2de444b13009f2af71271e42d93a637137c7
1133 | languageName: node
1134 | linkType: hard
1135 |
1136 | "eventemitter3@npm:^5.0.1":
1137 | version: 5.0.1
1138 | resolution: "eventemitter3@npm:5.0.1"
1139 | checksum: 10c0/4ba5c00c506e6c786b4d6262cfbce90ddc14c10d4667e5c83ae993c9de88aa856033994dd2b35b83e8dc1170e224e66a319fa80adc4c32adcd2379bbc75da814
1140 | languageName: node
1141 | linkType: hard
1142 |
1143 | "exponential-backoff@npm:^3.1.1":
1144 | version: 3.1.2
1145 | resolution: "exponential-backoff@npm:3.1.2"
1146 | checksum: 10c0/d9d3e1eafa21b78464297df91f1776f7fbaa3d5e3f7f0995648ca5b89c069d17055033817348d9f4a43d1c20b0eab84f75af6991751e839df53e4dfd6f22e844
1147 | languageName: node
1148 | linkType: hard
1149 |
1150 | "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3":
1151 | version: 3.1.3
1152 | resolution: "fast-deep-equal@npm:3.1.3"
1153 | checksum: 10c0/40dedc862eb8992c54579c66d914635afbec43350afbbe991235fdcb4e3a8d5af1b23ae7e79bef7d4882d0ecee06c3197488026998fb19f72dc95acff1d1b1d0
1154 | languageName: node
1155 | linkType: hard
1156 |
1157 | "fast-json-stable-stringify@npm:^2.0.0":
1158 | version: 2.1.0
1159 | resolution: "fast-json-stable-stringify@npm:2.1.0"
1160 | checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b
1161 | languageName: node
1162 | linkType: hard
1163 |
1164 | "fast-levenshtein@npm:^2.0.6":
1165 | version: 2.0.6
1166 | resolution: "fast-levenshtein@npm:2.0.6"
1167 | checksum: 10c0/111972b37338bcb88f7d9e2c5907862c280ebf4234433b95bc611e518d192ccb2d38119c4ac86e26b668d75f7f3894f4ff5c4982899afced7ca78633b08287c4
1168 | languageName: node
1169 | linkType: hard
1170 |
1171 | "fdir@npm:^6.4.4":
1172 | version: 6.4.4
1173 | resolution: "fdir@npm:6.4.4"
1174 | peerDependencies:
1175 | picomatch: ^3 || ^4
1176 | peerDependenciesMeta:
1177 | picomatch:
1178 | optional: true
1179 | checksum: 10c0/6ccc33be16945ee7bc841e1b4178c0b4cf18d3804894cb482aa514651c962a162f96da7ffc6ebfaf0df311689fb70091b04dd6caffe28d56b9ebdc0e7ccadfdd
1180 | languageName: node
1181 | linkType: hard
1182 |
1183 | "file-entry-cache@npm:^8.0.0":
1184 | version: 8.0.0
1185 | resolution: "file-entry-cache@npm:8.0.0"
1186 | dependencies:
1187 | flat-cache: "npm:^4.0.0"
1188 | checksum: 10c0/9e2b5938b1cd9b6d7e3612bdc533afd4ac17b2fc646569e9a8abbf2eb48e5eb8e316bc38815a3ef6a1b456f4107f0d0f055a614ca613e75db6bf9ff4d72c1638
1189 | languageName: node
1190 | linkType: hard
1191 |
1192 | "fill-range@npm:^7.1.1":
1193 | version: 7.1.1
1194 | resolution: "fill-range@npm:7.1.1"
1195 | dependencies:
1196 | to-regex-range: "npm:^5.0.1"
1197 | checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018
1198 | languageName: node
1199 | linkType: hard
1200 |
1201 | "find-up@npm:^5.0.0":
1202 | version: 5.0.0
1203 | resolution: "find-up@npm:5.0.0"
1204 | dependencies:
1205 | locate-path: "npm:^6.0.0"
1206 | path-exists: "npm:^4.0.0"
1207 | checksum: 10c0/062c5a83a9c02f53cdd6d175a37ecf8f87ea5bbff1fdfb828f04bfa021441bc7583e8ebc0872a4c1baab96221fb8a8a275a19809fb93fbc40bd69ec35634069a
1208 | languageName: node
1209 | linkType: hard
1210 |
1211 | "flat-cache@npm:^4.0.0":
1212 | version: 4.0.1
1213 | resolution: "flat-cache@npm:4.0.1"
1214 | dependencies:
1215 | flatted: "npm:^3.2.9"
1216 | keyv: "npm:^4.5.4"
1217 | checksum: 10c0/2c59d93e9faa2523e4fda6b4ada749bed432cfa28c8e251f33b25795e426a1c6dbada777afb1f74fcfff33934fdbdea921ee738fcc33e71adc9d6eca984a1cfc
1218 | languageName: node
1219 | linkType: hard
1220 |
1221 | "flat@npm:^5.0.2":
1222 | version: 5.0.2
1223 | resolution: "flat@npm:5.0.2"
1224 | bin:
1225 | flat: cli.js
1226 | checksum: 10c0/f178b13482f0cd80c7fede05f4d10585b1f2fdebf26e12edc138e32d3150c6ea6482b7f12813a1091143bad52bb6d3596bca51a162257a21163c0ff438baa5fe
1227 | languageName: node
1228 | linkType: hard
1229 |
1230 | "flatted@npm:^3.2.9":
1231 | version: 3.3.2
1232 | resolution: "flatted@npm:3.3.2"
1233 | checksum: 10c0/24cc735e74d593b6c767fe04f2ef369abe15b62f6906158079b9874bdb3ee5ae7110bb75042e70cd3f99d409d766f357caf78d5ecee9780206f5fdc5edbad334
1234 | languageName: node
1235 | linkType: hard
1236 |
1237 | "foreground-child@npm:^3.1.0":
1238 | version: 3.3.1
1239 | resolution: "foreground-child@npm:3.3.1"
1240 | dependencies:
1241 | cross-spawn: "npm:^7.0.6"
1242 | signal-exit: "npm:^4.0.1"
1243 | checksum: 10c0/8986e4af2430896e65bc2788d6679067294d6aee9545daefc84923a0a4b399ad9c7a3ea7bd8c0b2b80fdf4a92de4c69df3f628233ff3224260e9c1541a9e9ed3
1244 | languageName: node
1245 | linkType: hard
1246 |
1247 | "fs-minipass@npm:^3.0.0":
1248 | version: 3.0.3
1249 | resolution: "fs-minipass@npm:3.0.3"
1250 | dependencies:
1251 | minipass: "npm:^7.0.3"
1252 | checksum: 10c0/63e80da2ff9b621e2cb1596abcb9207f1cf82b968b116ccd7b959e3323144cce7fb141462200971c38bbf2ecca51695069db45265705bed09a7cd93ae5b89f94
1253 | languageName: node
1254 | linkType: hard
1255 |
1256 | "fsevents@npm:~2.3.2":
1257 | version: 2.3.3
1258 | resolution: "fsevents@npm:2.3.3"
1259 | dependencies:
1260 | node-gyp: "npm:latest"
1261 | checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60
1262 | conditions: os=darwin
1263 | languageName: node
1264 | linkType: hard
1265 |
1266 | "fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin":
1267 | version: 2.3.3
1268 | resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1"
1269 | dependencies:
1270 | node-gyp: "npm:latest"
1271 | conditions: os=darwin
1272 | languageName: node
1273 | linkType: hard
1274 |
1275 | "get-caller-file@npm:^2.0.5":
1276 | version: 2.0.5
1277 | resolution: "get-caller-file@npm:2.0.5"
1278 | checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde
1279 | languageName: node
1280 | linkType: hard
1281 |
1282 | "get-east-asian-width@npm:^1.0.0":
1283 | version: 1.3.0
1284 | resolution: "get-east-asian-width@npm:1.3.0"
1285 | checksum: 10c0/1a049ba697e0f9a4d5514c4623781c5246982bdb61082da6b5ae6c33d838e52ce6726407df285cdbb27ec1908b333cf2820989bd3e986e37bb20979437fdf34b
1286 | languageName: node
1287 | linkType: hard
1288 |
1289 | "get-tsconfig@npm:^4.8.1":
1290 | version: 4.10.0
1291 | resolution: "get-tsconfig@npm:4.10.0"
1292 | dependencies:
1293 | resolve-pkg-maps: "npm:^1.0.0"
1294 | checksum: 10c0/c9b5572c5118923c491c04285c73bd55b19e214992af957c502a3be0fc0043bb421386ffd45ca3433c0a7fba81221ca300479e8393960acf15d0ed4563f38a86
1295 | languageName: node
1296 | linkType: hard
1297 |
1298 | "glob-parent@npm:^6.0.2":
1299 | version: 6.0.2
1300 | resolution: "glob-parent@npm:6.0.2"
1301 | dependencies:
1302 | is-glob: "npm:^4.0.3"
1303 | checksum: 10c0/317034d88654730230b3f43bb7ad4f7c90257a426e872ea0bf157473ac61c99bf5d205fad8f0185f989be8d2fa6d3c7dce1645d99d545b6ea9089c39f838e7f8
1304 | languageName: node
1305 | linkType: hard
1306 |
1307 | "glob-parent@npm:~5.1.2":
1308 | version: 5.1.2
1309 | resolution: "glob-parent@npm:5.1.2"
1310 | dependencies:
1311 | is-glob: "npm:^4.0.1"
1312 | checksum: 10c0/cab87638e2112bee3f839ef5f6e0765057163d39c66be8ec1602f3823da4692297ad4e972de876ea17c44d652978638d2fd583c6713d0eb6591706825020c9ee
1313 | languageName: node
1314 | linkType: hard
1315 |
1316 | "glob@npm:^10.2.2, glob@npm:^10.4.5":
1317 | version: 10.4.5
1318 | resolution: "glob@npm:10.4.5"
1319 | dependencies:
1320 | foreground-child: "npm:^3.1.0"
1321 | jackspeak: "npm:^3.1.2"
1322 | minimatch: "npm:^9.0.4"
1323 | minipass: "npm:^7.1.2"
1324 | package-json-from-dist: "npm:^1.0.0"
1325 | path-scurry: "npm:^1.11.1"
1326 | bin:
1327 | glob: dist/esm/bin.mjs
1328 | checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e
1329 | languageName: node
1330 | linkType: hard
1331 |
1332 | "globals@npm:^14.0.0":
1333 | version: 14.0.0
1334 | resolution: "globals@npm:14.0.0"
1335 | checksum: 10c0/b96ff42620c9231ad468d4c58ff42afee7777ee1c963013ff8aabe095a451d0ceeb8dcd8ef4cbd64d2538cef45f787a78ba3a9574f4a634438963e334471302d
1336 | languageName: node
1337 | linkType: hard
1338 |
1339 | "globals@npm:^15.11.0":
1340 | version: 15.15.0
1341 | resolution: "globals@npm:15.15.0"
1342 | checksum: 10c0/f9ae80996392ca71316495a39bec88ac43ae3525a438b5626cd9d5ce9d5500d0a98a266409605f8cd7241c7acf57c354a48111ea02a767ba4f374b806d6861fe
1343 | languageName: node
1344 | linkType: hard
1345 |
1346 | "globals@npm:^16.2.0":
1347 | version: 16.2.0
1348 | resolution: "globals@npm:16.2.0"
1349 | checksum: 10c0/c2b3ea163faa6f8a38076b471b12f4bda891f7df7f7d2e8294fb4801d735a51a73431bf4c1696c5bf5dbca5e0a0db894698acfcbd3068730c6b12eef185dea25
1350 | languageName: node
1351 | linkType: hard
1352 |
1353 | "graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6":
1354 | version: 4.2.11
1355 | resolution: "graceful-fs@npm:4.2.11"
1356 | checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2
1357 | languageName: node
1358 | linkType: hard
1359 |
1360 | "has-flag@npm:^4.0.0":
1361 | version: 4.0.0
1362 | resolution: "has-flag@npm:4.0.0"
1363 | checksum: 10c0/2e789c61b7888d66993e14e8331449e525ef42aac53c627cc53d1c3334e768bcb6abdc4f5f0de1478a25beec6f0bd62c7549058b7ac53e924040d4f301f02fd1
1364 | languageName: node
1365 | linkType: hard
1366 |
1367 | "he@npm:^1.2.0":
1368 | version: 1.2.0
1369 | resolution: "he@npm:1.2.0"
1370 | bin:
1371 | he: bin/he
1372 | checksum: 10c0/a27d478befe3c8192f006cdd0639a66798979dfa6e2125c6ac582a19a5ebfec62ad83e8382e6036170d873f46e4536a7e795bf8b95bf7c247f4cc0825ccc8c17
1373 | languageName: node
1374 | linkType: hard
1375 |
1376 | "http-cache-semantics@npm:^4.1.1":
1377 | version: 4.1.1
1378 | resolution: "http-cache-semantics@npm:4.1.1"
1379 | checksum: 10c0/ce1319b8a382eb3cbb4a37c19f6bfe14e5bb5be3d09079e885e8c513ab2d3cd9214902f8a31c9dc4e37022633ceabfc2d697405deeaf1b8f3552bb4ed996fdfc
1380 | languageName: node
1381 | linkType: hard
1382 |
1383 | "http-proxy-agent@npm:^7.0.0":
1384 | version: 7.0.2
1385 | resolution: "http-proxy-agent@npm:7.0.2"
1386 | dependencies:
1387 | agent-base: "npm:^7.1.0"
1388 | debug: "npm:^4.3.4"
1389 | checksum: 10c0/4207b06a4580fb85dd6dff521f0abf6db517489e70863dca1a0291daa7f2d3d2d6015a57bd702af068ea5cf9f1f6ff72314f5f5b4228d299c0904135d2aef921
1390 | languageName: node
1391 | linkType: hard
1392 |
1393 | "https-proxy-agent@npm:^7.0.1":
1394 | version: 7.0.6
1395 | resolution: "https-proxy-agent@npm:7.0.6"
1396 | dependencies:
1397 | agent-base: "npm:^7.1.2"
1398 | debug: "npm:4"
1399 | checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac
1400 | languageName: node
1401 | linkType: hard
1402 |
1403 | "iconv-lite@npm:^0.6.2":
1404 | version: 0.6.3
1405 | resolution: "iconv-lite@npm:0.6.3"
1406 | dependencies:
1407 | safer-buffer: "npm:>= 2.1.2 < 3.0.0"
1408 | checksum: 10c0/98102bc66b33fcf5ac044099d1257ba0b7ad5e3ccd3221f34dd508ab4070edff183276221684e1e0555b145fce0850c9f7d2b60a9fcac50fbb4ea0d6e845a3b1
1409 | languageName: node
1410 | linkType: hard
1411 |
1412 | "ignore@npm:^5.2.0, ignore@npm:^5.3.2":
1413 | version: 5.3.2
1414 | resolution: "ignore@npm:5.3.2"
1415 | checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337
1416 | languageName: node
1417 | linkType: hard
1418 |
1419 | "import-fresh@npm:^3.2.1":
1420 | version: 3.3.1
1421 | resolution: "import-fresh@npm:3.3.1"
1422 | dependencies:
1423 | parent-module: "npm:^1.0.0"
1424 | resolve-from: "npm:^4.0.0"
1425 | checksum: 10c0/bf8cc494872fef783249709385ae883b447e3eb09db0ebd15dcead7d9afe7224dad7bd7591c6b73b0b19b3c0f9640eb8ee884f01cfaf2887ab995b0b36a0cbec
1426 | languageName: node
1427 | linkType: hard
1428 |
1429 | "imurmurhash@npm:^0.1.4":
1430 | version: 0.1.4
1431 | resolution: "imurmurhash@npm:0.1.4"
1432 | checksum: 10c0/8b51313850dd33605c6c9d3fd9638b714f4c4c40250cff658209f30d40da60f78992fb2df5dabee4acf589a6a82bbc79ad5486550754bd9ec4e3fc0d4a57d6a6
1433 | languageName: node
1434 | linkType: hard
1435 |
1436 | "ip-address@npm:^9.0.5":
1437 | version: 9.0.5
1438 | resolution: "ip-address@npm:9.0.5"
1439 | dependencies:
1440 | jsbn: "npm:1.1.0"
1441 | sprintf-js: "npm:^1.1.3"
1442 | checksum: 10c0/331cd07fafcb3b24100613e4b53e1a2b4feab11e671e655d46dc09ee233da5011284d09ca40c4ecbdfe1d0004f462958675c224a804259f2f78d2465a87824bc
1443 | languageName: node
1444 | linkType: hard
1445 |
1446 | "is-binary-path@npm:~2.1.0":
1447 | version: 2.1.0
1448 | resolution: "is-binary-path@npm:2.1.0"
1449 | dependencies:
1450 | binary-extensions: "npm:^2.0.0"
1451 | checksum: 10c0/a16eaee59ae2b315ba36fad5c5dcaf8e49c3e27318f8ab8fa3cdb8772bf559c8d1ba750a589c2ccb096113bb64497084361a25960899cb6172a6925ab6123d38
1452 | languageName: node
1453 | linkType: hard
1454 |
1455 | "is-extglob@npm:^2.1.1":
1456 | version: 2.1.1
1457 | resolution: "is-extglob@npm:2.1.1"
1458 | checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912
1459 | languageName: node
1460 | linkType: hard
1461 |
1462 | "is-fullwidth-code-point@npm:^3.0.0":
1463 | version: 3.0.0
1464 | resolution: "is-fullwidth-code-point@npm:3.0.0"
1465 | checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc
1466 | languageName: node
1467 | linkType: hard
1468 |
1469 | "is-fullwidth-code-point@npm:^4.0.0":
1470 | version: 4.0.0
1471 | resolution: "is-fullwidth-code-point@npm:4.0.0"
1472 | checksum: 10c0/df2a717e813567db0f659c306d61f2f804d480752526886954a2a3e2246c7745fd07a52b5fecf2b68caf0a6c79dcdace6166fdf29cc76ed9975cc334f0a018b8
1473 | languageName: node
1474 | linkType: hard
1475 |
1476 | "is-fullwidth-code-point@npm:^5.0.0":
1477 | version: 5.0.0
1478 | resolution: "is-fullwidth-code-point@npm:5.0.0"
1479 | dependencies:
1480 | get-east-asian-width: "npm:^1.0.0"
1481 | checksum: 10c0/cd591b27d43d76b05fa65ed03eddce57a16e1eca0b7797ff7255de97019bcaf0219acfc0c4f7af13319e13541f2a53c0ace476f442b13267b9a6a7568f2b65c8
1482 | languageName: node
1483 | linkType: hard
1484 |
1485 | "is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1":
1486 | version: 4.0.3
1487 | resolution: "is-glob@npm:4.0.3"
1488 | dependencies:
1489 | is-extglob: "npm:^2.1.1"
1490 | checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a
1491 | languageName: node
1492 | linkType: hard
1493 |
1494 | "is-number@npm:^7.0.0":
1495 | version: 7.0.0
1496 | resolution: "is-number@npm:7.0.0"
1497 | checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811
1498 | languageName: node
1499 | linkType: hard
1500 |
1501 | "is-plain-obj@npm:^2.1.0":
1502 | version: 2.1.0
1503 | resolution: "is-plain-obj@npm:2.1.0"
1504 | checksum: 10c0/e5c9814cdaa627a9ad0a0964ded0e0491bfd9ace405c49a5d63c88b30a162f1512c069d5b80997893c4d0181eadc3fed02b4ab4b81059aba5620bfcdfdeb9c53
1505 | languageName: node
1506 | linkType: hard
1507 |
1508 | "is-unicode-supported@npm:^0.1.0":
1509 | version: 0.1.0
1510 | resolution: "is-unicode-supported@npm:0.1.0"
1511 | checksum: 10c0/00cbe3455c3756be68d2542c416cab888aebd5012781d6819749fefb15162ff23e38501fe681b3d751c73e8ff561ac09a5293eba6f58fdf0178462ce6dcb3453
1512 | languageName: node
1513 | linkType: hard
1514 |
1515 | "isexe@npm:^2.0.0":
1516 | version: 2.0.0
1517 | resolution: "isexe@npm:2.0.0"
1518 | checksum: 10c0/228cfa503fadc2c31596ab06ed6aa82c9976eec2bfd83397e7eaf06d0ccf42cd1dfd6743bf9aeb01aebd4156d009994c5f76ea898d2832c1fe342da923ca457d
1519 | languageName: node
1520 | linkType: hard
1521 |
1522 | "isexe@npm:^3.1.1":
1523 | version: 3.1.1
1524 | resolution: "isexe@npm:3.1.1"
1525 | checksum: 10c0/9ec257654093443eb0a528a9c8cbba9c0ca7616ccb40abd6dde7202734d96bb86e4ac0d764f0f8cd965856aacbff2f4ce23e730dc19dfb41e3b0d865ca6fdcc7
1526 | languageName: node
1527 | linkType: hard
1528 |
1529 | "jackspeak@npm:^3.1.2":
1530 | version: 3.4.3
1531 | resolution: "jackspeak@npm:3.4.3"
1532 | dependencies:
1533 | "@isaacs/cliui": "npm:^8.0.2"
1534 | "@pkgjs/parseargs": "npm:^0.11.0"
1535 | dependenciesMeta:
1536 | "@pkgjs/parseargs":
1537 | optional: true
1538 | checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9
1539 | languageName: node
1540 | linkType: hard
1541 |
1542 | "js-yaml@npm:^4.1.0":
1543 | version: 4.1.0
1544 | resolution: "js-yaml@npm:4.1.0"
1545 | dependencies:
1546 | argparse: "npm:^2.0.1"
1547 | bin:
1548 | js-yaml: bin/js-yaml.js
1549 | checksum: 10c0/184a24b4eaacfce40ad9074c64fd42ac83cf74d8c8cd137718d456ced75051229e5061b8633c3366b8aada17945a7a356b337828c19da92b51ae62126575018f
1550 | languageName: node
1551 | linkType: hard
1552 |
1553 | "jsbn@npm:1.1.0":
1554 | version: 1.1.0
1555 | resolution: "jsbn@npm:1.1.0"
1556 | checksum: 10c0/4f907fb78d7b712e11dea8c165fe0921f81a657d3443dde75359ed52eb2b5d33ce6773d97985a089f09a65edd80b11cb75c767b57ba47391fee4c969f7215c96
1557 | languageName: node
1558 | linkType: hard
1559 |
1560 | "json-buffer@npm:3.0.1":
1561 | version: 3.0.1
1562 | resolution: "json-buffer@npm:3.0.1"
1563 | checksum: 10c0/0d1c91569d9588e7eef2b49b59851f297f3ab93c7b35c7c221e288099322be6b562767d11e4821da500f3219542b9afd2e54c5dc573107c1126ed1080f8e96d7
1564 | languageName: node
1565 | linkType: hard
1566 |
1567 | "json-schema-traverse@npm:^0.4.1":
1568 | version: 0.4.1
1569 | resolution: "json-schema-traverse@npm:0.4.1"
1570 | checksum: 10c0/108fa90d4cc6f08243aedc6da16c408daf81793bf903e9fd5ab21983cda433d5d2da49e40711da016289465ec2e62e0324dcdfbc06275a607fe3233fde4942ce
1571 | languageName: node
1572 | linkType: hard
1573 |
1574 | "json-stable-stringify-without-jsonify@npm:^1.0.1":
1575 | version: 1.0.1
1576 | resolution: "json-stable-stringify-without-jsonify@npm:1.0.1"
1577 | checksum: 10c0/cb168b61fd4de83e58d09aaa6425ef71001bae30d260e2c57e7d09a5fd82223e2f22a042dedaab8db23b7d9ae46854b08bb1f91675a8be11c5cffebef5fb66a5
1578 | languageName: node
1579 | linkType: hard
1580 |
1581 | "keyv@npm:^4.5.4":
1582 | version: 4.5.4
1583 | resolution: "keyv@npm:4.5.4"
1584 | dependencies:
1585 | json-buffer: "npm:3.0.1"
1586 | checksum: 10c0/aa52f3c5e18e16bb6324876bb8b59dd02acf782a4b789c7b2ae21107fab95fab3890ed448d4f8dba80ce05391eeac4bfabb4f02a20221342982f806fa2cf271e
1587 | languageName: node
1588 | linkType: hard
1589 |
1590 | "levn@npm:^0.4.1":
1591 | version: 0.4.1
1592 | resolution: "levn@npm:0.4.1"
1593 | dependencies:
1594 | prelude-ls: "npm:^1.2.1"
1595 | type-check: "npm:~0.4.0"
1596 | checksum: 10c0/effb03cad7c89dfa5bd4f6989364bfc79994c2042ec5966cb9b95990e2edee5cd8969ddf42616a0373ac49fac1403437deaf6e9050fbbaa3546093a59b9ac94e
1597 | languageName: node
1598 | linkType: hard
1599 |
1600 | "lilconfig@npm:^3.1.3":
1601 | version: 3.1.3
1602 | resolution: "lilconfig@npm:3.1.3"
1603 | checksum: 10c0/f5604e7240c5c275743561442fbc5abf2a84ad94da0f5adc71d25e31fa8483048de3dcedcb7a44112a942fed305fd75841cdf6c9681c7f640c63f1049e9a5dcc
1604 | languageName: node
1605 | linkType: hard
1606 |
1607 | "lint-staged@npm:^16.1.0":
1608 | version: 16.1.0
1609 | resolution: "lint-staged@npm:16.1.0"
1610 | dependencies:
1611 | chalk: "npm:^5.4.1"
1612 | commander: "npm:^14.0.0"
1613 | debug: "npm:^4.4.1"
1614 | lilconfig: "npm:^3.1.3"
1615 | listr2: "npm:^8.3.3"
1616 | micromatch: "npm:^4.0.8"
1617 | nano-spawn: "npm:^1.0.2"
1618 | pidtree: "npm:^0.6.0"
1619 | string-argv: "npm:^0.3.2"
1620 | yaml: "npm:^2.8.0"
1621 | bin:
1622 | lint-staged: bin/lint-staged.js
1623 | checksum: 10c0/5cc33d61ec2c682e488eb3fcea5c153ce486623b80314f2c56af438ad78d73c7fcd3e7c911d273ac740bd34f1e030d35d4fb92d8e476984150c0c59724ac7fa4
1624 | languageName: node
1625 | linkType: hard
1626 |
1627 | "listr2@npm:^8.3.3":
1628 | version: 8.3.3
1629 | resolution: "listr2@npm:8.3.3"
1630 | dependencies:
1631 | cli-truncate: "npm:^4.0.0"
1632 | colorette: "npm:^2.0.20"
1633 | eventemitter3: "npm:^5.0.1"
1634 | log-update: "npm:^6.1.0"
1635 | rfdc: "npm:^1.4.1"
1636 | wrap-ansi: "npm:^9.0.0"
1637 | checksum: 10c0/0792f8a7fd482fa516e21689e012e07081cab3653172ca606090622cfa0024c784a1eba8095a17948a0e9a4aa98a80f7c9c90f78a0dd35173d6802f9cc123a82
1638 | languageName: node
1639 | linkType: hard
1640 |
1641 | "locate-path@npm:^6.0.0":
1642 | version: 6.0.0
1643 | resolution: "locate-path@npm:6.0.0"
1644 | dependencies:
1645 | p-locate: "npm:^5.0.0"
1646 | checksum: 10c0/d3972ab70dfe58ce620e64265f90162d247e87159b6126b01314dd67be43d50e96a50b517bce2d9452a79409c7614054c277b5232377de50416564a77ac7aad3
1647 | languageName: node
1648 | linkType: hard
1649 |
1650 | "lodash.merge@npm:^4.6.2":
1651 | version: 4.6.2
1652 | resolution: "lodash.merge@npm:4.6.2"
1653 | checksum: 10c0/402fa16a1edd7538de5b5903a90228aa48eb5533986ba7fa26606a49db2572bf414ff73a2c9f5d5fd36b31c46a5d5c7e1527749c07cbcf965ccff5fbdf32c506
1654 | languageName: node
1655 | linkType: hard
1656 |
1657 | "log-symbols@npm:^4.1.0":
1658 | version: 4.1.0
1659 | resolution: "log-symbols@npm:4.1.0"
1660 | dependencies:
1661 | chalk: "npm:^4.1.0"
1662 | is-unicode-supported: "npm:^0.1.0"
1663 | checksum: 10c0/67f445a9ffa76db1989d0fa98586e5bc2fd5247260dafb8ad93d9f0ccd5896d53fb830b0e54dade5ad838b9de2006c826831a3c528913093af20dff8bd24aca6
1664 | languageName: node
1665 | linkType: hard
1666 |
1667 | "log-update@npm:^6.1.0":
1668 | version: 6.1.0
1669 | resolution: "log-update@npm:6.1.0"
1670 | dependencies:
1671 | ansi-escapes: "npm:^7.0.0"
1672 | cli-cursor: "npm:^5.0.0"
1673 | slice-ansi: "npm:^7.1.0"
1674 | strip-ansi: "npm:^7.1.0"
1675 | wrap-ansi: "npm:^9.0.0"
1676 | checksum: 10c0/4b350c0a83d7753fea34dcac6cd797d1dc9603291565de009baa4aa91c0447eab0d3815a05c8ec9ac04fdfffb43c82adcdb03ec1fceafd8518e1a8c1cff4ff89
1677 | languageName: node
1678 | linkType: hard
1679 |
1680 | "lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0":
1681 | version: 10.4.3
1682 | resolution: "lru-cache@npm:10.4.3"
1683 | checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb
1684 | languageName: node
1685 | linkType: hard
1686 |
1687 | "make-fetch-happen@npm:^14.0.3":
1688 | version: 14.0.3
1689 | resolution: "make-fetch-happen@npm:14.0.3"
1690 | dependencies:
1691 | "@npmcli/agent": "npm:^3.0.0"
1692 | cacache: "npm:^19.0.1"
1693 | http-cache-semantics: "npm:^4.1.1"
1694 | minipass: "npm:^7.0.2"
1695 | minipass-fetch: "npm:^4.0.0"
1696 | minipass-flush: "npm:^1.0.5"
1697 | minipass-pipeline: "npm:^1.2.4"
1698 | negotiator: "npm:^1.0.0"
1699 | proc-log: "npm:^5.0.0"
1700 | promise-retry: "npm:^2.0.1"
1701 | ssri: "npm:^12.0.0"
1702 | checksum: 10c0/c40efb5e5296e7feb8e37155bde8eb70bc57d731b1f7d90e35a092fde403d7697c56fb49334d92d330d6f1ca29a98142036d6480a12681133a0a1453164cb2f0
1703 | languageName: node
1704 | linkType: hard
1705 |
1706 | "micromatch@npm:^4.0.8":
1707 | version: 4.0.8
1708 | resolution: "micromatch@npm:4.0.8"
1709 | dependencies:
1710 | braces: "npm:^3.0.3"
1711 | picomatch: "npm:^2.3.1"
1712 | checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8
1713 | languageName: node
1714 | linkType: hard
1715 |
1716 | "mimic-function@npm:^5.0.0":
1717 | version: 5.0.1
1718 | resolution: "mimic-function@npm:5.0.1"
1719 | checksum: 10c0/f3d9464dd1816ecf6bdf2aec6ba32c0728022039d992f178237d8e289b48764fee4131319e72eedd4f7f094e22ded0af836c3187a7edc4595d28dd74368fd81d
1720 | languageName: node
1721 | linkType: hard
1722 |
1723 | "minimatch@npm:^3.1.2":
1724 | version: 3.1.2
1725 | resolution: "minimatch@npm:3.1.2"
1726 | dependencies:
1727 | brace-expansion: "npm:^1.1.7"
1728 | checksum: 10c0/0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311
1729 | languageName: node
1730 | linkType: hard
1731 |
1732 | "minimatch@npm:^5.1.6":
1733 | version: 5.1.6
1734 | resolution: "minimatch@npm:5.1.6"
1735 | dependencies:
1736 | brace-expansion: "npm:^2.0.1"
1737 | checksum: 10c0/3defdfd230914f22a8da203747c42ee3c405c39d4d37ffda284dac5e45b7e1f6c49aa8be606509002898e73091ff2a3bbfc59c2c6c71d4660609f63aa92f98e3
1738 | languageName: node
1739 | linkType: hard
1740 |
1741 | "minimatch@npm:^9.0.4, minimatch@npm:^9.0.5":
1742 | version: 9.0.5
1743 | resolution: "minimatch@npm:9.0.5"
1744 | dependencies:
1745 | brace-expansion: "npm:^2.0.1"
1746 | checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed
1747 | languageName: node
1748 | linkType: hard
1749 |
1750 | "minipass-collect@npm:^2.0.1":
1751 | version: 2.0.1
1752 | resolution: "minipass-collect@npm:2.0.1"
1753 | dependencies:
1754 | minipass: "npm:^7.0.3"
1755 | checksum: 10c0/5167e73f62bb74cc5019594709c77e6a742051a647fe9499abf03c71dca75515b7959d67a764bdc4f8b361cf897fbf25e2d9869ee039203ed45240f48b9aa06e
1756 | languageName: node
1757 | linkType: hard
1758 |
1759 | "minipass-fetch@npm:^4.0.0":
1760 | version: 4.0.1
1761 | resolution: "minipass-fetch@npm:4.0.1"
1762 | dependencies:
1763 | encoding: "npm:^0.1.13"
1764 | minipass: "npm:^7.0.3"
1765 | minipass-sized: "npm:^1.0.3"
1766 | minizlib: "npm:^3.0.1"
1767 | dependenciesMeta:
1768 | encoding:
1769 | optional: true
1770 | checksum: 10c0/a3147b2efe8e078c9bf9d024a0059339c5a09c5b1dded6900a219c218cc8b1b78510b62dae556b507304af226b18c3f1aeb1d48660283602d5b6586c399eed5c
1771 | languageName: node
1772 | linkType: hard
1773 |
1774 | "minipass-flush@npm:^1.0.5":
1775 | version: 1.0.5
1776 | resolution: "minipass-flush@npm:1.0.5"
1777 | dependencies:
1778 | minipass: "npm:^3.0.0"
1779 | checksum: 10c0/2a51b63feb799d2bb34669205eee7c0eaf9dce01883261a5b77410c9408aa447e478efd191b4de6fc1101e796ff5892f8443ef20d9544385819093dbb32d36bd
1780 | languageName: node
1781 | linkType: hard
1782 |
1783 | "minipass-pipeline@npm:^1.2.4":
1784 | version: 1.2.4
1785 | resolution: "minipass-pipeline@npm:1.2.4"
1786 | dependencies:
1787 | minipass: "npm:^3.0.0"
1788 | checksum: 10c0/cbda57cea20b140b797505dc2cac71581a70b3247b84480c1fed5ca5ba46c25ecc25f68bfc9e6dcb1a6e9017dab5c7ada5eab73ad4f0a49d84e35093e0c643f2
1789 | languageName: node
1790 | linkType: hard
1791 |
1792 | "minipass-sized@npm:^1.0.3":
1793 | version: 1.0.3
1794 | resolution: "minipass-sized@npm:1.0.3"
1795 | dependencies:
1796 | minipass: "npm:^3.0.0"
1797 | checksum: 10c0/298f124753efdc745cfe0f2bdfdd81ba25b9f4e753ca4a2066eb17c821f25d48acea607dfc997633ee5bf7b6dfffb4eee4f2051eb168663f0b99fad2fa4829cb
1798 | languageName: node
1799 | linkType: hard
1800 |
1801 | "minipass@npm:^3.0.0":
1802 | version: 3.3.6
1803 | resolution: "minipass@npm:3.3.6"
1804 | dependencies:
1805 | yallist: "npm:^4.0.0"
1806 | checksum: 10c0/a114746943afa1dbbca8249e706d1d38b85ed1298b530f5808ce51f8e9e941962e2a5ad2e00eae7dd21d8a4aae6586a66d4216d1a259385e9d0358f0c1eba16c
1807 | languageName: node
1808 | linkType: hard
1809 |
1810 | "minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2":
1811 | version: 7.1.2
1812 | resolution: "minipass@npm:7.1.2"
1813 | checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557
1814 | languageName: node
1815 | linkType: hard
1816 |
1817 | "minizlib@npm:^3.0.1":
1818 | version: 3.0.2
1819 | resolution: "minizlib@npm:3.0.2"
1820 | dependencies:
1821 | minipass: "npm:^7.1.2"
1822 | checksum: 10c0/9f3bd35e41d40d02469cb30470c55ccc21cae0db40e08d1d0b1dff01cc8cc89a6f78e9c5d2b7c844e485ec0a8abc2238111213fdc5b2038e6d1012eacf316f78
1823 | languageName: node
1824 | linkType: hard
1825 |
1826 | "mkdirp@npm:^3.0.1":
1827 | version: 3.0.1
1828 | resolution: "mkdirp@npm:3.0.1"
1829 | bin:
1830 | mkdirp: dist/cjs/src/bin.js
1831 | checksum: 10c0/9f2b975e9246351f5e3a40dcfac99fcd0baa31fbfab615fe059fb11e51f10e4803c63de1f384c54d656e4db31d000e4767e9ef076a22e12a641357602e31d57d
1832 | languageName: node
1833 | linkType: hard
1834 |
1835 | "mocha@npm:11.1.0":
1836 | version: 11.1.0
1837 | resolution: "mocha@npm:11.1.0"
1838 | dependencies:
1839 | ansi-colors: "npm:^4.1.3"
1840 | browser-stdout: "npm:^1.3.1"
1841 | chokidar: "npm:^3.5.3"
1842 | debug: "npm:^4.3.5"
1843 | diff: "npm:^5.2.0"
1844 | escape-string-regexp: "npm:^4.0.0"
1845 | find-up: "npm:^5.0.0"
1846 | glob: "npm:^10.4.5"
1847 | he: "npm:^1.2.0"
1848 | js-yaml: "npm:^4.1.0"
1849 | log-symbols: "npm:^4.1.0"
1850 | minimatch: "npm:^5.1.6"
1851 | ms: "npm:^2.1.3"
1852 | serialize-javascript: "npm:^6.0.2"
1853 | strip-json-comments: "npm:^3.1.1"
1854 | supports-color: "npm:^8.1.1"
1855 | workerpool: "npm:^6.5.1"
1856 | yargs: "npm:^17.7.2"
1857 | yargs-parser: "npm:^21.1.1"
1858 | yargs-unparser: "npm:^2.0.0"
1859 | bin:
1860 | _mocha: bin/_mocha
1861 | mocha: bin/mocha.js
1862 | checksum: 10c0/46e063fb014bef8c7f290094325ee2666ef9f63a918573f5278781631d4b3d04e45abe35f776307ff19e837bc2b42e4f2a7c60c53b69517539890636cf8e49ec
1863 | languageName: node
1864 | linkType: hard
1865 |
1866 | "ms@npm:^2.1.3":
1867 | version: 2.1.3
1868 | resolution: "ms@npm:2.1.3"
1869 | checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48
1870 | languageName: node
1871 | linkType: hard
1872 |
1873 | "nano-spawn@npm:^1.0.2":
1874 | version: 1.0.2
1875 | resolution: "nano-spawn@npm:1.0.2"
1876 | checksum: 10c0/d8cec78f127a44aa5e38be01746b3d963a8dcf8b00b4a05bf259b5369af2225b8c7dc9d12517050b90234e5c3eeea4ece5d18a5f9c6c3462b56f9f595f07e632
1877 | languageName: node
1878 | linkType: hard
1879 |
1880 | "natural-compare@npm:^1.4.0":
1881 | version: 1.4.0
1882 | resolution: "natural-compare@npm:1.4.0"
1883 | checksum: 10c0/f5f9a7974bfb28a91afafa254b197f0f22c684d4a1731763dda960d2c8e375b36c7d690e0d9dc8fba774c537af14a7e979129bca23d88d052fbeb9466955e447
1884 | languageName: node
1885 | linkType: hard
1886 |
1887 | "negotiator@npm:^1.0.0":
1888 | version: 1.0.0
1889 | resolution: "negotiator@npm:1.0.0"
1890 | checksum: 10c0/4c559dd52669ea48e1914f9d634227c561221dd54734070791f999c52ed0ff36e437b2e07d5c1f6e32909fc625fe46491c16e4a8f0572567d4dd15c3a4fda04b
1891 | languageName: node
1892 | linkType: hard
1893 |
1894 | "node-gyp@npm:latest":
1895 | version: 11.2.0
1896 | resolution: "node-gyp@npm:11.2.0"
1897 | dependencies:
1898 | env-paths: "npm:^2.2.0"
1899 | exponential-backoff: "npm:^3.1.1"
1900 | graceful-fs: "npm:^4.2.6"
1901 | make-fetch-happen: "npm:^14.0.3"
1902 | nopt: "npm:^8.0.0"
1903 | proc-log: "npm:^5.0.0"
1904 | semver: "npm:^7.3.5"
1905 | tar: "npm:^7.4.3"
1906 | tinyglobby: "npm:^0.2.12"
1907 | which: "npm:^5.0.0"
1908 | bin:
1909 | node-gyp: bin/node-gyp.js
1910 | checksum: 10c0/bd8d8c76b06be761239b0c8680f655f6a6e90b48e44d43415b11c16f7e8c15be346fba0cbf71588c7cdfb52c419d928a7d3db353afc1d952d19756237d8f10b9
1911 | languageName: node
1912 | linkType: hard
1913 |
1914 | "nopt@npm:^8.0.0":
1915 | version: 8.1.0
1916 | resolution: "nopt@npm:8.1.0"
1917 | dependencies:
1918 | abbrev: "npm:^3.0.0"
1919 | bin:
1920 | nopt: bin/nopt.js
1921 | checksum: 10c0/62e9ea70c7a3eb91d162d2c706b6606c041e4e7b547cbbb48f8b3695af457dd6479904d7ace600856bf923dd8d1ed0696f06195c8c20f02ac87c1da0e1d315ef
1922 | languageName: node
1923 | linkType: hard
1924 |
1925 | "normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0":
1926 | version: 3.0.0
1927 | resolution: "normalize-path@npm:3.0.0"
1928 | checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046
1929 | languageName: node
1930 | linkType: hard
1931 |
1932 | "onetime@npm:^7.0.0":
1933 | version: 7.0.0
1934 | resolution: "onetime@npm:7.0.0"
1935 | dependencies:
1936 | mimic-function: "npm:^5.0.0"
1937 | checksum: 10c0/5cb9179d74b63f52a196a2e7037ba2b9a893245a5532d3f44360012005c9cadb60851d56716ebff18a6f47129dab7168022445df47c2aff3b276d92585ed1221
1938 | languageName: node
1939 | linkType: hard
1940 |
1941 | "optionator@npm:^0.9.3":
1942 | version: 0.9.4
1943 | resolution: "optionator@npm:0.9.4"
1944 | dependencies:
1945 | deep-is: "npm:^0.1.3"
1946 | fast-levenshtein: "npm:^2.0.6"
1947 | levn: "npm:^0.4.1"
1948 | prelude-ls: "npm:^1.2.1"
1949 | type-check: "npm:^0.4.0"
1950 | word-wrap: "npm:^1.2.5"
1951 | checksum: 10c0/4afb687a059ee65b61df74dfe87d8d6815cd6883cb8b3d5883a910df72d0f5d029821f37025e4bccf4048873dbdb09acc6d303d27b8f76b1a80dd5a7d5334675
1952 | languageName: node
1953 | linkType: hard
1954 |
1955 | "p-limit@npm:^3.0.2":
1956 | version: 3.1.0
1957 | resolution: "p-limit@npm:3.1.0"
1958 | dependencies:
1959 | yocto-queue: "npm:^0.1.0"
1960 | checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a
1961 | languageName: node
1962 | linkType: hard
1963 |
1964 | "p-locate@npm:^5.0.0":
1965 | version: 5.0.0
1966 | resolution: "p-locate@npm:5.0.0"
1967 | dependencies:
1968 | p-limit: "npm:^3.0.2"
1969 | checksum: 10c0/2290d627ab7903b8b70d11d384fee714b797f6040d9278932754a6860845c4d3190603a0772a663c8cb5a7b21d1b16acb3a6487ebcafa9773094edc3dfe6009a
1970 | languageName: node
1971 | linkType: hard
1972 |
1973 | "p-map@npm:^7.0.2":
1974 | version: 7.0.3
1975 | resolution: "p-map@npm:7.0.3"
1976 | checksum: 10c0/46091610da2b38ce47bcd1d8b4835a6fa4e832848a6682cf1652bc93915770f4617afc844c10a77d1b3e56d2472bb2d5622353fa3ead01a7f42b04fc8e744a5c
1977 | languageName: node
1978 | linkType: hard
1979 |
1980 | "package-json-from-dist@npm:^1.0.0":
1981 | version: 1.0.1
1982 | resolution: "package-json-from-dist@npm:1.0.1"
1983 | checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b
1984 | languageName: node
1985 | linkType: hard
1986 |
1987 | "parent-module@npm:^1.0.0":
1988 | version: 1.0.1
1989 | resolution: "parent-module@npm:1.0.1"
1990 | dependencies:
1991 | callsites: "npm:^3.0.0"
1992 | checksum: 10c0/c63d6e80000d4babd11978e0d3fee386ca7752a02b035fd2435960ffaa7219dc42146f07069fb65e6e8bf1caef89daf9af7535a39bddf354d78bf50d8294f556
1993 | languageName: node
1994 | linkType: hard
1995 |
1996 | "path-exists@npm:^4.0.0":
1997 | version: 4.0.0
1998 | resolution: "path-exists@npm:4.0.0"
1999 | checksum: 10c0/8c0bd3f5238188197dc78dced15207a4716c51cc4e3624c44fc97acf69558f5ebb9a2afff486fe1b4ee148e0c133e96c5e11a9aa5c48a3006e3467da070e5e1b
2000 | languageName: node
2001 | linkType: hard
2002 |
2003 | "path-key@npm:^3.1.0":
2004 | version: 3.1.1
2005 | resolution: "path-key@npm:3.1.1"
2006 | checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c
2007 | languageName: node
2008 | linkType: hard
2009 |
2010 | "path-scurry@npm:^1.11.1":
2011 | version: 1.11.1
2012 | resolution: "path-scurry@npm:1.11.1"
2013 | dependencies:
2014 | lru-cache: "npm:^10.2.0"
2015 | minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0"
2016 | checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d
2017 | languageName: node
2018 | linkType: hard
2019 |
2020 | "picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1":
2021 | version: 2.3.1
2022 | resolution: "picomatch@npm:2.3.1"
2023 | checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be
2024 | languageName: node
2025 | linkType: hard
2026 |
2027 | "picomatch@npm:^4.0.2":
2028 | version: 4.0.2
2029 | resolution: "picomatch@npm:4.0.2"
2030 | checksum: 10c0/7c51f3ad2bb42c776f49ebf964c644958158be30d0a510efd5a395e8d49cb5acfed5b82c0c5b365523ce18e6ab85013c9ebe574f60305892ec3fa8eee8304ccc
2031 | languageName: node
2032 | linkType: hard
2033 |
2034 | "pidtree@npm:^0.6.0":
2035 | version: 0.6.0
2036 | resolution: "pidtree@npm:0.6.0"
2037 | bin:
2038 | pidtree: bin/pidtree.js
2039 | checksum: 10c0/0829ec4e9209e230f74ebf4265f5ccc9ebfb488334b525cb13f86ff801dca44b362c41252cd43ae4d7653a10a5c6ab3be39d2c79064d6895e0d78dc50a5ed6e9
2040 | languageName: node
2041 | linkType: hard
2042 |
2043 | "prelude-ls@npm:^1.2.1":
2044 | version: 1.2.1
2045 | resolution: "prelude-ls@npm:1.2.1"
2046 | checksum: 10c0/b00d617431e7886c520a6f498a2e14c75ec58f6d93ba48c3b639cf241b54232d90daa05d83a9e9b9fef6baa63cb7e1e4602c2372fea5bc169668401eb127d0cd
2047 | languageName: node
2048 | linkType: hard
2049 |
2050 | "prettier@npm:^3.5.3":
2051 | version: 3.5.3
2052 | resolution: "prettier@npm:3.5.3"
2053 | bin:
2054 | prettier: bin/prettier.cjs
2055 | checksum: 10c0/3880cb90b9dc0635819ab52ff571518c35bd7f15a6e80a2054c05dbc8a3aa6e74f135519e91197de63705bcb38388ded7e7230e2178432a1468005406238b877
2056 | languageName: node
2057 | linkType: hard
2058 |
2059 | "proc-log@npm:^5.0.0":
2060 | version: 5.0.0
2061 | resolution: "proc-log@npm:5.0.0"
2062 | checksum: 10c0/bbe5edb944b0ad63387a1d5b1911ae93e05ce8d0f60de1035b218cdcceedfe39dbd2c697853355b70f1a090f8f58fe90da487c85216bf9671f9499d1a897e9e3
2063 | languageName: node
2064 | linkType: hard
2065 |
2066 | "promise-retry@npm:^2.0.1":
2067 | version: 2.0.1
2068 | resolution: "promise-retry@npm:2.0.1"
2069 | dependencies:
2070 | err-code: "npm:^2.0.2"
2071 | retry: "npm:^0.12.0"
2072 | checksum: 10c0/9c7045a1a2928094b5b9b15336dcd2a7b1c052f674550df63cc3f36cd44028e5080448175b6f6ca32b642de81150f5e7b1a98b728f15cb069f2dd60ac2616b96
2073 | languageName: node
2074 | linkType: hard
2075 |
2076 | "punycode@npm:^2.1.0":
2077 | version: 2.3.1
2078 | resolution: "punycode@npm:2.3.1"
2079 | checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9
2080 | languageName: node
2081 | linkType: hard
2082 |
2083 | "randombytes@npm:^2.1.0":
2084 | version: 2.1.0
2085 | resolution: "randombytes@npm:2.1.0"
2086 | dependencies:
2087 | safe-buffer: "npm:^5.1.0"
2088 | checksum: 10c0/50395efda7a8c94f5dffab564f9ff89736064d32addf0cc7e8bf5e4166f09f8ded7a0849ca6c2d2a59478f7d90f78f20d8048bca3cdf8be09d8e8a10790388f3
2089 | languageName: node
2090 | linkType: hard
2091 |
2092 | "readdirp@npm:~3.6.0":
2093 | version: 3.6.0
2094 | resolution: "readdirp@npm:3.6.0"
2095 | dependencies:
2096 | picomatch: "npm:^2.2.1"
2097 | checksum: 10c0/6fa848cf63d1b82ab4e985f4cf72bd55b7dcfd8e0a376905804e48c3634b7e749170940ba77b32804d5fe93b3cc521aa95a8d7e7d725f830da6d93f3669ce66b
2098 | languageName: node
2099 | linkType: hard
2100 |
2101 | "require-directory@npm:^2.1.1":
2102 | version: 2.1.1
2103 | resolution: "require-directory@npm:2.1.1"
2104 | checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99
2105 | languageName: node
2106 | linkType: hard
2107 |
2108 | "resolve-from@npm:^4.0.0":
2109 | version: 4.0.0
2110 | resolution: "resolve-from@npm:4.0.0"
2111 | checksum: 10c0/8408eec31a3112ef96e3746c37be7d64020cda07c03a920f5024e77290a218ea758b26ca9529fd7b1ad283947f34b2291c1c0f6aa0ed34acfdda9c6014c8d190
2112 | languageName: node
2113 | linkType: hard
2114 |
2115 | "resolve-pkg-maps@npm:^1.0.0":
2116 | version: 1.0.0
2117 | resolution: "resolve-pkg-maps@npm:1.0.0"
2118 | checksum: 10c0/fb8f7bbe2ca281a73b7ef423a1cbc786fb244bd7a95cbe5c3fba25b27d327150beca8ba02f622baea65919a57e061eb5005204daa5f93ed590d9b77463a567ab
2119 | languageName: node
2120 | linkType: hard
2121 |
2122 | "restore-cursor@npm:^5.0.0":
2123 | version: 5.1.0
2124 | resolution: "restore-cursor@npm:5.1.0"
2125 | dependencies:
2126 | onetime: "npm:^7.0.0"
2127 | signal-exit: "npm:^4.1.0"
2128 | checksum: 10c0/c2ba89131eea791d1b25205bdfdc86699767e2b88dee2a590b1a6caa51737deac8bad0260a5ded2f7c074b7db2f3a626bcf1fcf3cdf35974cbeea5e2e6764f60
2129 | languageName: node
2130 | linkType: hard
2131 |
2132 | "retry@npm:^0.12.0":
2133 | version: 0.12.0
2134 | resolution: "retry@npm:0.12.0"
2135 | checksum: 10c0/59933e8501727ba13ad73ef4a04d5280b3717fd650408460c987392efe9d7be2040778ed8ebe933c5cbd63da3dcc37919c141ef8af0a54a6e4fca5a2af177bfe
2136 | languageName: node
2137 | linkType: hard
2138 |
2139 | "rfdc@npm:^1.4.1":
2140 | version: 1.4.1
2141 | resolution: "rfdc@npm:1.4.1"
2142 | checksum: 10c0/4614e4292356cafade0b6031527eea9bc90f2372a22c012313be1dcc69a3b90c7338158b414539be863fa95bfcb2ddcd0587be696841af4e6679d85e62c060c7
2143 | languageName: node
2144 | linkType: hard
2145 |
2146 | "safe-buffer@npm:^5.1.0":
2147 | version: 5.2.1
2148 | resolution: "safe-buffer@npm:5.2.1"
2149 | checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3
2150 | languageName: node
2151 | linkType: hard
2152 |
2153 | "safer-buffer@npm:>= 2.1.2 < 3.0.0":
2154 | version: 2.1.2
2155 | resolution: "safer-buffer@npm:2.1.2"
2156 | checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4
2157 | languageName: node
2158 | linkType: hard
2159 |
2160 | "semver@npm:^7.3.5, semver@npm:^7.5.4, semver@npm:^7.6.3":
2161 | version: 7.7.1
2162 | resolution: "semver@npm:7.7.1"
2163 | bin:
2164 | semver: bin/semver.js
2165 | checksum: 10c0/fd603a6fb9c399c6054015433051bdbe7b99a940a8fb44b85c2b524c4004b023d7928d47cb22154f8d054ea7ee8597f586605e05b52047f048278e4ac56ae958
2166 | languageName: node
2167 | linkType: hard
2168 |
2169 | "serialize-javascript@npm:^6.0.2":
2170 | version: 6.0.2
2171 | resolution: "serialize-javascript@npm:6.0.2"
2172 | dependencies:
2173 | randombytes: "npm:^2.1.0"
2174 | checksum: 10c0/2dd09ef4b65a1289ba24a788b1423a035581bef60817bea1f01eda8e3bda623f86357665fe7ac1b50f6d4f583f97db9615b3f07b2a2e8cbcb75033965f771dd2
2175 | languageName: node
2176 | linkType: hard
2177 |
2178 | "shebang-command@npm:^2.0.0":
2179 | version: 2.0.0
2180 | resolution: "shebang-command@npm:2.0.0"
2181 | dependencies:
2182 | shebang-regex: "npm:^3.0.0"
2183 | checksum: 10c0/a41692e7d89a553ef21d324a5cceb5f686d1f3c040759c50aab69688634688c5c327f26f3ecf7001ebfd78c01f3c7c0a11a7c8bfd0a8bc9f6240d4f40b224e4e
2184 | languageName: node
2185 | linkType: hard
2186 |
2187 | "shebang-regex@npm:^3.0.0":
2188 | version: 3.0.0
2189 | resolution: "shebang-regex@npm:3.0.0"
2190 | checksum: 10c0/1dbed0726dd0e1152a92696c76c7f06084eb32a90f0528d11acd764043aacf76994b2fb30aa1291a21bd019d6699164d048286309a278855ee7bec06cf6fb690
2191 | languageName: node
2192 | linkType: hard
2193 |
2194 | "signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0":
2195 | version: 4.1.0
2196 | resolution: "signal-exit@npm:4.1.0"
2197 | checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83
2198 | languageName: node
2199 | linkType: hard
2200 |
2201 | "simple-git-hooks@npm:^2.13.0":
2202 | version: 2.13.0
2203 | resolution: "simple-git-hooks@npm:2.13.0"
2204 | bin:
2205 | simple-git-hooks: cli.js
2206 | checksum: 10c0/aebd7c36711d6805c1ecb26a53203c259d0587fdd214a650217dccd57a42fd808075e3f92826c5d4838b3e68870f5561820072a5989f039cb7659742421d3ceb
2207 | languageName: node
2208 | linkType: hard
2209 |
2210 | "slice-ansi@npm:^5.0.0":
2211 | version: 5.0.0
2212 | resolution: "slice-ansi@npm:5.0.0"
2213 | dependencies:
2214 | ansi-styles: "npm:^6.0.0"
2215 | is-fullwidth-code-point: "npm:^4.0.0"
2216 | checksum: 10c0/2d4d40b2a9d5cf4e8caae3f698fe24ae31a4d778701724f578e984dcb485ec8c49f0c04dab59c401821e80fcdfe89cace9c66693b0244e40ec485d72e543914f
2217 | languageName: node
2218 | linkType: hard
2219 |
2220 | "slice-ansi@npm:^7.1.0":
2221 | version: 7.1.0
2222 | resolution: "slice-ansi@npm:7.1.0"
2223 | dependencies:
2224 | ansi-styles: "npm:^6.2.1"
2225 | is-fullwidth-code-point: "npm:^5.0.0"
2226 | checksum: 10c0/631c971d4abf56cf880f034d43fcc44ff883624867bf11ecbd538c47343911d734a4656d7bc02362b40b89d765652a7f935595441e519b59e2ad3f4d5d6fe7ca
2227 | languageName: node
2228 | linkType: hard
2229 |
2230 | "smart-buffer@npm:^4.2.0":
2231 | version: 4.2.0
2232 | resolution: "smart-buffer@npm:4.2.0"
2233 | checksum: 10c0/a16775323e1404dd43fabafe7460be13a471e021637bc7889468eb45ce6a6b207261f454e4e530a19500cc962c4cc5348583520843b363f4193cee5c00e1e539
2234 | languageName: node
2235 | linkType: hard
2236 |
2237 | "socks-proxy-agent@npm:^8.0.3":
2238 | version: 8.0.5
2239 | resolution: "socks-proxy-agent@npm:8.0.5"
2240 | dependencies:
2241 | agent-base: "npm:^7.1.2"
2242 | debug: "npm:^4.3.4"
2243 | socks: "npm:^2.8.3"
2244 | checksum: 10c0/5d2c6cecba6821389aabf18728325730504bf9bb1d9e342e7987a5d13badd7a98838cc9a55b8ed3cb866ad37cc23e1086f09c4d72d93105ce9dfe76330e9d2a6
2245 | languageName: node
2246 | linkType: hard
2247 |
2248 | "socks@npm:^2.8.3":
2249 | version: 2.8.4
2250 | resolution: "socks@npm:2.8.4"
2251 | dependencies:
2252 | ip-address: "npm:^9.0.5"
2253 | smart-buffer: "npm:^4.2.0"
2254 | checksum: 10c0/00c3271e233ccf1fb83a3dd2060b94cc37817e0f797a93c560b9a7a86c4a0ec2961fb31263bdd24a3c28945e24868b5f063cd98744171d9e942c513454b50ae5
2255 | languageName: node
2256 | linkType: hard
2257 |
2258 | "sprintf-js@npm:^1.1.3":
2259 | version: 1.1.3
2260 | resolution: "sprintf-js@npm:1.1.3"
2261 | checksum: 10c0/09270dc4f30d479e666aee820eacd9e464215cdff53848b443964202bf4051490538e5dd1b42e1a65cf7296916ca17640aebf63dae9812749c7542ee5f288dec
2262 | languageName: node
2263 | linkType: hard
2264 |
2265 | "ssri@npm:^12.0.0":
2266 | version: 12.0.0
2267 | resolution: "ssri@npm:12.0.0"
2268 | dependencies:
2269 | minipass: "npm:^7.0.3"
2270 | checksum: 10c0/caddd5f544b2006e88fa6b0124d8d7b28208b83c72d7672d5ade44d794525d23b540f3396108c4eb9280dcb7c01f0bef50682f5b4b2c34291f7c5e211fd1417d
2271 | languageName: node
2272 | linkType: hard
2273 |
2274 | "string-argv@npm:^0.3.2":
2275 | version: 0.3.2
2276 | resolution: "string-argv@npm:0.3.2"
2277 | checksum: 10c0/75c02a83759ad1722e040b86823909d9a2fc75d15dd71ec4b537c3560746e33b5f5a07f7332d1e3f88319909f82190843aa2f0a0d8c8d591ec08e93d5b8dec82
2278 | languageName: node
2279 | linkType: hard
2280 |
2281 | "string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3":
2282 | version: 4.2.3
2283 | resolution: "string-width@npm:4.2.3"
2284 | dependencies:
2285 | emoji-regex: "npm:^8.0.0"
2286 | is-fullwidth-code-point: "npm:^3.0.0"
2287 | strip-ansi: "npm:^6.0.1"
2288 | checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b
2289 | languageName: node
2290 | linkType: hard
2291 |
2292 | "string-width@npm:^5.0.1, string-width@npm:^5.1.2":
2293 | version: 5.1.2
2294 | resolution: "string-width@npm:5.1.2"
2295 | dependencies:
2296 | eastasianwidth: "npm:^0.2.0"
2297 | emoji-regex: "npm:^9.2.2"
2298 | strip-ansi: "npm:^7.0.1"
2299 | checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca
2300 | languageName: node
2301 | linkType: hard
2302 |
2303 | "string-width@npm:^7.0.0":
2304 | version: 7.2.0
2305 | resolution: "string-width@npm:7.2.0"
2306 | dependencies:
2307 | emoji-regex: "npm:^10.3.0"
2308 | get-east-asian-width: "npm:^1.0.0"
2309 | strip-ansi: "npm:^7.1.0"
2310 | checksum: 10c0/eb0430dd43f3199c7a46dcbf7a0b34539c76fe3aa62763d0b0655acdcbdf360b3f66f3d58ca25ba0205f42ea3491fa00f09426d3b7d3040e506878fc7664c9b9
2311 | languageName: node
2312 | linkType: hard
2313 |
2314 | "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1":
2315 | version: 6.0.1
2316 | resolution: "strip-ansi@npm:6.0.1"
2317 | dependencies:
2318 | ansi-regex: "npm:^5.0.1"
2319 | checksum: 10c0/1ae5f212a126fe5b167707f716942490e3933085a5ff6c008ab97ab2f272c8025d3aa218b7bd6ab25729ca20cc81cddb252102f8751e13482a5199e873680952
2320 | languageName: node
2321 | linkType: hard
2322 |
2323 | "strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0":
2324 | version: 7.1.0
2325 | resolution: "strip-ansi@npm:7.1.0"
2326 | dependencies:
2327 | ansi-regex: "npm:^6.0.1"
2328 | checksum: 10c0/a198c3762e8832505328cbf9e8c8381de14a4fa50a4f9b2160138158ea88c0f5549fb50cb13c651c3088f47e63a108b34622ec18c0499b6c8c3a5ddf6b305ac4
2329 | languageName: node
2330 | linkType: hard
2331 |
2332 | "strip-json-comments@npm:^3.1.1":
2333 | version: 3.1.1
2334 | resolution: "strip-json-comments@npm:3.1.1"
2335 | checksum: 10c0/9681a6257b925a7fa0f285851c0e613cc934a50661fa7bb41ca9cbbff89686bb4a0ee366e6ecedc4daafd01e83eee0720111ab294366fe7c185e935475ebcecd
2336 | languageName: node
2337 | linkType: hard
2338 |
2339 | "supports-color@npm:^7.1.0":
2340 | version: 7.2.0
2341 | resolution: "supports-color@npm:7.2.0"
2342 | dependencies:
2343 | has-flag: "npm:^4.0.0"
2344 | checksum: 10c0/afb4c88521b8b136b5f5f95160c98dee7243dc79d5432db7efc27efb219385bbc7d9427398e43dd6cc730a0f87d5085ce1652af7efbe391327bc0a7d0f7fc124
2345 | languageName: node
2346 | linkType: hard
2347 |
2348 | "supports-color@npm:^8.1.1":
2349 | version: 8.1.1
2350 | resolution: "supports-color@npm:8.1.1"
2351 | dependencies:
2352 | has-flag: "npm:^4.0.0"
2353 | checksum: 10c0/ea1d3c275dd604c974670f63943ed9bd83623edc102430c05adb8efc56ba492746b6e95386e7831b872ec3807fd89dd8eb43f735195f37b5ec343e4234cc7e89
2354 | languageName: node
2355 | linkType: hard
2356 |
2357 | "tapable@npm:^2.2.0":
2358 | version: 2.2.1
2359 | resolution: "tapable@npm:2.2.1"
2360 | checksum: 10c0/bc40e6efe1e554d075469cedaba69a30eeb373552aaf41caeaaa45bf56ffacc2674261b106245bd566b35d8f3329b52d838e851ee0a852120acae26e622925c9
2361 | languageName: node
2362 | linkType: hard
2363 |
2364 | "tar@npm:^7.4.3":
2365 | version: 7.4.3
2366 | resolution: "tar@npm:7.4.3"
2367 | dependencies:
2368 | "@isaacs/fs-minipass": "npm:^4.0.0"
2369 | chownr: "npm:^3.0.0"
2370 | minipass: "npm:^7.1.2"
2371 | minizlib: "npm:^3.0.1"
2372 | mkdirp: "npm:^3.0.1"
2373 | yallist: "npm:^5.0.0"
2374 | checksum: 10c0/d4679609bb2a9b48eeaf84632b6d844128d2412b95b6de07d53d8ee8baf4ca0857c9331dfa510390a0727b550fd543d4d1a10995ad86cdf078423fbb8d99831d
2375 | languageName: node
2376 | linkType: hard
2377 |
2378 | "tinyglobby@npm:^0.2.12":
2379 | version: 0.2.13
2380 | resolution: "tinyglobby@npm:0.2.13"
2381 | dependencies:
2382 | fdir: "npm:^6.4.4"
2383 | picomatch: "npm:^4.0.2"
2384 | checksum: 10c0/ef07dfaa7b26936601d3f6d999f7928a4d1c6234c5eb36896bb88681947c0d459b7ebe797022400e555fe4b894db06e922b95d0ce60cb05fd827a0a66326b18c
2385 | languageName: node
2386 | linkType: hard
2387 |
2388 | "to-regex-range@npm:^5.0.1":
2389 | version: 5.0.1
2390 | resolution: "to-regex-range@npm:5.0.1"
2391 | dependencies:
2392 | is-number: "npm:^7.0.0"
2393 | checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892
2394 | languageName: node
2395 | linkType: hard
2396 |
2397 | "type-check@npm:^0.4.0, type-check@npm:~0.4.0":
2398 | version: 0.4.0
2399 | resolution: "type-check@npm:0.4.0"
2400 | dependencies:
2401 | prelude-ls: "npm:^1.2.1"
2402 | checksum: 10c0/7b3fd0ed43891e2080bf0c5c504b418fbb3e5c7b9708d3d015037ba2e6323a28152ec163bcb65212741fa5d2022e3075ac3c76440dbd344c9035f818e8ecee58
2403 | languageName: node
2404 | linkType: hard
2405 |
2406 | "unique-filename@npm:^4.0.0":
2407 | version: 4.0.0
2408 | resolution: "unique-filename@npm:4.0.0"
2409 | dependencies:
2410 | unique-slug: "npm:^5.0.0"
2411 | checksum: 10c0/38ae681cceb1408ea0587b6b01e29b00eee3c84baee1e41fd5c16b9ed443b80fba90c40e0ba69627e30855570a34ba8b06702d4a35035d4b5e198bf5a64c9ddc
2412 | languageName: node
2413 | linkType: hard
2414 |
2415 | "unique-slug@npm:^5.0.0":
2416 | version: 5.0.0
2417 | resolution: "unique-slug@npm:5.0.0"
2418 | dependencies:
2419 | imurmurhash: "npm:^0.1.4"
2420 | checksum: 10c0/d324c5a44887bd7e105ce800fcf7533d43f29c48757ac410afd42975de82cc38ea2035c0483f4de82d186691bf3208ef35c644f73aa2b1b20b8e651be5afd293
2421 | languageName: node
2422 | linkType: hard
2423 |
2424 | "uri-js@npm:^4.2.2":
2425 | version: 4.4.1
2426 | resolution: "uri-js@npm:4.4.1"
2427 | dependencies:
2428 | punycode: "npm:^2.1.0"
2429 | checksum: 10c0/4ef57b45aa820d7ac6496e9208559986c665e49447cb072744c13b66925a362d96dd5a46c4530a6b8e203e5db5fe849369444440cb22ecfc26c679359e5dfa3c
2430 | languageName: node
2431 | linkType: hard
2432 |
2433 | "which@npm:^2.0.1":
2434 | version: 2.0.2
2435 | resolution: "which@npm:2.0.2"
2436 | dependencies:
2437 | isexe: "npm:^2.0.0"
2438 | bin:
2439 | node-which: ./bin/node-which
2440 | checksum: 10c0/66522872a768b60c2a65a57e8ad184e5372f5b6a9ca6d5f033d4b0dc98aff63995655a7503b9c0a2598936f532120e81dd8cc155e2e92ed662a2b9377cc4374f
2441 | languageName: node
2442 | linkType: hard
2443 |
2444 | "which@npm:^5.0.0":
2445 | version: 5.0.0
2446 | resolution: "which@npm:5.0.0"
2447 | dependencies:
2448 | isexe: "npm:^3.1.1"
2449 | bin:
2450 | node-which: bin/which.js
2451 | checksum: 10c0/e556e4cd8b7dbf5df52408c9a9dd5ac6518c8c5267c8953f5b0564073c66ed5bf9503b14d876d0e9c7844d4db9725fb0dcf45d6e911e17e26ab363dc3965ae7b
2452 | languageName: node
2453 | linkType: hard
2454 |
2455 | "word-wrap@npm:^1.2.5":
2456 | version: 1.2.5
2457 | resolution: "word-wrap@npm:1.2.5"
2458 | checksum: 10c0/e0e4a1ca27599c92a6ca4c32260e8a92e8a44f4ef6ef93f803f8ed823f486e0889fc0b93be4db59c8d51b3064951d25e43d434e95dc8c960cc3a63d65d00ba20
2459 | languageName: node
2460 | linkType: hard
2461 |
2462 | "workerpool@npm:^6.5.1":
2463 | version: 6.5.1
2464 | resolution: "workerpool@npm:6.5.1"
2465 | checksum: 10c0/58e8e969782292cb3a7bfba823f1179a7615250a0cefb4841d5166234db1880a3d0fe83a31dd8d648329ec92c2d0cd1890ad9ec9e53674bb36ca43e9753cdeac
2466 | languageName: node
2467 | linkType: hard
2468 |
2469 | "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0":
2470 | version: 7.0.0
2471 | resolution: "wrap-ansi@npm:7.0.0"
2472 | dependencies:
2473 | ansi-styles: "npm:^4.0.0"
2474 | string-width: "npm:^4.1.0"
2475 | strip-ansi: "npm:^6.0.0"
2476 | checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da
2477 | languageName: node
2478 | linkType: hard
2479 |
2480 | "wrap-ansi@npm:^8.1.0":
2481 | version: 8.1.0
2482 | resolution: "wrap-ansi@npm:8.1.0"
2483 | dependencies:
2484 | ansi-styles: "npm:^6.1.0"
2485 | string-width: "npm:^5.0.1"
2486 | strip-ansi: "npm:^7.0.1"
2487 | checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60
2488 | languageName: node
2489 | linkType: hard
2490 |
2491 | "wrap-ansi@npm:^9.0.0":
2492 | version: 9.0.0
2493 | resolution: "wrap-ansi@npm:9.0.0"
2494 | dependencies:
2495 | ansi-styles: "npm:^6.2.1"
2496 | string-width: "npm:^7.0.0"
2497 | strip-ansi: "npm:^7.1.0"
2498 | checksum: 10c0/a139b818da9573677548dd463bd626a5a5286271211eb6e4e82f34a4f643191d74e6d4a9bb0a3c26ec90e6f904f679e0569674ac099ea12378a8b98e20706066
2499 | languageName: node
2500 | linkType: hard
2501 |
2502 | "y18n@npm:^5.0.5":
2503 | version: 5.0.8
2504 | resolution: "y18n@npm:5.0.8"
2505 | checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249
2506 | languageName: node
2507 | linkType: hard
2508 |
2509 | "yallist@npm:^4.0.0":
2510 | version: 4.0.0
2511 | resolution: "yallist@npm:4.0.0"
2512 | checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a
2513 | languageName: node
2514 | linkType: hard
2515 |
2516 | "yallist@npm:^5.0.0":
2517 | version: 5.0.0
2518 | resolution: "yallist@npm:5.0.0"
2519 | checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416
2520 | languageName: node
2521 | linkType: hard
2522 |
2523 | "yaml@npm:^2.8.0":
2524 | version: 2.8.0
2525 | resolution: "yaml@npm:2.8.0"
2526 | bin:
2527 | yaml: bin.mjs
2528 | checksum: 10c0/f6f7310cf7264a8107e72c1376f4de37389945d2fb4656f8060eca83f01d2d703f9d1b925dd8f39852a57034fafefde6225409ddd9f22aebfda16c6141b71858
2529 | languageName: node
2530 | linkType: hard
2531 |
2532 | "yargs-parser@npm:^21.1.1":
2533 | version: 21.1.1
2534 | resolution: "yargs-parser@npm:21.1.1"
2535 | checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2
2536 | languageName: node
2537 | linkType: hard
2538 |
2539 | "yargs-unparser@npm:^2.0.0":
2540 | version: 2.0.0
2541 | resolution: "yargs-unparser@npm:2.0.0"
2542 | dependencies:
2543 | camelcase: "npm:^6.0.0"
2544 | decamelize: "npm:^4.0.0"
2545 | flat: "npm:^5.0.2"
2546 | is-plain-obj: "npm:^2.1.0"
2547 | checksum: 10c0/a5a7d6dc157efa95122e16780c019f40ed91d4af6d2bac066db8194ed0ec5c330abb115daa5a79ff07a9b80b8ea80c925baacf354c4c12edd878c0529927ff03
2548 | languageName: node
2549 | linkType: hard
2550 |
2551 | "yargs@npm:^17.7.2":
2552 | version: 17.7.2
2553 | resolution: "yargs@npm:17.7.2"
2554 | dependencies:
2555 | cliui: "npm:^8.0.1"
2556 | escalade: "npm:^3.1.1"
2557 | get-caller-file: "npm:^2.0.5"
2558 | require-directory: "npm:^2.1.1"
2559 | string-width: "npm:^4.2.3"
2560 | y18n: "npm:^5.0.5"
2561 | yargs-parser: "npm:^21.1.1"
2562 | checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05
2563 | languageName: node
2564 | linkType: hard
2565 |
2566 | "yocto-queue@npm:^0.1.0":
2567 | version: 0.1.0
2568 | resolution: "yocto-queue@npm:0.1.0"
2569 | checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f
2570 | languageName: node
2571 | linkType: hard
2572 |
--------------------------------------------------------------------------------