diff --git a/package-lock.json b/package-lock.json index d199fb327..678a8d6d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@wikimedia/codex-icons": "1.6.1", "@wikimedia/mw-node-qunit": "7.2.0", "@wikimedia/types-wikimedia": "0.4.4", - "eslint-config-wikimedia": "0.27.0", + "eslint-config-wikimedia": "0.28.0", "eslint-plugin-no-jquery": "2.7.0", "grunt-banana-checker": "0.13.0", "jest": "27.4.7", @@ -617,11 +617,14 @@ } }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.42.0.tgz", - "integrity": "sha512-R1w57YlVA6+YE01wch3GPYn6bCsrOV3YW/5oGGE2tmX6JcL9Nr+b5IikrjMPF+v9CV3ay+obImEdsDhovhJrzw==", + "version": "0.43.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.43.1.tgz", + "integrity": "sha512-I238eDtOolvCuvtxrnqtlBaw0BwdQuYqK7eA6XIonicMdOOOb75mqdIzkGDUbS04+1Di007rgm9snFRNeVrOog==", "dev": true, "dependencies": { + "@types/eslint": "^8.56.5", + "@types/estree": "^1.0.5", + "@typescript-eslint/types": "^7.2.0", "comment-parser": "1.4.1", "esquery": "^1.5.0", "jsdoc-type-pratt-parser": "~4.0.0" @@ -1663,6 +1666,22 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/eslint": { + "version": "8.56.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", + "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -1770,12 +1789,6 @@ "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "dev": true }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true - }, "node_modules/@types/sizzle": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", @@ -1816,16 +1829,16 @@ "dev": true }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.12.0.tgz", + "integrity": "sha512-itF1pTnN6F3unPak+kutH9raIkL3lhH1YRPGgt7QQOh43DQKVJXmWkpb+vpc/TiDHs6RSd9CTbDsc/Y+Ygq7kg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" + "@typescript-eslint/types": "7.12.0", + "@typescript-eslint/visitor-keys": "7.12.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -1833,12 +1846,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.12.0.tgz", + "integrity": "sha512-o+0Te6eWp2ppKY3mLCU+YA9pVJxhUJE15FV7kxuD9jgwIAa+w/ycGJBMrYDTpVGUM/tgpa9SeMOugSabWFq7bg==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -1846,21 +1859,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.12.0.tgz", + "integrity": "sha512-5bwqLsWBULv1h6pn7cMW5dXX/Y2amRqLaKqsASVwbBHMZSnHqE/HN4vT4fE0aFsiwxYvr98kqOWh1a8ZKXalCQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", + "@typescript-eslint/types": "7.12.0", + "@typescript-eslint/visitor-keys": "7.12.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -1872,6 +1886,30 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { "version": "7.6.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", @@ -1885,76 +1923,38 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.12.0.tgz", + "integrity": "sha512-Y6hhwxwDx41HNpjuYswYp6gDbkiZ8Hin9Bf5aJQn1bpTs3afYY4GX+MPYxma8jtoIV2GRwTM/UJm/2uGCVv+DQ==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.12.0", + "@typescript-eslint/types": "7.12.0", + "@typescript-eslint/typescript-estree": "7.12.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.12.0.tgz", + "integrity": "sha512-uZk7DevrQLL3vSnfFl5bj4sL75qC9D6EdjemIdbtkuUmIheWpuiiylSY01JxJE7+zGrOWDZrp1WxOuDntvKrHQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "7.12.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -2678,9 +2678,9 @@ } }, "node_modules/browserslist-config-wikimedia": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/browserslist-config-wikimedia/-/browserslist-config-wikimedia-0.6.1.tgz", - "integrity": "sha512-F3O+12ud7ZwBaiB/RZIMGDgz3nEuXz8RhtdPB4Lkd/WVP5Vy77EqBWRMz4vJ64x8LTTH3BOaHCD2ZuUcgShqyQ==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/browserslist-config-wikimedia/-/browserslist-config-wikimedia-0.7.0.tgz", + "integrity": "sha512-CTa0lv78dXKEgrYsOLCkqO+9UUS3CV9MWEOYHcymgEvx4mYxB80sCoKRCR7wW2SOMNxjaP9hohrZripjnKuRTA==", "dev": true }, "node_modules/bs-logger": { @@ -2722,27 +2722,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/builtins": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.1.0.tgz", - "integrity": "sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==", - "dev": true, - "dependencies": { - "semver": "^7.0.0" - } - }, - "node_modules/builtins/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3709,6 +3688,19 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/enhanced-resolve": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", + "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -3862,28 +3854,28 @@ } }, "node_modules/eslint-config-wikimedia": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.27.0.tgz", - "integrity": "sha512-KkZ54+MUnggz17C/RCEMXQSpiiqZRF7p9fjrz4phaaeKlTrjg0B+QbM5zcDWcjGiAWaJUptHaH17+RZldadkUw==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/eslint-config-wikimedia/-/eslint-config-wikimedia-0.28.0.tgz", + "integrity": "sha512-qDkELhXoDvQP+kYYo5MilhvqJ4Agk78Pzu95K1l1h68xPEWagD4k/dvrgmVyKjcp5KyAnZr++7qChLiFhDdBDA==", "dev": true, "dependencies": { - "browserslist-config-wikimedia": "^0.6.1", + "browserslist-config-wikimedia": "^0.7.0", "eslint": "^8.57.0", "eslint-plugin-compat": "^4.2.0", "eslint-plugin-es-x": "^7.6.0", - "eslint-plugin-jest": "^27.9.0", - "eslint-plugin-jsdoc": "48.2.1", - "eslint-plugin-json-es": "^1.5.7", - "eslint-plugin-mediawiki": "^0.6.0", - "eslint-plugin-mocha": "^10.4.1", - "eslint-plugin-n": "^16.6.2", + "eslint-plugin-jest": "^28.5.0", + "eslint-plugin-jsdoc": "48.2.5", + "eslint-plugin-json-es": "^1.6.0", + "eslint-plugin-mediawiki": "^0.7.0", + "eslint-plugin-mocha": "^10.4.3", + "eslint-plugin-n": "^17.7.0", "eslint-plugin-no-jquery": "^2.7.0", "eslint-plugin-qunit": "^8.1.1", "eslint-plugin-security": "^1.7.1", - "eslint-plugin-unicorn": "^51.0.1", - "eslint-plugin-vue": "^9.23.0", + "eslint-plugin-unicorn": "^53.0.0", + "eslint-plugin-vue": "^9.26.0", "eslint-plugin-wdio": "^8.24.12", - "eslint-plugin-yml": "^1.13.2" + "eslint-plugin-yml": "^1.14.0" } }, "node_modules/eslint-plugin-compat": { @@ -4001,19 +3993,19 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "27.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz", - "integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==", + "version": "28.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.5.0.tgz", + "integrity": "sha512-6np6DGdmNq/eBbA7HOUNV8fkfL86PYwBfwyb8n23FXgJNTR8+ot3smRHjza9LGsBBZRypK3qyF79vMjohIL8eQ==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "^5.10.0" + "@typescript-eslint/utils": "^6.0.0 || ^7.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^16.10.0 || ^18.12.0 || >=20.0.0" }, "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0 || ^7.0.0", - "eslint": "^7.0.0 || ^8.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0", + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", "jest": "*" }, "peerDependenciesMeta": { @@ -4026,19 +4018,19 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "48.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.1.tgz", - "integrity": "sha512-iUvbcyDZSO/9xSuRv2HQBw++8VkV/pt3UWtX9cpPH0l7GKPq78QC/6+PmyQHHvNZaTjAce6QVciEbnc6J/zH5g==", + "version": "48.2.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.2.5.tgz", + "integrity": "sha512-ZeTfKV474W1N9niWfawpwsXGu+ZoMXu4417eBROX31d7ZuOk8zyG66SO77DpJ2+A9Wa2scw/jRqBPnnQo7VbcQ==", "dev": true, "dependencies": { - "@es-joy/jsdoccomment": "~0.42.0", + "@es-joy/jsdoccomment": "~0.43.0", "are-docs-informative": "^0.0.2", "comment-parser": "1.4.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "esquery": "^1.5.0", "is-builtin-module": "^3.2.1", - "semver": "^7.6.0", + "semver": "^7.6.1", "spdx-expression-parse": "^4.0.0" }, "engines": { @@ -4086,9 +4078,9 @@ } }, "node_modules/eslint-plugin-mediawiki": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-mediawiki/-/eslint-plugin-mediawiki-0.6.0.tgz", - "integrity": "sha512-a2Zm18N5nPyflBajM2ZWATxucIpYPEmOSjFzUR1OBH3hAL0GY9fx1mpezEwzqAQ862d+kPkolgQOzktnZe8nKA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mediawiki/-/eslint-plugin-mediawiki-0.7.0.tgz", + "integrity": "sha512-1Y2nsFDPp96xOZCB5ivZAgqYe9i6w2u64VoCIaAzPyZnd/2h8VQR3CtD+u4Yk/KrpbKq9AAJjrs5LS8VAz6KOA==", "dev": true, "dependencies": { "eslint-plugin-vue": "^9.23.0", @@ -4143,48 +4135,66 @@ } }, "node_modules/eslint-plugin-n": { - "version": "16.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", - "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.8.1.tgz", + "integrity": "sha512-KdG0h0voZms8UhndNu8DeWx1eM4sY+A4iXtsNo6kOfJLYHNeTGPacGalJ9GcvrbmOL3r/7QOMwVZDSw+1SqsrA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "builtins": "^5.0.1", + "enhanced-resolve": "^5.17.0", "eslint-plugin-es-x": "^7.5.0", "get-tsconfig": "^4.7.0", - "globals": "^13.24.0", + "globals": "^15.0.0", "ignore": "^5.2.4", - "is-builtin-module": "^3.2.1", - "is-core-module": "^2.12.1", - "minimatch": "^3.1.2", - "resolve": "^1.22.2", + "minimatch": "^9.0.0", "semver": "^7.5.3" }, "engines": { - "node": ">=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" + "url": "https://opencollective.com/eslint" }, "peerDependencies": { - "eslint": ">=7.0.0" + "eslint": ">=8.23.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" } }, "node_modules/eslint-plugin-n/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.3.0.tgz", + "integrity": "sha512-cCdyVjIUVTtX8ZsPkq1oCsOsLmGIswqnjZYMJJTGaNApj1yHtLSymKhwH51ttirREn75z3p4k051clwg7rvNKA==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint-plugin-n/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/eslint-plugin-n/node_modules/semver": { "version": "7.6.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", @@ -4197,18 +4207,6 @@ "node": ">=10" } }, - "node_modules/eslint-plugin-n/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint-plugin-no-jquery": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/eslint-plugin-no-jquery/-/eslint-plugin-no-jquery-2.7.0.tgz", @@ -4241,17 +4239,17 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "51.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", - "integrity": "sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==", + "version": "53.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-53.0.0.tgz", + "integrity": "sha512-kuTcNo9IwwUCfyHGwQFOK/HjJAYzbODHN3wP0PgqbW+jbXqpNWxNVpVhj2tO9SixBwuAdmal8rVcWKBxwFnGuw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.5", "@eslint-community/eslint-utils": "^4.4.0", - "@eslint/eslintrc": "^2.1.4", + "@eslint/eslintrc": "^3.0.2", "ci-info": "^4.0.0", "clean-regexp": "^1.0.0", - "core-js-compat": "^3.34.0", + "core-js-compat": "^3.37.0", "esquery": "^1.5.0", "indent-string": "^4.0.0", "is-builtin-module": "^3.2.1", @@ -4260,11 +4258,11 @@ "read-pkg-up": "^7.0.1", "regexp-tree": "^0.1.27", "regjsparser": "^0.10.0", - "semver": "^7.5.4", + "semver": "^7.6.1", "strip-indent": "^3.0.0" }, "engines": { - "node": ">=16" + "node": ">=18.18" }, "funding": { "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1" @@ -4273,6 +4271,88 @@ "eslint": ">=8.56.0" } }, + "node_modules/eslint-plugin-unicorn/node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint-plugin-unicorn/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/espree": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-unicorn/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/eslint-plugin-unicorn/node_modules/jsesc": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", @@ -10305,6 +10385,15 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -10480,6 +10569,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-jest": { "version": "27.1.5", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.5.tgz", @@ -10565,27 +10666,6 @@ "node": ">=0.10.0" } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -10804,9 +10884,9 @@ "dev": true }, "node_modules/vue-eslint-parser": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz", - "integrity": "sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", + "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", "dev": true, "dependencies": { "debug": "^4.3.4", diff --git a/package.json b/package.json index ae34f765e..fe2235ea4 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@wikimedia/codex-icons": "1.6.1", "@wikimedia/mw-node-qunit": "7.2.0", "@wikimedia/types-wikimedia": "0.4.4", - "eslint-config-wikimedia": "0.27.0", + "eslint-config-wikimedia": "0.28.0", "eslint-plugin-no-jquery": "2.7.0", "grunt-banana-checker": "0.13.0", "jest": "27.4.7", diff --git a/resources/skins.vector.clientPreferences/clientPreferences.js b/resources/skins.vector.clientPreferences/clientPreferences.js index 2870f9075..4933666b9 100644 --- a/resources/skins.vector.clientPreferences/clientPreferences.js +++ b/resources/skins.vector.clientPreferences/clientPreferences.js @@ -63,7 +63,7 @@ function toggleDocClassAndSave( featureName, value, config ) { } ); document.documentElement.classList.add( `${ featureName }-clientpref-${ value }` ); // Ideally this should be taken care of via a single core helper function. - mw.util.debounce( function () { + mw.util.debounce( () => { api = api || new mw.Api(); api.saveOption( pref.preferenceKey, value ).then( () => { callback(); @@ -213,9 +213,8 @@ function createRow( className ) { * @param {string} featureName * @return {MwMessage} */ -const getFeatureLabelMsg = ( featureName ) => - // eslint-disable-next-line mediawiki/msg-doc - mw.message( `${ featureName }-name` ); +// eslint-disable-next-line mediawiki/msg-doc +const getFeatureLabelMsg = ( featureName ) => mw.message( `${ featureName }-name` ); /** * adds a toggle button diff --git a/resources/skins.vector.js/dropdownMenus.js b/resources/skins.vector.js/dropdownMenus.js index 2a031619e..e3254fd6a 100644 --- a/resources/skins.vector.js/dropdownMenus.js +++ b/resources/skins.vector.js/dropdownMenus.js @@ -16,7 +16,7 @@ function dropdownMenus( containers ) { // Search for all dropdown containers using the CHECKBOX_HACK_CONTAINER_SELECTOR. containers = containers || document.querySelectorAll( CHECKBOX_HACK_CONTAINER_SELECTOR ); - Array.prototype.forEach.call( containers, function ( container ) { + Array.prototype.forEach.call( containers, ( container ) => { const checkbox = container.querySelector( CHECKBOX_HACK_CHECKBOX_SELECTOR ), button = container.querySelector( CHECKBOX_HACK_BUTTON_SELECTOR ), @@ -157,7 +157,7 @@ function addPortletLinkHandler( item, data ) { // Enhance previously added items. Array.prototype.forEach.call( document.querySelectorAll( '.mw-list-item-js' ), - function ( item ) { + ( item ) => { addPortletLinkHandler( item, { id: item.getAttribute( 'id' ) } ); diff --git a/resources/skins.vector.js/echo.js b/resources/skins.vector.js/echo.js index 861a47291..f337722a2 100644 --- a/resources/skins.vector.js/echo.js +++ b/resources/skins.vector.js/echo.js @@ -7,7 +7,7 @@ function init() { return; } - mw.hook( 'ext.echo.NotificationBadgeWidget.onInitialize' ).add( function ( badge ) { + mw.hook( 'ext.echo.NotificationBadgeWidget.onInitialize' ).add( ( badge ) => { const element = badge.$element[ 0 ]; element.classList.add( 'mw-list-item' ); const anchor = /** @type {HTMLElement} */ ( element.querySelector( 'a' ) ); diff --git a/resources/skins.vector.js/features.js b/resources/skins.vector.js/features.js index 2969c4892..b24e7e535 100644 --- a/resources/skins.vector.js/features.js +++ b/resources/skins.vector.js/features.js @@ -23,7 +23,7 @@ function save( feature, enabled ) { break; } } else { - debounce( function () { + debounce( () => { api = api || new mw.Api(); api.saveOption( 'vector-' + feature, enabled ? 1 : 0 ); }, 500 )(); diff --git a/resources/skins.vector.js/languageButton.js b/resources/skins.vector.js/languageButton.js index d15abb44b..3891fa7d3 100644 --- a/resources/skins.vector.js/languageButton.js +++ b/resources/skins.vector.js/languageButton.js @@ -25,7 +25,7 @@ function addInterwikiLinkToMainMenu() { ); if ( addInterlanguageLink ) { - addInterlanguageLink.addEventListener( 'click', function ( /** @type {Event} */ e ) { + addInterlanguageLink.addEventListener( 'click', ( /** @type {Event} */ e ) => { e.preventDefault(); // redirect to the detached and original edit link editLink.click(); diff --git a/resources/skins.vector.js/limitedWidthToggle.js b/resources/skins.vector.js/limitedWidthToggle.js index 589deaf66..2020317ac 100644 --- a/resources/skins.vector.js/limitedWidthToggle.js +++ b/resources/skins.vector.js/limitedWidthToggle.js @@ -55,7 +55,7 @@ function init() { } ); }; - toggle.addEventListener( 'click', function () { + toggle.addEventListener( 'click', () => { const isLimitedWidth = features.isEnabled( LIMITED_WIDTH_FEATURE_NAME ); const oldIcon = isLimitedWidth ? 'fullScreen' : 'exitFullscreen'; const newIcon = isLimitedWidth ? 'exitFullscreen' : 'fullScreen'; diff --git a/resources/skins.vector.js/menuTabs.js b/resources/skins.vector.js/menuTabs.js index d5651fee8..6018cf3fa 100644 --- a/resources/skins.vector.js/menuTabs.js +++ b/resources/skins.vector.js/menuTabs.js @@ -24,7 +24,7 @@ function init() { /** * @param {HTMLElement} item */ - function ( item ) { + ( item ) => { // Check if this menu item belongs to a tabs menu. if ( item.closest( TABS_SELECTOR ) ) { addNoIconClass( item ); diff --git a/resources/skins.vector.js/pinnableElement.js b/resources/skins.vector.js/pinnableElement.js index 9f3e97fd8..60ba24e37 100644 --- a/resources/skins.vector.js/pinnableElement.js +++ b/resources/skins.vector.js/pinnableElement.js @@ -152,7 +152,7 @@ function setFocusAfterToggle( pinnableElementId ) { */ function bindPinnableToggleButtons( header ) { const toggleButtons = header.querySelectorAll( '.vector-pinnable-header-toggle-button' ); - toggleButtons.forEach( function ( button ) { + toggleButtons.forEach( ( button ) => { button.addEventListener( 'click', pinnableElementClickHandler.bind( null, header ) ); } ); } @@ -277,9 +277,9 @@ function initPinnableElement() { function hasPinnedElements() { const suffixesToCheck = [ 'pinned-clientpref-1', 'pinned-enabled' ]; const htmlElement = document.documentElement; - return Array.from( htmlElement.classList ).some( ( className ) => { - return suffixesToCheck.some( ( suffix ) => className.endsWith( suffix ) ); - } ); + return Array.from( htmlElement.classList ).some( + ( className ) => suffixesToCheck.some( ( suffix ) => className.endsWith( suffix ) ) + ); } /** diff --git a/resources/skins.vector.js/scrollObserver.js b/resources/skins.vector.js/scrollObserver.js index 4fe388e5c..780d9abaf 100644 --- a/resources/skins.vector.js/scrollObserver.js +++ b/resources/skins.vector.js/scrollObserver.js @@ -38,7 +38,7 @@ function firePageTitleScrollHook( direction ) { * @return {IntersectionObserver} */ function initScrollObserver( show, hide ) { - return new IntersectionObserver( function ( entries ) { + return new IntersectionObserver( ( entries ) => { if ( !entries[ 0 ].isIntersecting && entries[ 0 ].boundingClientRect.top < 0 ) { // Viewport has crossed the bottom edge of the target element. show(); diff --git a/resources/skins.vector.js/searchLoader.js b/resources/skins.vector.js/searchLoader.js index 7f35aeb70..f2963ff84 100644 --- a/resources/skins.vector.js/searchLoader.js +++ b/resources/skins.vector.js/searchLoader.js @@ -96,7 +96,7 @@ function initSearchLoader( document ) { return; } - Array.prototype.forEach.call( searchBoxes, function ( searchBox ) { + Array.prototype.forEach.call( searchBoxes, ( searchBox ) => { const searchInner = searchBox.querySelector( 'form > div' ), searchInput = searchBox.querySelector( 'input[name="search"]' ), isPrimarySearch = searchInput && searchInput.getAttribute( 'id' ) === 'searchInput'; @@ -111,7 +111,7 @@ function initSearchLoader( document ) { 'skins.vector.search', isPrimarySearch ? LOAD_START_MARK : null, // Note, loading Vue.js will remove the element from the DOM. - function () { + () => { if ( isPrimarySearch ) { markLoadEnd( LOAD_START_MARK, LOAD_END_MARK, LOAD_MEASURE ); } diff --git a/resources/skins.vector.js/setupIntersectionObservers.js b/resources/skins.vector.js/setupIntersectionObservers.js index 9f9be6021..c94c2e45d 100644 --- a/resources/skins.vector.js/setupIntersectionObservers.js +++ b/resources/skins.vector.js/setupIntersectionObservers.js @@ -29,11 +29,12 @@ const belowDesktopMedia = window.matchMedia( '(max-width: 999px)' ); * @param {Function} changeActiveSection * @return {OnIntersection} */ -const getHeadingIntersectionHandler = ( changeActiveSection ) => { +const getHeadingIntersectionHandler = ( changeActiveSection ) => /** * @param {HTMLElement} section */ - return ( section ) => { + // eslint-disable-next-line implicit-arrow-linebreak + ( section ) => { const headline = section.classList.contains( 'mw-body-content' ) ? section : section.querySelector( HEADLINE_SELECTOR ); @@ -41,7 +42,6 @@ const getHeadingIntersectionHandler = ( changeActiveSection ) => { changeActiveSection( `${ TOC_SECTION_ID_PREFIX }${ headline.id }` ); } }; -}; /** * Initialize sticky header AB tests and determine whether to show the sticky header @@ -221,8 +221,8 @@ const setupTableOfContents = ( tocElement, bodyContent, initSectionObserverFn ) mw.hook( 've.activationStart' ).add( () => { sectionObserver.pause(); } ); - mw.hook( 'wikipage.tableOfContents' ).add( function ( sections ) { - tableOfContents.reloadTableOfContents( sections ).then( function () { + mw.hook( 'wikipage.tableOfContents' ).add( ( sections ) => { + tableOfContents.reloadTableOfContents( sections ).then( () => { /** * @stable for use in gadgets and extensions * @since 1.40 diff --git a/resources/skins.vector.js/skin.js b/resources/skins.vector.js/skin.js index b90bae9ac..1b4f79126 100644 --- a/resources/skins.vector.js/skin.js +++ b/resources/skins.vector.js/skin.js @@ -124,7 +124,7 @@ function init( window ) { // When EventLogging is not available this will reject. // This code can be removed by the end of the Desktop improvements project. // https://www.mediawiki.org/wiki/Desktop_improvements - mw.loader.using( 'ext.eventLogging' ).then( function () { + mw.loader.using( 'ext.eventLogging' ).then( () => { if ( mw.eventLog && mw.eventLog.eventInSample( 100 /* 1 in 100 */ ) && @@ -145,7 +145,7 @@ if ( document.readyState === 'interactive' || document.readyState === 'complete' main( window ); } else { // This is needed when document.readyState === 'loading'. - document.addEventListener( 'DOMContentLoaded', function () { + document.addEventListener( 'DOMContentLoaded', () => { main( window ); } ); } diff --git a/resources/skins.vector.js/stickyHeader.js b/resources/skins.vector.js/stickyHeader.js index c38049ddf..7e1cb1597 100644 --- a/resources/skins.vector.js/stickyHeader.js +++ b/resources/skins.vector.js/stickyHeader.js @@ -145,7 +145,7 @@ function updateStickyWatchlink( watchLink, isWatched ) { * @param {string} className */ function removeClassFromNodes( nodes, className ) { - Array.prototype.forEach.call( nodes, function ( node ) { + Array.prototype.forEach.call( nodes, ( node ) => { node.classList.remove( className ); } ); } @@ -154,7 +154,7 @@ function removeClassFromNodes( nodes, className ) { * @param {NodeList} nodes */ function removeNodes( nodes ) { - Array.prototype.forEach.call( nodes, function ( node ) { + Array.prototype.forEach.call( nodes, ( node ) => { node.parentNode.removeChild( node ); } ); } @@ -293,7 +293,7 @@ function prepareEditIcons( copyButtonAttributes( primaryEdit, primaryEditSticky ); suffixStickyHref( primaryEditSticky ); - primaryEditSticky.addEventListener( 'click', function ( ev ) { + primaryEditSticky.addEventListener( 'click', ( ev ) => { const target = ev.target; // T336639: // eslint-disable-next-line no-jquery/no-jquery-constructor @@ -316,7 +316,7 @@ function prepareEditIcons( if ( secondaryEdit ) { copyButtonAttributes( secondaryEdit, wikitextSticky ); suffixStickyHref( wikitextSticky ); - wikitextSticky.addEventListener( 'click', function ( ev ) { + wikitextSticky.addEventListener( 'click', ( ev ) => { const target = ev.target; if ( target ) { // T336639: @@ -424,7 +424,7 @@ function prepareUserLinksDropdown( userLinksDropdown ) { // Make the logout go through the API (T324638) const logoutLink = /** @type {HTMLAnchorElement} */( userLinksDropdownClone.querySelector( '#pt-logout-sticky-header a' ) ); if ( logoutLink ) { - logoutLink.addEventListener( 'click', function ( ev ) { + logoutLink.addEventListener( 'click', ( ev ) => { ev.preventDefault(); mw.hook( 'skin.logout' ).fire( logoutLink.href ); } ); @@ -568,7 +568,7 @@ function initStickyHeader( props ) { addVisualEditorHooks( props.stickyIntersection, props.observer ); // Make sure ULS outside sticky header disables the sticky header behaviour. - mw.hook( 'mw.uls.compact_language_links.open' ).add( function ( $trigger ) { + mw.hook( 'mw.uls.compact_language_links.open' ).add( ( $trigger ) => { const trigger = $trigger[ 0 ]; if ( trigger.id !== 'p-lang-btn-sticky-header' ) { const bodyClassList = document.body.classList; @@ -580,7 +580,7 @@ function initStickyHeader( props ) { // Make sure ULS dialog is sticky. const langBtn = props.header.querySelector( '#p-lang-btn-sticky-header' ); if ( langBtn ) { - langBtn.addEventListener( 'click', function () { + langBtn.addEventListener( 'click', () => { const bodyClassList = document.body.classList; bodyClassList.remove( ULS_HIDE_CLASS ); bodyClassList.add( ULS_STICKY_CLASS ); diff --git a/resources/skins.vector.js/tableOfContents.js b/resources/skins.vector.js/tableOfContents.js index d29055c65..5bf744821 100644 --- a/resources/skins.vector.js/tableOfContents.js +++ b/resources/skins.vector.js/tableOfContents.js @@ -126,9 +126,7 @@ module.exports = function tableOfContents( props ) { * * @return {boolean} */ - const prefersReducedMotion = () => { - return window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches; - }; + const prefersReducedMotion = () => window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches; /** * Sets an `ACTIVE_SECTION_CLASS` on the element with an id that matches `id`. @@ -301,7 +299,7 @@ module.exports = function tableOfContents( props ) { */ function collapseSections( selectedIds ) { const sectionIdsToCollapse = selectedIds || getExpandedSectionIds(); - expandedSections = expandedSections.filter( function ( section ) { + expandedSections = expandedSections.filter( ( section ) => { const isSelected = sectionIdsToCollapse.indexOf( section.id ) > -1; const toggle = isSelected ? section.getElementsByClassName( TOGGLE_CLASS ) : undefined; if ( isSelected && toggle && toggle.length > 0 ) { @@ -383,7 +381,7 @@ module.exports = function tableOfContents( props ) { */ function bindPinnedToggleListeners() { const toggleButtons = document.querySelectorAll( '.vector-toc-pinnable-header button' ); - toggleButtons.forEach( function ( btn ) { + toggleButtons.forEach( ( btn ) => { btn.addEventListener( 'click', () => { props.onTogglePinned(); } ); @@ -394,7 +392,7 @@ module.exports = function tableOfContents( props ) { * Bind event listeners for clicking on section headings and toggle buttons. */ function bindSubsectionToggleListeners() { - props.container.addEventListener( 'click', function ( e ) { + props.container.addEventListener( 'click', ( e ) => { if ( !( e.target instanceof HTMLElement ) ) { diff --git a/resources/skins.vector.js/watchstar.js b/resources/skins.vector.js/watchstar.js index bd8badf33..f05c06b15 100644 --- a/resources/skins.vector.js/watchstar.js +++ b/resources/skins.vector.js/watchstar.js @@ -27,7 +27,7 @@ const updateWatchIcon = ( watchIcon, isWatched, expiry ) => { const init = () => { mw.hook( 'wikipage.watchlistChange' ).add( - function ( /** @type {boolean} */ isWatched, /** @type {string} */ expiry ) { + ( /** @type {boolean} */ isWatched, /** @type {string} */ expiry ) => { const watchIcons = document.querySelectorAll( '.mw-watchlink .vector-icon' ); if ( !watchIcons ) { return; diff --git a/resources/skins.vector.legacy.js/collapsibleTabs.js b/resources/skins.vector.legacy.js/collapsibleTabs.js index f93e78ca3..4f95e023a 100644 --- a/resources/skins.vector.legacy.js/collapsibleTabs.js +++ b/resources/skins.vector.legacy.js/collapsibleTabs.js @@ -42,7 +42,7 @@ function init() { // if we haven't already bound our resize handler, bind it now if ( !boundEvent ) { boundEvent = true; - $( window ).on( 'resize', mw.util.debounce( function () { + $( window ).on( 'resize', mw.util.debounce( () => { rAF( $.collapsibleTabs.handleResize ); }, 10 ) ); } @@ -96,7 +96,7 @@ function init() { return settings || {}; }, handleResize: function () { - $.collapsibleTabs.instances.forEach( function ( $el ) { + $.collapsibleTabs.instances.forEach( ( $el ) => { const data = $.collapsibleTabs.getSettings( $el ); if ( $.isEmptyObject( data ) || data.shifting ) { @@ -188,7 +188,7 @@ function init() { .data( 'collapsibleTabsSettings', data ) .animate( { width: expandedWidth + 'px' }, 'normal', function () { $( this ).attr( 'style', 'display: block;' ); - rAF( function () { + rAF( () => { // Update the 'expandedWidth' in case someone was brazen enough to // change the tab's contents after the page load *gasp* (T71729). This // doesn't prevent a tab from collapsing back and forth once, but at diff --git a/resources/skins.vector.legacy.js/vector.js b/resources/skins.vector.legacy.js/vector.js index 8a1fa0ac8..4c34bd181 100644 --- a/resources/skins.vector.legacy.js/vector.js +++ b/resources/skins.vector.legacy.js/vector.js @@ -26,7 +26,7 @@ function init() { // Bind callback functions to animate our drop down menu in and out // and then call the collapsibleTabs function on the menu $tabContainer - .on( 'beforeTabCollapse', function () { + .on( 'beforeTabCollapse', () => { let expandedWidth; // If the dropdown was hidden, show it if ( !mw.util.isPortletVisible( cactionsId ) ) { @@ -42,7 +42,7 @@ function init() { .animate( { width: expandedWidth }, 'normal' ); } } ) - .on( 'beforeTabExpand', function () { + .on( 'beforeTabExpand', () => { // If we're removing the last child node right now, hide the dropdown if ( $cactions.find( 'li' ).length === 1 ) { // eslint-disable-next-line no-jquery/no-animate @@ -98,7 +98,7 @@ function init() { // 3. and, the left-navigation and right-navigation are overlapping // each other, e.g. when making the window very narrow, or if a gadget // added a lot of tabs. - $tabContainer.children( 'li.collapsible' ).each( function ( _index, element ) { + $tabContainer.children( 'li.collapsible' ).each( ( _index, element ) => { collapsibleWidth += $( element ).width() || 0; if ( collapsibleWidth > initialCactionsWidth() ) { // We've found one or more collapsible links that are wider diff --git a/resources/skins.vector.search/restSearchClient.js b/resources/skins.vector.search/restSearchClient.js index 781b152ca..31e1bf5a0 100644 --- a/resources/skins.vector.search/restSearchClient.js +++ b/resources/skins.vector.search/restSearchClient.js @@ -108,9 +108,9 @@ function restSearchClient( config ) { } } ); const searchResponsePromise = result.fetch - .then( ( /** @type {RestResponse} */ res ) => { - return adaptApiResponse( config, q, res, showDescription ); - } ); + .then( ( /** @type {RestResponse} */ res ) => adaptApiResponse( + config, q, res, showDescription + ) ); return { abort: result.abort, fetch: searchResponsePromise diff --git a/tests/integration-qunit/integration.test.js b/tests/integration-qunit/integration.test.js index 1f2a7b913..10dc74047 100644 --- a/tests/integration-qunit/integration.test.js +++ b/tests/integration-qunit/integration.test.js @@ -7,7 +7,7 @@ const clientPreferences = require( 'skins.vector.clientPreferences' ); * This should only be used to test APIs that Vector depends on to work. * For unit tests please see tests/jest. */ -QUnit.module( 'Vector (integration)', function () { +QUnit.module( 'Vector (integration)', () => { QUnit.test( 'Client preferences: Behaves same for all users', function ( assert ) { const sandbox = this.sandbox; const helper = ( feature, isNamedReturnValue ) => { diff --git a/tests/jest/App.test.js b/tests/jest/App.test.js index 01222f41f..f2e0391ca 100644 --- a/tests/jest/App.test.js +++ b/tests/jest/App.test.js @@ -12,23 +12,21 @@ const defaultProps = { searchQuery: '' }; -const mount = ( /** @type {Object} */ customProps ) => { - return VueTestUtils.shallowMount( App, { - props: Object.assign( {}, defaultProps, customProps ), - global: { - mocks: { - $i18n: ( /** @type {string} */ str ) => ( { - text: () => str - } ) - }, - directives: { - 'i18n-html': ( el, binding ) => { - el.innerHTML = `${ binding.arg } (${ binding.value })`; - } +const mount = ( /** @type {Object} */ customProps ) => VueTestUtils.shallowMount( App, { + props: Object.assign( {}, defaultProps, customProps ), + global: { + mocks: { + $i18n: ( /** @type {string} */ str ) => ( { + text: () => str + } ) + }, + directives: { + 'i18n-html': ( el, binding ) => { + el.innerHTML = `${ binding.arg } (${ binding.value })`; } } - } ); -}; + } +} ); describe( 'App', () => { it( 'renders a typeahead search component', () => { diff --git a/tests/jest/pinnableElement.test.js b/tests/jest/pinnableElement.test.js index fcddc5a9f..6182e240d 100644 --- a/tests/jest/pinnableElement.test.js +++ b/tests/jest/pinnableElement.test.js @@ -29,9 +29,7 @@ let pinnedStatus = false; features.toggle = jest.fn( () => { pinnedStatus = !pinnedStatus; } ); -features.isEnabled = jest.fn( () => { - return pinnedStatus; -} ); +features.isEnabled = jest.fn( () => pinnedStatus ); const simpleData = { 'is-pinned': false, diff --git a/tests/jest/skins.vector.clientPreferences.test.js b/tests/jest/skins.vector.clientPreferences.test.js index 93e3ddb0c..760277e54 100644 --- a/tests/jest/skins.vector.clientPreferences.test.js +++ b/tests/jest/skins.vector.clientPreferences.test.js @@ -32,11 +32,9 @@ describe( 'clientPreferences', () => { } ) ); } ); - test( 'render empty', () => { - return clientPreferences.render( '#cp', {} ).then( () => { - expect( cp.innerHTML ).toMatchSnapshot(); - } ); - } ); + test( 'render empty', () => clientPreferences.render( '#cp', {} ).then( () => { + expect( cp.innerHTML ).toMatchSnapshot(); + } ) ); test( 'render font size', () => { document.documentElement.setAttribute( 'class', 'vector-feature-custom-font-size-clientpref-2' ); diff --git a/tests/jest/skins.vector.es6/main.test.js b/tests/jest/skins.vector.es6/main.test.js index 978139139..7550e43c3 100644 --- a/tests/jest/skins.vector.es6/main.test.js +++ b/tests/jest/skins.vector.es6/main.test.js @@ -90,9 +90,7 @@ describe( 'main.js', () => { isInBucket: () => true, isInSample: () => true, getBucket: () => 'bucket', - isInTreatmentBucket: () => { - return isUserInTreatmentBucket; - } + isInTreatmentBucket: () => isUserInTreatmentBucket } ) ); expect( result ).toMatchObject( expectedResult ); @@ -117,21 +115,18 @@ describe( 'Table of contents re-rendering', () => { const mockMwHook = () => { /** @type {Object.} */ const callback = {}; - jest.spyOn( mw, 'hook' ).mockImplementation( ( name ) => { + jest.spyOn( mw, 'hook' ).mockImplementation( ( name ) => ( { + add: function ( fn ) { + callback[ name ] = fn; - return { - add: function ( fn ) { - callback[ name ] = fn; - - return this; - }, - fire: ( data ) => { - if ( callback[ name ] ) { - callback[ name ]( data ); - } + return this; + }, + fire: ( data ) => { + if ( callback[ name ] ) { + callback[ name ]( data ); } - }; - } ); + } + } ) ); }; afterEach( () => { diff --git a/tests/jest/skins.vector.js/menuTabs.test.js b/tests/jest/skins.vector.js/menuTabs.test.js index f11e3aab0..0915b2958 100644 --- a/tests/jest/skins.vector.js/menuTabs.test.js +++ b/tests/jest/skins.vector.js/menuTabs.test.js @@ -4,20 +4,18 @@ describe( 'menuTabs', () => { beforeEach( () => { /** @type {Function} */ let callback; - jest.spyOn( mw, 'hook' ).mockImplementation( () => { - return { - add: function ( fn ) { - callback = fn; + jest.spyOn( mw, 'hook' ).mockImplementation( () => ( { + add: function ( fn ) { + callback = fn; - return this; - }, - fire: ( data ) => { - if ( callback ) { - callback( data ); - } + return this; + }, + fire: ( data ) => { + if ( callback ) { + callback( data ); } - }; - } ); + } + } ) ); } ); diff --git a/tests/jest/tableOfContents.test.js b/tests/jest/tableOfContents.test.js index 0adcf8b14..7b36416de 100644 --- a/tests/jest/tableOfContents.test.js +++ b/tests/jest/tableOfContents.test.js @@ -182,9 +182,7 @@ describe( 'Table of contents', () => { expect( onToggleClick ).toBeCalled(); } ); test( 'for onHashChange', () => { - mw.util.getTargetFromFragment = jest.fn().mockImplementation( ( hash ) => { - return hash === 'toc-foo' ? fooSection : null; - } ); + mw.util.getTargetFromFragment = jest.fn().mockImplementation( ( hash ) => hash === 'toc-foo' ? fooSection : null ); mount(); // Jest doesn't trigger a hashchange event when setting a hash location. @@ -273,9 +271,7 @@ describe( 'Table of contents', () => { describe( 'when the hash fragment changes', () => { test( 'expands and activates corresponding section', () => { - mw.util.getTargetFromFragment = jest.fn().mockImplementation( ( hash ) => { - return hash === 'toc-qux' ? quxSection : null; - } ); + mw.util.getTargetFromFragment = jest.fn().mockImplementation( ( hash ) => hash === 'toc-qux' ? quxSection : null ); toc = mount( { 'vector-is-collapse-sections-enabled': true } ); expect( quxSection.classList.contains( toc.ACTIVE_SECTION_CLASS ) @@ -316,15 +312,13 @@ describe( 'Table of contents', () => { jest.spyOn( mw.loader, 'using' ).mockImplementation( () => Promise.resolve() ); mw.template.getCompiler = () => {}; jest.spyOn( mw, 'message' ).mockImplementation( ( msg ) => { - const msgFactory = ( /** @type {string} */ text ) => { - return { - parse: () => '', - plain: () => '', - escaped: () => '', - exists: () => true, - text: () => text - }; - }; + const msgFactory = ( /** @type {string} */ text ) => ( { + parse: () => '', + plain: () => '', + escaped: () => '', + exists: () => true, + text: () => text + } ); switch ( msg ) { case 'vector-toc-beginning': return msgFactory( 'Beginning' ); @@ -333,21 +327,13 @@ describe( 'Table of contents', () => { } } ); - jest.spyOn( mw.template, 'getCompiler' ).mockImplementation( () => { - return { - compile: () => { - return { - render: ( /** @type {Object} */ data ) => { - return { - html: () => { - return render( data ); - } - }; - } - }; - } - }; - } ); + jest.spyOn( mw.template, 'getCompiler' ).mockImplementation( () => ( { + compile: () => ( { + render: ( /** @type {Object} */ data ) => ( { + html: () => render( data ) + } ) + } ) + } ) ); toc = mount(); diff --git a/tests/jest/urlGenerator.test.js b/tests/jest/urlGenerator.test.js index 5da912a1f..63cd382ba 100644 --- a/tests/jest/urlGenerator.test.js +++ b/tests/jest/urlGenerator.test.js @@ -22,9 +22,7 @@ describe( 'urlGenerator', () => { test( 'custom params, articlePath', () => { const config = { - get: jest.fn().mockImplementation( ( _key, fallback ) => { - return fallback; - } ), + get: jest.fn().mockImplementation( ( _key, fallback ) => fallback ), set: jest.fn() };