diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..7cdff179 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,6 @@ +node_modules +**/.next/** +**/_next/** +**/dist/** +./packages/client/src/tiptap/next.config.js +./packages/client/src/tiptap/wrappers/mind/mind-elixir/iconfont/iconfont.js diff --git a/.eslintrc.client.js b/.eslintrc.client.js new file mode 100644 index 00000000..f2cdd8c3 --- /dev/null +++ b/.eslintrc.client.js @@ -0,0 +1,47 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 8, + sourceType: 'module', + ecmaFeatures: { + impliedStrict: true, + experimentalObjectRestSpread: true, + }, + allowImportExportEverywhere: true, + project: ['./packages/client/tsconfig.json'], + }, + plugins: ['@typescript-eslint', 'react-hooks'], + extends: ['eslint:recommended', 'plugin:react/recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], + settings: { + react: { + version: 'detect', + }, + }, + env: { + es6: true, + browser: true, + node: true, + }, + rules: { + 'func-names': 0, + 'no-shadow': 0, + '@typescript-eslint/no-shadow': 0, + '@typescript-eslint/explicit-function-return-type': 0, + '@typescript-eslint/no-unused-vars': [0, { argsIgnorePattern: '^_' }], + '@typescript-eslint/no-use-before-define': 0, + '@typescript-eslint/ban-ts-ignore': 0, + '@typescript-eslint/no-empty-function': 0, + '@typescript-eslint/ban-ts-comment': 0, + '@typescript-eslint/no-var-requires': 0, + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/no-this-alias': 0, + '@typescript-eslint/explicit-module-boundary-types': 0, + '@typescript-eslint/ban-types': 0, + 'react-hooks/rules-of-hooks': 2, + 'react-hooks/exhaustive-deps': 1, + 'react/prop-types': 0, + 'testing-library/no-unnecessary-act': 0, + 'react/react-in-jsx-scope': 0, + }, + ignorePatterns: ['dist/', 'node_modules', 'scripts', 'examples'], +}; diff --git a/.eslintrc.server.js b/.eslintrc.server.js new file mode 100644 index 00000000..93a5328d --- /dev/null +++ b/.eslintrc.server.js @@ -0,0 +1,19 @@ +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: './packages/server/tsconfig.json', + sourceType: 'module', + }, + plugins: ['@typescript-eslint/eslint-plugin'], + extends: ['plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], + root: true, + env: { + node: true, + jest: true, + }, + rules: { + '@typescript-eslint/interface-name-prefix': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-explicit-any': 'off', + }, +}; diff --git a/package.json b/package.json index e0fc34f8..c328fbaf 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,10 @@ "pm2": "pnpm run pm2:server && pnpm run pm2:client", "pm2:server": "pnpm run --dir packages/server pm2", "pm2:client": "pnpm run --dir packages/client pm2", + "lint:client": "eslint --fix './packages/client/**/*.{ts,tsx,js,jsx}' -c '.eslintrc.client.js'", + "lint:server": "eslint --fix './packages/server/src/*.{ts,js}' -c '.eslintrc.server.js'", "format": "concurrently \"pnpm:format:*\"", - "format:ts": "prettier --write --parser typescript \"git status\"", + "format:ts": "prettier --write --parser typescript \"packages/**/*.{ts,tsx,js,jsx}\"", "format:css": "stylelint --fix --formatter verbose --allow-empty-input \"packages/**/*.{css,scss,sass}\"", "prepare": "husky install", "precommit": "lint-staged" @@ -36,6 +38,16 @@ "node": ">=16.5.0" }, "devDependencies": { + "@typescript-eslint/eslint-plugin": "^5.21.0", + "@typescript-eslint/parser": "^5.21.0", + "eslint": "^8.14.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-react": "^7.29.4", + "eslint-plugin-react-hooks": "^4.5.0", + "eslint-plugin-simple-import-sort": "^7.0.0", "husky": "^7.0.4", "lint-staged": "^12.4.1", "prettier": "^2.3.2", diff --git a/packages/client/next.config.js b/packages/client/next.config.js index ea4a5c79..b5536209 100644 --- a/packages/client/next.config.js +++ b/packages/client/next.config.js @@ -1,10 +1,9 @@ -const semi = require('@douyinfe/semi-next').default({}); +/* eslint-disable */ const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin'); const { getConfig } = require('@think/config'); const config = getConfig(); -/** @type {import('next').NextConfig} */ -const nextConfig = semi({ +const nextConfig = require('@douyinfe/semi-next').default({})({ assetPrefix: config.assetPrefix, env: { SERVER_API_URL: config?.client?.apiUrl, diff --git a/packages/client/src/components/banner/index.tsx b/packages/client/src/components/banner/index.tsx index b1b53a43..3be20174 100644 --- a/packages/client/src/components/banner/index.tsx +++ b/packages/client/src/components/banner/index.tsx @@ -22,7 +22,7 @@ export const Banner: React.FC = ({ type, description, duration = 0 }) => return () => { clearTimeout(timer.current); }; - }, [duration]); + }, [duration, toggleVisible]); if (!visible) return null; diff --git a/packages/client/src/components/data-render/index.tsx b/packages/client/src/components/data-render/index.tsx index 365a40c1..0e3db44a 100644 --- a/packages/client/src/components/data-render/index.tsx +++ b/packages/client/src/components/data-render/index.tsx @@ -47,7 +47,7 @@ const defaultEmpty = () => { ); }; -const runRender = (fn, ...args) => (typeof fn === 'function' ? fn.apply(null, args) : fn); +const runRender = (fn, ...args) => (typeof fn === 'function' ? fn(...args) : fn); export const DataRender: React.FC = ({ loading, diff --git a/packages/client/src/components/data-render/loading.tsx b/packages/client/src/components/data-render/loading.tsx index 4260649b..dc4fae56 100644 --- a/packages/client/src/components/data-render/loading.tsx +++ b/packages/client/src/components/data-render/loading.tsx @@ -28,7 +28,7 @@ export const LoadingWrap: React.FC = ({ loading, delay = 200, loadingCon return () => { clearTimeout(timer.current); }; - }, [delay, loading]); + }, [delay, loading, toggleShowLoading]); if (loading) { return showLoading ? loadingContent : null; diff --git a/packages/client/src/components/document/collaboration/index.tsx b/packages/client/src/components/document/collaboration/index.tsx index 196a6c03..3efb130c 100644 --- a/packages/client/src/components/document/collaboration/index.tsx +++ b/packages/client/src/components/document/collaboration/index.tsx @@ -31,6 +31,7 @@ interface IProps { const { Paragraph } = Typography; const { Column } = Table; +// eslint-disable-next-line react/display-name const renderChecked = (onChange, authKey: 'readable' | 'editable') => (checked, docAuth) => { const handle = (evt) => { const data = { @@ -67,8 +68,8 @@ export const DocumentCollaboration: React.FC = ({ wikiId, documentId }) }; useEffect(() => { - const handler = (users) => { - const newCollaborationUsers = users + const handler = (mentionUsers) => { + const newCollaborationUsers = mentionUsers .filter(Boolean) .filter((state) => state.user) .map((state) => ({ ...state.user, clientId: state.clientId })); @@ -90,6 +91,7 @@ export const DocumentCollaboration: React.FC = ({ wikiId, documentId }) setCollaborationUsers(newCollaborationUsers); }; + event.on(JOIN_USER, handler); return () => { diff --git a/packages/client/src/components/document/comments/comments/Item/index.tsx b/packages/client/src/components/document/comments/comments/Item/index.tsx index 8bf09a2c..dd469068 100644 --- a/packages/client/src/components/document/comments/comments/Item/index.tsx +++ b/packages/client/src/components/document/comments/comments/Item/index.tsx @@ -16,10 +16,11 @@ interface IProps { const { Text } = Typography; export const CommentItem: React.FC = ({ comment, replyComment, editComment, deleteComment }) => { - if (!comment) return null; const { user } = useUser(); const { createUser = {} } = comment; + if (!comment) return null; + return (
diff --git a/packages/client/src/components/document/delete/index.tsx b/packages/client/src/components/document/delete/index.tsx index 68cce8c1..ba933986 100644 --- a/packages/client/src/components/document/delete/index.tsx +++ b/packages/client/src/components/document/delete/index.tsx @@ -33,7 +33,7 @@ export const DocumentDeletor: React.FC = ({ wikiId, documentId, onDelete okButtonProps: { loading, type: 'danger' }, style: { maxWidth: '96vw' }, }); - }, [wikiId, documentId, api, loading, onDelete]); + }, [wikiId, api, loading, onDelete]); return ( diff --git a/packages/client/src/components/document/editor/users.tsx b/packages/client/src/components/document/editor/users.tsx index fca51f0b..139222e8 100644 --- a/packages/client/src/components/document/editor/users.tsx +++ b/packages/client/src/components/document/editor/users.tsx @@ -15,6 +15,7 @@ interface IProps { const { Text } = Typography; const { Column } = Table; +// eslint-disable-next-line react/display-name const renderChecked = (onChange, authKey: 'readable' | 'editable') => (checked, data) => { const handle = (evt) => { const ret = { diff --git a/packages/client/src/components/document/reader/index.tsx b/packages/client/src/components/document/reader/index.tsx index cea3e66e..694d7e16 100644 --- a/packages/client/src/components/document/reader/index.tsx +++ b/packages/client/src/components/document/reader/index.tsx @@ -27,8 +27,6 @@ interface IProps { } export const DocumentReader: React.FC = ({ documentId }) => { - if (!documentId) return null; - const [container, setContainer] = useState(); const { width: windowWidth } = useWindowSize(); const { width, fontSize } = useDocumentStyle(); @@ -43,6 +41,8 @@ export const DocumentReader: React.FC = ({ documentId }) => { Router.push(`/wiki/${document.wikiId}/document/${document.id}/edit`); }, [document]); + if (!documentId) return null; + return (
diff --git a/packages/client/src/components/document/reader/public/content.tsx b/packages/client/src/components/document/reader/public/content.tsx index 2d9f8261..3b7c7aa2 100644 --- a/packages/client/src/components/document/reader/public/content.tsx +++ b/packages/client/src/components/document/reader/public/content.tsx @@ -12,7 +12,7 @@ interface IProps { export const DocumentContent: React.FC = ({ document, createUserContainerSelector }) => { const c = safeJSONParse(document.content); - let json = c.default || c; + const json = c.default || c; const editor = useEditor({ editable: false, diff --git a/packages/client/src/components/document/reader/public/index.tsx b/packages/client/src/components/document/reader/public/index.tsx index fc48fc70..936f91ed 100644 --- a/packages/client/src/components/document/reader/public/index.tsx +++ b/packages/client/src/components/document/reader/public/index.tsx @@ -37,8 +37,6 @@ interface IProps { } export const DocumentPublicReader: React.FC = ({ documentId, hideLogo = true }) => { - if (!documentId) return null; - const { data, loading, error, query } = usePublicDocument(documentId); const { width, fontSize } = useDocumentStyle(); const editorWrapClassNames = useMemo(() => { @@ -72,6 +70,8 @@ export const DocumentPublicReader: React.FC = ({ documentId, hideLogo = }); }, [error, query]); + if (!documentId) return null; + return (
diff --git a/packages/client/src/components/document/version/index.tsx b/packages/client/src/components/document/version/index.tsx index de178207..08acdb3f 100644 --- a/packages/client/src/components/document/version/index.tsx +++ b/packages/client/src/components/document/version/index.tsx @@ -32,7 +32,7 @@ export const DocumentVersion: React.FC = ({ documentId, onSelect }) => { const close = useCallback(() => { toggleVisible(false); setSelectedVersion(null); - }, []); + }, [toggleVisible]); const select = useCallback( (version) => { @@ -46,20 +46,20 @@ export const DocumentVersion: React.FC = ({ documentId, onSelect }) => { if (!selectedVersion || !onSelect) return; onSelect(safeJSONParse(selectedVersion.data, { default: {} }).default); close(); - }, [selectedVersion]); + }, [selectedVersion, close, onSelect]); useEffect(() => { if (visible) { refresh(); } - }, [visible]); + }, [visible, refresh]); useEffect(() => { if (!editor) return; if (!data.length) return; if (selectedVersion) return; select(data[0]); - }, [editor, data, selectedVersion]); + }, [editor, data, selectedVersion, select]); return ( <> diff --git a/packages/client/src/components/grid-select/grid-select.tsx b/packages/client/src/components/grid-select/grid-select.tsx index c4d1eff0..2ca58051 100644 --- a/packages/client/src/components/grid-select/grid-select.tsx +++ b/packages/client/src/components/grid-select/grid-select.tsx @@ -60,15 +60,12 @@ export const GridSelect = ({ [onSelect] ); - const onHover = useCallback( - debounce(({ x, y, isCellDisabled }) => { - if (isCellDisabled) { - return setHoverCell(null); - } - setHoverCell({ x, y }); - }, 5), - [disabled] - ); + const onHover = useCallback(({ x, y, isCellDisabled }) => { + if (isCellDisabled) { + return setHoverCell(null); + } + setHoverCell({ x, y }); + }, []); const cells = useMemo(() => { const cells = []; diff --git a/packages/client/src/components/message/index.tsx b/packages/client/src/components/message/index.tsx index 0ee9ae08..a030ccf8 100644 --- a/packages/client/src/components/message/index.tsx +++ b/packages/client/src/components/message/index.tsx @@ -40,7 +40,7 @@ const MessagesRender = ({ messageData, loading, error, onClick = null, page = 1, <> {messages.map((msg) => { return ( -
handleRead(msg.id)}> +
handleRead(msg.id)}>
@@ -140,7 +140,7 @@ const MessageBox = () => { readMessage(msg.id); }, }); - }, [unreadMsgs]); + }, [unreadMsgs, readMessage]); return ( = ({ }, }, }); - }, [maxWidth, isEditable]); + }, [maxWidth, isEditable, onChange, onChangeEnd]); useEffect(() => { Object.assign($container.current.style, { diff --git a/packages/client/src/components/template/editor/editor.tsx b/packages/client/src/components/template/editor/editor.tsx index 83a4ab96..0b380ddf 100644 --- a/packages/client/src/components/template/editor/editor.tsx +++ b/packages/client/src/components/template/editor/editor.tsx @@ -40,7 +40,6 @@ interface IProps { } export const Editor: React.FC = ({ user, data, loading, error, updateTemplate, deleteTemplate }) => { - if (!user) return null; const { width: windowWidth } = useWindowSize(); const [title, setTitle] = useState(data.title); const provider = useMemo(() => { @@ -51,7 +50,7 @@ export const Editor: React.FC = ({ user, data, loading, error, updateTem user, docType: 'template', }); - }, [data, user.token]); + }, [data, user]); const editor = useEditor({ editable: true, extensions: [...BaseKit, DocumentWithTitle, getCollaborationExtension(provider)], @@ -59,7 +58,9 @@ export const Editor: React.FC = ({ user, data, loading, error, updateTem try { const title = transaction.doc.content.firstChild.content.firstChild.textContent; setTitle(title); - } catch (e) {} + } catch (e) { + // + } }, }); const [isPublic, setPublic] = useState(false); @@ -76,7 +77,7 @@ export const Editor: React.FC = ({ user, data, loading, error, updateTem deleteTemplate().then(() => { goback(); }); - }, [deleteTemplate]); + }, [deleteTemplate, goback]); useEffect(() => { if (!data) return; @@ -99,6 +100,8 @@ export const Editor: React.FC = ({ user, data, loading, error, updateTem }; }, []); + if (!user) return null; + return (
diff --git a/packages/client/src/components/template/list/index.tsx b/packages/client/src/components/template/list/index.tsx index 6882fcff..0984320d 100644 --- a/packages/client/src/components/template/list/index.tsx +++ b/packages/client/src/components/template/list/index.tsx @@ -38,7 +38,7 @@ export const TemplateList: React.FC = ({ const start = (page - 1) * pageSize; const end = page * pageSize; return arr.slice(start, end); - }, [data, page]); + }, [data, page, pageSize]); return ( = ({ user, data, loading, error }) => { - if (!user) return null; - const c = safeJSONParse(data.content); let json = c.default || c; @@ -44,6 +42,8 @@ export const Editor: React.FC = ({ user, data, loading, error }) => { return width === 'standardWidth' ? styles.isStandardWidth : styles.isFullWidth; }, [width]); + if (!user) return null; + return (
diff --git a/packages/client/src/components/wiki/setting/documents/index.tsx b/packages/client/src/components/wiki/setting/documents/index.tsx index 3d51524a..015c1901 100644 --- a/packages/client/src/components/wiki/setting/documents/index.tsx +++ b/packages/client/src/components/wiki/setting/documents/index.tsx @@ -26,7 +26,7 @@ export const Documents: React.FC = ({ wikiId }) => { const [publicDocumentIds, setPublicDocumentIds] = useState([]); // 公开的 const privateDocumentIds = useMemo(() => { return documents.filter((doc) => !publicDocumentIds.includes(doc.id)).map((doc) => doc.id); - }, [tocs, publicDocumentIds]); + }, [documents, publicDocumentIds]); const submit = () => { const data = { nextStatus, publicDocumentIds, privateDocumentIds }; @@ -77,7 +77,7 @@ export const Documents: React.FC = ({ wikiId }) => { if (!documents.length) return; const activeIds = documents.filter((doc) => isPublicDocument(doc.status)).map((doc) => doc.id); setPublicDocumentIds(activeIds); - }, [tocs]); + }, [tocs, documents]); return (
@@ -115,7 +115,11 @@ export const Documents: React.FC = ({ wikiId }) => { setNextStatus(e.target.value)}> {WIKI_STATUS_LIST.map((status) => { - return {status.label}; + return ( + + {status.label} + + ); })}
diff --git a/packages/client/src/components/wiki/setting/users/add.tsx b/packages/client/src/components/wiki/setting/users/add.tsx index b504bf77..052d3d33 100644 --- a/packages/client/src/components/wiki/setting/users/add.tsx +++ b/packages/client/src/components/wiki/setting/users/add.tsx @@ -20,7 +20,7 @@ export const AddUser: React.FC = ({ visible, toggleVisible, onOk }) => { setUserName(''); toggleVisible(false); }); - }, [onOk, userName, userRole]); + }, [onOk, userName, userRole, toggleVisible]); return ( = ({ visible, toggleVisible, onOk }) => { = ({ visible, toggleVisible, onOk }) => setUserRole(WikiUserRole.normal); toggleVisible(false); }); - }, [onOk, userRole]); + }, [onOk, userRole, toggleVisible]); return ( = ({ visible, toggleVisible, onOk }) => ) : null}