🚨(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);
|
.filter((index) => index !== -1);
|
||||||
|
|
||||||
switch (event.key) {
|
switch (event.key) {
|
||||||
case 'ArrowDown':
|
case 'ArrowDown': {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const nextIndex =
|
const nextIndex =
|
||||||
focusedIndex < enabledIndices.length - 1 ? focusedIndex + 1 : 0;
|
focusedIndex < enabledIndices.length - 1 ? focusedIndex + 1 : 0;
|
||||||
@@ -40,8 +40,9 @@ export const useDropdownKeyboardNav = ({
|
|||||||
setFocusedIndex(nextIndex);
|
setFocusedIndex(nextIndex);
|
||||||
menuItemRefs.current[nextEnabledIndex]?.focus();
|
menuItemRefs.current[nextEnabledIndex]?.focus();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'ArrowUp':
|
case 'ArrowUp': {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const prevIndex =
|
const prevIndex =
|
||||||
focusedIndex > 0 ? focusedIndex - 1 : enabledIndices.length - 1;
|
focusedIndex > 0 ? focusedIndex - 1 : enabledIndices.length - 1;
|
||||||
@@ -49,9 +50,10 @@ export const useDropdownKeyboardNav = ({
|
|||||||
setFocusedIndex(prevIndex);
|
setFocusedIndex(prevIndex);
|
||||||
menuItemRefs.current[prevEnabledIndex]?.focus();
|
menuItemRefs.current[prevEnabledIndex]?.focus();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'Enter':
|
case 'Enter':
|
||||||
case ' ':
|
case ' ': {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (focusedIndex >= 0 && focusedIndex < enabledIndices.length) {
|
if (focusedIndex >= 0 && focusedIndex < enabledIndices.length) {
|
||||||
const selectedOptionIndex = enabledIndices[focusedIndex];
|
const selectedOptionIndex = enabledIndices[focusedIndex];
|
||||||
@@ -62,6 +64,7 @@ export const useDropdownKeyboardNav = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'Escape':
|
case 'Escape':
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ export const QuickSearchInput = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Command.Input
|
<Command.Input
|
||||||
/* eslint-disable-next-line jsx-a11y/no-autofocus */
|
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
aria-label={t('Quick search input')}
|
aria-label={t('Quick search input')}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
|
|
||||||
declare module '*.svg' {
|
declare module '*.svg' {
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ type ItemProps = Omit<ItemDefault, 'onClick'> & {
|
|||||||
onClick: (e: React.MouseEvent) => void;
|
onClick: (e: React.MouseEvent) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface AIMenuItemTransform {
|
interface AIMenuItemTransformProps {
|
||||||
action: AITransformActions;
|
action: AITransformActions;
|
||||||
docId: string;
|
docId: string;
|
||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
@@ -216,7 +216,7 @@ const AIMenuItemTransform = ({
|
|||||||
action,
|
action,
|
||||||
children,
|
children,
|
||||||
icon,
|
icon,
|
||||||
}: PropsWithChildren<AIMenuItemTransform>) => {
|
}: PropsWithChildren<AIMenuItemTransformProps>) => {
|
||||||
const { mutateAsync: requestAI, isPending } = useDocAITransform();
|
const { mutateAsync: requestAI, isPending } = useDocAITransform();
|
||||||
const editor = useBlockNoteEditor();
|
const editor = useBlockNoteEditor();
|
||||||
|
|
||||||
@@ -244,7 +244,7 @@ const AIMenuItemTransform = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface AIMenuItemTranslate {
|
interface AIMenuItemTranslateProps {
|
||||||
language: string;
|
language: string;
|
||||||
docId: string;
|
docId: string;
|
||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
@@ -255,7 +255,7 @@ const AIMenuItemTranslate = ({
|
|||||||
docId,
|
docId,
|
||||||
icon,
|
icon,
|
||||||
language,
|
language,
|
||||||
}: PropsWithChildren<AIMenuItemTranslate>) => {
|
}: PropsWithChildren<AIMenuItemTranslateProps>) => {
|
||||||
const { mutateAsync: requestAI, isPending } = useDocAITranslate();
|
const { mutateAsync: requestAI, isPending } = useDocAITranslate();
|
||||||
const editor = useBlockNoteEditor();
|
const editor = useBlockNoteEditor();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable react-hooks/rules-of-hooks */
|
|
||||||
import { createReactInlineContentSpec } from '@blocknote/react';
|
import { createReactInlineContentSpec } from '@blocknote/react';
|
||||||
import { TFunction } from 'i18next';
|
import { TFunction } from 'i18next';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable react-hooks/rules-of-hooks */
|
|
||||||
import {
|
import {
|
||||||
PartialCustomInlineContentFromConfig,
|
PartialCustomInlineContentFromConfig,
|
||||||
StyleSchema,
|
StyleSchema,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable jsx-a11y/alt-text */
|
|
||||||
import { DefaultProps } from '@blocknote/core';
|
import { DefaultProps } from '@blocknote/core';
|
||||||
import { Image, Text, View } from '@react-pdf/renderer';
|
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 { Image, Link, Text } from '@react-pdf/renderer';
|
||||||
|
|
||||||
import DocSelectedIcon from '../assets/doc-selected.png';
|
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 { useTreeContext } from '@gouvfr-lasuite/ui-kit';
|
||||||
import { Tooltip } from '@openfun/cunningham-react';
|
import { Tooltip } from '@openfun/cunningham-react';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
|||||||
@@ -65,24 +65,20 @@ export function useDuplicateDoc(options?: DuplicateDocOptions) {
|
|||||||
|
|
||||||
return useMutation<DuplicateDocResponse, APIError, DuplicateDocParams>({
|
return useMutation<DuplicateDocResponse, APIError, DuplicateDocParams>({
|
||||||
mutationFn: async (variables) => {
|
mutationFn: async (variables) => {
|
||||||
try {
|
// Save the document if we can first, to ensure the latest state is duplicated
|
||||||
// Save the document if we can first, to ensure the latest state is duplicated
|
const canSave =
|
||||||
if (
|
variables.canSave &&
|
||||||
variables.canSave &&
|
provider &&
|
||||||
provider &&
|
provider.document.guid === variables.docId;
|
||||||
provider.document.guid === variables.docId
|
|
||||||
) {
|
|
||||||
await updateDoc({
|
|
||||||
id: variables.docId,
|
|
||||||
content: toBase64(Y.encodeStateAsUpdate(provider.document)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return await duplicateDoc(variables);
|
if (canSave) {
|
||||||
} catch (error) {
|
await updateDoc({
|
||||||
// If save fails, throw the error to prevent duplication
|
id: variables.docId,
|
||||||
throw error;
|
content: toBase64(Y.encodeStateAsUpdate(provider.document)),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return await duplicateDoc(variables);
|
||||||
},
|
},
|
||||||
onSuccess: (data, variables, context) => {
|
onSuccess: (data, variables, context) => {
|
||||||
void queryClient.resetQueries({
|
void queryClient.resetQueries({
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export const DocTree = ({ currentDoc }: DocTreeProps) => {
|
|||||||
docId: currentDoc.id,
|
docId: currentDoc.id,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
enabled: !!!treeContext?.root?.id,
|
enabled: !treeContext?.root?.id,
|
||||||
queryKey: [KEY_DOC_TREE, { id: currentDoc.id }],
|
queryKey: [KEY_DOC_TREE, { id: currentDoc.id }],
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { Doc, DocsResponse } from '@/docs/doc-management';
|
|||||||
|
|
||||||
import { RequestData, RequestSerializer } from './RequestSerializer';
|
import { RequestData, RequestSerializer } from './RequestSerializer';
|
||||||
|
|
||||||
// eslint-disable-next-line import/order
|
|
||||||
import pkg from '@/../package.json';
|
import pkg from '@/../package.json';
|
||||||
|
|
||||||
export type DBRequest = {
|
export type DBRequest = {
|
||||||
|
|||||||
@@ -129,7 +129,6 @@ describe('ApiPlugin', () => {
|
|||||||
const request = await apiPlugin.requestWillFetch?.(requestInit);
|
const request = await apiPlugin.requestWillFetch?.(requestInit);
|
||||||
|
|
||||||
if (withClone) {
|
if (withClone) {
|
||||||
// eslint-disable-next-line jest/no-conditional-expect
|
|
||||||
expect(mockedClone).toHaveBeenCalled();
|
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
|
* API routes
|
||||||
*/
|
*/
|
||||||
@@ -45,8 +48,7 @@ registerRoute(
|
|||||||
);
|
);
|
||||||
|
|
||||||
registerRoute(
|
registerRoute(
|
||||||
({ url }) =>
|
({ url }) => isDocumentApiUrl(url),
|
||||||
isApiUrl(url.href) && url.href.match(/.*\/documents\/([a-z0-9\-]+)\/$/g),
|
|
||||||
new NetworkOnly({
|
new NetworkOnly({
|
||||||
plugins: [
|
plugins: [
|
||||||
new ApiPlugin({
|
new ApiPlugin({
|
||||||
@@ -61,8 +63,7 @@ registerRoute(
|
|||||||
);
|
);
|
||||||
|
|
||||||
registerRoute(
|
registerRoute(
|
||||||
({ url }) =>
|
({ url }) => isDocumentApiUrl(url),
|
||||||
isApiUrl(url.href) && url.href.match(/.*\/documents\/([a-z0-9\-]+)\/$/g),
|
|
||||||
new NetworkOnly({
|
new NetworkOnly({
|
||||||
plugins: [
|
plugins: [
|
||||||
new ApiPlugin({
|
new ApiPlugin({
|
||||||
@@ -90,8 +91,7 @@ registerRoute(
|
|||||||
);
|
);
|
||||||
|
|
||||||
registerRoute(
|
registerRoute(
|
||||||
({ url }) =>
|
({ url }) => isDocumentApiUrl(url),
|
||||||
isApiUrl(url.href) && url.href.match(/.*\/documents\/([a-z0-9\-]+)\/$/g),
|
|
||||||
new NetworkOnly({
|
new NetworkOnly({
|
||||||
plugins: [
|
plugins: [
|
||||||
new ApiPlugin({
|
new ApiPlugin({
|
||||||
|
|||||||
@@ -18,13 +18,11 @@ import {
|
|||||||
StrategyOptions,
|
StrategyOptions,
|
||||||
} from 'workbox-strategies';
|
} from 'workbox-strategies';
|
||||||
|
|
||||||
// eslint-disable-next-line import/order
|
|
||||||
import { DAYS_EXP, SW_DEV_URL, SW_VERSION, getCacheNameVersion } from './conf';
|
import { DAYS_EXP, SW_DEV_URL, SW_VERSION, getCacheNameVersion } from './conf';
|
||||||
import { ApiPlugin } from './plugins/ApiPlugin';
|
import { ApiPlugin } from './plugins/ApiPlugin';
|
||||||
import { OfflinePlugin } from './plugins/OfflinePlugin';
|
import { OfflinePlugin } from './plugins/OfflinePlugin';
|
||||||
import { isApiUrl } from './service-worker-api';
|
import { isApiUrl } from './service-worker-api';
|
||||||
|
|
||||||
// eslint-disable-next-line import/order
|
|
||||||
import pkg from '@/../package.json';
|
import pkg from '@/../package.json';
|
||||||
|
|
||||||
declare const self: ServiceWorkerGlobalScope & {
|
declare const self: ServiceWorkerGlobalScope & {
|
||||||
@@ -130,12 +128,14 @@ setCatchHandler(async ({ request, url, event }) => {
|
|||||||
case isApiUrl(url.href):
|
case isApiUrl(url.href):
|
||||||
return ApiPlugin.getApiCatchHandler();
|
return ApiPlugin.getApiCatchHandler();
|
||||||
|
|
||||||
case request.destination === 'document':
|
case request.destination === 'document': {
|
||||||
if (url.pathname.match(/^\/docs\/([a-z0-9\-]+)\/$/g)) {
|
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.docs });
|
||||||
}
|
}
|
||||||
|
|
||||||
return precacheStrategy.handle({ event, request: FALLBACK.offline });
|
return precacheStrategy.handle({ event, request: FALLBACK.offline });
|
||||||
|
}
|
||||||
|
|
||||||
case request.destination === 'image':
|
case request.destination === 'image':
|
||||||
return precacheStrategy.handle({ event, request: FALLBACK.images });
|
return precacheStrategy.handle({ event, request: FALLBACK.images });
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export const isValidEmail = (email: string) => {
|
export const isValidEmail = (email: string) => {
|
||||||
return !!email.match(
|
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,}))$/,
|
/^(([^<>()[\]\\.,;:\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