Switch to BiomeJS (#306)

This commit is contained in:
Gregory Schier
2025-11-23 08:38:13 -08:00
committed by GitHub
parent 2bac610efe
commit ec3e2e16a9
332 changed files with 3007 additions and 4097 deletions

View File

@@ -19,7 +19,7 @@ export function useSubscribeActiveCookieJarId() {
useEffect(() => {
if (search == null) return; // Happens during Vite hot reload
const activeCookieJar = cookieJars?.find((j) => j.id == cookieJarId) ?? null;
const activeCookieJar = cookieJars?.find((j) => j.id === cookieJarId) ?? null;
jotaiStore.set(activeCookieJarAtom, activeCookieJar);
}, [cookieJarId, cookieJars, search]);
}
@@ -33,6 +33,11 @@ export function useEnsureActiveCookieJar() {
const { cookie_jar_id: activeCookieJarId } = useSearch({ from: '/workspaces/$workspaceId/' });
// Set the active cookie jar to the first one, if none set
// NOTE: We only run this on cookieJars to prevent data races when switching workspaces since a lot of
// things change when switching workspaces, and we don't currently have a good way to ensure that all
// stores have updated.
// TODO: Create a global data store that can handle this case
// biome-ignore lint/correctness/useExhaustiveDependencies: none
useEffect(() => {
if (cookieJars == null) return; // Hasn't loaded yet
@@ -49,11 +54,5 @@ export function useEnsureActiveCookieJar() {
// There's no active jar, so set it to the first one
console.log('Defaulting active cookie jar to first jar', firstJar);
setWorkspaceSearchParams({ cookie_jar_id: firstJar.id });
// NOTE: We only run this on cookieJars to prevent data races when switching workspaces since a lot of
// things change when switching workspaces, and we don't currently have a good way to ensure that all
// stores have updated.
// TODO: Create a global data store that can handle this case
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [cookieJars]);
}

View File

@@ -1,7 +1,7 @@
import { useSearch } from '@tanstack/react-router';
import type { Environment } from '@yaakapp-internal/models';
import { environmentsAtom } from '@yaakapp-internal/models';
import { useAtomValue , atom } from 'jotai';
import { atom, useAtomValue } from 'jotai';
import { useEffect } from 'react';
import { jotaiStore } from '../lib/jotai';

View File

@@ -5,9 +5,11 @@ import {
} from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
export const allRequestsAtom = atom(function (get) {
return [...get(httpRequestsAtom), ...get(grpcRequestsAtom), ...get(websocketRequestsAtom)];
});
export const allRequestsAtom = atom((get) => [
...get(httpRequestsAtom),
...get(grpcRequestsAtom),
...get(websocketRequestsAtom),
]);
export function useAllRequests() {
return useAtomValue(allRequestsAtom);

View File

@@ -40,7 +40,7 @@ export function useAuthTab<T extends string>(tabValue: T, model: AuthenticatedMo
{
label: 'Inherit from Parent',
shortLabel:
inheritedAuth != null && inheritedAuth.authenticationType != 'none' ? (
inheritedAuth != null && inheritedAuth.authenticationType !== 'none' ? (
<HStack space={1.5}>
{authentication.find((a) => a.name === inheritedAuth.authenticationType)
?.shortLabel ?? 'UNKNOWN'}

View File

@@ -1,5 +1,5 @@
import { useFastMutation } from './useFastMutation';
import { event } from '@tauri-apps/api';
import { useFastMutation } from './useFastMutation';
export function useCancelHttpResponse(id: string | null) {
return useFastMutation<void>({

View File

@@ -1,18 +1,18 @@
import type { MutableRefObject } from 'react';
import type { RefObject } from 'react';
import { useLayoutEffect, useState } from 'react';
export function useContainerSize(ref: MutableRefObject<HTMLElement | null>) {
export function useContainerSize(ref: RefObject<HTMLElement | null>) {
const [size, setSize] = useState<{ width: number; height: number }>({ width: 0, height: 0 });
useLayoutEffect(() => {
const el = ref.current;
if (el) {
const observer = new ResizeObserver((entries) => {
entries.forEach((entry) => {
for (const entry of entries) {
if (entry.target === el) {
setSize({ width: entry.contentRect.width, height: entry.contentRect.height });
}
});
}
});
observer.observe(el);

View File

@@ -11,9 +11,8 @@ export const environmentsBreakdownAtom = atom((get) => {
?.sort((a, b) => {
if (a.sortPriority === b.sortPriority) {
return a.updatedAt > b.updatedAt ? 1 : -1;
} else {
return a.sortPriority - b.sortPriority;
}
return a.sortPriority - b.sortPriority;
}) ?? [];
const folderEnvironments =

View File

@@ -65,6 +65,6 @@ export function useFastMutation<TData = unknown, TError = unknown, TVariables =
) {
return useMemo(() => {
return createFastMutation(defaultArgs);
// eslint-disable-next-line react-hooks/exhaustive-deps
// biome-ignore lint/correctness/useExhaustiveDependencies: Force it!
}, defaultArgs.mutationKey);
}

View File

@@ -17,13 +17,14 @@ export function useFormatText({
queryFn: async () => {
if (text === '' || !pretty) {
return text;
} else if (language === 'json') {
return tryFormatJson(text);
} else if (language === 'xml' || language === 'html') {
return tryFormatXml(text);
} else {
return text;
}
if (language === 'json') {
return tryFormatJson(text);
}
if (language === 'xml' || language === 'html') {
return tryFormatXml(text);
}
return text;
},
}).data;
}

View File

@@ -1,10 +1,10 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import { emit } from '@tauri-apps/api/event';
import type { GrpcConnection, GrpcRequest } from '@yaakapp-internal/models';
import {jotaiStore} from "../lib/jotai";
import { jotaiStore } from '../lib/jotai';
import { minPromiseMillis } from '../lib/minPromiseMillis';
import { invokeCmd } from '../lib/tauri';
import {activeEnvironmentIdAtom, useActiveEnvironment} from './useActiveEnvironment';
import { activeEnvironmentIdAtom, useActiveEnvironment } from './useActiveEnvironment';
import { useDebouncedValue } from './useDebouncedValue';
export interface ReflectResponseService {

View File

@@ -24,9 +24,9 @@ export function useGrpcRequestActions() {
},
});
// biome-ignore lint/correctness/useExhaustiveDependencies: none
const actions = useMemo(() => {
return actionsResult.data ?? [];
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(actionsResult.data)]);
return actions;

View File

@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import { useMemo } from 'react';
import { CountBadge } from '../components/core/CountBadge';
import type { TabItem } from '../components/core/Tabs/Tabs';
import type { HeaderModel } from './useInheritedHeaders';

View File

@@ -281,16 +281,15 @@ export function useFormattedHotkey(action: HotkeyAction | null): string[] | null
if (os === 'macos') {
return labelParts;
} else {
return [labelParts.join('+')];
}
return [labelParts.join('+')];
}
const resolveHotkeyKey = (key: string) => {
const os = type();
if (key === 'CmdCtrl' && os === 'macos') return 'Meta';
else if (key === 'CmdCtrl') return 'Control';
else return key;
if (key === 'CmdCtrl') return 'Control';
return key;
};
function compareKeys(keysA: string[], keysB: string[]) {

View File

@@ -1,6 +1,6 @@
import { useQuery } from '@tanstack/react-query';
import type { GetHttpAuthenticationSummaryResponse } from '@yaakapp-internal/plugins';
import { useAtomValue , atom } from 'jotai';
import { atom, useAtomValue } from 'jotai';
import { useState } from 'react';
import { jotaiStore } from '../lib/jotai';
import { invokeCmd } from '../lib/tauri';
@@ -26,7 +26,7 @@ export function useSubscribeHttpAuthentication() {
// NOTE: visibilitychange (refetchOnWindowFocus) does not work on Windows, so we'll rely on this logic
// to refetch things until that's working again
// TODO: Update plugin system to wait for plugins to initialize before sending the first event to them
refetchInterval: numResults > 0 ? Infinity : 1000,
refetchInterval: numResults > 0 ? Number.POSITIVE_INFINITY : 1000,
refetchOnMount: true,
placeholderData: (prev) => prev, // Keep previous data on refetch
queryFn: async () => {

View File

@@ -21,9 +21,9 @@ export function useHttpRequestActions() {
queryFn: () => getHttpRequestActions(),
});
// biome-ignore lint/correctness/useExhaustiveDependencies: none
const actions = useMemo(() => {
return actionsResult.data ?? [];
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(actionsResult.data)]);
return actions;

View File

@@ -24,7 +24,7 @@ export function useImportCurl() {
workspaceId,
});
let verb;
let verb: string;
if (overwriteRequestId == null) {
verb = 'Created';
await createRequestAndNavigate(importedRequest);

View File

@@ -8,15 +8,11 @@ import type {
import { foldersAtom, workspacesAtom } from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
const ancestorsAtom = atom(function (get) {
return [...get(foldersAtom), ...get(workspacesAtom)];
});
const ancestorsAtom = atom((get) => [...get(foldersAtom), ...get(workspacesAtom)]);
export type AuthenticatedModel = HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace;
export function useInheritedAuthentication(
baseModel: AuthenticatedModel | null,
) {
export function useInheritedAuthentication(baseModel: AuthenticatedModel | null) {
const parents = useAtomValue(ancestorsAtom);
if (baseModel == null) return null;
@@ -35,7 +31,7 @@ export function useInheritedAuthentication(
// Recurse up the tree
const parent = parents.find((p) => {
if (child.folderId) return p.id === child.folderId;
else return p.id === child.workspaceId;
return p.id === child.workspaceId;
});
// Failed to find parent (should never happen)

View File

@@ -9,9 +9,7 @@ import type {
import { foldersAtom, workspacesAtom } from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
const ancestorsAtom = atom(function (get) {
return [...get(foldersAtom), ...get(workspacesAtom)];
});
const ancestorsAtom = atom((get) => [...get(foldersAtom), ...get(workspacesAtom)]);
export type HeaderModel = HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace;
@@ -30,7 +28,7 @@ export function useInheritedHeaders(baseModel: HeaderModel | null) {
// Recurse up the tree
const parent = parents.find((p) => {
if (child.folderId) return p.id === child.folderId;
else return p.id === child.workspaceId;
return p.id === child.workspaceId;
});
// Failed to find parent (should never happen)

View File

@@ -1,5 +1,5 @@
import { useFastMutation } from './useFastMutation';
import { invokeCmd } from '../lib/tauri';
import { useFastMutation } from './useFastMutation';
export function useInstallPlugin() {
return useFastMutation<void, unknown, string>({

View File

@@ -1,7 +1,7 @@
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { invoke } from '@tauri-apps/api/core';
import type { GraphQlIntrospection, HttpRequest } from '@yaakapp-internal/models';
import type { GraphQLSchema } from 'graphql';
import type { GraphQLSchema, IntrospectionQuery } from 'graphql';
import { buildClientSchema, getIntrospectionQuery } from 'graphql';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { minPromiseMillis } from '../lib/minPromiseMillis';
@@ -86,6 +86,7 @@ export function useIntrospectGraphQL(
}
}, [activeEnvironment?.id, baseRequest, upsertIntrospection]);
// biome-ignore lint/correctness/useExhaustiveDependencies: none
useEffect(() => {
// Skip introspection if automatic is disabled and we already have one
if (options.disabled) {
@@ -93,8 +94,6 @@ export function useIntrospectGraphQL(
}
refetch().catch(console.error);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [baseRequest.id, debouncedRequest.url, debouncedRequest.method, activeEnvironment?.id]);
const clear = useCallback(async () => {
@@ -142,17 +141,17 @@ export function useCurrentGraphQLSchema(request: HttpRequest) {
function tryParseIntrospectionToSchema(
content: string,
): { schema: GraphQLSchema } | { error: string } {
let parsedResponse;
let parsedResponse: IntrospectionQuery;
try {
parsedResponse = JSON.parse(content).data;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// biome-ignore lint/suspicious/noExplicitAny: none
} catch (e: any) {
return { error: String('message' in e ? e.message : e) };
}
try {
return { schema: buildClientSchema(parsedResponse, {}) };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// biome-ignore lint/suspicious/noExplicitAny: none
} catch (e: any) {
return { error: String('message' in e ? e.message : e) };
}

View File

@@ -19,6 +19,7 @@ export function useKeyValue<T extends object | boolean | number | string | null>
fallback: T;
}) {
const { value, isLoading } = useAtomValue(
// biome-ignore lint/correctness/useExhaustiveDependencies: Only create a new atom when the key changes. Fallback might not be a stable reference, so we don't want to refresh on that.
useMemo(
() =>
selectAtom(
@@ -32,9 +33,6 @@ export function useKeyValue<T extends object | boolean | number | string | null>
},
(a, b) => deepEqual(a, b),
),
// Only create a new atom when the key changes. Fallback might not be a stable reference, so
// we don't want to refresh on that.
// eslint-disable-next-line react-hooks/exhaustive-deps
[buildKeyValueKey(key)],
),
);
@@ -44,6 +42,7 @@ export function useKeyValue<T extends object | boolean | number | string | null>
mutationFn: (value) => setKeyValue<T>({ namespace, key, value }),
});
// biome-ignore lint/correctness/useExhaustiveDependencies: none
const set = useCallback(
async (valueOrUpdate: ((v: T) => T) | T) => {
if (typeof valueOrUpdate === 'function') {
@@ -56,7 +55,6 @@ export function useKeyValue<T extends object | boolean | number | string | null>
await mutateAsync(valueOrUpdate);
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[typeof key === 'string' ? key : key.join('::'), namespace, value],
);

View File

@@ -5,14 +5,12 @@ export function useKeyboardEvent(
key: KeyboardEvent['key'],
cb: () => void,
) {
// biome-ignore lint/correctness/useExhaustiveDependencies: Don't have `cb` as a dep for caller convenience
useEffect(() => {
const fn = (e: KeyboardEvent) => {
if (e.key === key) cb();
};
document.addEventListener(event, fn);
return () => document.removeEventListener(event, fn);
// Don't have `cb` as a dep for caller convenience
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [event]);
}

View File

@@ -1,4 +1,4 @@
import type { GrpcConnection} from '@yaakapp-internal/models';
import type { GrpcConnection } from '@yaakapp-internal/models';
import { grpcConnectionsAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';

View File

@@ -1,4 +1,4 @@
import type { HttpResponse} from '@yaakapp-internal/models';
import type { HttpResponse } from '@yaakapp-internal/models';
import { httpResponsesAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';

View File

@@ -15,7 +15,9 @@ function getParentFolders(
): Folder[] {
if (currentModel == null) return [];
const parentFolder = currentModel.folderId ? folders.find((f) => f.id === currentModel.folderId) : null;
const parentFolder = currentModel.folderId
? folders.find((f) => f.id === currentModel.folderId)
: null;
if (parentFolder == null) {
return [];
}

View File

@@ -5,7 +5,7 @@ import {
grpcEventsAtom,
replaceModelsInStore,
} from '@yaakapp-internal/models';
import { useAtomValue , atom } from 'jotai';
import { atom, useAtomValue } from 'jotai';
import { useEffect } from 'react';
import { atomWithKVStorage } from '../lib/atoms/atomWithKVStorage';
import { activeRequestIdAtom } from './useActiveRequestId';
@@ -40,7 +40,7 @@ export const pinnedGrpcConnectionIdAtom = atom(
);
function recordKey(activeRequestId: string | null, latestConnection: GrpcConnection | null) {
return activeRequestId + '-' + (latestConnection?.id ?? 'none');
return `${activeRequestId}-${latestConnection?.id ?? 'none'}`;
}
export const activeGrpcConnections = atom<GrpcConnection[]>((get) => {

View File

@@ -1,4 +1,4 @@
import type { HttpResponse} from '@yaakapp-internal/models';
import type { HttpResponse } from '@yaakapp-internal/models';
import { httpResponsesAtom } from '@yaakapp-internal/models';
import { useAtomValue } from 'jotai';
import { useKeyValue } from './useKeyValue';

View File

@@ -17,7 +17,7 @@ const pinnedWebsocketConnectionIdAtom = atomWithKVStorage<Record<string, string
);
function recordKey(activeRequestId: string | null, latestConnection: WebsocketConnection | null) {
return activeRequestId + '-' + (latestConnection?.id ?? 'none');
return `${activeRequestId}-${latestConnection?.id ?? 'none'}`;
}
export const activeWebsocketConnectionsAtom = atom<WebsocketConnection[]>((get) => {

View File

@@ -21,7 +21,7 @@ export function useRefreshPlugins() {
mutationKey: ['refresh_plugins'],
mutationFn: async () => {
await minPromiseMillis(
(async function () {
(async () => {
await invokeCmd('cmd_reload_plugins');
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
await changeModelStoreWorkspace(workspaceId); // Force refresh models

View File

@@ -6,7 +6,7 @@ import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
import { activeCookieJarAtom } from './useActiveCookieJar';
import { useKeyValue } from './useKeyValue';
const kvKey = (workspaceId: string) => 'recent_cookie_jars::' + workspaceId;
const kvKey = (workspaceId: string) => `recent_cookie_jars::${workspaceId}`;
const namespace = 'global';
const fallback: string[] = [];

View File

@@ -5,7 +5,7 @@ import { activeEnvironmentAtom } from './useActiveEnvironment';
import { useEnvironmentsBreakdown } from './useEnvironmentsBreakdown';
import { useKeyValue } from './useKeyValue';
const kvKey = (workspaceId: string) => 'recent_environments::' + workspaceId;
const kvKey = (workspaceId: string) => `recent_environments::${workspaceId}`;
const namespace = 'global';
const fallback: string[] = [];

View File

@@ -1,11 +1,11 @@
import { useEffect, useMemo } from 'react';
import { jotaiStore } from '../lib/jotai';
import { getKeyValue, setKeyValue } from '../lib/keyValueStore';
import { useKeyValue } from './useKeyValue';
import { useAllRequests } from './useAllRequests';
import { activeRequestAtom } from './useActiveRequest';
import { useAllRequests } from './useAllRequests';
import { useKeyValue } from './useKeyValue';
const kvKey = (workspaceId: string) => 'recent_requests::' + workspaceId;
const kvKey = (workspaceId: string) => `recent_requests::${workspaceId}`;
const namespace = 'global';
const fallback: string[] = [];

View File

@@ -1,5 +1,5 @@
import EventEmitter from 'eventemitter3';
import { atom , useAtom } from 'jotai';
import { atom, useAtom } from 'jotai';
import type { DependencyList } from 'react';
import { useCallback, useEffect } from 'react';
@@ -17,7 +17,7 @@ export function useRequestEditorEvent<
return () => {
emitter.off(event, fn);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
// biome-ignore lint/correctness/useExhaustiveDependencies: We're handing deps manually
}, deps);
}

View File

@@ -6,9 +6,9 @@ import { useEffect, useState } from 'react';
*/
export function useStateWithDeps<T>(defaultValue: T | (() => T), deps: DependencyList) {
const [value, setValue] = useState(defaultValue);
// biome-ignore lint/correctness/useExhaustiveDependencies: none
useEffect(() => {
setValue(defaultValue);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [...deps]);
return [value, setValue] as const;
}

View File

@@ -46,7 +46,7 @@ export function useSubscribeTemplateFunctions() {
// NOTE: visibilitychange (refetchOnWindowFocus) does not work on Windows, so we'll rely on this logic
// to refetch things until that's working again
// TODO: Update plugin system to wait for plugins to initialize before sending the first event to them
refetchInterval: numFns > 0 ? Infinity : 1000,
refetchInterval: numFns > 0 ? Number.POSITIVE_INFINITY : 1000,
refetchOnMount: true,
queryFn: async () => {
const result = await invokeCmd<GetTemplateFunctionSummaryResponse[]>(