R: Upgrade to eslint9
This commit is contained in:
parent
be0dfdefd8
commit
edc728fe00
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
@ -28,7 +28,9 @@
|
|||
"editor.tabSize": 4,
|
||||
"editor.insertSpaces": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": "explicit"
|
||||
"source.organizeImports": "explicit",
|
||||
"source.fixAll.ts": "never",
|
||||
"source.fixAll.eslint": "never"
|
||||
}
|
||||
},
|
||||
"python.testing.unittestArgs": ["-v", "-s", "./tests", "-p", "test*.py"],
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
**/parser.ts
|
||||
**/node_modules/**
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended-type-checked",
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module",
|
||||
"project": ["tsconfig.json", "tsconfig.node.json"]
|
||||
},
|
||||
"plugins": ["react-refresh", "simple-import-sort", "eslint-plugin-tsdoc"],
|
||||
"rules": {
|
||||
"no-console": "off",
|
||||
"require-jsdoc": "off",
|
||||
"react-refresh/only-export-components": ["off", { "allowConstantExport": true }],
|
||||
"simple-import-sort/imports": "warn",
|
||||
"tsdoc/syntax": "warn"
|
||||
}
|
||||
}
|
61
rsconcept/frontend/eslint.config.js
Normal file
61
rsconcept/frontend/eslint.config.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
import globals from 'globals';
|
||||
import typescriptPlugin from 'typescript-eslint';
|
||||
import typescriptParser from '@typescript-eslint/parser';
|
||||
import reactPlugin from 'eslint-plugin-react';
|
||||
// import { fixupPluginRules } from '@eslint/compat';
|
||||
// import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
||||
|
||||
import simpleImportSort from 'eslint-plugin-simple-import-sort';
|
||||
|
||||
export default [
|
||||
...typescriptPlugin.configs.recommendedTypeChecked,
|
||||
...typescriptPlugin.configs.stylisticTypeChecked,
|
||||
{
|
||||
ignores: ['**/parser.ts', '**/node_modules/**', '**/public/**', 'eslint.config.js']
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
parser: typescriptParser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
globals: { ...globals.browser, ...globals.es2020, ...globals.jest },
|
||||
project: ['./tsconfig.json', './tsconfig.node.json']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
plugins: {
|
||||
'react': reactPlugin,
|
||||
// 'react-hooks': fixupPluginRules(reactHooksPlugin),
|
||||
'simple-import-sort': simpleImportSort
|
||||
},
|
||||
settings: { react: { version: 'detect' } },
|
||||
rules: {
|
||||
'@typescript-eslint/no-empty-object-type': ['error', { allowInterfaces: 'with-single-extends' }],
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/no-inferrable-types': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
caughtErrorsIgnorePattern: '^_',
|
||||
destructuredArrayIgnorePattern: '^_'
|
||||
}
|
||||
],
|
||||
|
||||
'react-refresh/only-export-components': ['off', { allowConstantExport: true }],
|
||||
|
||||
'simple-import-sort/imports': 'warn',
|
||||
'simple-import-sort/exports': 'error'
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
'require-jsdoc': 'off'
|
||||
}
|
||||
}
|
||||
];
|
2045
rsconcept/frontend/package-lock.json
generated
2045
rsconcept/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -8,7 +8,7 @@
|
|||
"test": "jest",
|
||||
"dev": "vite --host",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"lint": "eslint . --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -48,16 +48,16 @@
|
|||
"@typescript-eslint/parser": "^8.0.1",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-react-refresh": "^0.4.9",
|
||||
"eslint": "^9.8.0",
|
||||
"eslint-plugin-react": "^7.35.0",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"eslint-plugin-tsdoc": "^0.3.0",
|
||||
"globals": "^15.9.0",
|
||||
"jest": "^29.7.0",
|
||||
"postcss": "^8.4.40",
|
||||
"tailwindcss": "^3.4.7",
|
||||
"ts-jest": "^29.2.4",
|
||||
"typescript": "^5.5.4",
|
||||
"typescript-eslint": "^8.0.1",
|
||||
"vite": "^5.3.5"
|
||||
},
|
||||
"jest": {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Search new icons at https://reactsvgicons.com/
|
||||
// Note: save this file using Ctrl + K, Ctrl + Shift + S to disable autoformat
|
||||
|
||||
/* eslint-disable simple-import-sort/exports */
|
||||
// ==== General actions =======
|
||||
export { BiMenu as IconMenu } from 'react-icons/bi';
|
||||
export { LuLogOut as IconLogout } from 'react-icons/lu';
|
||||
|
|
12
rsconcept/frontend/src/components/props.d.ts
vendored
12
rsconcept/frontend/src/components/props.d.ts
vendored
|
@ -2,11 +2,11 @@
|
|||
import { HTMLMotionProps } from 'framer-motion';
|
||||
|
||||
export namespace CProps {
|
||||
export type Titled = {
|
||||
export interface Titled {
|
||||
title?: string;
|
||||
titleHtml?: string;
|
||||
hideTitle?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export type Control = Titled & {
|
||||
disabled?: boolean;
|
||||
|
@ -14,18 +14,18 @@ export namespace CProps {
|
|||
noOutline?: boolean;
|
||||
};
|
||||
|
||||
export type Styling = {
|
||||
export interface Styling {
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type Editor = Control & {
|
||||
label?: string;
|
||||
};
|
||||
|
||||
export type Colors = {
|
||||
export interface Colors {
|
||||
colors?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type Div = React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>;
|
||||
export type Button = Titled &
|
||||
|
|
|
@ -25,7 +25,7 @@ function SelectLocation({ value, folderTree, dense, prefix, onClick, className,
|
|||
const [folded, setFolded] = useState<FolderNode[]>(items);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setFolded(items.filter(item => item !== activeNode && (!activeNode || !activeNode.hasPredecessor(item))));
|
||||
setFolded(items.filter(item => item !== activeNode && !activeNode?.hasPredecessor(item)));
|
||||
}, [items, activeNode]);
|
||||
|
||||
const onFoldItem = useCallback(
|
||||
|
|
|
@ -51,7 +51,7 @@ function SelectUser({
|
|||
options={options}
|
||||
value={value ? { value: value, label: getUserLabel(value) } : null}
|
||||
onChange={data => {
|
||||
if (data !== null && data.value !== undefined) onSelectValue(data.value);
|
||||
if (data?.value !== undefined) onSelectValue(data.value);
|
||||
}}
|
||||
// @ts-expect-error: TODO: use type definitions from react-select in filter object
|
||||
filterOption={filter}
|
||||
|
|
|
@ -23,7 +23,7 @@ import TableBody from './TableBody';
|
|||
import TableFooter from './TableFooter';
|
||||
import TableHeader from './TableHeader';
|
||||
|
||||
export { createColumnHelper, type ColumnSort, type RowSelectionState, type VisibilityState };
|
||||
export { type ColumnSort, createColumnHelper, type RowSelectionState, type VisibilityState };
|
||||
|
||||
export interface IConditionalStyle<TData> {
|
||||
when: (rowData: TData) => boolean;
|
||||
|
|
|
@ -44,7 +44,7 @@ function TableBody<TData>({
|
|||
lastIndex > currentIndex ? currentIndex : lastIndex + 1,
|
||||
lastIndex > currentIndex ? lastIndex : currentIndex + 1
|
||||
);
|
||||
const newSelection: { [key: string]: boolean } = {};
|
||||
const newSelection: Record<string, boolean> = {};
|
||||
toggleRows.forEach(row => {
|
||||
newSelection[row.id] = !target.getIsSelected();
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export {
|
||||
default,
|
||||
createColumnHelper,
|
||||
default,
|
||||
type IConditionalStyle,
|
||||
type RowSelectionState,
|
||||
type VisibilityState
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
import { GraphCanvas as GraphUI } from 'reagraph';
|
||||
|
||||
export {
|
||||
type CollapseProps,
|
||||
type GraphCanvasRef,
|
||||
type GraphEdge,
|
||||
type GraphNode,
|
||||
type GraphCanvasRef,
|
||||
Sphere,
|
||||
useSelection,
|
||||
type CollapseProps
|
||||
useSelection
|
||||
} from 'reagraph';
|
||||
export { type LayoutTypes as GraphLayout } from 'reagraph';
|
||||
|
||||
|
|
|
@ -274,7 +274,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
onError: setProcessingError,
|
||||
onSuccess: () =>
|
||||
reloadItems(() => {
|
||||
if (user && user.subscriptions.includes(target)) {
|
||||
if (user?.subscriptions.includes(target)) {
|
||||
user.subscriptions.splice(
|
||||
user.subscriptions.findIndex(item => item === target),
|
||||
1
|
||||
|
|
|
@ -102,7 +102,6 @@ export const OssState = ({ itemID, children }: OssStateProps) => {
|
|||
return false;
|
||||
}
|
||||
return schema.subscribers.includes(user.id);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [user, schema, toggleTracking]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -143,7 +143,6 @@ export const RSFormState = ({ itemID, versionID, children }: RSFormStateProps) =
|
|||
return false;
|
||||
}
|
||||
return schema.subscribers.includes(user.id);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [user, schema, toggleTracking]);
|
||||
|
||||
const update = useCallback(
|
||||
|
|
|
@ -61,7 +61,6 @@ function DlgEditOperation({ hideWindow, oss, target, onSubmit }: DlgEditOperatio
|
|||
|
||||
useEffect(() => {
|
||||
cache.preload(schemasIDs);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [schemasIDs]);
|
||||
|
||||
const handleSubmit = () => {
|
||||
|
|
|
@ -73,7 +73,6 @@ function useRSFormCache() {
|
|||
}
|
||||
})
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [pending]);
|
||||
|
||||
return { preload, getSchema, getConstituenta, getSchemaByCst, loading, error, setError };
|
||||
|
|
|
@ -26,7 +26,6 @@ function useWindowSize() {
|
|||
}
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return windowSize;
|
||||
|
|
|
@ -72,7 +72,7 @@ export class FolderNode {
|
|||
*
|
||||
*/
|
||||
export class FolderTree {
|
||||
roots: Map<string, FolderNode> = new Map();
|
||||
roots = new Map<string, FolderNode>();
|
||||
|
||||
constructor(arr?: string[]) {
|
||||
arr?.forEach(path => this.addPath(path));
|
||||
|
|
|
@ -48,7 +48,7 @@ export class GraphNode {
|
|||
* This class is optimized for TermGraph use case and not supposed to be used as generic graph implementation.
|
||||
*/
|
||||
export class Graph {
|
||||
nodes: Map<number, GraphNode> = new Map();
|
||||
nodes = new Map<number, GraphNode>();
|
||||
|
||||
constructor(arr?: number[][]) {
|
||||
if (!arr) {
|
||||
|
|
|
@ -20,7 +20,7 @@ import {
|
|||
export class OssLoader {
|
||||
private oss: IOperationSchemaData;
|
||||
private graph: Graph = new Graph();
|
||||
private operationByID: Map<OperationID, IOperation> = new Map();
|
||||
private operationByID = new Map<OperationID, IOperation>();
|
||||
private schemas: LibraryItemID[] = [];
|
||||
|
||||
constructor(input: IOperationSchemaData) {
|
||||
|
@ -53,7 +53,7 @@ export class OssLoader {
|
|||
}
|
||||
|
||||
private extractSchemas() {
|
||||
this.schemas = this.oss.items.map(operation => operation.result as LibraryItemID).filter(item => item !== null);
|
||||
this.schemas = this.oss.items.map(operation => operation.result).filter(item => item !== null);
|
||||
}
|
||||
|
||||
private inferOperationAttributes() {
|
||||
|
|
|
@ -18,8 +18,8 @@ import { extractGlobals, isSimpleExpression, splitTemplateDefinition } from './r
|
|||
export class RSFormLoader {
|
||||
private schema: IRSFormData;
|
||||
private graph: Graph = new Graph();
|
||||
private cstByAlias: Map<string, IConstituenta> = new Map();
|
||||
private cstByID: Map<ConstituentaID, IConstituenta> = new Map();
|
||||
private cstByAlias = new Map<string, IConstituenta>();
|
||||
private cstByID = new Map<ConstituentaID, IConstituenta>();
|
||||
|
||||
constructor(input: IRSFormData) {
|
||||
this.schema = input;
|
||||
|
@ -116,7 +116,7 @@ export class RSFormLoader {
|
|||
}
|
||||
|
||||
private extractSources(target: IConstituenta): Set<ConstituentaID> {
|
||||
const sources: Set<ConstituentaID> = new Set();
|
||||
const sources = new Set<ConstituentaID>();
|
||||
if (!isFunctional(target.cst_type)) {
|
||||
const node = this.graph.at(target.id)!;
|
||||
node.inputs.forEach(id => {
|
||||
|
|
|
@ -108,7 +108,7 @@ export enum HelpTopic {
|
|||
/**
|
||||
* Manual topics hierarchy.
|
||||
*/
|
||||
export const topicParent: Map<HelpTopic, HelpTopic> = new Map([
|
||||
export const topicParent = new Map<HelpTopic, HelpTopic>([
|
||||
[HelpTopic.MAIN, HelpTopic.MAIN],
|
||||
|
||||
[HelpTopic.INTERFACE, HelpTopic.INTERFACE],
|
||||
|
|
|
@ -254,7 +254,7 @@ export function isFunctional(type: CstType): boolean {
|
|||
* Validate new alias against {@link CstType} and {@link IRSForm}.
|
||||
*/
|
||||
export function validateNewAlias(alias: string, type: CstType, schema: IRSForm): boolean {
|
||||
return alias.length >= 2 && alias[0] == getCstTypePrefix(type) && !schema.cstByAlias.has(alias);
|
||||
return alias.length >= 2 && alias.startsWith(getCstTypePrefix(type)) && !schema.cstByAlias.has(alias);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -91,7 +91,7 @@ export function substituteTemplateArgs(expression: string, args: IArgumentValue[
|
|||
return expression;
|
||||
}
|
||||
|
||||
const mapping: { [key: string]: string } = {};
|
||||
const mapping: Record<string, string> = {};
|
||||
args
|
||||
.filter(arg => !!arg.value)
|
||||
.forEach(arg => {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
import LinkTopic from '@/components/ui/LinkTopic';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
|
||||
function HelpCstAttributes() {
|
||||
return (
|
||||
|
|
|
@ -196,7 +196,7 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
|
|||
const openOperationSchema = useCallback(
|
||||
(target: OperationID) => {
|
||||
const node = model.schema?.operationByID.get(target);
|
||||
if (!node || !node.result) {
|
||||
if (!node?.result) {
|
||||
return;
|
||||
}
|
||||
router.push(urls.schema(node.result));
|
||||
|
|
|
@ -56,7 +56,7 @@ function ToolbarConstituenta({
|
|||
onSelect={(event, value) => controller.viewOSS(value.id, event.ctrlKey || event.metaKey)}
|
||||
/>
|
||||
) : null}
|
||||
{activeCst && activeCst.is_inherited ? (
|
||||
{activeCst?.is_inherited ? (
|
||||
<MiniButton
|
||||
title='Перейти к исходной конституенте в ОСС'
|
||||
onClick={() => controller.viewPredecessor(activeCst.id)}
|
||||
|
|
|
@ -117,7 +117,7 @@ function EditorRSExpression({
|
|||
);
|
||||
|
||||
const handleEdit = useCallback((id: TokenID, key?: string) => {
|
||||
if (!rsInput.current || !rsInput.current.editor || !rsInput.current.state || !rsInput.current.view) {
|
||||
if (!rsInput.current?.editor || !rsInput.current.state || !rsInput.current.view) {
|
||||
return;
|
||||
}
|
||||
const text = new RSTextWrapper(rsInput.current as Required<ReactCodeMirrorRef>);
|
||||
|
|
|
@ -387,7 +387,7 @@ export const RSEditState = ({
|
|||
const oldCount = model.schema.items.length;
|
||||
model.inlineSynthesis(data, newSchema => {
|
||||
setSelected([]);
|
||||
toast.success(information.addedConstituents(newSchema['items'].length - oldCount));
|
||||
toast.success(information.addedConstituents(newSchema.items.length - oldCount));
|
||||
});
|
||||
},
|
||||
[model, setSelected]
|
||||
|
|
|
@ -75,7 +75,7 @@ function RSTabs() {
|
|||
setIsModified(false);
|
||||
if (activeTab === RSTabID.CST_EDIT) {
|
||||
const cstID = Number(cstQuery);
|
||||
if (cstID && schema && schema.cstByID.has(cstID)) {
|
||||
if (cstID && schema?.cstByID.has(cstID)) {
|
||||
setSelected([cstID]);
|
||||
} else if (schema && schema?.items.length > 0) {
|
||||
setSelected([schema.items[0].id]);
|
||||
|
|
|
@ -53,7 +53,7 @@ function TableSideConstituents({
|
|||
useLayoutEffect(() => {
|
||||
setColumnVisibility(prev => {
|
||||
const newValue = (windowSize.width ?? 0) >= denseThreshold;
|
||||
if (newValue === prev['expression']) {
|
||||
if (newValue === prev.expression) {
|
||||
return prev;
|
||||
} else {
|
||||
return { expression: newValue };
|
||||
|
|
|
@ -37,11 +37,11 @@ function cursorNode({ type, from, to }: TreeCursor, isLeaf = false): CursorNode
|
|||
return { type, from, to, isLeaf };
|
||||
}
|
||||
|
||||
type TreeTraversalOptions = {
|
||||
interface TreeTraversalOptions {
|
||||
beforeEnter?: (cursor: TreeCursor) => void;
|
||||
onEnter: (node: CursorNode) => false | void;
|
||||
onLeave?: (node: CursorNode) => false | void;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements depth-first traversal.
|
||||
|
|
|
@ -296,7 +296,7 @@ export function describeLocationHead(head: LocationHead): string {
|
|||
/**
|
||||
* Retrieves label for graph layout mode.
|
||||
*/
|
||||
export const mapLabelLayout: Map<GraphLayout, string> = new Map([
|
||||
export const mapLabelLayout = new Map<GraphLayout, string>([
|
||||
['treeTd2d', 'Граф: ДеревоВ 2D'],
|
||||
['treeTd3d', 'Граф: ДеревоВ 3D'],
|
||||
['forceatlas2', 'Граф: Атлас 2D'],
|
||||
|
@ -312,7 +312,7 @@ export const mapLabelLayout: Map<GraphLayout, string> = new Map([
|
|||
/**
|
||||
* Retrieves label for {@link GraphColoring}.
|
||||
*/
|
||||
export const mapLabelColoring: Map<GraphColoring, string> = new Map([
|
||||
export const mapLabelColoring = new Map<GraphColoring, string>([
|
||||
['none', 'Цвет: Моно'],
|
||||
['status', 'Цвет: Статус'],
|
||||
['type', 'Цвет: Класс']
|
||||
|
@ -321,7 +321,7 @@ export const mapLabelColoring: Map<GraphColoring, string> = new Map([
|
|||
/**
|
||||
* Retrieves label for {@link GraphSizing}.
|
||||
*/
|
||||
export const mapLabelSizing: Map<GraphSizing, string> = new Map([
|
||||
export const mapLabelSizing = new Map<GraphSizing, string>([
|
||||
['none', 'Узлы: Моно'],
|
||||
['derived', 'Узлы: Порожденные'],
|
||||
['complex', 'Узлы: Простые']
|
||||
|
|
|
@ -29,14 +29,14 @@ export class TextMatcher {
|
|||
}
|
||||
try {
|
||||
this.query = new RegExp(query, isCaseSensitive ? '' : 'i');
|
||||
} catch (exception: unknown) {
|
||||
} catch (_exception: unknown) {
|
||||
this.query = query;
|
||||
}
|
||||
}
|
||||
|
||||
test(text: string): boolean {
|
||||
if (typeof this.query === 'string') {
|
||||
return text.indexOf(this.query) !== -1;
|
||||
return text.includes(this.query);
|
||||
} else {
|
||||
return !!text.match(this.query);
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ export class TextMatcher {
|
|||
/**
|
||||
* Text substitution guided by mapping and regular expression.
|
||||
*/
|
||||
export function applyPattern(text: string, mapping: { [key: string]: string }, pattern: RegExp): string {
|
||||
export function applyPattern(text: string, mapping: Record<string, string>, pattern: RegExp): string {
|
||||
if (text === '' || pattern === null) {
|
||||
return text;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user