From 6ec1df9f047eaf4a0146a2ea207beea6aedb4a54 Mon Sep 17 00:00:00 2001 From: "Pascal S." Date: Tue, 16 Jun 2026 16:26:43 +0200 Subject: [PATCH 1/3] chore: #ENABLING-938 rendre les warnings de lint bloquants (--max-warnings 0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ajoute --max-warnings 0 aux scripts lint de tous les packages (eslint + stylelint) pour que les warnings fassent échouer la tâche et remontent dans le résumé Turbo, au lieu de rester masqués (exit 0, cache vert). Nettoie les 16 warnings préexistants sans changement de comportement : - useDate : vraies dépendances (toComputedDate, t) - useCheckable : forme fonctionnelle de setSelectedItems - useHttpErrorToast : pattern latest-ref pour options - useUploadFiles : ajout de resetInputValue (déjà stable) - useWorkspaceFoldersTree : ajout de t, retrait de user inutile + import mort - useHelp : booléen dérivé isHelpEnabled - EditorToolbar : ajout de cantooEditor - CantooAdaptTextBoxView / useOnboardingModal / WorkspaceFolders : eslint-disable justifiés (effets one-shot / déclencheur intentionnel) - image.ts : paramètre element inutilisé retiré Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/bootstrap/package.json | 2 +- packages/client/package.json | 4 ++-- packages/extensions/package.json | 2 +- packages/extensions/src/image/image.ts | 2 +- packages/react/package.json | 2 +- .../react/src/components/Layout/hooks/useHelp.tsx | 7 +++++-- .../react/src/hooks/useCheckable/useCheckable.ts | 14 ++++++++------ packages/react/src/hooks/useDate/useDate.ts | 12 ++++++------ .../hooks/useHttpErrorToast/useHttpErrorToast.ts | 7 ++++++- .../src/hooks/useUploadFiles/useUploadFiles.ts | 2 +- .../useWorkspaceFolders/useWorkspaceFoldersTree.ts | 4 +--- .../components/Editor/CantooAdaptTextBoxView.tsx | 2 ++ .../components/EditorToolbar/EditorToolbar.tsx | 1 + .../modals/OnboardingModal/useOnboardingModal.ts | 3 +++ .../WorkspaceFolders/WorkspaceFolders.tsx | 3 +++ packages/rest-client-base/package.json | 2 +- packages/utilities/package.json | 2 +- 17 files changed, 44 insertions(+), 27 deletions(-) diff --git a/packages/bootstrap/package.json b/packages/bootstrap/package.json index 1e208f101..0819c4ea7 100644 --- a/packages/bootstrap/package.json +++ b/packages/bootstrap/package.json @@ -31,7 +31,7 @@ "format": "prettier --check src/**/*.scss", "format:write": "prettier --write src/**/*.scss", "generate-commit-version": "node scripts/version.cjs", - "lint": "stylelint src/**/*.scss", + "lint": "stylelint src/**/*.scss --max-warnings 0", "watch": "sass --watch --load-path=node_modules/ --style=compressed --quiet-deps --silence-deprecation=import src/index.scss dist/index.css" }, "dependencies": { diff --git a/packages/client/package.json b/packages/client/package.json index 1e3359e79..fc75cb243 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -39,8 +39,8 @@ "fix": "eslint --fix --report-unused-disable-directives --max-warnings 0", "format": "prettier --check \"src/ts/**/*.ts\"", "format:write": "prettier --write \"src/ts/**/*.ts\"", - "lint": "eslint", - "test": "vitest", + "lint": "eslint --max-warnings 0", + "test": "vitest" "test:coverage": "vitest run --coverage" }, "dependencies": { diff --git a/packages/extensions/package.json b/packages/extensions/package.json index 15e30774a..3960e0302 100644 --- a/packages/extensions/package.json +++ b/packages/extensions/package.json @@ -126,7 +126,7 @@ "fix": "eslint --fix --report-unused-disable-directives --max-warnings 0", "format": "prettier --check \"src/**/*.ts\"", "format:write": "prettier --write \"src/**/*.ts\"", - "lint": "eslint" + "lint": "eslint --max-warnings 0" }, "dependencies": { "@edifice.io/utilities": "workspace:*", diff --git a/packages/extensions/src/image/image.ts b/packages/extensions/src/image/image.ts index d3194d60e..78ce3a266 100644 --- a/packages/extensions/src/image/image.ts +++ b/packages/extensions/src/image/image.ts @@ -114,7 +114,7 @@ export const Image = TiptapImage.extend({ } : {}; }, - parseHTML: (element) => { + parseHTML: () => { return null; }, }, diff --git a/packages/react/package.json b/packages/react/package.json index 55d1b33e3..fc90e31bd 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -84,7 +84,7 @@ "fix": "eslint --fix --report-unused-disable-directives --max-warnings 0", "format": "prettier --check \"src/**/*.{ts,tsx}\"", "format:write": "prettier --write \"src/**/*.{ts,tsx}\"", - "lint": "eslint", + "lint": "eslint --max-warnings 0", "test": "vitest", "test:watch": "vitest --watch", "test:coverage": "vitest run --coverage" diff --git a/packages/react/src/components/Layout/hooks/useHelp.tsx b/packages/react/src/components/Layout/hooks/useHelp.tsx index 6c4973c65..23616bbd6 100644 --- a/packages/react/src/components/Layout/hooks/useHelp.tsx +++ b/packages/react/src/components/Layout/hooks/useHelp.tsx @@ -18,9 +18,12 @@ export function useHelp( const [isModalOpen, setIsModalOpen] = useState(false); const [error, setError] = useState(false); const helpPath = theme?.is1d ? '/help-1d' : '/help-2d'; + // Only the truthiness matters here, so derive a stable primitive to avoid + // re-running the effect when an object prop is recreated on each render. + const isHelpEnabled = !!hasOldHelpEnableWorkflow; useEffect(() => { - if (!hasOldHelpEnableWorkflow) { + if (!isHelpEnabled) { return; } @@ -61,7 +64,7 @@ export function useHelp( console.error(error); } })(); - }, [appCode, helpPath]); + }, [appCode, helpPath, isHelpEnabled]); const options = { replace: (domNode: any) => { diff --git a/packages/react/src/hooks/useCheckable/useCheckable.ts b/packages/react/src/hooks/useCheckable/useCheckable.ts index e89c5fbc1..01892df1b 100644 --- a/packages/react/src/hooks/useCheckable/useCheckable.ts +++ b/packages/react/src/hooks/useCheckable/useCheckable.ts @@ -11,12 +11,14 @@ export const useCheckable = ( useEffect(() => { // If data changes, check if selected items are still valid if (data) { - const validSelectedItems = selectedItems.filter((id) => - data.some((item) => item._id === id), - ); - if (validSelectedItems.length !== selectedItems.length) { - setSelectedItems(validSelectedItems); - } + setSelectedItems((currentSelection) => { + const validSelectedItems = currentSelection.filter((id) => + data.some((item) => item._id === id), + ); + return validSelectedItems.length !== currentSelection.length + ? validSelectedItems + : currentSelection; + }); } }, [data]); diff --git a/packages/react/src/hooks/useDate/useDate.ts b/packages/react/src/hooks/useDate/useDate.ts index b6945e777..218c20269 100644 --- a/packages/react/src/hooks/useDate/useDate.ts +++ b/packages/react/src/hooks/useDate/useDate.ts @@ -138,7 +138,7 @@ export default function useDate() { // format D MMM YYYY return computedDate.format(t('date.format.previousYear')); }, - [currentLanguage, parseDate], + [t, toComputedDate], ); /** Compute a user-friendly elapsed duration, between now and a date. */ @@ -147,7 +147,7 @@ export default function useDate() { const computedDate = toComputedDate(date); return computedDate?.isValid() ? computedDate.fromNow() : ''; }, - [currentLanguage, parseDate], + [toComputedDate], ); /** @@ -181,7 +181,7 @@ export default function useDate() { ? computedDate.locale(currentLanguage as string).format(dayjsFormat) : ''; }, - [currentLanguage, parseDate], + [currentLanguage, toComputedDate], ); /** Check if two dates are the same, according to the specified unit. See https://day.js.org/docs/en/query/is-same for more details. @@ -196,7 +196,7 @@ export default function useDate() { const computedDate2 = toComputedDate(date2); return computedDate?.isSame(computedDate2, unit) ?? false; }, - [currentLanguage, parseDate], + [toComputedDate], ); /** Check if a date is same or after another date. See https://day.js.org/docs/en/query/is-same-or-after for more details. @@ -211,7 +211,7 @@ export default function useDate() { const computedDate2 = toComputedDate(date2); return computedDate?.isSameOrAfter(computedDate2, unit) ?? false; }, - [currentLanguage, parseDate], + [toComputedDate], ); /** Check if a date is today. See https://day.js.org/docs/en/plugin/is-today for more details. @@ -223,7 +223,7 @@ export default function useDate() { const computedDate = toComputedDate(date); return computedDate?.isToday() ?? false; }, - [currentLanguage, parseDate], + [toComputedDate], ); return { diff --git a/packages/react/src/hooks/useHttpErrorToast/useHttpErrorToast.ts b/packages/react/src/hooks/useHttpErrorToast/useHttpErrorToast.ts index 8ece8c740..10cca2ac0 100644 --- a/packages/react/src/hooks/useHttpErrorToast/useHttpErrorToast.ts +++ b/packages/react/src/hooks/useHttpErrorToast/useHttpErrorToast.ts @@ -16,6 +16,11 @@ export const useHttpErrorToast = ({ const toast = useToast(); const { t } = useTranslation(); + // Keep the latest options in a ref so the subscription always uses fresh + // values without re-subscribing on every render (options is a new object each time). + const optionsRef = useRef(options); + optionsRef.current = options; + useEffect(() => { if (active) { // Subscribe to HTTP error events @@ -39,7 +44,7 @@ export const useHttpErrorToast = ({ message.current = t(i18nKey); toast.error( React.createElement('div', { children: [message.current] }), - options, + optionsRef.current, ); }); diff --git a/packages/react/src/hooks/useUploadFiles/useUploadFiles.ts b/packages/react/src/hooks/useUploadFiles/useUploadFiles.ts index d34353c38..7ea70697b 100644 --- a/packages/react/src/hooks/useUploadFiles/useUploadFiles.ts +++ b/packages/react/src/hooks/useUploadFiles/useUploadFiles.ts @@ -67,7 +67,7 @@ const useUploadFiles = ({ } }); }, - [uploadAlternateFile, uploadFile, replaceFileAt], + [uploadAlternateFile, uploadFile, replaceFileAt, resetInputValue], ); /* Try to upload more files when diff --git a/packages/react/src/hooks/useWorkspaceFolders/useWorkspaceFoldersTree.ts b/packages/react/src/hooks/useWorkspaceFolders/useWorkspaceFoldersTree.ts index 885a694bd..3980b3b9c 100644 --- a/packages/react/src/hooks/useWorkspaceFolders/useWorkspaceFoldersTree.ts +++ b/packages/react/src/hooks/useWorkspaceFolders/useWorkspaceFoldersTree.ts @@ -1,7 +1,6 @@ import { WorkspaceElement } from '@edifice.io/client'; import { useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useEdificeClient } from '../../providers/EdificeClientProvider/EdificeClientProvider.hook'; interface FolderTreeNode { id: string; @@ -14,7 +13,6 @@ export const WORKSPACE_SHARED_FOLDER_ID = 'workspace-shared-folder-id'; function useWorkspaceFoldersTree(folders?: WorkspaceElement[]) { const { t } = useTranslation(); - const { user } = useEdificeClient(); const [searchQuery, setSearchQuery] = useState(''); @@ -28,7 +26,7 @@ function useWorkspaceFoldersTree(folders?: WorkspaceElement[]) { ); return searchQuery ? filterTree(foldersTree, searchQuery) : foldersTree; - }, [folders, searchQuery, user]); + }, [folders, searchQuery, t]); return { foldersTree: filtredFoldersTree, diff --git a/packages/react/src/modules/editor/components/Editor/CantooAdaptTextBoxView.tsx b/packages/react/src/modules/editor/components/Editor/CantooAdaptTextBoxView.tsx index 93528bdea..a15118afc 100644 --- a/packages/react/src/modules/editor/components/Editor/CantooAdaptTextBoxView.tsx +++ b/packages/react/src/modules/editor/components/Editor/CantooAdaptTextBoxView.tsx @@ -22,6 +22,8 @@ const CantooAdaptTextBoxView = ({ inline: 'nearest', }); } + // Run only on mount: scroll the box into view once when it opens. + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( diff --git a/packages/react/src/modules/editor/components/EditorToolbar/EditorToolbar.tsx b/packages/react/src/modules/editor/components/EditorToolbar/EditorToolbar.tsx index 44ed1bf08..e496249a7 100644 --- a/packages/react/src/modules/editor/components/EditorToolbar/EditorToolbar.tsx +++ b/packages/react/src/modules/editor/components/EditorToolbar/EditorToolbar.tsx @@ -438,6 +438,7 @@ export const EditorToolbar = ({ }, [ alignmentOptions, canRecognizeSpeech, + cantooEditor, editor, listOptions, mediaLibraryRef, diff --git a/packages/react/src/modules/modals/OnboardingModal/useOnboardingModal.ts b/packages/react/src/modules/modals/OnboardingModal/useOnboardingModal.ts index 68d24dfbc..cc3c0dcfc 100644 --- a/packages/react/src/modules/modals/OnboardingModal/useOnboardingModal.ts +++ b/packages/react/src/modules/modals/OnboardingModal/useOnboardingModal.ts @@ -36,6 +36,9 @@ export const useOnboardingModal = ( state.current = undefined; } })(); + // Run only on mount: read the stored preference once to decide whether to + // start the onboarding. Re-running on prop changes would re-open the modal. + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const handleSavePreference = useCallback(async () => { diff --git a/packages/react/src/modules/multimedia/WorkspaceFolders/WorkspaceFolders.tsx b/packages/react/src/modules/multimedia/WorkspaceFolders/WorkspaceFolders.tsx index 631cecbc9..1282b122b 100644 --- a/packages/react/src/modules/multimedia/WorkspaceFolders/WorkspaceFolders.tsx +++ b/packages/react/src/modules/multimedia/WorkspaceFolders/WorkspaceFolders.tsx @@ -48,6 +48,9 @@ export default function WorkspaceFolders({ onFolderSelected(selectedFolderIdForAPI, canCopyFileInto); } + // Intentionally react only to a change of the selected folder. The callback + // and helper below are merely invoked here, not meant to re-trigger it. + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedFolderId]); const handleSearchChange = (e: ChangeEvent) => { diff --git a/packages/rest-client-base/package.json b/packages/rest-client-base/package.json index d4f4d88ff..4ad7e082f 100644 --- a/packages/rest-client-base/package.json +++ b/packages/rest-client-base/package.json @@ -37,7 +37,7 @@ "clean": "rm -rf dist node_modules", "format": "prettier --check src", "format:write": "prettier --write src", - "lint": "eslint src", + "lint": "eslint src --max-warnings 0", "lint:fix": "eslint src --fix", "build": "pnpm run build:node && pnpm run build:browser && pnpm run build:react-native", "build:node": "vite build --config vite.config.node.ts && tsc src/bin/cli.ts --outDir dist/node --module commonjs --target ES2020 && mv dist/node/cli.js dist/node/cli.cjs", diff --git a/packages/utilities/package.json b/packages/utilities/package.json index 877648ebc..c635c5780 100644 --- a/packages/utilities/package.json +++ b/packages/utilities/package.json @@ -38,7 +38,7 @@ "fix": "eslint --fix --report-unused-disable-directives --max-warnings 0", "format": "prettier --check \"src/**/*.ts\"", "format:write": "prettier --write \"src/**/*.ts\"", - "lint": "eslint" + "lint": "eslint --max-warnings 0" }, "devDependencies": { "@types/node": "catalog:", From 0461f8d61d767cc70779be7147b94a10ba4aa182 Mon Sep 17 00:00:00 2001 From: "Pascal S." Date: Tue, 16 Jun 2026 16:31:57 +0200 Subject: [PATCH 2/3] chore: remove deprecated husky v9 boilerplate from pre-commit hook The shebang and husky.sh sourcing lines are deprecated since husky v9 and will fail in v10. Keep only the hook command. Co-Authored-By: Claude Opus 4.8 (1M context) --- .husky/pre-commit | 3 --- 1 file changed, 3 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 43f88f01f..fb24d7c1a 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,6 +1,3 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - # Icons guard: when an icon SVG is added, modified or deleted, ensure the # generated components are in sync. Same check as CI (see # .github/workflows/icons-check.yml). From 746051f6f1af15137e9890c3d78fc518e6dc2f90 Mon Sep 17 00:00:00 2001 From: "Pascal S." Date: Mon, 20 Jul 2026 22:30:32 +0200 Subject: [PATCH 3/3] fix: #ENABLING-938 corrige le JSON invalide de client/package.json et les warnings ESLint post-rebase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le rebase avait laissé une virgule manquante dans packages/client/package.json (JSON invalide) et réintroduit 2 warnings ESLint dans PageLayoutOverlay.tsx, faisant échouer le lint --max-warnings 0. --- packages/client/package.json | 4 ++-- .../components/PageLayout/components/PageLayoutOverlay.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/client/package.json b/packages/client/package.json index fc75cb243..2fd1f2763 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -40,7 +40,7 @@ "format": "prettier --check \"src/ts/**/*.ts\"", "format:write": "prettier --write \"src/ts/**/*.ts\"", "lint": "eslint --max-warnings 0", - "test": "vitest" + "test": "vitest", "test:coverage": "vitest run --coverage" }, "dependencies": { @@ -61,4 +61,4 @@ "vite": "catalog:", "vite-plugin-dts": "catalog:" } -} +} \ No newline at end of file diff --git a/packages/react/src/components/PageLayout/components/PageLayoutOverlay.tsx b/packages/react/src/components/PageLayout/components/PageLayoutOverlay.tsx index 76cd9a33d..2d249dbe0 100644 --- a/packages/react/src/components/PageLayout/components/PageLayoutOverlay.tsx +++ b/packages/react/src/components/PageLayout/components/PageLayoutOverlay.tsx @@ -1,6 +1,7 @@ import { type ComponentPropsWithoutRef, type ReactNode, + useCallback, useEffect, } from 'react'; @@ -30,10 +31,10 @@ const PageLayoutOverlay = ({ const { t } = useTranslation(); const { isOverlayOpen, updateOverlayOpen } = useOverlay(); - const handleClose = () => { + const handleClose = useCallback(() => { updateOverlayOpen(false); onClose?.(); - }; + }, [onClose, updateOverlayOpen]); useEffect(() => { if (!isOverlayOpen) return; @@ -60,7 +61,6 @@ const PageLayoutOverlay = ({ className, )} aria-hidden={!isOverlayOpen} - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore — inert is not yet in React's HTMLAttributes but is valid HTML inert={!isOverlayOpen ? '' : undefined} {...props}