Files
advanced-camera-card/eslint.config.mjs
T

113 lines
3.3 KiB
JavaScript

import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { FlatCompat } from '@eslint/eslintrc';
import js from '@eslint/js';
import tsParser from '@typescript-eslint/parser';
import { defineConfig } from 'eslint/config';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all,
});
// Local rule: ban the em-dash character (U+2014) anywhere, including comments and
// strings. The codebase uses a colon or a double hyphen `--` instead. The regex uses
// the unicode escape so the character never appears literally in this file.
const noEmDash = {
meta: {
type: 'problem',
docs: { description: 'Disallow the em-dash character; use ":" or "--" instead.' },
messages: {
emDash: 'Em-dash character is not allowed; use ":" or "--" instead.',
},
},
create(context) {
const sourceCode = context.sourceCode ?? context.getSourceCode();
return {
Program(node) {
const text = sourceCode.getText();
const re = /\u2014/g;
let match;
while ((match = re.exec(text)) !== null) {
context.report({
node,
loc: {
start: sourceCode.getLocFromIndex(match.index),
end: sourceCode.getLocFromIndex(match.index + 1),
},
messageId: 'emDash',
});
}
},
};
},
};
export default defineConfig([
{
extends: compat.extends('plugin:@typescript-eslint/recommended', 'prettier'),
languageOptions: {
parser: tsParser,
ecmaVersion: 2020,
sourceType: 'module',
},
plugins: {
local: { rules: { 'no-em-dash': noEmDash } },
},
rules: {
curly: 'error',
'local/no-em-dash': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-non-null-assertion': 'error',
'@typescript-eslint/parameter-properties': 'error',
},
},
// Timers must go through the Timer class (src/utils/timer.ts).
{
files: ['src/**/*.ts'],
rules: {
'no-restricted-syntax': [
'error',
{
selector:
'CallExpression > Identifier.callee[name=/^(setTimeout|setInterval)$/]',
message:
'Use the Timer class (src/utils/timer.ts) instead of setTimeout/setInterval.',
},
{
selector: 'MemberExpression[property.name=/^(setTimeout|setInterval)$/]',
message:
'Use the Timer class (src/utils/timer.ts) instead of window.setTimeout/setInterval.',
},
],
},
},
// A fire-and-forget promise must be marked with `void` to show it is deliberate.
// To know which calls return a promise, this rule needs type information, so the
// `project` option below runs the TypeScript type-checker over source while linting.
// That makes linting source slower. Tests are excluded: they leave promises unawaited
// freely.
{
files: ['src/**/*.ts'],
languageOptions: {
parser: tsParser,
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
},
rules: {
'@typescript-eslint/no-floating-promises': 'error',
},
},
]);