diff --git a/dev-server/serve-dev.js b/dev-server/serve-dev.js
index 4d4630d69d4..4e1b858ed0d 100644
--- a/dev-server/serve-dev.js
+++ b/dev-server/serve-dev.js
@@ -130,7 +130,7 @@ export function serveDev(port, config) {
if (fs.existsSync(path)) {
const content = fs.readFileSync(path, 'utf8');
const headers = readCustomHeaderTags(content);
- for (let [key, value] of headers) {
+ for (const [key, value] of headers) {
res.set(key, value);
}
res.render(req.params.document, templateContext(config));
diff --git a/eslint.config.js b/eslint.config.js
index e960242eca6..2030200b1d4 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -1,9 +1,9 @@
-import hypothesis from 'eslint-config-hypothesis';
-import jsxA11y from 'eslint-plugin-jsx-a11y';
+import hypothesisBase from 'eslint-config-hypothesis/base';
+import hypothesisJSX from 'eslint-config-hypothesis/jsx';
+import hypothesisTS from 'eslint-config-hypothesis/ts';
import globals from 'globals';
-import tseslint from 'typescript-eslint';
-export default tseslint.config(
+export default [
{
ignores: [
'.tox/**/*',
@@ -16,41 +16,10 @@ export default tseslint.config(
'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',
- },
- },
+ ...hypothesisBase,
+ ...hypothesisJSX,
+ ...hypothesisTS,
// Annotator module
{
@@ -85,4 +54,4 @@ export default tseslint.config(
},
},
},
-);
+];
diff --git a/package.json b/package.json
index bce268458c2..9c5325bafbe 100644
--- a/package.json
+++ b/package.json
@@ -57,7 +57,7 @@
"escape-html": "^1.0.3",
"escape-string-regexp": "^4.0.0",
"eslint": "^9.12.0",
- "eslint-config-hypothesis": "^3.0.0",
+ "eslint-config-hypothesis": "^3.1.0",
"eslint-plugin-jsx-a11y": "^6.10.0",
"eslint-plugin-mocha": "^10.5.0",
"eslint-plugin-react": "^7.37.1",
diff --git a/scripts/generate-change-list.js b/scripts/generate-change-list.js
index 77aad6a0e80..5f363f6f6a4 100755
--- a/scripts/generate-change-list.js
+++ b/scripts/generate-change-list.js
@@ -43,7 +43,7 @@ function getHighestVersionTag() {
*/
async function* itemsInGitHubAPIResponse(octokit, options) {
for await (const page of octokit.paginate.iterator(options)) {
- for (let item of page.data) {
+ for (const item of page.data) {
yield item;
}
}
diff --git a/src/annotator/anchoring/test/xpath-test.js b/src/annotator/anchoring/test/xpath-test.js
index 3c7c13bb9b6..ad7de4d69a9 100644
--- a/src/annotator/anchoring/test/xpath-test.js
+++ b/src/annotator/anchoring/test/xpath-test.js
@@ -70,7 +70,7 @@ describe('annotator/anchoring/xpath', () => {
},
].forEach(test => {
it('produces the correct xpath for the provided node', () => {
- let node = document.getElementById(test.id);
+ const node = document.getElementById(test.id);
assert.equal(xpathFromNode(node, document.body), test.xpaths[0]);
});
diff --git a/src/annotator/config/test/app-test.js b/src/annotator/config/test/app-test.js
index c822c1fc6a7..2df2f89601e 100644
--- a/src/annotator/config/test/app-test.js
+++ b/src/annotator/config/test/app-test.js
@@ -77,7 +77,7 @@ describe('createAppConfig', () => {
'onHelpRequest',
];
const service = {};
- for (let callback of callbacks) {
+ for (const callback of callbacks) {
service[callback] = sinon.stub();
}
@@ -87,7 +87,7 @@ describe('createAppConfig', () => {
const sidebarConfig = createAppConfig(appURL, config);
const sidebarServiceConfig = sidebarConfig.services[0];
- for (let callback of callbacks) {
+ for (const callback of callbacks) {
assert.equal(sidebarServiceConfig[callback + 'Provided'], true);
}
});
diff --git a/src/annotator/integrations/test/html-metadata-test.js b/src/annotator/integrations/test/html-metadata-test.js
index cb911ff2d23..3c9e8c600ab 100644
--- a/src/annotator/integrations/test/html-metadata-test.js
+++ b/src/annotator/integrations/test/html-metadata-test.js
@@ -82,7 +82,7 @@ describe('HTMLMetadata', () => {
},
];
- for (let source of sources) {
+ for (const source of sources) {
const metadata = testDocument.getDocumentMetadata();
assert.equal(metadata.title, source.value);
diff --git a/src/annotator/integrations/test/html-test.js b/src/annotator/integrations/test/html-test.js
index a623de0ae3e..df66000dbb0 100644
--- a/src/annotator/integrations/test/html-test.js
+++ b/src/annotator/integrations/test/html-test.js
@@ -182,7 +182,7 @@ describe('HTMLIntegration', () => {
const listeners = features.on.args;
integration.destroy();
- for (let [event, callback] of listeners) {
+ for (const [event, callback] of listeners) {
assert.calledWith(features.off, event, callback);
}
});
diff --git a/src/annotator/integrations/test/image-text-layer-test.js b/src/annotator/integrations/test/image-text-layer-test.js
index 6becb69b299..be3520198d5 100644
--- a/src/annotator/integrations/test/image-text-layer-test.js
+++ b/src/annotator/integrations/test/image-text-layer-test.js
@@ -299,7 +299,7 @@ describe('ImageTextLayer', () => {
const tolerance = 0.01;
assert.equal(originalBoxes.length, newBoxes.length);
- for (let [i, originalBox] of originalBoxes.entries()) {
+ for (const [i, originalBox] of originalBoxes.entries()) {
const newBox = newBoxes[i];
const leftGap = originalBox.left - imageBox.left;
diff --git a/src/annotator/integrations/test/vitalsource-test.js b/src/annotator/integrations/test/vitalsource-test.js
index 56c9fdb9e4a..6183a949826 100644
--- a/src/annotator/integrations/test/vitalsource-test.js
+++ b/src/annotator/integrations/test/vitalsource-test.js
@@ -379,7 +379,7 @@ describe('annotator/integrations/vitalsource', () => {
const events = ['mouseup', 'mousedown', 'mouseout'];
- for (let eventName of events) {
+ for (const eventName of events) {
const listener = sinon.stub();
document.addEventListener(eventName, listener);
diff --git a/src/annotator/test/annotation-counts-test.js b/src/annotator/test/annotation-counts-test.js
index 1e7e20d41f0..da1d566f954 100644
--- a/src/annotator/test/annotation-counts-test.js
+++ b/src/annotator/test/annotation-counts-test.js
@@ -34,7 +34,6 @@ describe('annotationCounts', () => {
describe('listen for "publicAnnotationCountChanged" event', () => {
const emitEvent = function () {
- let crossFrameArgs;
let evt;
let fn;
@@ -42,7 +41,7 @@ describe('annotationCounts', () => {
const args =
2 <= arguments.length ? Array.prototype.slice.call(arguments, 1) : [];
- crossFrameArgs = fakeCrossFrame.on.args;
+ const crossFrameArgs = fakeCrossFrame.on.args;
for (let i = 0, len = crossFrameArgs.length; i < len; i++) {
evt = crossFrameArgs[i][0];
fn = crossFrameArgs[i][1];
diff --git a/src/annotator/test/guest-test.js b/src/annotator/test/guest-test.js
index 1323f72b408..de514e78609 100644
--- a/src/annotator/test/guest-test.js
+++ b/src/annotator/test/guest-test.js
@@ -79,7 +79,7 @@ describe('Guest', () => {
};
const emitSidebarEvent = (event, ...args) => {
- for (let [evt, fn] of sidebarRPC().on.args) {
+ for (const [evt, fn] of sidebarRPC().on.args) {
if (event === evt) {
fn(...args);
}
diff --git a/src/annotator/test/highlighter-test.js b/src/annotator/test/highlighter-test.js
index 1ed28266062..1998b5a705a 100644
--- a/src/annotator/test/highlighter-test.js
+++ b/src/annotator/test/highlighter-test.js
@@ -408,7 +408,7 @@ describe('annotator/highlighter', () => {
* Returns all the highlight elements.
*/
function createHighlights(root, cssClass = '') {
- let highlights = [];
+ const highlights = [];
for (let i = 0; i < 3; i++) {
const span = document.createElement('span');
diff --git a/src/annotator/test/sidebar-test.js b/src/annotator/test/sidebar-test.js
index fb985d5a5e1..57fcdeda701 100644
--- a/src/annotator/test/sidebar-test.js
+++ b/src/annotator/test/sidebar-test.js
@@ -52,7 +52,7 @@ describe('Sidebar', () => {
const emitNthGuestEvent = (index = 1, event, ...args) => {
const result = [];
- for (let [evt, fn] of guestRPC(index).on.args) {
+ for (const [evt, fn] of guestRPC(index).on.args) {
if (event === evt) {
result.push(fn(...args));
}
@@ -66,7 +66,7 @@ describe('Sidebar', () => {
const emitSidebarEvent = (event, ...args) => {
const result = [];
- for (let [evt, fn] of sidebarRPC().on.args) {
+ for (const [evt, fn] of sidebarRPC().on.args) {
if (event === evt) {
result.push(fn(...args));
}
diff --git a/src/annotator/util/test/normalize-test.js b/src/annotator/util/test/normalize-test.js
index 4c0843b8b9a..e84b87832be 100644
--- a/src/annotator/util/test/normalize-test.js
+++ b/src/annotator/util/test/normalize-test.js
@@ -117,7 +117,7 @@ describe('annotator/util/normalize', () => {
const end = start + 'bar'.length;
const outStrs = ['', 'foo b', 'fooba'];
- for (let outStr of outStrs) {
+ for (const outStr of outStrs) {
const [outStart, outEnd] = translateOffsets(
inStr,
outStr,
diff --git a/src/annotator/util/test/preact-container-test.js b/src/annotator/util/test/preact-container-test.js
index 2e6edf2ccad..b44deed1fc2 100644
--- a/src/annotator/util/test/preact-container-test.js
+++ b/src/annotator/util/test/preact-container-test.js
@@ -44,7 +44,7 @@ describe('PreactContainer', () => {
});
it('should unmount and remove element when `destroy` is called', () => {
- let label = 'foo';
+ const label = 'foo';
const container = new PreactContainer('widget', () => (
));
diff --git a/src/boot/test/boot-test.js b/src/boot/test/boot-test.js
index 3aeac328d7f..383c6affdb2 100644
--- a/src/boot/test/boot-test.js
+++ b/src/boot/test/boot-test.js
@@ -40,7 +40,7 @@ describe('bootstrap', () => {
return manifest;
}, {});
- let extraSettings = {};
+ const extraSettings = {};
let bootApp;
if (app === 'annotator') {
bootApp = bootHypothesisClient;
diff --git a/src/shared/messaging/test/port-finder-test.js b/src/shared/messaging/test/port-finder-test.js
index 5a42f8b7fe1..a022aec0384 100644
--- a/src/shared/messaging/test/port-finder-test.js
+++ b/src/shared/messaging/test/port-finder-test.js
@@ -11,8 +11,6 @@ import {
const requestId = 'abcdef';
describe('PortFinder', () => {
- let fakeGenerateHexString;
-
const frame1 = 'guest';
const type = 'offer';
let portFinder;
@@ -36,7 +34,7 @@ describe('PortFinder', () => {
}
// Generate predictable IDs for port requests
- fakeGenerateHexString = sinon.stub().returns(requestId);
+ const fakeGenerateHexString = sinon.stub().returns(requestId);
beforeEach(() => {
portFinders = [];
diff --git a/src/sidebar/components/hooks/test/use-filter-options-test.js b/src/sidebar/components/hooks/test/use-filter-options-test.js
index e77a4dcc82f..ac61e9f3b3a 100644
--- a/src/sidebar/components/hooks/test/use-filter-options-test.js
+++ b/src/sidebar/components/hooks/test/use-filter-options-test.js
@@ -9,10 +9,10 @@ describe('sidebar/components/hooks/use-user-filter-options', () => {
let lastUserOptions;
// Mock `annotationDisplayName` as if it's returning display names
- let fakeAnnotationUserDisplay = annotation =>
+ const fakeAnnotationUserDisplay = annotation =>
annotation.user_info.display_name;
// Mock `annotationDisplayName` as if it's returning usernames
- let fakeAnnotationUserUsername = annotation => annotation.user;
+ const fakeAnnotationUserUsername = annotation => annotation.user;
// Mount a dummy component to be able to use the hook
function DummyComponent() {
diff --git a/src/sidebar/components/test/MarkdownEditor-test.js b/src/sidebar/components/test/MarkdownEditor-test.js
index c31348bc53e..d7940f5d6ec 100644
--- a/src/sidebar/components/test/MarkdownEditor-test.js
+++ b/src/sidebar/components/test/MarkdownEditor-test.js
@@ -319,7 +319,7 @@ describe('MarkdownEditor', () => {
.simulate('keydown', { key });
function testArrowKeySequence(buttons) {
- for (let button of buttons) {
+ for (const button of buttons) {
pressKey('ArrowRight');
const label =
document.activeElement.getAttribute('title') ||
diff --git a/src/sidebar/components/test/MenuItem-test.js b/src/sidebar/components/test/MenuItem-test.js
index e7c8d11443c..f63197a158f 100644
--- a/src/sidebar/components/test/MenuItem-test.js
+++ b/src/sidebar/components/test/MenuItem-test.js
@@ -11,7 +11,7 @@ import MenuItem, { $imports } from '../MenuItem';
describe('MenuItem', () => {
let containers = [];
const createMenuItem = props => {
- let newContainer = document.createElement('div');
+ const newContainer = document.createElement('div');
containers.push(newContainer);
document.body.appendChild(newContainer);
return mount(, {
diff --git a/src/sidebar/components/test/MenuKeyboardNavigation-test.js b/src/sidebar/components/test/MenuKeyboardNavigation-test.js
index 640d055b53d..205254e154d 100644
--- a/src/sidebar/components/test/MenuKeyboardNavigation-test.js
+++ b/src/sidebar/components/test/MenuKeyboardNavigation-test.js
@@ -12,7 +12,7 @@ describe('MenuKeyboardNavigation', () => {
let containers = [];
const createMenuItem = props => {
- let newContainer = document.createElement('div');
+ const newContainer = document.createElement('div');
containers.push(newContainer);
document.body.appendChild(newContainer);
return mount(
diff --git a/src/sidebar/components/test/TagEditor-test.js b/src/sidebar/components/test/TagEditor-test.js
index 9d089a3a7dc..82c60fcfa9f 100644
--- a/src/sidebar/components/test/TagEditor-test.js
+++ b/src/sidebar/components/test/TagEditor-test.js
@@ -11,7 +11,7 @@ import { $imports } from '../TagEditor';
describe('TagEditor', () => {
let containers = [];
- let fakeTags = ['tag1', 'tag2'];
+ const fakeTags = ['tag1', 'tag2'];
let fakeTagsService;
let fakeServiceUrl;
let fakeOnAddTag;
@@ -21,7 +21,7 @@ describe('TagEditor', () => {
function createComponent(props) {
// Use an array of containers so we can test more
// than one component at a time.
- let newContainer = document.createElement('div');
+ const newContainer = document.createElement('div');
containers.push(newContainer);
document.body.appendChild(newContainer);
return mount(
diff --git a/src/sidebar/helpers/test/query-parser-test.js b/src/sidebar/helpers/test/query-parser-test.js
index 2ec99856864..41f3d681c38 100644
--- a/src/sidebar/helpers/test/query-parser-test.js
+++ b/src/sidebar/helpers/test/query-parser-test.js
@@ -17,7 +17,7 @@ describe('sidebar/helpers/query-parser', () => {
assert.isFalse(isEmptyFilter(parseHypothesisSearchQuery('some query')));
// Now check various queries which should produce empty filters
- for (let emptyQuery of ['', '""', "''", ' ']) {
+ for (const emptyQuery of ['', '""', "''", ' ']) {
const result = parseHypothesisSearchQuery(emptyQuery);
assert.isTrue(
isEmptyFilter(result),
diff --git a/src/sidebar/services/test/load-annotations-test.js b/src/sidebar/services/test/load-annotations-test.js
index a0c361885dc..a91b69f8b92 100644
--- a/src/sidebar/services/test/load-annotations-test.js
+++ b/src/sidebar/services/test/load-annotations-test.js
@@ -383,7 +383,7 @@ describe('LoadAnnotationsService', () => {
});
describe('#loadThread', () => {
- let threadAnnotations = [
+ const threadAnnotations = [
{ id: 'parent_annotation_1' },
{ id: 'parent_annotation_2', references: ['parent_annotation_1'] },
{
diff --git a/src/sidebar/store/modules/test/filters-test.js b/src/sidebar/store/modules/test/filters-test.js
index 6807afc1e52..fa80781cf73 100644
--- a/src/sidebar/store/modules/test/filters-test.js
+++ b/src/sidebar/store/modules/test/filters-test.js
@@ -4,7 +4,7 @@ import { selectionModule } from '../selection';
describe('sidebar/store/modules/filters', () => {
let store;
- let fakeSettings = [{}, {}];
+ const fakeSettings = [{}, {}];
const getFiltersState = () => {
return store.getState().filters;
diff --git a/src/sidebar/store/modules/test/groups-test.js b/src/sidebar/store/modules/test/groups-test.js
index c2aa9340771..62775c023f0 100644
--- a/src/sidebar/store/modules/test/groups-test.js
+++ b/src/sidebar/store/modules/test/groups-test.js
@@ -58,7 +58,7 @@ describe('sidebar/store/modules/groups', () => {
};
let allGroups = [];
- for (let groups of Object.values(allLists)) {
+ for (const groups of Object.values(allLists)) {
allGroups = allGroups.concat(groups);
}
diff --git a/src/sidebar/store/modules/test/real-time-updates-test.js b/src/sidebar/store/modules/test/real-time-updates-test.js
index 8baa89428bf..c1c0da2ba0c 100644
--- a/src/sidebar/store/modules/test/real-time-updates-test.js
+++ b/src/sidebar/store/modules/test/real-time-updates-test.js
@@ -11,7 +11,7 @@ describe('sidebar/store/modules/real-time-updates', () => {
let fakeAnnotationExists;
let fakeFocusedGroupId;
let fakeRoute;
- let fakeSettings = {};
+ const fakeSettings = {};
let store;
beforeEach(() => {
diff --git a/src/sidebar/store/modules/test/selection-test.js b/src/sidebar/store/modules/test/selection-test.js
index 604e563dbb0..960ab336a57 100644
--- a/src/sidebar/store/modules/test/selection-test.js
+++ b/src/sidebar/store/modules/test/selection-test.js
@@ -7,7 +7,7 @@ import { selectionModule } from '../selection';
describe('sidebar/store/modules/selection', () => {
let store;
- let fakeSettings = [{}, {}];
+ const fakeSettings = [{}, {}];
const getSelectionState = () => {
return store.getState().selection;
diff --git a/src/sidebar/store/test/util-test.js b/src/sidebar/store/test/util-test.js
index 5f7a1177f85..fd3205e1f29 100644
--- a/src/sidebar/store/test/util-test.js
+++ b/src/sidebar/store/test/util-test.js
@@ -42,11 +42,10 @@ describe('sidebar/store/util', () => {
});
it('should wait for awaitStateChange to return a non-null value', () => {
- let valPromise;
const expected = 5;
store.setState({ val: 2 });
- valPromise = awaitStateChange(store, getValWhenGreaterThanTwo);
+ const valPromise = awaitStateChange(store, getValWhenGreaterThanTwo);
store.setState({ val: 5 });
return valPromise.then(actual => {
diff --git a/src/sidebar/test/cross-origin-rpc-test.js b/src/sidebar/test/cross-origin-rpc-test.js
index d8b24388328..4ec6cbf40d5 100644
--- a/src/sidebar/test/cross-origin-rpc-test.js
+++ b/src/sidebar/test/cross-origin-rpc-test.js
@@ -250,7 +250,7 @@ describe('sidebar/cross-origin-rpc', () => {
it("responds with an error if there's no method", () => {
startServer(fakeStore, settings, fakeWindow);
- let jsonRpcRequest = { jsonrpc: '2.0', id: 42 }; // No "method" member.
+ const jsonRpcRequest = { jsonrpc: '2.0', id: 42 }; // No "method" member.
fakeWindow.emitter.emit('message', {
origin: 'https://allowed1.com',
diff --git a/yarn.lock b/yarn.lock
index e5c3f2dfd8f..7b7262aefeb 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7116,14 +7116,26 @@ __metadata:
languageName: node
linkType: hard
-"eslint-config-hypothesis@npm:^3.0.0":
- version: 3.0.0
- resolution: "eslint-config-hypothesis@npm:3.0.0"
+"eslint-config-hypothesis@npm:^3.1.0":
+ version: 3.1.0
+ resolution: "eslint-config-hypothesis@npm:3.1.0"
peerDependencies:
+ eslint-plugin-jsx-a11y: ">=6.10.0"
eslint-plugin-mocha: ">=10.4.0"
eslint-plugin-react: ">=7.34.0"
eslint-plugin-react-hooks: ">=5.0.0"
- checksum: 1194156f2d043ed866afe9ccd88d7364cdf8635cb9d762d24beffb03ee02a25d44081b58d95c5b1a658e686053948d8b2493e65f4de540e14d72ae83c35c31fe
+ globals: ">=15.11.0"
+ typescript-eslint: ">=8.10.0"
+ peerDependenciesMeta:
+ eslint-plugin-jsx-a11y:
+ optional: true
+ eslint-plugin-react:
+ optional: true
+ eslint-plugin-react-hooks:
+ optional: true
+ typescript-eslint:
+ optional: true
+ checksum: 12e17da643a306fec1c8121bf5f172f2132f816d85b2b6f626dc16d353f5cbac8bee0cce080a9df829c5eab3e4bcc007bb611ec6f5d9deebd627e530802c05f6
languageName: node
linkType: hard
@@ -8746,7 +8758,7 @@ __metadata:
escape-html: ^1.0.3
escape-string-regexp: ^4.0.0
eslint: ^9.12.0
- eslint-config-hypothesis: ^3.0.0
+ eslint-config-hypothesis: ^3.1.0
eslint-plugin-jsx-a11y: ^6.10.0
eslint-plugin-mocha: ^10.5.0
eslint-plugin-react: ^7.37.1