Commit d7d77ffd authored by Alejandro Celaya's avatar Alejandro Celaya Committed by Alejandro Celaya

Migrate to ESLint 9 and ESLint flat config

parent 25358a00
build/**
**/vendor/**/*.js
**/coverage/**
docs/_build/*
dev-server/static/**/*.js
{
"extends": [
"hypothesis",
"plugin:jsx-a11y/recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"rules": {
"prefer-arrow-callback": ["error", { "allowNamedFunctions": true }],
"object-shorthand": ["error", "properties"],
// Replaced by TypeScript's static checking.
"react/prop-types": "off",
// Upgrade TS rules from warning to error.
"@typescript-eslint/no-unused-vars": "error",
// Replace no-use-before-define with TS version, to avoid incorrect warnings.
// See https://typescript-eslint.io/rules/no-use-before-define
"no-use-before-define": "off",
"@typescript-eslint/no-use-before-define": [
"error",
{
"functions": false,
"typedefs": false,
"ignoreTypeReferences": false
}
],
// Disable TS rules that we dislike.
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-this-alias": "off",
// Enforce consistency in cases where TypeScript supports old and new
// syntaxes for the same thing.
//
// - Require `<var> as <type>` for casts
// - Require `import type` for type imports. The corresponding rule for
// exports is not enabled yet because that requires setting up type-aware
// linting.
"@typescript-eslint/consistent-type-assertions": "error",
"@typescript-eslint/consistent-type-imports": "error"
},
"overrides": [
{
"files": ["*.js"],
"excludedFiles": ["src/**"],
"env": { "node": true },
}
]
}
{
"parserOptions": {}
}
{
"env": {
"node": true,
},
"rules": {
"@typescript-eslint/no-var-requires": "off",
}
}
{
"parserOptions": {
"sourceType": "module"
},
"rules": {
"react/prop-types": "off",
}
}
{
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module",
}
}
import hypothesis from 'eslint-config-hypothesis';
import jsxA11y from 'eslint-plugin-jsx-a11y';
import globals from 'globals';
import tseslint from 'typescript-eslint';
export default tseslint.config(
{
ignores: [
'.tox/**/*',
'.yalc/**/*',
'.yarn/**/*',
'build/**/*',
'**/vendor/**/*.js',
'**/coverage/**/*',
'docs/_build/*',
'dev-server/static/**/*.js',
],
},
...hypothesis,
...tseslint.configs.recommended,
jsxA11y.flatConfigs.recommended,
{
rules: {
'prefer-arrow-callback': [
'error',
{
allowNamedFunctions: true,
},
],
'object-shorthand': ['error', 'properties'],
'react/prop-types': 'off',
'@typescript-eslint/no-unused-vars': 'error',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': [
'error',
{
functions: false,
typedefs: false,
ignoreTypeReferences: false,
},
],
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-this-alias': 'off',
'@typescript-eslint/consistent-type-assertions': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
},
},
// Annotator module
{
files: ['src/annotator/**/*.{js|tx|tsx}'],
rules: {
'no-restricted-properties': [
2,
{
// Disable `bind` usage in annotator/ code to prevent unexpected behavior
// due to broken bind polyfills. See
// https://github.com/hypothesis/client/issues/245
property: 'bind',
message:
'Use function expressions instead of bind() in annotator/ code',
},
],
},
},
// Scripts and configuration files
{
files: ['**/*.js'],
ignores: ['src/**'],
rules: {
'@typescript-eslint/no-var-requires': 'off',
'no-console': 'off',
'react-hooks/rules-of-hooks': 'off',
},
languageOptions: {
globals: {
...globals.node,
},
},
},
);
......@@ -38,8 +38,6 @@
"@types/shallowequal": "^1.1.1",
"@types/showdown": "^2.0.0",
"@types/sinon": "^17.0.3",
"@typescript-eslint/eslint-plugin": "^7.0.1",
"@typescript-eslint/parser": "^7.0.1",
"approx-string-match": "^2.0.0",
"autoprefixer": "^10.0.1",
"axe-core": "^4.0.0",
......@@ -58,16 +56,17 @@
"enzyme-adapter-preact-pure": "^4.0.1",
"escape-html": "^1.0.3",
"escape-string-regexp": "^4.0.0",
"eslint": "^8.3.0",
"eslint-config-hypothesis": "^2.6.0",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-mocha": "^10.0.1",
"eslint-plugin-react": "^7.12.4",
"eslint-plugin-react-hooks": "^4.0.4",
"eslint": "^9.12.0",
"eslint-config-hypothesis": "^3.0.0",
"eslint-plugin-jsx-a11y": "^6.10.0",
"eslint-plugin-mocha": "^10.5.0",
"eslint-plugin-react": "^7.37.1",
"eslint-plugin-react-hooks": "^5.0.0",
"express": "^5.0.1",
"fancy-log": "^2.0.0",
"fetch-mock": "11",
"focus-visible": "^5.0.0",
"globals": "^15.11.0",
"gulp": "^5.0.0",
"gulp-changed": "^5.0.1",
"hammerjs": "^2.0.4",
......@@ -101,6 +100,7 @@
"tailwindcss": "^3.0.2",
"tiny-emitter": "^2.0.2",
"typescript": "^5.0.2",
"typescript-eslint": "^8.9.0",
"wrap-text": "^1.0.7"
},
"browserslist": "chrome 92, firefox 90, safari 14.1",
......
{
"env": {
"node": true
},
"rules": {
"@typescript-eslint/no-var-requires": "off",
"no-console": "off",
"react-hooks/rules-of-hooks": "off"
}
}
// eslint-disable-next-line
'use strict';
module.exports = {
rules: {
'no-restricted-properties': [
2,
{
// Disable `bind` usage in annotator/ code to prevent unexpected behavior
// due to broken bind polyfills. See
// https://github.com/hypothesis/client/issues/245
property: 'bind',
message:
'Use function expressions instead of bind() in annotator/ code',
},
],
},
};
......@@ -126,7 +126,7 @@ export function describe(root: Element, range: Range) {
if (anchor) {
result.push(anchor.toSelector());
}
} catch (error) {
} catch {
// If resolving some anchor fails, we just want to skip it silently
}
}
......
......@@ -149,7 +149,7 @@ export function nodeFromXPath(
): Node | null {
try {
return evaluateSimpleXPath(xpath, root);
} catch (err) {
} catch {
return document.evaluate(
'.' + xpath,
root,
......
......@@ -148,7 +148,7 @@ export function settingsFrom(window_: Window): SettingsGetters {
if (queryFragmentMatch) {
try {
return decodeURIComponent(queryFragmentMatch[2]);
} catch (err) {
} catch {
// URI Error should return the page unfiltered.
}
}
......
......@@ -70,7 +70,7 @@ export class FrameObserver {
this._removeFrame(frame);
});
this._onFrameAdded(frame);
} catch (e) {
} catch {
console.warn(
`Unable to inject the Hypothesis client (from '${document.location.href}' into a cross-origin frame '${frame.src}')`,
);
......
......@@ -667,7 +667,7 @@ export class Guest extends TinyEmitter implements Annotator, Destroyable {
// this anchor.
const textRange = TextRange.fromRange(range);
anchor = { annotation, target, range: textRange };
} catch (err) {
} catch {
anchor = { annotation, target };
}
return anchor;
......
......@@ -182,7 +182,7 @@ export class HTMLMetadata {
try {
const href = this._absoluteUrl(link.href);
links.push({ href, rel: link.rel, type: link.type });
} catch (e) {
} catch {
// Ignore URIs which cannot be parsed.
}
}
......@@ -197,7 +197,7 @@ export class HTMLMetadata {
href: this._absoluteUrl(url),
type: 'application/pdf',
});
} catch (e) {
} catch {
// Ignore URIs which cannot be parsed.
}
}
......@@ -253,7 +253,7 @@ export class HTMLMetadata {
if (['shortcut icon', 'icon'].includes(link.rel)) {
try {
favicon = this._absoluteUrl(link.href);
} catch (e) {
} catch {
// Ignore URIs which cannot be parsed.
}
}
......
......@@ -330,7 +330,7 @@ export class PDFIntegration extends TinyEmitter implements Integration {
// Wait for PDF to load.
try {
await this.uri();
} catch (e) {
} catch {
return;
}
......
......@@ -342,9 +342,9 @@ describe('HTMLMetadata', () => {
// location in tests, create a proxy object in front of our blank HTML
// document.
const fakeDocument = {
createElement: htmlDoc.createElement.bind(htmlDoc), // eslint-disable-line no-restricted-properties
createElement: htmlDoc.createElement.bind(htmlDoc),
baseURI: baseURI ?? href,
querySelectorAll: htmlDoc.querySelectorAll.bind(htmlDoc), // eslint-disable-line no-restricted-properties
querySelectorAll: htmlDoc.querySelectorAll.bind(htmlDoc),
location: {
href,
},
......
......@@ -79,7 +79,7 @@ export function isNodeInRange(range: Range, node: Node) {
// Check end of node is after start of range.
range.comparePoint(node, length) >= 0
);
} catch (e) {
} catch {
// `comparePoint` may fail if the `range` and `node` do not share a common
// ancestor or `node` is a doctype.
return false;
......
......@@ -467,7 +467,7 @@ export class Sidebar implements Destroyable {
// Suppressing ban-types here because the functions are originally defined
// as `Function` somewhere else. To be fixed when that is migrated to TS
// eslint-disable-next-line @typescript-eslint/ban-types
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
const eventHandlers: Array<[SidebarToHostEvent, Function | undefined]> = [
['loginRequested', this.onLoginRequest],
['logoutRequested', this.onLogoutRequest],
......
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
/*
* Disable @typescript-eslint/ban-types for the whole file, as changing the
......
......@@ -39,7 +39,7 @@ export function isBrowserSupported(): boolean {
try {
return checks.every(check => check());
} catch (err) {
} catch {
return false;
}
}
/* global __dirname */
/* global __dirname require module */
// eslint-disable-next-line @typescript-eslint/no-var-requires
// eslint-disable-next-line @typescript-eslint/no-require-imports
const path = require('path');
module.exports = function (config) {
......
......@@ -48,7 +48,6 @@ export class ListenerCollection {
this._listeners.set(symbol, {
eventTarget,
eventType,
// eslint-disable-next-line object-shorthand
listener: listener as EventListener,
options,
});
......
......@@ -139,7 +139,7 @@ function AnnotationEditor({
try {
await annotationsService.save(annotation);
toastMessenger.success(successMessage, { visuallyHidden: true });
} catch (err) {
} catch {
toastMessenger.error('Saving annotation failed');
}
};
......
......@@ -97,7 +97,7 @@ function AnnotationShareControl({
try {
await copyPlainText(shareUri);
toastMessenger.success('Copied share link to clipboard');
} catch (err) {
} catch {
toastMessenger.error('Unable to copy link');
}
};
......
......@@ -83,7 +83,7 @@ function GroupListItem({
try {
await copyPlainText(url);
toastMessenger.success(`Copied link for "${group.name}"`);
} catch (err) {
} catch {
toastMessenger.error('Unable to copy link');
}
};
......
......@@ -38,7 +38,7 @@ function ShareAnnotations({ toastMessenger }: ShareAnnotationsProps) {
await copyPlainText(shareURI);
toastMessenger.success('Copied share link to clipboard');
}
} catch (err) {
} catch {
toastMessenger.error('Unable to copy link');
}
}, [shareURI, toastMessenger]);
......
......@@ -41,7 +41,7 @@ function VersionInfo({ toastMessenger, versionData }: VersionInfoProps) {
try {
await copyPlainText(versionData.asFormattedString());
toastMessenger.success('Copied version info to clipboard');
} catch (err) {
} catch {
toastMessenger.error('Unable to copy version info');
}
};
......
......@@ -384,7 +384,6 @@ describe('MarkdownEditor', () => {
'should pass a11y checks',
checkAccessibility([
{
// eslint-disable-next-line react/display-name
content: () => createComponent(),
},
{
......
......@@ -72,7 +72,6 @@ describe('MarkdownView', () => {
it(
'should pass a11y checks',
checkAccessibility({
// eslint-disable-next-line react/display-name
content: () => <MarkdownView markdown="foo" />,
}),
);
......
......@@ -264,7 +264,6 @@ describe('Menu', () => {
'should pass a11y checks',
checkAccessibility([
{
// eslint-disable-next-line react/display-name
content: () => (
<Menu label={<TestLabel />} title="Test menu">
<TestMenuItem />
......
......@@ -281,7 +281,6 @@ describe('MenuItem', () => {
'should pass a11y checks',
checkAccessibility([
{
// eslint-disable-next-line react/display-name
content: () => (
<div role="menu">
<MenuItem label="Test item" />
......@@ -290,7 +289,6 @@ describe('MenuItem', () => {
},
{
name: 'menu radio button',
// eslint-disable-next-line react/display-name
content: () => (
<div role="menu">
<MenuItem label="Test" isSelected={false} />
......@@ -299,7 +297,6 @@ describe('MenuItem', () => {
},
{
name: 'with link',
// eslint-disable-next-line react/display-name
content: () => (
<div role="menu">
<MenuItem label="Test" href="https://foobar.com" />
......@@ -308,7 +305,6 @@ describe('MenuItem', () => {
},
{
name: 'with icon',
// eslint-disable-next-line react/display-name
content: () => (
<div role="menu">
<MenuItem label="Test" icon="edit" />
......@@ -317,7 +313,6 @@ describe('MenuItem', () => {
},
{
name: 'with submenu',
// eslint-disable-next-line react/display-name
content: () => (
<div role="menu">
<MenuItem
......
......@@ -123,7 +123,6 @@ describe('MenuKeyboardNavigation', () => {
it(
'should pass a11y checks',
checkAccessibility({
// eslint-disable-next-line react/display-name
content: () => (
<div>
<MenuKeyboardNavigation>
......
......@@ -11,7 +11,7 @@ export async function readExportFile(file: File): Promise<APIAnnotationData[]> {
let json;
try {
json = await readJSONFile(file);
} catch (err) {
} catch {
throw new Error('Not a valid JSON file');
}
......
......@@ -65,7 +65,6 @@ function extractMath(content: string): {
let pos = 0;
let replacedContent = content;
// eslint-disable-next-line no-constant-condition
while (true) {
const blockMathStart = replacedContent.indexOf('$$', pos);
const inlineMathStart = replacedContent.indexOf('\\(', pos);
......@@ -134,7 +133,7 @@ function insertMath(html: string, mathBlocks: MathBlock[]) {
displayMode: true,
});
}
} catch (err) {
} catch {
renderedMath = escapeHtml(block.expression);
}
return html.replace(mathPlaceholder(block.id), renderedMath);
......
......@@ -325,7 +325,7 @@ export class GroupsService {
directLinkedAnn.group,
);
featuredGroups.push(directLinkedAnnGroup);
} catch (e) {
} catch {
this._toastMessenger.error(
'Unable to fetch group for linked annotation',
);
......
......@@ -44,7 +44,7 @@ export class LocalStorageService {
$window.localStorage.setItem(testKey, testKey);
$window.localStorage.getItem(testKey);
$window.localStorage.removeItem(testKey);
} catch (e) {
} catch {
this._storage = new InMemoryStorage();
}
}
......
......@@ -39,7 +39,6 @@ const linksResponse = {
* Fake `retryPromiseOperation` that does not wait between retries.
*/
async function fakeRetryPromiseOperation(callback) {
// eslint-disable-next-line no-constant-condition
while (true) {
try {
const result = await callback();
......
......@@ -435,7 +435,7 @@ describe('LoadAnnotationsService', () => {
const svc = createService();
try {
await svc.loadThread('target_annotation');
} catch (e) {
} catch {
assert.calledOnce(fakeStore.annotationFetchStarted);
assert.calledOnce(fakeStore.annotationFetchFinished);
}
......
......@@ -294,7 +294,7 @@ describe('SessionService', () => {
const session = createService();
try {
await session.logout();
} catch (e) {
} catch {
// Ignored.
}
assert.calledWith(fakeToastMessenger.error, 'Log out failed');
......
......@@ -70,7 +70,7 @@ export async function fetchJSON(
let data;
try {
data = await response.json();
} catch (err) {
} catch {
throw new FetchError(url, response, 'Failed to parse response');
}
......
......@@ -95,7 +95,7 @@ export function init(config: SentryConfig) {
isTrusted: originalErr.isTrusted,
});
}
} catch (e) {
} catch {
// If something went wrong serializing the data, just ignore it.
}
......@@ -105,7 +105,7 @@ export function init(config: SentryConfig) {
try {
Sentry.setExtra('host_config', parseConfigFragment(window.location.href));
} catch (e) {
} catch {
// Ignore errors parsing configuration.
}
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment