Просмотр исходного кода

configure biome for apps/app/src/client/util

Futa Arai 3 месяцев назад
Родитель
Сommit
a942827c5b

+ 1 - 0
apps/app/.eslintrc.js

@@ -87,6 +87,7 @@ module.exports = {
     'src/server/service/normalize-data/**',
     'src/client/interfaces/**',
     'src/client/models/**',
+    'src/client/util/**',
   ],
   settings: {
     // resolve path aliases by eslint-import-resolver-typescript

BIN
apps/app/.swc/plugins/v7_linux_aarch64_0.106.16/85face98bcf0ea217842cb93383692497c6ae8a7da71a76bf3d93a3a42b4228c


+ 26 - 9
apps/app/src/client/util/apiv1-client.ts

@@ -5,7 +5,6 @@ import axios from '~/utils/axios';
 const apiv1Root = '/_api';
 
 class Apiv1ErrorHandler extends Error {
-
   code;
 
   data;
@@ -16,12 +15,14 @@ class Apiv1ErrorHandler extends Error {
     this.message = message;
     this.code = code;
     this.data = data;
-
   }
-
 }
 
-export async function apiRequest<T>(method: string, path: string, params: unknown): Promise<T> {
+export async function apiRequest<T>(
+  method: string,
+  path: string,
+  params: unknown,
+): Promise<T> {
   const res = await axios[method](urljoin(apiv1Root, path), params);
 
   if (res.data.ok) {
@@ -30,25 +31,41 @@ export async function apiRequest<T>(method: string, path: string, params: unknow
 
   // Return error code if code is exist
   if (res.data.code != null) {
-    const error = new Apiv1ErrorHandler(res.data.error, res.data.code, res.data.data);
+    const error = new Apiv1ErrorHandler(
+      res.data.error,
+      res.data.code,
+      res.data.data,
+    );
     throw error;
   }
 
   throw new Error(res.data.error);
 }
 
-export async function apiGet<T>(path: string, params: unknown = {}): Promise<T> {
+export async function apiGet<T>(
+  path: string,
+  params: unknown = {},
+): Promise<T> {
   return apiRequest<T>('get', path, { params });
 }
 
-export async function apiPost<T>(path: string, params: unknown = {}): Promise<T> {
+export async function apiPost<T>(
+  path: string,
+  params: unknown = {},
+): Promise<T> {
   return apiRequest<T>('post', path, params);
 }
 
-export async function apiPostForm<T>(path: string, formData: FormData): Promise<T> {
+export async function apiPostForm<T>(
+  path: string,
+  formData: FormData,
+): Promise<T> {
   return apiRequest<T>('postForm', path, formData);
 }
 
-export async function apiDelete<T>(path: string, params: unknown = {}): Promise<T> {
+export async function apiDelete<T>(
+  path: string,
+  params: unknown = {},
+): Promise<T> {
   return apiRequest<T>('delete', path, { data: params });
 }

+ 29 - 10
apps/app/src/client/util/apiv3-client.ts

@@ -10,12 +10,13 @@ const apiv3Root = '/_api/v3';
 
 const logger = loggerFactory('growi:apiv3');
 
-
 const apiv3ErrorHandler = (_err: any): any[] => {
   // extract api errors from general 400 err
   const err = axios.isAxiosError(_err) ? _err.response?.data.errors : _err;
   const errs = toArrayIfNot(err);
-  const errorInfo = axios.isAxiosError(_err) ? _err.response?.data.info : undefined;
+  const errorInfo = axios.isAxiosError(_err)
+    ? _err.response?.data.info
+    : undefined;
 
   for (const err of errs) {
     logger.error(err.message);
@@ -28,33 +29,51 @@ const apiv3ErrorHandler = (_err: any): any[] => {
 };
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
-export async function apiv3Request<T = any>(method: string, path: string, params: unknown): Promise<AxiosResponse<T>> {
+export async function apiv3Request<T = any>(
+  method: string,
+  path: string,
+  params: unknown,
+): Promise<AxiosResponse<T>> {
   try {
     const res = await axios[method](urljoin(apiv3Root, path), params);
     return res;
-  }
-  catch (err) {
+  } catch (err) {
     const errors = apiv3ErrorHandler(err);
     throw errors;
   }
 }
 
-export async function apiv3Get<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {
+export async function apiv3Get<T = any>(
+  path: string,
+  params: unknown = {},
+): Promise<AxiosResponse<T>> {
   return apiv3Request('get', path, { params });
 }
 
-export async function apiv3Post<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {
+export async function apiv3Post<T = any>(
+  path: string,
+  params: unknown = {},
+): Promise<AxiosResponse<T>> {
   return apiv3Request('post', path, params);
 }
 
-export async function apiv3PostForm<T = any>(path: string, formData: FormData): Promise<AxiosResponse<T>> {
+export async function apiv3PostForm<T = any>(
+  path: string,
+  formData: FormData,
+): Promise<AxiosResponse<T>> {
   return apiv3Request('postForm', path, formData);
 }
 
-export async function apiv3Put<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {
+export async function apiv3Put<T = any>(
+  path: string,
+  params: unknown = {},
+): Promise<AxiosResponse<T>> {
   return apiv3Request('put', path, params);
 }
 
-export async function apiv3Delete<T = any>(path: string, params: unknown = {}): Promise<AxiosResponse<T>> {
+export async function apiv3Delete<T = any>(
+  path: string,
+  params: unknown = {},
+): Promise<AxiosResponse<T>> {
   return apiv3Request('delete', path, { params });
 }

+ 48 - 12
apps/app/src/client/util/bookmark-utils.ts

@@ -1,44 +1,80 @@
 import type { IRevision, Ref } from '@growi/core';
 
-import type { BookmarkFolderItems, BookmarkedPage } from '~/interfaces/bookmark-info';
+import type {
+  BookmarkedPage,
+  BookmarkFolderItems,
+} from '~/interfaces/bookmark-info';
 
 import { apiv3Delete, apiv3Post, apiv3Put } from './apiv3-client';
 
 // Check if bookmark folder item has childFolder or bookmarks
-export const hasChildren = ({ childFolder, bookmarks }: { childFolder?: BookmarkFolderItems[], bookmarks?: BookmarkedPage[] }): boolean => {
-  return !!((childFolder && childFolder.length > 0) || (bookmarks && bookmarks.length > 0));
+export const hasChildren = ({
+  childFolder,
+  bookmarks,
+}: {
+  childFolder?: BookmarkFolderItems[];
+  bookmarks?: BookmarkedPage[];
+}): boolean => {
+  return !!(
+    (childFolder && childFolder.length > 0) ||
+    (bookmarks && bookmarks.length > 0)
+  );
 };
 
 // Add new folder helper
-export const addNewFolder = async(name: string, parent: string | null): Promise<void> => {
+export const addNewFolder = async (
+  name: string,
+  parent: string | null,
+): Promise<void> => {
   await apiv3Post('/bookmark-folder', { name, parent });
 };
 
 // Put bookmark to a folder
-export const addBookmarkToFolder = async(pageId: string, folderId: string | null): Promise<void> => {
-  await apiv3Post('/bookmark-folder/add-bookmark-to-folder', { pageId, folderId });
+export const addBookmarkToFolder = async (
+  pageId: string,
+  folderId: string | null,
+): Promise<void> => {
+  await apiv3Post('/bookmark-folder/add-bookmark-to-folder', {
+    pageId,
+    folderId,
+  });
 };
 
 // Delete bookmark folder
-export const deleteBookmarkFolder = async(bookmarkFolderId: string): Promise<void> => {
+export const deleteBookmarkFolder = async (
+  bookmarkFolderId: string,
+): Promise<void> => {
   await apiv3Delete(`/bookmark-folder/${bookmarkFolderId}`);
 };
 
 // Rename page from bookmark item control
-export const renamePage = async(pageId: string, revisionId: Ref<IRevision> | undefined, newPagePath: string): Promise<void> => {
+export const renamePage = async (
+  pageId: string,
+  revisionId: Ref<IRevision> | undefined,
+  newPagePath: string,
+): Promise<void> => {
   await apiv3Put('/pages/rename', { pageId, revisionId, newPagePath });
 };
 
 // Update bookmark by isBookmarked status
-export const toggleBookmark = async(pageId: string, status: boolean): Promise<void> => {
+export const toggleBookmark = async (
+  pageId: string,
+  status: boolean,
+): Promise<void> => {
   await apiv3Put('/bookmark-folder/update-bookmark', { pageId, status });
 };
 
 // Update Bookmark folder
-export const updateBookmarkFolder = async(
-    bookmarkFolderId: string, name: string, parent: string | null, childFolder: BookmarkFolderItems[],
+export const updateBookmarkFolder = async (
+  bookmarkFolderId: string,
+  name: string,
+  parent: string | null,
+  childFolder: BookmarkFolderItems[],
 ): Promise<void> => {
   await apiv3Put('/bookmark-folder', {
-    bookmarkFolderId, name, parent, childFolder,
+    bookmarkFolderId,
+    name,
+    parent,
+    childFolder,
   });
 };

+ 14 - 7
apps/app/src/client/util/scope-util.test.ts

@@ -1,10 +1,9 @@
 import { SCOPE } from '@growi/core/dist/interfaces';
-import { describe, it, expect } from 'vitest';
+import { describe, expect, it } from 'vitest';
 
-import { parseScopes, getDisabledScopes, extractScopes } from './scope-util';
+import { extractScopes, getDisabledScopes, parseScopes } from './scope-util';
 
 describe('scope-util', () => {
-
   const mockScopes = {
     READ: {
       USER: 'read:user',
@@ -45,8 +44,12 @@ describe('scope-util', () => {
     expect(result.ALL).toBeDefined();
 
     // Check admin settings
-    expect(result.ADMIN['admin:setting']['read:admin:setting']).toBe('read:admin:setting');
-    expect(result.ADMIN['admin:setting']['write:admin:setting']).toBe('write:admin:setting');
+    expect(result.ADMIN['admin:setting']['read:admin:setting']).toBe(
+      'read:admin:setting',
+    );
+    expect(result.ADMIN['admin:setting']['write:admin:setting']).toBe(
+      'write:admin:setting',
+    );
 
     // Check ALL category
     expect(result.ALL['read:all']).toBe('read:all');
@@ -79,8 +82,12 @@ describe('scope-util', () => {
   it('should handle multiple wildcard selections', () => {
     const selectedScopes = [SCOPE.READ.ALL, SCOPE.WRITE.ALL];
     const availableScopes = [
-      SCOPE.READ.FEATURES.PAGE, SCOPE.READ.FEATURES.ATTACHMENT, SCOPE.READ.ALL,
-      SCOPE.WRITE.FEATURES.PAGE, SCOPE.WRITE.FEATURES.ATTACHMENT, SCOPE.WRITE.ALL,
+      SCOPE.READ.FEATURES.PAGE,
+      SCOPE.READ.FEATURES.ATTACHMENT,
+      SCOPE.READ.ALL,
+      SCOPE.WRITE.FEATURES.PAGE,
+      SCOPE.WRITE.FEATURES.ATTACHMENT,
+      SCOPE.WRITE.ALL,
     ];
 
     const result = getDisabledScopes(selectedScopes, availableScopes);

+ 22 - 16
apps/app/src/client/util/scope-util.ts

@@ -1,6 +1,5 @@
 import { ALL_SIGN, type Scope } from '@growi/core/dist/interfaces';
 
-
 // Data structure for the final merged scopes
 interface ScopeMap {
   [key: string]: Scope | ScopeMap;
@@ -9,17 +8,17 @@ interface ScopeMap {
 // Input object with arbitrary action keys (e.g., READ, WRITE)
 type ScopesInput = Record<string, any>;
 
-
 function parseSubScope(
-    parentKey: string,
-    subObjForActions: Record<string, any>,
-    actions: string[],
+  parentKey: string,
+  subObjForActions: Record<string, any>,
+  actions: string[],
 ): ScopeMap {
   const result: ScopeMap = {};
 
   for (const action of actions) {
     if (typeof subObjForActions[action] === 'string') {
-      result[`${action.toLowerCase()}:${parentKey.toLowerCase()}`] = subObjForActions[action];
+      result[`${action.toLowerCase()}:${parentKey.toLowerCase()}`] =
+        subObjForActions[action];
       subObjForActions[action] = undefined;
     }
   }
@@ -28,7 +27,7 @@ function parseSubScope(
   for (const action of actions) {
     const obj = subObjForActions[action];
     if (obj && typeof obj === 'object') {
-      Object.keys(obj).forEach(k => childKeys.add(k));
+      Object.keys(obj).forEach((k) => childKeys.add(k));
     }
   }
 
@@ -37,7 +36,8 @@ function parseSubScope(
       for (const action of actions) {
         const val = subObjForActions[action]?.[ck];
         if (typeof val === 'string') {
-          result[`${action.toLowerCase()}:${parentKey.toLowerCase()}:all`] = val as Scope;
+          result[`${action.toLowerCase()}:${parentKey.toLowerCase()}:all`] =
+            val as Scope;
         }
       }
       continue;
@@ -55,13 +55,19 @@ function parseSubScope(
   return result;
 }
 
-export function parseScopes({ scopes, isAdmin = false }: { scopes: ScopesInput ; isAdmin?: boolean }): ScopeMap {
+export function parseScopes({
+  scopes,
+  isAdmin = false,
+}: {
+  scopes: ScopesInput;
+  isAdmin?: boolean;
+}): ScopeMap {
   const actions = Object.keys(scopes);
   const topKeys = new Set<string>();
 
   // Collect all top-level keys (e.g., ALL, ADMIN, USER) across all actions
   for (const action of actions) {
-    Object.keys(scopes[action] || {}).forEach(k => topKeys.add(k));
+    Object.keys(scopes[action] || {}).forEach((k) => topKeys.add(k));
   }
 
   const result: ScopeMap = {};
@@ -81,8 +87,7 @@ export function parseScopes({ scopes, isAdmin = false }: { scopes: ScopesInput ;
         }
       }
       result.ALL = allObj;
-    }
-    else {
+    } else {
       const subObjForActions: Record<string, any> = {};
       for (const action of actions) {
         subObjForActions[action] = scopes[action]?.[key];
@@ -97,10 +102,12 @@ export function parseScopes({ scopes, isAdmin = false }: { scopes: ScopesInput ;
 /**
  * Determines which scopes should be disabled based on wildcard selections
  */
-export function getDisabledScopes(selectedScopes: Scope[], availableScopes: string[]): Set<Scope> {
+export function getDisabledScopes(
+  selectedScopes: Scope[],
+  availableScopes: string[],
+): Set<Scope> {
   const disabledSet = new Set<Scope>();
 
-
   // If no selected scopes, return empty set
   if (!selectedScopes || selectedScopes.length === 0) {
     return disabledSet;
@@ -133,8 +140,7 @@ export function extractScopes(obj: Record<string, any>): string[] {
   Object.values(obj).forEach((value) => {
     if (typeof value === 'string') {
       result.push(value);
-    }
-    else if (typeof value === 'object' && !Array.isArray(value)) {
+    } else if (typeof value === 'object' && !Array.isArray(value)) {
       result = result.concat(extractScopes(value));
     }
   });

+ 10 - 9
apps/app/src/client/util/t-with-opt.ts

@@ -1,15 +1,16 @@
 import { useCallback } from 'react';
-
 import { useTranslation } from 'next-i18next';
 
-export const useTWithOpt = (): (key: string, opt?: any) => string => {
-
+export const useTWithOpt = (): ((key: string, opt?: any) => string) => {
   const { t } = useTranslation();
 
-  return useCallback((key, opt) => {
-    if (typeof opt === 'object') {
-      return t(key, opt).toString();
-    }
-    return t(key);
-  }, [t]);
+  return useCallback(
+    (key, opt) => {
+      if (typeof opt === 'object') {
+        return t(key, opt).toString();
+      }
+      return t(key);
+    },
+    [t],
+  );
 };

+ 13 - 5
apps/app/src/client/util/toastr.ts

@@ -3,12 +3,14 @@ import { toast } from 'react-toastify';
 
 import { toArrayIfNot } from '~/utils/array-utils';
 
-
 export const toastErrorOption: ToastOptions = {
   autoClose: false,
   closeButton: true,
 };
-export const toastError = (err: string | Error | Error[], option: ToastOptions = toastErrorOption): void => {
+export const toastError = (
+  err: string | Error | Error[],
+  option: ToastOptions = toastErrorOption,
+): void => {
   const errs = toArrayIfNot(err);
 
   if (errs.length === 0) {
@@ -16,7 +18,7 @@ export const toastError = (err: string | Error | Error[], option: ToastOptions =
   }
 
   for (const err of errs) {
-    const message = (typeof err === 'string') ? err : err.message;
+    const message = typeof err === 'string' ? err : err.message;
     toast.error(message, option);
   }
 };
@@ -25,7 +27,10 @@ export const toastSuccessOption: ToastOptions = {
   autoClose: 2000,
   closeButton: true,
 };
-export const toastSuccess = (content: ToastContent, option: ToastOptions = toastSuccessOption): void => {
+export const toastSuccess = (
+  content: ToastContent,
+  option: ToastOptions = toastSuccessOption,
+): void => {
   toast.success(content, option);
 };
 
@@ -33,6 +38,9 @@ export const toastWarningOption: ToastOptions = {
   autoClose: 5000,
   closeButton: true,
 };
-export const toastWarning = (content: ToastContent, option: ToastOptions = toastWarningOption): void => {
+export const toastWarning = (
+  content: ToastContent,
+  option: ToastOptions = toastWarningOption,
+): void => {
   toast.warning(content, option);
 };

+ 36 - 30
apps/app/src/client/util/use-input-validator.ts

@@ -1,5 +1,4 @@
 import { useCallback } from 'react';
-
 import { useTranslation } from 'next-i18next';
 
 export const AlertType = {
@@ -7,7 +6,7 @@ export const AlertType = {
   ERROR: 'Error',
 } as const;
 
-export type AlertType = typeof AlertType[keyof typeof AlertType];
+export type AlertType = (typeof AlertType)[keyof typeof AlertType];
 
 export const ValidationTarget = {
   FOLDER: 'folder_name',
@@ -15,42 +14,49 @@ export const ValidationTarget = {
   DEFAULT: 'field',
 };
 
-export type ValidationTarget = typeof ValidationTarget[keyof typeof ValidationTarget];
+export type ValidationTarget =
+  (typeof ValidationTarget)[keyof typeof ValidationTarget];
 
 export type AlertInfo = {
-  type?: AlertType
-  message?: string,
-  target?: string
-}
-
+  type?: AlertType;
+  message?: string;
+  target?: string;
+};
 
 export type InputValidationResult = {
-  type: AlertType
-  typeLabel: string,
-  message: string,
-  target: string
-}
+  type: AlertType;
+  typeLabel: string;
+  message: string;
+  target: string;
+};
 
-export type InputValidator = (input?: string, alertType?: AlertType) => InputValidationResult | void;
+export type InputValidator = (
+  input?: string,
+  alertType?: AlertType,
+) => InputValidationResult | void;
 
-export const useInputValidator = (validationTarget: ValidationTarget = ValidationTarget.DEFAULT): InputValidator => {
+export const useInputValidator = (
+  validationTarget: ValidationTarget = ValidationTarget.DEFAULT,
+): InputValidator => {
   const { t } = useTranslation();
 
-  const inputValidator: InputValidator = useCallback((input?, alertType = AlertType.WARNING) => {
-    if ((input ?? '').trim() === '') {
-      return {
-        target: validationTarget,
-        type: alertType,
-        typeLabel: t(alertType),
-        message: t(
-          'input_validation.message.field_required',
-          { target: t(`input_validation.target.${validationTarget}`) },
-        ),
-      };
-    }
-
-    return;
-  }, [t, validationTarget]);
+  const inputValidator: InputValidator = useCallback(
+    (input?, alertType = AlertType.WARNING) => {
+      if ((input ?? '').trim() === '') {
+        return {
+          target: validationTarget,
+          type: alertType,
+          typeLabel: t(alertType),
+          message: t('input_validation.message.field_required', {
+            target: t(`input_validation.target.${validationTarget}`),
+          }),
+        };
+      }
+
+      return;
+    },
+    [t, validationTarget],
+  );
 
   return inputValidator;
 };

+ 0 - 1
biome.json

@@ -30,7 +30,6 @@
       "!packages/pdf-converter-client/specs",
       "!apps/app/src/client/components",
       "!apps/app/src/client/services",
-      "!apps/app/src/client/util",
       "!apps/app/src/server/service/page"
     ]
   },