diff --git a/dts.config.ts b/dts.config.ts index f48cce5..4933b0e 100644 --- a/dts.config.ts +++ b/dts.config.ts @@ -1,4 +1,4 @@ -const {globSync} = require('node:fs'); +const {globSync} = require('glob'); const entries = globSync('./src/js/**/*.ts').map(file => ({ filePath: `${__dirname}/${file}`, diff --git a/package-lock.json b/package-lock.json index 3f923e2..8c722fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@types/node": "^22.7", "@vitest/coverage-istanbul": "^2.1", "dts-bundle-generator": "^9.5", + "glob": "^11", "happy-dom": "^15.7", "sass": "^1.79", "typescript": "^5.6", @@ -52,9 +53,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.7.tgz", - "integrity": "sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", + "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", "dev": true, "license": "MIT", "engines": { @@ -62,9 +63,9 @@ } }, "node_modules/@babel/core": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.7.tgz", - "integrity": "sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", + "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", "dev": true, "license": "MIT", "dependencies": { @@ -74,10 +75,10 @@ "@babel/helper-compilation-targets": "^7.25.7", "@babel/helper-module-transforms": "^7.25.7", "@babel/helpers": "^7.25.7", - "@babel/parser": "^7.25.7", + "@babel/parser": "^7.25.8", "@babel/template": "^7.25.7", "@babel/traverse": "^7.25.7", - "@babel/types": "^7.25.7", + "@babel/types": "^7.25.8", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -253,13 +254,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", - "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", + "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.7" + "@babel/types": "^7.25.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -303,9 +304,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", - "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", + "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", "dev": true, "license": "MIT", "dependencies": { @@ -953,6 +954,292 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@parcel/watcher": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.1.tgz", + "integrity": "sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.4.1", + "@parcel/watcher-darwin-arm64": "2.4.1", + "@parcel/watcher-darwin-x64": "2.4.1", + "@parcel/watcher-freebsd-x64": "2.4.1", + "@parcel/watcher-linux-arm-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-glibc": "2.4.1", + "@parcel/watcher-linux-arm64-musl": "2.4.1", + "@parcel/watcher-linux-x64-glibc": "2.4.1", + "@parcel/watcher-linux-x64-musl": "2.4.1", + "@parcel/watcher-win32-arm64": "2.4.1", + "@parcel/watcher-win32-ia32": "2.4.1", + "@parcel/watcher-win32-x64": "2.4.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1196,9 +1483,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.7.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz", - "integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==", + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1397,6 +1684,19 @@ "balanced-match": "^1.0.0" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", @@ -1441,9 +1741,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001667", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz", - "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==", + "version": "1.0.30001668", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001668.tgz", + "integrity": "sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw==", "dev": true, "funding": [ { @@ -1700,6 +2000,19 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/dts-bundle-generator": { "version": "9.5.1", "resolved": "https://registry.npmjs.org/dts-bundle-generator/-/dts-bundle-generator-9.5.1.tgz", @@ -1725,9 +2038,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.32", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.32.tgz", - "integrity": "sha512-M+7ph0VGBQqqpTT2YrabjNKSQ2fEl9PVx6AK3N558gDH9NO8O6XN9SXXFWRo9u9PbEg/bWq+tjXQr+eXmxubCw==", + "version": "1.5.36", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.36.tgz", + "integrity": "sha512-HYTX8tKge/VNp6FGO+f/uVDmUkq+cEfcxYhKf15Akc4M5yxt5YmorwlAitKWjWhWQnKcDRBAQKXkhqqXMqcrjw==", "dev": true, "license": "ISC" }, @@ -1820,6 +2133,19 @@ "@types/estree": "^1.0.0" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -1872,33 +2198,26 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -1952,6 +2271,16 @@ "dev": true, "license": "MIT" }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1962,6 +2291,29 @@ "node": ">=8" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2064,19 +2416,19 @@ } }, "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, + "engines": { + "node": "20 || >=22" + }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/js-tokens": { @@ -2113,14 +2465,11 @@ } }, "node_modules/loupe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", - "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", + "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.1" - } + "license": "MIT" }, "node_modules/lru-cache": { "version": "5.1.1", @@ -2133,9 +2482,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, "license": "MIT", "dependencies": { @@ -2170,17 +2519,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -2222,6 +2585,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", @@ -2247,28 +2617,31 @@ } }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", + "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "engines": { + "node": "20 || >=22" + } }, "node_modules/pathe": { "version": "1.1.2", @@ -2294,6 +2667,19 @@ "dev": true, "license": "ISC" }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { "version": "8.4.47", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", @@ -2384,12 +2770,13 @@ } }, "node_modules/sass": { - "version": "1.79.4", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.4.tgz", - "integrity": "sha512-K0QDSNPXgyqO4GZq2HO5Q70TLxTH6cIT59RdoCHMivrC8rqzaTw5ab9prjz9KUN1El4FLXrBXJhik61JR4HcGg==", + "version": "1.79.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.5.tgz", + "integrity": "sha512-W1h5kp6bdhqFh2tk3DsI771MoEJjvrSY/2ihJRJS4pjIyfJCw0nTsxqhnrUzaLMOJjFchj8rOvraI/YUVjtx5g==", "dev": true, "license": "MIT", "dependencies": { + "@parcel/watcher": "^2.4.1", "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" @@ -2613,6 +3000,83 @@ "node": ">=18" } }, + "node_modules/test-exclude/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/test-exclude/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -2667,6 +3131,19 @@ "node": ">=4" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/type-fest": { "version": "4.26.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", @@ -2680,9 +3157,9 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/package.json b/package.json index 22f7bd3..e7dbb21 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@types/node": "^22.7", "@vitest/coverage-istanbul": "^2.1", "dts-bundle-generator": "^9.5", + "glob": "^11", "happy-dom": "^15.7", "sass": "^1.79", "typescript": "^5.6", @@ -223,11 +224,11 @@ "build:js": "npx vite build && npm run types", "clean": "rm -rf ./dist && rm -rf ./types && rm -f ./tsconfig.tsbuildinfo", "test": "npx vitest --coverage", - "types": "npx tsc && npx dts-bundle-generator --config ./dts.config.ts", + "types": "npx tsc && npx dts-bundle-generator --config ./dts.config.ts --silent", "watch:css": "npx sass ./src/css:./dist/css --no-source-map --watch", "watch:js": "npx vite build --watch" }, "type": "module", "types": "./types/index.d.cts", - "version": "0.75.0" + "version": "0.76.0" } diff --git a/src/js/array/chunk.ts b/src/js/array/chunk.ts index fd9feeb..26d5df6 100644 --- a/src/js/array/chunk.ts +++ b/src/js/array/chunk.ts @@ -1,16 +1,17 @@ +import {clamp} from '~/number'; + /** - * Chunks an array into smaller arrays of a specified size + * Chunk an array _(into smaller arrays of a specified size)_ */ -export function chunk(array: Value[], size?: number): Value[][] { +export function chunk(array: Item[], size?: number): Item[][] { + const chunkSize = clamp(size ?? 64_000, 1, 64_000); const {length} = array; - const chunkSize = typeof size === 'number' && size > 0 ? size : 64_000; - if (length <= chunkSize) { return [array]; } - const chunks: Value[][] = []; + const chunks: Item[][] = []; let remaining = Number(length); diff --git a/src/js/array/compact.ts b/src/js/array/compact.ts index 24e51a6..478a3f4 100644 --- a/src/js/array/compact.ts +++ b/src/js/array/compact.ts @@ -1,19 +1,17 @@ /** - * Compacts and returns an array with all falsey values removed + * Compact an array _(removing all `null` and `undefined` values)_ */ -export function compact( - array: Value[], - strict: true, -): Exclude[]; +export function compact(array: Item[]): Exclude[]; /** - * Compacts and returns an array with all `null` and `undefined` values removed + * Compact an array _(removing all falsey values)_ */ -export function compact( - array: Value[], -): Exclude[]; +export function compact( + array: Item[], + strict: true, +): Exclude[]; -export function compact(array: Value[], strict?: boolean): Value[] { +export function compact(array: Item[], strict?: boolean): Item[] { return strict === true ? array.filter(item => !!item) : array.filter(item => item != null); diff --git a/src/js/array/count.ts b/src/js/array/count.ts index 6675f28..3cfa562 100644 --- a/src/js/array/count.ts +++ b/src/js/array/count.ts @@ -1,29 +1,49 @@ -import type {BooleanCallback, KeyCallback} from '@/array/models'; -import {findValues} from '@/internal/array-find'; -import type {Key} from '@/models'; +import {findValues} from '~/internal/array/find'; +import type {BooleanCallback, KeyCallback} from '~/array/models'; /** - * Returns the number of items _(count)_ that match the given value + * Get the number of items _(count)_ that match the given value */ -export function count( - array: Model[], - value: Value | BooleanCallback, +export function count(array: Item[], value: Item): number; + +/** + * Get the number of items _(count)_ that match the given value + */ +export function count( + array: Item[], + matches: BooleanCallback, +): number; + +/** + * Get the number of items _(count)_ that match the given value + */ +export function count( + array: Item[], + key: Key, + value: Item[Key], ): number; /** - * - Returns the number of items _(count)_ that match the given value - * - Use `key` to find a comparison value to match with `value` + * Get the number of items _(count)_ that match the given value */ -export function count( - array: Model[], - value: Value, - key: Key | KeyCallback, +export function count>( + array: Item[], + key: Key, + value: ReturnType, ): number; -export function count( - array: Model[], - value: Value | BooleanCallback, - key?: Key | KeyCallback, -): number { - return findValues('all', array, value, key).length; +export function count(array: unknown[], ...parameters: unknown[]): number { + const {length} = parameters; + + return findValues( + 'all', + array, + length === 1 && typeof parameters[0] === 'function' + ? parameters[0] + : undefined, + length === 2 ? parameters[0] : undefined, + length === 1 && typeof parameters[0] !== 'function' + ? parameters[0] + : parameters[1], + ).length; } diff --git a/src/js/array/exists.ts b/src/js/array/exists.ts index 35d4d0b..b3b8a6e 100644 --- a/src/js/array/exists.ts +++ b/src/js/array/exists.ts @@ -1,29 +1,53 @@ -import type {BooleanCallback, KeyCallback} from '@/array/models'; -import {findValue} from '@/internal/array-find'; -import type {Key} from '@/models'; +import {findValue} from '~/internal/array/find'; +import type {BooleanCallback, KeyCallback} from './models'; /** * Does the value exist in array? */ -export function exists( - array: Model[], - value: Value | BooleanCallback, +export function exists(array: Item[], value: Item): boolean; + +/** + * Does the value exist in array? + */ +export function exists( + array: Item[], + matches: BooleanCallback, ): boolean; /** * - Does the value exist in array? * - Use `key` to find a comparison value to match with `value` */ -export function exists( - array: Model[], - value: Value, - key: Key | KeyCallback, +export function exists( + array: Item[], + key: Key, + value: Item[Key], ): boolean; -export function exists( - array: Model[], - value: Value | BooleanCallback, - key?: Key | KeyCallback, -): boolean { - return (findValue('index', array, value, key) as number) > -1; +/** + * - Does the value exist in array? + * - Use `key` to find a comparison value to match with `value` + */ +export function exists>( + array: Item[], + key: Key, + value: ReturnType, +): boolean; + +export function exists(array: unknown[], ...parameters: unknown[]): boolean { + const {length} = parameters; + + return ( + (findValue( + 'index', + array, + length === 1 && typeof parameters[0] === 'function' + ? parameters[0] + : undefined, + length === 2 ? parameters[0] : undefined, + length === 1 && typeof parameters[0] !== 'function' + ? parameters[0] + : parameters[1], + ) as number) > -1 + ); } diff --git a/src/js/array/filter.ts b/src/js/array/filter.ts index 808f118..bb3d30f 100644 --- a/src/js/array/filter.ts +++ b/src/js/array/filter.ts @@ -1,29 +1,51 @@ -import type {BooleanCallback, KeyCallback} from '@/array/models'; -import {findValues} from '@/internal/array-find'; -import type {Key} from '@/models'; +import type {BooleanCallback, KeyCallback} from '~/array/models'; +import {findValues} from '~/internal/array/find'; /** - * Returns a filtered array of items matching `value` + * Get a filtered array of items matching `value` */ -export function filter( - array: Model[], - value: Value | BooleanCallback, -): Model[]; +export function filter(array: Item[], value: Item): Item[]; /** - * - Returns a filtered array of items + * Get a filtered array of items matching `value` + */ +export function filter( + array: Item[], + matches: BooleanCallback, +): Item[]; + +/** + * - Get a filtered array of items + * - Use `key` to find a comparison value to match with `value` + */ +export function filter( + array: Item[], + key: Key, + value: Item[Key], +): Item[]; + +/** + * - Get a filtered array of items * - Use `key` to find a comparison value to match with `value` */ -export function filter( - array: Model[], - value: Value, - key: Key | KeyCallback, -): Model[]; +export function filter>( + array: Item[], + key: Key, + value: ReturnType, +): Item[]; + +export function filter(array: unknown[], ...parameters: unknown[]): unknown[] { + const {length} = parameters; -export function filter( - array: Model[], - value: Value | BooleanCallback, - key?: Key | KeyCallback, -): Model[] { - return findValues('all', array, value, key); + return findValues( + 'all', + array, + length === 1 && typeof parameters[0] === 'function' + ? parameters[0] + : undefined, + length === 2 ? parameters[0] : undefined, + length === 1 && typeof parameters[0] !== 'function' + ? parameters[0] + : parameters[1], + ); } diff --git a/src/js/array/find.ts b/src/js/array/find.ts index e70d5a0..ba13a23 100644 --- a/src/js/array/find.ts +++ b/src/js/array/find.ts @@ -1,29 +1,54 @@ -import type {BooleanCallback, KeyCallback} from '@/array/models'; -import {findValue} from '@/internal/array-find'; -import type {Key} from '@/models'; +import type {BooleanCallback, KeyCallback} from '~/array/models'; +import {findValue} from '~/internal/array/find'; /** - * Returns the first item matching `value`, or `undefined` if no match is found + * Get the first item matching `value` _(or `undefined` if no match is found)_ */ -export function find( - array: Model[], - value: Value | BooleanCallback, -): Model | undefined; +export function find(array: Item[], value: Item): Item | undefined; /** - * - Returns the first matching item, or `undefined` if no match is found + * Get the first item matching `value` _(or `undefined` if no match is found)_ + */ +export function find( + array: Item[], + matches: BooleanCallback, +): Item | undefined; + +/** + * - Get the first matching item _(or `undefined` if no match is found)_ + * - Use `key` to find a comparison value to match with `value` + */ +export function find( + array: Item[], + key: Key, + value: Item[Key], +): Item | undefined; + +/** + * - Get the first matching item _(or `undefined` if no match is found)_ * - Use `key` to find a comparison value to match with `value` */ -export function find( - array: Model[], - value: Value, - key: Key | KeyCallback, -): Model | undefined; +export function find>( + array: Item[], + key: Key, + value: ReturnType, +): Item | undefined; + +export function find( + array: unknown[], + ...parameters: unknown[] +): Item | undefined { + const {length} = parameters; -export function find( - array: Model[], - value: Value | BooleanCallback, - key?: Key | KeyCallback, -): Model | undefined { - return findValue('value', array, value, key) as Model | undefined; + return findValue( + 'value', + array, + length === 1 && typeof parameters[0] === 'function' + ? parameters[0] + : undefined, + length === 2 ? parameters[0] : undefined, + length === 1 && typeof parameters[0] !== 'function' + ? parameters[0] + : parameters[1], + ) as Item | undefined; } diff --git a/src/js/array/group-by.ts b/src/js/array/group-by.ts index 3830f88..1a1d230 100644 --- a/src/js/array/group-by.ts +++ b/src/js/array/group-by.ts @@ -1,47 +1,178 @@ -import type {KeyCallback} from '@/array/models'; -import {getCallbacks} from '@/internal/array-callbacks'; -import type {Key} from '@/models'; +import type {KeyCallback, ValueCallback} from '~/array/models'; +import {getCallbacks} from '~/internal/array/callbacks'; +import type {Key, KeyedValue, PlainObject} from '~/models'; /** - * Groups an array of items using a key or callback + * Create a record from an array of items using a specific key */ -export function groupBy( - array: Value[], - key: Key | KeyCallback, -): Record { - return groupValues(array, key, true, false) as never; +export function groupBy( + array: Item[], + key: Key, +): Record, Item>; + +/** + * Create a record from an array of items using a specific key, and grouping them into arrays + */ +export function groupBy( + array: Item[], + key: Key, + arrays: true, +): Record, Item[]>; + +/** + * Create a record from an array of items using a specific key + */ +export function groupBy>( + array: Item[], + key: Key, +): Record, Item>; + +/** + * Create a record from an array of items using a specific key, and grouping them into arrays + */ +export function groupBy>( + array: Item[], + key: Key, + arrays: true, +): Record, Item[]>; + +/** + * Create a record from an array of items using a specific key and value + */ +export function groupBy( + array: Item[], + key: Key, + value: Value, +): Record, KeyedValue>; + +/** + * Create a record from an array of items using a specific key and value, and grouping them into arrays + */ +export function groupBy( + array: Item[], + key: Key, + value: Value, + arrays: true, +): Record, Array>>; + +/** + * Create a record from an array of items using a specific key and value + */ +export function groupBy< + Item, + Key extends keyof Item, + Value extends ValueCallback, +>( + array: Item[], + key: Key, + value: Value, +): Record, ReturnType>; + +/** + * Create a record from an array of items using a specific key and value, and grouping them into arrays + */ +export function groupBy< + Item, + Key extends keyof Item, + Value extends ValueCallback, +>( + array: Item[], + key: Key, + value: Value, + arrays: true, +): Record, Array>>; + +/** + * Create a record from an array of items using a specific key and value + */ +export function groupBy< + Item, + Key extends KeyCallback, + Value extends keyof Item, +>( + array: Item[], + key: Key, + value: Value, +): Record, KeyedValue>; + +/** + * Create a record from an array of items using a specific key and value, and grouping them into arrays + */ +export function groupBy< + Item, + Key extends KeyCallback, + Value extends keyof Item, +>( + array: Item[], + key: Key, + value: Value, + arrays: true, +): Record, Array>>; + +/** + * Create a record from an array of items using a specific key and value + */ +export function groupBy< + Item, + Key extends KeyCallback, + Value extends ValueCallback, +>( + array: Item[], + key: Key, + value: Value, +): Record, ReturnType>; + +/** + * Create a record from an array of items using a specific key and value, and grouping them into arrays + */ +export function groupBy< + Item, + Key extends KeyCallback, + Value extends ValueCallback, +>( + array: Item[], + key: Key, + value: Value, + arrays: true, +): Record, Array>>; + +export function groupBy( + array: unknown[], + first?: boolean, + second?: unknown, + third?: boolean, +): PlainObject { + return groupValues( + array, + first, + second, + first === true || second === true || third === true, + ) as never; } -export function groupValues( - array: Value[], - key: Key | KeyCallback, +export function groupValues( + array: unknown[], + key: unknown, + value: unknown, arrays: boolean, - indicable: boolean, ): Record { - const callbacks = getCallbacks(undefined, key); - const hasCallback = typeof callbacks?.key === 'function'; - - if (!hasCallback && !indicable) { - return {}; - } - + const callbacks = getCallbacks(undefined, key, value); const record: Record = {}; const {length} = array; for (let index = 0; index < length; index += 1) { - const value = array[index]; + const item = array[index]; - const key = hasCallback - ? (callbacks?.key?.(value, index, array) ?? index) - : index; + const key = callbacks?.key?.(item, index, array) ?? index; + const value = callbacks?.value?.(item, index, array) ?? item; if (arrays) { const existing = record[key]; - if (Array.isArray(existing)) { - existing.push(value); - } else { + if (existing == null) { record[key] = [value]; + } else { + (existing as unknown[]).push(value); } } else { record[key] = value; diff --git a/src/js/array/index-of.ts b/src/js/array/index-of.ts index abc8337..5a288e9 100644 --- a/src/js/array/index-of.ts +++ b/src/js/array/index-of.ts @@ -1,29 +1,51 @@ -import type {BooleanCallback, KeyCallback} from '@/array/models'; -import {findValue} from '@/internal/array-find'; -import type {Key} from '@/models'; +import type {BooleanCallback, KeyCallback} from '~/array/models'; +import {findValue} from '~/internal/array/find'; /** - * Returns the index for the first item matching `value`, or `-1` if no match is found + * Get the index for the first item matching `value` _(or `-1` if no match is found)_ */ -export function indexOf( - array: Model[], - value: Value | BooleanCallback, +export function indexOf(array: Item[], value: Item): number; + +/** + * Get the index for the first item matching `value` _(or `-1` if no match is found)_ + */ +export function indexOf( + array: Item[], + matches: BooleanCallback, ): number; /** - * - Returns the index for the first matching item, or `-1` if no match is found + * - Get the index for the first matching item _(or `-1` if no match is found)_ * - Use `key` to find a comparison value to match with `value` */ -export function indexOf( - array: Model[], - value: Value, - key: Key | KeyCallback, +export function indexOf( + array: Item[], + key: Key, + value: Item[Key], ): number; -export function indexOf( - array: Model[], - value: Value | BooleanCallback, - key?: Key | KeyCallback, -): number { - return findValue('index', array, value, key) as number; +/** + * - Get the index for the first matching item _(or `-1` if no match is found)_ + * - Use `key` to find a comparison value to match with `value` + */ +export function indexOf>( + array: Item[], + key: Key, + value: ReturnType, +): number; + +export function indexOf(array: unknown[], ...parameters: unknown[]): number { + const {length} = parameters; + + return findValue( + 'index', + array, + length === 1 && typeof parameters[0] === 'function' + ? parameters[0] + : undefined, + length === 2 ? parameters[0] : undefined, + length === 1 && typeof parameters[0] !== 'function' + ? parameters[0] + : parameters[1], + ) as number; } diff --git a/src/js/array/index.ts b/src/js/array/index.ts index 5513dce..75dc238 100644 --- a/src/js/array/index.ts +++ b/src/js/array/index.ts @@ -1,19 +1,20 @@ -import {insertValues} from '@/array/insert'; -import type {NestedArrayType} from '@/models'; +import {insertValues} from '~/array/insert'; +import type {NestedArrayType} from '~/models'; /** - * Flattens an array _(using native `flat` and maximum depth)_ + * Flatten an array _(using native `flat` and maximum depth)_ */ -export function flatten(array: Value[]): NestedArrayType[] { - return array.flat(Number.POSITIVE_INFINITY) as NestedArrayType[]; +export function flatten(array: Item[]): NestedArrayType[] { + return array.flat(Number.POSITIVE_INFINITY) as NestedArrayType[]; } /** - * - Pushes values to the end of an array, returning the new length - * - Uses chunking to avoid stack overflow + * - Push values to the end of an array + * - Returns the new length + * - _(Uses chunking to avoid stack overflow)_ */ -export function push(array: Value[], values: Value[]): number { - return insertValues('push', array, values, array.length, 0) as number; +export function push(array: Item[], pushed: Item[]): number { + return insertValues('push', array, pushed, array.length, 0) as number; } export * from './chunk'; diff --git a/src/js/array/insert.ts b/src/js/array/insert.ts index b6bc819..152e624 100644 --- a/src/js/array/insert.ts +++ b/src/js/array/insert.ts @@ -1,30 +1,44 @@ -import {chunk} from '@/array/chunk'; -import type {InsertType} from '@/array/models'; +import {chunk} from '~/array/chunk'; +import type {InsertType} from '~/array/models'; /** - * - Inserts values into an array at a specified index - * - Uses chunking to avoid stack overflow + * - Insert values into an array _(at the end)_ + * - _(Uses chunking to avoid stack overflow_) */ -export function insert( - array: Value[], - index: number, - values: Value[], +export function insert(array: Item[], items: Item[]): void; + +/** + * - Insert values into an array at a specified index + * - _(Uses chunking to avoid stack overflow_) + */ +export function insert(array: Item[], index: number, items: Item[]): void; + +export function insert( + array: unknown[], + first: unknown, + second?: unknown[], ): void { - insertValues('splice', array, values, index, 0); + insertValues( + 'splice', + array, + Array.isArray(first) ? first : (second ?? []), + typeof first === 'number' ? first : array.length, + 0, + ); } -export function insertValues( +export function insertValues( type: InsertType, - array: Value[], - values: Value[], + array: Item[], + items: Item[], start: number, deleteCount: number, ): unknown { - const chunked = chunk(values); + const chunked = chunk(items); const lastIndex = chunked.length - 1; let index = Number(chunked.length); - let returned: Value[] | undefined; + let returned: Item[] | undefined; while (--index >= 0) { const result = array.splice( diff --git a/src/js/array/models.ts b/src/js/array/models.ts index cb9a05a..f9edae9 100644 --- a/src/js/array/models.ts +++ b/src/js/array/models.ts @@ -1,32 +1,35 @@ -import type {Key} from '@/models'; +import type {GenericCallback, Key} from '~/models'; -export type ArrayCallback = ( - value: Value, +export type ArrayCallback = ( + item: Item, index: number, - array: Value[], -) => Returned; + array: Item[], +) => Value; -export type BooleanCallback = ArrayCallback; +export type BooleanCallback = ArrayCallback; -export type Callbacks = { - bool?: BooleanCallback; - key?: KeyCallback; +export type Callbacks = { + bool?: GenericCallback; + key?: GenericCallback; + value?: GenericCallback; }; export type FindType = 'index' | 'value'; export type InsertType = 'push' | 'splice'; -export type KeyCallback = ArrayCallback; +export type KeyCallback = ArrayCallback; -export type SortKey = { +export type SortKey = { direction: 'asc' | 'desc'; - value: Key | SortKeyCallback; + value: Key | SortKeyCallback; }; -export type SortKeyCallback = (value: Value) => Key; +export type SortKeyCallback = (item: Item) => unknown; -export type SortKeyWithCallback = { - callback: SortKeyCallback; +export type SortKeyWithCallback = { + callback: SortKeyCallback; direction: 'asc' | 'desc'; }; + +export type ValueCallback = ArrayCallback; diff --git a/src/js/array/shuffle.ts b/src/js/array/shuffle.ts index 60a0a47..ca8b075 100644 --- a/src/js/array/shuffle.ts +++ b/src/js/array/shuffle.ts @@ -1,9 +1,9 @@ -import {getRandomInteger} from '@/random'; +import {getRandomInteger} from '~/random'; /** - * Shuffles an array + * Shuffle an array */ -export function shuffle(array: Value[]): Value[] { +export function shuffle(array: Item[]): Item[] { const shuffled = array.slice(); const {length} = shuffled; diff --git a/src/js/array/sort.ts b/src/js/array/sort.ts index 83dde2a..dbb6e14 100644 --- a/src/js/array/sort.ts +++ b/src/js/array/sort.ts @@ -2,46 +2,41 @@ import type { SortKey, SortKeyCallback, SortKeyWithCallback, -} from '@/array/models'; -import {isKey} from '@/is'; -import type {Key, PlainObject} from '@/models'; -import {compare} from '@/value/compare'; +} from '~/array/models'; +import {isKey} from '~/is'; +import type {Key, PlainObject} from '~/models'; +import {compare} from '~/value/compare'; /** - * Sorts an array of items _(ascending by default)_ + * Sort an array of items _(defaults to ascending)_ */ -export function sort(array: Value[], descending?: boolean): Value[]; +export function sort(array: Item[], descending?: boolean): Item[]; /** - * - Sorts an array of items, using a `key` to sort by a specific value - * - Ascending by default, but can be changed by setting `descending` to `true`, or using a `SortKey` + * - Sort an array of items, using a `key` to sort by a specific value + * - Defaults to ascending, but can be changed by setting `descending` to `true`, or using a `SortKey` */ -export function sort( - array: Value[], - key: Key | SortKey | SortKeyCallback, +export function sort( + array: Item[], + key: Key | SortKey | SortKeyCallback, descending?: boolean, -): Value[]; +): Item[]; /** - * - Sorts an array of items, using multiple `keys` to sort by specific values - * - Ascending by default, but can be changed by setting `descending` to `true`, or using `SortKey` + * - Sort an array of items, using multiple `keys` to sort by specific values + * - Defaults to ascending, but can be changed by setting `descending` to `true`, or using `SortKey` */ -export function sort( - array: Value[], - keys: Array | SortKeyCallback>, +export function sort( + array: Item[], + keys: Array | SortKeyCallback>, descending?: boolean, -): Value[]; - -export function sort( - array: Value[], - first?: - | boolean - | Key - | SortKey - | SortKeyCallback - | Array | SortKeyCallback>, - second?: boolean, -): Value[] { +): Item[]; + +export function sort( + array: unknown[], + first?: unknown, + second?: unknown, +): unknown[] { if (array.length < 2) { return array; } @@ -56,23 +51,22 @@ export function sort( const keys = (Array.isArray(first) ? first : [first]) .map(key => { - const returned: SortKeyWithCallback = { + const returned: SortKeyWithCallback = { direction, callback: undefined as never, }; if (isKey(key)) { - returned.callback = (value: Value) => - (value as PlainObject)[key] as never; + returned.callback = value => (value as PlainObject)[key] as never; } else if (typeof key === 'function') { returned.callback = key; } else if (typeof key?.value === 'function' || isKey(key?.value)) { returned.direction = key?.direction ?? direction; + returned.callback = typeof key.value === 'function' ? key.value - : (value: Value) => - (value as PlainObject)[key.value as Key] as never; + : value => (value as PlainObject)[key.value as Key] as never; } return returned; diff --git a/src/js/array/splice.ts b/src/js/array/splice.ts index aae51f7..a446755 100644 --- a/src/js/array/splice.ts +++ b/src/js/array/splice.ts @@ -1,57 +1,53 @@ -import {insertValues} from '@/array/insert'; +import {insertValues} from '~/array/insert'; /** * Removes and returns all items from an array starting from a specific index */ -export function splice(array: Value[], start: number): Value[]; +export function splice(array: Item[], start: number): Item[]; /** * Removes and returns _(up to)_ a specific amount of items from an array, starting from a specific index */ -export function splice( - array: Value[], +export function splice( + array: Item[], start: number, amount: number, -): Value[]; +): Item[]; /** * - Splices values into an array and returns any removed values * - Uses chunking to avoid stack overflow */ -export function splice( - array: Value[], +export function splice( + array: Item[], start: number, - values: Value[], -): Value[]; + added: Item[], +): Item[]; /** * - Splices values into an array and returns any removed values * - Uses chunking to avoid stack overflow */ -export function splice( - array: Value[], +export function splice( + array: Item[], start: number, amount: number, - values: Value[], -): Value[]; + added: Item[], +): Item[]; -export function splice( - array: Value[], +export function splice( + array: unknown[], start: number, - amountOrValues?: number | Value[], - values?: Value[], -): Value[] { - const amoutOrValuesIsArray = Array.isArray(amountOrValues); + first?: number | unknown[], + second?: unknown[], +): unknown[] { + const isArray = Array.isArray(first); return insertValues( 'splice', array, - amoutOrValuesIsArray ? amountOrValues : values ?? [], + isArray ? first : (second ?? []), start, - amoutOrValuesIsArray - ? array.length - : typeof amountOrValues === 'number' && amountOrValues > 0 - ? amountOrValues - : 0, - ) as Value[]; + isArray ? array.length : typeof first === 'number' && first > 0 ? first : 0, + ) as unknown[]; } diff --git a/src/js/array/to-map.ts b/src/js/array/to-map.ts index 670b70b..9a5e303 100644 --- a/src/js/array/to-map.ts +++ b/src/js/array/to-map.ts @@ -1,82 +1,170 @@ -import type {KeyCallback} from '@/array/models'; -import {getCallbacks} from '@/internal/array-callbacks'; -import type {Key} from '@/models'; +import type {KeyCallback, ValueCallback} from '~/array/models'; +import {getCallbacks} from '~/internal/array/callbacks'; +import type {Key, KeyedValue} from '~/models'; /** - * Converts an array into a map, using indices as keys + * Create a map from an array of items _(using their indices as keys)_ */ -export function toMap(array: Value[]): Map; +export function toMap(array: Item[]): Map; /** - * Converts an array into a map, using indices as keys and grouping values into arrays + * Create a map from an array of items using a specific key */ -export function toMap( - array: Value[], +export function toMap( + array: Item[], + key: Key, +): Map, Item>; + +/** + * Create a map from an array of items using a specific key, and grouping them into arrays + */ +export function toMap( + array: Item[], + key: Key, arrays: true, -): Map; +): Map, Item[]>; /** - * - Converts an array into a map - * - Uses `key` to find an identifcation value to use as keys + * Create a map from an array of items using a specific key */ -export function toMap(array: Value[], key: Key): Map; +export function toMap>( + array: Item[], + key: Key, +): Map, Item>; /** - * - Converts an array into a map - * - Uses `key` to find an identifcation value to use as keys - * - Groups values into arrays + * Create a map from an array of items using a specific key, and grouping them into arrays */ -export function toMap( - array: Value[], +export function toMap>( + array: Item[], key: Key, arrays: true, -): Map; +): Map, Item[]>; /** - * - Converts an array into a map - * - Uses `key` to find an identifcation value to use as keys + * Create a map from an array of items using a specific key and value */ -export function toMap( - array: Value[], - key: KeyCallback, -): Map; +export function toMap( + array: Item[], + key: Key, + value: Value, +): Map, KeyedValue>; /** - * - Converts an array into a map - * - Uses `key` to find an identifcation value to use as keys - * - Groups values into arrays + * Create a map from an array of items using a specific key and value, and grouping them into arrays */ -export function toMap( - array: Value[], - key: KeyCallback, +export function toMap( + array: Item[], + key: Key, + value: Value, arrays: true, -): Map; +): Map, Array>>; -export function toMap( - array: Value[], - first?: boolean | Key | KeyCallback, - second?: boolean, +/** + * Create a map from an array of items using a specific key and value + */ +export function toMap< + Item, + Key extends keyof Item, + Value extends ValueCallback, +>( + array: Item[], + key: Key, + value: Value, +): Map, ReturnType>; + +/** + * Create a map from an array of items using a specific key and value, and grouping them into arrays + */ +export function toMap< + Item, + Key extends keyof Item, + Value extends ValueCallback, +>( + array: Item[], + key: Key, + value: Value, + arrays: true, +): Map, Array>>; + +/** + * Create a map from an array of items using a specific key and value + */ +export function toMap< + Item, + Key extends KeyCallback, + Value extends keyof Item, +>( + array: Item[], + key: Key, + value: Value, +): Map, KeyedValue>; + +/** + * Create a map from an array of items using a specific key and value, and grouping them into arrays + */ +export function toMap< + Item, + Key extends KeyCallback, + Value extends keyof Item, +>( + array: Item[], + key: Key, + value: Value, + arrays: true, +): Map, Array>>; + +/** + * Create a map from an array of items using a specific key and value + */ +export function toMap< + Item, + Key extends KeyCallback, + Value extends ValueCallback, +>( + array: Item[], + key: Key, + value: Value, +): Map, ReturnType>; + +/** + * Create a map from an array of items using a specific key and value, and grouping them into arrays + */ +export function toMap< + Item, + Key extends KeyCallback, + Value extends ValueCallback, +>( + array: Item[], + key: Key, + value: Value, + arrays: true, +): Map, Array>>; + +export function toMap( + array: unknown[], + first?: unknown, + second?: unknown, + third?: unknown, ): Map { - const asArrays = first === true || second === true; - const callbacks = getCallbacks(undefined, first); - const hasCallback = typeof callbacks?.key === 'function'; + const asArrays = first === true || second === true || third === true; + const callbacks = getCallbacks(undefined, first, second); const map = new Map(); const {length} = array; for (let index = 0; index < length; index += 1) { - const value = array[index]; + const item = array[index]; - const key = hasCallback - ? (callbacks?.key?.(value, index, array) ?? index) - : index; + const key = callbacks?.key?.(item, index, array) ?? index; + const value = callbacks?.value?.(item, index, array) ?? item; if (asArrays) { const existing = map.get(key); - if (Array.isArray(existing)) { - existing.push(value); - } else { + if (existing == null) { map.set(key, [value]); + } else { + (existing as unknown[]).push(value); } } else { map.set(key, value); diff --git a/src/js/array/to-record.ts b/src/js/array/to-record.ts index c209a4e..8a3a825 100644 --- a/src/js/array/to-record.ts +++ b/src/js/array/to-record.ts @@ -1,66 +1,164 @@ -import {groupValues} from '@/array/group-by'; -import type {KeyCallback} from '@/array/models'; -import type {Key} from '@/models'; +import {groupValues} from '~/array/group-by'; +import type {KeyCallback, ValueCallback} from '~/array/models'; +import type {KeyedValue, PlainObject} from '~/models'; /** - * Converts an array into a record, using indices as keys + * Create a record from an array of items _(using their indices as keys)_ */ -export function toRecord(array: Value[]): Record; +export function toRecord(array: Item[]): Record; /** - * Converts an array into a record, using indices as keys and grouping values into arrays + * Create a record from an array of items using a specific key */ -export function toRecord( - array: Value[], +export function toRecord( + array: Item[], + key: Key, +): Record, Item>; + +/** + * Create a record from an array of items using a specific key, and grouping them into arrays + */ +export function toRecord( + array: Item[], + key: Key, + arrays: true, +): Record, Item[]>; + +/** + * Create a record from an array of items using a specific key + */ +export function toRecord>( + array: Item[], + key: Key, +): Record, Item>; + +/** + * Create a record from an array of items using a specific key, and grouping them into arrays + */ +export function toRecord>( + array: Item[], + key: Key, arrays: true, -): Record; +): Record, Item[]>; /** - * - Converts an array into a record - * - Uses `key` to find an identifcation value to use as keys + * Create a record from an array of items using a specific key and value */ -export function toRecord(array: Value[], key: Key): Record; +export function toRecord< + Item, + Key extends keyof Item, + Value extends keyof Item, +>( + array: Item[], + key: Key, + value: Value, +): Record, KeyedValue>; /** - * - Converts an array into a record - * - Uses `key` to find an identifcation value to use as keys - * - Groups values into arrays + * Create a record from an array of items using a specific key and value, and grouping them into arrays */ -export function toRecord( - array: Value[], +export function toRecord< + Item, + Key extends keyof Item, + Value extends keyof Item, +>( + array: Item[], key: Key, + value: Value, arrays: true, -): Record; +): Record, Array>>; /** - * - Converts an array into a record - * - Uses `key` to find an identifcation value to use as keys + * Create a record from an array of items using a specific key and value */ -export function toRecord( - array: Value[], - key: KeyCallback, -): Record; +export function toRecord< + Item, + Key extends keyof Item, + Value extends ValueCallback, +>( + array: Item[], + key: Key, + value: Value, +): Record, ReturnType>; /** - * - Converts an array into a record - * - Uses `key` to find an identifcation value to use as keys - * - Groups values into arrays + * Create a record from an array of items using a specific key and value, and grouping them into arrays */ -export function toRecord( - array: Value[], - key: KeyCallback, +export function toRecord< + Item, + Key extends keyof Item, + Value extends ValueCallback, +>( + array: Item[], + key: Key, + value: Value, + arrays: true, +): Record, Array>>; + +/** + * Create a record from an array of items using a specific key and value + */ +export function toRecord< + Item, + Key extends KeyCallback, + Value extends keyof Item, +>( + array: Item[], + key: Key, + value: Value, +): Record, KeyedValue>; + +/** + * Create a record from an array of items using a specific key and value, and grouping them into arrays + */ +export function toRecord< + Item, + Key extends KeyCallback, + Value extends keyof Item, +>( + array: Item[], + key: Key, + value: Value, + arrays: true, +): Record, Array>>; + +/** + * Create a record from an array of items using a specific key and value + */ +export function toRecord< + Item, + Key extends KeyCallback, + Value extends ValueCallback, +>( + array: Item[], + key: Key, + value: Value, +): Record, ReturnType>; + +/** + * Create a record from an array of items using a specific key and value, and grouping them into arrays + */ +export function toRecord< + Item, + Key extends KeyCallback, + Value extends ValueCallback, +>( + array: Item[], + key: Key, + value: Value, arrays: true, -): Record; +): Record, Array>>; -export function toRecord( - array: Value[], - first?: boolean | Key | KeyCallback, - second?: boolean, -): Record { +export function toRecord( + array: unknown[], + first?: unknown, + second?: unknown, + third?: unknown, +): PlainObject { return groupValues( array, - first as Key | KeyCallback, - first === true || second === true, - true, + first as never, + second, + first === true || second === true || third === true, ); } diff --git a/src/js/array/unique.ts b/src/js/array/unique.ts index 7408eae..16d44d1 100644 --- a/src/js/array/unique.ts +++ b/src/js/array/unique.ts @@ -1,24 +1,29 @@ -import type {KeyCallback} from '@/array/models'; -import {findValues} from '@/internal/array-find'; -import type {Key} from '@/models'; +import type {KeyCallback} from '~/array/models'; +import {findValues} from '~/internal/array/find'; /** - * Returns an array of unique items + * Get an array of unique items */ -export function unique(array: Value[]): Value[]; +export function unique(array: Item[]): Item[]; /** - * - Returns an array of unique items + * - Get an array of unique items * - Use `key` to find a comparison value to match with `value` */ -export function unique( - array: Value[], - key: Key | KeyCallback, -): Value[]; +export function unique( + array: Item[], + key: Key, +): Item[]; -export function unique( - array: Value[], - key?: Key | KeyCallback, -): Value[] { - return findValues('unique', array, undefined, key); +/** + * - Get an array of unique items + * - Use `key` to find a comparison value to match with `value` + */ +export function unique>( + array: Item[], + key: Key, +): Item[]; + +export function unique(array: unknown[], key?: unknown): unknown[] { + return findValues('unique', array, undefined, key, undefined); } diff --git a/src/js/colour/base.ts b/src/js/colour/base.ts index 9d831f6..cf008bc 100644 --- a/src/js/colour/base.ts +++ b/src/js/colour/base.ts @@ -1,12 +1,12 @@ -import type {HexColour} from '@/colour/hex'; -import {isColourValue} from '@/colour/is'; +import type {HexColour} from '~/colour/hex'; +import {isColourValue} from '~/colour/is'; export abstract class Colour { private declare readonly $colour: string; protected declare readonly state: ColourState; /** - * Gets the current value of the colour + * Get the current value of the colour */ get value(): Model { return {...this.state.value}; diff --git a/src/js/colour/functions.ts b/src/js/colour/functions.ts index 35d7ffb..a53a3c8 100644 --- a/src/js/colour/functions.ts +++ b/src/js/colour/functions.ts @@ -1,7 +1,7 @@ -import {HexColour} from '@/colour/hex'; -import {HSLColour, type HSLColourValue} from '@/colour/hsl'; -import {RGBColour, type RGBColourValue} from '@/colour/rgb'; -import {clamp} from '@/number'; +import {HexColour} from '~/colour/hex'; +import {HSLColour, type HSLColourValue} from '~/colour/hsl'; +import {RGBColour, type RGBColourValue} from '~/colour/rgb'; +import {clamp} from '~/number'; export const anyPattern = /^#*([a-f0-9]{3}){1,2}$/i; const groupedPattern = /^#*([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i; @@ -115,8 +115,6 @@ export function rgbToHsl(rgb: RGBColourValue): HSLColour { case red: hue = (green - blue) / delta + (green < blue ? 6 : 0); break; - default: - break; } hue *= 60; diff --git a/src/js/colour/hex.ts b/src/js/colour/hex.ts index f6635c0..75a070a 100644 --- a/src/js/colour/hex.ts +++ b/src/js/colour/hex.ts @@ -1,6 +1,6 @@ -import {anyPattern, getNormalisedHex, hexToRgb} from '@/colour/functions'; -import type {HSLColour} from '@/colour/hsl'; -import type {RGBColour} from '@/colour/rgb'; +import {anyPattern, getNormalisedHex, hexToRgb} from '~/colour/functions'; +import type {HSLColour} from '~/colour/hsl'; +import type {RGBColour} from '~/colour/rgb'; type State = { value: string; @@ -11,14 +11,14 @@ export class HexColour { private readonly state: State; /** - * Gets the value of the colour + * Get the value of the colour */ get value(): string { return `#${this.state.value}`; } /** - * Sets the value of the colour + * Set the value of the colour */ set value(value: string) { this.state.value = anyPattern.test(value) diff --git a/src/js/colour/hsl.ts b/src/js/colour/hsl.ts index da08c4e..6122076 100644 --- a/src/js/colour/hsl.ts +++ b/src/js/colour/hsl.ts @@ -1,8 +1,8 @@ -import {Colour} from '@/colour/base'; -import {hslToRgb} from '@/colour/functions'; -import type {HexColour} from '@/colour/hex'; -import type {RGBColour} from '@/colour/rgb'; -import {clamp} from '@/number'; +import {Colour} from '~/colour/base'; +import {hslToRgb} from '~/colour/functions'; +import type {HexColour} from '~/colour/hex'; +import type {RGBColour} from '~/colour/rgb'; +import {clamp} from '~/number'; export type HSLColourValue = { hue: number; @@ -12,42 +12,42 @@ export type HSLColourValue = { export class HSLColour extends Colour { /** - * Gets the current hue + * Get the current hue */ get hue(): number { return +this.state.value.hue; } /** - * Sets the current hue + * Set the current hue */ set hue(value: number) { this.state.value.hue = clamp(value, 0, 360); } /** - * Gets the current lightness + * Get the current lightness */ get lightness(): number { return +this.state.value.lightness; } /** - * Sets the current lightness + * Set the current lightness */ set lightness(value: number) { this.state.value.lightness = clamp(value, 0, 100); } /** - * Gets the current saturation + * Get the current saturation */ get saturation(): number { return +this.state.value.saturation; } /** - * Sets the current saturation + * Set the current saturation */ set saturation(value: number) { this.state.value.saturation = clamp(value, 0, 100); @@ -57,17 +57,23 @@ export class HSLColour extends Colour { super('hsl', value, defaults, properties); } + /** + * @inheritdoc + */ toHex(): HexColour { return HSLColour.toRgb(this.state.value).toHex(); } /** - * Converts the colour to an RGB-colour + * Convert the colour to an RGB-colour */ toRgb(): RGBColour { return HSLColour.toRgb(this.state.value); } + /** + * @inheritdoc + */ toString(): string { return `hsl(${this.state.value.hue}, ${this.state.value.saturation}%, ${this.state.value.lightness}%)`; } diff --git a/src/js/colour/index.ts b/src/js/colour/index.ts index 26822de..2358e28 100644 --- a/src/js/colour/index.ts +++ b/src/js/colour/index.ts @@ -1,4 +1,4 @@ -import type {RGBColourValue} from '@/colour/rgb'; +import type {RGBColourValue} from '~/colour/rgb'; /** * Get a foreground colour _(usually text)_ based on a background colour's luminance diff --git a/src/js/colour/is.ts b/src/js/colour/is.ts index cd171c1..63cd7fa 100644 --- a/src/js/colour/is.ts +++ b/src/js/colour/is.ts @@ -1,6 +1,6 @@ -import type {HexColour} from '@/colour/hex'; -import type {HSLColour} from '@/colour/hsl'; -import type {RGBColour} from '@/colour/rgb'; +import type {HexColour} from '~/colour/hex'; +import type {HSLColour} from '~/colour/hsl'; +import type {RGBColour} from '~/colour/rgb'; /** * Is the value a colour? diff --git a/src/js/colour/rgb.ts b/src/js/colour/rgb.ts index 35e22a8..9a42557 100644 --- a/src/js/colour/rgb.ts +++ b/src/js/colour/rgb.ts @@ -1,8 +1,8 @@ -import {Colour} from '@/colour/base'; -import {rgbToHex, rgbToHsl} from '@/colour/functions'; -import type {HexColour} from '@/colour/hex'; -import type {HSLColour} from '@/colour/hsl'; -import {clamp} from '@/number'; +import {Colour} from '~/colour/base'; +import {rgbToHex, rgbToHsl} from '~/colour/functions'; +import type {HexColour} from '~/colour/hex'; +import type {HSLColour} from '~/colour/hsl'; +import {clamp} from '~/number'; export type RGBColourValue = { blue: number; @@ -12,42 +12,42 @@ export type RGBColourValue = { export class RGBColour extends Colour { /** - * Gets the current blue value + * Get the current blue value */ get blue(): number { return +this.state.value.blue; } /** - * Sets the current blue value + * Set the current blue value */ set blue(value: number) { this.state.value.blue = clamp(value, 0, 255); } /** - * Gets the current green value + * Get the current green value */ get green(): number { return +this.state.value.green; } /** - * Sets the current green value + * Set the current green value */ set green(value: number) { this.state.value.green = clamp(value, 0, 255); } /** - * Gets the current red value + * Get the current red value */ get red(): number { return +this.state.value.red; } /** - * Sets the current red value + * Set the current red value */ set red(value: number) { this.state.value.red = clamp(value, 0, 255); @@ -57,6 +57,9 @@ export class RGBColour extends Colour { super('rgb', value, defaults, properties); } + /** + * @inheritdoc + */ toHex(): HexColour { return RGBColour.toHex(this.value); } @@ -68,6 +71,9 @@ export class RGBColour extends Colour { return RGBColour.toHsl(this.value); } + /** + * @inheritdoc + */ toString(): string { return `rgb(${this.value.red}, ${this.value.green}, ${this.value.blue})`; } diff --git a/src/js/emitter.ts b/src/js/emitter.ts index 4158e47..01f3c88 100644 --- a/src/js/emitter.ts +++ b/src/js/emitter.ts @@ -1,4 +1,4 @@ -import {noop} from '@/function'; +import {noop} from '~/function'; class Emitter { private declare readonly state: EmitterState; @@ -36,14 +36,14 @@ class Emitter { } /** - * Destroys the emitter + * Destroy the emitter */ destroy(): void { finishEmitter(this.state, false); } /** - * Emits a new value _(and optionally finishes the emitter)_ + * Emit a new value _(and optionally finishes the emitter)_ */ emit(value: Value, finish?: boolean): void { if (this.state.active) { @@ -60,7 +60,7 @@ class Emitter { } /** - * Emits an error _(and optionally finishes the emitter)_ + * Emit an error _(and optionally finishes the emitter)_ */ error(error: Error, finish?: boolean): void { if (this.state.active) { @@ -75,7 +75,7 @@ class Emitter { } /** - * Finishes the emitter + * Finish the emitter */ finish(): void { finishEmitter(this.state, true); @@ -103,12 +103,12 @@ class Observable { } /** - * Subscribes to value changes + * Subscribe to value changes */ subscribe(observer: Observer): Subscription; /** - * Subscribes to value changes + * Subscribe to value changes */ subscribe( onNext: (value: Value) => void, @@ -169,6 +169,9 @@ class Subscription { return this.state.closed || !(this.state.emitter?.active ?? false); } + /** + * Destroy the subscription + */ destroy(): void { this.unsubscribe(); @@ -177,7 +180,7 @@ class Subscription { } /** - * Unsubscribes from the observable + * Unsubscribe from its observable */ unsubscribe(): void { if (!this.state.closed) { @@ -197,7 +200,7 @@ type SubscriptionState = { const properties: Array> = ['complete', 'error', 'next']; /** - * Creates a new emitter + * Create a new emitter */ export function emitter(value: Value): Emitter { return new Emitter(value); diff --git a/src/js/function.ts b/src/js/function.ts index 01a74c7..b209090 100644 --- a/src/js/function.ts +++ b/src/js/function.ts @@ -1,9 +1,9 @@ -import type {GenericCallback} from '@/models'; -import {clamp} from '@/number'; +import type {GenericCallback} from '~/models'; +import {clamp} from '~/number'; type Debounced = Callback & { /** - * Cancels the debounce + * Cancel the debounce */ cancel: () => void; }; @@ -34,21 +34,21 @@ class Memoised { } /** - * Clears the cache + * Clear the cache */ clear(): void { this.state.cache?.clear(); } /** - * Deletes a result from the cache + * Delete a result from the cache */ delete(key: Parameters[0]): boolean { return this.state.cache?.delete(key); } /** - * Destroys the instance, clearing its cache and removing its callback + * Destroy the instance, clearing its cache and removing its callback */ destroy(): void { this.state.cache.clear(); @@ -58,21 +58,21 @@ class Memoised { } /** - * Retrieves the result from the cache if it exists, or `undefined` otherwise + * Get a result from the cache if it exists _(or `undefined` otherwise)_ */ get(key: Parameters[0]): ReturnType | undefined { return this.state.cache?.get(key); } /** - * Checks if the cache has a result for a given key + * Does the result exist? */ has(key: Parameters[0]): boolean { return this.state.cache?.has(key) ?? false; } /** - * Retrieves the result from the cache if it exists, otherwise runs the callback, caches the result, and returns it + * Get the result from the cache if it exists; otherwise runs the callback, caches the result, and returns it */ run(...parameters: Parameters): ReturnType { return this.state.getter(...parameters); @@ -85,7 +85,7 @@ type MemoisedState = { }; /** - * - Debounces a function, ensuring it is only called after `time` milliseconds have passed + * - Debounce a function, ensuring it is only called after `time` milliseconds have passed * - On subsequent calls, the timer is reset and will wait another `time` milliseconds _(and so on...)_ * - Time is clamped between _0_ and _1000_ milliseconds * - Returns the callback with an added `cancel`-method for manually cancelling the debounce @@ -114,7 +114,7 @@ export function debounce( } /** - * Memoises a function, caching and retrieving results based on the first parameter + * Memoise a function, caching and retrieving results based on the first parameter */ export function memoise( callback: Callback, @@ -128,7 +128,7 @@ export function memoise( export function noop(): void {} /** - * - Throttles a function, ensuring it is only called once every `time` milliseconds + * - Throttle a function, ensuring it is only called once every `time` milliseconds * - Time is clamped between _0_ and _1000_ milliseconds */ export function throttle( diff --git a/src/js/index.ts b/src/js/index.ts index 0af58d3..6e7c367 100644 --- a/src/js/index.ts +++ b/src/js/index.ts @@ -1,16 +1,16 @@ -export * from '@/array/index'; -export * from '@/colour/index'; -export * from '@/emitter'; -export * from '@/function'; -export * from '@/is'; -export * from '@/logger'; -export * from '@/math'; -export * from '@/models'; -export * from '@/number'; -export * from '@/query'; -export * from '@/queue'; -export * from '@/random'; -export * from '@/sized'; -export * from '@/string/index'; -export * from '@/touch'; -export * from '@/value/index'; +export * from '~/array/index'; +export * from '~/colour/index'; +export * from '~/emitter'; +export * from '~/function'; +export * from '~/is'; +export * from '~/logger'; +export * from '~/math'; +export * from '~/models'; +export * from '~/number'; +export * from '~/query'; +export * from '~/queue'; +export * from '~/random'; +export * from '~/sized'; +export * from '~/string/index'; +export * from '~/touch'; +export * from '~/value/index'; diff --git a/src/js/internal/array-callbacks.ts b/src/js/internal/array-callbacks.ts deleted file mode 100644 index 81d7fbd..0000000 --- a/src/js/internal/array-callbacks.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type {BooleanCallback, Callbacks, KeyCallback} from '@/array/models'; -import type {Key, PlainObject} from '@/models'; - -export function getCallbacks( - bool: unknown, - key: unknown, -): Callbacks | undefined { - if (typeof bool === 'function') { - return {bool: bool as BooleanCallback}; - } - - if (typeof key === 'function') { - return {key: key as KeyCallback}; - } - - const isString = typeof key === 'string'; - - if ( - (!isString && typeof key !== 'number') || - (isString && key.includes('.')) - ) { - return; - } - - return { - key: (value: Value) => (value as PlainObject)?.[key as string] as Key, - }; -} diff --git a/src/js/internal/array/callbacks.ts b/src/js/internal/array/callbacks.ts new file mode 100644 index 0000000..1a0fcc6 --- /dev/null +++ b/src/js/internal/array/callbacks.ts @@ -0,0 +1,33 @@ +import type {BooleanCallback, Callbacks, KeyCallback} from '~/array/models'; +import type {GenericCallback, PlainObject} from '~/models'; + +function getCallback(value: unknown): GenericCallback | undefined { + switch (typeof value) { + case 'function': + return value as GenericCallback; + + case 'number': + case 'string': + return typeof value === 'string' && value.includes('.') + ? undefined + : (obj: PlainObject) => obj[value]; + + default: + return; + } +} + +export function getCallbacks( + bool?: unknown, + key?: unknown, + value?: unknown, +): Callbacks | undefined { + if (typeof bool === 'function') { + return {bool: bool as BooleanCallback}; + } + + return { + key: getCallback(key) as KeyCallback, + value: getCallback(value) as GenericCallback, + }; +} diff --git a/src/js/internal/array-find.ts b/src/js/internal/array/find.ts similarity index 51% rename from src/js/internal/array-find.ts rename to src/js/internal/array/find.ts index e4464aa..fe0b6a3 100644 --- a/src/js/internal/array-find.ts +++ b/src/js/internal/array/find.ts @@ -1,18 +1,18 @@ -import type {BooleanCallback, FindType, KeyCallback} from '@/array/models'; -import {getCallbacks} from '@/internal/array-callbacks'; -import type {Key} from '@/models'; +import type {FindType} from '~/array/models'; +import {getCallbacks} from '~/internal/array/callbacks'; -export function findValue( +export function findValue( type: FindType, - array: Model[], - value: Value | BooleanCallback, - key?: Key | KeyCallback, + array: unknown[], + bool: unknown, + key: unknown, + value: unknown, ): unknown { - const callbacks = getCallbacks(value, key); + const callbacks = getCallbacks(bool, key); if (callbacks?.bool == null && callbacks?.key == null) { return type === 'index' - ? array.indexOf(value as Model) + ? array.findIndex(item => item === value) : array.find(item => item === value); } @@ -35,21 +35,21 @@ export function findValue( return type === 'index' ? -1 : undefined; } -export function findValues( +export function findValues( type: 'all' | 'unique', - array: Model[], - value: Value | BooleanCallback, - key?: Key | KeyCallback, -): Model[] { - const callbacks = getCallbacks(value, key); - + array: unknown[], + bool: unknown, + key: unknown, + value: unknown, +): unknown[] { + const callbacks = getCallbacks(bool, key); const {length} = array; if (type === 'unique' && callbacks?.key == null && length >= 100) { return Array.from(new Set(array)); } - if (typeof callbacks?.bool === 'function') { + if (callbacks?.bool != null) { return array.filter(callbacks.bool); } @@ -57,20 +57,19 @@ export function findValues( return array.filter(item => item === value); } - const hasCallback = typeof callbacks?.key === 'function'; - const result: Model[] = []; - const values: unknown[] = hasCallback ? [] : result; + const result: unknown[] = []; + const values: unknown[] = callbacks?.key != null ? [] : result; for (let index = 0; index < length; index += 1) { const item = array[index]; - const itemKey = hasCallback ? callbacks.key?.(item, index, array) : item; + const keyed = callbacks?.key?.(item, index, array) ?? item; if ( - (type === 'all' && itemKey === value) || - (type === 'unique' && values.indexOf(itemKey) === -1) + (type === 'all' && keyed === value) || + (type === 'unique' && values.indexOf(keyed) === -1) ) { if (values !== result) { - values.push(itemKey); + values.push(keyed); } result.push(item); diff --git a/src/js/internal/value-handle.ts b/src/js/internal/value/handle.ts similarity index 94% rename from src/js/internal/value-handle.ts rename to src/js/internal/value/handle.ts index 2abc418..8441818 100644 --- a/src/js/internal/value-handle.ts +++ b/src/js/internal/value/handle.ts @@ -1,4 +1,4 @@ -import type {PlainObject} from '@/models'; +import type {PlainObject} from '~/models'; function findKey( needle: string, diff --git a/src/js/is.ts b/src/js/is.ts index 93ef47e..a038133 100644 --- a/src/js/is.ts +++ b/src/js/is.ts @@ -1,5 +1,5 @@ -import type {ArrayOrPlainObject, Key, PlainObject, Primitive} from '@/models'; -import {getString} from '@/string'; +import type {ArrayOrPlainObject, Key, PlainObject, Primitive} from '~/models'; +import {getString} from '~/string'; /** * Is the value an array or a record? diff --git a/src/js/logger.ts b/src/js/logger.ts index 4863e9b..58aebe3 100644 --- a/src/js/logger.ts +++ b/src/js/logger.ts @@ -1,4 +1,4 @@ -import {noop} from '@/function'; +import {noop} from '~/function'; declare global { var _atomic_logging: boolean; @@ -10,14 +10,14 @@ if (globalThis._atomic_logging == null) { class Logger { /** - * Logs any number of values at the "debug" log level + * Log any number of values at the "debug" log level */ get debug() { return this.enabled ? console.debug : noop; } /** - * Logs the value and shows all its properties + * Log the value and shows all its properties */ get dir() { return this.enabled ? console.dir : noop; @@ -38,49 +38,49 @@ class Logger { } /** - * Logs any number of values at the "error" log level + * Log any number of values at the "error" log level */ get error() { return this.enabled ? console.error : noop; } /** - * Logs any number of values at the "info" log level + * Log any number of values at the "info" log level */ get info() { return this.enabled ? console.info : noop; } /** - * Logs any number of values at the "log" log level + * Log any number of values at the "log" log level */ get log() { return this.enabled ? console.log : noop; } /** - * Logs data as a table, with optional properties to use as columns + * Log data as a table, with optional properties to use as columns */ get table() { return this.enabled ? console.table : noop; } /** - * Logs any number of values together with a trace from where it was called + * Log any number of values together with a trace from where it was called */ get trace() { return this.enabled ? console.trace : noop; } /** - * Logs any number of values at the "warn" log level + * Log any number of values at the "warn" log level */ get warn() { return this.enabled ? console.warn : noop; } /** - * - Starts a logged timer with a label + * - Start a logged timer with a label * - Returns a `Time`-object for logging the current duration of the timer and stopping the timer _(and logging the total duration)_ */ time(label: string): Time { @@ -104,8 +104,8 @@ class Time { } /** - * - Logs the current duration of the timer - * - Ignored if logging is disabled + * - Log the current duration of the timer + * - _(Ignored if logging is disabled)_ */ log(): void { if (this.state.started && !this.state.stopped && logger.enabled) { @@ -114,8 +114,8 @@ class Time { } /** - * - Stops the timer and logs the total duration - * - Will always log the total duration, even if logging is disabled + * - Stop the timer and logs the total duration + * - _(Will always log the total duration, even if logging is disabled)_ */ stop(): void { if (this.state.started && !this.state.stopped) { diff --git a/src/js/math.ts b/src/js/math.ts index c5c6d2b..87f5f7d 100644 --- a/src/js/math.ts +++ b/src/js/math.ts @@ -20,7 +20,7 @@ export function min(values: number[]): number { } /** - * Rounds a number to a specific number of decimal places _(defaults to 0)_ + * Round a number to a specific number of decimal places _(defaults to 0)_ */ export function round(value: number, decimals?: number): number { if (typeof decimals !== 'number' || decimals < 1) { diff --git a/src/js/models.ts b/src/js/models.ts index 3f6035d..759df2c 100644 --- a/src/js/models.ts +++ b/src/js/models.ts @@ -13,6 +13,11 @@ export type EventPosition = { y: number; }; +export type KeyedValue< + Item, + Key extends keyof Item, +> = Item[Key] extends PropertyKey ? Item[Key] : never; + export type NestedArrayType = Value extends Array ? NestedArrayType : Value; @@ -20,11 +25,6 @@ export type NestedArrayType = Value extends Array // biome-ignore lint/suspicious/noExplicitAny: export type GenericCallback = (...args: any[]) => any; -export type GetterSetter = { - get(): Value; - set(value: Value): void; -}; - export type Key = number | string; export type PlainObject = UnknownRecord; diff --git a/src/js/number.ts b/src/js/number.ts index 8559108..468476f 100644 --- a/src/js/number.ts +++ b/src/js/number.ts @@ -6,7 +6,7 @@ export function between(value: number, min: number, max: number): boolean { } /** - * - Clamps a number between a minimum and maximum value + * - Clamp a number between a minimum and maximum value * - If `loop` is `true`, when the value is less than the minimum, it will be clamped as the maximum, and vice versa */ export function clamp( @@ -23,7 +23,7 @@ export function clamp( } /** - * - Gets the number value from an unknown value + * - Get the number value from an unknown value * - Returns `NaN` if the value is `undefined`, `null`, or cannot be parsed * - Based on Lodash :-) */ diff --git a/src/js/query.ts b/src/js/query.ts index a0eac5d..6f24be8 100644 --- a/src/js/query.ts +++ b/src/js/query.ts @@ -1,10 +1,10 @@ -import {isNullableOrWhitespace, isPlainObject} from '@/is'; -import type {ArrayOrPlainObject, PlainObject} from '@/models'; -import {join} from '@/string'; -import {setValue} from '@/value'; +import {isPlainObject} from '~/is'; +import type {ArrayOrPlainObject, PlainObject} from '~/models'; +import {join} from '~/string'; +import {setValue} from '~/value'; /** - * Converts a query string to a plain _(nested)_ object + * Convert a query string to a plain _(nested)_ object */ export function fromQuery(query: string): PlainObject { const parts = query.split('&'); @@ -15,10 +15,6 @@ export function fromQuery(query: string): PlainObject { for (let index = 0; index < length; index += 1) { const [key, value] = parts[index].split('=').map(decodeURIComponent); - if (isNullableOrWhitespace(key)) { - continue; - } - if (key.includes('.')) { setValue(parameters, key, getValue(value)); } else { @@ -86,7 +82,7 @@ function isDecodable(value: unknown): value is boolean | number | string { } /** - * Converts a plain _(nested)_ object to a query string + * Convert a plain _(nested)_ object to a query string */ export function toQuery(parameters: PlainObject): string { return getParts(parameters, false) diff --git a/src/js/queue.ts b/src/js/queue.ts index 9dc185c..083747d 100644 --- a/src/js/queue.ts +++ b/src/js/queue.ts @@ -13,7 +13,7 @@ if (globalThis._atomic_queued == null) { } /** - * Queues a callback to be executed at the next best time + * Queue a callback to be executed at the next best time */ export function queue(callback: () => void): void { _atomic_queued.add(callback); diff --git a/src/js/random.ts b/src/js/random.ts index ee040e5..ef547d7 100644 --- a/src/js/random.ts +++ b/src/js/random.ts @@ -1,14 +1,14 @@ -import {shuffle} from '@/array/shuffle'; +import {shuffle} from '~/array/shuffle'; /** - * Returns a random boolean + * Get a random boolean */ export function getRandomBoolean(): boolean { return Math.random() > 0.5; } /** - * Returns a random string of characters with a specified length + * Get a random string of characters with a specified length * - `selection` defaults to all lowercase letters in the English alphabet */ export function getRandomCharacters( @@ -34,24 +34,14 @@ export function getRandomCharacters( } /** - * Returns a random hexadecimal colour + * Get a random hexadecimal colour */ export function getRandomColour(): string { return `#${Array.from({length: 6}, getRandomHex).join('')}`; } /** - * Returns a random date - */ -export function getRandomDate(earliest?: Date, latest?: Date): Date { - const earliestTime = earliest?.getTime() ?? -8_640_000_000_000_000; - const latestTime = latest?.getTime() ?? 8_640_000_000_000_000; - - return new Date(getRandomInteger(earliestTime, latestTime)); -} - -/** - * Returns a random floating-point number + * Get a random floating-point number */ export function getRandomFloat(min?: number, max?: number): number { const minimum = min ?? Number.MIN_SAFE_INTEGER; @@ -60,28 +50,28 @@ export function getRandomFloat(min?: number, max?: number): number { } /** - * Returns a random hexadecimal character + * Get a random hexadecimal character */ export function getRandomHex(): string { return '0123456789ABCDEF'[getRandomInteger(0, 16)]; } /** - * Returns a random integer + * Get a random integer */ export function getRandomInteger(min?: number, max?: number): number { return Math.floor(getRandomFloat(min, max)); } /** - * Returns a random item from an array + * Get a random item from an array */ export function getRandomItem(array: Value[]): Value { return array[getRandomInteger(0, array.length)]; } /** - * - Returns an amount of random items from an array + * - Get an amount of random items from an array * - If `amount` is not specified, a shuffled array will be returned instead */ export function getRandomItems( diff --git a/src/js/sized.ts b/src/js/sized.ts index 38bab4a..1829c43 100644 --- a/src/js/sized.ts +++ b/src/js/sized.ts @@ -1,4 +1,4 @@ -import {clamp} from '@/number'; +import {clamp} from '~/number'; /** * A Map with a maximum size @@ -6,85 +6,85 @@ import {clamp} from '@/number'; * - Behaviour is similar to a _LRU_-cache, where the least recently used entries are removed */ export class SizedMap extends Map { - private readonly maximumSize: number; + private readonly maximumSize: number; - /** - * Is the Map full? - */ - get full() { - return this.size >= this.maximumSize; - } - - /** - * The maximum size of the Map - */ - get maximum() { - return this.maximumSize; - } + /** + * Is the Map full? + */ + get full() { + return this.size >= this.maximumSize; + } - /** - * Creates a new Map with entries and a maximum size _(2^20)_ - */ - constructor(entries: Array<[Key, Value]>); - - /** - * Creates a new Map with a maximum size _(but clamped at 2^24)_ - */ - constructor(maximum: number); - - /** - * Creates a new Map with _(optional)_ entries and a maximum size _(defaults to 2^20; clamped at 2^24)_ - */ - constructor(entries?: Array<[Key, Value]>, maximum?: number); - - constructor(entries?: Array<[Key, Value]> | number, maximum?: number) { - const maximumSize = getMaximum( - typeof entries === 'number' - ? entries - : typeof maximum === 'number' - ? maximum - : undefined, - ); + /** + * The maximum size of the Map + */ + get maximum() { + return this.maximumSize; + } - super(Array.isArray(entries) ? entries.slice(0, maximumSize) : undefined); + /** + * Create a new Map with entries and a maximum size _(2^20)_ + */ + constructor(entries: Array<[Key, Value]>); + + /** + * Create a new Map with a maximum size _(but clamped at 2^24)_ + */ + constructor(maximum: number); + + /** + * Create a new Map with _(optional)_ entries and a maximum size _(defaults to 2^20; clamped at 2^24)_ + */ + constructor(entries?: Array<[Key, Value]>, maximum?: number); + + constructor(entries?: Array<[Key, Value]> | number, maximum?: number) { + const maximumSize = getMaximum( + typeof entries === 'number' + ? entries + : typeof maximum === 'number' + ? maximum + : undefined, + ); + + super(Array.isArray(entries) ? entries.slice(0, maximumSize) : undefined); + + this.maximumSize = maximumSize; + + if (Array.isArray(entries) && entries.length > maximumSize) { + for (let index = 0; index < maximumSize; index += 1) { + this.set(...entries[entries.length - maximumSize + index]); + } + } + } - this.maximumSize = maximumSize; + /** + * @inheritdoc + */ + get(key: Key): Value | undefined { + const value = super.get(key); - if (Array.isArray(entries) && entries.length > maximumSize) { - for (let index = 0; index < maximumSize; index += 1) { - this.set(...entries[entries.length - maximumSize + index]); + if (value == null && !this.has(key)) { + return; } - } - } - /** - * @inheritdoc - */ - get(key: Key): Value | undefined { - const value = super.get(key); + this.set(key, value as Value); - if (value === undefined && !this.has(key)) { - return; + return value; } - this.set(key, value as Value); - - return value; - } + /** + * @inheritdoc + */ + set(key: Key, value: Value): this { + if (this.has(key)) { + this.delete(key); + } else if (this.size >= this.maximumSize) { + this.delete(this.keys().next().value as Key); + } - /** - * @inheritdoc - */ - set(key: Key, value: Value): this { - if (this.has(key)) { - this.delete(key); - } else if (this.size >= this.maximumSize) { - this.delete(this.keys().next().value as Key); + return super.set(key, value); } - - return super.set(key, value); } -} /** * A Set with a maximum size @@ -92,104 +92,104 @@ export class SizedMap extends Map { * - Behaviour is similar to a _LRU_-cache, where the oldest values are removed */ export class SizedSet extends Set { - private readonly maximumSize: number; + private readonly maximumSize: number; - /** - * Is the Set full? - */ - get full() { - return this.size >= this.maximumSize; - } + /** + * Is the Set full? + */ + get full() { + return this.size >= this.maximumSize; + } - /** - * The maximum size of the Set - */ - get maximum() { - return this.maximumSize; - } + /** + * The maximum size of the Set + */ + get maximum() { + return this.maximumSize; + } - /** - * Creates a new Set with values and a maximum size _(2^20)_ - */ - constructor(values: Value[]); - - /** - * Creates a new Set with a maximum size _(but clamped at 2^24)_ - */ - constructor(maximum: number); - - /** - * Creates a new Set with _(optional)_ values and a maximum size _(defaults to 2^20; clamped at 2^24)_ - */ - constructor(values?: Value[], maximum?: number); - - constructor(values?: Value[] | number, maximum?: number) { - const maximumSize = getMaximum( - typeof values === 'number' - ? values - : typeof maximum === 'number' - ? maximum + /** + * Create a new Set with values and a maximum size _(2^20)_ + */ + constructor(values: Value[]); + + /** + * Create a new Set with a maximum size _(but clamped at 2^24)_ + */ + constructor(maximum: number); + + /** + * Create a new Set with _(optional)_ values and a maximum size _(defaults to 2^20; clamped at 2^24)_ + */ + constructor(values?: Value[], maximum?: number); + + constructor(values?: Value[] | number, maximum?: number) { + const maximumSize = getMaximum( + typeof values === 'number' + ? values + : typeof maximum === 'number' + ? maximum + : undefined, + ); + + super( + Array.isArray(values) && values.length <= maximumSize + ? values : undefined, - ); + ); - super( - Array.isArray(values) && values.length <= maximumSize - ? values - : undefined, - ); + this.maximumSize = maximumSize; - this.maximumSize = maximumSize; - - if (Array.isArray(values) && values.length > maximumSize) { - for (let index = 0; index < maximumSize; index += 1) { - this.add(values[values.length - maximumSize + index]); + if (Array.isArray(values) && values.length > maximumSize) { + for (let index = 0; index < maximumSize; index += 1) { + this.add(values[values.length - maximumSize + index]); + } } } - } - - /** - * @inheritdoc - */ - add(value: Value): this { - if (this.has(value)) { - this.delete(value); - } else if (this.size >= this.maximumSize) { - this.delete(this.values().next().value as Value); - } - return super.add(value); - } + /** + * @inheritdoc + */ + add(value: Value): this { + if (this.has(value)) { + this.delete(value); + } else if (this.size >= this.maximumSize) { + this.delete(this.values().next().value as Value); + } - /** - * Get a value from an index in the Set, if it exists - * - Negative indices are counted from the end - * - Optionally move the value to the end with `update` - */ - at(index: number, update?: boolean): Value | undefined { - const value = [...this.values()][index < 0 ? this.size + index : index]; - - if ((update ?? false) && this.has(value)) { - this.delete(value); - this.add(value); + return super.add(value); } - return value; - } + /** + * Get a value from an index in the Set, if it exists + * - Negative indices are counted from the end + * - Optionally move the value to the end with `update` + */ + at(index: number, update?: boolean): Value | undefined { + const value = [...this.values()][index < 0 ? this.size + index : index]; - /** - * Get a value from the Set, if it exists _(and move it to the end)_ - */ - get(value: Value, update?: boolean): Value | undefined { - if (this.has(value)) { - if (update ?? false) { + if ((update ?? false) && this.has(value)) { this.delete(value); this.add(value); } return value; } + + /** + * Get a value from the Set, if it exists _(and move it to the end)_ + */ + get(value: Value, update?: boolean): Value | undefined { + if (this.has(value)) { + if (update ?? false) { + this.delete(value); + this.add(value); + } + + return value; + } + } } -} function getMaximum(first?: unknown, second?: unknown): number { const actual = diff --git a/src/js/string/case.ts b/src/js/string/case.ts index e83092c..3843c7b 100644 --- a/src/js/string/case.ts +++ b/src/js/string/case.ts @@ -1,4 +1,4 @@ -import {words} from '@/string/index'; +import {words} from '~/string/index'; /** * Convert a string to camel case _(thisIsCamelCase)_ diff --git a/src/js/string/index.ts b/src/js/string/index.ts index 06fe626..e557453 100644 --- a/src/js/string/index.ts +++ b/src/js/string/index.ts @@ -1,4 +1,4 @@ -import {compact} from '@/array'; +import {compact} from '~/array'; /** * Create a new UUID @@ -34,7 +34,7 @@ export function getString(value: unknown): string { } /** - * Joins an array into a string while ignoring empty values _(with an optional delimiter)_ + * Join an array into a string while ignoring empty values _(with an optional delimiter)_ */ export function join(value: unknown[], delimiter?: string): string { return compact(value) @@ -44,7 +44,7 @@ export function join(value: unknown[], delimiter?: string): string { } /** - * Parses a JSON string into its proper value _(or `undefined` if it fails)_ + * Parse a JSON string into its proper value _(or `undefined` if it fails)_ */ export function parse( value: string, @@ -58,7 +58,7 @@ export function parse( } /** - * Truncates a string to a specified length, when possible + * Truncate a string to a specified length, when possible * - Returned as-is if the string is already short enough * - A suffix may be appended to the truncated string, e.g., an ellipsis */ @@ -67,12 +67,18 @@ export function truncate( length: number, suffix?: string, ): string { + if (length <= 0) { + return ''; + } + + if (length >= value.length) { + return value; + } + const suffixLength = suffix?.length ?? 0; const truncatedLength = length - suffixLength; - return value.length <= length - ? value - : `${value.slice(0, truncatedLength)}${suffix ?? ''}`; + return `${value.slice(0, truncatedLength)}${suffix ?? ''}`; } /** diff --git a/src/js/string/template.ts b/src/js/string/template.ts index 8c6425f..6f02b6b 100644 --- a/src/js/string/template.ts +++ b/src/js/string/template.ts @@ -1,6 +1,6 @@ -import type {PlainObject} from '@/models'; -import {getString} from '@/string/index'; -import {getValue} from '@/value/get'; +import type {PlainObject} from '~/models'; +import {getString} from '~/string/index'; +import {getValue} from '~/value/get'; type Options = { /** @@ -13,6 +13,9 @@ type Options = { pattern?: RegExp; }; +/** + * Render a string from a template with variables + */ export function template( value: string, variables: PlainObject, diff --git a/src/js/value/clone.ts b/src/js/value/clone.ts index 40ea3a2..48b8aa0 100644 --- a/src/js/value/clone.ts +++ b/src/js/value/clone.ts @@ -1,12 +1,8 @@ -import {isArrayOrPlainObject} from '@/is'; -import type {ArrayOrPlainObject, PlainObject} from '@/models'; - -export function clone(fn: (...args: unknown[]) => unknown): null; - -export function clone(value: Value): Value; +import {isArrayOrPlainObject} from '~/is'; +import type {ArrayOrPlainObject, GenericCallback, PlainObject} from '~/models'; /** - * Clones any kind of value _(deeply, if needed)_ + * Clone any kind of value _(deeply, if needed)_ */ export function clone(value: unknown) { switch (true) { diff --git a/src/js/value/compare.ts b/src/js/value/compare.ts index 36fa151..c2d03f8 100644 --- a/src/js/value/compare.ts +++ b/src/js/value/compare.ts @@ -1,6 +1,6 @@ -import {max} from '@/math'; -import {getNumber} from '@/number'; -import {words, getString, join} from '@/string'; +import {max} from '~/math'; +import {getNumber} from '~/number'; +import {words, getString, join} from '~/string'; /** * Compare two values _(for sorting purposes)_ @@ -62,8 +62,6 @@ export function compare(first: unknown, second: unknown): number { // Same value on last part? let's not fall through to string comparison return 0; } - - continue; } return firstNumber - secondNumber; diff --git a/src/js/value/diff.ts b/src/js/value/diff.ts index e9bfc7b..5cb5269 100644 --- a/src/js/value/diff.ts +++ b/src/js/value/diff.ts @@ -1,7 +1,7 @@ -import {isArrayOrPlainObject} from '@/is'; -import type {ArrayOrPlainObject, Key, PlainObject} from '@/models'; -import {join} from '@/string/index'; -import {equal} from '@/value/equal'; +import {isArrayOrPlainObject} from '~/is'; +import type {ArrayOrPlainObject, Key, PlainObject} from '~/models'; +import {join} from '~/string/index'; +import {equal} from '~/value/equal'; export type DiffType = 'full' | 'none' | 'partial'; diff --git a/src/js/value/equal.ts b/src/js/value/equal.ts index d8cf75c..9a4b13a 100644 --- a/src/js/value/equal.ts +++ b/src/js/value/equal.ts @@ -1,5 +1,5 @@ -import {isPlainObject} from '@/is'; -import type {ArrayOrPlainObject, PlainObject} from '@/models'; +import {isPlainObject} from '~/is'; +import type {ArrayOrPlainObject, PlainObject} from '~/models'; /** * Are two strings equal? _(Case-sensitive by default)_ diff --git a/src/js/value/get.ts b/src/js/value/get.ts index f0cbd2a..2723aed 100644 --- a/src/js/value/get.ts +++ b/src/js/value/get.ts @@ -1,6 +1,6 @@ -import {handleValue} from '@/internal/value-handle'; -import type {Get, Paths, PlainObject} from '@/models'; import type {ToString} from 'type-fest/source/internal/string'; +import {handleValue} from '~/internal/value/handle'; +import type {Get, Paths, PlainObject} from '~/models'; /** * - Get the value from an object using a known path @@ -34,7 +34,7 @@ export function getValue( const {length} = parts; let index = 0; - let value = typeof data === 'object' ? (data ?? {}) : {}; + let value: PlainObject = data; while (index < length && value != null) { value = handleValue( diff --git a/src/js/value/index.ts b/src/js/value/index.ts index 00f77c0..d75246f 100644 --- a/src/js/value/index.ts +++ b/src/js/value/index.ts @@ -1,7 +1,7 @@ -import type {PlainObject} from '@/models'; +import type {PlainObject} from '~/models'; /** - * Creates a new object with only the specified keys + * Create a new object with only the specified keys */ export function partial( value: Value, diff --git a/src/js/value/merge.ts b/src/js/value/merge.ts index c9aeb94..fa1a5cd 100644 --- a/src/js/value/merge.ts +++ b/src/js/value/merge.ts @@ -1,5 +1,5 @@ -import {isArrayOrPlainObject} from '@/is'; -import type {ArrayOrPlainObject, PlainObject} from '@/models'; +import {isArrayOrPlainObject} from '~/is'; +import type {ArrayOrPlainObject, PlainObject} from '~/models'; type MergeOptions = { /** @@ -10,7 +10,7 @@ type MergeOptions = { }; /** - * Merges multiple arrays or objects into a single one + * Merge multiple arrays or objects into a single one */ export function merge( values: Model[], diff --git a/src/js/value/set.ts b/src/js/value/set.ts index e233920..ad9f972 100644 --- a/src/js/value/set.ts +++ b/src/js/value/set.ts @@ -1,5 +1,5 @@ -import {handleValue} from '@/internal/value-handle'; -import type {Paths, PlainObject} from '@/models'; +import {handleValue} from '~/internal/value/handle'; +import type {Paths, PlainObject} from '~/models'; /** * - Set the value in an object using a known path @@ -38,8 +38,7 @@ export function setValue( const {length} = parts; const lastIndex = length - 1; - let target: PlainObject = - typeof data === 'object' && data !== null ? data : {}; + let target: PlainObject = data; for (let index = 0; index < length; index += 1) { const part = parts[index]; diff --git a/src/js/value/smush.ts b/src/js/value/smush.ts index 38235ee..64bb300 100644 --- a/src/js/value/smush.ts +++ b/src/js/value/smush.ts @@ -1,8 +1,8 @@ -import {isArrayOrPlainObject} from '@/is'; -import type {ArrayOrPlainObject, PlainObject} from '@/models'; -import {join} from '@/string/index'; import type {Get, Paths, Simplify} from 'type-fest'; import type {ToString} from 'type-fest/source/internal/string'; +import {isArrayOrPlainObject} from '~/is'; +import type {ArrayOrPlainObject, PlainObject} from '~/models'; +import {join} from '~/string/index'; type Smushed = Simplify<{ [Key in Paths]: Get>; @@ -31,7 +31,7 @@ function flatten(value: ArrayOrPlainObject, prefix?: string): PlainObject { } /** - * Smushes an object into a flat object with dot notation keys + * Smush an object into a flat object that uses dot notation keys */ export function smush(value: Value): Smushed { return flatten(value) as never; diff --git a/src/js/value/unsmush.ts b/src/js/value/unsmush.ts index 9893a2a..d5b4e32 100644 --- a/src/js/value/unsmush.ts +++ b/src/js/value/unsmush.ts @@ -1,7 +1,7 @@ -import {isArrayOrPlainObject} from '@/is'; -import type {PlainObject} from '@/models'; -import {setValue} from '@/value/set'; import type {KeysOfUnion, Simplify} from 'type-fest'; +import {isArrayOrPlainObject} from '~/is'; +import type {PlainObject} from '~/models'; +import {setValue} from '~/value/set'; type Unsmushed = Simplify< Omit< @@ -32,7 +32,7 @@ function getKeyGroups(value: PlainObject): string[][] { } /** - * Unsmushes a smushed object _(turning dot notation keys into nested keys)_ + * Unsmush a smushed object _(turning dot notation keys into nested keys)_ */ export function unsmush( value: Value, diff --git a/test/array.test.ts b/test/array.test.ts index 524c9cd..4a41121 100644 --- a/test/array.test.ts +++ b/test/array.test.ts @@ -22,16 +22,17 @@ import {getRandomInteger} from '../src/js/random'; import {diff, equal} from '../src/js/value'; type Item = { + age: number; id: number; name: string; }; const complex: Item[] = [ - {id: 1, name: 'Alice'}, - {id: 2, name: 'Bob'}, - {id: 3, name: 'Charlie'}, - {id: 4, name: 'Alice'}, - {id: 5, name: 'David'}, + {id: 1, age: 25, name: 'Alice'}, + {id: 2, age: 30, name: 'Bob'}, + {id: 3, age: 25, name: 'Charlie'}, + {id: 4, age: 30, name: 'Alice'}, + {id: 5, age: 35, name: 'David'}, ]; const simple = [1, 2, 3, 4]; @@ -66,21 +67,21 @@ test('count', () => { expect(count(simple, 2)).toBe(1); expect(count(simple, 5)).toBe(0); - const countByKeyValue = count(complex, 3, 'id'); - const countByKeyCallback = count(complex, 3, item => item.id); - const countByValueCallback = count(complex, item => item.id === 3); + const countByCallback = count(complex, item => item.id === 3); + const countByKeyValue = count(complex, 'id', 3); + const countByKeyCallback = count(complex, item => item.id, 3); + expect(countByCallback).toBe(1); expect(countByKeyValue).toBe(1); expect(countByKeyCallback).toBe(1); - expect(countByValueCallback).toBe(1); }); test('exists', () => { expect(exists(simple, 2)).toBe(true); expect(exists(simple, 5)).toBe(false); - const existsByKeyCallback = exists(complex, 3, item => item.id); - const existsByKeyValue = exists(complex, 3, 'id'); + const existsByKeyCallback = exists(complex, item => item.id, 3); + const existsByKeyValue = exists(complex, 'id', 3); expect(existsByKeyCallback).toEqual(true); expect(existsByKeyValue).toEqual(true); @@ -94,15 +95,15 @@ test('filter', () => { expect(filter(simple, 2)).toEqual([2]); expect(filter(simple, 5)).toEqual([]); - const filterByKeyValue = filter(complex, 3, 'id'); - const filterByKeyCallback = filter(complex, 3, item => item.id); + const filterByKeyValue = filter(complex, 'id', 3); + const filterByKeyCallback = filter(complex, item => item.id, 3); - expect(filterByKeyValue).toEqual([{id: 3, name: 'Charlie'}]); - expect(filterByKeyCallback).toEqual([{id: 3, name: 'Charlie'}]); + expect(filterByKeyValue).toEqual([{id: 3, age: 25, name: 'Charlie'}]); + expect(filterByKeyCallback).toEqual([{id: 3, age: 25, name: 'Charlie'}]); const filterByValueCallback = filter(complex, item => item.id === 3); - expect(filterByValueCallback).toEqual([{id: 3, name: 'Charlie'}]); + expect(filterByValueCallback).toEqual([{id: 3, age: 25, name: 'Charlie'}]); }); test('find', () => { @@ -110,14 +111,16 @@ test('find', () => { expect(find(simple, 5)).toBeUndefined(); const findByKeyCallback = find(complex, item => item.id === 3); - const findByKeyValue = find(complex, 3, 'id'); + const findByKeyValue = find(complex, 'id', 3); - expect(findByKeyCallback).toEqual({id: 3, name: 'Charlie'}); - expect(findByKeyValue).toEqual({id: 3, name: 'Charlie'}); + expect(findByKeyCallback).toEqual({id: 3, age: 25, name: 'Charlie'}); + expect(findByKeyValue).toEqual({id: 3, age: 25, name: 'Charlie'}); const findByValueCallback = find(complex, item => item.id === 3); - expect(findByValueCallback).toEqual({id: 3, name: 'Charlie'}); + expect(findByValueCallback).toEqual({id: 3, age: 25, name: 'Charlie'}); + + expect(find(complex, 'id', 99)).toBeUndefined(); }); test('flatten', () => { @@ -126,12 +129,45 @@ test('flatten', () => { ]); }); +test('groupBy', () => { + const simpleResult: Record = { + 25: {id: 3, name: 'Charlie', age: 25}, + 30: {id: 4, name: 'Alice', age: 30}, + 35: {id: 5, name: 'David', age: 35}, + }; + + const groupedByAgeCallback = groupBy(complex, item => item.age); + const groupedByAgeKey = groupBy(complex, 'age'); + + expect(groupedByAgeCallback).toEqual(simpleResult); + expect(groupedByAgeKey).toEqual(simpleResult); + expect(groupedByAgeCallback).toEqual(groupedByAgeKey); + + const blah = { + 25: 'Charlie', + 30: 'Alice', + 35: 'David', + }; + + const groupedByAgeWithNameCallback = groupBy( + complex, + 'age', + item => item.name, + ); + + const groupedByAgeWithNameKey = groupBy(complex, 'age', 'name'); + + expect(groupedByAgeWithNameCallback).toEqual(blah); + expect(groupedByAgeWithNameKey).toEqual(blah); + expect(groupedByAgeWithNameCallback).toEqual(groupedByAgeWithNameKey); +}); + test('indexOf', () => { expect(indexOf(simple, 2)).toBe(1); expect(indexOf(simple, 5)).toBe(-1); - const indexOfByKeyCallback = indexOf(complex, 3, item => item.id); - const indexOfByKeyValue = indexOf(complex, 3, 'id'); + const indexOfByKeyCallback = indexOf(complex, item => item.id, 3); + const indexOfByKeyValue = indexOf(complex, 'id', 3); expect(indexOfByKeyCallback).toEqual(2); expect(indexOfByKeyValue).toEqual(2); @@ -139,31 +175,8 @@ test('indexOf', () => { const indexOfByValueCallback = indexOf(complex, item => item.id === 3); expect(indexOfByValueCallback).toEqual(2); -}); - -test('groupBy', () => { - const array = [ - {name: 'Alice', age: 25}, - {name: 'Bob', age: 30}, - {name: 'Charlie', age: 25}, - ]; - - const result = { - 25: [ - {name: 'Alice', age: 25}, - {name: 'Charlie', age: 25}, - ], - 30: [{name: 'Bob', age: 30}], - }; - const groupedByAgeCallback = groupBy(array, item => item.age); - const groupedByAgeKey = groupBy(array, 'age'); - - expect(groupedByAgeCallback).toEqual(result); - expect(groupedByAgeKey).toEqual(result); - expect(groupedByAgeCallback).toEqual(groupedByAgeKey); - - expect(groupBy(array, 'not.ok')).toEqual({}); + expect(indexOf(complex, 'id', 99)).toBe(-1); }); test('insert', () => { @@ -188,6 +201,12 @@ test('insert', () => { expect(array[1]).toBe('#1'); expect(array[length + 1]).toBe(2); expect(array[length + 2]).toBe(3); + + const appended = []; + + insert(appended, [1, 2, 3]); + + expect(appended).toEqual([1, 2, 3]); }); test('push', () => { @@ -349,30 +368,31 @@ test('toMap', () => { expect(indicedObjects).toEqual( new Map([ - [0, {id: 1, name: 'Alice'}], - [1, {id: 2, name: 'Bob'}], - [2, {id: 3, name: 'Charlie'}], - [3, {id: 4, name: 'Alice'}], - [4, {id: 5, name: 'David'}], + [0, {id: 1, age: 25, name: 'Alice'}], + [1, {id: 2, age: 30, name: 'Bob'}], + [2, {id: 3, age: 25, name: 'Charlie'}], + [3, {id: 4, age: 30, name: 'Alice'}], + [4, {id: 5, age: 35, name: 'David'}], ]), ); expect(keyedObjects).toEqual( new Map([ - [1, {id: 1, name: 'Alice'}], - [2, {id: 2, name: 'Bob'}], - [3, {id: 3, name: 'Charlie'}], - [4, {id: 4, name: 'Alice'}], - [5, {id: 5, name: 'David'}], + [1, {id: 1, age: 25, name: 'Alice'}], + [2, {id: 2, age: 30, name: 'Bob'}], + [3, {id: 3, age: 25, name: 'Charlie'}], + [4, {id: 4, age: 30, name: 'Alice'}], + [5, {id: 5, age: 35, name: 'David'}], ]), ); expect(callbackedObjects).toEqual( new Map([ - ['Alice', {id: 4, name: 'Alice'}], - ['Bob', {id: 2, name: 'Bob'}], - ['Charlie', {id: 3, name: 'Charlie'}], - ['David', {id: 5, name: 'David'}], + ['Alice', {id: 1, age: 25, name: 'Alice'}], + ['Bob', {id: 2, age: 30, name: 'Bob'}], + ['Charlie', {id: 3, age: 25, name: 'Charlie'}], + ['Alice', {id: 4, age: 30, name: 'Alice'}], + ['David', {id: 5, age: 35, name: 'David'}], ]), ); @@ -382,21 +402,21 @@ test('toMap', () => { expect(indicedArrays).toEqual( new Map([ - [0, [{id: 1, name: 'Alice'}]], - [1, [{id: 2, name: 'Bob'}]], - [2, [{id: 3, name: 'Charlie'}]], - [3, [{id: 4, name: 'Alice'}]], - [4, [{id: 5, name: 'David'}]], + [0, [{id: 1, age: 25, name: 'Alice'}]], + [1, [{id: 2, age: 30, name: 'Bob'}]], + [2, [{id: 3, age: 25, name: 'Charlie'}]], + [3, [{id: 4, age: 30, name: 'Alice'}]], + [4, [{id: 5, age: 35, name: 'David'}]], ]), ); expect(keyedArrays).toEqual( new Map([ - [1, [{id: 1, name: 'Alice'}]], - [2, [{id: 2, name: 'Bob'}]], - [3, [{id: 3, name: 'Charlie'}]], - [4, [{id: 4, name: 'Alice'}]], - [5, [{id: 5, name: 'David'}]], + [1, [{id: 1, age: 25, name: 'Alice'}]], + [2, [{id: 2, age: 30, name: 'Bob'}]], + [3, [{id: 3, age: 25, name: 'Charlie'}]], + [4, [{id: 4, age: 30, name: 'Alice'}]], + [5, [{id: 5, age: 35, name: 'David'}]], ]), ); @@ -405,13 +425,13 @@ test('toMap', () => { [ 'Alice', [ - {id: 1, name: 'Alice'}, - {id: 4, name: 'Alice'}, + {id: 1, age: 25, name: 'Alice'}, + {id: 4, age: 30, name: 'Alice'}, ], ], - ['Bob', [{id: 2, name: 'Bob'}]], - ['Charlie', [{id: 3, name: 'Charlie'}]], - ['David', [{id: 5, name: 'David'}]], + ['Bob', [{id: 2, age: 30, name: 'Bob'}]], + ['Charlie', [{id: 3, age: 25, name: 'Charlie'}]], + ['David', [{id: 5, age: 35, name: 'David'}]], ]), ); }); @@ -422,26 +442,26 @@ test('toRecord', () => { const callbackedObjects = toRecord(complex, item => item.name); expect(indicedObjects).toEqual({ - 0: {id: 1, name: 'Alice'}, - 1: {id: 2, name: 'Bob'}, - 2: {id: 3, name: 'Charlie'}, - 3: {id: 4, name: 'Alice'}, - 4: {id: 5, name: 'David'}, + 0: {id: 1, age: 25, name: 'Alice'}, + 1: {id: 2, age: 30, name: 'Bob'}, + 2: {id: 3, age: 25, name: 'Charlie'}, + 3: {id: 4, age: 30, name: 'Alice'}, + 4: {id: 5, age: 35, name: 'David'}, }); expect(keyedObjects).toEqual({ - 1: {id: 1, name: 'Alice'}, - 2: {id: 2, name: 'Bob'}, - 3: {id: 3, name: 'Charlie'}, - 4: {id: 4, name: 'Alice'}, - 5: {id: 5, name: 'David'}, + 1: {id: 1, age: 25, name: 'Alice'}, + 2: {id: 2, age: 30, name: 'Bob'}, + 3: {id: 3, age: 25, name: 'Charlie'}, + 4: {id: 4, age: 30, name: 'Alice'}, + 5: {id: 5, age: 35, name: 'David'}, }); expect(callbackedObjects).toEqual({ - Alice: {id: 4, name: 'Alice'}, - Bob: {id: 2, name: 'Bob'}, - Charlie: {id: 3, name: 'Charlie'}, - David: {id: 5, name: 'David'}, + Alice: {id: 4, age: 30, name: 'Alice'}, + Bob: {id: 2, age: 30, name: 'Bob'}, + Charlie: {id: 3, age: 25, name: 'Charlie'}, + David: {id: 5, age: 35, name: 'David'}, }); const indicedArrays = toRecord(complex, true); @@ -449,29 +469,29 @@ test('toRecord', () => { const callbackedArrays = toRecord(complex, item => item.name, true); expect(indicedArrays).toEqual({ - 0: [{id: 1, name: 'Alice'}], - 1: [{id: 2, name: 'Bob'}], - 2: [{id: 3, name: 'Charlie'}], - 3: [{id: 4, name: 'Alice'}], - 4: [{id: 5, name: 'David'}], + 0: [{id: 1, age: 25, name: 'Alice'}], + 1: [{id: 2, age: 30, name: 'Bob'}], + 2: [{id: 3, age: 25, name: 'Charlie'}], + 3: [{id: 4, age: 30, name: 'Alice'}], + 4: [{id: 5, age: 35, name: 'David'}], }); expect(keyedArrays).toEqual({ - 1: [{id: 1, name: 'Alice'}], - 2: [{id: 2, name: 'Bob'}], - 3: [{id: 3, name: 'Charlie'}], - 4: [{id: 4, name: 'Alice'}], - 5: [{id: 5, name: 'David'}], + 1: [{id: 1, age: 25, name: 'Alice'}], + 2: [{id: 2, age: 30, name: 'Bob'}], + 3: [{id: 3, age: 25, name: 'Charlie'}], + 4: [{id: 4, age: 30, name: 'Alice'}], + 5: [{id: 5, age: 35, name: 'David'}], }); expect(callbackedArrays).toEqual({ Alice: [ - {id: 1, name: 'Alice'}, - {id: 4, name: 'Alice'}, + {id: 1, age: 25, name: 'Alice'}, + {id: 4, age: 30, name: 'Alice'}, ], - Bob: [{id: 2, name: 'Bob'}], - Charlie: [{id: 3, name: 'Charlie'}], - David: [{id: 5, name: 'David'}], + Bob: [{id: 2, age: 30, name: 'Bob'}], + Charlie: [{id: 3, age: 25, name: 'Charlie'}], + David: [{id: 5, age: 35, name: 'David'}], }); }); diff --git a/test/colour.test.ts b/test/colour.test.ts index 4f21cf4..c612b30 100644 --- a/test/colour.test.ts +++ b/test/colour.test.ts @@ -4,7 +4,9 @@ import { HexColour, RGBColour, getForegroundColour, + getHSLColour, getHexColour, + getRGBColour, isColour, isHSLColour, isHexColour, @@ -79,26 +81,32 @@ test('getHexColour', () => { expect(getHexColour('invalid').value).toBe('#000000'); }); -test('is', () => { - const hex = getHexColour(hexes[0]); - const hsl = hex.toHsl(); - const rgb = hsl.toRgb(); +test('getHslColour', () => { + const {length} = hsls; - expect(isHexColour(hex)).toBe(true); - expect(isHexColour(hsl)).toBe(false); - expect(isHexColour(rgb)).toBe(false); + for (let index = 0; index < length; index += 1) { + const {hue, lightness, saturation} = hsls[index]; - expect(isHSLColour(hex)).toBe(false); - expect(isHSLColour(hsl)).toBe(true); - expect(isHSLColour(rgb)).toBe(false); + const hsl = getHSLColour(hsls[index]); - expect(isRGBColour(hex)).toBe(false); - expect(isRGBColour(hsl)).toBe(false); - expect(isRGBColour(rgb)).toBe(true); + expect(hsl.hue).toBe(hue); + expect(hsl.lightness).toBe(lightness); + expect(hsl.saturation).toBe(saturation); + } +}); - expect(isColour(hex)).toBe(true); - expect(isColour(hsl)).toBe(true); - expect(isColour(rgb)).toBe(true); +test('getRgbColour', () => { + const {length} = hsls; + + for (let index = 0; index < length; index += 1) { + const {blue, green, red} = rgbs[index]; + + const rgb = getRGBColour(rgbs[index]); + + expect(rgb.blue).toBe(blue); + expect(rgb.green).toBe(green); + expect(rgb.red).toBe(red); + } }); test('hexToRgb', () => { @@ -121,6 +129,28 @@ test('hslToRgb', () => { ).toBe('rgb(128, 64, 191)'); }); +test('is', () => { + const hex = getHexColour(hexes[0]); + const hsl = hex.toHsl(); + const rgb = hsl.toRgb(); + + expect(isHexColour(hex)).toBe(true); + expect(isHexColour(hsl)).toBe(false); + expect(isHexColour(rgb)).toBe(false); + + expect(isHSLColour(hex)).toBe(false); + expect(isHSLColour(hsl)).toBe(true); + expect(isHSLColour(rgb)).toBe(false); + + expect(isRGBColour(hex)).toBe(false); + expect(isRGBColour(hsl)).toBe(false); + expect(isRGBColour(rgb)).toBe(true); + + expect(isColour(hex)).toBe(true); + expect(isColour(hsl)).toBe(true); + expect(isColour(rgb)).toBe(true); +}); + test('rgbToHex', () => { for (let index = 0; index < length; index += 1) { expect(RGBColour.toHex(rgbs[index]).value).toBe(hexes[index]); diff --git a/test/equal.test.ts b/test/equal.test.ts index 30caf21..356548d 100644 --- a/test/equal.test.ts +++ b/test/equal.test.ts @@ -155,8 +155,8 @@ test('regular expression', () => { }); test('set', () => { - const first = new Set([1, 2, 3]); - const second = new Set([3, 2, 1]); + let first = new Set([1, 2, 3]); + let second = new Set([3, 2, 1]); expect(equal(first, second)).toBe(true); @@ -164,6 +164,11 @@ test('set', () => { expect(equal(first, second)).toBe(false); + first = new Set([1, 2, 3]); + second = new Set([97, 98, 99]); + + expect(equal(first, second)).toBe(false); + const firstNested = new Set([{id: 1}, {id: 2}]); const secondNested = new Set([{id: 2}, {id: 1}]); diff --git a/test/function.test.ts b/test/function.test.ts index dffe4e3..77ed259 100644 --- a/test/function.test.ts +++ b/test/function.test.ts @@ -71,9 +71,9 @@ test('memoise', () => { }); test('noop', () => { - // expect(noop).toBeInstanceOf(Function); + expect(noop).toBeInstanceOf(Function); // expect(noop.toString()).toMatch(/\s*function\s*noop\(\)\s*\{\s*\}\s*/); - // expect(noop()).toBeUndefined(); + expect(noop()).toBeUndefined(); }); test('throttle', () => diff --git a/test/random.test.ts b/test/random.test.ts index f8305ce..206b27b 100644 --- a/test/random.test.ts +++ b/test/random.test.ts @@ -3,7 +3,6 @@ import { getRandomBoolean, getRandomCharacters, getRandomColour, - getRandomDate, getRandomFloat, getRandomHex, getRandomInteger, @@ -84,7 +83,9 @@ test('getRandomCharacters', () => } setTimeout(() => { + expect(getRandomCharacters(-99)).toBe(''); expect(defaultFailed).toBe(false); + expect(selectionFailed).toBe(false); done(); }, 250); @@ -105,24 +106,6 @@ test('getRandomColour', () => { expect(invalid).toBe(0); }); -test('getRandomDate', () => { - let index = 0; - let invalid = 0; - - for (; index < size; index += 1) { - if (!(getRandomDate() instanceof Date)) { - invalid += 1; - } - } - - expect(invalid).toBe(0); - - const earliest = new Date(0); - const latest = new Date(); - - expect(getRandomDate(earliest, latest)).toBeInstanceOf(Date); -}); - test('getRandomFloat', () => new Promise(done => { async function run() { @@ -135,18 +118,6 @@ test('getRandomFloat', () => run(); })); -test('getRandomInteger', () => - new Promise(done => { - async function run() { - await getRandomNumber(getRandomInteger); - await getRandomNumber(getRandomInteger, 0, 100); - - done(); - } - - run(); - })); - test('getRandomHex', () => { const possible = new Set('0123456789ABCDEF'); @@ -164,6 +135,18 @@ test('getRandomHex', () => { expect(invalid).toBe(0); }); +test('getRandomInteger', () => + new Promise(done => { + async function run() { + await getRandomNumber(getRandomInteger); + await getRandomNumber(getRandomInteger, 0, 100); + + done(); + } + + run(); + })); + test('getRandomItem', () => { const items = Array.from({length: 10}, (_, index) => index); diff --git a/test/sized.test.ts b/test/sized.test.ts index 8343208..003db6d 100644 --- a/test/sized.test.ts +++ b/test/sized.test.ts @@ -39,6 +39,7 @@ test('SizedMap', () => expect(entriesMap.maximum).toBe(niceMax); expect(entriesMap.size).toBe(3); expect(joinMap(entriesMap)).toBe('0:a; 1:b; 2:c'); + expect(entriesMap.get(123)).toBeUndefined(); expect(maxMap.full).toBe(false); expect(maxMap.maximum).toBe(2); @@ -106,11 +107,8 @@ test('SizedMap', () => test('SizedSet', () => new Promise(done => { const valuesSet = new Sized.SizedSet(['a', 'b', 'c']); - const maxSet = new Sized.SizedSet(2); - const shrunkSet = new Sized.SizedSet(['a', 'b', 'c', 'd', 'e'], 3); - const clampedSet = new Sized.SizedSet(actualMax + 1); expect(valuesSet.full).toBe(false); @@ -141,6 +139,7 @@ test('SizedSet', () => maxSet.add('a'); maxSet.add('b'); maxSet.add('c'); + maxSet.add('c'); expect(maxSet.full).toBe(true); expect(maxSet.maximum).toBe(2); diff --git a/test/string.test.ts b/test/string.test.ts index 1a753ba..ff2826a 100644 --- a/test/string.test.ts +++ b/test/string.test.ts @@ -164,8 +164,8 @@ test('snakeCase', () => { }); test('template', () => { - const basic = '{{a.0.b.1.c}}'; - const custom = ''; + const basic = '{{a.0.b.1.c}}, {{a.0.b.1.c}}!'; + const custom = ', !'; const variables = { a: [ @@ -173,28 +173,28 @@ test('template', () => { B: [ null, { - c: 'Hello!', + c: 'Hello', }, ], }, ], }; - expect(template(basic, variables)).toBe(''); - expect(template(custom, variables)).toBe(''); + expect(template(basic, variables)).toBe(', !'); + expect(template(custom, variables)).toBe(', !'); expect( template(basic, variables, { ignoreCase: true, }), - ).toBe('Hello!'); + ).toBe('Hello, Hello!'); expect( template(custom, variables, { ignoreCase: true, pattern: /<([^>]+)>/g, }), - ).toBe('Hello!'); + ).toBe('Hello, Hello!'); }); test('truncate', () => { @@ -219,6 +219,8 @@ test('truncate', () => { expected[index], ); } + + expect(truncate('Hello, world!', -99)).toBe(''); }); test('titleCase', () => { diff --git a/test/value.test.ts b/test/value.test.ts index 1fd8108..abdbcfd 100644 --- a/test/value.test.ts +++ b/test/value.test.ts @@ -33,10 +33,16 @@ type Mergeable = { }; test('compare', () => { + expect(compare(null, null)).toBe(0); + expect(compare(null, '')).toBe(-1); expect(compare('', '')).toBe(0); + expect(compare('a', '')).toBe(1); expect(compare('', 'a')).toBe(-1); + expect(compare(1, 'a')).toBe(-1); + expect(compare('a', 1)).toBe(1); + expect(compare('a', 'a')).toBe(0); expect(compare('a', 'b')).toBe(-1); expect(compare('b', 'a')).toBe(1); @@ -176,6 +182,16 @@ test('merge', () => { }); expect(merge([])).toEqual({}); + + expect( + merge( + [ + [1, 2, 3, 4, 5], + [null, null, 99], + ], + {skipNullable: true}, + ), + ).toEqual([1, 2, 99, 4, 5]); }); test('partial', () => { diff --git a/tsconfig.json b/tsconfig.json index 0f0ee84..ebef093 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,7 +17,7 @@ "moduleDetection": "force", "moduleResolution": "bundler", "paths": { - "@/*": ["./src/js/*"] + "~/*": ["./src/js/*"] }, "skipLibCheck": true, "strict": true, diff --git a/vite.config.ts b/vite.config.ts index 1c2d051..c95471d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,7 @@ /// -import {globSync} from 'node:fs'; import {dirname, extname, relative, resolve} from 'node:path'; import {fileURLToPath} from 'node:url'; +import {globSync} from 'glob'; import {defineConfig} from 'vite'; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -27,6 +27,7 @@ export default defineConfig({ rollupOptions: { input: Object.fromEntries(files), output: { + generatedCode: 'es2015', preserveModules: true, }, }, @@ -40,6 +41,6 @@ export default defineConfig({ watch: false, }, resolve: { - alias: [{find: '@', replacement: resolve(__dirname, 'src/js')}], + alias: [{find: '~', replacement: resolve(__dirname, 'src/js')}], }, });