🚨(docs) upgrade eslint to v9 with Docs app
We upgraded ESLint to version 9 in the Docs app, which includes several improvements and fixes. This change also involves updating the ESLint configuration files to the new format and ensuring compatibility with the latest ESLint features.
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['impress/next'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
settings: {
|
||||
next: {
|
||||
rootDir: __dirname,
|
||||
},
|
||||
},
|
||||
ignorePatterns: ['node_modules', '.eslintrc.js', 'service-worker.js'],
|
||||
};
|
||||
24
src/frontend/apps/impress/eslint.config.mjs
Normal file
24
src/frontend/apps/impress/eslint.config.mjs
Normal file
@@ -0,0 +1,24 @@
|
||||
import { defineConfig } from '@eslint/config-helpers';
|
||||
import docsPlugin from 'eslint-plugin-docs';
|
||||
|
||||
const eslintConfig = defineConfig([
|
||||
{
|
||||
plugins: {
|
||||
docs: docsPlugin,
|
||||
},
|
||||
extends: ['docs/next'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
next: {
|
||||
rootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
export default eslintConfig;
|
||||
@@ -32,7 +32,7 @@ export const useDropdownKeyboardNav = ({
|
||||
.filter((index) => index !== -1);
|
||||
|
||||
switch (event.key) {
|
||||
case 'ArrowDown':
|
||||
case 'ArrowDown': {
|
||||
event.preventDefault();
|
||||
const nextIndex =
|
||||
focusedIndex < enabledIndices.length - 1 ? focusedIndex + 1 : 0;
|
||||
@@ -40,8 +40,9 @@ export const useDropdownKeyboardNav = ({
|
||||
setFocusedIndex(nextIndex);
|
||||
menuItemRefs.current[nextEnabledIndex]?.focus();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ArrowUp':
|
||||
case 'ArrowUp': {
|
||||
event.preventDefault();
|
||||
const prevIndex =
|
||||
focusedIndex > 0 ? focusedIndex - 1 : enabledIndices.length - 1;
|
||||
@@ -49,9 +50,10 @@ export const useDropdownKeyboardNav = ({
|
||||
setFocusedIndex(prevIndex);
|
||||
menuItemRefs.current[prevEnabledIndex]?.focus();
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Enter':
|
||||
case ' ':
|
||||
case ' ': {
|
||||
event.preventDefault();
|
||||
if (focusedIndex >= 0 && focusedIndex < enabledIndices.length) {
|
||||
const selectedOptionIndex = enabledIndices[focusedIndex];
|
||||
@@ -62,6 +64,7 @@ export const useDropdownKeyboardNav = ({
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Escape':
|
||||
event.preventDefault();
|
||||
|
||||
@@ -55,7 +55,6 @@ export const QuickSearchInput = ({
|
||||
</div>
|
||||
)}
|
||||
<Command.Input
|
||||
/* eslint-disable-next-line jsx-a11y/no-autofocus */
|
||||
autoFocus={true}
|
||||
aria-label={t('Quick search input')}
|
||||
onClick={(e) => {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
|
||||
declare module '*.svg' {
|
||||
import * as React from 'react';
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ type ItemProps = Omit<ItemDefault, 'onClick'> & {
|
||||
onClick: (e: React.MouseEvent) => void;
|
||||
};
|
||||
|
||||
interface AIMenuItemTransform {
|
||||
interface AIMenuItemTransformProps {
|
||||
action: AITransformActions;
|
||||
docId: string;
|
||||
icon?: ReactNode;
|
||||
@@ -216,7 +216,7 @@ const AIMenuItemTransform = ({
|
||||
action,
|
||||
children,
|
||||
icon,
|
||||
}: PropsWithChildren<AIMenuItemTransform>) => {
|
||||
}: PropsWithChildren<AIMenuItemTransformProps>) => {
|
||||
const { mutateAsync: requestAI, isPending } = useDocAITransform();
|
||||
const editor = useBlockNoteEditor();
|
||||
|
||||
@@ -244,7 +244,7 @@ const AIMenuItemTransform = ({
|
||||
);
|
||||
};
|
||||
|
||||
interface AIMenuItemTranslate {
|
||||
interface AIMenuItemTranslateProps {
|
||||
language: string;
|
||||
docId: string;
|
||||
icon?: ReactNode;
|
||||
@@ -255,7 +255,7 @@ const AIMenuItemTranslate = ({
|
||||
docId,
|
||||
icon,
|
||||
language,
|
||||
}: PropsWithChildren<AIMenuItemTranslate>) => {
|
||||
}: PropsWithChildren<AIMenuItemTranslateProps>) => {
|
||||
const { mutateAsync: requestAI, isPending } = useDocAITranslate();
|
||||
const editor = useBlockNoteEditor();
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { createReactInlineContentSpec } from '@blocknote/react';
|
||||
import { TFunction } from 'i18next';
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import {
|
||||
PartialCustomInlineContentFromConfig,
|
||||
StyleSchema,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable jsx-a11y/alt-text */
|
||||
import { DefaultProps } from '@blocknote/core';
|
||||
import { Image, Text, View } from '@react-pdf/renderer';
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable jsx-a11y/alt-text */
|
||||
import { Image, Link, Text } from '@react-pdf/renderer';
|
||||
|
||||
import DocSelectedIcon from '../assets/doc-selected.png';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
|
||||
import { useTreeContext } from '@gouvfr-lasuite/ui-kit';
|
||||
import { Tooltip } from '@openfun/cunningham-react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
@@ -65,24 +65,20 @@ export function useDuplicateDoc(options?: DuplicateDocOptions) {
|
||||
|
||||
return useMutation<DuplicateDocResponse, APIError, DuplicateDocParams>({
|
||||
mutationFn: async (variables) => {
|
||||
try {
|
||||
// Save the document if we can first, to ensure the latest state is duplicated
|
||||
if (
|
||||
variables.canSave &&
|
||||
provider &&
|
||||
provider.document.guid === variables.docId
|
||||
) {
|
||||
await updateDoc({
|
||||
id: variables.docId,
|
||||
content: toBase64(Y.encodeStateAsUpdate(provider.document)),
|
||||
});
|
||||
}
|
||||
// Save the document if we can first, to ensure the latest state is duplicated
|
||||
const canSave =
|
||||
variables.canSave &&
|
||||
provider &&
|
||||
provider.document.guid === variables.docId;
|
||||
|
||||
return await duplicateDoc(variables);
|
||||
} catch (error) {
|
||||
// If save fails, throw the error to prevent duplication
|
||||
throw error;
|
||||
if (canSave) {
|
||||
await updateDoc({
|
||||
id: variables.docId,
|
||||
content: toBase64(Y.encodeStateAsUpdate(provider.document)),
|
||||
});
|
||||
}
|
||||
|
||||
return await duplicateDoc(variables);
|
||||
},
|
||||
onSuccess: (data, variables, context) => {
|
||||
void queryClient.resetQueries({
|
||||
|
||||
@@ -43,7 +43,7 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
|
||||
docId: currentDoc.id,
|
||||
},
|
||||
{
|
||||
enabled: !!!treeContext?.root?.id,
|
||||
enabled: !treeContext?.root?.id,
|
||||
queryKey: [KEY_DOC_TREE, { id: currentDoc.id }],
|
||||
},
|
||||
);
|
||||
|
||||
@@ -4,7 +4,6 @@ import { Doc, DocsResponse } from '@/docs/doc-management';
|
||||
|
||||
import { RequestData, RequestSerializer } from './RequestSerializer';
|
||||
|
||||
// eslint-disable-next-line import/order
|
||||
import pkg from '@/../package.json';
|
||||
|
||||
export type DBRequest = {
|
||||
|
||||
@@ -129,7 +129,6 @@ describe('ApiPlugin', () => {
|
||||
const request = await apiPlugin.requestWillFetch?.(requestInit);
|
||||
|
||||
if (withClone) {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect(mockedClone).toHaveBeenCalled();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ export const isApiUrl = (href: string) => {
|
||||
);
|
||||
};
|
||||
|
||||
const isDocumentApiUrl = (url: URL) =>
|
||||
isApiUrl(url.href) && /.*\/documents\/([a-z0-9-]+)\/$/g.test(url.href);
|
||||
|
||||
/**
|
||||
* API routes
|
||||
*/
|
||||
@@ -45,8 +48,7 @@ registerRoute(
|
||||
);
|
||||
|
||||
registerRoute(
|
||||
({ url }) =>
|
||||
isApiUrl(url.href) && url.href.match(/.*\/documents\/([a-z0-9\-]+)\/$/g),
|
||||
({ url }) => isDocumentApiUrl(url),
|
||||
new NetworkOnly({
|
||||
plugins: [
|
||||
new ApiPlugin({
|
||||
@@ -61,8 +63,7 @@ registerRoute(
|
||||
);
|
||||
|
||||
registerRoute(
|
||||
({ url }) =>
|
||||
isApiUrl(url.href) && url.href.match(/.*\/documents\/([a-z0-9\-]+)\/$/g),
|
||||
({ url }) => isDocumentApiUrl(url),
|
||||
new NetworkOnly({
|
||||
plugins: [
|
||||
new ApiPlugin({
|
||||
@@ -90,8 +91,7 @@ registerRoute(
|
||||
);
|
||||
|
||||
registerRoute(
|
||||
({ url }) =>
|
||||
isApiUrl(url.href) && url.href.match(/.*\/documents\/([a-z0-9\-]+)\/$/g),
|
||||
({ url }) => isDocumentApiUrl(url),
|
||||
new NetworkOnly({
|
||||
plugins: [
|
||||
new ApiPlugin({
|
||||
|
||||
@@ -18,13 +18,11 @@ import {
|
||||
StrategyOptions,
|
||||
} from 'workbox-strategies';
|
||||
|
||||
// eslint-disable-next-line import/order
|
||||
import { DAYS_EXP, SW_DEV_URL, SW_VERSION, getCacheNameVersion } from './conf';
|
||||
import { ApiPlugin } from './plugins/ApiPlugin';
|
||||
import { OfflinePlugin } from './plugins/OfflinePlugin';
|
||||
import { isApiUrl } from './service-worker-api';
|
||||
|
||||
// eslint-disable-next-line import/order
|
||||
import pkg from '@/../package.json';
|
||||
|
||||
declare const self: ServiceWorkerGlobalScope & {
|
||||
@@ -130,12 +128,14 @@ setCatchHandler(async ({ request, url, event }) => {
|
||||
case isApiUrl(url.href):
|
||||
return ApiPlugin.getApiCatchHandler();
|
||||
|
||||
case request.destination === 'document':
|
||||
if (url.pathname.match(/^\/docs\/([a-z0-9\-]+)\/$/g)) {
|
||||
case request.destination === 'document': {
|
||||
const isDocPath = /^\/docs\/([a-z0-9-]+)\/$/g.test(url.pathname);
|
||||
if (isDocPath) {
|
||||
return precacheStrategy.handle({ event, request: FALLBACK.docs });
|
||||
}
|
||||
|
||||
return precacheStrategy.handle({ event, request: FALLBACK.offline });
|
||||
}
|
||||
|
||||
case request.destination === 'image':
|
||||
return precacheStrategy.handle({ event, request: FALLBACK.images });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const isValidEmail = (email: string) => {
|
||||
return !!email.match(
|
||||
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z\-0-9]{2,}))$/,
|
||||
);
|
||||
const EMAIL_REGEX =
|
||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z\-0-9]{2,}))$/;
|
||||
return EMAIL_REGEX.test(email);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user