Bump dev deps, use @typescript-eslint type checked style preset (#4313)

This commit is contained in:
Felix Boehm
2024-12-23 12:15:54 +00:00
committed by GitHub
parent 3c1004836d
commit 5f4e84b19b
18 changed files with 262 additions and 243 deletions

View File

@@ -90,7 +90,8 @@
"extends": [ "extends": [
"plugin:expect-type/recommended", "plugin:expect-type/recommended",
"plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended-type-checked",
"plugin:@typescript-eslint/stylistic-type-checked",
"prettier" "prettier"
], ],
"parserOptions": { "parserOptions": {

View File

@@ -18,12 +18,17 @@ const benchmarkFilter = filterIndex >= 0 ? process.argv[filterIndex] : '';
const cheerioOnly = process.argv.includes('--cheerio-only'); const cheerioOnly = process.argv.includes('--cheerio-only');
interface SuiteOptions<T> { type SuiteOptions<T> = T extends void
test($: CheerioAPI, data: T): void; ? {
setup($: CheerioAPI): T; test(this: void, $: CheerioAPI): void;
} setup?: (this: void, $: CheerioAPI) => T;
}
: {
test(this: void, $: CheerioAPI, data: T): void;
setup(this: void, $: CheerioAPI): T;
};
async function benchmark<T>( async function benchmark<T = void>(
name: string, name: string,
fileName: string, fileName: string,
options: SuiteOptions<T>, options: SuiteOptions<T>,
@@ -40,7 +45,7 @@ async function benchmark<T>(
// Add Cheerio test // Add Cheerio test
const $ = load(markup); const $ = load(markup);
const setupData: T = setup($); const setupData = setup?.($) as T;
bench.add('cheerio', () => { bench.add('cheerio', () => {
test($, setupData); test($, setupData);
@@ -52,23 +57,20 @@ async function benchmark<T>(
jQueryScript.runInContext(dom.getInternalVMContext()); jQueryScript.runInContext(dom.getInternalVMContext());
const setupData: T = setup(dom.window['$']); const setupData = setup?.(dom.window['$'] as CheerioAPI) as T;
bench.add('jsdom', () => test(dom.window['$'], setupData)); bench.add('jsdom', () => test(dom.window['$'] as CheerioAPI, setupData));
} }
await bench.warmup(); // Make results more reliable, ref: https://github.com/tinylibs/tinybench/pull/50
await bench.run(); await bench.run();
console.table(bench.table()); console.table(bench.table());
} }
await benchmark<void>('Select all', 'jquery.html', { await benchmark('Select all', 'jquery.html', {
setup() {},
test: ($) => $('*').length, test: ($) => $('*').length,
}); });
await benchmark<void>('Select some', 'jquery.html', { await benchmark('Select some', 'jquery.html', {
setup() {},
test: ($) => $('li').length, test: ($) => $('li').length,
}); });
@@ -116,7 +118,7 @@ await benchmark<Cheerio<Element>>('manipulation - remove', 'jquery.html', {
}, },
}); });
await benchmark<void>('manipulation - replaceWith', 'jquery.html', { await benchmark('manipulation - replaceWith', 'jquery.html', {
setup($) { setup($) {
$('body').append('<div id="foo">'); $('body').append('<div id="foo">');
}, },
@@ -147,8 +149,7 @@ await benchmark<Cheerio<Element>>('manipulation - html render', 'jquery.html', {
const HTML_INDEPENDENT_MARKUP = const HTML_INDEPENDENT_MARKUP =
'<div class="foo"><div id="bar">bat<hr>baz</div> </div>'.repeat(6); '<div class="foo"><div id="bar">bat<hr>baz</div> </div>'.repeat(6);
await benchmark<void>('manipulation - html independent', 'jquery.html', { await benchmark('manipulation - html independent', 'jquery.html', {
setup() {},
test: ($) => $(HTML_INDEPENDENT_MARKUP).html(), test: ($) => $(HTML_INDEPENDENT_MARKUP).html(),
}); });
await benchmark<Cheerio<Element>>('manipulation - text', 'jquery.html', { await benchmark<Cheerio<Element>>('manipulation - text', 'jquery.html', {

245
package-lock.json generated
View File

@@ -35,19 +35,19 @@
"eslint-plugin-expect-type": "^0.6.2", "eslint-plugin-expect-type": "^0.6.2",
"eslint-plugin-jsdoc": "^50.6.1", "eslint-plugin-jsdoc": "^50.6.1",
"eslint-plugin-n": "^17.15.1", "eslint-plugin-n": "^17.15.1",
"eslint-plugin-unicorn": "^55.0.0", "eslint-plugin-unicorn": "^56.0.1",
"eslint-plugin-vitest": "^0.5.4", "eslint-plugin-vitest": "^0.5.4",
"husky": "^9.1.7", "husky": "^9.1.7",
"jquery": "^3.7.1", "jquery": "^3.7.1",
"jsdom": "^24.1.1", "jsdom": "^25.0.1",
"lint-staged": "^15.2.11", "lint-staged": "^15.2.11",
"prettier": "^3.4.2", "prettier": "^3.4.2",
"prettier-plugin-jsdoc": "^1.3.0", "prettier-plugin-jsdoc": "^1.3.0",
"tinybench": "^2.9.0", "tinybench": "^3.1.0",
"tshy": "^3.0.2", "tshy": "^3.0.2",
"tsx": "^4.19.2", "tsx": "^4.19.2",
"typescript": "^5.5.4", "typescript": "^5.7.2",
"vitest": "^2.0.5" "vitest": "^2.1.8"
}, },
"engines": { "engines": {
"node": ">=18.17" "node": ">=18.17"
@@ -2282,9 +2282,9 @@
} }
}, },
"node_modules/browserslist": { "node_modules/browserslist": {
"version": "4.23.0", "version": "4.24.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz",
"integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -2300,11 +2300,12 @@
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"license": "MIT",
"dependencies": { "dependencies": {
"caniuse-lite": "^1.0.30001587", "caniuse-lite": "^1.0.30001688",
"electron-to-chromium": "^1.4.668", "electron-to-chromium": "^1.5.73",
"node-releases": "^2.0.14", "node-releases": "^2.0.19",
"update-browserslist-db": "^1.0.13" "update-browserslist-db": "^1.1.1"
}, },
"bin": { "bin": {
"browserslist": "cli.js" "browserslist": "cli.js"
@@ -2344,9 +2345,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001609", "version": "1.0.30001690",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001609.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz",
"integrity": "sha512-JFPQs34lHKx1B5t1EpQpWH4c+29zIyn/haGsbpfq3suuV9v56enjFt23zqijxGTMwy1p/4H2tjnQMY+p1WoAyA==", "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -2361,7 +2362,8 @@
"type": "github", "type": "github",
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
] ],
"license": "CC-BY-4.0"
}, },
"node_modules/chai": { "node_modules/chai": {
"version": "5.1.2", "version": "5.1.2",
@@ -2609,13 +2611,13 @@
"dev": true "dev": true
}, },
"node_modules/core-js-compat": { "node_modules/core-js-compat": {
"version": "3.37.1", "version": "3.39.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz",
"integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"browserslist": "^4.23.0" "browserslist": "^4.24.2"
}, },
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@@ -2623,10 +2625,11 @@
} }
}, },
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.3", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"path-key": "^3.1.0", "path-key": "^3.1.0",
"shebang-command": "^2.0.0", "shebang-command": "^2.0.0",
@@ -2672,12 +2675,13 @@
} }
}, },
"node_modules/cssstyle": { "node_modules/cssstyle": {
"version": "4.0.1", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.0.1.tgz", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz",
"integrity": "sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==", "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"rrweb-cssom": "^0.6.0" "rrweb-cssom": "^0.7.1"
}, },
"engines": { "engines": {
"node": ">=18" "node": ">=18"
@@ -2862,10 +2866,11 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.4.736", "version": "1.5.75",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.736.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.75.tgz",
"integrity": "sha512-Rer6wc3ynLelKNM4lOCg7/zPQj8tPOCB2hzD32PX9wd3hgRRi9MxEbmkFCokzcEhRVMiOVLjnL9ig9cefJ+6+Q==", "integrity": "sha512-Lf3++DumRE/QmweGjU+ZcKqQ+3bKkU/qjaKYhIJKEOhgIO9Xs6IiAQFkfFoj+RhgDk4LUeNsLo6plExHqSyu6Q==",
"dev": true "dev": true,
"license": "ISC"
}, },
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
"version": "10.4.0", "version": "10.4.0",
@@ -2979,10 +2984,11 @@
} }
}, },
"node_modules/escalade": { "node_modules/escalade": {
"version": "3.1.2", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@@ -3214,18 +3220,19 @@
} }
}, },
"node_modules/eslint-plugin-unicorn": { "node_modules/eslint-plugin-unicorn": {
"version": "55.0.0", "version": "56.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-55.0.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-56.0.1.tgz",
"integrity": "sha512-n3AKiVpY2/uDcGrS3+QsYDkjPfaOrNrsfQxU9nt5nitd9KuvVXrfAvgCO9DYPSfap+Gqjw9EOrXIsBp5tlHZjA==", "integrity": "sha512-FwVV0Uwf8XPfVnKSGpMg7NtlZh0G0gBarCaFcMUOoqPxXryxdYxTRRv4kH6B9TFCVIrjRXG+emcxIk2ayZilog==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"@babel/helper-validator-identifier": "^7.24.5", "@babel/helper-validator-identifier": "^7.24.7",
"@eslint-community/eslint-utils": "^4.4.0", "@eslint-community/eslint-utils": "^4.4.0",
"ci-info": "^4.0.0", "ci-info": "^4.0.0",
"clean-regexp": "^1.0.0", "clean-regexp": "^1.0.0",
"core-js-compat": "^3.37.0", "core-js-compat": "^3.38.1",
"esquery": "^1.5.0", "esquery": "^1.6.0",
"globals": "^15.7.0", "globals": "^15.9.0",
"indent-string": "^4.0.0", "indent-string": "^4.0.0",
"is-builtin-module": "^3.2.1", "is-builtin-module": "^3.2.1",
"jsesc": "^3.0.2", "jsesc": "^3.0.2",
@@ -3233,7 +3240,7 @@
"read-pkg-up": "^7.0.1", "read-pkg-up": "^7.0.1",
"regexp-tree": "^0.1.27", "regexp-tree": "^0.1.27",
"regjsparser": "^0.10.0", "regjsparser": "^0.10.0",
"semver": "^7.6.1", "semver": "^7.6.3",
"strip-indent": "^3.0.0" "strip-indent": "^3.0.0"
}, },
"engines": { "engines": {
@@ -3247,10 +3254,11 @@
} }
}, },
"node_modules/eslint-plugin-unicorn/node_modules/globals": { "node_modules/eslint-plugin-unicorn/node_modules/globals": {
"version": "15.8.0", "version": "15.14.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-15.8.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz",
"integrity": "sha512-VZAJ4cewHTExBWDHR6yptdIBlx9YSSZuwojj9Nt5mBRXQzrKakDsVKQ1J63sklLvzAJm0X5+RpO4i3Y2hcOnFw==", "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=18" "node": ">=18"
}, },
@@ -4120,12 +4128,13 @@
} }
}, },
"node_modules/jsdom": { "node_modules/jsdom": {
"version": "24.1.1", "version": "25.0.1",
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.1.tgz", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz",
"integrity": "sha512-5O1wWV99Jhq4DV7rCLIoZ/UIhyQeDR7wHVyZAHAshbrvZsLs+Xzz7gtwnlJTJDjleiTKh54F4dXrX70vJQTyJQ==", "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"cssstyle": "^4.0.1", "cssstyle": "^4.1.0",
"data-urls": "^5.0.0", "data-urls": "^5.0.0",
"decimal.js": "^10.4.3", "decimal.js": "^10.4.3",
"form-data": "^4.0.0", "form-data": "^4.0.0",
@@ -4138,7 +4147,7 @@
"rrweb-cssom": "^0.7.1", "rrweb-cssom": "^0.7.1",
"saxes": "^6.0.0", "saxes": "^6.0.0",
"symbol-tree": "^3.2.4", "symbol-tree": "^3.2.4",
"tough-cookie": "^4.1.4", "tough-cookie": "^5.0.0",
"w3c-xmlserializer": "^5.0.0", "w3c-xmlserializer": "^5.0.0",
"webidl-conversions": "^7.0.0", "webidl-conversions": "^7.0.0",
"whatwg-encoding": "^3.1.1", "whatwg-encoding": "^3.1.1",
@@ -4159,13 +4168,6 @@
} }
} }
}, },
"node_modules/jsdom/node_modules/rrweb-cssom": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz",
"integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==",
"dev": true,
"license": "MIT"
},
"node_modules/jsesc": { "node_modules/jsesc": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
@@ -5225,10 +5227,11 @@
"dev": true "dev": true
}, },
"node_modules/node-releases": { "node_modules/node-releases": {
"version": "2.0.14", "version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
"integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
"dev": true "dev": true,
"license": "MIT"
}, },
"node_modules/normalize-package-data": { "node_modules/normalize-package-data": {
"version": "2.5.0", "version": "2.5.0",
@@ -5649,13 +5652,6 @@
"prettier": "^3.0.0" "prettier": "^3.0.0"
} }
}, },
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
"dev": true,
"license": "MIT"
},
"node_modules/punycode": { "node_modules/punycode": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -5665,13 +5661,6 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
"dev": true,
"license": "MIT"
},
"node_modules/queue-microtask": { "node_modules/queue-microtask": {
"version": "1.2.3", "version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -5837,13 +5826,6 @@
"jsesc": "bin/jsesc" "jsesc": "bin/jsesc"
} }
}, },
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"dev": true,
"license": "MIT"
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.8", "version": "1.22.8",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
@@ -6023,10 +6005,11 @@
} }
}, },
"node_modules/rrweb-cssom": { "node_modules/rrweb-cssom": {
"version": "0.6.0", "version": "0.7.1",
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz",
"integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==",
"dev": true "dev": true,
"license": "MIT"
}, },
"node_modules/run-parallel": { "node_modules/run-parallel": {
"version": "1.2.0", "version": "1.2.0",
@@ -6516,11 +6499,14 @@
"dev": true "dev": true
}, },
"node_modules/tinybench": { "node_modules/tinybench": {
"version": "2.9.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-3.1.0.tgz",
"integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "integrity": "sha512-Km+oMh2xqNCxuyoUsqbRmHgFSd8sATh7v7xreP+kHN6x67w28Pawr83WmBxcaORvxkc0Ex6zgqK951yBnTFaaQ==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT",
"engines": {
"node": ">=18.0.0"
}
}, },
"node_modules/tinyexec": { "node_modules/tinyexec": {
"version": "0.3.1", "version": "0.3.1",
@@ -6556,6 +6542,26 @@
"node": ">=14.0.0" "node": ">=14.0.0"
} }
}, },
"node_modules/tldts": {
"version": "6.1.69",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.69.tgz",
"integrity": "sha512-Oh/CqRQ1NXNY7cy9NkTPUauOWiTro0jEYZTioGbOmcQh6EC45oribyIMJp0OJO3677r13tO6SKdWoGZUx2BDFw==",
"dev": true,
"license": "MIT",
"dependencies": {
"tldts-core": "^6.1.69"
},
"bin": {
"tldts": "bin/cli.js"
}
},
"node_modules/tldts-core": {
"version": "6.1.69",
"resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.69.tgz",
"integrity": "sha512-nygxy9n2PBUFQUtAXAc122gGo+04/j5qr5TGQFZTHafTKYvmARVXt2cA5rgero2/dnXUfkdPtiJoKmrd3T+wdA==",
"dev": true,
"license": "MIT"
},
"node_modules/to-regex-range": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -6570,29 +6576,16 @@
} }
}, },
"node_modules/tough-cookie": { "node_modules/tough-cookie": {
"version": "4.1.4", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz",
"integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==",
"dev": true, "dev": true,
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"psl": "^1.1.33", "tldts": "^6.1.32"
"punycode": "^2.1.1",
"universalify": "^0.2.0",
"url-parse": "^1.5.3"
}, },
"engines": { "engines": {
"node": ">=6" "node": ">=16"
}
},
"node_modules/tough-cookie/node_modules/universalify": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 4.0.0"
} }
}, },
"node_modules/tr46": { "node_modules/tr46": {
@@ -7176,10 +7169,11 @@
} }
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.5.4", "version": "5.7.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
"dev": true, "dev": true,
"license": "Apache-2.0",
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@@ -7238,9 +7232,9 @@
} }
}, },
"node_modules/update-browserslist-db": { "node_modules/update-browserslist-db": {
"version": "1.0.13", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
"integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -7256,9 +7250,10 @@
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"license": "MIT",
"dependencies": { "dependencies": {
"escalade": "^3.1.1", "escalade": "^3.2.0",
"picocolors": "^1.0.0" "picocolors": "^1.1.0"
}, },
"bin": { "bin": {
"update-browserslist-db": "cli.js" "update-browserslist-db": "cli.js"
@@ -7276,17 +7271,6 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/url-parse": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
}
},
"node_modules/validate-npm-package-license": { "node_modules/validate-npm-package-license": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
@@ -7453,6 +7437,13 @@
} }
} }
}, },
"node_modules/vitest/node_modules/tinybench": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
"integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
"dev": true,
"license": "MIT"
},
"node_modules/w3c-xmlserializer": { "node_modules/w3c-xmlserializer": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",

View File

@@ -141,19 +141,19 @@
"eslint-plugin-expect-type": "^0.6.2", "eslint-plugin-expect-type": "^0.6.2",
"eslint-plugin-jsdoc": "^50.6.1", "eslint-plugin-jsdoc": "^50.6.1",
"eslint-plugin-n": "^17.15.1", "eslint-plugin-n": "^17.15.1",
"eslint-plugin-unicorn": "^55.0.0", "eslint-plugin-unicorn": "^56.0.1",
"eslint-plugin-vitest": "^0.5.4", "eslint-plugin-vitest": "^0.5.4",
"husky": "^9.1.7", "husky": "^9.1.7",
"jquery": "^3.7.1", "jquery": "^3.7.1",
"jsdom": "^24.1.1", "jsdom": "^25.0.1",
"lint-staged": "^15.2.11", "lint-staged": "^15.2.11",
"prettier": "^3.4.2", "prettier": "^3.4.2",
"prettier-plugin-jsdoc": "^1.3.0", "prettier-plugin-jsdoc": "^1.3.0",
"tinybench": "^2.9.0", "tinybench": "^3.1.0",
"tshy": "^3.0.2", "tshy": "^3.0.2",
"tsx": "^4.19.2", "tsx": "^4.19.2",
"typescript": "^5.5.4", "typescript": "^5.7.2",
"vitest": "^2.0.5" "vitest": "^2.1.8"
}, },
"engines": { "engines": {
"node": ">=18.17" "node": ">=18.17"

View File

@@ -9,7 +9,11 @@ import { domEach, camelCase, cssCase } from '../utils.js';
import { isTag, type AnyNode, type Element } from 'domhandler'; import { isTag, type AnyNode, type Element } from 'domhandler';
import type { Cheerio } from '../cheerio.js'; import type { Cheerio } from '../cheerio.js';
import { innerText, textContent } from 'domutils'; import { innerText, textContent } from 'domutils';
const hasOwn = Object.prototype.hasOwnProperty; const hasOwn =
// @ts-expect-error `hasOwn` is a standard object method
(Object.hasOwn as (object: unknown, prop: string) => boolean) ??
((object: unknown, prop: string) =>
Object.prototype.hasOwnProperty.call(object, prop));
const rspace = /\s+/; const rspace = /\s+/;
const dataAttrPrefix = 'data-'; const dataAttrPrefix = 'data-';
@@ -56,7 +60,7 @@ function getAttr(
return elem.attribs; return elem.attribs;
} }
if (hasOwn.call(elem.attribs, name)) { if (hasOwn(elem.attribs, name)) {
// Get the (decoded) attribute // Get the (decoded) attribute
return !xmlMode && rboolean.test(name) ? name : elem.attribs[name]; return !xmlMode && rboolean.test(name) ? name : elem.attribs[name];
} }
@@ -209,14 +213,14 @@ export function attr<T extends AnyNode>(
setAttr(el, objName, objValue); setAttr(el, objName, objValue);
} }
} else { } else {
setAttr(el, name as string, value as string); setAttr(el, name!, value!);
} }
}); });
} }
return arguments.length > 1 return arguments.length > 1
? this ? this
: getAttr(this[0], name as string, this.options.xmlMode); : getAttr(this[0], name!, this.options.xmlMode);
} }
/** /**
@@ -233,10 +237,10 @@ function getProp(
el: Element, el: Element,
name: string, name: string,
xmlMode?: boolean, xmlMode?: boolean,
): string | undefined | Element[keyof Element] { ): string | undefined | boolean | Element[keyof Element] {
return name in el return name in el
? // @ts-expect-error TS doesn't like us accessing the value directly here. ? // @ts-expect-error TS doesn't like us accessing the value directly here.
el[name] (el[name] as string | undefined)
: !xmlMode && rboolean.test(name) : !xmlMode && rboolean.test(name)
? getAttr(el, name, false) !== undefined ? getAttr(el, name, false) !== undefined
: getAttr(el, name, xmlMode); : getAttr(el, name, xmlMode);
@@ -259,7 +263,11 @@ function setProp(el: Element, name: string, value: unknown, xmlMode?: boolean) {
setAttr( setAttr(
el, el,
name, name,
!xmlMode && rboolean.test(name) ? (value ? '' : null) : `${value}`, !xmlMode && rboolean.test(name)
? value
? ''
: null
: `${value as string}`,
); );
} }
} }
@@ -396,14 +404,15 @@ export function prop<T extends AnyNode>(this: Cheerio<T>, name: string): string;
export function prop<T extends AnyNode>( export function prop<T extends AnyNode>(
this: Cheerio<T>, this: Cheerio<T>,
name: string | Record<string, string | Element[keyof Element] | boolean>, name: string | Record<string, string | Element[keyof Element] | boolean>,
value?: value?: unknown,
| (( ):
this: Element, | Cheerio<T>
i: number, | string
prop: string | undefined, | boolean
) => string | Element[keyof Element] | boolean) | undefined
| unknown, | null
): Cheerio<T> | string | undefined | null | Element[keyof Element] | StyleProp { | Element[keyof Element]
| StyleProp {
if (typeof name === 'string' && value === undefined) { if (typeof name === 'string' && value === undefined) {
const el = this[0]; const el = this[0];
@@ -552,7 +561,7 @@ function readAllData(el: DataElement): unknown {
const jsName = camelCase(domName.slice(dataAttrPrefix.length)); const jsName = camelCase(domName.slice(dataAttrPrefix.length));
if (!hasOwn.call(el.data, jsName)) { if (!hasOwn(el.data, jsName)) {
el.data![jsName] = parseDataValue(el.attribs[domName]); el.data![jsName] = parseDataValue(el.attribs[domName]);
} }
} }
@@ -574,11 +583,11 @@ function readData(el: DataElement, name: string): unknown {
const domName = dataAttrPrefix + cssCase(name); const domName = dataAttrPrefix + cssCase(name);
const data = el.data!; const data = el.data!;
if (hasOwn.call(data, name)) { if (hasOwn(data, name)) {
return data[name]; return data[name];
} }
if (hasOwn.call(el.attribs, domName)) { if (hasOwn(el.attribs, domName)) {
return (data[name] = parseDataValue(el.attribs[domName])); return (data[name] = parseDataValue(el.attribs[domName]));
} }
@@ -629,7 +638,7 @@ function parseDataValue(value: string): unknown {
export function data<T extends AnyNode>( export function data<T extends AnyNode>(
this: Cheerio<T>, this: Cheerio<T>,
name: string, name: string,
): unknown | undefined; ): unknown;
/** /**
* Method for getting all of an element's data attributes, for only the first * Method for getting all of an element's data attributes, for only the first
* element in the matched set. * element in the matched set.
@@ -698,7 +707,7 @@ export function data<T extends AnyNode>(
this: Cheerio<T>, this: Cheerio<T>,
name?: string | Record<string, unknown>, name?: string | Record<string, unknown>,
value?: unknown, value?: unknown,
): unknown | Cheerio<T> | undefined | Record<string, unknown> { ): unknown {
const elem = this[0]; const elem = this[0];
if (!elem || !isTag(elem)) return; if (!elem || !isTag(elem)) return;
@@ -716,7 +725,7 @@ export function data<T extends AnyNode>(
domEach(this, (el) => { domEach(this, (el) => {
if (isTag(el)) { if (isTag(el)) {
if (typeof name === 'object') setData(el, name); if (typeof name === 'object') setData(el, name);
else setData(el, name, value as unknown); else setData(el, name, value);
} }
}); });
return this; return this;
@@ -816,7 +825,7 @@ export function val<T extends AnyNode>(
* @param name - Name of the attribute to remove. * @param name - Name of the attribute to remove.
*/ */
function removeAttribute(elem: Element, name: string) { function removeAttribute(elem: Element, name: string) {
if (!elem.attribs || !hasOwn.call(elem.attribs, name)) return; if (!elem.attribs || !hasOwn(elem.attribs, name)) return;
delete elem.attribs[name]; delete elem.attribs[name];
} }
@@ -1030,7 +1039,7 @@ export function removeClass<T extends AnyNode, R extends ArrayLike<T>>(
for (let j = 0; j < numClasses; j++) { for (let j = 0; j < numClasses; j++) {
const index = elClasses.indexOf(classes[j]); const index = elClasses.indexOf(classes[j]);
if (index >= 0) { if (index !== -1) {
elClasses.splice(index, 1); elClasses.splice(index, 1);
changed = true; changed = true;
@@ -1115,9 +1124,9 @@ export function toggleClass<T extends AnyNode, R extends ArrayLike<T>>(
const index = elementClasses.indexOf(classNames[j]); const index = elementClasses.indexOf(classNames[j]);
// Add if stateValue === true or we are toggling and there is no value // Add if stateValue === true or we are toggling and there is no value
if (state >= 0 && index < 0) { if (state >= 0 && index === -1) {
elementClasses.push(classNames[j]); elementClasses.push(classNames[j]);
} else if (state <= 0 && index >= 0) { } else if (state <= 0 && index !== -1) {
// Otherwise remove but only if the item exists // Otherwise remove but only if the item exists
elementClasses.splice(index, 1); elementClasses.splice(index, 1);
} }

View File

@@ -16,9 +16,7 @@ interface ExtractDescriptor {
type ExtractValue = string | ExtractDescriptor | [string | ExtractDescriptor]; type ExtractValue = string | ExtractDescriptor | [string | ExtractDescriptor];
export interface ExtractMap { export type ExtractMap = Record<string, ExtractValue>;
[key: string]: ExtractValue;
}
type ExtractedValue<V extends ExtractValue, M extends ExtractMap> = V extends [ type ExtractedValue<V extends ExtractValue, M extends ExtractMap> = V extends [
string | ExtractDescriptor, string | ExtractDescriptor,

View File

@@ -82,7 +82,7 @@ export function serializeArray<T extends AnyNode>(
} }
>((_, elem) => { >((_, elem) => {
const $elem = this._make(elem); const $elem = this._make(elem);
const name = $elem.attr('name') as string; // We have filtered for elements with a name before. const name = $elem.attr('name')!; // We have filtered for elements with a name before.
// If there is no value set (e.g. `undefined`, `null`), then default value to empty // If there is no value set (e.g. `undefined`, `null`), then default value to empty
const value = $elem.val() ?? ''; const value = $elem.val() ?? '';

View File

@@ -20,6 +20,7 @@ import { domEach, isHtml, isCheerio } from '../utils.js';
import { removeElement } from 'domutils'; import { removeElement } from 'domutils';
import type { Cheerio } from '../cheerio.js'; import type { Cheerio } from '../cheerio.js';
import type { BasicAcceptedElems, AcceptedElems } from '../types.js'; import type { BasicAcceptedElems, AcceptedElems } from '../types.js';
import { ElementType } from 'htmlparser2';
/** /**
* Create an array of nodes, recursing into arrays and parsing strings if * Create an array of nodes, recursing into arrays and parsing strings if
@@ -129,7 +130,7 @@ function uniqueSplice(
newElems: AnyNode[], newElems: AnyNode[],
parent: ParentNode, parent: ParentNode,
): AnyNode[] { ): AnyNode[] {
const spliceArgs: Parameters<typeof Array.prototype.splice> = [ const spliceArgs: Parameters<AnyNode[]['splice']> = [
spliceIdx, spliceIdx,
spliceCount, spliceCount,
...newElems, ...newElems,
@@ -152,7 +153,7 @@ function uniqueSplice(
const oldSiblings: AnyNode[] = oldParent.children; const oldSiblings: AnyNode[] = oldParent.children;
const prevIdx = oldSiblings.indexOf(node); const prevIdx = oldSiblings.indexOf(node);
if (prevIdx > -1) { if (prevIdx !== -1) {
oldParent.children.splice(prevIdx, 1); oldParent.children.splice(prevIdx, 1);
if (parent === oldParent && spliceIdx > prevIdx) { if (parent === oldParent && spliceIdx > prevIdx) {
spliceArgs[0]--; spliceArgs[0]--;
@@ -588,7 +589,9 @@ export function wrapAll<T extends AnyNode>(
let elInsertLocation: Element | undefined; let elInsertLocation: Element | undefined;
for (let i = 0; i < wrap.length; i++) { for (let i = 0; i < wrap.length; i++) {
if (wrap[i].type === 'tag') elInsertLocation = wrap[i] as Element; if (wrap[i].type === ElementType.Tag) {
elInsertLocation = wrap[i] as Element;
}
} }
let j = 0; let j = 0;
@@ -599,8 +602,8 @@ export function wrapAll<T extends AnyNode>(
*/ */
while (elInsertLocation && j < elInsertLocation.children.length) { while (elInsertLocation && j < elInsertLocation.children.length) {
const child = elInsertLocation.children[j]; const child = elInsertLocation.children[j];
if (child.type === 'tag') { if (child.type === ElementType.Tag) {
elInsertLocation = child as Element; elInsertLocation = child;
j = 0; j = 0;
} else { } else {
j++; j++;
@@ -652,7 +655,7 @@ export function after<T extends AnyNode>(
// If not found, move on // If not found, move on
/* istanbul ignore next */ /* istanbul ignore next */
if (index < 0) return; if (index === -1) return;
const domSrc = const domSrc =
typeof elems[0] === 'function' typeof elems[0] === 'function'
@@ -711,7 +714,7 @@ export function insertAfter<T extends AnyNode>(
// If not found, move on // If not found, move on
/* istanbul ignore next */ /* istanbul ignore next */
if (index < 0) continue; if (index === -1) continue;
// Add cloned `this` element(s) after target element // Add cloned `this` element(s) after target element
uniqueSplice(siblings, index + 1, 0, clonedSelf, parent); uniqueSplice(siblings, index + 1, 0, clonedSelf, parent);
@@ -761,7 +764,7 @@ export function before<T extends AnyNode>(
// If not found, move on // If not found, move on
/* istanbul ignore next */ /* istanbul ignore next */
if (index < 0) return; if (index === -1) return;
const domSrc = const domSrc =
typeof elems[0] === 'function' typeof elems[0] === 'function'
@@ -818,7 +821,7 @@ export function insertBefore<T extends AnyNode>(
// If not found, move on // If not found, move on
/* istanbul ignore next */ /* istanbul ignore next */
if (index < 0) return; if (index === -1) return;
// Add cloned `this` element(s) after target element // Add cloned `this` element(s) after target element
uniqueSplice(siblings, index, 0, clonedSelf, parent); uniqueSplice(siblings, index, 0, clonedSelf, parent);
@@ -1097,8 +1100,9 @@ export function text<T extends AnyNode>(
* @see {@link https://api.jquery.com/clone/} * @see {@link https://api.jquery.com/clone/}
*/ */
export function clone<T extends AnyNode>(this: Cheerio<T>): Cheerio<T> { export function clone<T extends AnyNode>(this: Cheerio<T>): Cheerio<T> {
const clone = Array.prototype.map.call(this.get(), (el) => const clone = Array.prototype.map.call(
cloneNode(el, true), this.get(),
(el) => cloneNode(el, true) as T,
) as T[]; ) as T[];
// Add a root node around the cloned nodes // Add a root node around the cloned nodes

View File

@@ -30,7 +30,8 @@ describe('$(...)', () => {
describe('.load', () => { describe('.load', () => {
it('should throw a TypeError if given invalid input', () => { it('should throw a TypeError if given invalid input', () => {
expect(() => { expect(() => {
(load as any)(); // @ts-expect-error Testing invalid input
load();
}).toThrow('cheerio.load() expects a string'); }).toThrow('cheerio.load() expects a string');
}); });
}); });
@@ -858,7 +859,7 @@ describe('$(...)', () => {
it('should yield each element', () => { it('should yield each element', () => {
// The equivalent of: for (const element of $('li')) ... // The equivalent of: for (const element of $('li')) ...
const $li = $('li'); const $li = $('li');
const iterator = $li[Symbol.iterator](); const iterator = $li[Symbol.iterator]() as Iterator<Element, Element>;
expect(iterator.next().value.attribs).toHaveProperty('class', 'apple'); expect(iterator.next().value.attribs).toHaveProperty('class', 'apple');
expect(iterator.next().value.attribs).toHaveProperty('class', 'orange'); expect(iterator.next().value.attribs).toHaveProperty('class', 'orange');
expect(iterator.next().value.attribs).toHaveProperty('class', 'pear'); expect(iterator.next().value.attribs).toHaveProperty('class', 'pear');

View File

@@ -1025,7 +1025,7 @@ export function get<T>(this: Cheerio<T>, i?: number): T | T[] {
* @returns The contained items. * @returns The contained items.
*/ */
export function toArray<T>(this: Cheerio<T>): T[] { export function toArray<T>(this: Cheerio<T>): T[] {
return Array.prototype.slice.call(this); return (Array.prototype as T[]).slice.call(this);
} }
/** /**
@@ -1097,7 +1097,7 @@ export function slice<T>(
start?: number, start?: number,
end?: number, end?: number,
): Cheerio<T> { ): Cheerio<T> {
return this._make(Array.prototype.slice.call(this, start, end)); return this._make<T>(Array.prototype.slice.call(this, start, end));
} }
/** /**
@@ -1116,7 +1116,7 @@ export function slice<T>(
* @see {@link https://api.jquery.com/end/} * @see {@link https://api.jquery.com/end/}
*/ */
export function end<T>(this: Cheerio<T>): Cheerio<AnyNode> { export function end<T>(this: Cheerio<T>): Cheerio<AnyNode> {
return this.prevObject ?? this._make([]); return (this.prevObject as Cheerio<AnyNode> | null) ?? this._make([]);
} }
/** /**
@@ -1166,6 +1166,8 @@ export function addBack<T extends AnyNode>(
selector?: string, selector?: string,
): Cheerio<AnyNode> { ): Cheerio<AnyNode> {
return this.prevObject return this.prevObject
? this.add(selector ? this.prevObject.filter(selector) : this.prevObject) ? this.add<AnyNode, T>(
selector ? this.prevObject.filter(selector) : this.prevObject,
)
: this; : this;
} }

View File

@@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest';
import { parseDOM } from 'htmlparser2'; import { parseDOM } from 'htmlparser2';
import { type Cheerio } from './index.js'; import { type Cheerio } from './index.js';
import { cheerio, fruits, food, noscript } from './__fixtures__/fixtures.js'; import { cheerio, fruits, food, noscript } from './__fixtures__/fixtures.js';
import type { Element } from 'domhandler'; import type { AnyNode, Element } from 'domhandler';
declare module './index.js' { declare module './index.js' {
interface Cheerio<T> { interface Cheerio<T> {
@@ -10,7 +10,7 @@ declare module './index.js' {
context: Cheerio<T>; context: Cheerio<T>;
args: unknown[]; args: unknown[];
}; };
foo(): void; foo(this: void): void;
} }
} }
@@ -221,17 +221,23 @@ describe('cheerio', () => {
}); });
it('(extended Array) should not interfere with prototype methods (issue #119)', () => { it('(extended Array) should not interfere with prototype methods (issue #119)', () => {
const extended: any = []; const extended: AnyNode[] = [];
// @ts-expect-error - Ignore for testing
extended.find = extended.find =
// @ts-expect-error - Ignore for testing
extended.children = extended.children =
// @ts-expect-error - Ignore for testing
extended.each = extended.each =
function () { function () {
/* Ignore */ /* Ignore */
}; };
const $empty = cheerio(extended); const $empty = cheerio(extended);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
expect($empty.find).toBe(cheerio.prototype.find); expect($empty.find).toBe(cheerio.prototype.find);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
expect($empty.children).toBe(cheerio.prototype.children); expect($empty.children).toBe(cheerio.prototype.children);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
expect($empty.each).toBe(cheerio.prototype.each); expect($empty.each).toBe(cheerio.prototype.each);
}); });
@@ -360,7 +366,9 @@ describe('cheerio', () => {
it('should honor extensions defined on `prototype` property', () => { it('should honor extensions defined on `prototype` property', () => {
const $ = cheerio.load('<div>'); const $ = cheerio.load('<div>');
$.prototype.myPlugin = function (...args: unknown[]) { ($.prototype as Cheerio<AnyNode>).myPlugin = function (
...args: unknown[]
) {
return { return {
context: this, context: this,
args, args,
@@ -394,7 +402,7 @@ describe('cheerio', () => {
const $a = cheerio.load('<div>'); const $a = cheerio.load('<div>');
const $b = cheerio.load('<div>'); const $b = cheerio.load('<div>');
$a.prototype.foo = function () { ($a.prototype as Cheerio<AnyNode>).foo = function () {
/* Ignore */ /* Ignore */
}; };

View File

@@ -9,12 +9,12 @@ function noop() {
// Returns a promise and a resolve function // Returns a promise and a resolve function
function getPromise() { function getPromise() {
let cb: (error: Error | null | undefined, $: cheerio.CheerioAPI) => void; let cb!: (error: Error | null | undefined, $: cheerio.CheerioAPI) => void;
const promise = new Promise<cheerio.CheerioAPI>((resolve, reject) => { const promise = new Promise<cheerio.CheerioAPI>((resolve, reject) => {
cb = (error, $) => (error ? reject(error) : resolve($)); cb = (error, $) => (error ? reject(error) : resolve($));
}); });
return { promise, cb: cb! }; return { promise, cb };
} }
const TEST_HTML = '<h1>Hello World</h1>'; const TEST_HTML = '<h1>Hello World</h1>';

View File

@@ -228,10 +228,11 @@ export async function fromURL(
const promise = new Promise<CheerioAPI>((resolve, reject) => { const promise = new Promise<CheerioAPI>((resolve, reject) => {
undiciStream = undici.stream(url, requestOptions, (res) => { undiciStream = undici.stream(url, requestOptions, (res) => {
const contentType = res.headers['content-type'] ?? 'text/html'; const contentTypeHeader = res.headers['content-type'] ?? 'text/html';
const mimeType = new MIMEType( const contentType = Array.isArray(contentTypeHeader)
Array.isArray(contentType) ? contentType[0] : contentType, ? contentTypeHeader[0]
); : contentTypeHeader;
const mimeType = new MIMEType(contentType);
if (!mimeType.isHTML() && !mimeType.isXML()) { if (!mimeType.isHTML() && !mimeType.isXML()) {
throw new RangeError( throw new RangeError(

View File

@@ -8,6 +8,7 @@ import { Cheerio } from './cheerio.js';
import { isHtml, isCheerio } from './utils.js'; import { isHtml, isCheerio } from './utils.js';
import type { AnyNode, Document, Element, ParentNode } from 'domhandler'; import type { AnyNode, Document, Element, ParentNode } from 'domhandler';
import type { SelectorType, BasicAcceptedElems } from './types.js'; import type { SelectorType, BasicAcceptedElems } from './types.js';
import { ElementType } from 'htmlparser2';
type StaticType = typeof staticMethods; type StaticType = typeof staticMethods;
@@ -104,7 +105,7 @@ export interface CheerioAPI extends StaticType {
} }
export function getLoad( export function getLoad(
parse: typeof Cheerio.prototype._parse, parse: Cheerio<AnyNode>['_parse'],
render: ( render: (
dom: AnyNode | ArrayLike<AnyNode>, dom: AnyNode | ArrayLike<AnyNode>,
options: InternalOptions, options: InternalOptions,
@@ -209,7 +210,7 @@ export function getLoad(
const instance = new LoadedCheerio(elements, rootInstance, options); const instance = new LoadedCheerio(elements, rootInstance, options);
if (elements) { if (elements) {
return instance as any; return instance as Cheerio<Result>;
} }
if (typeof selector !== 'string') { if (typeof selector !== 'string') {
@@ -243,7 +244,7 @@ export function getLoad(
: rootInstance; : rootInstance;
// If we still don't have a context, return // If we still don't have a context, return
if (!searchContext) return instance as any; if (!searchContext) return instance as Cheerio<Result>;
/* /*
* #id, .class, tag * #id, .class, tag
@@ -267,11 +268,15 @@ export function getLoad(
}; };
} }
function isNode(obj: any): obj is AnyNode { function isNode(obj: unknown): obj is AnyNode {
return ( return (
// @ts-expect-error: TS doesn't know about the `name` property.
!!obj.name || !!obj.name ||
obj.type === 'root' || // @ts-expect-error: TS doesn't know about the `type` property.
obj.type === 'text' || obj.type === ElementType.Root ||
obj.type === 'comment' // @ts-expect-error: TS doesn't know about the `type` property.
obj.type === ElementType.Text ||
// @ts-expect-error: TS doesn't know about the `type` property.
obj.type === ElementType.Comment
); );
} }

View File

@@ -264,7 +264,7 @@ describe('parse', () => {
expect(childNodes[0].previousSibling).toBe(null); expect(childNodes[0].previousSibling).toBe(null);
expect(childNodes[0].nextSibling).toBe(childNodes[1]); expect(childNodes[0].nextSibling).toBe(childNodes[1]);
expect(childNodes[0].parentNode).toBe(root); expect(childNodes[0].parentNode).toBe(root);
expect((childNodes[0] as Element).childNodes).toHaveLength(0); expect(childNodes[0].childNodes).toHaveLength(0);
expect(childNodes[0].firstChild).toBe(null); expect(childNodes[0].firstChild).toBe(null);
expect(childNodes[0].lastChild).toBe(null); expect(childNodes[0].lastChild).toBe(null);
@@ -335,23 +335,17 @@ describe('parse', () => {
false, false,
null, null,
); );
const childNodes = root.childNodes as Element[];
expect(childNodes[0].tagName).toBe('table'); const table = root.childNodes[0] as Element;
expect(childNodes[0].childNodes.length).toBe(1); expect(table.tagName).toBe('table');
expect(childNodes[0].childNodes[0]).toHaveProperty('tagName', 'tbody'); expect(table.childNodes.length).toBe(1);
expect((childNodes[0] as any).childNodes[0].childNodes[0]).toHaveProperty( const tbody = table.childNodes[0] as Element;
'tagName', expect(table.childNodes[0]).toHaveProperty('tagName', 'tbody');
'tr', const tr = tbody.childNodes[0] as Element;
); expect(tr).toHaveProperty('tagName', 'tr');
expect( const td = tr.childNodes[0] as Element;
(childNodes[0] as any).childNodes[0].childNodes[0].childNodes[0] expect(td).toHaveProperty('tagName', 'td');
.tagName, expect(td.childNodes[0]).toHaveProperty('data', 'bar');
).toBe('td');
expect(
(childNodes[0] as any).childNodes[0].childNodes[0].childNodes[0]
.childNodes[0].data,
).toBe('bar');
}); });
it('Should parse custom tag <line>', () => { it('Should parse custom tag <line>', () => {

View File

@@ -152,14 +152,14 @@ export function text(
export function parseHTML( export function parseHTML(
this: CheerioAPI, this: CheerioAPI,
data: string, data: string,
context?: unknown | boolean, context?: unknown,
keepScripts?: boolean, keepScripts?: boolean,
): AnyNode[]; ): AnyNode[];
export function parseHTML(this: CheerioAPI, data?: '' | null): null; export function parseHTML(this: CheerioAPI, data?: '' | null): null;
export function parseHTML( export function parseHTML(
this: CheerioAPI, this: CheerioAPI,
data?: string | null, data?: string | null,
context?: unknown | boolean, context?: unknown,
keepScripts = typeof context === 'boolean' ? context : false, keepScripts = typeof context === 'boolean' ? context : false,
): AnyNode[] | null { ): AnyNode[] | null {
if (!data || typeof data !== 'string') { if (!data || typeof data !== 'string') {

View File

@@ -8,8 +8,10 @@ import type { Cheerio } from './cheerio.js';
* @param maybeCheerio - The object to check. * @param maybeCheerio - The object to check.
* @returns Whether the object is a Cheerio instance. * @returns Whether the object is a Cheerio instance.
*/ */
export function isCheerio<T>(maybeCheerio: any): maybeCheerio is Cheerio<T> { export function isCheerio<T>(
return maybeCheerio.cheerio != null; maybeCheerio: unknown,
): maybeCheerio is Cheerio<T> {
return (maybeCheerio as Cheerio<T>).cheerio != null;
} }
/** /**
@@ -21,7 +23,7 @@ export function isCheerio<T>(maybeCheerio: any): maybeCheerio is Cheerio<T> {
* @returns String in camel case notation. * @returns String in camel case notation.
*/ */
export function camelCase(str: string): string { export function camelCase(str: string): string {
return str.replace(/[._-](\w|$)/g, (_, x) => x.toUpperCase()); return str.replace(/[._-](\w|$)/g, (_, x) => (x as string).toUpperCase());
} }
/** /**
@@ -58,7 +60,7 @@ export function domEach<
return array; return array;
} }
const enum CharacterCodes { const enum CharacterCode {
LowerA = 97, LowerA = 97,
LowerZ = 122, LowerZ = 122,
UpperA = 65, UpperA = 65,
@@ -80,14 +82,14 @@ const enum CharacterCodes {
export function isHtml(str: string): boolean { export function isHtml(str: string): boolean {
const tagStart = str.indexOf('<'); const tagStart = str.indexOf('<');
if (tagStart < 0 || tagStart > str.length - 3) return false; if (tagStart === -1 || tagStart > str.length - 3) return false;
const tagChar = str.charCodeAt(tagStart + 1); const tagChar = str.charCodeAt(tagStart + 1) as CharacterCode;
return ( return (
((tagChar >= CharacterCodes.LowerA && tagChar <= CharacterCodes.LowerZ) || ((tagChar >= CharacterCode.LowerA && tagChar <= CharacterCode.LowerZ) ||
(tagChar >= CharacterCodes.UpperA && tagChar <= CharacterCodes.UpperZ) || (tagChar >= CharacterCode.UpperA && tagChar <= CharacterCode.UpperZ) ||
tagChar === CharacterCodes.Exclamation) && tagChar === CharacterCode.Exclamation) &&
str.includes('>', tagStart + 2) str.includes('>', tagStart + 2)
); );
} }

View File

@@ -1,6 +1,6 @@
import { defineConfig } from 'vitest/config'; import { defineConfig, type ViteUserConfig } from 'vitest/config';
export default defineConfig({ const config: ViteUserConfig = defineConfig({
test: { test: {
coverage: { coverage: {
exclude: [ exclude: [
@@ -13,3 +13,5 @@ export default defineConfig({
}, },
}, },
}); });
export default config;