Upgrade to ESLint v9 flat config (#34324)

Co-authored-by: Nick Schonning <nschonni@gmail.com>
This commit is contained in:
Echo 2025-04-01 18:30:18 +02:00 committed by GitHub
parent 9686ae7060
commit e8270e2807
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 1011 additions and 874 deletions

View file

@ -1,13 +0,0 @@
/build/**
/coverage/**
/db/**
/lib/**
/log/**
/node_modules/**
/nonobox/**
/public/**
!/public/embed.js
/spec/**
/tmp/**
/vendor/**
!.eslintrc.js

View file

@ -1,367 +0,0 @@
// @ts-check
const { defineConfig } = require('eslint-define-config');
module.exports = defineConfig({
root: true,
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'plugin:import/recommended',
'plugin:promise/recommended',
'plugin:jsdoc/recommended',
],
env: {
browser: true,
node: true,
es6: true,
},
parser: '@typescript-eslint/parser',
plugins: [
'react',
'jsx-a11y',
'import',
'promise',
'@typescript-eslint',
'formatjs',
],
parserOptions: {
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 2021,
requireConfigFile: false,
babelOptions: {
configFile: false,
presets: ['@babel/react', '@babel/env'],
},
},
settings: {
react: {
version: 'detect',
},
'import/ignore': [
'node_modules',
'\\.(css|scss|json)$',
],
'import/resolver': {
typescript: {},
},
},
rules: {
'consistent-return': 'error',
'dot-notation': 'error',
eqeqeq: ['error', 'always', { 'null': 'ignore' }],
'indent': ['error', 2],
'jsx-quotes': ['error', 'prefer-single'],
'semi': ['error', 'always'],
'no-catch-shadow': 'error',
'no-console': [
'warn',
{
allow: [
'error',
'warn',
],
},
],
'no-empty': ['error', { "allowEmptyCatch": true }],
'no-restricted-properties': [
'error',
{ property: 'substring', message: 'Use .slice instead of .substring.' },
{ property: 'substr', message: 'Use .slice instead of .substr.' },
],
'no-restricted-syntax': [
'error',
{
// eslint-disable-next-line no-restricted-syntax
selector: 'Literal[value=/•/], JSXText[value=/•/]',
// eslint-disable-next-line no-restricted-syntax
message: "Use '·' (middle dot) instead of '•' (bullet)",
},
],
'no-unused-expressions': 'error',
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
vars: 'all',
args: 'after-used',
destructuredArrayIgnorePattern: '^_',
ignoreRestSiblings: true,
},
],
'valid-typeof': 'error',
'react/jsx-filename-extension': ['error', { extensions: ['.jsx', 'tsx'] }],
'react/jsx-boolean-value': 'error',
'react/display-name': 'off',
'react/jsx-fragments': ['error', 'syntax'],
'react/jsx-equals-spacing': 'error',
'react/jsx-no-bind': 'error',
'react/jsx-no-useless-fragment': 'error',
'react/jsx-no-target-blank': ['error', { allowReferrer: true }],
'react/jsx-tag-spacing': 'error',
'react/jsx-uses-react': 'off', // not needed with new JSX transform
'react/jsx-wrap-multilines': 'error',
'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
'react/self-closing-comp': 'error',
// recommended values found in https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/v6.8.0/src/index.js#L46
'jsx-a11y/click-events-have-key-events': 'off',
'jsx-a11y/label-has-associated-control': 'off',
'jsx-a11y/media-has-caption': 'off',
'jsx-a11y/no-autofocus': 'off',
// recommended rule is:
// 'jsx-a11y/no-interactive-element-to-noninteractive-role': [
// 'error',
// {
// tr: ['none', 'presentation'],
// canvas: ['img'],
// },
// ],
'jsx-a11y/no-interactive-element-to-noninteractive-role': 'off',
// recommended rule is:
// 'jsx-a11y/no-noninteractive-tabindex': [
// 'error',
// {
// tags: [],
// roles: ['tabpanel'],
// allowExpressionValues: true,
// },
// ],
'jsx-a11y/no-noninteractive-tabindex': 'off',
// recommended is full 'error'
'jsx-a11y/no-static-element-interactions': [
'warn',
{
handlers: [
'onClick',
],
},
],
// See https://github.com/import-js/eslint-plugin-import/blob/v2.29.1/config/recommended.js
'import/extensions': [
'error',
'always',
{
js: 'never',
jsx: 'never',
mjs: 'never',
ts: 'never',
tsx: 'never',
},
],
'import/first': 'error',
'import/newline-after-import': 'error',
'import/no-anonymous-default-export': 'error',
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: [
'.eslintrc.js',
'config/webpack/**',
'app/javascript/mastodon/performance.js',
'app/javascript/mastodon/test_setup.js',
'app/javascript/**/__tests__/**',
],
},
],
'import/no-amd': 'error',
'import/no-commonjs': 'error',
'import/no-import-module-exports': 'error',
'import/no-relative-packages': 'error',
'import/no-self-import': 'error',
'import/no-useless-path-segments': 'error',
'import/no-webpack-loader-syntax': 'error',
'import/order': [
'error',
{
alphabetize: { order: 'asc' },
'newlines-between': 'always',
groups: [
'builtin',
'external',
'internal',
'parent',
['index', 'sibling'],
'object',
],
pathGroups: [
// React core packages
{
pattern: '{react,react-dom,react-dom/client,prop-types}',
group: 'builtin',
position: 'after',
},
// I18n
{
pattern: '{react-intl,intl-messageformat}',
group: 'builtin',
position: 'after',
},
// Common React utilities
{
pattern: '{classnames,react-helmet,react-router,react-router-dom}',
group: 'external',
position: 'before',
},
// Immutable / Redux / data store
{
pattern: '{immutable,@reduxjs/toolkit,react-redux,react-immutable-proptypes,react-immutable-pure-component}',
group: 'external',
position: 'before',
},
// Internal packages
{
pattern: '{mastodon/**}',
group: 'internal',
position: 'after',
},
],
pathGroupsExcludedImportTypes: [],
},
],
'promise/always-return': 'off',
'promise/catch-or-return': [
'error',
{
allowFinally: true,
},
],
'promise/no-callback-in-promise': 'off',
'promise/no-nesting': 'off',
'promise/no-promise-in-callback': 'off',
'formatjs/blocklist-elements': 'error',
'formatjs/enforce-default-message': ['error', 'literal'],
'formatjs/enforce-description': 'off', // description values not currently used
'formatjs/enforce-id': 'off', // Explicit IDs are used in the project
'formatjs/enforce-placeholders': 'off', // Issues in short_number.jsx
'formatjs/enforce-plural-rules': 'error',
'formatjs/no-camel-case': 'off', // disabledAccount is only non-conforming
'formatjs/no-complex-selectors': 'error',
'formatjs/no-emoji': 'error',
'formatjs/no-id': 'off', // IDs are used for translation keys
'formatjs/no-invalid-icu': 'error',
'formatjs/no-literal-string-in-jsx': 'off', // Should be looked at, but mainly flagging punctuation outside of strings
'formatjs/no-multiple-whitespaces': 'error',
'formatjs/no-offset': 'error',
'formatjs/no-useless-message': 'error',
'formatjs/prefer-formatted-message': 'error',
'formatjs/prefer-pound-in-plural': 'error',
'jsdoc/check-types': 'off',
'jsdoc/no-undefined-types': 'off',
'jsdoc/require-jsdoc': 'off',
'jsdoc/require-param-description': 'off',
'jsdoc/require-property-description': 'off',
'jsdoc/require-returns-description': 'off',
'jsdoc/require-returns': 'off',
},
overrides: [
{
files: [
'.eslintrc.js',
'*.config.js',
'.*rc.js',
'ide-helper.js',
'config/webpack/**/*',
'config/formatjs-formatter.js',
],
env: {
commonjs: true,
},
parserOptions: {
sourceType: 'script',
},
rules: {
'import/no-commonjs': 'off',
},
},
{
files: [
'**/*.ts',
'**/*.tsx',
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/strict-type-checked',
'plugin:@typescript-eslint/stylistic-type-checked',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
'plugin:promise/recommended',
'plugin:jsdoc/recommended-typescript',
],
parserOptions: {
projectService: true,
tsconfigRootDir: __dirname,
},
rules: {
// Disable formatting rules that have been enabled in the base config
'indent': 'off',
// This is not needed as we use noImplicitReturns, which handles this in addition to understanding types
'consistent-return': 'off',
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
'@typescript-eslint/consistent-type-exports': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
"@typescript-eslint/prefer-nullish-coalescing": ['error', { ignorePrimitives: { boolean: true } }],
"@typescript-eslint/no-restricted-imports": [
"warn",
{
"name": "react-redux",
"importNames": ["useSelector", "useDispatch"],
"message": "Use typed hooks `useAppDispatch` and `useAppSelector` instead."
}
],
"@typescript-eslint/restrict-template-expressions": ['warn', { allowNumber: true }],
'jsdoc/require-jsdoc': 'off',
// Those rules set stricter rules for TS files
// to enforce better practices when converting from JS
'import/no-default-export': 'warn',
'react/prefer-stateless-function': 'warn',
'react/function-component-definition': ['error', { namedComponents: 'arrow-function' }],
'react/jsx-uses-react': 'off', // not needed with new JSX transform
'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
'react/prop-types': 'off',
},
},
{
files: [
'**/__tests__/*.js',
'**/__tests__/*.jsx',
],
env: {
jest: true,
},
}
],
});

View file

@ -97,7 +97,13 @@
{ {
// Group all eslint-related packages with `eslint` in the same PR // Group all eslint-related packages with `eslint` in the same PR
matchManagers: ['npm'], matchManagers: ['npm'],
matchPackageNames: ['eslint', 'eslint-*', '@typescript-eslint/*'], matchPackageNames: [
'eslint',
'eslint-*',
'typescript-eslint',
'@eslint/*',
'globals',
],
matchUpdateTypes: ['patch', 'minor'], matchUpdateTypes: ['patch', 'minor'],
groupName: 'eslint (non-major)', groupName: 'eslint (non-major)',
}, },

View file

@ -11,7 +11,7 @@ on:
- 'tsconfig.json' - 'tsconfig.json'
- '.nvmrc' - '.nvmrc'
- '.prettier*' - '.prettier*'
- '.eslint*' - 'eslint.config.mjs'
- '**/*.js' - '**/*.js'
- '**/*.jsx' - '**/*.jsx'
- '**/*.ts' - '**/*.ts'
@ -25,7 +25,7 @@ on:
- 'tsconfig.json' - 'tsconfig.json'
- '.nvmrc' - '.nvmrc'
- '.prettier*' - '.prettier*'
- '.eslint*' - 'eslint.config.mjs'
- '**/*.js' - '**/*.js'
- '**/*.jsx' - '**/*.jsx'
- '**/*.ts' - '**/*.ts'
@ -44,7 +44,7 @@ jobs:
uses: ./.github/actions/setup-javascript uses: ./.github/actions/setup-javascript
- name: ESLint - name: ESLint
run: yarn lint:js --max-warnings 0 run: yarn workspaces foreach --all --parallel run lint:js --max-warnings 0
- name: Typecheck - name: Typecheck
run: yarn typecheck run: yarn typecheck

View file

@ -1,4 +1,4 @@
module.exports = { module.exports = {
singleQuote: true, singleQuote: true,
jsxSingleQuote: true jsxSingleQuote: true
} };

View file

@ -68,7 +68,7 @@ function loaded() {
if (id) message = localeData[id]; if (id) message = localeData[id];
if (!message) message = defaultMessage as string; message ??= defaultMessage as string;
const messageFormat = new IntlMessageFormat(message, locale); const messageFormat = new IntlMessageFormat(message, locale);
return messageFormat.format(values) as string; return messageFormat.format(values) as string;

View file

@ -1,4 +1,4 @@
import React from 'react'; import type React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';

View file

@ -1,5 +1,5 @@
import type { PropsWithChildren } from 'react'; import type { PropsWithChildren } from 'react';
import React from 'react'; import type React from 'react';
import { Router as OriginalRouter, useHistory } from 'react-router'; import { Router as OriginalRouter, useHistory } from 'react-router';

View file

@ -1,5 +1,3 @@
/* eslint-disable import/no-commonjs --
We need to use CommonJS here due to preval */
// @preval // @preval
// http://www.unicode.org/Public/emoji/5.0/emoji-test.txt // http://www.unicode.org/Public/emoji/5.0/emoji-test.txt
// This file contains the compressed version of the emoji data from // This file contains the compressed version of the emoji data from

View file

@ -33,11 +33,8 @@ function processEmojiMapData(
shortCode?: ShortCodesToEmojiDataKey, shortCode?: ShortCodesToEmojiDataKey,
) { ) {
const [native, _filename] = emojiMapData; const [native, _filename] = emojiMapData;
let filename = emojiMapData[1]; // filename name can be derived from unicodeToFilename
if (!filename) { const filename = emojiMapData[1] ?? unicodeToFilename(native);
// filename name can be derived from unicodeToFilename
filename = unicodeToFilename(native);
}
unicodeMapping[native] = { unicodeMapping[native] = {
shortCode, shortCode,
filename, filename,

View file

@ -1,6 +1,3 @@
/* eslint-disable import/no-commonjs --
We need to use CommonJS here as its imported into a preval file (`emoji_compressed.js`) */
// taken from: // taken from:
// https://github.com/twitter/twemoji/blob/47732c7/twemoji-generator.js#L848-L866 // https://github.com/twitter/twemoji/blob/47732c7/twemoji-generator.js#L848-L866
exports.unicodeToFilename = (str) => { exports.unicodeToFilename = (str) => {

View file

@ -1,6 +1,3 @@
/* eslint-disable import/no-commonjs --
We need to use CommonJS here as its imported into a preval file (`emoji_compressed.js`) */
function padLeft(str, num) { function padLeft(str, num) {
while (str.length < num) { while (str.length < num) {
str = '0' + str; str = '0' + str;

View file

@ -1,6 +1,3 @@
/* eslint-disable import/no-commonjs --
We need to use CommonJS here as its imported into a preval file (`emoji_compressed.js`) */
/* @preval */ /* @preval */
const fs = require('fs'); const fs = require('fs');

395
eslint.config.mjs Normal file
View file

@ -0,0 +1,395 @@
// @ts-check
import js from '@eslint/js';
import { globalIgnores } from 'eslint/config';
import formatjs from 'eslint-plugin-formatjs';
// @ts-expect-error -- No typings
import importPlugin from 'eslint-plugin-import';
import jsdoc from 'eslint-plugin-jsdoc';
import jsxA11Y from 'eslint-plugin-jsx-a11y';
import promisePlugin from 'eslint-plugin-promise';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import globals from 'globals';
import tseslint from 'typescript-eslint';
/** @type {import('typescript-eslint').ConfigArray} */
export const baseConfig = [
js.configs.recommended,
importPlugin.flatConfigs.recommended,
jsdoc.configs['flat/recommended'],
promisePlugin.configs['flat/recommended'],
{
linterOptions: {
reportUnusedDisableDirectives: 'error',
reportUnusedInlineConfigs: 'error',
},
rules: {
'consistent-return': 'error',
'dot-notation': 'error',
eqeqeq: [
'error',
'always',
{
null: 'ignore',
},
],
'no-console': [
'warn',
{
allow: ['error', 'warn'],
},
],
'no-empty': [
'error',
{
allowEmptyCatch: true,
},
],
'no-restricted-properties': [
'error',
{
property: 'substring',
message: 'Use .slice instead of .substring.',
},
{
property: 'substr',
message: 'Use .slice instead of .substr.',
},
],
'no-unused-expressions': 'error',
'no-unused-vars': 'off',
'valid-typeof': 'error',
'import/extensions': [
'error',
'always',
{
js: 'never',
jsx: 'never',
mjs: 'never',
ts: 'never',
mts: 'never',
tsx: 'never',
},
],
'import/first': 'error',
'import/newline-after-import': 'error',
'import/no-anonymous-default-export': 'error',
'import/no-amd': 'error',
'import/no-commonjs': 'error',
'import/no-import-module-exports': 'error',
'import/no-relative-packages': 'error',
'import/no-self-import': 'error',
'import/no-useless-path-segments': 'error',
'import/order': [
'error',
{
alphabetize: {
order: 'asc',
},
'newlines-between': 'always',
groups: [
'builtin',
'external',
'internal',
'parent',
['index', 'sibling'],
'object',
],
pathGroups: [
{
pattern: '{react,react-dom,react-dom/client,prop-types}',
group: 'builtin',
position: 'after',
},
{
pattern: '{react-intl,intl-messageformat}',
group: 'builtin',
position: 'after',
},
{
pattern:
'{classnames,react-helmet,react-router,react-router-dom}',
group: 'external',
position: 'before',
},
{
pattern:
'{immutable,@reduxjs/toolkit,react-redux,react-immutable-proptypes,react-immutable-pure-component}',
group: 'external',
position: 'before',
},
{
pattern: '{mastodon/**}',
group: 'internal',
position: 'after',
},
],
pathGroupsExcludedImportTypes: [],
},
],
'jsdoc/check-types': 'off',
'jsdoc/no-undefined-types': 'off',
'jsdoc/require-jsdoc': 'off',
'jsdoc/require-param-description': 'off',
'jsdoc/require-property-description': 'off',
'jsdoc/require-returns-description': 'off',
'jsdoc/require-returns': 'off',
'promise/always-return': 'off',
'promise/catch-or-return': [
'error',
{
allowFinally: true,
},
],
'promise/no-callback-in-promise': 'off',
'promise/no-nesting': 'off',
'promise/no-promise-in-callback': 'off',
},
},
];
export default tseslint.config([
baseConfig,
globalIgnores([
'build/**/*',
'coverage/**/*',
'db/**/*',
'lib/**/*',
'log/**/*',
'node_modules/**/*',
'public/**/*',
'!public/embed.js',
'spec/**/*',
'tmp/**/*',
'vendor/**/*',
'streaming/**/*',
]),
react.configs.flat.recommended,
react.configs.flat['jsx-runtime'],
reactHooks.configs['recommended-latest'],
jsxA11Y.flatConfigs.recommended,
importPlugin.flatConfigs.react,
// @ts-expect-error -- For some reason the formatjs package exports an empty object?
formatjs.configs.strict,
{
languageOptions: {
globals: {
...globals.browser,
},
parser: tseslint.parser,
ecmaVersion: 2021,
sourceType: 'module',
},
settings: {
react: {
version: 'detect',
},
'import/ignore': ['node_modules', '\\.(css|scss|json)$'],
'import/resolver': {
typescript: {},
},
},
rules: {
'no-restricted-syntax': [
'error',
{
// eslint-disable-next-line no-restricted-syntax
selector: 'Literal[value=/•/], JSXText[value=/•/]',
// eslint-disable-next-line no-restricted-syntax
message: "Use '·' (middle dot) instead of '•' (bullet)",
},
],
'formatjs/enforce-description': 'off', // description values not currently used
'formatjs/enforce-id': 'off', // Explicit IDs are used in the project
'formatjs/enforce-placeholders': 'off', // Issues in short_number.jsx
'formatjs/no-invalid-icu': 'error',
'formatjs/no-literal-string-in-jsx': 'off', // Should be looked at, but mainly flagging punctuation outside of strings
'formatjs/no-multiple-plurals': 'off', // Should be looked at
'jsx-a11y/click-events-have-key-events': 'off',
'jsx-a11y/label-has-associated-control': 'off',
'jsx-a11y/media-has-caption': 'off',
'jsx-a11y/no-autofocus': 'off',
'jsx-a11y/no-interactive-element-to-noninteractive-role': 'off',
'jsx-a11y/no-noninteractive-tabindex': 'off',
'jsx-a11y/no-static-element-interactions': [
'warn',
{
handlers: ['onClick'],
},
],
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: [
'eslint.config.mjs',
'config/webpack/**',
'app/javascript/mastodon/performance.js',
'app/javascript/mastodon/test_setup.js',
'app/javascript/**/__tests__/**',
],
},
],
'import/no-webpack-loader-syntax': 'error',
'react/jsx-filename-extension': [
'error',
{
extensions: ['.jsx', 'tsx'],
},
],
'react/jsx-boolean-value': 'error',
'react/display-name': 'off',
'react/jsx-fragments': ['error', 'syntax'],
'react/jsx-equals-spacing': 'error',
'react/jsx-no-bind': 'error',
'react/jsx-no-useless-fragment': 'error',
'react/jsx-no-target-blank': [
'error',
{
allowReferrer: true,
},
],
'react/jsx-tag-spacing': 'error',
'react/jsx-wrap-multilines': 'error',
'react/self-closing-comp': 'error',
},
},
{
files: [
'app/javascript/mastodon/common.js',
'app/javascript/mastodon/features/emoji/unicode_to_unified_name.js',
'app/javascript/mastodon/features/emoji/emoji_compressed.js',
'app/javascript/mastodon/features/emoji/unicode_to_filename.js',
'app/javascript/mastodon/service_worker/web_push_locales.js',
'**/*.config.js',
'**/.*rc.js',
'**/ide-helper.js',
'config/webpack/**/*',
'config/formatjs-formatter.js',
],
languageOptions: {
globals: {
...globals.commonjs,
...globals.node,
},
ecmaVersion: 5,
sourceType: 'commonjs',
},
rules: {
'import/no-commonjs': 'off',
},
},
{
files: ['**/*.ts', '**/*.tsx'],
extends: [
tseslint.configs.strictTypeChecked,
tseslint.configs.stylisticTypeChecked,
react.configs.flat.recommended,
react.configs.flat['jsx-runtime'],
reactHooks.configs['recommended-latest'],
jsxA11Y.flatConfigs.recommended,
importPlugin.flatConfigs.react,
importPlugin.flatConfigs.typescript,
jsdoc.configs['flat/recommended-typescript'],
],
languageOptions: {
parserOptions: {
projectService: true,
},
},
rules: {
// This is not needed as we use noImplicitReturns, which handles this in addition to understanding types
'consistent-return': 'off',
'formatjs/enforce-plural-rules': 'off',
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
'import/no-default-export': 'warn',
'jsdoc/require-jsdoc': 'off',
'react/prefer-stateless-function': 'warn',
'react/function-component-definition': [
'error',
{
namedComponents: 'arrow-function',
},
],
'react/prop-types': 'off',
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
'@typescript-eslint/consistent-type-exports': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/prefer-nullish-coalescing': [
'error',
{
ignorePrimitives: {
boolean: true,
},
},
],
'@typescript-eslint/no-restricted-imports': [
'warn',
{
name: 'react-redux',
importNames: ['useSelector', 'useDispatch'],
message:
'Use typed hooks `useAppDispatch` and `useAppSelector` instead.',
},
],
'@typescript-eslint/no-unused-vars': [
'error',
{
vars: 'all',
args: 'after-used',
destructuredArrayIgnorePattern: '^_',
ignoreRestSiblings: true,
},
],
'@typescript-eslint/restrict-template-expressions': [
'warn',
{
allowNumber: true,
},
],
},
},
{
files: ['**/__tests__/*.js', '**/__tests__/*.jsx'],
languageOptions: {
globals: {
...globals.jest,
},
},
},
]);

View file

@ -12,14 +12,14 @@
"scripts": { "scripts": {
"build:development": "cross-env RAILS_ENV=development NODE_ENV=development ./bin/webpack", "build:development": "cross-env RAILS_ENV=development NODE_ENV=development ./bin/webpack",
"build:production": "cross-env RAILS_ENV=production NODE_ENV=production ./bin/webpack", "build:production": "cross-env RAILS_ENV=production NODE_ENV=production ./bin/webpack",
"fix:js": "eslint . --ext=.js,.jsx,.ts,.tsx --cache --report-unused-disable-directives --fix", "fix:js": "eslint . --cache --fix",
"fix:css": "stylelint --fix \"**/*.{css,scss}\"", "fix:css": "stylelint --fix \"**/*.{css,scss}\"",
"fix": "yarn fix:js && yarn fix:css", "fix": "yarn fix:js && yarn fix:css",
"format": "prettier --write --log-level warn .", "format": "prettier --write --log-level warn .",
"format:check": "prettier --check --ignore-unknown .", "format:check": "prettier --check --ignore-unknown .",
"i18n:extract": "formatjs extract 'app/javascript/**/*.{js,jsx,ts,tsx}' --ignore '**/*.d.ts' --out-file app/javascript/mastodon/locales/en.json --format config/formatjs-formatter.js", "i18n:extract": "formatjs extract 'app/javascript/**/*.{js,jsx,ts,tsx}' --ignore '**/*.d.ts' --out-file app/javascript/mastodon/locales/en.json --format config/formatjs-formatter.js",
"jest": "cross-env NODE_ENV=test jest", "jest": "cross-env NODE_ENV=test jest",
"lint:js": "eslint . --ext=.js,.jsx,.ts,.tsx --cache --report-unused-disable-directives", "lint:js": "cd $INIT_CWD && eslint --cache --report-unused-disable-directives",
"lint:css": "stylelint \"**/*.{css,scss}\"", "lint:css": "stylelint \"**/*.{css,scss}\"",
"lint": "yarn lint:js && yarn lint:css", "lint": "yarn lint:js && yarn lint:css",
"postversion": "git push --tags", "postversion": "git push --tags",
@ -142,6 +142,7 @@
"workbox-window": "^7.0.0" "workbox-window": "^7.0.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.23.0",
"@formatjs/cli": "^6.1.1", "@formatjs/cli": "^6.1.1",
"@testing-library/dom": "^10.2.0", "@testing-library/dom": "^10.2.0",
"@testing-library/jest-dom": "^6.0.0", "@testing-library/jest-dom": "^6.0.0",
@ -173,19 +174,17 @@
"@types/requestidlecallback": "^0.3.5", "@types/requestidlecallback": "^0.3.5",
"@types/webpack": "^4.41.33", "@types/webpack": "^4.41.33",
"@types/webpack-env": "^1.18.4", "@types/webpack-env": "^1.18.4",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"babel-jest": "^29.5.0", "babel-jest": "^29.5.0",
"eslint": "^8.41.0", "eslint": "^9.23.0",
"eslint-define-config": "^2.0.0", "eslint-import-resolver-typescript": "^4.2.5",
"eslint-import-resolver-typescript": "^3.5.5", "eslint-plugin-formatjs": "^5.3.1",
"eslint-plugin-formatjs": "^5.0.0",
"eslint-plugin-import": "~2.31.0", "eslint-plugin-import": "~2.31.0",
"eslint-plugin-jsdoc": "^50.0.0", "eslint-plugin-jsdoc": "^50.6.9",
"eslint-plugin-jsx-a11y": "~6.10.0", "eslint-plugin-jsx-a11y": "~6.10.2",
"eslint-plugin-promise": "~7.2.0", "eslint-plugin-promise": "~7.2.1",
"eslint-plugin-react": "^7.33.2", "eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-hooks": "^5.2.0",
"globals": "^16.0.0",
"husky": "^9.0.11", "husky": "^9.0.11",
"jest": "^29.5.0", "jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0", "jest-environment-jsdom": "^29.5.0",
@ -196,6 +195,7 @@
"stylelint-config-prettier-scss": "^1.0.0", "stylelint-config-prettier-scss": "^1.0.0",
"stylelint-config-standard-scss": "^14.0.0", "stylelint-config-standard-scss": "^14.0.0",
"typescript": "^5.0.4", "typescript": "^5.0.4",
"typescript-eslint": "^8.28.0",
"webpack-dev-server": "^3.11.3" "webpack-dev-server": "^3.11.3"
}, },
"resolutions": { "resolutions": {

View file

@ -1,43 +0,0 @@
/* eslint-disable import/no-commonjs */
// @ts-check
// @ts-ignore - This needs to be a CJS file (eslint does not yet support ESM configs), and TS is complaining we use require
const { defineConfig } = require('eslint-define-config');
module.exports = defineConfig({
extends: ['../.eslintrc.js'],
env: {
browser: false,
},
parserOptions: {
project: true,
tsconfigRootDir: __dirname,
ecmaFeatures: {
jsx: false,
},
ecmaVersion: 2021,
},
rules: {
// In the streaming server we need to delete some variables to ensure
// garbage collection takes place on the values referenced by those objects;
// The alternative is to declare the variable as nullable, but then we need
// to assert it's in existence before every use, which becomes much harder
// to maintain.
'no-delete-var': 'off',
// This overrides the base configuration for this rule to pick up
// dependencies for the streaming server from the correct package.json file.
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: ['streaming/.eslintrc.cjs'],
optionalDependencies: false,
peerDependencies: false,
includeTypes: true,
packageDir: __dirname,
},
],
'import/extensions': ['error', 'always'],
},
});

View file

@ -0,0 +1,45 @@
// @ts-check
import globals from 'globals';
import tseslint from 'typescript-eslint';
// eslint-disable-next-line import/no-relative-packages -- Must import from the root
import { baseConfig } from '../eslint.config.mjs';
export default tseslint.config([
baseConfig,
{
languageOptions: {
globals: globals.node,
parser: tseslint.parser,
ecmaVersion: 2021,
sourceType: 'module',
},
settings: {
'import/ignore': ['node_modules', '\\.(json)$'],
'import/resolver': {
typescript: {},
},
},
rules: {
// In the streaming server we need to delete some variables to ensure
// garbage collection takes place on the values referenced by those objects;
// The alternative is to declare the variable as nullable, but then we need
// to assert it's in existence before every use, which becomes much harder
// to maintain.
'no-delete-var': 'off',
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: ['**/*.config.mjs'],
},
],
'import/extensions': ['error', 'always'],
},
},
]);

View file

@ -0,0 +1,7 @@
const config = {
'*': 'prettier --ignore-unknown --write',
'*.{js,ts}': 'eslint --fix',
'**/*.ts': () => 'tsc -p tsconfig.json --noEmit',
};
export default config;

View file

@ -31,14 +31,16 @@
"ws": "^8.12.1" "ws": "^8.12.1"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.23.0",
"@types/cors": "^2.8.16", "@types/cors": "^2.8.16",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/pg": "^8.6.6", "@types/pg": "^8.6.6",
"@types/uuid": "^10.0.0", "@types/uuid": "^10.0.0",
"@types/ws": "^8.5.9", "@types/ws": "^8.5.9",
"eslint-define-config": "^2.0.0", "globals": "^16.0.0",
"pino-pretty": "^13.0.0", "pino-pretty": "^13.0.0",
"typescript": "^5.0.4" "typescript": "^5.0.4",
"typescript-eslint": "^8.28.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"bufferutil": "^4.0.7", "bufferutil": "^4.0.7",

View file

@ -8,5 +8,5 @@
"tsBuildInfoFile": "../tmp/cache/streaming/tsconfig.tsbuildinfo", "tsBuildInfoFile": "../tmp/cache/streaming/tsconfig.tsbuildinfo",
"paths": {} "paths": {}
}, },
"include": ["./*.js", "./.eslintrc.cjs"] "include": ["./*.js"]
} }

941
yarn.lock

File diff suppressed because it is too large Load diff