diff --git a/.changeset/hip-cycles-agree.md b/.changeset/hip-cycles-agree.md new file mode 100644 index 00000000000..1b9e2bf98c6 --- /dev/null +++ b/.changeset/hip-cycles-agree.md @@ -0,0 +1,5 @@ +--- +'@graphiql/react': minor +--- + +compile source code with react-compiler, remove `useMemo` and `useCallback` usages diff --git a/.eslintrc.js b/.eslintrc.js index 61a312a566a..1f4dde1c454 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -453,6 +453,21 @@ module.exports = { 'promise/prefer-await-to-then': 'error', }, }, + { + files: ['packages/graphiql-react/**'], + plugins: ['react-compiler'], + rules: { + '@typescript-eslint/no-restricted-imports': [ + 'error', + ...RESTRICTED_IMPORTS, + { + name: 'react', + importNames: ['memo', 'useCallback', 'useMemo'], + }, + ], + 'react-compiler/react-compiler': 'error', + }, + }, { // Monaco-GraphQL rules files: ['packages/monaco-graphql/**'], diff --git a/package.json b/package.json index 01b18d6cbbf..bc16188c03a 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "gen-agenda": "wgutils agenda gen" }, "dependencies": { + "eslint-plugin-react-compiler": "19.0.0-beta-37ed2a7-20241206", "graphql-http": "^1.22.1", "@babel/cli": "^7.21.0", "@babel/core": "^7.21.0", @@ -109,7 +110,7 @@ "concurrently": "^7.0.0", "copy": "^0.3.2", "cspell": "^5.15.2", - "eslint": "^9.7.0", + "eslint": "9.14.0", "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-cypress": "^3.4.0", @@ -117,8 +118,8 @@ "eslint-plugin-jest": "^28.6.0", "eslint-plugin-mdx": "^3.1.5", "eslint-plugin-promise": "^7.0.0", - "eslint-plugin-react": "^7.35.0", - "eslint-plugin-react-hooks": "^5.1.0-rc-76002254-20240724", + "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-sonar": "^0.14.1", "eslint-plugin-sonarjs": "^1.0.4", "eslint-plugin-unicorn": "^55.0.0", diff --git a/packages/graphiql-react/package.json b/packages/graphiql-react/package.json index 4e8b589b125..8a0dd8f188c 100644 --- a/packages/graphiql-react/package.json +++ b/packages/graphiql-react/package.json @@ -48,6 +48,7 @@ "react-dom": "^16.8.0 || ^17 || ^18" }, "dependencies": { + "react-compiler-runtime": "19.0.0-beta-37ed2a7-20241206", "@graphiql/toolkit": "^0.11.0", "@headlessui/react": "^1.7.15", "@radix-ui/react-dialog": "^1.0.4", @@ -66,6 +67,7 @@ "set-value": "^4.1.0" }, "devDependencies": { + "babel-plugin-react-compiler": "19.0.0-beta-37ed2a7-20241206", "@types/react-dom": "^18.3.1", "@babel/helper-string-parser": "^7.19.4", "@testing-library/dom": "^10.4.0", diff --git a/packages/graphiql-react/src/editor/common.ts b/packages/graphiql-react/src/editor/common.ts index 8ed6d7f936e..40d45515a17 100644 --- a/packages/graphiql-react/src/editor/common.ts +++ b/packages/graphiql-react/src/editor/common.ts @@ -1,3 +1,5 @@ +'use no memo'; + import { KeyMap } from './types'; import { isMacOs } from '../utility/is-macos'; diff --git a/packages/graphiql-react/src/editor/completion.ts b/packages/graphiql-react/src/editor/completion.ts index 231a53851ec..490ec4a573b 100644 --- a/packages/graphiql-react/src/editor/completion.ts +++ b/packages/graphiql-react/src/editor/completion.ts @@ -1,3 +1,5 @@ +'use no memo'; + import type { Editor, EditorChange } from 'codemirror'; import type { IHint } from 'codemirror-graphql/hint'; import { diff --git a/packages/graphiql-react/src/editor/components/index.ts b/packages/graphiql-react/src/editor/components/index.ts index 9fbe6db2a47..cf1bc1d09eb 100644 --- a/packages/graphiql-react/src/editor/components/index.ts +++ b/packages/graphiql-react/src/editor/components/index.ts @@ -1,3 +1,5 @@ +'use no memo'; + export { HeaderEditor } from './header-editor'; export { ImagePreview } from './image-preview'; export { QueryEditor } from './query-editor'; diff --git a/packages/graphiql-react/src/editor/context.tsx b/packages/graphiql-react/src/editor/context.tsx index 339f84cf03b..81bd501ebea 100644 --- a/packages/graphiql-react/src/editor/context.tsx +++ b/packages/graphiql-react/src/editor/context.tsx @@ -7,14 +7,7 @@ import { visit, } from 'graphql'; import { VariableToType } from 'graphql-language-service'; -import { - ReactNode, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { ReactNode, useEffect, useRef, useState } from 'react'; import { useStorageContext } from '../storage'; import { createContextHook, createNullableContext } from '../utility/context'; @@ -329,7 +322,7 @@ export function EditorContextProvider(props: EditorContextProviderProps) { const [tabState, setTabState] = useState(initialState.tabState); - const setShouldPersistHeaders = useCallback( + const setShouldPersistHeaders = // eslint-disable-line react-hooks/exhaustive-deps -- false positive, function is optimized by react-compiler, no need to wrap with useCallback (persist: boolean) => { if (persist) { storage?.set(STORAGE_KEY_HEADERS, headerEditor?.getValue() ?? ''); @@ -341,9 +334,7 @@ export function EditorContextProvider(props: EditorContextProviderProps) { } setShouldPersistHeadersInternal(persist); storage?.set(PERSIST_HEADERS_STORAGE_KEY, persist.toString()); - }, - [storage, tabState, headerEditor], - ); + }; const lastShouldPersistHeadersProp = useRef(); useEffect(() => { @@ -369,7 +360,7 @@ export function EditorContextProvider(props: EditorContextProviderProps) { defaultHeaders, }); - const addTab = useCallback(() => { + const addTab: EditorContextType['addTab'] = () => { setTabState(current => { // Make sure the current tab stores the latest values const updatedValues = synchronizeActiveTabValues(current); @@ -388,67 +379,49 @@ export function EditorContextProvider(props: EditorContextProviderProps) { onTabChange?.(updated); return updated; }); - }, [ - defaultHeaders, - defaultQuery, - onTabChange, - setEditorValues, - storeTabs, - synchronizeActiveTabValues, - ]); - - const changeTab = useCallback( - index => { - setTabState(current => { - const updated = { - ...current, - activeTabIndex: index, - }; - storeTabs(updated); - setEditorValues(updated.tabs[updated.activeTabIndex]); - onTabChange?.(updated); - return updated; - }); - }, - [onTabChange, setEditorValues, storeTabs], - ); + }; - const moveTab = useCallback( - newOrder => { - setTabState(current => { - const activeTab = current.tabs[current.activeTabIndex]; - const updated = { - tabs: newOrder, - activeTabIndex: newOrder.indexOf(activeTab), - }; - storeTabs(updated); - setEditorValues(updated.tabs[updated.activeTabIndex]); - onTabChange?.(updated); - return updated; - }); - }, - [onTabChange, setEditorValues, storeTabs], - ); + const changeTab: EditorContextType['changeTab'] = index => { + setTabState(current => { + const updated = { + ...current, + activeTabIndex: index, + }; + storeTabs(updated); + setEditorValues(updated.tabs[updated.activeTabIndex]); + onTabChange?.(updated); + return updated; + }); + }; - const closeTab = useCallback( - index => { - setTabState(current => { - const updated = { - tabs: current.tabs.filter((_tab, i) => index !== i), - activeTabIndex: Math.max(current.activeTabIndex - 1, 0), - }; - storeTabs(updated); - setEditorValues(updated.tabs[updated.activeTabIndex]); - onTabChange?.(updated); - return updated; - }); - }, - [onTabChange, setEditorValues, storeTabs], - ); + const moveTab: EditorContextType['moveTab'] = newOrder => { + setTabState(current => { + const activeTab = current.tabs[current.activeTabIndex]; + const updated = { + tabs: newOrder, + activeTabIndex: newOrder.indexOf(activeTab), + }; + storeTabs(updated); + setEditorValues(updated.tabs[updated.activeTabIndex]); + onTabChange?.(updated); + return updated; + }); + }; + + const closeTab: EditorContextType['closeTab'] = index => { + setTabState(current => { + const updated = { + tabs: current.tabs.filter((_tab, i) => index !== i), + activeTabIndex: Math.max(current.activeTabIndex - 1, 0), + }; + storeTabs(updated); + setEditorValues(updated.tabs[updated.activeTabIndex]); + onTabChange?.(updated); + return updated; + }); + }; - const updateActiveTabValues = useCallback< - EditorContextType['updateActiveTabValues'] - >( + const updateActiveTabValues: EditorContextType['updateActiveTabValues'] = partialTab => { setTabState(current => { const updated = setPropertiesInActiveTab(current, partialTab); @@ -456,25 +429,21 @@ export function EditorContextProvider(props: EditorContextProviderProps) { onTabChange?.(updated); return updated; }); - }, - [onTabChange, storeTabs], - ); + }; const { onEditOperationName } = props; - const setOperationName = useCallback( + const setOperationName: EditorContextType['setOperationName'] = operationName => { if (!queryEditor) { return; } - queryEditor.operationName = operationName; + updateQueryEditor(queryEditor, operationName); updateActiveTabValues({ operationName }); onEditOperationName?.(operationName); - }, - [onEditOperationName, queryEditor, updateActiveTabValues], - ); + }; - const externalFragments = useMemo(() => { + const externalFragments = (() => { const map = new Map(); if (Array.isArray(props.externalFragments)) { for (const fragment of props.externalFragments) { @@ -492,74 +461,54 @@ export function EditorContextProvider(props: EditorContextProviderProps) { ); } return map; - }, [props.externalFragments]); + })(); - const validationRules = useMemo( - () => props.validationRules || [], - [props.validationRules], - ); + const validationRules = props.validationRules || []; - const value = useMemo( - () => ({ - ...tabState, - addTab, - changeTab, - moveTab, - closeTab, - updateActiveTabValues, - - headerEditor, - queryEditor, - responseEditor, - variableEditor, - setHeaderEditor, - setQueryEditor, - setResponseEditor, - setVariableEditor, - - setOperationName, - - initialQuery: initialState.query, - initialVariables: initialState.variables, - initialHeaders: initialState.headers, - initialResponse: initialState.response, - - externalFragments, - validationRules, + const value: EditorContextType = { + ...tabState, + addTab, + changeTab, + moveTab, + closeTab, + updateActiveTabValues, - shouldPersistHeaders, - setShouldPersistHeaders, - }), - [ - tabState, - addTab, - changeTab, - moveTab, - closeTab, - updateActiveTabValues, - - headerEditor, - queryEditor, - responseEditor, - variableEditor, + headerEditor, + queryEditor, + responseEditor, + variableEditor, + setHeaderEditor, + setQueryEditor, + setResponseEditor, + setVariableEditor, - setOperationName, + setOperationName, - initialState, + initialQuery: initialState.query, + initialVariables: initialState.variables, + initialHeaders: initialState.headers, + initialResponse: initialState.response, - externalFragments, - validationRules, + externalFragments, + validationRules, - shouldPersistHeaders, - setShouldPersistHeaders, - ], - ); + shouldPersistHeaders, + setShouldPersistHeaders, + }; return ( {children} ); } +// To make react-compiler happy, otherwise it fails due mutating props +function updateQueryEditor( + queryEditor: CodeMirrorEditorWithOperationFacts, + operationName: string, +) { + queryEditor.operationName = operationName; +} + export const useEditorContext = createContextHook(EditorContext); const PERSIST_HEADERS_STORAGE_KEY = 'shouldPersistHeaders'; diff --git a/packages/graphiql-react/src/editor/header-editor.ts b/packages/graphiql-react/src/editor/header-editor.ts index 609b7487c62..e9e19a74bee 100644 --- a/packages/graphiql-react/src/editor/header-editor.ts +++ b/packages/graphiql-react/src/editor/header-editor.ts @@ -25,6 +25,15 @@ export type UseHeaderEditorArgs = WriteableEditorProps & { onEdit?(value: string): void; }; +// To make react-compiler happy, otherwise complains about using dynamic imports in Component +function importCodeMirrorImports() { + return importCodeMirror([ + // @ts-expect-error + import('codemirror/mode/javascript/javascript.js'), + ]); +} +const _useHeaderEditor = useHeaderEditor; + export function useHeaderEditor( { editorTheme = DEFAULT_EDITOR_THEME, @@ -41,20 +50,17 @@ export function useHeaderEditor( shouldPersistHeaders, } = useEditorContext({ nonNull: true, - caller: caller || useHeaderEditor, + caller: caller || _useHeaderEditor, }); const executionContext = useExecutionContext(); - const merge = useMergeQuery({ caller: caller || useHeaderEditor }); - const prettify = usePrettifyEditors({ caller: caller || useHeaderEditor }); + const merge = useMergeQuery({ caller: caller || _useHeaderEditor }); + const prettify = usePrettifyEditors({ caller: caller || _useHeaderEditor }); const ref = useRef(null); useEffect(() => { let isActive = true; - void importCodeMirror([ - // @ts-expect-error - import('codemirror/mode/javascript/javascript.js'), - ]).then(CodeMirror => { + void importCodeMirrorImports().then(CodeMirror => { // Don't continue if the effect has already been cleaned up if (!isActive) { return; @@ -119,7 +125,7 @@ export function useHeaderEditor( onEdit, shouldPersistHeaders ? STORAGE_KEY : null, 'headers', - useHeaderEditor, + _useHeaderEditor, ); useKeyMap(headerEditor, ['Cmd-Enter', 'Ctrl-Enter'], executionContext?.run); diff --git a/packages/graphiql-react/src/editor/hooks.ts b/packages/graphiql-react/src/editor/hooks.ts index e946c59926b..ed9339b3b09 100644 --- a/packages/graphiql-react/src/editor/hooks.ts +++ b/packages/graphiql-react/src/editor/hooks.ts @@ -3,7 +3,7 @@ import type { EditorChange, EditorConfiguration } from 'codemirror'; import type { SchemaReference } from 'codemirror-graphql/utils/SchemaReference'; import copyToClipboard from 'copy-to-clipboard'; import { parse, print } from 'graphql'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useExplorerContext } from '../explorer'; import { usePluginContext } from '../plugin'; @@ -16,7 +16,7 @@ import { CodeMirrorEditor } from './types'; export function useSynchronizeValue( editor: CodeMirrorEditor | null, - value: string | undefined, + value?: string, ) { useEffect(() => { if (editor && typeof value === 'string' && value !== editor.getValue()) { @@ -65,7 +65,7 @@ export function useChangeHandler( const handleChange = ( editorInstance: CodeMirrorEditor, - changeObj: EditorChange | undefined, + changeObj?: EditorChange, ) => { // When we signal a change manually without actually changing anything // we don't want to invoke the callback. @@ -130,7 +130,7 @@ type EmptyCallback = () => void; export function useKeyMap( editor: CodeMirrorEditor | null, keys: string[], - callback: EmptyCallback | undefined, + callback?: EmptyCallback, ) { useEffect(() => { if (!editor) { @@ -163,12 +163,18 @@ export type UseCopyQueryArgs = { onCopyQuery?: (query: string) => void; }; +// To make react-compiler happy, otherwise complains about - Hooks may not be referenced as normal values +const _useCopyQuery = useCopyQuery; +const _useMergeQuery = useMergeQuery; +const _usePrettifyEditors = usePrettifyEditors; +const _useAutoCompleteLeafs = useAutoCompleteLeafs; + export function useCopyQuery({ caller, onCopyQuery }: UseCopyQueryArgs = {}) { const { queryEditor } = useEditorContext({ nonNull: true, - caller: caller || useCopyQuery, + caller: caller || _useCopyQuery, }); - return useCallback(() => { + return () => { if (!queryEditor) { return; } @@ -177,7 +183,7 @@ export function useCopyQuery({ caller, onCopyQuery }: UseCopyQueryArgs = {}) { copyToClipboard(query); onCopyQuery?.(query); - }, [queryEditor, onCopyQuery]); + }; } type UseMergeQueryArgs = { @@ -190,10 +196,13 @@ type UseMergeQueryArgs = { export function useMergeQuery({ caller }: UseMergeQueryArgs = {}) { const { queryEditor } = useEditorContext({ nonNull: true, - caller: caller || useMergeQuery, + caller: caller || _useMergeQuery, + }); + const { schema } = useSchemaContext({ + nonNull: true, + caller: _useMergeQuery, }); - const { schema } = useSchemaContext({ nonNull: true, caller: useMergeQuery }); - return useCallback(() => { + return () => { const documentAST = queryEditor?.documentAST; const query = queryEditor?.getValue(); if (!documentAST || !query) { @@ -201,7 +210,7 @@ export function useMergeQuery({ caller }: UseMergeQueryArgs = {}) { } queryEditor.setValue(print(mergeAst(documentAST, schema))); - }, [queryEditor, schema]); + }; } type UsePrettifyEditorsArgs = { @@ -214,9 +223,9 @@ type UsePrettifyEditorsArgs = { export function usePrettifyEditors({ caller }: UsePrettifyEditorsArgs = {}) { const { queryEditor, headerEditor, variableEditor } = useEditorContext({ nonNull: true, - caller: caller || usePrettifyEditors, + caller: caller || _usePrettifyEditors, }); - return useCallback(() => { + return () => { if (variableEditor) { const variableEditorContent = variableEditor.getValue(); try { @@ -258,7 +267,7 @@ export function usePrettifyEditors({ caller }: UsePrettifyEditorsArgs = {}) { queryEditor.setValue(prettifiedEditorContent); } } - }, [queryEditor, variableEditor, headerEditor]); + }; } export type UseAutoCompleteLeafsArgs = { @@ -280,13 +289,13 @@ export function useAutoCompleteLeafs({ }: UseAutoCompleteLeafsArgs = {}) { const { schema } = useSchemaContext({ nonNull: true, - caller: caller || useAutoCompleteLeafs, + caller: caller || _useAutoCompleteLeafs, }); const { queryEditor } = useEditorContext({ nonNull: true, - caller: caller || useAutoCompleteLeafs, + caller: caller || _useAutoCompleteLeafs, }); - return useCallback(() => { + return () => { if (!queryEditor) { return; } @@ -330,14 +339,14 @@ export function useAutoCompleteLeafs({ } return result; - }, [getDefaultFieldNames, queryEditor, schema]); + }; } -export type InitialState = string | (() => string); - // https://react.dev/learn/you-might-not-need-an-effect -export const useEditorState = (editor: 'query' | 'variable' | 'header') => { +export const useEditorState = ( + editor: 'query' | 'variable' | 'header', +): [string, (val: string) => void] => { const context = useEditorContext({ nonNull: true, }); @@ -349,14 +358,8 @@ export const useEditorState = (editor: 'query' | 'variable' | 'header') => { valueString = editorValue; } - const handleEditorValue = useCallback( - (value: string) => editorInstance?.setValue(value), - [editorInstance], - ); - return useMemo<[string, (val: string) => void]>( - () => [valueString, handleEditorValue], - [valueString, handleEditorValue], - ); + const handleEditorValue = (value: string) => editorInstance?.setValue(value); + return [valueString, handleEditorValue]; }; /** @@ -444,20 +447,17 @@ export function useOptimisticState([ } }, [upstreamState, state, upstreamSetState]); - const setState = useCallback( - (newState: string) => { - setOperationsText(newState); - if ( - lastStateRef.current.pending === null && - lastStateRef.current.last !== newState - ) { - // No pending updates and change has occurred... send it upstream - lastStateRef.current.pending = newState; - upstreamSetState(newState); - } - }, - [upstreamSetState], - ); + const setState = (newState: string) => { + setOperationsText(newState); + if ( + lastStateRef.current.pending === null && + lastStateRef.current.last !== newState + ) { + // No pending updates and change has occurred... send it upstream + lastStateRef.current.pending = newState; + upstreamSetState(newState); + } + }; - return useMemo(() => [state, setState], [state, setState]); + return [state, setState]; } diff --git a/packages/graphiql-react/src/editor/index.ts b/packages/graphiql-react/src/editor/index.ts index c7a902c4307..5e277a103d8 100644 --- a/packages/graphiql-react/src/editor/index.ts +++ b/packages/graphiql-react/src/editor/index.ts @@ -1,3 +1,5 @@ +'use no memo'; + export { HeaderEditor, ImagePreview, diff --git a/packages/graphiql-react/src/editor/query-editor.ts b/packages/graphiql-react/src/editor/query-editor.ts index a589fbc06ae..455053253f9 100644 --- a/packages/graphiql-react/src/editor/query-editor.ts +++ b/packages/graphiql-react/src/editor/query-editor.ts @@ -9,14 +9,9 @@ import type { import { getOperationFacts, GraphQLDocumentMode, + OperationFacts, } from 'graphql-language-service'; -import { - MutableRefObject, - useCallback, - useEffect, - useMemo, - useRef, -} from 'react'; +import { MutableRefObject, useEffect, useRef } from 'react'; import { useExecutionContext } from '../execution'; import { useExplorerContext } from '../explorer'; @@ -67,6 +62,57 @@ export type UseQueryEditorArgs = WriteableEditorProps & onEdit?(value: string, documentAST?: DocumentNode): void; }; +// To make react-compiler happy, otherwise complains about using dynamic imports in Component +function importCodeMirrorImports() { + return importCodeMirror([ + import('codemirror/addon/comment/comment.js'), + import('codemirror/addon/search/search.js'), + import('codemirror-graphql/esm/hint.js'), + import('codemirror-graphql/esm/lint.js'), + import('codemirror-graphql/esm/info.js'), + import('codemirror-graphql/esm/jump.js'), + import('codemirror-graphql/esm/mode.js'), + ]); +} +const _useQueryEditor = useQueryEditor; + +// To make react-compiler happy since we mutate variableEditor +function updateVariableEditor( + variableEditor: CodeMirrorEditor, + operationFacts?: OperationFacts, +) { + variableEditor.state.lint.linterOptions.variableToType = + operationFacts?.variableToType; + variableEditor.options.lint.variableToType = operationFacts?.variableToType; + variableEditor.options.hintOptions.variableToType = + operationFacts?.variableToType; +} +function updateEditorSchema( + editor: CodeMirrorEditor, + schema: GraphQLSchema | null, +) { + editor.state.lint.linterOptions.schema = schema; + editor.options.lint.schema = schema; + editor.options.hintOptions.schema = schema; + editor.options.info.schema = schema; + editor.options.jump.schema = schema; +} +function updateEditorValidationRules( + editor: CodeMirrorEditor, + validationRules: ValidationRule[] | null, +) { + editor.state.lint.linterOptions.validationRules = validationRules; + editor.options.lint.validationRules = validationRules; +} +function updateEditorExternalFragments( + editor: CodeMirrorEditor, + externalFragmentList: FragmentDefinitionNode[], +) { + editor.state.lint.linterOptions.externalFragments = externalFragmentList; + editor.options.lint.externalFragments = externalFragmentList; + editor.options.hintOptions.externalFragments = externalFragmentList; +} + export function useQueryEditor( { editorTheme = DEFAULT_EDITOR_THEME, @@ -80,7 +126,7 @@ export function useQueryEditor( ) { const { schema } = useSchemaContext({ nonNull: true, - caller: caller || useQueryEditor, + caller: caller || _useQueryEditor, }); const { externalFragments, @@ -93,15 +139,15 @@ export function useQueryEditor( updateActiveTabValues, } = useEditorContext({ nonNull: true, - caller: caller || useQueryEditor, + caller: caller || _useQueryEditor, }); const executionContext = useExecutionContext(); const storage = useStorageContext(); const explorer = useExplorerContext(); const plugin = usePluginContext(); - const copy = useCopyQuery({ caller: caller || useQueryEditor, onCopyQuery }); - const merge = useMergeQuery({ caller: caller || useQueryEditor }); - const prettify = usePrettifyEditors({ caller: caller || useQueryEditor }); + const copy = useCopyQuery({ caller: caller || _useQueryEditor, onCopyQuery }); + const merge = useMergeQuery({ caller: caller || _useQueryEditor }); + const prettify = usePrettifyEditors({ caller: caller || _useQueryEditor }); const ref = useRef(null); const codeMirrorRef = useRef(); @@ -143,15 +189,7 @@ export function useQueryEditor( useEffect(() => { let isActive = true; - void importCodeMirror([ - import('codemirror/addon/comment/comment.js'), - import('codemirror/addon/search/search.js'), - import('codemirror-graphql/esm/hint.js'), - import('codemirror-graphql/esm/lint.js'), - import('codemirror-graphql/esm/info.js'), - import('codemirror-graphql/esm/jump.js'), - import('codemirror-graphql/esm/mode.js'), - ]).then(CodeMirror => { + void importCodeMirrorImports().then(CodeMirror => { // Don't continue if the effect has already been cleaned up if (!isActive) { return; @@ -317,12 +355,7 @@ export function useQueryEditor( // Update variable types for the variable editor if (variableEditor) { - variableEditor.state.lint.linterOptions.variableToType = - operationFacts?.variableToType; - variableEditor.options.lint.variableToType = - operationFacts?.variableToType; - variableEditor.options.hintOptions.variableToType = - operationFacts?.variableToType; + updateVariableEditor(variableEditor, operationFacts); codeMirrorRef.current?.signal(variableEditor, 'change', variableEditor); } @@ -387,10 +420,10 @@ export function useQueryEditor( codeMirrorRef, ); - useCompletion(queryEditor, onClickReference || null, useQueryEditor); + useCompletion(queryEditor, onClickReference || null, _useQueryEditor); const run = executionContext?.run; - const runAtCursor = useCallback(() => { + const runAtCursor = () => { if ( !run || !queryEditor || @@ -420,7 +453,7 @@ export function useQueryEditor( } run(); - }, [queryEditor, run, setOperationName]); + }; useKeyMap(queryEditor, ['Cmd-Enter', 'Ctrl-Enter'], runAtCursor); useKeyMap(queryEditor, ['Shift-Ctrl-C'], copy); @@ -449,12 +482,7 @@ function useSynchronizeSchema( } const didChange = editor.options.lint.schema !== schema; - - editor.state.lint.linterOptions.schema = schema; - editor.options.lint.schema = schema; - editor.options.hintOptions.schema = schema; - editor.options.info.schema = schema; - editor.options.jump.schema = schema; + updateEditorSchema(editor, schema); if (didChange && codeMirrorRef.current) { codeMirrorRef.current.signal(editor, 'change', editor); @@ -473,9 +501,7 @@ function useSynchronizeValidationRules( } const didChange = editor.options.lint.validationRules !== validationRules; - - editor.state.lint.linterOptions.validationRules = validationRules; - editor.options.lint.validationRules = validationRules; + updateEditorValidationRules(editor, validationRules); if (didChange && codeMirrorRef.current) { codeMirrorRef.current.signal(editor, 'change', editor); @@ -488,10 +514,7 @@ function useSynchronizeExternalFragments( externalFragments: Map, codeMirrorRef: MutableRefObject, ) { - const externalFragmentList = useMemo( - () => [...externalFragments.values()], - [externalFragments], - ); + const externalFragmentList = [...externalFragments.values()]; // eslint-disable-line react-hooks/exhaustive-deps -- false positive, variable is optimized by react-compiler, no need to wrap with useMemo useEffect(() => { if (!editor) { @@ -500,10 +523,7 @@ function useSynchronizeExternalFragments( const didChange = editor.options.lint.externalFragments !== externalFragmentList; - - editor.state.lint.linterOptions.externalFragments = externalFragmentList; - editor.options.lint.externalFragments = externalFragmentList; - editor.options.hintOptions.externalFragments = externalFragmentList; + updateEditorExternalFragments(editor, externalFragmentList); if (didChange && codeMirrorRef.current) { codeMirrorRef.current.signal(editor, 'change', editor); diff --git a/packages/graphiql-react/src/editor/response-editor.tsx b/packages/graphiql-react/src/editor/response-editor.tsx index 405e6eae784..18ae1e8a112 100644 --- a/packages/graphiql-react/src/editor/response-editor.tsx +++ b/packages/graphiql-react/src/editor/response-editor.tsx @@ -34,6 +34,27 @@ export type UseResponseEditorArgs = CommonEditorProps & { responseTooltip?: ResponseTooltipType; }; +// To make react-compiler happy, otherwise complains about using dynamic imports in Component +function importCodeMirrorImports() { + return importCodeMirror( + [ + import('codemirror/addon/fold/foldgutter.js'), + import('codemirror/addon/fold/brace-fold.js'), + import('codemirror/addon/dialog/dialog.js'), + import('codemirror/addon/search/search.js'), + import('codemirror/addon/search/searchcursor.js'), + import('codemirror/addon/search/jump-to-line.js'), + // @ts-expect-error + import('codemirror/keymap/sublime.js'), + import('codemirror-graphql/esm/results/mode.js'), + import('codemirror-graphql/esm/utils/info-addon.js'), + ], + { useCommonAddons: false }, + ); +} +// To make react-compiler happy, otherwise complains about - Hooks may not be referenced as normal values +const _useResponseEditor = useResponseEditor; + export function useResponseEditor( { responseTooltip, @@ -44,12 +65,12 @@ export function useResponseEditor( ) { const { fetchError, validationErrors } = useSchemaContext({ nonNull: true, - caller: caller || useResponseEditor, + caller: caller || _useResponseEditor, }); const { initialResponse, responseEditor, setResponseEditor } = useEditorContext({ nonNull: true, - caller: caller || useResponseEditor, + caller: caller || _useResponseEditor, }); const ref = useRef(null); @@ -62,21 +83,7 @@ export function useResponseEditor( useEffect(() => { let isActive = true; - void importCodeMirror( - [ - import('codemirror/addon/fold/foldgutter.js'), - import('codemirror/addon/fold/brace-fold.js'), - import('codemirror/addon/dialog/dialog.js'), - import('codemirror/addon/search/search.js'), - import('codemirror/addon/search/searchcursor.js'), - import('codemirror/addon/search/jump-to-line.js'), - // @ts-expect-error - import('codemirror/keymap/sublime.js'), - import('codemirror-graphql/esm/results/mode.js'), - import('codemirror-graphql/esm/utils/info-addon.js'), - ], - { useCommonAddons: false }, - ).then(CodeMirror => { + void importCodeMirrorImports().then(CodeMirror => { // Don't continue if the effect has already been cleaned up if (!isActive) { return; diff --git a/packages/graphiql-react/src/editor/tabs.ts b/packages/graphiql-react/src/editor/tabs.ts index 067d730666b..01eb887dab7 100644 --- a/packages/graphiql-react/src/editor/tabs.ts +++ b/packages/graphiql-react/src/editor/tabs.ts @@ -1,4 +1,7 @@ +'use no memo'; // can't figure why it isn't optimized + import { StorageAPI } from '@graphiql/toolkit'; +// eslint-disable-next-line @typescript-eslint/no-restricted-imports -- fixme import { useCallback, useMemo } from 'react'; import debounce from '../utility/debounce'; diff --git a/packages/graphiql-react/src/editor/variable-editor.ts b/packages/graphiql-react/src/editor/variable-editor.ts index 364a0658b58..a03a93cbdce 100644 --- a/packages/graphiql-react/src/editor/variable-editor.ts +++ b/packages/graphiql-react/src/editor/variable-editor.ts @@ -33,6 +33,18 @@ export type UseVariableEditorArgs = WriteableEditorProps & { onEdit?(value: string): void; }; +// To make react-compiler happy, otherwise complains about using dynamic imports in Component +function importCodeMirrorImports() { + return importCodeMirror([ + import('codemirror-graphql/esm/variables/hint.js'), + import('codemirror-graphql/esm/variables/lint.js'), + import('codemirror-graphql/esm/variables/mode.js'), + ]); +} + +// To make react-compiler happy, otherwise complains about - Hooks may not be referenced as normal values +const _useVariableEditor = useVariableEditor; + export function useVariableEditor( { editorTheme = DEFAULT_EDITOR_THEME, @@ -46,22 +58,18 @@ export function useVariableEditor( const { initialVariables, variableEditor, setVariableEditor } = useEditorContext({ nonNull: true, - caller: caller || useVariableEditor, + caller: caller || _useVariableEditor, }); const executionContext = useExecutionContext(); - const merge = useMergeQuery({ caller: caller || useVariableEditor }); - const prettify = usePrettifyEditors({ caller: caller || useVariableEditor }); + const merge = useMergeQuery({ caller: caller || _useVariableEditor }); + const prettify = usePrettifyEditors({ caller: caller || _useVariableEditor }); const ref = useRef(null); const codeMirrorRef = useRef(); useEffect(() => { let isActive = true; - void importCodeMirror([ - import('codemirror-graphql/esm/variables/hint.js'), - import('codemirror-graphql/esm/variables/lint.js'), - import('codemirror-graphql/esm/variables/mode.js'), - ]).then(CodeMirror => { + void importCodeMirrorImports().then(CodeMirror => { // Don't continue if the effect has already been cleaned up if (!isActive) { return; @@ -139,10 +147,10 @@ export function useVariableEditor( onEdit, STORAGE_KEY, 'variables', - useVariableEditor, + _useVariableEditor, ); - useCompletion(variableEditor, onClickReference || null, useVariableEditor); + useCompletion(variableEditor, onClickReference || null, _useVariableEditor); useKeyMap(variableEditor, ['Cmd-Enter', 'Ctrl-Enter'], executionContext?.run); useKeyMap(variableEditor, ['Shift-Ctrl-P'], prettify); diff --git a/packages/graphiql-react/src/editor/whitespace.ts b/packages/graphiql-react/src/editor/whitespace.ts index e0c7d4e86c2..1b78adbf939 100644 --- a/packages/graphiql-react/src/editor/whitespace.ts +++ b/packages/graphiql-react/src/editor/whitespace.ts @@ -1,3 +1,5 @@ +'use no memo'; + // Unicode whitespace characters that break the interface. export const invalidCharacters = Array.from({ length: 11 }, (_, i) => { // \u2000 -> \u200a diff --git a/packages/graphiql-react/src/execution.tsx b/packages/graphiql-react/src/execution.tsx index 5df69e39bbf..3c13d335fa3 100644 --- a/packages/graphiql-react/src/execution.tsx +++ b/packages/graphiql-react/src/execution.tsx @@ -13,7 +13,7 @@ import { print, } from 'graphql'; import { getFragmentDependenciesForAST } from 'graphql-language-service'; -import { ReactNode, useCallback, useMemo, useRef, useState } from 'react'; +import { ReactNode, useRef, useState } from 'react'; import setValue from 'set-value'; import getValue from 'get-value'; @@ -103,13 +103,13 @@ export function ExecutionContextProvider({ const [subscription, setSubscription] = useState(null); const queryIdRef = useRef(0); - const stop = useCallback(() => { + const stop = () => { subscription?.unsubscribe(); setIsFetching(false); setSubscription(null); - }, [subscription]); + }; - const run = useCallback(async () => { + const run: ExecutionContextType['run'] = async () => { if (!queryEditor || !responseEditor) { return; } @@ -186,7 +186,8 @@ export function ExecutionContextProvider({ headers: headersString, operationName: opName, }); - + const _headers = headers ?? undefined; + const documentAST = queryEditor.documentAST ?? undefined; try { const fullResponse: ExecutionResult = {}; const handleResponse = (result: ExecutionResult) => { @@ -227,8 +228,8 @@ export function ExecutionContextProvider({ operationName: opName, }, { - headers: headers ?? undefined, - documentAST: queryEditor.documentAST ?? undefined, + headers: _headers, + documentAST, }, ); @@ -259,9 +260,7 @@ export function ExecutionContextProvider({ setSubscription({ unsubscribe: () => value[Symbol.asyncIterator]().return?.(), }); - for await (const result of value) { - handleResponse(result); - } + await handleAsyncResults(handleResponse, value); setIsFetching(false); setSubscription(null); } else { @@ -272,32 +271,16 @@ export function ExecutionContextProvider({ setResponse(formatError(error)); setSubscription(null); } - }, [ - autoCompleteLeafs, - externalFragments, - fetcher, - headerEditor, - history, - operationName, - queryEditor, - responseEditor, - stop, - subscription, - updateActiveTabValues, - variableEditor, - ]); + }; const isSubscribed = Boolean(subscription); - const value = useMemo( - () => ({ - isFetching, - isSubscribed, - operationName: operationName ?? null, - run, - stop, - }), - [isFetching, isSubscribed, operationName, run, stop], - ); + const value: ExecutionContextType = { + isFetching, + isSubscribed, + operationName: operationName ?? null, + run, + stop, + }; return ( @@ -306,6 +289,16 @@ export function ExecutionContextProvider({ ); } +// Extract function because react-compiler doesn't support `for await` yet +async function handleAsyncResults( + onResponse: (result: ExecutionResult) => void, + value: any, +): Promise { + for await (const result of value) { + onResponse(result); + } +} + export const useExecutionContext = createContextHook(ExecutionContext); function tryParseJsonObject({ @@ -403,8 +396,8 @@ function mergeIncrementalResult( for (const item of items) { setValue(executionResult, path.join('.'), item); // Increment the last path segment (the array index) to merge the next item at the next index - // eslint-disable-next-line unicorn/prefer-at -- cannot mutate the array using Array.at() - (path[path.length - 1] as number)++; + // @ts-expect-error -- (path[path.length - 1] as number)++ breaks react compiler + path[path.length - 1]++; } } } diff --git a/packages/graphiql-react/src/explorer/components/field-documentation.tsx b/packages/graphiql-react/src/explorer/components/field-documentation.tsx index 3ed044be5c4..105a0ee0786 100644 --- a/packages/graphiql-react/src/explorer/components/field-documentation.tsx +++ b/packages/graphiql-react/src/explorer/components/field-documentation.tsx @@ -1,5 +1,5 @@ import { GraphQLArgument } from 'graphql'; -import { useCallback, useState } from 'react'; +import { useState } from 'react'; import { Button, MarkdownContent } from '../../ui'; import { ExplorerFieldDef } from '../context'; @@ -38,9 +38,9 @@ export function FieldDocumentation(props: FieldDocumentationProps) { function Arguments({ field }: { field: ExplorerFieldDef }) { const [showDeprecated, setShowDeprecated] = useState(false); - const handleShowDeprecated = useCallback(() => { + const handleShowDeprecated = () => { setShowDeprecated(true); - }, []); + }; if (!('args' in field)) { return null; diff --git a/packages/graphiql-react/src/explorer/components/search.tsx b/packages/graphiql-react/src/explorer/components/search.tsx index e090f513258..089e22bf85e 100644 --- a/packages/graphiql-react/src/explorer/components/search.tsx +++ b/packages/graphiql-react/src/explorer/components/search.tsx @@ -7,14 +7,7 @@ import { isInterfaceType, isObjectType, } from 'graphql'; -import { - FocusEventHandler, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { FocusEventHandler, useEffect, useRef, useState } from 'react'; import { Combobox } from '@headlessui/react'; import { MagnifyingGlassIcon } from '../../icons'; import { useSchemaContext } from '../../schema'; @@ -36,13 +29,10 @@ export function Search() { const getSearchResults = useSearchResults(); const [searchValue, setSearchValue] = useState(''); const [results, setResults] = useState(getSearchResults(searchValue)); - const debouncedGetSearchResults = useMemo( - () => - debounce(200, (search: string) => { - setResults(getSearchResults(search)); - }), - [getSearchResults], - ); + const [isFocused, setIsFocused] = useState(false); + const debouncedGetSearchResults = debounce(200, (search: string) => { + setResults(getSearchResults(search)); + }); useEffect(() => { debouncedGetSearchResults(searchValue); }, [debouncedGetSearchResults, searchValue]); @@ -60,20 +50,16 @@ export function Search() { const navItem = explorerNavStack.at(-1)!; - const onSelect = useCallback( - (def: TypeMatch | FieldMatch) => { - push( - 'field' in def - ? { name: def.field.name, def: def.field } - : { name: def.type.name, def: def.type }, - ); - }, - [push], - ); - const isFocused = useRef(false); - const handleFocus: FocusEventHandler = useCallback(e => { - isFocused.current = e.type === 'focus'; - }, []); + const onSelect = (def: TypeMatch | FieldMatch) => { + push( + 'field' in def + ? { name: def.field.name, def: def.field } + : { name: def.type.name, def: def.type }, + ); + }; + const handleFocus: FocusEventHandler = e => { + setIsFocused(e.type === 'focus'); + }; const shouldSearchBoxAppear = explorerNavStack.length === 1 || @@ -112,7 +98,7 @@ export function Search() { {/* display on focus */} - {isFocused.current && ( + {isFocused && ( {results.within.length + results.types.length + @@ -171,97 +157,95 @@ type FieldMatch = { argument?: GraphQLArgument; }; +// To make react-compiler happy, otherwise complains about - Hooks may not be referenced as normal values +const _useSearchResults = useSearchResults; + export function useSearchResults(caller?: Function) { const { explorerNavStack } = useExplorerContext({ nonNull: true, - caller: caller || useSearchResults, + caller: caller || _useSearchResults, }); const { schema } = useSchemaContext({ nonNull: true, - caller: caller || useSearchResults, + caller: caller || _useSearchResults, }); const navItem = explorerNavStack.at(-1)!; - return useCallback( - (searchValue: string) => { - const matches: { - within: FieldMatch[]; - types: TypeMatch[]; - fields: FieldMatch[]; - } = { - within: [], - types: [], - fields: [], - }; - - if (!schema) { - return matches; - } + return (searchValue: string) => { + const matches: { + within: FieldMatch[]; + types: TypeMatch[]; + fields: FieldMatch[]; + } = { + within: [], + types: [], + fields: [], + }; + + if (!schema) { + return matches; + } + + const withinType = navItem.def; - const withinType = navItem.def; + const typeMap = schema.getTypeMap(); + let typeNames = Object.keys(typeMap); - const typeMap = schema.getTypeMap(); - let typeNames = Object.keys(typeMap); + // Move the within type name to be the first searched. + if (withinType) { + typeNames = typeNames.filter(n => n !== withinType.name); + typeNames.unshift(withinType.name); + } + for (const typeName of typeNames) { + if ( + matches.within.length + matches.types.length + matches.fields.length >= + 100 + ) { + break; + } - // Move the within type name to be the first searched. - if (withinType) { - typeNames = typeNames.filter(n => n !== withinType.name); - typeNames.unshift(withinType.name); + const type = typeMap[typeName]; + if (withinType !== type && isMatch(typeName, searchValue)) { + matches.types.push({ type }); } - for (const typeName of typeNames) { - if ( - matches.within.length + - matches.types.length + - matches.fields.length >= - 100 - ) { - break; - } - const type = typeMap[typeName]; - if (withinType !== type && isMatch(typeName, searchValue)) { - matches.types.push({ type }); - } + if ( + !isObjectType(type) && + !isInterfaceType(type) && + !isInputObjectType(type) + ) { + continue; + } - if ( - !isObjectType(type) && - !isInterfaceType(type) && - !isInputObjectType(type) - ) { - continue; - } + const fields = type.getFields(); + for (const fieldName in fields) { + const field = fields[fieldName]; + let matchingArgs: GraphQLArgument[] | undefined; - const fields = type.getFields(); - for (const fieldName in fields) { - const field = fields[fieldName]; - let matchingArgs: GraphQLArgument[] | undefined; - - if (!isMatch(fieldName, searchValue)) { - if ('args' in field) { - matchingArgs = field.args.filter(arg => - isMatch(arg.name, searchValue), - ); - if (matchingArgs.length === 0) { - continue; - } - } else { + if (!isMatch(fieldName, searchValue)) { + if ('args' in field) { + matchingArgs = field.args.filter(arg => + isMatch(arg.name, searchValue), + ); + if (matchingArgs.length === 0) { continue; } + } else { + continue; } - - matches[withinType === type ? 'within' : 'fields'].push( - ...(matchingArgs - ? matchingArgs.map(argument => ({ type, field, argument })) - : [{ type, field }]), - ); } + + matches[withinType === type ? 'within' : 'fields'].push( + ...(matchingArgs + ? matchingArgs.map(argument => ({ type, field, argument })) + : [{ type, field }]), + ); } + } - return matches; - }, - [navItem.def, schema], - ); + return matches; + }; } function isMatch(sourceText: string, searchValue: string): boolean { diff --git a/packages/graphiql-react/src/explorer/components/type-documentation.tsx b/packages/graphiql-react/src/explorer/components/type-documentation.tsx index 3e5bccbea62..9aad254f0f5 100644 --- a/packages/graphiql-react/src/explorer/components/type-documentation.tsx +++ b/packages/graphiql-react/src/explorer/components/type-documentation.tsx @@ -8,7 +8,7 @@ import { isNamedType, isObjectType, } from 'graphql'; -import { useCallback, useState } from 'react'; +import { useState } from 'react'; import { useSchemaContext } from '../../schema'; import { Button, MarkdownContent } from '../../ui'; @@ -63,9 +63,9 @@ function ImplementsInterfaces({ type }: { type: GraphQLNamedType }) { function Fields({ type }: { type: GraphQLNamedType }) { const [showDeprecated, setShowDeprecated] = useState(false); - const handleShowDeprecated = useCallback(() => { + const handleShowDeprecated = () => { setShowDeprecated(true); - }, []); + }; if ( !isObjectType(type) && @@ -157,9 +157,9 @@ function Field({ field }: { field: ExplorerFieldDef }) { function EnumValues({ type }: { type: GraphQLNamedType }) { const [showDeprecated, setShowDeprecated] = useState(false); - const handleShowDeprecated = useCallback(() => { + const handleShowDeprecated = () => { setShowDeprecated(true); - }, []); + }; if (!isEnumType(type)) { return null; diff --git a/packages/graphiql-react/src/explorer/components/utils.tsx b/packages/graphiql-react/src/explorer/components/utils.tsx index 6bef3892d7e..44de0d8109f 100644 --- a/packages/graphiql-react/src/explorer/components/utils.tsx +++ b/packages/graphiql-react/src/explorer/components/utils.tsx @@ -1,3 +1,5 @@ +'use no memo'; + import { GraphQLNamedType, GraphQLType, diff --git a/packages/graphiql-react/src/explorer/context.tsx b/packages/graphiql-react/src/explorer/context.tsx index cf545334306..110edcb17e5 100644 --- a/packages/graphiql-react/src/explorer/context.tsx +++ b/packages/graphiql-react/src/explorer/context.tsx @@ -13,7 +13,7 @@ import { isScalarType, isUnionType, } from 'graphql'; -import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'; +import { ReactNode, useEffect, useState } from 'react'; import { useSchemaContext } from '../schema'; import { createContextHook, createNullableContext } from '../utility/context'; @@ -81,7 +81,7 @@ export function ExplorerContextProvider(props: ExplorerContextProviderProps) { initialNavStackItem, ]); - const push = useCallback((item: ExplorerNavStackItem) => { + const push = (item: ExplorerNavStackItem) => { setNavStack(currentState => { const lastItem = currentState.at(-1)!; return lastItem.def === item.def @@ -89,21 +89,21 @@ export function ExplorerContextProvider(props: ExplorerContextProviderProps) { currentState : [...currentState, item]; }); - }, []); + }; - const pop = useCallback(() => { + const pop = () => { setNavStack(currentState => currentState.length > 1 ? (currentState.slice(0, -1) as ExplorerNavStack) : currentState, ); - }, []); + }; - const reset = useCallback(() => { + const reset = () => { setNavStack(currentState => currentState.length === 1 ? currentState : [initialNavStackItem], ); - }, []); + }; useEffect(() => { // Whenever the schema changes, we must revalidate/replace the nav stack. @@ -167,7 +167,7 @@ export function ExplorerContextProvider(props: ExplorerContextProviderProps) { break; } else { // lastEntity must be a field (because it's not a named type) - const field: GraphQLField = lastEntity; + const field: GraphQLField = lastEntity; // Thus item.def must be an argument, so find the same named argument in the new schema const arg = field.args.find(a => a.name === item.name); if (arg) { @@ -188,12 +188,14 @@ export function ExplorerContextProvider(props: ExplorerContextProviderProps) { return newNavStack; }); } - }, [reset, schema, validationErrors]); - - const value = useMemo( - () => ({ explorerNavStack: navStack, push, pop, reset }), - [navStack, push, pop, reset], - ); + }, [schema, validationErrors]); + + const value: ExplorerContextType = { + explorerNavStack: navStack, + push, + pop, + reset, + }; return ( diff --git a/packages/graphiql-react/src/explorer/index.ts b/packages/graphiql-react/src/explorer/index.ts index 0991d26a7b8..0a0279af82f 100644 --- a/packages/graphiql-react/src/explorer/index.ts +++ b/packages/graphiql-react/src/explorer/index.ts @@ -1,3 +1,5 @@ +'use no memo'; + export { Argument } from './components/argument'; export { DefaultValue } from './components/default-value'; export { DeprecationReason } from './components/deprecation-reason'; diff --git a/packages/graphiql-react/src/history/components.tsx b/packages/graphiql-react/src/history/components.tsx index 9ee49574be3..acf4dbbf6c2 100644 --- a/packages/graphiql-react/src/history/components.tsx +++ b/packages/graphiql-react/src/history/components.tsx @@ -1,11 +1,5 @@ import type { QueryStoreItem } from '@graphiql/toolkit'; -import { - MouseEventHandler, - useCallback, - useEffect, - useRef, - useState, -} from 'react'; +import { MouseEventHandler, useEffect, useRef, useState } from 'react'; import { clsx } from 'clsx'; import { useEditorContext } from '../editor'; @@ -49,7 +43,7 @@ export function History() { } }, [clearStatus]); - const handleClearStatus = useCallback(() => { + const handleClearStatus = () => { try { for (const item of items) { deleteFromHistory(item, true); @@ -58,7 +52,7 @@ export function History() { } catch { setClearStatus('error'); } - }, [deleteFromHistory, items]); + }; return (
@@ -131,50 +125,40 @@ export function HistoryItem(props: QueryHistoryItemProps) { props.item.operationName || formatQuery(props.item.query); - const handleSave = useCallback(() => { + const handleSave = () => { setIsEditable(false); const { index, ...item } = props.item; editLabel({ ...item, label: inputRef.current?.value }, index); - }, [editLabel, props.item]); + }; - const handleClose = useCallback(() => { + const handleClose = () => { setIsEditable(false); - }, []); - - const handleEditLabel: MouseEventHandler = useCallback( - e => { - e.stopPropagation(); - setIsEditable(true); - }, - [], - ); - - const handleHistoryItemClick: MouseEventHandler = - useCallback(() => { - const { query, variables, headers } = props.item; - queryEditor?.setValue(query ?? ''); - variableEditor?.setValue(variables ?? ''); - headerEditor?.setValue(headers ?? ''); - setActive(props.item); - }, [headerEditor, props.item, queryEditor, setActive, variableEditor]); - - const handleDeleteItemFromHistory: MouseEventHandler = - useCallback( - e => { - e.stopPropagation(); - deleteFromHistory(props.item); - }, - [props.item, deleteFromHistory], - ); - - const handleToggleFavorite: MouseEventHandler = - useCallback( - e => { - e.stopPropagation(); - toggleFavorite(props.item); - }, - [props.item, toggleFavorite], - ); + }; + + const handleEditLabel: MouseEventHandler = e => { + e.stopPropagation(); + setIsEditable(true); + }; + + const handleHistoryItemClick: MouseEventHandler = () => { + const { query, variables, headers } = props.item; + queryEditor?.setValue(query ?? ''); + variableEditor?.setValue(variables ?? ''); + headerEditor?.setValue(headers ?? ''); + setActive(props.item); + }; + + const handleDeleteItemFromHistory: MouseEventHandler< + HTMLButtonElement + > = e => { + e.stopPropagation(); + deleteFromHistory(props.item); + }; + + const handleToggleFavorite: MouseEventHandler = e => { + e.stopPropagation(); + toggleFavorite(props.item); + }; return (
  • diff --git a/packages/graphiql-react/src/history/context.tsx b/packages/graphiql-react/src/history/context.tsx index 6d3068595e8..66f6508fe80 100644 --- a/packages/graphiql-react/src/history/context.tsx +++ b/packages/graphiql-react/src/history/context.tsx @@ -1,5 +1,5 @@ import { HistoryStore, QueryStoreItem, StorageAPI } from '@graphiql/toolkit'; -import { ReactNode, useMemo, useState } from 'react'; +import { ReactNode, useState } from 'react'; import { useStorageContext } from '../storage'; import { createContextHook, createNullableContext } from '../utility/context'; @@ -99,29 +99,26 @@ export function HistoryContextProvider({ ); const [items, setItems] = useState(() => historyStore.queries || []); - const value = useMemo( - () => ({ - addToHistory(operation) { - historyStore.updateHistory(operation); - setItems(historyStore.queries); - }, - editLabel(operation, index) { - historyStore.editLabel(operation, index); - setItems(historyStore.queries); - }, - items, - toggleFavorite(operation) { - historyStore.toggleFavorite(operation); - setItems(historyStore.queries); - }, - setActive: item => item, - deleteFromHistory(item, clearFavorites) { - historyStore.deleteHistory(item, clearFavorites); - setItems(historyStore.queries); - }, - }), - [items, historyStore], - ); + const value: HistoryContextType = { + addToHistory(operation) { + historyStore.updateHistory(operation); + setItems(historyStore.queries); + }, + editLabel(operation, index) { + historyStore.editLabel(operation, index); + setItems(historyStore.queries); + }, + items, + toggleFavorite(operation) { + historyStore.toggleFavorite(operation); + setItems(historyStore.queries); + }, + setActive: item => item, + deleteFromHistory(item, clearFavorites) { + historyStore.deleteHistory(item, clearFavorites); + setItems(historyStore.queries); + }, + }; return ( {children} diff --git a/packages/graphiql-react/src/history/index.ts b/packages/graphiql-react/src/history/index.ts index e032d22d9a0..4524257dbc2 100644 --- a/packages/graphiql-react/src/history/index.ts +++ b/packages/graphiql-react/src/history/index.ts @@ -1,3 +1,5 @@ +'use no memo'; + export { History } from './components'; export { HistoryContext, diff --git a/packages/graphiql-react/src/index.ts b/packages/graphiql-react/src/index.ts index bf4b99d71d4..9c72085b2f8 100644 --- a/packages/graphiql-react/src/index.ts +++ b/packages/graphiql-react/src/index.ts @@ -1,3 +1,5 @@ +'use no memo'; + import './style/root.css'; export { diff --git a/packages/graphiql-react/src/markdown.ts b/packages/graphiql-react/src/markdown.ts index 9386e370973..e8d24628cff 100644 --- a/packages/graphiql-react/src/markdown.ts +++ b/packages/graphiql-react/src/markdown.ts @@ -1,3 +1,5 @@ +'use no memo'; + import MarkdownIt from 'markdown-it'; export const markdown = new MarkdownIt({ diff --git a/packages/graphiql-react/src/plugin.tsx b/packages/graphiql-react/src/plugin.tsx index 27194b13f97..8354805a16e 100644 --- a/packages/graphiql-react/src/plugin.tsx +++ b/packages/graphiql-react/src/plugin.tsx @@ -1,11 +1,4 @@ -import { - ComponentType, - ReactNode, - useCallback, - useEffect, - useMemo, - useState, -} from 'react'; +import { ComponentType, ReactNode, useEffect, useState } from 'react'; import { DocExplorer, useExplorerContext } from './explorer'; import { History, useHistoryContext } from './history'; import { DocsFilledIcon, DocsIcon, HistoryIcon } from './icons'; @@ -99,7 +92,7 @@ export function PluginContextProvider(props: PluginContextProviderProps) { const hasExplorerContext = Boolean(explorerContext); const hasHistoryContext = Boolean(historyContext); - const plugins = useMemo(() => { + const plugins = (() => { const pluginList: GraphiQLPlugin[] = []; const pluginTitles: Record = {}; @@ -127,7 +120,7 @@ export function PluginContextProvider(props: PluginContextProviderProps) { } return pluginList; - }, [hasExplorerContext, hasHistoryContext, props.plugins]); + })(); const [visiblePlugin, internalSetVisiblePlugin] = useState(() => { @@ -157,7 +150,7 @@ export function PluginContextProvider(props: PluginContextProviderProps) { }); const { onTogglePluginVisibility, children } = props; - const setVisiblePlugin = useCallback( + const setVisiblePlugin: PluginContextType['setVisiblePlugin'] = // eslint-disable-line react-hooks/exhaustive-deps -- false positive, function is optimized by react-compiler, no need to wrap with useCallback plugin => { const newVisiblePlugin = plugin ? plugins.find( @@ -171,9 +164,7 @@ export function PluginContextProvider(props: PluginContextProviderProps) { onTogglePluginVisibility?.(newVisiblePlugin); return newVisiblePlugin; }); - }, - [onTogglePluginVisibility, plugins], - ); + }; useEffect(() => { if (props.visiblePlugin) { @@ -181,10 +172,7 @@ export function PluginContextProvider(props: PluginContextProviderProps) { } }, [plugins, props.visiblePlugin, setVisiblePlugin]); - const value = useMemo( - () => ({ plugins, setVisiblePlugin, visiblePlugin }), - [plugins, setVisiblePlugin, visiblePlugin], - ); + const value = { plugins, setVisiblePlugin, visiblePlugin }; return ( {children} diff --git a/packages/graphiql-react/src/schema.tsx b/packages/graphiql-react/src/schema.tsx index 6284fc95ddc..20346f3e903 100644 --- a/packages/graphiql-react/src/schema.tsx +++ b/packages/graphiql-react/src/schema.tsx @@ -15,14 +15,7 @@ import { isSchema, validateSchema, } from 'graphql'; -import { - ReactNode, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { ReactNode, useEffect, useRef, useState } from 'react'; import { useEditorContext } from './editor'; import { createContextHook, createNullableContext } from './utility/context'; @@ -113,8 +106,12 @@ export type SchemaContextProviderProps = { schema?: GraphQLSchema | IntrospectionQuery | null; } & IntrospectionArgs; -export function SchemaContextProvider(props: SchemaContextProviderProps) { - if (!props.fetcher) { +export function SchemaContextProvider({ + fetcher, + onSchemaChange, + ...props +}: SchemaContextProviderProps) { + if (!fetcher) { throw new TypeError( 'The `SchemaContextProvider` component requires a `fetcher` function to be passed as prop.', ); @@ -139,11 +136,7 @@ export function SchemaContextProvider(props: SchemaContextProviderProps) { */ useEffect(() => { setSchema( - isSchema(props.schema) || - props.schema === null || - props.schema === undefined - ? props.schema - : undefined, + isSchema(props.schema) || props.schema == null ? props.schema : undefined, ); /** @@ -179,132 +172,127 @@ export function SchemaContextProvider(props: SchemaContextProviderProps) { /** * Fetch the schema */ - const { fetcher, onSchemaChange, dangerouslyAssumeSchemaIsValid, children } = - props; - const introspect = useCallback(() => { - /** - * Only introspect if there is no schema provided via props. If the - * prop is passed an introspection result, we do continue but skip the - * introspection request. - */ - if (isSchema(props.schema) || props.schema === null) { - return; - } - - const counter = ++counterRef.current; - - const maybeIntrospectionData = props.schema; - - async function fetchIntrospectionData() { - if (maybeIntrospectionData) { - // No need to introspect if we already have the data - return maybeIntrospectionData; - } - - const parsedHeaders = parseHeaderString(headersRef.current); - if (!parsedHeaders.isValidJSON) { - setFetchError('Introspection failed as headers are invalid.'); + const introspect = // eslint-disable-line react-hooks/exhaustive-deps -- false positive, function is optimized by react-compiler no need to wrap with useCallback + () => { + /** + * Only introspect if there is no schema provided via props. If the + * prop is passed an introspection result, we do continue but skip the + * introspection request. + */ + if (isSchema(props.schema) || props.schema === null) { return; } - const fetcherOpts: FetcherOpts = parsedHeaders.headers - ? { headers: parsedHeaders.headers } - : {}; - - const fetch = fetcherReturnToPromise( - fetcher( - { - query: introspectionQuery, - operationName: introspectionQueryName, - }, - fetcherOpts, - ), - ); - - if (!isPromise(fetch)) { - setFetchError('Fetcher did not return a Promise for introspection.'); - return; - } + const counter = ++counterRef.current; - setIsFetching(true); - setFetchError(null); + const maybeIntrospectionData = props.schema; - let result = await fetch; + async function fetchIntrospectionData() { + if (maybeIntrospectionData) { + // No need to introspect if we already have the data + return maybeIntrospectionData; + } - if ( - typeof result !== 'object' || - result === null || - !('data' in result) - ) { - // Try the stock introspection query first, falling back on the - // sans-subscriptions query for services which do not yet support it. - const fetch2 = fetcherReturnToPromise( + const parsedHeaders = parseHeaderString(headersRef.current); + if (!parsedHeaders.isValidJSON) { + setFetchError('Introspection failed as headers are invalid.'); + return; + } + + const fetcherOpts: FetcherOpts = parsedHeaders.headers + ? { headers: parsedHeaders.headers } + : {}; + + const fetch = fetcherReturnToPromise( fetcher( { - query: introspectionQuerySansSubscriptions, + query: introspectionQuery, operationName: introspectionQueryName, }, fetcherOpts, ), ); - if (!isPromise(fetch2)) { - throw new Error( - 'Fetcher did not return a Promise for introspection.', + + if (!isPromise(fetch)) { + setFetchError('Fetcher did not return a Promise for introspection.'); + return; + } + + setIsFetching(true); + setFetchError(null); + + let result = await fetch; + + if ( + typeof result !== 'object' || + result === null || + !('data' in result) + ) { + // Try the stock introspection query first, falling back on the + // sans-subscriptions query for services which do not yet support it. + const fetch2 = fetcherReturnToPromise( + fetcher( + { + query: introspectionQuerySansSubscriptions, + operationName: introspectionQueryName, + }, + fetcherOpts, + ), ); + if (!isPromise(fetch2)) { + throw new Error( + 'Fetcher did not return a Promise for introspection.', + ); + } + result = await fetch2; } - result = await fetch2; - } - setIsFetching(false); + setIsFetching(false); - if (result?.data && '__schema' in result.data) { - return result.data as IntrospectionQuery; - } + if (result?.data && '__schema' in result.data) { + return result.data as IntrospectionQuery; + } - // handle as if it were an error if the fetcher response is not a string or response.data is not present - const responseString = - typeof result === 'string' ? result : formatResult(result); - setFetchError(responseString); - } + // handle as if it were an error if the fetcher response is not a string or response.data is not present + const responseString = + typeof result === 'string' ? result : formatResult(result); + setFetchError(responseString); + } - fetchIntrospectionData() - .then(introspectionData => { - /** - * Don't continue if another introspection request has been started in - * the meantime or if there is no introspection data. - */ - if (counter !== counterRef.current || !introspectionData) { - return; - } + fetchIntrospectionData() + .then(introspectionData => { + /** + * Don't continue if another introspection request has been started in + * the meantime or if there is no introspection data. + */ + if (counter !== counterRef.current || !introspectionData) { + return; + } + + try { + const newSchema = buildClientSchema(introspectionData); + setSchema(newSchema); + // Optional chaining inside try-catch isn't supported yet by react-compiler + if (onSchemaChange) { + onSchemaChange(newSchema); + } + } catch (error) { + setFetchError(formatError(error)); + } + }) + .catch(error => { + /** + * Don't continue if another introspection request has been started in + * the meantime. + */ + if (counter !== counterRef.current) { + return; + } - try { - const newSchema = buildClientSchema(introspectionData); - setSchema(newSchema); - onSchemaChange?.(newSchema); - } catch (error) { setFetchError(formatError(error)); - } - }) - .catch(error => { - /** - * Don't continue if another introspection request has been started in - * the meantime. - */ - if (counter !== counterRef.current) { - return; - } - - setFetchError(formatError(error)); - setIsFetching(false); - }); - }, [ - fetcher, - introspectionQueryName, - introspectionQuery, - introspectionQuerySansSubscriptions, - onSchemaChange, - props.schema, - ]); + setIsFetching(false); + }); + }; /** * Trigger introspection automatically @@ -324,35 +312,34 @@ export function SchemaContextProvider(props: SchemaContextProviderProps) { } window.addEventListener('keydown', triggerIntrospection); - return () => window.removeEventListener('keydown', triggerIntrospection); + return () => { + window.removeEventListener('keydown', triggerIntrospection); + }; }); /** * Derive validation errors from the schema */ - const validationErrors = useMemo(() => { - if (!schema || dangerouslyAssumeSchemaIsValid) { - return []; - } - return validateSchema(schema); - }, [schema, dangerouslyAssumeSchemaIsValid]); + const validationErrors = + !schema || props.dangerouslyAssumeSchemaIsValid + ? [] + : validateSchema(schema); /** * Memoize context value */ - const value = useMemo( - () => ({ - fetchError, - introspect, - isFetching, - schema, - validationErrors, - }), - [fetchError, introspect, isFetching, schema, validationErrors], - ); + const value = { + fetchError, + introspect, + isFetching, + schema, + validationErrors, + }; return ( - {children} + + {props.children} + ); } @@ -384,31 +371,26 @@ function useIntrospectionQuery({ introspectionQueryName, schemaDescription, }: IntrospectionArgs) { - return useMemo(() => { - const queryName = introspectionQueryName || 'IntrospectionQuery'; - - let query = getIntrospectionQuery({ - inputValueDeprecation, - schemaDescription, - }); - if (introspectionQueryName) { - query = query.replace('query IntrospectionQuery', `query ${queryName}`); - } + const queryName = introspectionQueryName || 'IntrospectionQuery'; - const querySansSubscriptions = query.replace( - 'subscriptionType { name }', - '', - ); + let query = getIntrospectionQuery({ + inputValueDeprecation, + schemaDescription, + }); + if (introspectionQueryName) { + query = query.replace('query IntrospectionQuery', `query ${queryName}`); + } - return { - introspectionQueryName: queryName, - introspectionQuery: query, - introspectionQuerySansSubscriptions: querySansSubscriptions, - }; - }, [inputValueDeprecation, introspectionQueryName, schemaDescription]); + const querySansSubscriptions = query.replace('subscriptionType { name }', ''); + + return { + introspectionQueryName: queryName, + introspectionQuery: query, + introspectionQuerySansSubscriptions: querySansSubscriptions, + }; } -function parseHeaderString(headersString: string | undefined) { +function parseHeaderString(headersString?: string) { let headers: Record | null = null; let isValidJSON = true; diff --git a/packages/graphiql-react/src/theme.ts b/packages/graphiql-react/src/theme.ts index a8bd0b7126c..b9b4e24b7c2 100644 --- a/packages/graphiql-react/src/theme.ts +++ b/packages/graphiql-react/src/theme.ts @@ -1,4 +1,4 @@ -import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { useLayoutEffect, useState } from 'react'; import { useStorageContext } from './storage'; /** @@ -41,15 +41,12 @@ export function useTheme(defaultTheme: Theme = null) { } }, [theme]); - const setTheme = useCallback( - (newTheme: Theme) => { - storageContext?.set(STORAGE_KEY, newTheme || ''); - setThemeInternal(newTheme); - }, - [storageContext], - ); + const setTheme = (newTheme: Theme) => { + storageContext?.set(STORAGE_KEY, newTheme || ''); + setThemeInternal(newTheme); + }; - return useMemo(() => ({ theme, setTheme }), [theme, setTheme]); + return { theme, setTheme }; } const STORAGE_KEY = 'theme'; diff --git a/packages/graphiql-react/src/toolbar/button.tsx b/packages/graphiql-react/src/toolbar/button.tsx index 5c132264187..9bc872408f5 100644 --- a/packages/graphiql-react/src/toolbar/button.tsx +++ b/packages/graphiql-react/src/toolbar/button.tsx @@ -1,4 +1,4 @@ -import { forwardRef, MouseEventHandler, useCallback, useState } from 'react'; +import { forwardRef, MouseEventHandler, useState } from 'react'; import { clsx } from 'clsx'; import { Tooltip, UnStyledButton } from '../ui'; @@ -13,21 +13,21 @@ export const ToolbarButton = forwardRef< ToolbarButtonProps & JSX.IntrinsicElements['button'] >(({ label, onClick, ...props }, ref) => { const [error, setError] = useState(null); - const handleClick: MouseEventHandler = useCallback( - event => { - try { - onClick?.(event); - setError(null); - } catch (err) { - setError( - err instanceof Error - ? err - : new Error(`Toolbar button click failed: ${err}`), - ); + const handleClick: MouseEventHandler = event => { + try { + // Optional chaining inside try-catch isn't supported yet by react-compiler + if (onClick) { + onClick(event); } - }, - [onClick], - ); + setError(null); + } catch (err) { + setError( + err instanceof Error + ? err + : new Error(`Toolbar button click failed: ${err}`), + ); + } + }; return ( diff --git a/packages/graphiql-react/src/toolbar/index.ts b/packages/graphiql-react/src/toolbar/index.ts index 9a180077030..e9c7642a64e 100644 --- a/packages/graphiql-react/src/toolbar/index.ts +++ b/packages/graphiql-react/src/toolbar/index.ts @@ -1,3 +1,5 @@ +'use no memo'; + export * from './button'; export * from './execute'; export * from './menu'; diff --git a/packages/graphiql-react/src/toolbar/menu.tsx b/packages/graphiql-react/src/toolbar/menu.tsx index fd5ab6f5075..1874679cbfc 100644 --- a/packages/graphiql-react/src/toolbar/menu.tsx +++ b/packages/graphiql-react/src/toolbar/menu.tsx @@ -19,22 +19,24 @@ const ToolbarMenuRoot = ({ }: ToolbarMenuProps & { children: ReactNode; className?: string; -} & DropdownMenuProps) => ( - - - - {button} - - - {children} - -); +} & DropdownMenuProps) => { + return ( + + + + {button} + + + {children} + + ); +}; export const ToolbarMenu = createComponentGroup(ToolbarMenuRoot, { Item: DropdownMenu.Item, diff --git a/packages/graphiql-react/src/ui/index.ts b/packages/graphiql-react/src/ui/index.ts index a0559b7103d..3cb27b27ae2 100644 --- a/packages/graphiql-react/src/ui/index.ts +++ b/packages/graphiql-react/src/ui/index.ts @@ -1,3 +1,5 @@ +'use no memo'; + export * from './button'; export * from './button-group'; export * from './dialog'; diff --git a/packages/graphiql-react/src/utility/component-group.ts b/packages/graphiql-react/src/utility/component-group.ts index d8cae861a00..71e67ac3c32 100644 --- a/packages/graphiql-react/src/utility/component-group.ts +++ b/packages/graphiql-react/src/utility/component-group.ts @@ -1,3 +1,5 @@ +'use no memo'; + import { JSXElementConstructor } from 'react'; export const createComponentGroup = < diff --git a/packages/graphiql-react/src/utility/context.ts b/packages/graphiql-react/src/utility/context.ts index e6a4d314ead..5d4403234fe 100644 --- a/packages/graphiql-react/src/utility/context.ts +++ b/packages/graphiql-react/src/utility/context.ts @@ -1,3 +1,5 @@ +'use no memo'; + import { Context, createContext, useContext } from 'react'; export function createNullableContext(name: string): Context { diff --git a/packages/graphiql-react/src/utility/debounce.ts b/packages/graphiql-react/src/utility/debounce.ts index 290cccbe3c8..c85f61fbeb6 100644 --- a/packages/graphiql-react/src/utility/debounce.ts +++ b/packages/graphiql-react/src/utility/debounce.ts @@ -1,3 +1,5 @@ +'use no memo'; + /** * Provided a duration and a function, returns a new function which is called * `duration` milliseconds after the last call. diff --git a/packages/graphiql-react/src/utility/is-macos.ts b/packages/graphiql-react/src/utility/is-macos.ts index a71ebf16978..79f4a44fad9 100644 --- a/packages/graphiql-react/src/utility/is-macos.ts +++ b/packages/graphiql-react/src/utility/is-macos.ts @@ -1,2 +1,4 @@ +'use no memo'; + export const isMacOs = typeof navigator !== 'undefined' && navigator.userAgent.includes('Mac'); diff --git a/packages/graphiql-react/src/utility/resize.ts b/packages/graphiql-react/src/utility/resize.ts index 38e53ffafbf..8d2aefd80f9 100644 --- a/packages/graphiql-react/src/utility/resize.ts +++ b/packages/graphiql-react/src/utility/resize.ts @@ -1,11 +1,4 @@ -import { - useCallback, - useEffect, - useLayoutEffect, - useMemo, - useRef, - useState, -} from 'react'; +import { useEffect, useLayoutEffect, useRef, useState } from 'react'; import { useStorageContext } from '../storage'; import debounce from './debounce'; @@ -60,15 +53,11 @@ export function useDragResize({ }: UseDragResizeArgs) { const storage = useStorageContext(); - const store = useMemo( - () => - debounce(500, (value: string) => { - if (storageKey) { - storage?.set(storageKey, value); - } - }), - [storage, storageKey], - ); + const store = debounce(500, (value: string) => { + if (storageKey) { + storage?.set(storageKey, value); + } + }); const [hiddenElement, setHiddenElement] = useState( () => { @@ -83,15 +72,13 @@ export function useDragResize({ }, ); - const setHiddenElementWithCallback = useCallback( + const setHiddenElementWithCallback = // eslint-disable-line react-hooks/exhaustive-deps -- false positive, function is optimized by react-compiler, no need to wrap with useCallback (element: ResizableElement | null) => { if (element !== hiddenElement) { setHiddenElement(element); onHiddenElementChange?.(element); } - }, - [hiddenElement, onHiddenElementChange], - ); + }; const firstRef = useRef(null); const dragBarRef = useRef(null); @@ -124,35 +111,38 @@ export function useDragResize({ } }, [direction, storage, storageKey]); - const hide = useCallback((resizableElement: ResizableElement) => { - const element = - resizableElement === 'first' ? firstRef.current : secondRef.current; - if (!element) { - return; - } + /** + * Hide and show items when state changes + */ + useLayoutEffect(() => { + const hide = (resizableElement: ResizableElement) => { + const element = + resizableElement === 'first' ? firstRef.current : secondRef.current; + if (!element) { + return; + } - // We hide elements off screen because of codemirror. If the page is loaded - // and the codemirror container would have zero width, the layout isn't - // instant pretty. By always giving the editor some width we avoid any - // layout shifts when the editor reappears. - element.style.left = '-1000px'; - element.style.position = 'absolute'; - element.style.opacity = '0'; - element.style.height = '500px'; - element.style.width = '500px'; - - // Make sure that the flex value of the first item is at least equal to one - // so that the entire space of the parent element is filled up - if (firstRef.current) { - const flex = parseFloat(firstRef.current.style.flex); - if (!Number.isFinite(flex) || flex < 1) { - firstRef.current.style.flex = '1'; + // We hide elements off screen because of codemirror. If the page is loaded + // and the codemirror container would have zero width, the layout isn't + // instant pretty. By always giving the editor some width we avoid any + // layout shifts when the editor reappears. + element.style.left = '-1000px'; + element.style.position = 'absolute'; + element.style.opacity = '0'; + element.style.height = '500px'; + element.style.width = '500px'; + + // Make sure that the flex value of the first item is at least equal to one + // so that the entire space of the parent element is filled up + if (firstRef.current) { + const flex = parseFloat(firstRef.current.style.flex); + if (!Number.isFinite(flex) || flex < 1) { + firstRef.current.style.flex = '1'; + } } - } - }, []); + }; - const show = useCallback( - (resizableElement: ResizableElement) => { + const show = (resizableElement: ResizableElement) => { const element = resizableElement === 'first' ? firstRef.current : secondRef.current; if (!element) { @@ -175,14 +165,7 @@ export function useDragResize({ firstRef.current.style.flex = storedValue || defaultFlexRef.current; } } - }, - [storage, storageKey], - ); - - /** - * Hide and show items when state changes - */ - useLayoutEffect(() => { + }; if (hiddenElement === 'first') { hide('first'); } else { @@ -193,7 +176,7 @@ export function useDragResize({ } else { show('second'); } - }, [hiddenElement, hide, show]); + }, [hiddenElement, storage, storageKey]); useEffect(() => { if (!dragBarRef.current || !firstRef.current || !secondRef.current) { @@ -285,16 +268,13 @@ export function useDragResize({ store, ]); - return useMemo( - () => ({ - dragBarRef, - hiddenElement, - firstRef, - setHiddenElement, - secondRef, - }), - [hiddenElement, setHiddenElement], - ); + return { + dragBarRef, + hiddenElement, + firstRef, + setHiddenElement, + secondRef, + }; } const DEFAULT_FLEX = 1; diff --git a/packages/graphiql-react/vite.config.mts b/packages/graphiql-react/vite.config.mts index d04a1d5f524..d31848d40da 100644 --- a/packages/graphiql-react/vite.config.mts +++ b/packages/graphiql-react/vite.config.mts @@ -4,9 +4,20 @@ import svgr from 'vite-plugin-svgr'; import postCssNestingPlugin from 'postcss-nesting'; import packageJSON from './package.json'; +const ReactCompilerConfig = { + target: '17', + sources(filename) { + return filename.includes('graphiql-react'); + }, +}; + export default defineConfig({ plugins: [ - react(), + react({ + babel: { + plugins: [['babel-plugin-react-compiler', ReactCompilerConfig]], + }, + }), svgr({ exportAsDefault: true, svgrOptions: { diff --git a/resources/patches/@vitejs+plugin-react+4.3.1.patch b/resources/patches/@vitejs+plugin-react+4.3.1.patch new file mode 100644 index 00000000000..a0b5712ee19 --- /dev/null +++ b/resources/patches/@vitejs+plugin-react+4.3.1.patch @@ -0,0 +1,24 @@ +diff --git a/node_modules/@vitejs/plugin-react/dist/index.mjs b/node_modules/@vitejs/plugin-react/dist/index.mjs +index e61f42b..a6ad925 100644 +--- a/node_modules/@vitejs/plugin-react/dist/index.mjs ++++ b/node_modules/@vitejs/plugin-react/dist/index.mjs +@@ -202,6 +202,19 @@ function viteReact(opts = {}) { + }); + if (result) { + let code2 = result.code; ++ if (filepath.includes('graphiql-react')) { ++ if ( ++ /^import \{ c as _c } from "/m.test(code2) ++ ) { ++ console.info('🚀 File', filepath, 'was optimized with react-compiler') ++ } else if (!/^'use no memo'/m.test(code2)) { ++ console.error('❌ File', filepath, 'was not optimized with react-compiler') ++ console.log(code2) ++ if (process.env.NODE_ENV === 'production') { ++ process.exit(1) ++ } ++ } ++ } + if (useFastRefresh) { + if (refreshContentRE.test(code2)) { + code2 = addRefreshWrapper(code2, id); diff --git a/yarn.lock b/yarn.lock index 36a4fd3fecf..bf97bcd4502 100644 --- a/yarn.lock +++ b/yarn.lock @@ -173,6 +173,15 @@ "@babel/highlight" "^7.24.7" picocolors "^1.0.0" +"@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/compat-data@^7.22.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9", "@babel/compat-data@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" @@ -183,6 +192,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.9.tgz#53eee4e68f1c1d0282aa0eb05ddb02d033fc43a0" integrity sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng== +"@babel/compat-data@^7.25.9": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.3.tgz#99488264a56b2aded63983abd6a417f03b92ed02" + integrity sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g== + "@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.21.0", "@babel/core@^7.21.3", "@babel/core@^7.22.9", "@babel/core@^7.7.2", "@babel/core@^7.8.0": version "7.23.7" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.7.tgz#4d8016e06a14b5f92530a13ed0561730b5c6483f" @@ -204,6 +218,27 @@ json5 "^2.2.3" semver "^6.3.1" +"@babel/core@^7.24.4": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" + integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.0" + "@babel/generator" "^7.26.0" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.0" + "@babel/parser" "^7.26.0" + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.26.0" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/core@^7.24.5": version "7.24.9" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.9.tgz#dc07c9d307162c97fa9484ea997ade65841c7c82" @@ -271,6 +306,17 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^2.5.1" +"@babel/generator@^7.26.0": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.3.tgz#ab8d4360544a425c90c248df7059881f4b2ce019" + integrity sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ== + dependencies: + "@babel/parser" "^7.26.3" + "@babel/types" "^7.26.3" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + "@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" @@ -314,6 +360,17 @@ lru-cache "^5.1.1" semver "^6.3.1" +"@babel/helper-compilation-targets@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875" + integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ== + dependencies: + "@babel/compat-data" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.22.5": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz#c36ea240bb3348f942f08b0fbe28d6d979fab236" @@ -419,6 +476,14 @@ "@babel/traverse" "^7.24.7" "@babel/types" "^7.24.7" +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.23.3": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" @@ -441,6 +506,15 @@ "@babel/helper-split-export-declaration" "^7.24.7" "@babel/helper-validator-identifier" "^7.24.7" +"@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" @@ -539,11 +613,21 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d" integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + "@babel/helper-validator-identifier@^7.22.20", "@babel/helper-validator-identifier@^7.22.5", "@babel/helper-validator-identifier@^7.24.5", "@babel/helper-validator-identifier@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + "@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0", "@babel/helper-validator-option@^7.22.5", "@babel/helper-validator-option@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" @@ -554,6 +638,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz#3725cdeea8b480e86d34df15304806a06975e33d" integrity sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q== +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== + "@babel/helper-wrap-function@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz#189937248c45b0182c1dcf32f3444ca153944cb9" @@ -580,6 +669,14 @@ "@babel/template" "^7.24.7" "@babel/types" "^7.24.8" +"@babel/helpers@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" + integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== + dependencies: + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.0" + "@babel/highlight@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" @@ -595,6 +692,13 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.5.tgz#4a4d5ab4315579e5398a82dcf636ca80c3392790" integrity sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg== +"@babel/parser@^7.24.4", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.3": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.3.tgz#8c51c5db6ddf08134af1ddbacf16aaab48bac234" + integrity sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA== + dependencies: + "@babel/types" "^7.26.3" + "@babel/parser@^7.24.7": version "7.25.3" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.3.tgz#91fb126768d944966263f0657ab222a642b82065" @@ -660,6 +764,14 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" +"@babel/plugin-proposal-private-methods@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": version "7.21.0-placeholder-for-preset-env.2" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" @@ -1454,7 +1566,16 @@ "@babel/parser" "^7.25.0" "@babel/types" "^7.25.0" -"@babel/traverse@^7.16.8", "@babel/traverse@^7.23.2", "@babel/traverse@^7.23.7", "@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@babel/traverse@^7.25.0", "@babel/traverse@^7.7.2": +"@babel/template@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" + integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== + dependencies: + "@babel/code-frame" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/traverse@^7.16.8", "@babel/traverse@^7.23.2", "@babel/traverse@^7.23.7", "@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.9", "@babel/traverse@^7.7.2": version "7.25.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.6.tgz#04fad980e444f182ecf1520504941940a90fea41" integrity sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ== @@ -1476,6 +1597,14 @@ "@babel/helper-validator-identifier" "^7.24.7" to-fast-properties "^2.0.0" +"@babel/types@^7.19.0", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" + integrity sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/types@^7.24.8", "@babel/types@^7.25.0", "@babel/types@^7.25.2": version "7.25.2" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.2.tgz#55fb231f7dc958cd69ea141a4c2997e819646125" @@ -2633,24 +2762,34 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.11.0", "@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.8.0": +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.8.0": version "4.11.0" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== -"@eslint/config-array@^0.17.0": - version "0.17.1" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.17.1.tgz#d9b8b8b6b946f47388f32bedfd3adf29ca8f8910" - integrity sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA== +"@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/config-array@^0.18.0": + version "0.18.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.18.0.tgz#37d8fe656e0d5e3dbaea7758ea56540867fd074d" + integrity sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw== dependencies: "@eslint/object-schema" "^2.1.4" debug "^4.3.1" minimatch "^3.1.2" +"@eslint/core@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.7.0.tgz#a1bb4b6a4e742a5ff1894b7ee76fbf884ec72bd3" + integrity sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw== + "@eslint/eslintrc@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" - integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== + version "3.2.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.2.0.tgz#57470ac4e2e283a6bf76044d63281196e370542c" + integrity sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -2662,15 +2801,22 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.7.0": - version "9.7.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.7.0.tgz#b712d802582f02b11cfdf83a85040a296afec3f0" - integrity sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng== +"@eslint/js@9.14.0": + version "9.14.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.14.0.tgz#2347a871042ebd11a00fd8c2d3d56a265ee6857e" + integrity sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg== "@eslint/object-schema@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" - integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== + version "2.1.5" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.5.tgz#8670a8f6258a2be5b2c620ff314a1d984c23eb2e" + integrity sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ== + +"@eslint/plugin-kit@^0.2.0": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz#2b78e7bb3755784bb13faa8932a1d994d6537792" + integrity sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg== + dependencies: + levn "^0.4.1" "@fastify/busboy@^2.0.0": version "2.1.1" @@ -2912,6 +3058,19 @@ dependencies: client-only "^0.0.1" +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" + "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" @@ -2922,6 +3081,11 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== +"@humanwhocodes/retry@^0.4.0": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" + integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== + "@iarna/toml@^2.2.5": version "2.2.5" resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" @@ -3432,7 +3596,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": +"@nodelib/fs.walk@^1.2.3": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -4354,6 +4518,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== +"@types/estree@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": version "4.17.33" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz#de35d30a9d637dc1450ad18dd583d75d5733d543" @@ -4462,7 +4631,7 @@ expect "^29.0.0" pretty-format "^29.0.0" -"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -5575,6 +5744,11 @@ acorn@^8.0.0, acorn@^8.0.4, acorn@^8.1.0, acorn@^8.11.3, acorn@^8.12.0, acorn@^8 resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c" integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== +acorn@^8.14.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -6218,6 +6392,13 @@ babel-plugin-polyfill-regenerator@^0.5.1: dependencies: "@babel/helper-define-polyfill-provider" "^0.4.2" +babel-plugin-react-compiler@19.0.0-beta-37ed2a7-20241206: + version "19.0.0-beta-37ed2a7-20241206" + resolved "https://registry.yarnpkg.com/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.0.0-beta-37ed2a7-20241206.tgz#06c72781834e8cb0793de815bdf99afb5c71d6cc" + integrity sha512-nnkrHpeDKM8A5laq9tmFvvGbbDQ7laGfQLp50cvCkCXmWrPcZdCtaQpNh8UJS/yLREJnv2R4JDL5ADfxyAn+yQ== + dependencies: + "@babel/types" "^7.19.0" + babel-plugin-transform-import-meta@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-import-meta/-/babel-plugin-transform-import-meta-2.2.1.tgz#eb5b79019ff0a9157b94d8280955121189a2964b" @@ -6504,6 +6685,16 @@ browserslist@^4.23.1: node-releases "^2.0.14" update-browserslist-db "^1.1.0" +browserslist@^4.24.0: + version "4.24.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" + integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== + dependencies: + caniuse-lite "^1.0.30001669" + electron-to-chromium "^1.5.41" + node-releases "^2.0.18" + update-browserslist-db "^1.1.1" + bs-logger@0.x: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" @@ -6671,6 +6862,11 @@ caniuse-lite@^1.0.30001640: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz#9c004caef315de9452ab970c3da71085f8241dbd" integrity sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg== +caniuse-lite@^1.0.30001669: + version "1.0.30001687" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz#d0ac634d043648498eedf7a3932836beba90ebae" + integrity sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ== + capital-case@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" @@ -7464,7 +7660,7 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -7473,6 +7669,15 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +cross-spawn@^7.0.2: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + crosspath@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/crosspath/-/crosspath-1.0.0.tgz#0892ebb51676eb57df56db8c7c46d3344555051b" @@ -8302,6 +8507,11 @@ electron-to-chromium@^1.4.820: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.1.tgz#24640bd4dcfaccb6d82bb4c3f4c7311503241581" integrity sha512-FKbOCOQ5QRB3VlIbl1LZQefWIYwszlBloaXcY2rbfpu9ioJnNh3TK03YtIDKDo3WKBi8u+YV4+Fn2CkEozgf4w== +electron-to-chromium@^1.5.41: + version "1.5.72" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.72.tgz#a732805986d3a5b5fedd438ddf4616c7d78ac2df" + integrity sha512-ZpSAUOZ2Izby7qnZluSrAlGgGQzucmFbN0n64dYzocYxnxV5ufurpj3VgEe4cUp7ir9LmeLxNYo8bVnlM8bQHw== + elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" @@ -8489,6 +8699,27 @@ es-iterator-helpers@^1.0.19: iterator.prototype "^1.1.2" safe-array-concat "^1.1.2" +es-iterator-helpers@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz#2f1a3ab998b30cb2d10b195b587c6d9ebdebf152" + integrity sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.3" + es-errors "^1.3.0" + es-set-tostringtag "^2.0.3" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + globalthis "^1.0.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + iterator.prototype "^1.1.3" + safe-array-concat "^1.1.2" + es-module-lexer@^0.9.0, es-module-lexer@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" @@ -8623,6 +8854,11 @@ escalade@^3.1.1, escalade@^3.1.2: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== +escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -8856,15 +9092,27 @@ eslint-plugin-promise@^7.0.0: resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-7.0.0.tgz#2d54cf6e92213cb2915be62abf338b64c089431d" integrity sha512-wb1ECT+b90ndBdAujhIdAU8oQ3Vt5gKqP/t78KOmg0ifynrvc2jGR9f6ndbOVNFpKf6jLUBlBBDF3H3Wk0JICg== +eslint-plugin-react-compiler@19.0.0-beta-37ed2a7-20241206: + version "19.0.0-beta-37ed2a7-20241206" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-compiler/-/eslint-plugin-react-compiler-19.0.0-beta-37ed2a7-20241206.tgz#789e4e07d0ebc77ecd23a9d79a6863e0d18c11e7" + integrity sha512-5Pex1fUCJwLwwqEJe6NkgTn45kUjjj9TZP6IrW4IcpWM/YaEe+QvcOeF60huDjBq0kz1svGeW2nw8WdY+qszAw== + dependencies: + "@babel/core" "^7.24.4" + "@babel/parser" "^7.24.4" + "@babel/plugin-proposal-private-methods" "^7.18.6" + hermes-parser "^0.25.1" + zod "^3.22.4" + zod-validation-error "^3.0.3" + eslint-plugin-react-hooks@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596" integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== -eslint-plugin-react-hooks@^5.1.0-rc-76002254-20240724: - version "5.1.0-rc-fb9a90fa48-20240614" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0-rc-fb9a90fa48-20240614.tgz#206a7ec005f0b286aaf7091f4e566083d310b189" - integrity sha512-xsiRwaDNF5wWNC4ZHLut+x/YcAxksUd9Rizt7LaEn3bV8VyYRpXnRJQlLOfYaVy9esk4DFP4zPPnoNVjq5Gc0w== +eslint-plugin-react-hooks@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz#3d34e37d5770866c34b87d5b499f5f0b53bf0854" + integrity sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw== eslint-plugin-react@^7.33.2, eslint-plugin-react@^7.34.1: version "7.34.3" @@ -8890,17 +9138,17 @@ eslint-plugin-react@^7.33.2, eslint-plugin-react@^7.34.1: semver "^6.3.1" string.prototype.matchall "^4.0.11" -eslint-plugin-react@^7.35.0: - version "7.35.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz#00b1e4559896710e58af6358898f2ff917ea4c41" - integrity sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA== +eslint-plugin-react@^7.37.2: + version "7.37.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz#cd0935987876ba2900df2f58339f6d92305acc7a" + integrity sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w== dependencies: array-includes "^3.1.8" array.prototype.findlast "^1.2.5" array.prototype.flatmap "^1.3.2" array.prototype.tosorted "^1.1.4" doctrine "^2.1.0" - es-iterator-helpers "^1.0.19" + es-iterator-helpers "^1.1.0" estraverse "^5.3.0" hasown "^2.0.2" jsx-ast-utils "^2.4.1 || ^3.0.0" @@ -8984,10 +9232,10 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.0.2.tgz#5cbb33d4384c9136083a71190d548158fe128f94" - integrity sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA== +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -9019,27 +9267,36 @@ eslint-visitor-keys@^4.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb" integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw== -eslint@^9.7.0: - version "9.7.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.7.0.tgz#bedb48e1cdc2362a0caaa106a4c6ed943e8b09e4" - integrity sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@9.14.0: + version "9.14.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.14.0.tgz#534180a97c00af08bcf2b60b0ebf0c4d6c1b2c95" + integrity sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.11.0" - "@eslint/config-array" "^0.17.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.18.0" + "@eslint/core" "^0.7.0" "@eslint/eslintrc" "^3.1.0" - "@eslint/js" "9.7.0" + "@eslint/js" "9.14.0" + "@eslint/plugin-kit" "^0.2.0" + "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.3.0" - "@nodelib/fs.walk" "^1.2.8" + "@humanwhocodes/retry" "^0.4.0" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" escape-string-regexp "^4.0.0" - eslint-scope "^8.0.2" - eslint-visitor-keys "^4.0.0" - espree "^10.1.0" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" esquery "^1.5.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -9049,14 +9306,11 @@ eslint@^9.7.0: ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - is-path-inside "^3.0.3" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.3" - strip-ansi "^6.0.1" text-table "^0.2.0" esmoduleserve@^0.2.0: @@ -9069,7 +9323,7 @@ esmoduleserve@^0.2.0: resolve "^1.15.1" serve-static "^1.14.1" -espree@^10.0.1, espree@^10.1.0: +espree@^10.0.1: version "10.1.0" resolved "https://registry.yarnpkg.com/espree/-/espree-10.1.0.tgz#8788dae611574c0f070691f522e4116c5a11fc56" integrity sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA== @@ -9078,6 +9332,15 @@ espree@^10.0.1, espree@^10.1.0: acorn-jsx "^5.3.2" eslint-visitor-keys "^4.0.0" +espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" @@ -10337,7 +10600,7 @@ globals@^15.7.0: resolved "https://registry.yarnpkg.com/globals/-/globals-15.8.0.tgz#e64bb47b619dd8cbf32b3c1a0a61714e33cbbb41" integrity sha512-VZAJ4cewHTExBWDHR6yptdIBlx9YSSZuwojj9Nt5mBRXQzrKakDsVKQ1J63sklLvzAJm0X5+RpO4i3Y2hcOnFw== -globalthis@^1.0.3: +globalthis@^1.0.3, globalthis@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== @@ -10588,6 +10851,18 @@ helpertypes@^0.0.18: resolved "https://registry.yarnpkg.com/helpertypes/-/helpertypes-0.0.18.tgz#fd2bf5d3351cc7d80f7876732361d3adba63e5b4" integrity sha512-XRhfbSEmR+poXUC5/8AbmYNJb2riOT6qPzjGJZr0S9YedHiaY+/tzPYzWMUclYMEdCYo/1l8PDYrQFCj02v97w== +hermes-estree@0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480" + integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw== + +hermes-parser@^0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1" + integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA== + dependencies: + hermes-estree "0.25.1" + hey-listen@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" @@ -11306,7 +11581,7 @@ is-observable@^1.1.0: dependencies: symbol-observable "^1.1.0" -is-path-inside@^3.0.2, is-path-inside@^3.0.3: +is-path-inside@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== @@ -11622,6 +11897,17 @@ iterator.prototype@^1.1.2: reflect.getprototypeof "^1.0.4" set-function-name "^2.0.1" +iterator.prototype@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.3.tgz#016c2abe0be3bbdb8319852884f60908ac62bf9c" + integrity sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ== + dependencies: + define-properties "^1.2.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + reflect.getprototypeof "^1.0.4" + set-function-name "^2.0.1" + jackspeak@^3.1.2: version "3.4.0" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.0.tgz#a75763ff36ad778ede6a156d8ee8b124de445b4a" @@ -13906,6 +14192,11 @@ node-releases@^2.0.14, node-releases@^2.0.2: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== +node-releases@^2.0.18: + version "2.0.19" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" + integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== + nopt@^7.2.1: version "7.2.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7" @@ -15529,6 +15820,11 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7, rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-compiler-runtime@19.0.0-beta-37ed2a7-20241206: + version "19.0.0-beta-37ed2a7-20241206" + resolved "https://registry.yarnpkg.com/react-compiler-runtime/-/react-compiler-runtime-19.0.0-beta-37ed2a7-20241206.tgz#f05231928f2daf9ff5ff9b63e5861161de2c3e6d" + integrity sha512-9e6rCpVylr9EnVocgYAjft7+2v01BDpajeHKRoO+oc9pKcAMTpstHtHvE/TSVbyf4FvzCGjfKcfHM9XGTXI6Tw== + react-dom@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" @@ -17475,7 +17771,7 @@ test-exclude@^6.0.0: text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== thenify-all@^1.0.0: version "1.6.0" @@ -18166,6 +18462,14 @@ update-browserslist-db@^1.1.0: escalade "^3.1.2" picocolors "^1.0.1" +update-browserslist-db@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.0" + update-check@1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.2.tgz#2fe09f725c543440b3d7dabe8971f2d5caaedc28" @@ -19434,6 +19738,11 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +zod-validation-error@^3.0.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-3.4.0.tgz#3a8a1f55c65579822d7faa190b51336c61bee2a6" + integrity sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ== + zod@3.21.4: version "3.21.4" resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db"