MM-52624/MM-57094 Update ESLint and our ESLint plugin (#26398)

* Update ESLint and plugins

* Move most channels-specific ESLint configuration into ESLint plugin

* Add ESLint to types and client packages

* Add ESLint to components package
This commit is contained in:
Harrison Healey 2024-03-13 18:07:28 -04:00 committed by GitHub
parent 67f815e373
commit 4d03becdd1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
53 changed files with 1149 additions and 989 deletions

View File

@ -1,91 +1,16 @@
{ {
"root": true, "root": true,
"extends": [ "extends": [
"plugin:@mattermost/react", "plugin:@mattermost/react"
"plugin:react-hooks/recommended"
], ],
"plugins": [ "plugins": [
"@mattermost", "formatjs",
"import", "no-only-tests"
"no-only-tests",
"@typescript-eslint",
"formatjs"
], ],
"parser": "@typescript-eslint/parser",
"env": {
"jest": true
},
"settings": { "settings": {
"import/resolver": "webpack", "import/resolver": "webpack"
"react": {
"pragma": "React",
"version": "detect"
}
}, },
"rules": { "rules": {
"@mattermost/use-external-link": 2,
"header/header": [
2,
"line",
" Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.\n See LICENSE.txt for license information.",
2
],
"no-duplicate-imports": 0,
"import/no-duplicates": 2,
"max-nested-callbacks": ["error", 10],
"no-unused-expressions": 0,
"eol-last": ["error", "always"],
"import/no-unresolved": 2,
"import/order": [
2,
{
"newlines-between": "always",
"groups": [
"builtin",
"external",
"internal",
"sibling",
"parent",
"index"
],
"pathGroups": [
{
"pattern": "@mattermost/**",
"group": "external",
"position": "after"
},
{
"pattern": "mattermost-redux/**",
"group": "external",
"position": "after"
},
{
"pattern": "@(selectors|actions|stores|store|reducers){,/**}",
"group": "external",
"position": "after"
},
{
"pattern": "components/**",
"group": "external",
"position": "after"
},
{
"pattern": "types{,/**}",
"group": "internal",
"position": "after"
}
],
"alphabetize": {
"order": "asc",
"caseInsensitive": true
},
"distinctGroup": true,
"pathGroupsExcludedImportTypes": ["builtin"]
}
],
"no-undefined": 0,
"no-use-before-define": 0,
"react/jsx-filename-extension": 0,
"react/prop-types": [ "react/prop-types": [
2, 2,
{ {
@ -104,116 +29,16 @@
] ]
} }
], ],
"react/no-string-refs": 2,
"no-only-tests/no-only-tests": ["error", {"focus": ["only", "skip"]}],
"react/style-prop-object": [2, { "react/style-prop-object": [2, {
"allow": ["Timestamp"] "allow": ["Timestamp"]
}], }],
"no-restricted-imports": [ "formatjs/no-multiple-whitespaces": 2
"error",
{
"patterns": [
{
"group": ["@mattermost/compass-components/*"],
"message": "compass-components is now archived."
}
],
"paths": [
{
"name": "react-bootstrap",
"importNames": ["OverlayTrigger"],
"message": "Use OverlayTrigger from '/components/overlay_trigger' instead."
},
{
"name": "redux",
"importNames": ["DeepPartial"],
"message": "Use DeepPartial from '@mattermost/types/utilities instead."
},
{
"name": "lodash",
"message": "Import individual functions from lodash/<function> instead."
}
]
}
],
"max-lines": ["warn", {"max": 800, "skipBlankLines": true, "skipComments": true}],
"formatjs/no-multiple-whitespaces": 2,
"@typescript-eslint/consistent-type-imports": ["error", {"disallowTypeAnnotations": false}]
}, },
"overrides": [ "overrides": [
{ {
"files": ["**/*.tsx", "**/*.ts"], "files": ["*.test.*", "src/tests/**"],
"extends": [
"plugin:@typescript-eslint/recommended"
],
"rules": { "rules": {
"camelcase": 0, "no-only-tests/no-only-tests": ["error", {"focus": ["only", "skip"]}]
"no-shadow": 0,
"import/no-unresolved": 0, // ts handles this better
"@typescript-eslint/naming-convention": [
2,
{
"selector": "function",
"format": ["camelCase", "PascalCase"]
},
{
"selector": "variable",
"format": ["camelCase", "PascalCase", "UPPER_CASE"]
},
{
"selector": "parameter",
"format": ["camelCase", "PascalCase"],
"leadingUnderscore": "allow"
},
{
"selector": "typeLike",
"format": ["PascalCase"]
}
],
"@typescript-eslint/no-non-null-assertion": 0,
"@typescript-eslint/no-unused-vars": [
2,
{
"vars": "all",
"args": "after-used"
}
],
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/no-empty-function": 0,
"@typescript-eslint/prefer-interface": 0,
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/indent": [
2,
4,
{
"SwitchCase": 0
}
],
"@typescript-eslint/no-use-before-define": [
2,
{
"classes": false,
"functions": false,
"variables": false
}
]
}
},
{
"files": ["src/tests/**", "**/*.test.*", "src/tests/*.js", "src/packages/mattermost-redux/test/*"],
"env": {
"jest": true
},
"rules": {
"func-names": 0,
"global-require": 0,
"max-lines": 0,
"new-cap": 0,
"no-empty-function": 0,
"no-import-assign": 0,
"no-process-env": 0,
"prefer-arrow-callback": 0
} }
} }
] ]

View File

@ -141,8 +141,6 @@
"@types/shallow-equals": "1.0.3", "@types/shallow-equals": "1.0.3",
"@types/styled-components": "5.1.32", "@types/styled-components": "5.1.32",
"@types/tinycolor2": "1.4.3", "@types/tinycolor2": "1.4.3",
"@typescript-eslint/eslint-plugin": "5.57.1",
"@typescript-eslint/parser": "5.57.1",
"copy-webpack-plugin": "11.0.0", "copy-webpack-plugin": "11.0.0",
"emoji-datasource": "6.1.1", "emoji-datasource": "6.1.1",
"emoji-datasource-apple": "6.1.1", "emoji-datasource-apple": "6.1.1",
@ -150,11 +148,7 @@
"enzyme": "3.11.0", "enzyme": "3.11.0",
"enzyme-adapter-react-17-updated": "1.0.2", "enzyme-adapter-react-17-updated": "1.0.2",
"enzyme-to-json": "3.6.2", "enzyme-to-json": "3.6.2",
"eslint-plugin-header": "3.1.1",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-no-only-tests": "3.1.0", "eslint-plugin-no-only-tests": "3.1.0",
"eslint-plugin-react": "7.33.2",
"eslint-plugin-react-hooks": "4.6.0",
"external-remotes-plugin": "1.0.0", "external-remotes-plugin": "1.0.0",
"html-webpack-plugin": "5.5.0", "html-webpack-plugin": "5.5.0",
"identity-obj-proxy": "3.0.0", "identity-obj-proxy": "3.0.0",

View File

@ -287,7 +287,7 @@ describe('rhs view actions', () => {
jest.resetModules(); jest.resetModules();
const {submitCommand: remockedSubmitCommand} = require('actions/views/create_comment'); // eslint-disable-like @typescript-eslint/no-var-requires const {submitCommand: remockedSubmitCommand} = require('actions/views/create_comment');
await store.dispatch(remockedSubmitCommand(channelId, rootId, draft)); await store.dispatch(remockedSubmitCommand(channelId, rootId, draft));

View File

@ -283,7 +283,7 @@ describe('handleUserRemovedEvent', () => {
let redirectUserToDefaultTeam; let redirectUserToDefaultTeam;
beforeEach(async () => { beforeEach(async () => {
const globalActions = require('actions/global_actions'); // eslint-disable-line global-require const globalActions = require('actions/global_actions');
redirectUserToDefaultTeam = globalActions.redirectUserToDefaultTeam; redirectUserToDefaultTeam = globalActions.redirectUserToDefaultTeam;
redirectUserToDefaultTeam.mockReset(); redirectUserToDefaultTeam.mockReset();
}); });

916
webapp/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -36,9 +36,11 @@
"concurrently": "7.6.0", "concurrently": "7.6.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"css-loader": "6.7.3", "css-loader": "6.7.3",
"eslint": "8.37.0", "eslint": "8.57.0",
"eslint-import-resolver-webpack": "0.13.2", "eslint-import-resolver-webpack": "0.13.8",
"eslint-plugin-formatjs": "4.12.2", "eslint-plugin-formatjs": "4.12.2",
"eslint-plugin-react": "7.34.0",
"eslint-plugin-react-hooks": "4.6.0",
"mini-css-extract-plugin": "2.7.5", "mini-css-extract-plugin": "2.7.5",
"sass": "1.71.1", "sass": "1.71.1",
"sass-loader": "14.1.1", "sass-loader": "14.1.1",

View File

@ -0,0 +1,6 @@
{
"root": true,
"extends": [
"plugin:@mattermost/base"
]
}

View File

@ -21,6 +21,7 @@
"form-data": "^4.0.0" "form-data": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "28.1.8",
"jest": "27.1.0", "jest": "27.1.0",
"typescript": "^5.0.0" "typescript": "^5.0.0"
}, },
@ -34,10 +35,11 @@
} }
}, },
"scripts": { "scripts": {
"build": "tsc --build --verbose", "build": "tsc --build tsconfig.build.json --verbose",
"check": "eslint --ext .js,.jsx,.tsx,.ts ./src --quiet",
"run": "tsc --watch --preserveWatchOutput", "run": "tsc --watch --preserveWatchOutput",
"test": "jest", "test": "jest",
"test-ci": "jest --ci --forceExit --detectOpenHandles --maxWorkers=100%", "test-ci": "jest --ci --forceExit --detectOpenHandles --maxWorkers=100%",
"clean": "rm -rf lib node_modules tsconfig.tsbuildinfo" "clean": "rm -rf lib node_modules *.tsbuildinfo"
} }
} }

View File

@ -4,7 +4,7 @@
import nock from 'nock'; import nock from 'nock';
import Client4, {ClientError, HEADER_X_VERSION_ID} from './client4'; import Client4, {ClientError, HEADER_X_VERSION_ID} from './client4';
import {TelemetryHandler} from './telemetry'; import type {TelemetryHandler} from './telemetry';
describe('Client4', () => { describe('Client4', () => {
beforeAll(() => { beforeAll(() => {

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,6 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import Client4 from './client4'; import Client4 from './client4';
import {cleanUrlForLogging} from './errors'; import {cleanUrlForLogging} from './errors';
describe('cleanUrlForLogging', () => { describe('cleanUrlForLogging', () => {

View File

@ -0,0 +1,26 @@
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"target": "es2022",
"declaration": true,
"strict": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"jsx": "react",
"outDir": "./lib",
"rootDir": "./src",
"composite": true,
},
"include": [
"./src/**/*"
],
"exclude": [
"**/*.test.*"
],
"references": [
{"path": "../types"}
]
}

View File

@ -12,14 +12,11 @@
"jsx": "react", "jsx": "react",
"outDir": "./lib", "outDir": "./lib",
"rootDir": "./src", "rootDir": "./src",
"composite": true "composite": true,
}, },
"include": [ "include": [
"./src/**/*" "./src/**/*"
], ],
"exclude": [
"**/*.test.*"
],
"references": [ "references": [
{"path": "../types"} {"path": "../types"}
] ]

View File

@ -0,0 +1,6 @@
{
"root": true,
"extends": [
"plugin:@mattermost/base"
]
}

View File

@ -7,6 +7,7 @@
"styles": "dist/index.esm.css", "styles": "dist/index.esm.css",
"scripts": { "scripts": {
"build": "rollup -c", "build": "rollup -c",
"check": "eslint --ext .js,.jsx,.tsx,.ts ./src --quiet",
"run": "rollup -c --watch", "run": "rollup -c --watch",
"test": "cross-env TZ=Etc/UTC jest", "test": "cross-env TZ=Etc/UTC jest",
"test:updatesnapshot": "cross-env TZ=Etc/UTC jest --updateSnapshot", "test:updatesnapshot": "cross-env TZ=Etc/UTC jest --updateSnapshot",

View File

@ -1,7 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {MutableRefObject, useEffect} from 'react'; import type {MutableRefObject} from 'react';
import {useEffect} from 'react';
export function useClickOutsideRef(ref: MutableRefObject<HTMLElement | null>, handler: (event: MouseEvent) => void): void { export function useClickOutsideRef(ref: MutableRefObject<HTMLElement | null>, handler: (event: MouseEvent) => void): void {
useEffect(() => { useEffect(() => {

View File

@ -1,8 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {useLayoutEffect, useMemo, useState} from 'react';
import throttle from 'lodash/throttle'; import throttle from 'lodash/throttle';
import {useLayoutEffect, useMemo, useState} from 'react';
import {useElementAvailable} from './useElementAvailable'; import {useElementAvailable} from './useElementAvailable';

View File

@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React from 'react';
import {render, screen} from '@testing-library/react'; import {render, screen} from '@testing-library/react';
import React from 'react';
import {FooterPagination} from './footer_pagination'; import {FooterPagination} from './footer_pagination';
import {wrapIntl} from '../testUtils'
import {wrapIntl} from '../testUtils';
describe('LegacyGenericModal/FooterPagination', () => { describe('LegacyGenericModal/FooterPagination', () => {
const baseProps = { const baseProps = {
@ -28,7 +29,7 @@ describe('LegacyGenericModal/FooterPagination', () => {
page: 0, page: 0,
total: 17, total: 17,
itemsPerPage: 10, itemsPerPage: 10,
} };
render(wrapIntl(<FooterPagination {...props}/>)); render(wrapIntl(<FooterPagination {...props}/>));

View File

@ -1,10 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React from 'react';
import {render, screen} from '@testing-library/react'; import {render, screen} from '@testing-library/react';
import React from 'react';
import {GenericModal} from './generic_modal'; import {GenericModal} from './generic_modal';
import {wrapIntl} from '../testUtils'; import {wrapIntl} from '../testUtils';
describe('GenericModal', () => { describe('GenericModal', () => {

View File

@ -1,8 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import React from 'react';
import {Modal} from 'react-bootstrap'; import {Modal} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl'; import {FormattedMessage} from 'react-intl';
@ -73,7 +73,7 @@ export class GenericModal extends React.PureComponent<Props, State> {
onHide = () => { onHide = () => {
this.setState({show: false}); this.setState({show: false});
} };
handleCancel = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { handleCancel = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
event.preventDefault(); event.preventDefault();
@ -83,7 +83,7 @@ export class GenericModal extends React.PureComponent<Props, State> {
if (this.props.handleCancel) { if (this.props.handleCancel) {
this.props.handleCancel(); this.props.handleCancel();
} }
} };
handleConfirm = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => { handleConfirm = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
event.preventDefault(); event.preventDefault();
@ -93,7 +93,7 @@ export class GenericModal extends React.PureComponent<Props, State> {
if (this.props.handleConfirm) { if (this.props.handleConfirm) {
this.props.handleConfirm(); this.props.handleConfirm();
} }
} };
private onEnterKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => { private onEnterKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === 'Enter') { if (event.key === 'Enter') {
@ -108,7 +108,7 @@ export class GenericModal extends React.PureComponent<Props, State> {
} }
} }
this.props.handleKeydown?.(event); this.props.handleKeydown?.(event);
} };
render() { render() {
let confirmButton; let confirmButton;

View File

@ -3,7 +3,7 @@
import React from 'react'; import React from 'react';
import {Coords} from '../common/hooks/useMeasurePunchouts'; import type {Coords} from '../common/hooks/useMeasurePunchouts';
import './pulsating_dot.scss'; import './pulsating_dot.scss';

View File

@ -1,13 +1,14 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React, {ReactNode} from 'react' import type {ReactNode} from 'react';
import {IntlProvider, createIntl} from 'react-intl' import React from 'react';
import {IntlProvider, createIntl} from 'react-intl';
export const defaultIntl = createIntl({ export const defaultIntl = createIntl({
locale: 'en', locale: 'en',
defaultLocale: 'en', defaultLocale: 'en',
messages: {}, messages: {},
}) });
export const wrapIntl = (children?: ReactNode) => <IntlProvider {...defaultIntl}>{children}</IntlProvider> export const wrapIntl = (children?: ReactNode) => <IntlProvider {...defaultIntl}>{children}</IntlProvider>;

View File

@ -1,21 +1,22 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import React, {useRef, useState} from 'react';
import {FormattedMessage} from 'react-intl';
import Tippy from '@tippyjs/react'; import Tippy from '@tippyjs/react';
import {Placement} from 'tippy.js';
import classNames from 'classnames'; import classNames from 'classnames';
import React, {useRef} from 'react';
import {FormattedMessage} from 'react-intl';
import type {Placement} from 'tippy.js';
import {TourTipBackdrop} from './tour_tip_backdrop';
import type {Props as PunchOutCoordsHeightAndWidth} from '../common/hooks/useMeasurePunchouts'; import type {Props as PunchOutCoordsHeightAndWidth} from '../common/hooks/useMeasurePunchouts';
import {PulsatingDot} from '../pulsating_dot';
import 'tippy.js/dist/tippy.css'; import 'tippy.js/dist/tippy.css';
import 'tippy.js/themes/light-border.css'; import 'tippy.js/themes/light-border.css';
import 'tippy.js/animations/scale-subtle.css'; import 'tippy.js/animations/scale-subtle.css';
import 'tippy.js/animations/perspective-subtle.css'; import 'tippy.js/animations/perspective-subtle.css';
import {PulsatingDot} from '../pulsating_dot';
import {TourTipBackdrop} from './tour_tip_backdrop';
import './tour_tip.scss'; import './tour_tip.scss';
export type TourTipEventSource = 'next' | 'prev' | 'dismiss' | 'jump' | 'skipped' | 'open' | 'punchOut' export type TourTipEventSource = 'next' | 'prev' | 'dismiss' | 'jump' | 'skipped' | 'open' | 'punchOut'

View File

@ -1,10 +1,17 @@
{ {
"extends": [ "extends": [
"plugin:react/recommended" "plugin:react/recommended",
"plugin:react-hooks/recommended"
], ],
"plugins": [ "plugins": [
"react" "react"
], ],
"settings": {
"react": {
"pragma": "React",
"version": "detect"
}
},
"rules": { "rules": {
"react/display-name": [ "react/display-name": [
0, 0,
@ -39,7 +46,15 @@
2, 2,
"never" "never"
], ],
"react/jsx-filename-extension": 2, "react/jsx-filename-extension": [
2,
{
"extensions": [
".jsx",
".tsx"
]
}
],
"react/jsx-first-prop-new-line": [ "react/jsx-first-prop-new-line": [
2, 2,
"multiline" "multiline"
@ -101,7 +116,7 @@
], ],
"react/no-render-return-value": 2, "react/no-render-return-value": 2,
"react/no-set-state": 0, "react/no-set-state": 0,
"react/no-string-refs": 0, "react/no-string-refs": 2,
"react/no-unescaped-entities": 2, "react/no-unescaped-entities": 2,
"react/no-unknown-property": 2, "react/no-unknown-property": 2,
"react/no-unused-prop-types": [ "react/no-unused-prop-types": [

View File

@ -1,6 +1,7 @@
{ {
"extends": [ "extends": [
"eslint:recommended" "eslint:recommended",
"plugin:@typescript-eslint/recommended"
], ],
"parserOptions": { "parserOptions": {
"ecmaVersion": 8, "ecmaVersion": 8,
@ -11,9 +12,12 @@
"modules": true "modules": true
} }
}, },
"parser": "babel-eslint", "parser": "@typescript-eslint/parser",
"plugins": [ "plugins": [
"header" "@mattermost",
"@typescript-eslint",
"header",
"import"
], ],
"env": { "env": {
"browser": true, "browser": true,
@ -23,6 +27,59 @@
}, },
"rules": { "rules": {
"@mattermost/no-dispatch-getstate": 2, "@mattermost/no-dispatch-getstate": 2,
"@mattermost/use-external-link": 2,
"@typescript-eslint/array-type": [2, {"default": "array-simple"}],
"@typescript-eslint/consistent-type-imports": ["error", {"disallowTypeAnnotations": false}],
"@typescript-eslint/explicit-function-return-type": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/indent": [
2,
4,
{
"SwitchCase": 0
}
],
"@typescript-eslint/member-delimiter-style": 2,
"@typescript-eslint/naming-convention": [
2,
{
"selector": "function",
"format": ["camelCase", "PascalCase"]
},
{
"selector": "variable",
"format": ["camelCase", "PascalCase", "UPPER_CASE"]
},
{
"selector": "parameter",
"format": ["camelCase", "PascalCase"],
"leadingUnderscore": "allow"
},
{
"selector": "typeLike",
"format": ["PascalCase"]
}
],
"@typescript-eslint/no-dupe-class-members": 2,
"@typescript-eslint/no-empty-function": 0,
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": [
2,
{
"vars": "all",
"args": "after-used"
}
],
"@typescript-eslint/no-use-before-define": [
2,
{
"classes": false,
"functions": false,
"variables": false
}
],
"@typescript-eslint/no-var-requires": 0,
"@typescript-eslint/type-annotation-spacing": 2,
"array-bracket-spacing": [ "array-bracket-spacing": [
2, 2,
"never" "never"
@ -48,12 +105,7 @@
"allowSingleLine": false "allowSingleLine": false
} }
], ],
"camelcase": [ "camelcase": 0, // Handled by @typescript-eslint/naming-convention
2,
{
"properties": "never"
}
],
"capitalized-comments": 0, "capitalized-comments": 0,
"class-methods-use-this": 0, "class-methods-use-this": 0,
"comma-dangle": [ "comma-dangle": [
@ -94,6 +146,7 @@
"object" "object"
], ],
"dot-notation": 2, "dot-notation": 2,
"eol-last": ["error", "always"],
"eqeqeq": [ "eqeqeq": [
2, 2,
"smart" "smart"
@ -123,16 +176,60 @@
"header/header": [ "header/header": [
2, 2,
"line", "line",
" Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.\n See LICENSE.txt for license information." " Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.\n See LICENSE.txt for license information.",
2
], ],
"id-blacklist": 0, "id-blacklist": 0,
"indent": [ "import/no-duplicates": 2,
"import/no-unresolved": 0, // Handled better by TS
"import/order": [
2, 2,
4,
{ {
"SwitchCase": 0 "newlines-between": "always",
"groups": [
"builtin",
"external",
"internal",
"sibling",
"parent",
"index"
],
"pathGroups": [
{
"pattern": "@mattermost/**",
"group": "external",
"position": "after"
},
{
"pattern": "mattermost-redux/**",
"group": "external",
"position": "after"
},
{
"pattern": "@(selectors|actions|stores|store|reducers){,/**}",
"group": "external",
"position": "after"
},
{
"pattern": "components/**",
"group": "external",
"position": "after"
},
{
"pattern": "types{,/**}",
"group": "internal",
"position": "after"
} }
], ],
"alphabetize": {
"order": "asc",
"caseInsensitive": true
},
"distinctGroup": true,
"pathGroupsExcludedImportTypes": ["builtin"]
}
],
"indent": 0, // Handled by @typescript-eslint/indent
"jsx-quotes": [ "jsx-quotes": [
2, 2,
"prefer-single" "prefer-single"
@ -165,19 +262,14 @@
} }
], ],
"max-lines": [ "max-lines": [
1, "warn",
{ {
"max": 450, "max": 800,
"skipBlankLines": true, "skipBlankLines": true,
"skipComments": false "skipComments": false
} }
], ],
"max-nested-callbacks": [ "max-nested-callbacks": ["error", 10],
2,
{
"max": 2
}
],
"max-statements-per-line": [ "max-statements-per-line": [
2, 2,
{ {
@ -211,18 +303,13 @@
"no-debugger": 2, "no-debugger": 2,
"no-div-regex": 2, "no-div-regex": 2,
"no-dupe-args": 2, "no-dupe-args": 2,
"no-dupe-class-members": 2, "no-dupe-class-members": 0, // Handled by @typescript-eslint/no-dupe-class-members
"no-dupe-keys": 2, "no-dupe-keys": 2,
"no-duplicate-case": 2, "no-duplicate-case": 2,
"no-duplicate-imports": [ "no-duplicate-imports": 0, // Handled by import/no-duplicates
2,
{
"includeExports": true
}
],
"no-else-return": 2, "no-else-return": 2,
"no-empty": 2, "no-empty": 2,
"no-empty-function": 2, "no-empty-function": 0,
"no-empty-pattern": 2, "no-empty-pattern": 2,
"no-eval": 2, "no-eval": 2,
"no-ex-assign": 2, "no-ex-assign": 2,
@ -284,7 +371,33 @@
"no-process-exit": 2, "no-process-exit": 2,
"no-proto": 2, "no-proto": 2,
"no-prototype-builtins": 1, "no-prototype-builtins": 1,
"no-redeclare": 2, "no-restricted-imports": [
"error",
{
"patterns": [
{
"group": ["@mattermost/compass-components/*"],
"message": "compass-components is now archived."
}
],
"paths": [
{
"name": "react-bootstrap",
"importNames": ["OverlayTrigger"],
"message": "Use OverlayTrigger from '/components/overlay_trigger' instead."
},
{
"name": "redux",
"importNames": ["DeepPartial"],
"message": "Use DeepPartial from '@mattermost/types/utilities instead."
},
{
"name": "lodash",
"message": "Import individual functions from lodash/<function> instead."
}
]
}
],
"no-return-assign": [ "no-return-assign": [
2, 2,
"always" "always"
@ -299,12 +412,7 @@
], ],
"no-self-compare": 2, "no-self-compare": 2,
"no-sequences": 2, "no-sequences": 2,
"no-shadow": [ "no-shadow": 0, // This isn't currently enabled, but it probably should be
2,
{
"hoist": "functions"
}
],
"no-shadow-restricted-names": 2, "no-shadow-restricted-names": 2,
"no-spaced-func": 2, "no-spaced-func": 2,
"no-tabs": 0, "no-tabs": 0,
@ -319,7 +427,7 @@
} }
], ],
"no-undef-init": 2, "no-undef-init": 2,
"no-undefined": 2, "no-undefined": 0,
"no-underscore-dangle": 2, "no-underscore-dangle": 2,
"no-unexpected-multiline": 2, "no-unexpected-multiline": 2,
"no-unmodified-loop-condition": 2, "no-unmodified-loop-condition": 2,
@ -333,21 +441,8 @@
"no-unsafe-finally": 2, "no-unsafe-finally": 2,
"no-unsafe-negation": 2, "no-unsafe-negation": 2,
"no-unused-expressions": 2, "no-unused-expressions": 2,
"no-unused-vars": [ "no-unused-vars": 0, // Handled by @typescript-eslint/no-unused-vars
2, "no-use-before-define": 0, // Handled by @typescript-eslint/no-use-before-define
{
"vars": "all",
"args": "after-used"
}
],
"no-use-before-define": [
2,
{
"classes": false,
"functions": false,
"variables": false
}
],
"no-useless-computed-key": 2, "no-useless-computed-key": 2,
"no-useless-concat": 2, "no-useless-concat": 2,
"no-useless-constructor": 2, "no-useless-constructor": 2,
@ -475,14 +570,14 @@
"exceptRange": false, "exceptRange": false,
"onlyEquality": false "onlyEquality": false
} }
], ]
"@typescript-eslint/array-type": [2, {"default": "array-simple"}],
"@typescript-eslint/member-delimiter-style": 2,
"@typescript-eslint/type-annotation-spacing": 2
}, },
"overrides": [ "overrides": [
{ {
"files": ["*.test.js", "*.test.jsx", "*.test.ts", "*.test.tsx", "tests/**"], "files": ["*.test.*", "src/tests/**"],
"env": {
"jest": true
},
"globals": { "globals": {
"after": true, "after": true,
"afterAll": true, "afterAll": true,
@ -497,10 +592,14 @@
"test": true "test": true
}, },
"rules": { "rules": {
"no-empty-function": 0, "func-names": 0,
"global-require": 0,
"no-console": 0, "no-console": 0,
"no-import-assign": 0,
"max-lines": 0,
"max-nested-callbacks": 0, "max-nested-callbacks": 0,
"no-undefined": 0 "new-cap": 0,
"prefer-arrow-callback": 0
} }
} }
] ]

View File

@ -1,6 +1,6 @@
{ {
"name": "@mattermost/eslint-plugin", "name": "@mattermost/eslint-plugin",
"version": "1.0.0", "version": "1.1.0",
"description": "ESLint configuration and custom rules used by Mattermost", "description": "ESLint configuration and custom rules used by Mattermost",
"repository": { "repository": {
"type": "git", "type": "git",
@ -12,17 +12,23 @@
"homepage": "https://github.com/mattermost/mattermost/tree/master/webapp/platform/eslint-plugin#readme", "homepage": "https://github.com/mattermost/mattermost/tree/master/webapp/platform/eslint-plugin#readme",
"main": "index.js", "main": "index.js",
"dependencies": { "dependencies": {
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-jsx-a11y": "^6.7.1",
"jsx-ast-utils": "^3.3.3" "jsx-ast-utils": "^3.3.3"
}, },
"peerDependencies": { "peerDependencies": {
"@typescript-eslint/eslint-plugin": "^5.57.1", "eslint-plugin-react": "^7.34.0",
"eslint-plugin-header": "^3.1.1", "eslint-plugin-react-hooks": "^4.6.0"
"eslint-plugin-react": "^7.33.2"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"eslint-plugin-react": { "eslint-plugin-react": {
"optional": true "optional": true
},
"eslint-plugin-react-hooks": {
"optional": true
} }
} }
} }

View File

@ -0,0 +1,6 @@
{
"root": true,
"extends": [
"plugin:@mattermost/base"
]
}

View File

@ -38,7 +38,8 @@
}, },
"scripts": { "scripts": {
"build": "tsc --build --verbose", "build": "tsc --build --verbose",
"check": "eslint --ext .js,.jsx,.tsx,.ts ./src --quiet",
"run": "tsc --watch --preserveWatchOutput", "run": "tsc --watch --preserveWatchOutput",
"clean": "rm -rf tsconfig.tsbuildinfo ./lib" "clean": "rm -rf lib *.tsbuildinfo"
} }
} }

View File

@ -1,16 +1,16 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {Audit} from './audits'; import type {Audit} from './audits';
import {Compliance} from './compliance'; import type {Compliance} from './compliance';
import {AdminConfig, AllowedIPRange, ClientLicense, EnvironmentConfig} from './config'; import type {AdminConfig, ClientLicense, EnvironmentConfig} from './config';
import {DataRetentionCustomPolicies} from './data_retention'; import type {DataRetentionCustomPolicies} from './data_retention';
import {MixedUnlinkedGroupRedux} from './groups'; import type {MixedUnlinkedGroupRedux} from './groups';
import {PluginRedux, PluginStatusRedux} from './plugins'; import type {PluginRedux, PluginStatusRedux} from './plugins';
import {SamlCertificateStatus, SamlMetadataResponse} from './saml'; import type {SamlCertificateStatus, SamlMetadataResponse} from './saml';
import {Team} from './teams'; import type {Team} from './teams';
import {UserAccessToken, UserProfile} from './users'; import type {UserAccessToken, UserProfile} from './users';
import {RelationOneToOne} from './utilities'; import type {RelationOneToOne} from './utilities';
export enum LogLevelEnum { export enum LogLevelEnum {
SILLY = 'silly', SILLY = 'silly',

View File

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {ProductScope} from './products'; import type {ProductScope} from './products';
export enum Permission { export enum Permission {
UserJoinedChannelNotification = 'user_joined_channel_notification', UserJoinedChannelNotification = 'user_joined_channel_notification',

View File

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {UserProfile} from './users'; import type {UserProfile} from './users';
export type UserAutocomplete = { export type UserAutocomplete = {
users: UserProfile[]; users: UserProfile[];

View File

@ -1,10 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {Channel} from './channels'; import type {Channel} from './channels';
import {Team} from './teams'; import type {Team} from './teams';
import {UserProfile} from './users'; import type {UserProfile} from './users';
import {IDMappedObjects, RelationOneToOne} from './utilities'; import type {IDMappedObjects, RelationOneToOne} from './utilities';
export type ChannelCategoryType = 'favorites' | 'channels' | 'direct_messages' | 'custom'; export type ChannelCategoryType = 'favorites' | 'channels' | 'direct_messages' | 'custom';

View File

@ -1,8 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {IDMappedObjects, RelationOneToManyUnique, RelationOneToOne} from './utilities'; import type {Team} from './teams';
import {Team} from './teams'; import type {IDMappedObjects, RelationOneToManyUnique, RelationOneToOne} from './utilities';
// e.g. // e.g.
// **O**pen channel, // **O**pen channel,

View File

@ -1,8 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {AllowedIPRange} from './config'; import type {AllowedIPRange} from './config';
import {ValueOf} from './utilities'; import type {ValueOf} from './utilities';
export type CloudState = { export type CloudState = {
subscription?: Subscription; subscription?: Subscription;

View File

@ -1,8 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
/* eslint-disable max-lines */
export type ClientConfig = { export type ClientConfig = {
AboutLink: string; AboutLink: string;
AllowBannerDismissal: string; AllowBannerDismissal: string;
@ -838,7 +836,7 @@ export type DataRetentionSettings = {
FileRetentionHours: number; FileRetentionHours: number;
DeletionJobStartTime: string; DeletionJobStartTime: string;
BatchSize: number; BatchSize: number;
EnableBoardsDeletion: boolean, EnableBoardsDeletion: boolean;
BoardsRetentionDays: number; BoardsRetentionDays: number;
TimeBetweenBatchesMilliseconds: number; TimeBetweenBatchesMilliseconds: number;
RetentionIdsBatchSize: number; RetentionIdsBatchSize: number;
@ -869,8 +867,7 @@ export type JobSettings = {
CleanupConfigThresholdDays: number; CleanupConfigThresholdDays: number;
}; };
export type ProductSettings = { export type ProductSettings = Record<string, never>;
};
export type PluginSettings = { export type PluginSettings = {
Enable: boolean; Enable: boolean;
@ -1008,7 +1005,6 @@ export enum ServiceEnvironment {
DEV = 'dev', DEV = 'dev',
} }
export type AllowedIPRange = { export type AllowedIPRange = {
cidr_block: string; cidr_block: string;
description: string; description: string;

View File

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {PostMetadata, PostPriorityMetadata} from './posts'; import type {PostMetadata, PostPriorityMetadata} from './posts';
export type Draft = { export type Draft = {
create_at: number; create_at: number;

View File

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {ClientConfig, ClientLicense, WarnMetricStatus} from './config'; import type {ClientConfig, ClientLicense} from './config';
export type GeneralState = { export type GeneralState = {
config: Partial<ClientConfig>; config: Partial<ClientConfig>;

View File

@ -1,9 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {UserProfile} from './users'; import type {UserProfile} from './users';
import type {RelationOneToOne} from './utilities';
import {RelationOneToOne} from './utilities';
export enum SyncableType { export enum SyncableType {
Team = 'team', Team = 'team',

View File

@ -2,7 +2,7 @@
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import type {Address, Product, Invoice} from './cloud'; import type {Address, Product, Invoice} from './cloud';
import {ValueOf} from './utilities'; import type {ValueOf} from './utilities';
export const SelfHostedSignupProgress = { export const SelfHostedSignupProgress = {
START: 'START', START: 'START',

View File

@ -1,8 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {MessageAttachment} from './message_attachments'; import type {MessageAttachment} from './message_attachments';
import {IDMappedObjects} from './utilities'; import type {IDMappedObjects} from './utilities';
export type IncomingWebhook = { export type IncomingWebhook = {
id: string; id: string;

View File

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {IDMappedObjects} from './utilities'; import type {IDMappedObjects} from './utilities';
export type JobType = 'data_retention' | 'elasticsearch_post_indexing' | 'bleve_post_indexing' | 'ldap_sync' | 'message_export'; export type JobType = 'data_retention' | 'elasticsearch_post_indexing' | 'bleve_post_indexing' | 'ldap_sync' | 'message_export';
export type JobStatus = 'pending' | 'in_progress' | 'success' | 'error' | 'cancel_requested' | 'canceled' | 'warning'; export type JobStatus = 'pending' | 'in_progress' | 'success' | 'error' | 'cancel_requested' | 'canceled' | 'warning';

View File

@ -1,8 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {PluginManifest} from './plugins'; import type {AppManifest} from './apps';
import {AppManifest} from './apps'; import type {PluginManifest} from './plugins';
export type MarketplaceLabel = { export type MarketplaceLabel = {
name: string; name: string;

View File

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {PostAction} from './integration_actions'; import type {PostAction} from './integration_actions';
export type MessageAttachment = { export type MessageAttachment = {
id: number; id: number;

View File

@ -1,13 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {Channel, ChannelType} from './channels'; import type {Channel, ChannelType} from './channels';
import {CustomEmoji} from './emojis'; import type {CustomEmoji} from './emojis';
import {FileInfo} from './files'; import type {FileInfo} from './files';
import {Reaction} from './reactions'; import type {Reaction} from './reactions';
import { TeamType } from './teams'; import type {TeamType} from './teams';
import {UserProfile} from './users'; import type {UserProfile} from './users';
import { import type {
RelationOneToOne, RelationOneToOne,
RelationOneToMany, RelationOneToMany,
IDMappedObjects, IDMappedObjects,

View File

@ -1,27 +1,27 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {UserProfile} from './users'; import type {UserProfile} from './users';
export enum UserReportSortColumns { export enum UserReportSortColumns {
username = "Username", username = 'Username',
email = "Email", email = 'Email',
createAt = "CreateAt", createAt = 'CreateAt',
firstName = "FirstName", firstName = 'FirstName',
lastName = "LastName", lastName = 'LastName',
nickname = "Nickname", nickname = 'Nickname',
} }
export enum ReportSortDirection { export enum ReportSortDirection {
ascending = "asc", ascending = 'asc',
descending = "desc", descending = 'desc',
} }
export enum ReportDuration { export enum ReportDuration {
AllTime = "all_time", AllTime = 'all_time',
Last30Days = "last_30_days", Last30Days = 'last_30_days',
PreviousMonth = "previous_month", PreviousMonth = 'previous_month',
Last6Months = "last_6_months", Last6Months = 'last_6_months',
} }
export enum CursorPaginationDirection { export enum CursorPaginationDirection {
@ -30,12 +30,12 @@ export enum CursorPaginationDirection {
} }
export type UserReportFilter = { export type UserReportFilter = {
role_filter?: string, role_filter?: string;
has_no_team?: boolean, has_no_team?: boolean;
team_filter?: string, team_filter?: string;
hide_active?: boolean, hide_active?: boolean;
hide_inactive?: boolean, hide_inactive?: boolean;
search_term?: string, search_term?: string;
} }
export type UserReportOptions = UserReportFilter & { export type UserReportOptions = UserReportFilter & {
@ -56,7 +56,7 @@ export type UserReportOptions = UserReportFilter & {
/** /**
* The direction to paginate in. Either "up" or "down". Use the CursorPaginationDirection enum. * The direction to paginate in. Either "up" or "down". Use the CursorPaginationDirection enum.
*/ */
direction?: CursorPaginationDirection, direction?: CursorPaginationDirection;
/** /**
* The cursor to paginate from. * The cursor to paginate from.

View File

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {TeamMembership} from './teams'; import type {TeamMembership} from './teams';
export type Session = { export type Session = {
id: string; id: string;

View File

@ -1,35 +1,35 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {AdminState} from './admin'; import type {AdminState} from './admin';
import {Bot} from './bots'; import type {AppsState} from './apps';
import {ChannelsState} from './channels'; import type {Bot} from './bots';
import {ChannelCategoriesState} from './channel_categories'; import type {ChannelCategoriesState} from './channel_categories';
import {CloudState, CloudUsage} from './cloud'; import type {ChannelsState} from './channels';
import {HostedCustomerState} from './hosted_customer'; import type {CloudState, CloudUsage} from './cloud';
import {EmojisState} from './emojis'; import type {EmojisState} from './emojis';
import {FilesState} from './files'; import type {FilesState} from './files';
import {GeneralState} from './general'; import type {GeneralState} from './general';
import {GroupsState} from './groups'; import type {GroupsState} from './groups';
import {IntegrationsState} from './integrations'; import type {HostedCustomerState} from './hosted_customer';
import {JobsState} from './jobs'; import type {IntegrationsState} from './integrations';
import {PostsState} from './posts'; import type {JobsState} from './jobs';
import {PreferenceType} from './preferences'; import type {LimitsState} from './limits';
import { import type {PostsState} from './posts';
import type {PreferenceType} from './preferences';
import type {
AdminRequestsStatuses, ChannelsRequestsStatuses, AdminRequestsStatuses, ChannelsRequestsStatuses,
FilesRequestsStatuses, GeneralRequestsStatuses, FilesRequestsStatuses, GeneralRequestsStatuses,
PostsRequestsStatuses, RolesRequestsStatuses, PostsRequestsStatuses, RolesRequestsStatuses,
TeamsRequestsStatuses, UsersRequestsStatuses, TeamsRequestsStatuses, UsersRequestsStatuses,
} from './requests'; } from './requests';
import {Role} from './roles'; import type {Role} from './roles';
import {SchemesState} from './schemes'; import type {SchemesState} from './schemes';
import {SearchState} from './search'; import type {SearchState} from './search';
import {TeamsState} from './teams'; import type {TeamsState} from './teams';
import {ThreadsState} from './threads'; import type {ThreadsState} from './threads';
import {Typing} from './typing'; import type {Typing} from './typing';
import {UsersState} from './users'; import type {UsersState} from './users';
import {AppsState} from './apps';
import {LimitsState} from './limits';
export type GlobalState = { export type GlobalState = {
entities: { entities: {

View File

@ -1,9 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {ServerError} from './errors'; import type {ServerError} from './errors';
import {UserProfile} from './users'; import type {UserProfile} from './users';
import {RelationOneToOne} from './utilities'; import type {RelationOneToOne} from './utilities';
export type TeamMembership = TeamUnread & { export type TeamMembership = TeamUnread & {
user_id: string; user_id: string;

View File

@ -1,9 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import type {Channel} from './channels';
import type {Post} from './posts'; import type {Post} from './posts';
import type {Team} from './teams'; import type {Team} from './teams';
import type {Channel} from './channels';
import type {UserProfile} from './users'; import type {UserProfile} from './users';
import type {IDMappedObjects, RelationOneToMany, RelationOneToOne} from './utilities'; import type {IDMappedObjects, RelationOneToMany, RelationOneToOne} from './utilities';

View File

@ -1,12 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {Audit} from './audits'; import type {Audit} from './audits';
import {Channel} from './channels'; import type {Channel} from './channels';
import {Group} from './groups'; import type {Group} from './groups';
import {Session} from './sessions'; import type {Session} from './sessions';
import {Team} from './teams'; import type {Team} from './teams';
import {IDMappedObjects, RelationOneToMany, RelationOneToManyUnique, RelationOneToOne} from './utilities'; import type {IDMappedObjects, RelationOneToManyUnique, RelationOneToOne} from './utilities';
export type UserNotifyProps = { export type UserNotifyProps = {
desktop: 'default' | 'all' | 'mention' | 'none'; desktop: 'default' | 'all' | 'mention' | 'none';

View File

@ -14,17 +14,23 @@ export type RelationOneToManyUnique<E1 extends {id: string}, E2 extends {id: str
export type IDMappedObjects<E extends {id: string}> = RelationOneToOne<E, E>; export type IDMappedObjects<E extends {id: string}> = RelationOneToOne<E, E>;
export type DeepPartial<T> = { export type DeepPartial<T> = {
// For each field of T, make it optional and... // For each field of T, make it optional and...
[K in keyof T]?: [K in keyof T]?: (
// If that field is a Set or a Map, don't go further // If that field is a Set or a Map, don't go further
T[K] extends Set<any> ? T[K] : T[K] extends Set<any> ? T[K] :
T[K] extends Map<any, any> ? T[K] : T[K] extends Map<any, any> ? T[K] :
// If that field is an object, make it a deep partial object // If that field is an object, make it a deep partial object
T[K] extends object ? DeepPartial<T[K]> : T[K] extends object ? DeepPartial<T[K]> :
// Else if that field is an optional object, make that a deep partial object // Else if that field is an optional object, make that a deep partial object
T[K] extends object | undefined ? DeepPartial<T[K]> : T[K] extends object | undefined ? DeepPartial<T[K]> :
// Else leave it as an optional primitive // Else leave it as an optional primitive
T[K]; T[K]
);
} }
export type ValueOf<T> = T[keyof T]; export type ValueOf<T> = T[keyof T];