From 7f64b55f2a0bcccfd2b56306c4d7040573727fd4 Mon Sep 17 00:00:00 2001 From: rEl1cx Date: Tue, 7 Jan 2025 08:05:54 +0800 Subject: [PATCH] pref: rewrite slow code --- examples/dual-react-dom-lib/package.json | 8 +- .../docs/functions/isInitializedFromReact.md | 203 +----------------- packages/core/package.json | 6 +- .../core/src/component/component-collector.ts | 145 ++++++------- packages/core/src/component/misc.ts | 17 +- packages/core/src/hook/is.ts | 18 +- packages/core/src/utils/is-from-react.ts | 36 +++- .../src/utils/is-initialized-from-react.ts | 21 +- .../eslint-plugin-react-debug/package.json | 6 +- .../src/rules/is-from-react.ts | 16 +- .../eslint-plugin-react-dom/package.json | 6 +- .../src/rules/no-dangerously-set-innerhtml.ts | 19 +- .../src/rules/no-missing-button-type.ts | 15 +- .../src/rules/no-missing-iframe-sandbox.ts | 39 ++-- .../src/rules/no-unsafe-iframe-sandbox.ts | 44 ++-- .../package.json | 6 +- .../no-direct-set-state-in-use-effect.ts | 43 ++-- ...o-direct-set-state-in-use-layout-effect.ts | 43 ++-- .../package.json | 6 +- .../src/rules/component-name.ts | 34 ++- .../eslint-plugin-react-web-api/package.json | 6 +- .../eslint-plugin-react-x/package.json | 6 +- .../src/rules/no-array-index-key.ts | 6 +- .../src/rules/no-duplicate-key.ts | 19 +- .../src/rules/no-missing-key.ts | 8 +- .../src/rules/no-unstable-context-value.ts | 13 +- .../src/rules/no-unstable-default-props.ts | 12 +- .../no-unused-class-component-members.ts | 11 +- .../rules/prefer-destructuring-assignment.ts | 11 +- packages/plugins/eslint-plugin/package.json | 6 +- packages/shared/docs/README.md | 2 + .../docs/functions/getSettingsFromContext.md | 8 - packages/shared/docs/functions/tryRequire.md | 23 ++ .../docs/variables/normalizedSettingsCache.md | 9 + packages/shared/package.json | 14 +- packages/shared/src/index.ts | 2 + packages/shared/src/settings.ts | 77 +++++-- packages/types/package.json | 6 +- packages/utilities/ast/package.json | 6 +- packages/utilities/eff/package.json | 6 +- packages/utilities/jsx/package.json | 6 +- packages/utilities/var/package.json | 6 +- .../var/src/is-initialized-from-source.ts | 54 +++-- pnpm-lock.yaml | 32 +-- workspace/configs/package.json | 4 +- workspace/eslint-plugin-deps/package.json | 4 +- workspace/eslint-plugin-local/package.json | 4 +- 47 files changed, 468 insertions(+), 624 deletions(-) create mode 100644 packages/shared/docs/functions/tryRequire.md create mode 100644 packages/shared/docs/variables/normalizedSettingsCache.md diff --git a/examples/dual-react-dom-lib/package.json b/examples/dual-react-dom-lib/package.json index e08964d40e..1dc873e1f0 100644 --- a/examples/dual-react-dom-lib/package.json +++ b/examples/dual-react-dom-lib/package.json @@ -2,6 +2,7 @@ "name": "@examples/dual-react-dom-lib", "version": "0.0.0", "license": "MIT", + "sideEffects": false, "exports": { ".": { "import": { @@ -16,6 +17,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -46,10 +48,8 @@ "peerDependencies": { "react": "^19.0.0" }, + "packageManager": "pnpm@9.15.3", "engines": { "node": ">=18.18.0" - }, - "sideEffects": false, - "module": "dist/index.mjs", - "packageManager": "pnpm@9.15.3" + } } diff --git a/packages/core/docs/functions/isInitializedFromReact.md b/packages/core/docs/functions/isInitializedFromReact.md index a790aca913..f0a5bfa114 100644 --- a/packages/core/docs/functions/isInitializedFromReact.md +++ b/packages/core/docs/functions/isInitializedFromReact.md @@ -6,9 +6,7 @@ # Function: isInitializedFromReact() -> **isInitializedFromReact**(`name`, `initialScope`, `settings`): `boolean` - -Check if an identifier is initialized from React +> **isInitializedFromReact**(`name`, `initialScope`, `importSource`): `boolean` ## Parameters @@ -16,209 +14,14 @@ Check if an identifier is initialized from React `string` -The top-level identifier's name - ### initialScope `Scope` -Initial scope to search for the identifier - -### settings - -ESLint React settings - -#### additionalComponents - -`object`[] - -An array of user-defined components - -**Description** - -This is used to inform the ESLint React plugins how to treat these components during checks. - -**Example** - -```ts -`[{ name: "Link", as: "a", attributes: [{ name: "to", as: "href" }, { name: "rel", defaultValue: "noopener noreferrer" }] }]` -``` - -#### additionalHooks - -\{ `use`: `string`[]; `useActionState`: `string`[]; `useCallback`: `string`[]; `useContext`: `string`[]; `useDebugValue`: `string`[]; `useDeferredValue`: `string`[]; `useEffect`: `string`[]; `useFormStatus`: `string`[]; `useId`: `string`[]; `useImperativeHandle`: `string`[]; `useInsertionEffect`: `string`[]; `useLayoutEffect`: `string`[]; `useMemo`: `string`[]; `useOptimistic`: `string`[]; `useReducer`: `string`[]; `useRef`: `string`[]; `useState`: `string`[]; `useSyncExternalStore`: `string`[]; `useTransition`: `string`[]; \} - -A object of aliases for React built-in hooks. - -**Description** - -ESLint React will recognize these aliases as equivalent to the built-in hooks in all its rules. - -**Example** - -```ts -`{ useLayoutEffect: ["useIsomorphicLayoutEffect"] }` -``` - -#### additionalHooks.use - -`string`[] - -#### additionalHooks.useActionState - -`string`[] - -#### additionalHooks.useCallback - -`string`[] - -#### additionalHooks.useContext - -`string`[] - -#### additionalHooks.useDebugValue - -`string`[] - -#### additionalHooks.useDeferredValue - -`string`[] - -#### additionalHooks.useEffect - -`string`[] - -#### additionalHooks.useFormStatus - -`string`[] - -#### additionalHooks.useId - -`string`[] - -#### additionalHooks.useImperativeHandle - -`string`[] - -#### additionalHooks.useInsertionEffect - -`string`[] - -#### additionalHooks.useLayoutEffect - -`string`[] - -#### additionalHooks.useMemo - -`string`[] - -#### additionalHooks.useOptimistic - -`string`[] - -#### additionalHooks.useReducer - -`string`[] - -#### additionalHooks.useRef +### importSource -`string`[] - -#### additionalHooks.useState - -`string`[] - -#### additionalHooks.useSyncExternalStore - -`string`[] - -#### additionalHooks.useTransition - -`string`[] - -#### importSource - -`string` - -The source where React is imported from. - -**Description** - -This allows to specify a custom import location for React when not using the official distribution. - -**Default** - -`"react"` - -**Example** - -```ts -`"@pika/react"` -``` - -#### jsxPragma - -`string` - -The identifier that’s used for JSX Element creation. - -**Default** - -`"createElement"` - -**Deprecated** - -#### jsxPragmaFrag - -`string` - -The identifier that’s used for JSX fragment elements. - -**Description** - -This should not be a member expression (i.e. use "Fragment" instead of "React.Fragment"). - -**Default** - -`"Fragment"` - -**Deprecated** - -#### polymorphicPropName - -`string` - -The name of the prop that is used for polymorphic components. - -**Description** - -This is used to determine the type of the component. - -**Example** - -```ts -`"as"` -``` - -#### version - -`string` - -React version to use, "detect" means auto detect React version from the project’s dependencies. -If `importSource` is specified, an equivalent version of React should be provided here. - -**Example** - -```ts -`"18.3.1"` -``` - -**Default** - -`"detect"` +`string` = `"react"` ## Returns `boolean` - -Whether the identifier is initialized from React diff --git a/packages/core/package.json b/packages/core/package.json index 8106eca2c0..dfd33484fc 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -13,6 +13,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -27,6 +28,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -59,7 +61,5 @@ "engines": { "bun": ">=1.0.15", "node": ">=18.18.0" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/core/src/component/component-collector.ts b/packages/core/src/component/component-collector.ts index 3b6bbb38ef..e6fcb0fd11 100644 --- a/packages/core/src/component/component-collector.ts +++ b/packages/core/src/component/component-collector.ts @@ -66,20 +66,20 @@ export function useComponentCollector( ) { const jsxCtx = { getScope: (node: TSESTree.Node) => context.sourceCode.getScope(node) } as const; const components = new Map(); - const functionStack: { + const functionEntries: { key: string; node: AST.TSESTreeFunction; hookCalls: TSESTree.CallExpression[]; isComponent: boolean; }[] = []; - const getCurrentFunction = () => O.fromNullable(functionStack.at(-1)); + const getCurrentFunction = () => O.fromNullable(functionEntries.at(-1)); const onFunctionEnter = (node: AST.TSESTreeFunction) => { const key = getId(); - functionStack.push({ key, node, hookCalls: [], isComponent: false }); + functionEntries.push({ key, node, hookCalls: [], isComponent: false }); }; const onFunctionExit = () => { - const { key, node, isComponent } = functionStack.at(-1) ?? {}; - if (!key || !node || !isComponent) return functionStack.pop(); + const { key, node, isComponent } = functionEntries.at(-1) ?? {}; + if (!key || !node || !isComponent) return functionEntries.pop(); const shouldDrop = AST.getNestedReturnStatements(node.body) .slice() .reverse() @@ -89,7 +89,7 @@ export function useComponentCollector( && !JSX.isJSXValue(r.argument, jsxCtx, hint); }); if (shouldDrop) components.delete(key); - return functionStack.pop(); + return functionEntries.pop(); }; const ctx = { @@ -98,7 +98,7 @@ export function useComponentCollector( }, getCurrentFunction, getCurrentFunctionStack() { - return [...functionStack]; + return [...functionEntries]; }, } as const; @@ -106,90 +106,81 @@ export function useComponentCollector( ":function[type]": onFunctionEnter, ":function[type]:exit": onFunctionExit, "ArrowFunctionExpression[type][body.type!='BlockStatement']"() { - O.match(getCurrentFunction(), { - onNone() {}, - onSome(a) { - const { body } = a.node; - const isComponent = hasNoneOrValidComponentName(a.node, context) - && JSX.isJSXValue(body, jsxCtx, hint) - && hasValidHierarchy(a.node, context, hint); - if (!isComponent) return; - const initPath = AST.getFunctionInitPath(a.node); - const id = getFunctionComponentIdentifier(a.node, context); - const name = O.flatMapNullable(id, getComponentNameFromIdentifier); - const key = getId(); - components.set(key, { - _: key, - id, - kind: "function", - name, - node: a.node, - displayName: O.none(), - flag: getComponentFlag(initPath), - hint, - hookCalls: a.hookCalls, - initPath, - }); - }, + const mbEntry = getCurrentFunction(); + if (O.isNone(mbEntry)) return; + const entry = mbEntry.value; + const { body } = entry.node; + const isComponent = hasNoneOrValidComponentName(entry.node, context) + && JSX.isJSXValue(body, jsxCtx, hint) + && hasValidHierarchy(entry.node, context, hint); + if (!isComponent) return; + const initPath = AST.getFunctionInitPath(entry.node); + const id = getFunctionComponentIdentifier(entry.node, context); + const name = O.flatMapNullable(id, getComponentNameFromIdentifier); + const key = getId(); + components.set(key, { + _: key, + id, + kind: "function", + name, + node: entry.node, + displayName: O.none(), + flag: getComponentFlag(initPath), + hint, + hookCalls: entry.hookCalls, + initPath, }); }, "AssignmentExpression[type][operator='='][left.type='MemberExpression'][left.property.name='displayName']"( node: TSESTree.AssignmentExpression & { left: TSESTree.MemberExpression }, ) { const { left, right } = node; - const componentName = match(left.object) + const mbComponentName = match(left.object) .with({ type: T.Identifier }, n => O.some(n.name)) .otherwise(O.none); - O.match(componentName, { - onNone() {}, - onSome(a) { - const component = Array - .from(components.values()) - .findLast(({ name }) => O.exists(name, n => n === a)); - if (!component) return; - components.set(component._, { - ...component, - displayName: O.some(right), - }); - }, + if (O.isNone(mbComponentName)) return; + const componentName = mbComponentName.value; + const component = Array + .from(components.values()) + .findLast(({ name }) => O.exists(name, n => n === componentName)); + if (!component) return; + components.set(component._, { + ...component, + displayName: O.some(right), }); }, "CallExpression[type]:exit"(node: TSESTree.CallExpression) { if (!isReactHookCall(node)) return; - O.match(getCurrentFunction(), { - onNone() {}, - onSome(a) { - functionStack.pop(); - functionStack.push({ ...a, hookCalls: [...a.hookCalls, node] }); - }, - }); + const mbEntry = getCurrentFunction(); + if (O.isNone(mbEntry)) return; + const entry = mbEntry.value; + functionEntries.pop(); + functionEntries.push({ ...entry, hookCalls: [...entry.hookCalls, node] }); }, "ReturnStatement[type]"(node: TSESTree.ReturnStatement) { - O.match(getCurrentFunction(), { - onNone() {}, - onSome(a) { - const isComponent = hasNoneOrValidComponentName(a.node, context) - && JSX.isJSXValue(node.argument, jsxCtx, hint) - && hasValidHierarchy(a.node, context, hint); - if (!isComponent) return; - functionStack.pop(); - functionStack.push({ ...a, isComponent }); - const initPath = AST.getFunctionInitPath(a.node); - const id = getFunctionComponentIdentifier(a.node, context); - const name = O.flatMapNullable(id, getComponentNameFromIdentifier); - components.set(a.key, { - _: a.key, - id, - kind: "function", - name, - node: a.node, - displayName: O.none(), - flag: getComponentFlag(initPath), - hint, - hookCalls: a.hookCalls, - initPath, - }); - }, + const mbEntry = getCurrentFunction(); + if (O.isNone(mbEntry)) return; + const entry = mbEntry.value; + const isComponent = hasNoneOrValidComponentName(entry.node, context) + && JSX.isJSXValue(node.argument, jsxCtx, hint) + && hasValidHierarchy(entry.node, context, hint); + if (!isComponent) return; + functionEntries.pop(); + functionEntries.push({ ...entry, isComponent }); + const initPath = AST.getFunctionInitPath(entry.node); + const id = getFunctionComponentIdentifier(entry.node, context); + const name = O.flatMapNullable(id, getComponentNameFromIdentifier); + components.set(entry.key, { + _: entry.key, + id, + kind: "function", + name, + node: entry.node, + displayName: O.none(), + flag: getComponentFlag(initPath), + hint, + hookCalls: entry.hookCalls, + initPath, }); }, } as const satisfies ESLintUtils.RuleListener; diff --git a/packages/core/src/component/misc.ts b/packages/core/src/component/misc.ts index 781248a120..487f303157 100644 --- a/packages/core/src/component/misc.ts +++ b/packages/core/src/component/misc.ts @@ -6,16 +6,13 @@ import { getFunctionComponentIdentifier } from "./component-id"; import { isComponentName } from "./component-name"; export function hasNoneOrValidComponentName(node: AST.TSESTreeFunction, context: RuleContext) { - return O.match( + return F.pipe( getFunctionComponentIdentifier(node, context), - { - onNone: F.constTrue, - onSome: id => { - const name = Array.isArray(id) - ? id.at(-1)?.name - : id.name; - return !!name && isComponentName(name); - }, - }, + O.exists(id => { + const name = Array.isArray(id) + ? id.at(-1)?.name + : id.name; + return !!name && isComponentName(name); + }), ); } diff --git a/packages/core/src/hook/is.ts b/packages/core/src/hook/is.ts index a805642bee..cffbf18ba7 100644 --- a/packages/core/src/hook/is.ts +++ b/packages/core/src/hook/is.ts @@ -1,6 +1,6 @@ import * as AST from "@eslint-react/ast"; import { F, O } from "@eslint-react/eff"; -import { getSettingsFromContext } from "@eslint-react/shared"; +import { unsafeDecodeSettings } from "@eslint-react/shared"; import type { RuleContext } from "@eslint-react/types"; import type { TSESTree } from "@typescript-eslint/types"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; @@ -31,18 +31,20 @@ export function isReactHookCall(node: TSESTree.Node) { } export function isReactHookCallWithName(node: TSESTree.CallExpression, context: RuleContext) { - const settings = getSettingsFromContext(context); + const settings = unsafeDecodeSettings(context.settings); return (name: string) => { const initialScope = context.sourceCode.getScope(node); switch (true) { case node.callee.type === T.Identifier && node.callee.name === name: - return isInitializedFromReact(name, initialScope, settings); + return !settings.strictImportCheck + || isInitializedFromReact(name, initialScope, settings.importSource); case node.callee.type === T.MemberExpression && node.callee.property.type === T.Identifier && node.callee.property.name === name && "name" in node.callee.object: - return isInitializedFromReact(node.callee.object.name, initialScope, settings); + return !settings.strictImportCheck + || isInitializedFromReact(node.callee.object.name, initialScope, settings.importSource); default: return false; } @@ -63,18 +65,20 @@ export function isReactHookCallWithNameLoose(node: TSESTree.CallExpression) { } export function isReactHookCallWithNameAlias(name: string, context: RuleContext, alias: string[]) { - const settings = getSettingsFromContext(context); + const settings = unsafeDecodeSettings(context); return (node: TSESTree.CallExpression) => { const initialScope = context.sourceCode.getScope(node); switch (true) { case node.callee.type === T.Identifier && node.callee.name === name: - return isInitializedFromReact(name, initialScope, settings); + return !settings.strictImportCheck + || isInitializedFromReact(name, initialScope, settings.importSource); case node.callee.type === T.MemberExpression && node.callee.property.type === T.Identifier && node.callee.property.name === name && "name" in node.callee.object: - return isInitializedFromReact(node.callee.object.name, initialScope, settings); + return !settings.strictImportCheck + || isInitializedFromReact(node.callee.object.name, initialScope, settings.importSource); default: return alias.some(isReactHookCallWithNameLoose(node)); } diff --git a/packages/core/src/utils/is-from-react.ts b/packages/core/src/utils/is-from-react.ts index c831214be4..d45a7bf1ed 100644 --- a/packages/core/src/utils/is-from-react.ts +++ b/packages/core/src/utils/is-from-react.ts @@ -1,6 +1,6 @@ import * as AST from "@eslint-react/ast"; import { F } from "@eslint-react/eff"; -import { getSettingsFromContext } from "@eslint-react/shared"; +import { unsafeDecodeSettings } from "@eslint-react/shared"; import type { RuleContext } from "@eslint-react/types"; import type { TSESTree } from "@typescript-eslint/types"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; @@ -17,15 +17,25 @@ export function isFromReact(name: string) { node: TSESTree.Identifier | TSESTree.MemberExpression, context: RuleContext, ) => { - const settings = getSettingsFromContext(context); + const settings = unsafeDecodeSettings(context.settings); + if (!settings.strictImportCheck) { + if (node.type === T.MemberExpression) { + return node.object.type === T.Identifier + && node.property.type === T.Identifier + && node.property.name === name; + } + return node.name === name; + } const initialScope = context.sourceCode.getScope(node); if (node.type === T.MemberExpression) { return node.object.type === T.Identifier && node.property.type === T.Identifier && node.property.name === name - && isInitializedFromReact(node.object.name, initialScope, settings); + && isInitializedFromReact(node.object.name, initialScope, settings.importSource); + } + if (node.name === name) { + return isInitializedFromReact(name, initialScope, settings.importSource); } - if (node.name === name) return isInitializedFromReact(name, initialScope, settings); return false; }; } @@ -44,16 +54,28 @@ export function isFromReactMember( node: TSESTree.MemberExpression, context: RuleContext, ) => { - const settings = getSettingsFromContext(context); + const settings = unsafeDecodeSettings(context.settings); + if (!settings.strictImportCheck) { + if (node.property.type !== T.Identifier || node.property.name !== name) return false; + if (node.object.type === T.Identifier && node.object.name === memberName) return true; + if ( + node.object.type === T.MemberExpression + && node.object.object.type === T.Identifier + && node.object.property.type === T.Identifier + ) { + return node.object.property.name === memberName; + } + return false; + } const initialScope = context.sourceCode.getScope(node); if (node.property.type !== T.Identifier || node.property.name !== name) return false; if (node.object.type === T.Identifier && node.object.name === memberName) { - return isInitializedFromReact(node.object.name, initialScope, settings); + return isInitializedFromReact(node.object.name, initialScope, settings.importSource); } if ( node.object.type === T.MemberExpression && node.object.object.type === T.Identifier - && isInitializedFromReact(node.object.object.name, initialScope, settings) + && isInitializedFromReact(node.object.object.name, initialScope, settings.importSource) && node.object.property.type === T.Identifier ) { return node.object.property.name === memberName; diff --git a/packages/core/src/utils/is-initialized-from-react.ts b/packages/core/src/utils/is-initialized-from-react.ts index 3e2c0c8d5a..c79a98273c 100644 --- a/packages/core/src/utils/is-initialized-from-react.ts +++ b/packages/core/src/utils/is-initialized-from-react.ts @@ -1,22 +1,15 @@ -import type { ESLintReactSettings } from "@eslint-react/shared"; import * as VAR from "@eslint-react/var"; import type { Scope } from "@typescript-eslint/scope-manager"; -/** - * Check if an identifier is initialized from React - * @param name The top-level identifier's name - * @param initialScope Initial scope to search for the identifier - * @param settings ESLint React settings - * @returns Whether the identifier is initialized from React - */ export function isInitializedFromReact( name: string, initialScope: Scope, - settings: ESLintReactSettings, + importSource = "react", ) { - if (!settings.strictImportCheck) return true; - // Optimistic assertion when identifier is named react - if (name.toLowerCase() === "react") return true; - const { importSource = "react" } = settings; - return VAR.isInitializedFromSource(name, importSource, initialScope); + return name.toLowerCase() === "react" + || VAR.isInitializedFromSource( + name, + importSource, + initialScope, + ); } diff --git a/packages/plugins/eslint-plugin-react-debug/package.json b/packages/plugins/eslint-plugin-react-debug/package.json index ca846e9840..577bbd9b9f 100644 --- a/packages/plugins/eslint-plugin-react-debug/package.json +++ b/packages/plugins/eslint-plugin-react-debug/package.json @@ -21,6 +21,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -35,6 +36,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "./dist/index.d.ts", "files": [ "dist", @@ -85,7 +87,5 @@ }, "publishConfig": { "access": "public" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/plugins/eslint-plugin-react-debug/src/rules/is-from-react.ts b/packages/plugins/eslint-plugin-react-debug/src/rules/is-from-react.ts index 460f43acb0..a6451ef022 100644 --- a/packages/plugins/eslint-plugin-react-debug/src/rules/is-from-react.ts +++ b/packages/plugins/eslint-plugin-react-debug/src/rules/is-from-react.ts @@ -33,11 +33,11 @@ export default createRule<[], MessageID>({ }, name: RULE_NAME, create(context) { - const settings = getSettingsFromContext(context); - const finalSettings = { - ...settings, + const settings = { + importSource: "react", + ...getSettingsFromContext(context), strictImportCheck: true, - } satisfies typeof settings; + }; function isFromReact( node: TSESTree.Identifier | TSESTree.JSXIdentifier, initialScope: Scope, @@ -47,13 +47,13 @@ export default createRule<[], MessageID>({ case node.parent.type === T.MemberExpression && node.parent.property === node && node.parent.object.type === T.Identifier: - return isInitializedFromReact(node.parent.object.name, initialScope, finalSettings); + return isInitializedFromReact(node.parent.object.name, initialScope, settings.importSource); case node.parent.type === T.JSXMemberExpression && node.parent.property === node && node.parent.object.type === T.JSXIdentifier: - return isInitializedFromReact(node.parent.object.name, initialScope, finalSettings); + return isInitializedFromReact(node.parent.object.name, initialScope, settings.importSource); default: - return isInitializedFromReact(name, initialScope, finalSettings); + return isInitializedFromReact(name, initialScope, settings.importSource); } } function getReportDescriptor( @@ -72,7 +72,7 @@ export default createRule<[], MessageID>({ data: { type: node.type, name, - importSource: settings.importSource ?? "react", + importSource: settings.importSource, }, }); } diff --git a/packages/plugins/eslint-plugin-react-dom/package.json b/packages/plugins/eslint-plugin-react-dom/package.json index 87ac6d2fe9..0bfff4fb84 100644 --- a/packages/plugins/eslint-plugin-react-dom/package.json +++ b/packages/plugins/eslint-plugin-react-dom/package.json @@ -21,6 +21,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -35,6 +36,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -85,7 +87,5 @@ }, "publishConfig": { "access": "public" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml.ts index 662765e936..af18cddc66 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-dangerously-set-innerhtml.ts @@ -1,4 +1,4 @@ -import { O } from "@eslint-react/eff"; +import { F, O } from "@eslint-react/eff"; import * as JSX from "@eslint-react/jsx"; import type { RuleFeature } from "@eslint-react/types"; import type { CamelCase } from "string-ts"; @@ -30,17 +30,12 @@ export default createRule<[], MessageID>({ create(context) { return { JSXElement(node) { - const initialScope = context.sourceCode.getScope(node); - const prop = JSX.findPropInAttributes(node.openingElement.attributes, initialScope)("dangerouslySetInnerHTML"); - O.match(prop, { - onNone() {}, - onSome(a) { - context.report({ - messageId: "noDangerouslySetInnerhtml", - node: a, - }); - }, - }); + F.pipe( + O.some("dangerouslySetInnerHTML"), + O.flatMap(JSX.findPropInAttributes(node.openingElement.attributes, context.sourceCode.getScope(node))), + O.map(prop => ({ messageId: "noDangerouslySetInnerhtml", node: prop } as const)), + O.map(context.report), + ); }, }; }, diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts index 5dc6e58abc..b114c45299 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts @@ -1,5 +1,5 @@ import { getElementRepresentName } from "@eslint-react/core"; -import { O } from "@eslint-react/eff"; +import { F, O } from "@eslint-react/eff"; import * as JSX from "@eslint-react/jsx"; import type { RuleFeature } from "@eslint-react/types"; import type { CamelCase } from "string-ts"; @@ -35,14 +35,11 @@ export default createRule<[], MessageID>({ if (elementName !== "button") return; const { attributes } = node.openingElement; const initialScope = context.sourceCode.getScope(node); - O.match(JSX.findPropInAttributes(attributes, initialScope)("type"), { - onNone() { - context.report({ - messageId: "noMissingButtonType", - node: node.openingElement, - }); - }, - onSome() {}, + const mbProp = JSX.findPropInAttributes(attributes, initialScope)("type"); + if (O.isSome(mbProp)) return; + context.report({ + messageId: "noMissingButtonType", + node, }); }, }; diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts index 12bcfb3ca5..86d281b17e 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts @@ -54,26 +54,25 @@ export default createRule<[], MessageID>({ if (elementName !== "iframe") return; const { attributes } = node.openingElement; const initialScope = context.sourceCode.getScope(node); - O.match(JSX.findPropInAttributes(attributes, initialScope)("sandbox"), { - onNone() { - context.report({ - messageId: "noMissingIframeSandbox", - node: node.openingElement, - }); - }, - onSome(a) { - const hasValidSandbox = F.pipe( - JSX.getPropValue(a, context.sourceCode.getScope(a)), - O.filter(isString), - O.map((value) => value.split(" ")), - O.exists((values) => values.every((value) => validTypes.some((validType) => validType === value))), - ); - if (hasValidSandbox) return; - context.report({ - messageId: "noMissingIframeSandbox", - node: a, - }); - }, + const mbProp = JSX.findPropInAttributes(attributes, initialScope)("sandbox"); + if (O.isNone(mbProp)) { + context.report({ + messageId: "noMissingIframeSandbox", + node: node.openingElement, + }); + return; + } + const prop = mbProp.value; + const hasValidSandbox = F.pipe( + JSX.getPropValue(prop, context.sourceCode.getScope(prop)), + O.filter(isString), + O.map((value) => value.split(" ")), + O.exists((values) => values.every((value) => validTypes.some((validType) => validType === value))), + ); + if (hasValidSandbox) return; + context.report({ + messageId: "noMissingIframeSandbox", + node: prop, }); }, }; diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts index 73f95b46d7..275a89ec0b 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts @@ -41,29 +41,27 @@ export default createRule<[], MessageID>({ if (elementName !== "iframe") return; const { attributes } = node.openingElement; const initialScope = context.sourceCode.getScope(node); - O.match(JSX.findPropInAttributes(attributes, initialScope)("sandbox"), { - onNone() {}, - onSome(a) { - const isSafeSandboxValue = !F.pipe( - JSX.getPropValue(a, context.sourceCode.getScope(a)), - O.flatMapNullable(v => - match(v) - .with(P.string, F.identity) - .with({ sandbox: P.string }, ({ sandbox }) => sandbox) - .otherwise(F.constNull) - ), - O.filter(isString), - O.map((value) => value.split(" ")), - O.exists(values => - unsafeCombinations.some(combinations => combinations.every(unsafeValue => values.includes(unsafeValue))) - ), - ); - if (isSafeSandboxValue) return; - context.report({ - messageId: "noUnsafeIframeSandbox", - node: a, - }); - }, + const mbProp = JSX.findPropInAttributes(attributes, initialScope)("sandbox"); + if (O.isNone(mbProp)) return; + const prop = mbProp.value; + const isSafeSandboxValue = !F.pipe( + JSX.getPropValue(prop, context.sourceCode.getScope(prop)), + O.flatMapNullable(v => + match(v) + .with(P.string, F.identity) + .with({ sandbox: P.string }, ({ sandbox }) => sandbox) + .otherwise(F.constNull) + ), + O.filter(isString), + O.map((value) => value.split(" ")), + O.exists(values => + unsafeCombinations.some(combinations => combinations.every(unsafeValue => values.includes(unsafeValue))) + ), + ); + if (isSafeSandboxValue) return; + context.report({ + messageId: "noUnsafeIframeSandbox", + node: prop, }); }, }; diff --git a/packages/plugins/eslint-plugin-react-hooks-extra/package.json b/packages/plugins/eslint-plugin-react-hooks-extra/package.json index 87b999a1f8..a7e86e6c71 100644 --- a/packages/plugins/eslint-plugin-react-hooks-extra/package.json +++ b/packages/plugins/eslint-plugin-react-hooks-extra/package.json @@ -22,6 +22,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -36,6 +37,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -86,7 +88,5 @@ }, "publishConfig": { "access": "public" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/no-direct-set-state-in-use-effect.ts b/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/no-direct-set-state-in-use-effect.ts index 04d900557e..983a23b0c8 100644 --- a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/no-direct-set-state-in-use-effect.ts +++ b/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/no-direct-set-state-in-use-effect.ts @@ -127,16 +127,15 @@ export default createRule<[], MessageID>({ return; } default: { - O.match(AST.findParentNodeGuard(node, isVariableDeclaratorFromHookCall), { - onNone() { - const calls = indSetStateCalls.get(pEntry.node) ?? []; - indSetStateCalls.set(pEntry.node, [...calls, node]); - }, - onSome(a) { - const prevs = indSetStateCallsInUseMemoOrCallback.get(a.init) ?? []; - indSetStateCallsInUseMemoOrCallback.set(a.init, [...prevs, node]); - }, - }); + const mbVariableDeclarator = AST.findParentNodeGuard(node, isVariableDeclaratorFromHookCall); + if (O.isNone(mbVariableDeclarator)) { + const calls = indSetStateCalls.get(pEntry.node) ?? []; + indSetStateCalls.set(pEntry.node, [...calls, node]); + return; + } + const vd = mbVariableDeclarator.value; + const prevs = indSetStateCallsInUseMemoOrCallback.get(vd.init) ?? []; + indSetStateCallsInUseMemoOrCallback.set(vd.init, [...prevs, node]); } } }) @@ -162,13 +161,11 @@ export default createRule<[], MessageID>({ // const set = useMemo(() => setState, []); // useEffect(set, []); if (!isUseMemoCall(parent)) break; - O.match(AST.findParentNodeGuard(parent, isVariableDeclaratorFromHookCall), { - onNone() {}, - onSome(a) { - const calls = indSetStateCallsInUseEffectArg0.get(a.init) ?? []; - indSetStateCallsInUseEffectArg0.set(a.init, [...calls, node]); - }, - }); + const mbVariableDeclarator = AST.findParentNodeGuard(parent, isVariableDeclaratorFromHookCall); + if (O.isNone(mbVariableDeclarator)) break; + const variableDeclarator = mbVariableDeclarator.value; + const calls = indSetStateCallsInUseEffectArg0.get(variableDeclarator.init) ?? []; + indSetStateCallsInUseEffectArg0.set(variableDeclarator.init, [...calls, node]); break; } case T.CallExpression: { @@ -177,13 +174,11 @@ export default createRule<[], MessageID>({ // const set = useCallback(setState, []); // useEffect(set, []); if (isUseCallbackCall(node.parent)) { - O.match(AST.findParentNodeGuard(node.parent, isVariableDeclaratorFromHookCall), { - onNone() {}, - onSome(vd) { - const prevs = indSetStateCallsInUseEffectArg0.get(vd.init) ?? []; - indSetStateCallsInUseEffectArg0.set(vd.init, [...prevs, node]); - }, - }); + const mbVariableDeclarator = AST.findParentNodeGuard(node.parent, isVariableDeclaratorFromHookCall); + if (O.isNone(mbVariableDeclarator)) break; + const variableDeclarator = mbVariableDeclarator.value; + const prevs = indSetStateCallsInUseEffectArg0.get(variableDeclarator.init) ?? []; + indSetStateCallsInUseEffectArg0.set(variableDeclarator.init, [...prevs, node]); } // const [state, setState] = useState(); // useEffect(setState); diff --git a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/no-direct-set-state-in-use-layout-effect.ts b/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/no-direct-set-state-in-use-layout-effect.ts index 666c14a1b3..c8fb3ad13d 100644 --- a/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/no-direct-set-state-in-use-layout-effect.ts +++ b/packages/plugins/eslint-plugin-react-hooks-extra/src/rules/no-direct-set-state-in-use-layout-effect.ts @@ -132,16 +132,15 @@ export default createRule<[], MessageID>({ return; } default: { - O.match(AST.findParentNodeGuard(node, isVariableDeclaratorFromHookCall), { - onNone() { - const calls = indSetStateCalls.get(pEntry.node) ?? []; - indSetStateCalls.set(pEntry.node, [...calls, node]); - }, - onSome(a) { - const prevs = indSetStateCallsInUseMemoOrCallback.get(a.init) ?? []; - indSetStateCallsInUseMemoOrCallback.set(a.init, [...prevs, node]); - }, - }); + const mbVariableDeclarator = AST.findParentNodeGuard(node, isVariableDeclaratorFromHookCall); + if (O.isNone(mbVariableDeclarator)) { + const calls = indSetStateCalls.get(pEntry.node) ?? []; + indSetStateCalls.set(pEntry.node, [...calls, node]); + return; + } + const vd = mbVariableDeclarator.value; + const prevs = indSetStateCallsInUseMemoOrCallback.get(vd.init) ?? []; + indSetStateCallsInUseMemoOrCallback.set(vd.init, [...prevs, node]); } } }) @@ -167,13 +166,11 @@ export default createRule<[], MessageID>({ // const set = useMemo(() => setState, []); // useLayoutEffect(set, []); if (!isUseMemoCall(parent)) break; - O.match(AST.findParentNodeGuard(parent, isVariableDeclaratorFromHookCall), { - onNone() {}, - onSome(a) { - const calls = indSetStateCallsInUseLayoutEffectArg0.get(a.init) ?? []; - indSetStateCallsInUseLayoutEffectArg0.set(a.init, [...calls, node]); - }, - }); + const mbVariableDeclarator = AST.findParentNodeGuard(parent, isVariableDeclaratorFromHookCall); + if (O.isNone(mbVariableDeclarator)) break; + const variableDeclarator = mbVariableDeclarator.value; + const calls = indSetStateCallsInUseLayoutEffectArg0.get(variableDeclarator.init) ?? []; + indSetStateCallsInUseLayoutEffectArg0.set(variableDeclarator.init, [...calls, node]); break; } case T.CallExpression: { @@ -182,13 +179,11 @@ export default createRule<[], MessageID>({ // const set = useCallback(setState, []); // useLayoutEffect(set, []); if (isUseCallbackCall(node.parent)) { - O.match(AST.findParentNodeGuard(node.parent, isVariableDeclaratorFromHookCall), { - onNone() {}, - onSome(vd) { - const prevs = indSetStateCallsInUseLayoutEffectArg0.get(vd.init) ?? []; - indSetStateCallsInUseLayoutEffectArg0.set(vd.init, [...prevs, node]); - }, - }); + const mbVariableDeclarator = AST.findParentNodeGuard(node.parent, isVariableDeclaratorFromHookCall); + if (O.isNone(mbVariableDeclarator)) break; + const variableDeclarator = mbVariableDeclarator.value; + const prevs = indSetStateCallsInUseLayoutEffectArg0.get(variableDeclarator.init) ?? []; + indSetStateCallsInUseLayoutEffectArg0.set(variableDeclarator.init, [...prevs, node]); } // const [state, setState] = useState(); // useLayoutEffect(setState); diff --git a/packages/plugins/eslint-plugin-react-naming-convention/package.json b/packages/plugins/eslint-plugin-react-naming-convention/package.json index 77685b2011..447add6b2f 100644 --- a/packages/plugins/eslint-plugin-react-naming-convention/package.json +++ b/packages/plugins/eslint-plugin-react-naming-convention/package.json @@ -21,6 +21,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -35,6 +36,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -84,7 +86,5 @@ }, "publishConfig": { "access": "public" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/component-name.ts b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/component-name.ts index ad73d4827e..2d8bc52b4b 100644 --- a/packages/plugins/eslint-plugin-react-naming-convention/src/rules/component-name.ts +++ b/packages/plugins/eslint-plugin-react-naming-convention/src/rules/component-name.ts @@ -99,7 +99,7 @@ function validate(name: string, options: ReturnType) { .with("CONSTANT_CASE", () => RE_CONSTANT_CASE.test(normalized)) .with("PascalCase", () => { // Allow all caps if the string is shorter than 4 characters. e.g. UI, CSS, SVG, etc. - if ([...normalized].length > 3 && /^[A-Z]+$/u.test(normalized)) return options.allowAllCaps; + if (normalized.length > 3 && /^[A-Z]+$/u.test(normalized)) return options.allowAllCaps; return RE_PASCAL_CASE.test(normalized); }) .otherwise(F.constFalse); @@ -143,25 +143,23 @@ export default createRule({ const functionComponents = collector.ctx.getAllComponents(node); const classComponents = collectorLegacy.ctx.getAllComponents(node); for (const { node: component } of functionComponents.values()) { - O.match(AST.getFunctionIdentifier(component), { - onNone() {}, - onSome(id) { - if (validate(id.name, options)) return; - context.report({ - messageId: "componentName", - node: id, - data: { - case: options.rule, - }, - }); + const mbId = AST.getFunctionIdentifier(component); + if (O.isNone(mbId)) continue; + const id = mbId.value; + if (validate(id.name, options)) continue; + context.report({ + messageId: "componentName", + node: id, + data: { + case: options.rule, }, }); } for (const { node: component } of classComponents.values()) { - O.match(AST.getClassIdentifier(component), { - onNone() {}, - onSome(id) { - if (validate(id.name, options)) return; + F.pipe( + AST.getClassIdentifier(component), + O.filter(id => !validate(id.name, options)), + O.map(id => { context.report({ messageId: "componentName", node: id, @@ -169,8 +167,8 @@ export default createRule({ case: options.rule, }, }); - }, - }); + }), + ); } }, }; diff --git a/packages/plugins/eslint-plugin-react-web-api/package.json b/packages/plugins/eslint-plugin-react-web-api/package.json index d8db79bfea..38b47da23d 100644 --- a/packages/plugins/eslint-plugin-react-web-api/package.json +++ b/packages/plugins/eslint-plugin-react-web-api/package.json @@ -21,6 +21,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -35,6 +36,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -84,7 +86,5 @@ }, "publishConfig": { "access": "public" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/plugins/eslint-plugin-react-x/package.json b/packages/plugins/eslint-plugin-react-x/package.json index 29bd995005..1395bfe4ba 100644 --- a/packages/plugins/eslint-plugin-react-x/package.json +++ b/packages/plugins/eslint-plugin-react-x/package.json @@ -20,6 +20,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -34,6 +35,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -87,7 +89,5 @@ }, "publishConfig": { "access": "public" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-array-index-key.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-array-index-key.ts index f11acab8fb..bf5e0e513b 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/no-array-index-key.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-array-index-key.ts @@ -1,7 +1,7 @@ import * as AST from "@eslint-react/ast"; import { isCloneElementCall, isCreateElementCall, isInitializedFromReact } from "@eslint-react/core"; import { isNullable, O, or } from "@eslint-react/eff"; -import { getSettingsFromContext } from "@eslint-react/shared"; +import { unsafeDecodeSettings } from "@eslint-react/shared"; import type { RuleContext, RuleFeature } from "@eslint-react/types"; import { AST_NODE_TYPES as T } from "@typescript-eslint/types"; import type { TSESTree } from "@typescript-eslint/utils"; @@ -62,7 +62,7 @@ function isReactChildrenMethod(name: string): name is typeof reactChildrenMethod } function isUsingReactChildren(node: TSESTree.CallExpression, context: RuleContext) { - const settings = getSettingsFromContext(context); + const settings = unsafeDecodeSettings(context.settings); const { callee } = node; if (!("property" in callee) || !("object" in callee) || !("name" in callee.property)) { return false; @@ -71,7 +71,7 @@ function isUsingReactChildren(node: TSESTree.CallExpression, context: RuleContex const initialScope = context.sourceCode.getScope(node); if (callee.object.type === T.Identifier && callee.object.name === "Children") return true; if (callee.object.type === T.MemberExpression && "name" in callee.object.object) { - return isInitializedFromReact(callee.object.object.name, initialScope, { ...settings, strictImportCheck: true }); + return isInitializedFromReact(callee.object.object.name, initialScope, settings.importSource); } return false; } diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-duplicate-key.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-duplicate-key.ts index 915b8add41..b088770b0f 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/no-duplicate-key.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-duplicate-key.ts @@ -63,22 +63,19 @@ export default createRule<[], MessageID>({ break; } default: { - const entry = F.pipe( + const mbEntry = F.pipe( O.Do, O.bind("call", () => AST.findParentNodeGuard(jsxElement, AST.isMapCallLoose)), O.bind("iter", ({ call }) => AST.findParentNodeStop(jsxElement, call, AST.isFunction)), O.bind("arg0", ({ call }) => O.fromNullable(call.arguments[0])), ); - O.match(entry, { - onNone() {}, - onSome({ arg0, call, iter }) { - if (AST.unwrapTypeExpression(arg0) !== iter) return; - keyedEntries.set(call, { - hasDuplicate: node.value?.type === T.Literal, - keys: [node], - root: call, - }); - }, + if (O.isNone(mbEntry)) return; + const { arg0, call, iter } = mbEntry.value; + if (AST.unwrapTypeExpression(arg0) !== iter) return; + keyedEntries.set(call, { + hasDuplicate: node.value?.type === T.Literal, + keys: [node], + root: call, }); } } diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-missing-key.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-missing-key.ts index 31a23aa764..209fbc1c2e 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/no-missing-key.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-missing-key.ts @@ -79,10 +79,10 @@ export default createRule<[], MessageID>({ return AST.getNestedReturnStatements(node) .reduce[]>((acc, statement) => { if (!statement.argument) return acc; - return O.match(checkIteratorElement(statement.argument), { - onNone: () => acc, - onSome: descriptor => [...acc, descriptor], - }); + const mbDescriptor = checkIteratorElement(statement.argument); + return O.isNone(mbDescriptor) + ? acc + : [...acc, mbDescriptor.value]; }, []); } diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-context-value.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-context-value.ts index aa143d5a1f..9b00ce19a0 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-context-value.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-context-value.ts @@ -65,14 +65,11 @@ export default createRule<[], MessageID>({ const initialScope = context.sourceCode.getScope(valueExpression); return O.some(VAR.getValueConstruction(valueExpression, initialScope)); }), - O.match({ - onNone() {}, - onSome(a) { - if (a.construction.kind === "None") return; - if (isReactHookCall(a.construction.node)) return; - const prevs = constructions.get(a.function.node) ?? []; - constructions.set(a.function.node, [...prevs, a.construction]); - }, + O.map((vc) => { + if (vc.construction.kind === "None") return; + if (isReactHookCall(vc.construction.node)) return; + const prevs = constructions.get(vc.function.node) ?? []; + constructions.set(vc.function.node, [...prevs, vc.construction]); }), ); }, diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-default-props.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-default-props.ts index e54a60bae5..7e31464fbe 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-default-props.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-unstable-default-props.ts @@ -84,13 +84,11 @@ export default createRule<[], MessageID>({ } }, "VariableDeclarator[id.type='ObjectPattern'][init.type='Identifier']"(node: ObjectDestructuringDeclarator) { - O.match(ctx.getCurrentFunction(), { - onNone() {}, - onSome(a) { - const prevs = declarators.get(a.node) ?? []; - declarators.set(a.node, [...prevs, node]); - }, - }); + const mbEntry = ctx.getCurrentFunction(); + if (O.isNone(mbEntry)) return; + const entry = mbEntry.value; + const prevs = declarators.get(entry.node) ?? []; + declarators.set(entry.node, [...prevs, node]); }, }; }, diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-class-component-members.ts b/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-class-component-members.ts index 72b13d0d05..b2725060dd 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-class-component-members.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/no-unused-class-component-members.ts @@ -148,12 +148,11 @@ export default createRule<[], MessageID>({ if (node.init && AST.isThisExpression(node.init) && node.id.type === T.ObjectPattern) { for (const prop of node.id.properties) { if (prop.type === T.Property && AST.isKeyLiteralLike(prop, prop.key)) { - O.match(getName(prop.key), { - onNone() {}, - onSome(name) { - propertyUsages.get(currentClass)?.add(name); - }, - }); + const mbName = getName(prop.key); + if (O.isSome(mbName)) { + const name = mbName.value; + propertyUsages.get(currentClass)?.add(name); + } } } } diff --git a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-destructuring-assignment.ts b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-destructuring-assignment.ts index b6f4167520..fd0323d8b6 100644 --- a/packages/plugins/eslint-plugin-react-x/src/rules/prefer-destructuring-assignment.ts +++ b/packages/plugins/eslint-plugin-react-x/src/rules/prefer-destructuring-assignment.ts @@ -55,12 +55,13 @@ export default createRule<[], MessageID>({ const components = Array.from(ctx.getAllComponents(node).values()); function isFunctionComponent(block: TSESTree.Node): block is AST.TSESTreeFunction { if (!AST.isFunction(block)) return false; - return O.match(AST.getFunctionIdentifier(block), { - onNone: () => false, - onSome: (id) => + return O.exists( + AST.getFunctionIdentifier(block), + id => isComponentName(id.name) - && components.some((component) => component.node === block), - }); + && components + .some(component => component.node === block), + ); } for (const [initialScope, memberExpression] of memberExpressionWithNames) { diff --git a/packages/plugins/eslint-plugin/package.json b/packages/plugins/eslint-plugin/package.json index 7698761a38..9030f74029 100644 --- a/packages/plugins/eslint-plugin/package.json +++ b/packages/plugins/eslint-plugin/package.json @@ -24,6 +24,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -38,6 +39,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -86,7 +88,5 @@ }, "publishConfig": { "access": "public" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/shared/docs/README.md b/packages/shared/docs/README.md index a5c9dc5027..24759a015c 100644 --- a/packages/shared/docs/README.md +++ b/packages/shared/docs/README.md @@ -18,6 +18,7 @@ - [CustomComponentNormalizedSchema](variables/CustomComponentNormalizedSchema.md) - [DEFAULT\_ESLINT\_REACT\_SETTINGS](variables/DEFAULT_ESLINT_REACT_SETTINGS.md) - [GITHUB\_URL](variables/GITHUB_URL.md) +- [normalizedSettingsCache](variables/normalizedSettingsCache.md) - [NPM\_SCOPE](variables/NPM_SCOPE.md) - [RE\_CAMEL\_CASE](variables/RE_CAMEL_CASE.md) - [RE\_CONSTANT\_CASE](variables/RE_CONSTANT_CASE.md) @@ -33,3 +34,4 @@ - [defineSettings](functions/defineSettings.md) - [getReactVersion](functions/getReactVersion.md) - [getSettingsFromContext](functions/getSettingsFromContext.md) +- [tryRequire](functions/tryRequire.md) diff --git a/packages/shared/docs/functions/getSettingsFromContext.md b/packages/shared/docs/functions/getSettingsFromContext.md index bc939e0d6c..8360f4adb5 100644 --- a/packages/shared/docs/functions/getSettingsFromContext.md +++ b/packages/shared/docs/functions/getSettingsFromContext.md @@ -8,22 +8,14 @@ > **getSettingsFromContext**(`context`): `ESLintReactSettingsNormalized` -Get the normalized ESLint settings for "react-x" from the given context. - ## Parameters ### context -The context. - #### settings `unknown` -The ESLint settings. - ## Returns `ESLintReactSettingsNormalized` - -The normalized ESLint settings. diff --git a/packages/shared/docs/functions/tryRequire.md b/packages/shared/docs/functions/tryRequire.md new file mode 100644 index 0000000000..740e73221f --- /dev/null +++ b/packages/shared/docs/functions/tryRequire.md @@ -0,0 +1,23 @@ +[**@eslint-react/shared**](../README.md) + +*** + +[@eslint-react/shared](../README.md) / tryRequire + +# Function: tryRequire() + +> **tryRequire**(`id`, `at`): `E.Either`\<`unknown`, `Error`\> + +## Parameters + +### id + +`string` + +### at + +`string` = `...` + +## Returns + +`E.Either`\<`unknown`, `Error`\> diff --git a/packages/shared/docs/variables/normalizedSettingsCache.md b/packages/shared/docs/variables/normalizedSettingsCache.md new file mode 100644 index 0000000000..dcf4f16c2d --- /dev/null +++ b/packages/shared/docs/variables/normalizedSettingsCache.md @@ -0,0 +1,9 @@ +[**@eslint-react/shared**](../README.md) + +*** + +[@eslint-react/shared](../README.md) / normalizedSettingsCache + +# Variable: normalizedSettingsCache + +> `const` **normalizedSettingsCache**: `WeakMap`\<\{ `additionalComponents`: `object`[]; `additionalHooks`: \{ `use`: `string`[]; `useActionState`: `string`[]; `useCallback`: `string`[]; `useContext`: `string`[]; `useDebugValue`: `string`[]; `useDeferredValue`: `string`[]; `useEffect`: `string`[]; `useFormStatus`: `string`[]; `useId`: `string`[]; `useImperativeHandle`: `string`[]; `useInsertionEffect`: `string`[]; `useLayoutEffect`: `string`[]; `useMemo`: `string`[]; `useOptimistic`: `string`[]; `useReducer`: `string`[]; `useRef`: `string`[]; `useState`: `string`[]; `useSyncExternalStore`: `string`[]; `useTransition`: `string`[]; \}; `importSource`: `string`; `jsxPragma`: `string`; `jsxPragmaFrag`: `string`; `polymorphicPropName`: `string`; `version`: `string`; \}, `ESLintReactSettingsNormalized`\> diff --git a/packages/shared/package.json b/packages/shared/package.json index 212be9c0c7..840b68e53e 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -13,6 +13,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -27,6 +28,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -43,19 +45,19 @@ "@eslint-react/eff": "workspace:*", "@typescript-eslint/utils": "^8.19.0", "picomatch": "^4.0.2", - "ts-pattern": "^5.6.0", - "valibot": "^1.0.0-beta.10" + "ts-pattern": "^5.6.0" }, "devDependencies": { "@types/picomatch": "^3.0.1", "@workspace/configs": "workspace:*", + "fast-equals": "^5.2.0", + "micro-memoize": "^4.1.3", "tsup": "^8.3.5", - "type-fest": "^4.31.0" + "type-fest": "^4.31.0", + "valibot": "^1.0.0-beta.10" }, "engines": { "bun": ">=1.0.15", "node": ">=18.18.0" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 55ab4d0618..d3bb7cbddc 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,5 +1,7 @@ +export * from "./cache"; export * from "./constants"; export * from "./create-rule"; export * from "./get-react-version"; export * from "./schemas"; export * from "./settings"; +export * from "./try-require"; diff --git a/packages/shared/src/settings.ts b/packages/shared/src/settings.ts index 1083fa6090..99e5494d0e 100644 --- a/packages/shared/src/settings.ts +++ b/packages/shared/src/settings.ts @@ -1,13 +1,17 @@ import { E, F } from "@eslint-react/eff"; +import { shallowEqual } from "fast-equals"; +import memoize from "micro-memoize"; import pm from "picomatch"; import { match, P } from "ts-pattern"; -import { assert } from "valibot"; +import type { PartialDeep } from "type-fest"; +import { parse } from "valibot"; -import { normalizedSettingsCache } from "./cache"; import { getReactVersion } from "./get-react-version"; import type { ESLintReactSettings, ESLintReactSettingsNormalized } from "./schemas"; import { ESLintSettingsSchema } from "./schemas"; +// #region Constants + /** * The default ESLint settings for "react-x". */ @@ -20,26 +24,49 @@ export const DEFAULT_ESLINT_REACT_SETTINGS = { version: "detect", } as const satisfies ESLintReactSettings; +// #endregion + +// #region Decoding Functions + /** - * Get the normalized ESLint settings for "react-x" from the given context. - * @param context The context. - * @param context.settings The ESLint settings. - * @returns The normalized ESLint settings. + * Unsafely casts settings from a data object from `context.settings`. + * @internal + * @param data The data object. + * @returns settings The settings. */ -export function getSettingsFromContext(context: { settings: unknown }): ESLintReactSettingsNormalized { - assert(ESLintSettingsSchema, context.settings); - const raw = context.settings?.["react-x"] ?? {}; - const memoized = normalizedSettingsCache.get(raw); - if (memoized) { - return memoized; - } - const rawWithDefaults = { +export function unsafeDecodeSettings(data: unknown): PartialDeep { + // @ts-expect-error - skip type checking for unsafe cast + // eslint-disable-next-line @susisu/safe-typescript/no-type-assertion + return (data?.["react-x"] ?? {}) as PartialDeep; +} + +/** + * Decodes settings from a data object from `context.settings`. + * @internal + * @param data The data object. + * @returns settings The settings. + */ +export const decodeSettings = memoize((data: unknown): ESLintReactSettings => { + return { ...DEFAULT_ESLINT_REACT_SETTINGS, - ...raw, + ...parse(ESLintSettingsSchema, data)["react-x"] ?? {}, }; - const additionalComponents = rawWithDefaults.additionalComponents ?? []; - const normalized = { - ...rawWithDefaults, +}, { isEqual: (a, b) => a === b }); + +// #endregion + +// #region Normalization Functions + +/** + * Normalizes the settings by converting all shorthand properties to their full form. + * @param settings The settings. + * @returns The normalized settings. + * @internal + */ +export const normalizeSettings = memoize((settings: ESLintReactSettings): ESLintReactSettingsNormalized => { + const additionalComponents = settings.additionalComponents ?? []; + return { + ...settings, additionalComponents: additionalComponents.map((component) => ({ ...component, attributes: component.attributes?.map((attr) => ({ @@ -54,12 +81,18 @@ export function getSettingsFromContext(context: { settings: unknown }): ESLintRe if (!/^[\w-]+$/u.test(name)) return acc; return acc.set(name, as); }, new Map()), - version: match(rawWithDefaults.version) + version: match(settings.version) .with(P.union(P.nullish, "", "detect"), () => E.getOrElse(getReactVersion(), F.constant("19.0.0"))) .otherwise(F.identity), }; - normalizedSettingsCache.set(raw, normalized); - return normalized; +}, { isEqual: shallowEqual }); + +// #endregion + +// #region Helper Functions + +export function getSettingsFromContext(context: { settings: unknown }): ESLintReactSettingsNormalized { + return normalizeSettings(decodeSettings(context.settings)); } /** @@ -69,6 +102,8 @@ export function getSettingsFromContext(context: { settings: unknown }): ESLintRe */ export const defineSettings: (settings: ESLintReactSettings) => ESLintReactSettings = F.identity; +// #endregion + declare module "@typescript-eslint/utils/ts-eslint" { export interface SharedConfigurationSettings { // eslint-disable-next-line no-restricted-syntax diff --git a/packages/types/package.json b/packages/types/package.json index ba2875eb9b..3272eb81d3 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -13,6 +13,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -27,6 +28,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -51,7 +53,5 @@ "engines": { "bun": ">=1.0.15", "node": ">=18.18.0" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/utilities/ast/package.json b/packages/utilities/ast/package.json index 1139141b56..e82d1665e8 100644 --- a/packages/utilities/ast/package.json +++ b/packages/utilities/ast/package.json @@ -13,6 +13,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -27,6 +28,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -54,7 +56,5 @@ "engines": { "bun": ">=1.0.15", "node": ">=18.18.0" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/utilities/eff/package.json b/packages/utilities/eff/package.json index fb30a3e661..5d2bc298b0 100644 --- a/packages/utilities/eff/package.json +++ b/packages/utilities/eff/package.json @@ -13,6 +13,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -27,6 +28,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -47,7 +49,5 @@ "engines": { "bun": ">=1.0.15", "node": ">=18.18.0" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/utilities/jsx/package.json b/packages/utilities/jsx/package.json index b12cba0e20..a1f02990a3 100644 --- a/packages/utilities/jsx/package.json +++ b/packages/utilities/jsx/package.json @@ -13,6 +13,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -27,6 +28,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -55,7 +57,5 @@ "engines": { "bun": ">=1.0.15", "node": ">=18.18.0" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/utilities/var/package.json b/packages/utilities/var/package.json index 65b101ff13..7ebed04bf5 100644 --- a/packages/utilities/var/package.json +++ b/packages/utilities/var/package.json @@ -13,6 +13,7 @@ }, "license": "MIT", "author": "Eva1ent", + "sideEffects": false, "exports": { ".": { "import": { @@ -27,6 +28,7 @@ "./package.json": "./package.json" }, "main": "dist/index.js", + "module": "dist/index.mjs", "types": "dist/index.d.ts", "files": [ "dist", @@ -55,7 +57,5 @@ "engines": { "bun": ">=1.0.15", "node": ">=18.18.0" - }, - "sideEffects": false, - "module": "dist/index.mjs" + } } diff --git a/packages/utilities/var/src/is-initialized-from-source.ts b/packages/utilities/var/src/is-initialized-from-source.ts index bb106e5d05..ffc3079e61 100644 --- a/packages/utilities/var/src/is-initialized-from-source.ts +++ b/packages/utilities/var/src/is-initialized-from-source.ts @@ -19,36 +19,30 @@ export function isInitializedFromSource( source: string, initialScope: Scope, ): boolean { - return F.pipe( - findVariable(name, initialScope), - O.flatMapNullable((v) => v.defs.at(-1)), - O.match({ - onNone: () => false, - onSome: ({ node, parent }) => { - if (node.type === T.VariableDeclarator && node.init) { - const { init } = node; - // check for: `variable = Source.variable` - if (init.type === T.MemberExpression && init.object.type === T.Identifier) { - return isInitializedFromSource(init.object.name, source, initialScope); - } - // check for: `{ variable } = Source` - if (init.type === T.Identifier) { - return isInitializedFromSource(init.name, source, initialScope); - } - // check for: `variable = require('source')` or `variable = require('source').variable` - return F.pipe( - getRequireExpressionArguments(init), - O.flatMapNullable((args) => args[0]), - O.filter(AST.isStringLiteral), - // check for: `require('source')` or `require('source/...')` - O.exists((arg) => arg.value === source || arg.value.startsWith(`${source}/`)), - ); - } - // latest definition is an import declaration: import { variable } from 'source' - return isMatching({ type: "ImportDeclaration", source: { value: source } }, parent); - }, - }), - ); + const latestDef = O.flatMapNullable(findVariable(name, initialScope), (v) => v.defs.at(-1)); + if (O.isNone(latestDef)) return false; + const { node, parent } = latestDef.value; + if (node.type === T.VariableDeclarator && node.init) { + const { init } = node; + // check for: `variable = Source.variable` + if (init.type === T.MemberExpression && init.object.type === T.Identifier) { + return isInitializedFromSource(init.object.name, source, initialScope); + } + // check for: `{ variable } = Source` + if (init.type === T.Identifier) { + return isInitializedFromSource(init.name, source, initialScope); + } + // check for: `variable = require('source')` or `variable = require('source').variable` + return F.pipe( + getRequireExpressionArguments(init), + O.flatMapNullable((args) => args[0]), + O.filter(AST.isStringLiteral), + // check for: `require('source')` or `require('source/...')` + O.exists((arg) => arg.value === source || arg.value.startsWith(`${source}/`)), + ); + } + // latest definition is an import declaration: import { variable } from 'source' + return isMatching({ type: "ImportDeclaration", source: { value: source } }, parent); } function getRequireExpressionArguments(node: TSESTree.Node): O.Option { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3b4ef9eb5..ae842d02ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -919,9 +919,6 @@ importers: ts-pattern: specifier: ^5.6.0 version: 5.6.0 - valibot: - specifier: ^1.0.0-beta.10 - version: 1.0.0-beta.10(typescript@5.7.2) devDependencies: '@types/picomatch': specifier: ^3.0.1 @@ -929,12 +926,21 @@ importers: '@workspace/configs': specifier: workspace:* version: link:../../workspace/configs + fast-equals: + specifier: ^5.2.0 + version: 5.2.0 + micro-memoize: + specifier: ^4.1.3 + version: 4.1.3 tsup: specifier: ^8.3.5 version: 8.3.5(@swc/core@1.10.4(@swc/helpers@0.5.15))(jiti@2.4.2)(postcss@8.4.49)(tsx@4.19.2)(typescript@5.7.2)(yaml@2.6.1) type-fest: specifier: ^4.31.0 version: 4.31.0 + valibot: + specifier: ^1.0.0-beta.10 + version: 1.0.0-beta.10(typescript@5.7.2) packages/types: dependencies: @@ -5440,8 +5446,8 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-equals@5.0.1: - resolution: {integrity: sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==} + fast-equals@5.2.0: + resolution: {integrity: sha512-3VpaQYf+CDFdRQfgsb+3vY7XaKjM35WCMoQTTE8h4S/eUkHzyJFOOA/gATYgoLejy4FBrEQD/sXe5Auk4cW/AQ==} engines: {node: '>=6.0.0'} fast-glob@3.3.1: @@ -6386,8 +6392,8 @@ packages: mhchemparser@4.2.1: resolution: {integrity: sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==} - micro-memoize@4.1.2: - resolution: {integrity: sha512-+HzcV2H+rbSJzApgkj0NdTakkC+bnyeiUxgT6/m7mjcz1CmM22KYFKp+EVj1sWe4UYcnriJr5uqHQD/gMHLD+g==} + micro-memoize@4.1.3: + resolution: {integrity: sha512-DzRMi8smUZXT7rCGikRwldEh6eO6qzKiPPopcr1+2EY3AYKpy5fu159PKWwIS9A6IWnrvPKDMcuFtyrroZa8Bw==} micromark-core-commonmark@2.0.2: resolution: {integrity: sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==} @@ -9801,8 +9807,8 @@ snapshots: dependencies: '@eslint-react/eff': 1.23.1 '@typescript-eslint/utils': 8.19.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2) - fast-equals: 5.0.1 - micro-memoize: 4.1.2 + fast-equals: 5.2.0 + micro-memoize: 4.1.3 picomatch: 4.0.2 ts-pattern: 5.6.0 valibot: 1.0.0-beta.10(typescript@5.7.2) @@ -11910,7 +11916,7 @@ snapshots: '@cspell/cspell-pipe': 8.17.1 '@cspell/cspell-types': 8.17.1 cspell-trie-lib: 8.17.1 - fast-equals: 5.0.1 + fast-equals: 5.2.0 cspell-gitignore@8.17.1: dependencies: @@ -11953,7 +11959,7 @@ snapshots: cspell-io: 8.17.1 cspell-trie-lib: 8.17.1 env-paths: 3.0.0 - fast-equals: 5.0.1 + fast-equals: 5.2.0 gensequence: 7.0.0 import-fresh: 3.3.0 resolve-from: 5.0.0 @@ -12917,7 +12923,7 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-equals@5.0.1: {} + fast-equals@5.2.0: {} fast-glob@3.3.1: dependencies: @@ -14027,7 +14033,7 @@ snapshots: mhchemparser@4.2.1: {} - micro-memoize@4.1.2: {} + micro-memoize@4.1.3: {} micromark-core-commonmark@2.0.2: dependencies: diff --git a/workspace/configs/package.json b/workspace/configs/package.json index d901d889f1..242f1fa622 100644 --- a/workspace/configs/package.json +++ b/workspace/configs/package.json @@ -3,9 +3,9 @@ "version": "0.0.0", "private": true, "description": "Local configuration files", + "sideEffects": false, "exports": { "./tsconfig.base.json": "./tsconfig.base.json", "./typedoc.base.json": "./typedoc.base.json" - }, - "sideEffects": false + } } diff --git a/workspace/eslint-plugin-deps/package.json b/workspace/eslint-plugin-deps/package.json index eb5c43b381..abf8a620a5 100644 --- a/workspace/eslint-plugin-deps/package.json +++ b/workspace/eslint-plugin-deps/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "description": "Local ESLint dependencies", + "sideEffects": false, "dependencies": { "@eslint/markdown": "^6.2.1", "@susisu/eslint-plugin-safe-typescript": "^0.9.2", @@ -14,6 +15,5 @@ "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-unicorn": "^56.0.1", "eslint-plugin-vitest": "^0.5.4" - }, - "sideEffects": false + } } diff --git a/workspace/eslint-plugin-local/package.json b/workspace/eslint-plugin-local/package.json index 84f7897c71..e9c3f4dd52 100644 --- a/workspace/eslint-plugin-local/package.json +++ b/workspace/eslint-plugin-local/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "description": "Local ESLint pluginf for use in the workspace", + "sideEffects": false, "type": "module", "exports": { ".": { @@ -45,6 +46,5 @@ "engines": { "bun": ">=1.0.15", "node": ">=18.18.0" - }, - "sideEffects": false + } }