function getNamingConventionRules(additionalDefaultFormats = []) {
	return [
		{ selector: 'default', format: ['camelCase', ...additionalDefaultFormats], leadingUnderscore: 'forbid', trailingUnderscore: 'forbid' },
		{ selector: 'variable', format: ['camelCase', 'UPPER_CASE', ...additionalDefaultFormats] },
		{ selector: 'typeLike', format: ['PascalCase'] },
		{ selector: 'enumMember', format: ['PascalCase'] },
		{ selector: 'memberLike', modifiers: ['private'], leadingUnderscore: 'require', format: ['camelCase'] },
		{ selector: 'memberLike', modifiers: ['protected'], leadingUnderscore: 'require', format: ['camelCase'] },
		{
			selector: 'property',
			format: ['PascalCase'],
			filter: {
				match: true,
				regex: '^(Area|Baseline|Bar|Candlestick|Histogram|Line|Custom)$',
			},
		},
	];
}

const tsRulesExtendsWithoutTypeCheck = [
	'plugin:@typescript-eslint/eslint-recommended',
	'plugin:@typescript-eslint/recommended',
	'plugin:import/typescript',
];

const tsRulesExtendsWithTypeCheck = [
	...tsRulesExtendsWithoutTypeCheck,
	'plugin:@typescript-eslint/recommended-requiring-type-checking',
];

/** @type {import('eslint').Linter.Config} */
module.exports = {
	reportUnusedDisableDirectives: true,
	env: {
		browser: false,
		es6: true,
		node: true,
	},
	plugins: [
		'@typescript-eslint',
		'@typescript-eslint/tslint',
		'deprecation',
		'eslint-plugin-tsdoc',
		'import',
		'jsdoc',
		'markdown',
		'prefer-arrow',
		'unicorn',
		'jsdoc',
		'eslint-plugin-react',
	],
	settings: {
		jsdoc: {
			ignoreInternal: true,
		},
		react: {
			version: require('./website/package.json').dependencies.react.slice(1),
		},
	},
	extends: [
		'eslint:recommended',
		'plugin:react/recommended',
	],
	parserOptions: {
		ecmaVersion: 2020,
		sourceType: 'module',
	},
	overrides: [
		{
			// rules specific for js files only
			files: [
				'**/*.js',
				'**/*.jsx',
				'**/*.cjs',

				// that's for md/mdx files
				'**/*.javascript',
			],
			overrides: [
				{
					files: '**/*.jsx',
					env: {
						browser: true,
					},
					rules: {
						'react/prop-types': 'off',
						'import/no-default-export': 'off',
					},
				},
			],
			rules: {
				// enforces no braces where they can be omitted
				// http://eslint.org/docs/rules/arrow-body-style
				'arrow-body-style': ['error', 'as-needed'],

				// enforce one true brace style
				'brace-style': ['error', '1tbs', { allowSingleLine: true }],

				// require camel case names
				camelcase: 'error',

				// encourages use of dot notation whenever possible
				'dot-notation': ['error', { allowKeywords: true }],

				// this option sets a specific tab width for your code
				// https://github.com/eslint/eslint/blob/master/docs/rules/indent.md
				indent: ['error', 'tab', { SwitchCase: 1, VariableDeclarator: 1 }],

				// disallow creation of functions within loops
				'no-loop-func': 'error',

				// disallow variable declarations from shadowing variables declared in the outer scope
				'no-shadow': 'error',

				// disallow use of undeclared variables unless mentioned in a /*global */ block
				'no-undef': 'error',

				// disallow usage of expressions in statement position
				'no-unused-expressions': 'error',

				// disallow declaration of variables that are not used in the code
				'no-unused-vars': ['error', { vars: 'local', args: 'none', ignoreRestSiblings: true }],

				// specify whether double or single quotes should be used
				quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: true }],

				// require or disallow use of semicolons instead of ASI
				semi: ['error', 'always'],
			},
		},
		{
			files: ['**/*.md'],
			processor: 'markdown/markdown',
		},
		{
			files: ['**/*.mdx'],
			processor: 'mdx/remark',
			settings: {
				'mdx/code-blocks': true,
			},
			extends: [
				'plugin:mdx/recommended',
			],
		},
		{
			files: [
				'**/*.md/*.js',
				'**/*.md/*.javascript',

				'**/*.mdx/*.js',
				'**/*.mdx/*.javascript',
			],
			env: {
				browser: true,
				node: false,
			},
			globals: {
				LightweightCharts: false,

				areaSeries: true,
				barSeries: true,
				candlestickSeries: true,
				chart: true,
				container: true,
				histogramSeries: true,
				lineSeries: true,
				series: true,
			},
			rules: {
				'no-console': 'off',
				'no-undef': 'off',
				'no-unused-vars': 'off',
				indent: ['error', 4],
				'unicorn/filename-case': 'off',
				'react/prop-types': 'off',
			},
		},
		{
			files: ['**/*.ts', '**/*.tsx'],
			excludedFiles: ['dist/**'],
			parser: '@typescript-eslint/parser',
			extends: tsRulesExtendsWithTypeCheck,
			parserOptions: {
				project: 'tsconfig.json',
				sourceType: 'module',
			},
			overrides: [
				{
					files: ['website/**/*.ts', 'website/**/*.tsx'],
					parserOptions: {
						project: 'website/tsconfig.json',
						sourceType: 'module',
					},
					rules: {
						'@typescript-eslint/naming-convention': [
							'error',

							// allow PascalCase for react components
							...getNamingConventionRules(['PascalCase']),
						],
					},
				},
				{
					files: ['website/src/**/*.tsx'],
					rules: {
						'import/no-default-export': 'off',
					},
				},

				// note this rule MUST be the last in this overrides list
				// because it should be applied last and override parserOptions correctly
				// otherwise typescript-eslint will raise an error that it cannot
				{
					// well, for code blocks from md/mdx we shouldn't (and cannot) do type check
					// so let's just disable such rules from their linting
					files: [
						'**/*.md/*.ts',
						'**/*.md/*.tsx',
						'**/*.md/*.typescript',

						'**/*.mdx/*.ts',
						'**/*.mdx/*.tsx',
						'**/*.mdx/*.typescript',
					],
					extends: tsRulesExtendsWithoutTypeCheck,
					parserOptions: {
						project: null,
						sourceType: 'module',
					},
					rules: {
						'@typescript-eslint/await-thenable': 'off',
						'@typescript-eslint/dot-notation': 'off',
						'@typescript-eslint/no-floating-promises': 'off',
						'@typescript-eslint/no-implied-eval': 'off',
						'@typescript-eslint/no-misused-promises': 'off',
						'@typescript-eslint/no-unnecessary-qualifier': 'off',
						'@typescript-eslint/no-unnecessary-type-assertion': 'off',
						'@typescript-eslint/no-unsafe-argument': 'off',
						'@typescript-eslint/no-unsafe-call': 'off',
						'@typescript-eslint/no-unsafe-member-access': 'off',
						'@typescript-eslint/no-unsafe-return': 'off',
						'@typescript-eslint/no-unused-vars': 'off',
						'@typescript-eslint/prefer-regexp-exec': 'off',
						'@typescript-eslint/require-await': 'off',
						'@typescript-eslint/tslint/config': 'off',
						'@typescript-eslint/unbound-method': 'off',
						'deprecation/deprecation': 'off',
					},
				},
			],
			rules: {
				'@typescript-eslint/array-type': [
					'error',
					{
						default: 'array',
					},
				],
				'@typescript-eslint/brace-style': ['error', '1tbs', { allowSingleLine: true }],
				'@typescript-eslint/consistent-type-assertions': [
					'error',
					{
						assertionStyle: 'as',
						objectLiteralTypeAssertions: 'never',
					},
				],
				'@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
				'@typescript-eslint/dot-notation': 'error',
				'@typescript-eslint/explicit-member-accessibility': [
					'error',
					{
						accessibility: 'explicit',
						overrides: {
							accessors: 'explicit',
							constructors: 'explicit',
						},
					},
				],
				'@typescript-eslint/member-delimiter-style': 'error',
				'@typescript-eslint/member-ordering': [
					'error',
					{
						default: [
							'signature',
							'public-static-field',
							'protected-static-field',
							'private-static-field',
							'public-instance-field',
							'protected-instance-field',
							'private-instance-field',
							'constructor',
							'public-instance-method',
							'public-static-method',
							'protected-instance-method',
							'protected-static-method',
							'private-instance-method',
							'private-static-method',
						],
					},
				],
				'@typescript-eslint/naming-convention': [
					'error',
					...getNamingConventionRules(),
				],
				'@typescript-eslint/no-empty-interface': 'off',
				'@typescript-eslint/no-empty-function': 'off',
				'@typescript-eslint/no-explicit-any': 'error',
				'@typescript-eslint/no-extraneous-class': 'error',
				'@typescript-eslint/no-inferrable-types': [
					'error',
					{
						ignoreParameters: true,
						ignoreProperties: true,
					},
				],
				'@typescript-eslint/no-invalid-void-type': 'error',
				'@typescript-eslint/no-loop-func': 'error',
				'@typescript-eslint/no-namespace': 'off',
				'@typescript-eslint/no-non-null-assertion': 'error',
				'@typescript-eslint/no-parameter-properties': 'error',
				'@typescript-eslint/no-require-imports': 'off',
				'@typescript-eslint/no-shadow': 'error',
				'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off',
				'@typescript-eslint/no-unnecessary-qualifier': 'error',
				'@typescript-eslint/no-unnecessary-type-arguments': 'off',
				'@typescript-eslint/no-unsafe-assignment': 'off',
				'@typescript-eslint/no-unused-expressions': 'error',
				'@typescript-eslint/no-unused-vars': 'off',
				'@typescript-eslint/no-use-before-define': 'off',
				'@typescript-eslint/prefer-for-of': 'off',
				'@typescript-eslint/prefer-function-type': 'error',
				'@typescript-eslint/prefer-readonly': 'off', // TODO
				'@typescript-eslint/promise-function-async': 'off',
				'@typescript-eslint/restrict-template-expressions': 'off',
				'@typescript-eslint/quotes': ['error', 'single', { avoidEscape: true, allowTemplateLiterals: true }],
				'@typescript-eslint/restrict-plus-operands': 'off',
				'@typescript-eslint/semi': [
					'error',
					'always',
				],
				'@typescript-eslint/strict-boolean-expressions': 'off',
				'@typescript-eslint/triple-slash-reference': [
					'off',
					{
						path: 'never',
						types: 'prefer-import',
						lib: 'never',
					},
				],
				'@typescript-eslint/type-annotation-spacing': 'error',

				'deprecation/deprecation': 'error',

				'tsdoc/syntax': ['error'],
				'jsdoc/check-examples': [
					'error',
					{
						exampleCodeRegex: '/```js\\s+(.*)\\s+```/su',
					},
				],

				// can't use at the moment - see https://github.com/typescript-eslint/typescript-eslint/issues/1824
				// '@typescript-eslint/indent': [
				// 	'error',
				// 	'tab',
				// 	{
				// 		SwitchCase: 1,
				// 		VariableDeclarator: 1,
				// 	},
				// ],

				// see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/ROADMAP.md
				'@typescript-eslint/tslint/config': [
					'error',
					{
						rulesDirectory: [
							'node_modules/tslint-eslint-rules/dist/rules',
							'node_modules/tslint-microsoft-contrib',
						],
						rules: {
							// tslint-microsoft-contrib
							'no-typeof-undefined': true,
							'no-unnecessary-local-variable': true,
							'no-unnecessary-override': true,

							// tslint-eslint-rules
							'ter-indent': [
								true,
								'tab',
								{
									SwitchCase: 1,
									VariableDeclarator: 1,
								},
							],

							// tslint core
							align: [
								true,
								'parameters',
								'arguments',
								'statements',
							],
							'ordered-imports': [
								true,
								{
									'grouped-imports': true,
									groups: [
										{
											name: 'nodejs-core',
											match: '^(fs|path)$',
											order: 9,
										},
										{
											name: 'libraries',
											match: '^[^\\.]+',
											order: 10,
										},
										{
											name: 'api',
											match: '\\.\\./api/',
											order: 20,
										},
										{
											name: 'formatters',
											match: '\\.\\./formatters/',
											order: 30,
										},
										{
											name: 'gui',
											match: '\\.\\./gui/',
											order: 40,
										},
										{
											name: 'helpers',
											match: '\\.\\./helpers/',
											order: 50,
										},
										{
											name: 'model',
											match: '\\.\\./(renderers|views|model)/',
											order: 60,
										},
										{
											name: 'current directory',
											match: '^\\.',
											order: 70,
										},
									],
								},
							],
							'static-this': true,
							'strict-type-predicates': true,

							// we can't use @typescript-eslint/typedef in couple with @typescript-eslint/explicit-function-return-type
							// because it isn't the same - explicit-function-return-type requires to specify return type of arrow functions
							// which cannot be disabled or configured somehow
							// see https://github.com/typescript-eslint/typescript-eslint/issues/1731
							// and https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/typedef.md
							typedef: [
								true,
								'call-signature',
								'parameter',
								'arrow-parameter',
								'property-declaration',
								'member-variable-declaration',
							],
						},
					},
				],
			},
		},
		{
			files: ['dist/typings.d.ts'],
			parser: '@typescript-eslint/parser',
			env: {
				browser: true,
				node: false,
			},
			rules: {
				'no-unused-vars': 'off',
				'jsdoc/require-jsdoc': [
					'error',
					{
						contexts: [
							'TSEnumDeclaration',
							'TSEnumMember',
							'TSInterfaceDeclaration',
							'TSMethodSignature',
							'TSPropertySignature',
							'TSTypeAliasDeclaration',
						],
					},
				],
				'jsdoc/require-param': 'error',
				// d.ts files are mostly read by computers (to generate docs, provide intellisense, etc.)
				// so consistent quote characaters aren't important.
				'@typescript-eslint/quotes': 'off',
			},
		},
	],
	rules: {
		// enforces return statements in callbacks of array's methods
		// http://eslint.org/docs/rules/array-callback-return
		'array-callback-return': 'error',

		'arrow-parens': ['error', 'as-needed'],

		// enforce a maximum cyclomatic complexity allowed in a program
		complexity: ['error', { max: 13 }],

		// specify curly brace conventions for all control statements
		curly: ['error', 'all'],

		// require the use of === and !==
		eqeqeq: ['error', 'smart'],

		// require `for-in` loops to include an `if` statement
		'guard-for-in': 'error',

		// enforce a maximum number of classes per file
		'max-classes-per-file': ['error', 5],

		// disallow the use of alert, confirm, and prompt
		'no-alert': 'warn',

		// disallow use of arguments.caller or arguments.callee
		'no-caller': 'error',

		// disallow Unnecessary Labels
		// http://eslint.org/docs/rules/no-extra-label
		'no-extra-label': 'error',

		// disallow use of eval()
		'no-eval': 'error',

		// disallow adding to native types
		'no-extend-native': 'error',

		// disallow unnecessary function binding
		'no-extra-bind': 'error',

		// disallow the use of leading or trailing decimal points in numeric literals
		'no-floating-decimal': 'error',

		// disallow use of eval()-like methods
		'no-implied-eval': 'error',

		// disallow usage of __iterator__ property
		'no-iterator': 'error',

		// disallow use of labels for anything other then loops and switches
		'no-labels': ['error', { allowLoop: false, allowSwitch: false }],

		// disallow unnecessary nested blocks
		'no-lone-blocks': 'error',

		// disallow use of multiple spaces
		'no-multi-spaces': 'error',

		// disallow use of multiline strings
		'no-multi-str': 'error',

		// disallow use of new operator when not part of the assignment or comparison
		'no-new': 'error',

		// disallow use of new operator for Function object
		'no-new-func': 'error',

		// disallows creating new instances of String, Number, and Boolean
		'no-new-wrappers': 'error',

		// disallow use of octal escape sequences in string literals, such as
		// var foo = 'Copyright \251';
		'no-octal-escape': 'error',

		// disallow usage of __proto__ property
		'no-proto': 'error',

		// disallow use of assignment in return statement
		'no-return-assign': 'error',

		// disallow unnecessary `return await`
		'no-return-await': 'error',

		// disallow use of `javascript:` urls.
		'no-script-url': 'error',

		// disallow comparisons where both sides are exactly the same
		'no-self-compare': 'error',

		// disallow use of comma operator
		'no-sequences': 'error',

		// restrict what can be thrown as an exception
		'no-throw-literal': 'error',

		// requires to declare all vars on top of their containing scope
		// 'vars-on-top': 2,
		// require immediate function invocation to be wrapped in parentheses
		// http://eslint.org/docs/rules/wrap-iife.html
		'wrap-iife': ['error', 'inside'],

		// errors

		// disallow assignment in conditional expressions
		'no-cond-assign': ['error', 'always'],

		// disallow use of console
		'no-console': 'error',

		// disallow unnecessary parentheses
		'no-extra-parens': ['error', 'functions'],

		// disallow template literal placeholder syntax in regular strings
		'no-template-curly-in-string': 'error',

		// Avoid code that looks like two expressions but is actually one
		'no-unexpected-multiline': 'off',

		// es6

		// require space before/after arrow function's arrow
		// https://github.com/eslint/eslint/blob/master/docs/rules/arrow-spacing.md
		'arrow-spacing': ['error', { before: true, after: true }],

		// require trailing commas in multiline object literals
		'comma-dangle': ['error', {
			arrays: 'always-multiline',
			objects: 'always-multiline',
			imports: 'always-multiline',
			exports: 'always-multiline',
			functions: 'never',
		}],

		// disallow duplicate module imports
		'no-duplicate-imports': 'error',

		// require let or const instead of var
		'no-var': 'error',

		// disallow unnecessary constructor
		// http://eslint.org/docs/rules/no-useless-constructor
		'no-useless-constructor': 'error',

		// require method and property shorthand syntax for object literals
		// https://github.com/eslint/eslint/blob/master/docs/rules/object-shorthand.md
		'object-shorthand': 'off',

		// suggest using arrow functions as callbacks
		'prefer-arrow-callback': 'error',

		// suggest using of const declaration for variables that are never modified after declared
		'prefer-const': 'error',

		// require rest parameters instead of `arguments`
		'prefer-rest-params': 'error',

		// require template literals instead of string concatenation
		'prefer-template': 'off', // TODO

		// enforce usage of spacing in template strings
		// http://eslint.org/docs/rules/template-curly-spacing
		'template-curly-spacing': 'error',

		// enforce spacing around the * in yield* expressions
		// http://eslint.org/docs/rules/yield-star-spacing
		'yield-star-spacing': ['error', 'after'],

		// strict

		strict: 'off',

		// vars

		// disallow initializing variables to `undefined`
		'no-undef-init': 'error',

		// style

		// enforce spacing inside array brackets
		'array-bracket-spacing': ['error', 'never'],

		// enforce spacing before and after comma
		'comma-spacing': ['error', { before: false, after: true }],

		// enforce one true comma style
		'comma-style': ['error', 'last'],

		// disallow padding inside computed properties
		'computed-property-spacing': ['error', 'never'],

		// enforce newline at the end of file, with no multiple empty lines
		'eol-last': 'error',

		// specify whether double or single quotes should be used in JSX attributes
		// http://eslint.org/docs/rules/jsx-quotes
		'jsx-quotes': ['error', 'prefer-double'],

		// enforces spacing between keys and values in object literal properties
		'key-spacing': ['error', { beforeColon: false, afterColon: true }],

		// require a space before & after certain keywords
		'keyword-spacing': ['error', {
			before: true,
			after: true,
			overrides: {
				return: { after: true },
				throw: { after: true },
				case: { after: true },
			},
		}],

		// enforce a maximum number of parameters in function definitions
		'max-params': ['error', { max: 6 }],

		// require a capital letter for constructors
		'new-cap': ['error', { newIsCap: true, capIsNew: false }],

		// enforce or disallow parentheses when invoking a constructor with no arguments
		'new-parens': ['error', 'always'],

		// disallow use of the Array constructor
		'no-array-constructor': 'error',

		// disallow mixed spaces and tabs for indentation
		'no-mixed-spaces-and-tabs': 'error',

		// disallow multiple empty lines and only one newline at the end
		'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 1 }],

		// disallow nested ternary expressions
		// 'no-nested-ternary': 2,
		// disallow use of the Object constructor
		'no-new-object': 'error',

		// disallow specified syntax
		'no-restricted-syntax': ['error', 'ForInStatement', `BinaryExpression[operator='in']`],

		// disallow space between function identifier and application
		'no-spaced-func': 'error',

		// disallow trailing whitespace at the end of lines
		'no-trailing-spaces': 'error',

		// disallow the use of Boolean literals in conditional expressions
		// also, prefer `a || b` over `a ? a : b`
		// http://eslint.org/docs/rules/no-unneeded-ternary
		'no-unneeded-ternary': ['error', { defaultAssignment: false }],

		// disallow whitespace before properties
		// http://eslint.org/docs/rules/no-whitespace-before-property
		'no-whitespace-before-property': 'error',

		// require padding inside curly braces
		'object-curly-spacing': ['error', 'always'],

		// allow just one var statement per function
		'one-var': ['error', 'never'],

		// require a newline around variable declaration
		// http://eslint.org/docs/rules/one-var-declaration-per-line
		'one-var-declaration-per-line': ['error', 'always'],

		// enforce padding within blocks
		'padded-blocks': ['error', 'never'],

		// disallow using Object.assign with an object literal as the first argument and prefer the use of object spread instead.
		'prefer-object-spread': 'error',

		// require quotes around object literal property names
		// http://eslint.org/docs/rules/quote-props.html
		'quote-props': ['error', 'as-needed', { keywords: false, unnecessary: true, numbers: false }],

		// enforce spacing before and after semicolons
		'semi-spacing': ['error', { before: false, after: true }],

		// require or disallow space before blocks
		'space-before-blocks': 'error',

		// require or disallow space before function opening parenthesis
		// https://github.com/eslint/eslint/blob/master/docs/rules/space-before-function-paren.md
		'space-before-function-paren': ['error', { anonymous: 'never', named: 'never' }],

		// require or disallow spaces inside parentheses
		'space-in-parens': ['error', 'never'],

		// require spaces around operators
		'space-infix-ops': 'error',

		// require or disallow a space immediately following the // or /* in a comment
		'spaced-comment': ['error', 'always', {
			exceptions: ['-', '+'],
			markers: ['=', '!', '/'], // space here to support sprockets directives
		}],

		'jsdoc/check-indentation': 'error',
		'import/no-default-export': 'error',

		'prefer-arrow/prefer-arrow-functions': [
			'error',
			{
				singleReturnOnly: true,
				allowStandaloneDeclarations: true,
			},
		],

		'unicorn/empty-brace-spaces': ['error'],
		'unicorn/filename-case': ['error', { case: 'kebabCase' }],
		'unicorn/no-array-push-push': ['error'],
		'unicorn/prefer-date-now': ['error'],
	},
};