From 759faa7613f1b12341d4841e7ee02de4b33c2d34 Mon Sep 17 00:00:00 2001 From: Vlad Rindevich Date: Wed, 9 Oct 2024 13:16:39 +0300 Subject: [PATCH 1/7] chore: add typedoc to build documentation --- .eslintrc.json | 1 + .gitignore | 1 + package-lock.json | 650 +++++++++++++++++++++++++++++++++++++++ package.json | 5 +- src/resolver/resolver.ts | 4 +- src/resolver/types.d.ts | 6 + src/triggers/popstate.ts | 2 +- src/types.d.ts | 3 - tsconfig.json | 4 +- typedoc.json | 16 + 10 files changed, 684 insertions(+), 8 deletions(-) create mode 100644 typedoc.json diff --git a/.eslintrc.json b/.eslintrc.json index e7b69112..11926505 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -11,6 +11,7 @@ }, "plugins": ["tsdoc"], "rules": { + "@typescript-eslint/no-restricted-types": "off", "@typescript-eslint/no-invalid-void-type": "off", "@typescript-eslint/no-useless-template-literals": "off", "import/no-unassigned-import": "off", diff --git a/.gitignore b/.gitignore index 91a919b2..351e8d02 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ coverage .idea .vscode +.docs diff --git a/package-lock.json b/package-lock.json index e8d63e19..1c6a06b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ }, "devDependencies": { "@esm-bundle/chai": "^4.3.4-fix.0", + "@mxssfd/typedoc-theme": "^1.1.7", "@testing-library/dom": "^10.4.0", "@testing-library/user-event": "^14.5.2", "@types/chai-as-promised": "^8.0.1", @@ -56,6 +57,8 @@ "sinon-chai": "^4.0.0", "stylelint": "^16.9.0", "tsx": "^4.19.1", + "typedoc": "^0.26.8", + "typedoc-plugin-missing-exports": "^3.0.0", "typescript": "^5.6.2", "typescript-eslint": "^8.8.1", "vite": "5.4.8", @@ -1478,6 +1481,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@mxssfd/typedoc-theme": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@mxssfd/typedoc-theme/-/typedoc-theme-1.1.7.tgz", + "integrity": "sha512-dj4p0TjIoudD8j1u+Kf+KzcEc+je3IB/B1oohWcVJmMRfbw5uZYX6qOGHzfUzAPiU1pYe2u5Vo1IPqgLo58taA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "typedoc": "^0.26.7" + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -1927,6 +1943,62 @@ "dev": true, "license": "MIT" }, + "node_modules/@shikijs/core": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.22.0.tgz", + "integrity": "sha512-S8sMe4q71TJAW+qG93s5VaiihujRK6rqDFqBnxqvga/3LvqHEnxqBIOPkt//IdXVtHkQWKu4nOQNk0uBGicU7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-javascript": "1.22.0", + "@shikijs/engine-oniguruma": "1.22.0", + "@shikijs/types": "1.22.0", + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.3" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-1.22.0.tgz", + "integrity": "sha512-AeEtF4Gcck2dwBqCFUKYfsCq0s+eEbCEbkUuFou53NZ0sTGnJnJ/05KHQFZxpii5HMXbocV9URYVowOP2wH5kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.22.0", + "@shikijs/vscode-textmate": "^9.3.0", + "oniguruma-to-js": "0.4.3" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-1.22.0.tgz", + "integrity": "sha512-5iBVjhu/DYs1HB0BKsRRFipRrD7rqjxlWTj4F2Pf+nQSPqc3kcyqFFeZXnBMzDf0HdqaFVvhDRAGiYNvyLP+Mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "1.22.0", + "@shikijs/vscode-textmate": "^9.3.0" + } + }, + "node_modules/@shikijs/types": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-1.22.0.tgz", + "integrity": "sha512-Fw/Nr7FGFhlQqHfxzZY8Cwtwk5E9nKDUgeLjZgt3UuhcM3yJR9xj3ZGNravZZok8XmEZMiYkSMTPlPkULB8nww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-9.3.0.tgz", + "integrity": "sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==", + "dev": true, + "license": "MIT" + }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -2086,6 +2158,16 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -2124,6 +2206,16 @@ "@types/karma": "*" } }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/minimatch": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", @@ -2176,6 +2268,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.8.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz", @@ -3028,6 +3127,17 @@ ], "license": "CC-BY-4.0" }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chai": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", @@ -3089,6 +3199,28 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -3216,6 +3348,17 @@ "node": ">=0.1.90" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3528,6 +3671,20 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -5340,6 +5497,44 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-to-html": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz", + "integrity": "sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -5377,6 +5572,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/htmlparser2": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", @@ -6673,6 +6879,16 @@ "dev": true, "license": "MIT" }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -6823,6 +7039,13 @@ "yallist": "^3.0.2" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true, + "license": "MIT" + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -6872,6 +7095,24 @@ "node": ">=10" } }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, "node_modules/mathml-tag-names": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", @@ -6883,6 +7124,28 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdn-data": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", @@ -6890,6 +7153,13 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -6932,6 +7202,100 @@ "node": ">= 8" } }, + "node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -7685,6 +8049,19 @@ "wrappy": "1" } }, + "node_modules/oniguruma-to-js": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/oniguruma-to-js/-/oniguruma-to-js-0.4.3.tgz", + "integrity": "sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "regex": "^4.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -8128,6 +8505,17 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -8135,6 +8523,16 @@ "dev": true, "license": "MIT" }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qjobs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", @@ -8273,6 +8671,13 @@ "dev": true, "license": "MIT" }, + "node_modules/regex": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/regex/-/regex-4.3.3.tgz", + "integrity": "sha512-r/AadFO7owAq1QJVeZ/nq9jNS1vyZt+6t1p/E59B56Rn2GCya+gr1KSyOzNL/er+r+B7phv5jG2xU2Nz1YkmJg==", + "dev": true, + "license": "MIT" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", @@ -8643,6 +9048,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/shiki": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.22.0.tgz", + "integrity": "sha512-/t5LlhNs+UOKQCYBtl5ZsH/Vclz73GIqT2yQsCBygr8L/ppTdmpL4w3kPLoZJbMKVWtoG77Ue1feOjZfDxvMkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/core": "1.22.0", + "@shikijs/engine-javascript": "1.22.0", + "@shikijs/engine-oniguruma": "1.22.0", + "@shikijs/types": "1.22.0", + "@shikijs/vscode-textmate": "^9.3.0", + "@types/hast": "^3.0.4" + } + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -8882,6 +9302,17 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -9141,6 +9572,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -9665,6 +10111,17 @@ "node": ">=0.6" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -10305,6 +10762,65 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typedoc": { + "version": "0.26.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.8.tgz", + "integrity": "sha512-QBF0BMbnNeUc6U7pRHY7Jb8pjhmiNWZNQT8LU6uk9qP9t3goP9bJptdlNqMC0wBB2w9sQrxjZt835bpRSSq1LA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lunr": "^2.3.9", + "markdown-it": "^14.1.0", + "minimatch": "^9.0.5", + "shiki": "^1.16.2", + "yaml": "^2.5.1" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x" + } + }, + "node_modules/typedoc-plugin-missing-exports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-3.0.0.tgz", + "integrity": "sha512-R7D8fYrK34mBFZSlF1EqJxfqiUSlQSmyrCiQgTQD52nNm6+kUtqwiaqaNkuJ2rA2wBgWFecUA8JzHT7x2r7ePg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typedoc": "0.26.x" + } + }, + "node_modules/typedoc/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typedoc/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/typescript": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", @@ -10343,6 +10859,13 @@ } } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", @@ -10380,6 +10903,79 @@ "dev": true, "license": "MIT" }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -10489,6 +11085,36 @@ "node": ">= 0.8" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "5.4.8", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", @@ -11240,6 +11866,19 @@ "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -11342,6 +11981,17 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index 1292eb2c..154994f1 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "build": "npm-run-all --parallel build:*", "build:esbuild": "tsx scripts/build.ts", "build:copy-dts": "tsx scripts/copy-dts.ts", - "build:tsc": "tsc --emitDeclarationOnly --isolatedModules -p tsconfig.build.json", + "build:tsc": "tsc --emitDeclarationOnly -p tsconfig.build.json", "start": "vite", "test": "karma start karma.config.cjs", "test:watch": "npm run test -- --watch", @@ -58,6 +58,7 @@ }, "devDependencies": { "@esm-bundle/chai": "^4.3.4-fix.0", + "@mxssfd/typedoc-theme": "^1.1.7", "@testing-library/dom": "^10.4.0", "@testing-library/user-event": "^14.5.2", "@types/chai-as-promised": "^8.0.1", @@ -99,6 +100,8 @@ "sinon-chai": "^4.0.0", "stylelint": "^16.9.0", "tsx": "^4.19.1", + "typedoc": "^0.26.8", + "typedoc-plugin-missing-exports": "^3.0.0", "typescript": "^5.6.2", "typescript-eslint": "^8.8.1", "vite": "5.4.8", diff --git a/src/resolver/resolver.ts b/src/resolver/resolver.ts index 10495b94..53199f6d 100644 --- a/src/resolver/resolver.ts +++ b/src/resolver/resolver.ts @@ -104,7 +104,7 @@ export type ResolverOptions = Reado resolveRoute?: ResolveRouteCallback; }>; -export default class Resolver { +class Resolver { /** * The base URL for all routes in the router instance. By default, * if the base element exists in the ``, vaadin-router @@ -334,3 +334,5 @@ export default class Resolver = Readon commands: never, ): MaybePromise>>; }> & { + /** @internal @hidden */ __children?: ReadonlyArray>; + /** @internal @hidden */ __synthetic?: true; children?: ReadonlyArray> | ChildrenCallback; parent?: Route; @@ -72,9 +74,13 @@ export type RouteContext>, ): Promise>>; }> & { + /** @internal @hidden */ __divergedChainIndex?: number; + /** @internal @hidden */ __redirectCount?: number; + /** @internal @hidden */ __renderId: number; + /** @internal @hidden */ __skipAttach?: boolean; result?: T | RouteContext; } & ResolveContext; diff --git a/src/triggers/popstate.ts b/src/triggers/popstate.ts index c68871fc..08caf15c 100644 --- a/src/triggers/popstate.ts +++ b/src/triggers/popstate.ts @@ -1,5 +1,5 @@ -import { fireRouterEvent } from '../utils.js'; import type { NavigationTrigger } from '../types.js'; +import { fireRouterEvent } from '../utils.js'; function vaadinRouterGlobalPopstateHandler(event: PopStateEvent) { if (event.state === 'vaadin-router-ignore') { diff --git a/src/types.d.ts b/src/types.d.ts index 717bf2e4..377095d1 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -87,9 +87,6 @@ export type ContextExtension = Readonl chain?: Array>; }> & C; -// Readonly<{ -// next(resume?: boolean): Promise; -// }>; export type ChildrenCallback = _ChildrenCallback< ActionValue, diff --git a/tsconfig.json b/tsconfig.json index 70b31b6a..4081b5e8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,6 +20,6 @@ "useDefineForClassFields": true, "useUnknownInCatchVariables": true }, - "include": ["scripts", "src/**/*", "test/**/*", "./vite.config.ts", "./*.cjs"], - "exclude": ["scripts/**/*.js"] + "include": ["scripts/**/*", "src/**/*", "test/**/*", "./vite.config.ts", "./*.cjs"], + "exclude": ["dist/**/*", "scripts/**/*.js"] } diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 00000000..117755d6 --- /dev/null +++ b/typedoc.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "entryPoints": ["src/index.ts"], + "excludePrivate": true, + "out": ".docs", + "sort": ["source-order"], + "plugin": ["@mxssfd/typedoc-theme", "typedoc-plugin-missing-exports"], + "theme": "my-theme", + "tsconfig": "tsconfig.build.json", + "useTsLinkResolution": false, + "validation": { + "notExported": true, + "invalidLink": true, + "notDocumented": true + } +} From 9e294bb82ef5374ad1b05d9632d73a32bae2d408 Mon Sep 17 00:00:00 2001 From: Vlad Rindevich Date: Wed, 9 Oct 2024 18:46:18 +0300 Subject: [PATCH 2/7] docs: add more docs --- src/resolver/resolveRoute.ts | 1 + src/resolver/resolver.ts | 31 ++++++ src/resolver/types.d.ts | 190 +++++++++++++++++++++++++++++++++-- src/resolver/utils.ts | 59 ++++++++--- src/router.ts | 7 +- src/types.d.ts | 88 +++++++++++++++- src/utils.ts | 23 ++++- 7 files changed, 370 insertions(+), 29 deletions(-) diff --git a/src/resolver/resolveRoute.ts b/src/resolver/resolveRoute.ts index 72101aa9..5a9edffb 100644 --- a/src/resolver/resolveRoute.ts +++ b/src/resolver/resolveRoute.ts @@ -9,6 +9,7 @@ import type { ActionResult, AnyObject, MaybePromise, RouteContext } from './types.js'; import { isFunction } from './utils.js'; +/** @internal */ // eslint-disable-next-line @typescript-eslint/no-invalid-void-type export default function resolveRoute( context: RouteContext, diff --git a/src/resolver/resolver.ts b/src/resolver/resolver.ts index 53199f6d..561ec104 100644 --- a/src/resolver/resolver.ts +++ b/src/resolver/resolver.ts @@ -50,8 +50,18 @@ export interface ResolutionErrorOptions extends ErrorOptions { code?: number; } +/** + * An error that is thrown when a route resolution fails. + */ export class ResolutionError extends Error { + /** + * A HTTP status code associated with the error. + */ readonly code?: number; + + /** + * The context object associated with the route that was not found. + */ readonly context: RouteContext; constructor(context: RouteContext, options?: ResolutionErrorOptions) { @@ -65,6 +75,9 @@ export class ResolutionError( } } +/** + * A callback function that handles errors during route resolution. + */ export type ErrorHandlerCallback = (error: unknown) => T; +/** + * A callback function that resolves a route. It is used as a fallback in case + * the route is not correctly resolved. + */ export type ResolveRouteCallback = ( context: RouteContext, ) => MaybePromise>>; +/** + * Options for the constructor of the `Resolver` class. + * + * @interface + */ export type ResolverOptions = Readonly<{ baseUrl?: string; context?: RouteContext; @@ -160,10 +185,16 @@ class Resolver { return this.#root as Route; } + /** + * The current route context. + */ get context(): RouteContext { return this.#context; } diff --git a/src/resolver/types.d.ts b/src/resolver/types.d.ts index 3922d7c5..ee3c716e 100644 --- a/src/resolver/types.d.ts +++ b/src/resolver/types.d.ts @@ -1,3 +1,5 @@ +/** @module resolver */ + import type { EmptyObject } from 'type-fest'; import type Resolver from './resolver.js'; import type { NotFoundResult } from './utils.js'; @@ -5,38 +7,105 @@ import type { NotFoundResult } from './utils.js'; /* ======================== * Common Types * ======================== */ + +/** + * Represents any object. This is useful as a type guard in generics. + */ export type AnyObject = Readonly>; +/** + * Represents either a value or a promise of a value. + * + * @typeParam T - The type of the value. + */ export type MaybePromise = Promise | T; -// eslint-disable-next-line @typescript-eslint/no-invalid-void-type +/** + * A result of a {@link Route.action}. + * + * @typeParam T - The type of the result. + */ export type ActionResult = T | NotFoundResult | null | undefined | void; /* ======================== * Resolver-Specific Types * ======================== */ + +/** + * A function that dynamically creates children of a route. + * + * @typeParam T - The type of the result produced by the route. + * @typeParam R - The type of additional route-specific data. Defaults to an + * empty object. + * @typeParam C - The type of user-defined context-specific data. Defaults to an + * empty object. + * + * @param context - The context of the current route. + * + * @deprecated The route children callback is deprecated and will be removed in + * the next major version. + * + * @interface + */ export type ChildrenCallback = ( context: RouteChildrenContext, ) => MaybePromise | ReadonlyArray> | void>; +/** + * Defines a single route. + * + * A route represents a single or multiple sections in the URL. It defines the + * behavior of a page in response to URL updates. A route can act as a content + * producer or as middleware for child routes. + * + * @typeParam T - The type of the result produced by the route. + * @typeParam R - The type of additional route-specific data. Defaults to an + * empty object. + * @typeParam C - The type of user-defined context-specific data. Defaults to an + * empty object. + * + * @internal + */ export type BasicRoutePart = Readonly<{ + /** + * The name of the route. + */ name?: string; + /** + * The path pattern that the route matches. + */ path: string; + /** + * An action that is executed when the route is resolved. + * + * Actions are executed recursively from the root route to the child route and + * can either produce content or perform actions before or after the child's + * action. + * + * @param context - The context of the current route. + * + * @returns The result of the route resolution. It could be either a value + * produced by the action or a new context to continue the resolution process. + */ action?( this: Route, context: RouteContext, commands: never, ): MaybePromise>>; }> & { - /** @internal @hidden */ + /** @internal */ __children?: ReadonlyArray>; - /** @internal @hidden */ + /** @internal */ __synthetic?: true; children?: ReadonlyArray> | ChildrenCallback; parent?: Route; fullPath?: string; }; +/** + * {@inheritDoc BasicRoutePart} + * @interface + */ export type Route = BasicRoutePart< T, R, @@ -44,47 +113,141 @@ export type Route & R; +/** + * A matched route with its associated path. + * + * @typeParam T - The type of the result produced by the route. + * @typeParam R - The type of additional route-specific data. Defaults to an + * empty object. + * @typeParam C - The type of user-defined context-specific data. Defaults to an + * empty object. + * + * @internal + */ export type Match = Readonly<{ + /** The path of the matched route. */ path: string; + /** The route object associated with the matched path. */ route?: Route; }>; +/** + * An item of the resolved route sequence. + * + * @typeParam T - The type of the result produced by the route. + * @typeParam R - The type of additional route-specific data. Defaults to an + * empty object. + * @typeParam C - The type of user-defined context-specific data. Defaults to an + * empty object. + * + * @interface + */ export type ChainItem = { + /** A DOM element associated with the route. */ element?: Element; + /** The path of the route. */ path: string; + /** The route object containing route-specific information. */ route: Route; }; +/** + * The context for a `resolve` operation that can be extended with + * the user-defined properties. + * + * @typeParam C - The type of user-defined context-specific data. Defaults to an + * empty object. + * + * @interface + */ export type ResolveContext = Readonly<{ + /** The current location. */ pathname: string; }> & C; +/** + * The context for a {@link Route.action} that could be used to access the + * route-specific data during the resolution process. + * + * @typeParam T - The type of the result produced by the route. + * @typeParam R - The type of additional route-specific data. Defaults to + * `EmptyObject`. + * @typeParam C - The type of additional context-specific data. Defaults to + * `EmptyObject`. + * + * @interface + */ export type RouteContext = Readonly<{ + /** + * The {@link https://developer.mozilla.org/en-US/docs/Web/API/URL/hash | hash} + * fragment of the URL. + */ hash?: string; + /** + * The {@link https://developer.mozilla.org/en-US/docs/Web/API/URL/search | search} + * query string of the URL. + */ search?: string; + /** + * The sequence of the resolved route items, so said the path from the root + * route to the current route. + */ chain?: Array>; + /** + * The parameters resolved from the current URL. + */ params: IndexedParams; + /** + * The resolver instance. + */ resolver?: Resolver; + /** + * The URL from which a redirect occurred. + */ redirectFrom?: string; + /** + * The current route. + */ route: Route; + /** + * Proceed to the next route in the chain, down the route tree. + */ next( resume?: boolean, parent?: Route, prevResult?: ActionResult>, ): Promise>>; }> & { - /** @internal @hidden */ + /** @internal */ __divergedChainIndex?: number; - /** @internal @hidden */ + /** @internal */ __redirectCount?: number; - /** @internal @hidden */ + /** @internal */ __renderId: number; - /** @internal @hidden */ + /** @internal */ __skipAttach?: boolean; + /** + * The result of the route resolution. It could be either a value produced by + * the {@link Route.action} or a new context to continue the resolution + * process. + */ result?: T | RouteContext; } & ResolveContext; +/** + * Represents the context that is accessible from the route children callback. + * It is the a {@link RouteContext} without the 'next' property. + * + * @typeParam T - The type of the route parameters. + * @typeParam R - The type of the route's resolved data. Defaults to `EmptyObject`. + * @typeParam C - The type of the route's context. Defaults to `EmptyObject`. + * + * @deprecated The route children callback is deprecated and will be removed in + * the next major version. + * + * @interface + */ export type RouteChildrenContext = Omit< RouteContext, 'next' @@ -92,8 +255,21 @@ export type RouteChildrenContext>; export type Params = IndexedParams | ParamValue[]; diff --git a/src/resolver/utils.ts b/src/resolver/utils.ts index 5542ac33..9530278e 100644 --- a/src/resolver/utils.ts +++ b/src/resolver/utils.ts @@ -1,55 +1,82 @@ import type { AnyObject, ChildrenCallback, Route, RouteContext } from './types.js'; +/** + * {@inheritDoc "".NotFoundError} + */ +export const notFoundResult = Symbol('NotFoundResult'); + +/** + * A special result to be returned from a route action to indicate that the + * route was not found. + */ +export type NotFoundResult = typeof notFoundResult; + +/** + * An error to be thrown when a route is not found. + */ +export class NotFoundError extends Error { + /** + * The HTTP status code to be used when the route is not found. + */ + readonly code: number; + + /** + * The context object associated with the route that was not found. + */ + readonly context: RouteContext; + + constructor(context: RouteContext) { + // eslint-disable-next-line @typescript-eslint/no-use-before-define + super(log(`Page not found (${context.pathname})`)); + this.context = context; + this.code = 404; + } +} + +/** @internal */ export function isObject(o: unknown): o is object { // guard against null passing the typeof check return typeof o === 'object' && !!o; } -// eslint-disable-next-line @typescript-eslint/ban-types -export function isFunction(f: unknown): f is Function { +/** @internal */ +export function isFunction unknown>(f: unknown): f is F { return typeof f === 'function'; } +/** @internal */ export function isString(s: unknown): s is string { return typeof s === 'string'; } +/** @internal */ export function toArray(value: T | readonly T[] = []): readonly T[] { return Array.isArray(value) ? value : [value]; } +/** @internal */ export function log(msg: string): string { return `[Vaadin.Router] ${msg}`; } -export class NotFoundError extends Error { - readonly code: number; - readonly context: RouteContext; - - constructor(context: RouteContext) { - super(log(`Page not found (${context.pathname})`)); - this.context = context; - this.code = 404; - } -} - -export const notFoundResult = Symbol('NotFoundResult'); -export type NotFoundResult = typeof notFoundResult; - +/** @internal */ export function getNotFoundError( context: RouteContext, ): NotFoundError { return new NotFoundError(context); } +/** @internal */ export function resolvePath(path?: string | readonly string[]): string { return (Array.isArray(path) ? path[0] : path) ?? ''; } +/** @internal */ export function getRoutePath(route: Route | undefined): string { return resolvePath(route?.path); } +/** @internal */ export function unwrapChildren( children: ChildrenCallback | ReadonlyArray> | undefined, ): ReadonlyArray> | undefined { diff --git a/src/router.ts b/src/router.ts index 0d65bdcd..fa169c3a 100644 --- a/src/router.ts +++ b/src/router.ts @@ -125,6 +125,8 @@ export class Router; #urlForName?: ReturnType; @@ -325,7 +327,6 @@ export class Router | ReadonlyArray>, skipRender = false, @@ -699,7 +700,9 @@ export class Router) => Promise & RedirectContextInfo>; + declare ['resolve']: ( + contextOrPathname: RouteContext | string, + ) => Promise & RedirectContextInfo>; async #redirect( redirectData: RedirectContextInfo, diff --git a/src/types.d.ts b/src/types.d.ts index 377095d1..e7f48d55 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -2,6 +2,7 @@ import type { EmptyObject, RequireAtLeastOne } from 'type-fest'; import type { ResolutionError, ResolverOptions } from './resolver/resolver.js'; import type { ActionResult as _ActionResult, + AnyObject, ChildrenCallback as _ChildrenCallback, ChainItem as _ChainItem, RouteChildrenContext as _RouteChildrenContext, @@ -15,23 +16,41 @@ import type { } from './resolver/types.js'; import type { Router } from './router.js'; -export type { ResolutionError, IndexedParams, Params, ParamValue, PrimitiveParamValue }; +export type { AnyObject, ResolutionError, IndexedParams, Params, ParamValue, PrimitiveParamValue }; +/** + * A custom event that is triggered when the location changes. + */ export type VaadinRouterLocationChangedEvent = CustomEvent< Readonly<{ + /** The new location after the change */ location: RouterLocation; + /** The router instance that triggered the event */ router: Router; }> >; +/** + * A custom event triggered by an error occurred during route resolution. + * + * @typeParam R - The type of additional route-specific data. Defaults to an + * empty object. + * @typeParam C - The type of user-defined context-specific data. Defaults to an + * empty object. + */ export type VaadinRouterErrorEvent = CustomEvent< Readonly<{ + /** The error object. */ error: ResolutionError; + /** The router instance that triggered the error event. */ router: Router; }> & RouteContext >; +/** + * A custom event triggered when the user navigates to a new location. + */ export type VaadinRouterGoEvent = CustomEvent; declare global { @@ -46,33 +65,63 @@ declare global { } } -export type AnyObject = Record; - +/** + * A context information for a redirect operation. + */ export type RedirectContextInfo = Readonly<{ + /** The original path from which the redirect is happening. */ from: string; + /** An object containing URL parameters related to the redirect. */ params: IndexedParams; + /** The pathname of the new URL to which the redirect is directed. */ pathname: string; }>; +/** + * A result that can be returned from a route action to request a redirect to + * a different location. + */ export interface RedirectResult { + /** The path info to redirect to. */ readonly redirect: RedirectContextInfo; } +/** + * A result that can be returned from a route action to prevent the navigation. + */ export interface PreventResult { + /** A flag indicating that the navigation should be prevented. */ readonly cancel: true; } +/** + * A controller to set up and tear down navigation event listeners. + */ export interface NavigationTrigger { + /** Sets up navigation listeners. */ activate(): void; + /** Tears down navigation listeners. */ inactivate(): void; } +/** + * A value of a result that can be returned from the router action. + */ export type ActionValue = HTMLElement | PreventResult | RedirectResult; +/** + * A result of the {@link RouteContext.next} function. + */ export type NextResult = _ActionResult>; +/** + * A result of the {@link RouteExtension.action | Route.action}. + */ export type ActionResult = _ActionResult; +/** + * {@inheritDoc "".ChainItem} + */ export type ChainItem = _ChainItem< ActionValue, RouteExtension, @@ -82,18 +131,35 @@ export type ChainItem = _ChainItem< element?: WebComponentInterface; }>; +/** + * A specialized extension for the internal Resolver's + * {@link "".Context | Context} object that redefines some types to + * make it compatible with the {@link Router}. + * + * @internal + */ export type ContextExtension = Readonly<{ resolver?: Router; chain?: Array>; }> & C; +/** + * {@inheritDoc "".ChildrenCallback} + */ export type ChildrenCallback = _ChildrenCallback< ActionValue, RouteExtension, ContextExtension >; +/** + * An specialized extension for the internal Resolver's {@link "".Route | Route} + * object that redefines some types to make it compatible with the + * {@link Router}. + * + * @internal + */ export type RouteExtension = RequireAtLeastOne<{ children?: ChildrenCallback | ReadonlyArray>; component?: string; @@ -107,23 +173,39 @@ export type RouteExtension = RequireAt animate?: AnimateCustomClasses | boolean; } & R; +/** + * {@inheritDoc "".RouteContext} + * @interface + */ export type RouteContext = _RouteContext< ActionValue, RouteExtension, ContextExtension >; +/** + * {@inheritDoc "".RouteChildrenContext} + * @interface + */ export type RouteChildrenContext< R extends AnyObject = EmptyObject, C extends AnyObject = EmptyObject, > = _RouteChildrenContext, ContextExtension>; +/** + * {@inheritDoc "".Route} + * @interface + */ export type Route = _Route< ActionValue, RouteExtension, ContextExtension >; +/** + * {@inheritDoc "".ResolverOptions} + * @interface + */ export type RouterOptions = ResolverOptions< ActionValue, RouteExtension, diff --git a/src/utils.ts b/src/utils.ts index e7fa86fe..b1542ede 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -14,6 +14,7 @@ import type { WebComponentInterface, } from './types.js'; +/** @internal */ export function ensureRoute(route?: Route): void { if (!route || !isString(route.path)) { throw new Error( @@ -50,12 +51,14 @@ export function ensureRoute(route?: Ro } } +/** @internal */ export function ensureRoutes( routes: Route | ReadonlyArray>, ): void { toArray(routes).forEach((route) => ensureRoute(route)); } +/** @internal */ export function copyContextWithoutNext({ next: _, ...context @@ -63,6 +66,7 @@ export function copyContextWithoutNext return context; } +/** @internal */ export function getPathnameForRouter( pathname: string, router: Resolver, @@ -72,6 +76,7 @@ export function getPathnameForRouter>): string { return pathItems .map((pathItem) => pathItem.path) @@ -83,12 +88,15 @@ export function getMatchedPath(pathItems: ReadonlyArray(chain: ReadonlyArray>): string { return getMatchedPath(chain.map((chainItem) => chainItem.route)); } +/** @internal */ export type ResolverOnlyContext = Readonly<{ resolver: Router }>; +/** @internal */ type PartialRouteContext = Readonly<{ chain?: ReadonlyArray>; hash?: string; @@ -99,6 +107,7 @@ type PartialRouteContext = Readonly<{ search?: string; }>; +/** @internal */ export function createLocation({ resolver, }: ResolverOnlyContext): RouterLocation; @@ -114,7 +123,12 @@ export function createLocation( return { baseUrl: resolver?.baseUrl ?? '', getUrl: (userParams = {}) => - resolver ? getPathnameForRouter(compile(getRoutePath(chain))({ ...params, ...userParams } as Partial>), resolver) : '', + resolver + ? getPathnameForRouter( + compile(getRoutePath(chain))({ ...params, ...userParams } as Partial>), + resolver, + ) + : '', hash, params, pathname, @@ -126,6 +140,7 @@ export function createLocation( }; } +/** @internal */ export function createRedirect( context: RouteContext, pathname: string, @@ -140,6 +155,7 @@ export function createRedirect( }; } +/** @internal */ export function renderElement>( context: RouteContext, element: E, @@ -154,6 +170,7 @@ export function renderElement( callback: ((this: O, ...args: A) => R) | undefined, thisArg: O, @@ -166,6 +183,7 @@ export function maybeCall( return undefined; } +/** @internal */ export function amend< A extends readonly unknown[], N extends keyof O, @@ -180,6 +198,7 @@ export function amend< }; } +/** @internal */ export function processNewChildren( newChildren: Route | ReadonlyArray> | undefined | void, route: Route, @@ -199,10 +218,12 @@ export function processNewChildren( route.__children = children; } +/** @internal */ export function fireRouterEvent(type: string, detail: unknown): boolean { return !window.dispatchEvent(new CustomEvent(`vaadin-router-${type}`, { cancelable: type === 'go', detail })); } +/** @internal */ export function logValue(value: unknown): string { if (typeof value !== 'object') { return String(value); From 0e26076b1acdefc8e86ab8acf39ac57ffe8eee5f Mon Sep 17 00:00:00 2001 From: Vlad Rindevich Date: Wed, 9 Oct 2024 18:46:39 +0300 Subject: [PATCH 3/7] chore: add docs workflow --- .github/workflows/docs.yml | 42 ++++++++++++++++++++++++++++++++++++++ tsdoc.json | 13 ++++++++++++ typedoc.json | 3 ++- 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/docs.yml create mode 100644 tsdoc.json diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..7548481c --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,42 @@ +name: Publish Docs + +on: + push: + branches: ['main'] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: 'pages' + cancel-in-progress: false + +jobs: + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Use NodeJS LTS + uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install + run: npm ci + - name: Build Docs + run: npm run docs + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: './.docs' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/tsdoc.json b/tsdoc.json new file mode 100644 index 00000000..8d551f9c --- /dev/null +++ b/tsdoc.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "tagDefinitions": [ + { + "tagName": "@interface", + "syntaxKind": "modifier" + }, + { + "tagName": "@event", + "syntaxKind": "block" + } + ] +} diff --git a/typedoc.json b/typedoc.json index 117755d6..931610f2 100644 --- a/typedoc.json +++ b/typedoc.json @@ -3,11 +3,12 @@ "entryPoints": ["src/index.ts"], "excludePrivate": true, "out": ".docs", - "sort": ["source-order"], + "sort": ["alphabetical"], "plugin": ["@mxssfd/typedoc-theme", "typedoc-plugin-missing-exports"], "theme": "my-theme", "tsconfig": "tsconfig.build.json", "useTsLinkResolution": false, + "excludeInternal": true, "validation": { "notExported": true, "invalidLink": true, From 90a6c4a734cd0162e13c0c4d368659a55921fc01 Mon Sep 17 00:00:00 2001 From: Vlad Rindevich Date: Fri, 11 Oct 2024 17:49:06 +0300 Subject: [PATCH 4/7] refactor: small improvements --- src/resolver/types.d.ts | 4 ++-- src/types.d.ts | 12 ++++++++++++ tsconfig.json | 2 +- typedoc.json | 4 ++-- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/resolver/types.d.ts b/src/resolver/types.d.ts index ee3c716e..17107345 100644 --- a/src/resolver/types.d.ts +++ b/src/resolver/types.d.ts @@ -63,8 +63,6 @@ export type ChildrenCallback = ( * empty object. * @typeParam C - The type of user-defined context-specific data. Defaults to an * empty object. - * - * @internal */ export type BasicRoutePart = Readonly<{ /** @@ -86,6 +84,8 @@ export type BasicRoutePart = Readon * * @returns The result of the route resolution. It could be either a value * produced by the action or a new context to continue the resolution process. + * + * @internal */ action?( this: Route, diff --git a/src/types.d.ts b/src/types.d.ts index e7f48d55..b4656241 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -164,6 +164,18 @@ export type RouteExtension = RequireAt children?: ChildrenCallback | ReadonlyArray>; component?: string; redirect?: string; + /** + * An action that is executed when the route is resolved. + * + * Actions are executed recursively from the root route to the child route and + * can either produce content or perform actions before or after the child's + * action. + * + * @param context - The context of the current route. + * + * @returns The result of the route resolution. It could be either a value + * produced by the action or a new context to continue the resolution process. + */ action?( this: Route, context: RouteContext, diff --git a/tsconfig.json b/tsconfig.json index 4081b5e8..4e573d7e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,5 +21,5 @@ "useUnknownInCatchVariables": true }, "include": ["scripts/**/*", "src/**/*", "test/**/*", "./vite.config.ts", "./*.cjs"], - "exclude": ["dist/**/*", "scripts/**/*.js"] + "exclude": ["node_modules", "dist/**/*", "scripts/**/*.js"] } diff --git a/typedoc.json b/typedoc.json index 931610f2..bbf3fa53 100644 --- a/typedoc.json +++ b/typedoc.json @@ -4,11 +4,11 @@ "excludePrivate": true, "out": ".docs", "sort": ["alphabetical"], - "plugin": ["@mxssfd/typedoc-theme", "typedoc-plugin-missing-exports"], - "theme": "my-theme", + "plugin": ["typedoc-plugin-missing-exports"], "tsconfig": "tsconfig.build.json", "useTsLinkResolution": false, "excludeInternal": true, + "excludeExternals": true, "validation": { "notExported": true, "invalidLink": true, From b4b3a23873bc9cdaea7cd2ace35f607c220162a6 Mon Sep 17 00:00:00 2001 From: Vlad Rindevich Date: Mon, 14 Oct 2024 16:44:01 +0300 Subject: [PATCH 5/7] refactor: improve typings system --- __src/types/Commands.d.ts | 22 +++++++++++++++++++++ package.json | 2 +- src/resolver/generateUrls.ts | 10 +++++----- src/resolver/matchRoute.ts | 8 ++++---- src/resolver/resolveRoute.ts | 4 ++-- src/resolver/resolver.ts | 29 ++++++++++------------------ src/resolver/types.d.ts | 28 ++++++++------------------- src/resolver/utils.ts | 10 +++++----- src/router.ts | 3 +-- src/types.d.ts | 29 ++++++++++++++-------------- src/utils.ts | 29 ++++++++++++++-------------- test/router/dynamic-redirect.spec.ts | 3 --- test/router/test-utils.ts | 3 +-- tsconfig.json | 3 +-- 14 files changed, 88 insertions(+), 95 deletions(-) create mode 100644 __src/types/Commands.d.ts diff --git a/__src/types/Commands.d.ts b/__src/types/Commands.d.ts new file mode 100644 index 00000000..f706c36d --- /dev/null +++ b/__src/types/Commands.d.ts @@ -0,0 +1,22 @@ +import type { EmptyObject } from '@ausginer/router'; +import type { PreventResult, RedirectResult } from './general.js'; + +export interface Commands { + component(name: K): HTMLElementTagNameMap[K]; + component(name: string): HTMLElement; + /** + * Function that creates a special object that can be returned to abort + * the current navigation and fall back to the last one. If there is no + * existing one, an exception is thrown. + */ + prevent(): PreventResult; + redirect(path: string): RedirectResult; +} + +export type Command = { + [$command]: true; +}; + +export type EmptyCommands = EmptyObject; +export type PreventCommands = Pick; +export type PreventAndRedirectCommands = Pick; diff --git a/package.json b/package.json index 154994f1..da700a30 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "test": "karma start karma.config.cjs", "test:watch": "npm run test -- --watch", "test:coverage": "npm run test -- --coverage", - "docs": "npm run build && echo \"not implemented\" >&2 && exit 1", + "docs": "typedoc", "browserslist": "browserslist && browserslist --coverage", "prepack": "npm run clean:build", "prepare": "npm run build", diff --git a/src/resolver/generateUrls.ts b/src/resolver/generateUrls.ts index 93efbc5b..31fe7578 100644 --- a/src/resolver/generateUrls.ts +++ b/src/resolver/generateUrls.ts @@ -10,12 +10,12 @@ import { parse, type ParseOptions, type Token, tokensToFunction, type TokensToFunctionOptions } from 'path-to-regexp'; import type { EmptyObject, Writable } from 'type-fest'; import Resolver from './resolver.js'; -import type { AnyObject, ChildrenCallback, IndexedParams, Params, Route } from './types.js'; +import type { ChildrenCallback, IndexedParams, Params, Route } from './types.js'; import { getRoutePath, isString } from './utils.js'; export type UrlParams = Readonly | number | string>>; -function cacheRoutes( +function cacheRoutes( routesByName: Map>>, route: Route, routes?: ReadonlyArray> | ChildrenCallback, @@ -38,7 +38,7 @@ function cacheRoutes( } } -function getRouteByName( +function getRouteByName( routesByName: Map>>, routeName: string, ): Route | undefined { @@ -57,7 +57,7 @@ function getRouteByName( export type StringifyQueryParams = (params: UrlParams) => string; -export type GenerateUrlOptions = ParseOptions & +export type GenerateUrlOptions = ParseOptions & Readonly<{ /** * Add a query string to generated url based on unknown route params. @@ -78,7 +78,7 @@ type RouteCacheRecord = Readonly<{ export type UrlGenerator = (routeName: string, params?: Params) => string; -function generateUrls( +function generateUrls( resolver: Resolver, options: GenerateUrlOptions = {}, ): UrlGenerator { diff --git a/src/resolver/matchRoute.ts b/src/resolver/matchRoute.ts index b1b5d386..5c0092d8 100644 --- a/src/resolver/matchRoute.ts +++ b/src/resolver/matchRoute.ts @@ -9,15 +9,15 @@ import type { Key } from 'path-to-regexp'; import matchPath, { type Match } from './matchPath.js'; -import type { AnyObject, IndexedParams, Route } from './types.js'; +import type { IndexedParams, Route } from './types.js'; import { getRoutePath, unwrapChildren } from './utils.js'; -export type MatchWithRoute = Match & +export type MatchWithRoute = Match & Readonly<{ route: Route; }>; -type RouteMatchIterator = Iterator< +type RouteMatchIterator = Iterator< MatchWithRoute, undefined, Route | undefined @@ -69,7 +69,7 @@ type RouteMatchIterator = Iterator< * Prefix matching can be enabled also by `children: true`. */ // eslint-disable-next-line @typescript-eslint/max-params -function matchRoute( +function matchRoute( route: Route, pathname: string, ignoreLeadingSlash?: boolean, diff --git a/src/resolver/resolveRoute.ts b/src/resolver/resolveRoute.ts index 5a9edffb..9e8b43a2 100644 --- a/src/resolver/resolveRoute.ts +++ b/src/resolver/resolveRoute.ts @@ -6,12 +6,12 @@ * This source code is licensed under the MIT license found in the * LICENSE.txt file in the root directory of this source tree. */ -import type { ActionResult, AnyObject, MaybePromise, RouteContext } from './types.js'; +import type { ActionResult, MaybePromise, RouteContext } from './types.js'; import { isFunction } from './utils.js'; /** @internal */ // eslint-disable-next-line @typescript-eslint/no-invalid-void-type -export default function resolveRoute( +export default function resolveRoute( context: RouteContext, ): MaybePromise>> { if (isFunction(context.route.action)) { diff --git a/src/resolver/resolver.ts b/src/resolver/resolver.ts index 561ec104..42f27a68 100644 --- a/src/resolver/resolver.ts +++ b/src/resolver/resolver.ts @@ -9,19 +9,10 @@ import type { EmptyObject } from 'type-fest'; import matchRoute, { type MatchWithRoute } from './matchRoute.js'; import defaultResolveRoute from './resolveRoute.js'; -import type { - ActionResult, - AnyObject, - BasicRoutePart, - Match, - MaybePromise, - ResolveContext, - Route, - RouteContext, -} from './types.js'; +import type { ActionResult, RouteBase, Match, MaybePromise, ResolveContext, Route, RouteContext } from './types.js'; import { getNotFoundError, getRoutePath, isString, NotFoundError, notFoundResult, toArray } from './utils.js'; -function isDescendantRoute( +function isDescendantRoute( route?: Route, maybeParent?: Route, ) { @@ -35,7 +26,7 @@ function isDescendantRoute( return false; } -function isRouteContext(value: unknown): value is RouteContext { +function isRouteContext(value: unknown): value is RouteContext { return ( !!value && typeof value === 'object' && @@ -53,7 +44,7 @@ export interface ResolutionErrorOptions extends ErrorOptions { /** * An error that is thrown when a route resolution fails. */ -export class ResolutionError extends Error { +export class ResolutionError extends Error { /** * A HTTP status code associated with the error. */ @@ -83,7 +74,7 @@ export class ResolutionError( +function updateChainForRoute( context: RouteContext, match: Match, ) { @@ -113,23 +104,23 @@ export type ErrorHandlerCallback = (error: unknown) => T; * A callback function that resolves a route. It is used as a fallback in case * the route is not correctly resolved. */ -export type ResolveRouteCallback = ( +export type ResolveRouteCallback = ( context: RouteContext, ) => MaybePromise>>; /** * Options for the constructor of the `Resolver` class. - * + * * @interface */ -export type ResolverOptions = Readonly<{ +export type ResolverOptions = Readonly<{ baseUrl?: string; context?: RouteContext; errorHandler?: ErrorHandlerCallback; resolveRoute?: ResolveRouteCallback; }>; -class Resolver { +class Resolver { /** * The base URL for all routes in the router instance. By default, * if the base element exists in the ``, vaadin-router @@ -140,7 +131,7 @@ class Resolver; readonly errorHandler?: ErrorHandlerCallback; readonly resolveRoute: ResolveRouteCallback; - readonly #root: BasicRoutePart; + readonly #root: RouteBase; constructor(routes: ReadonlyArray> | Route, options?: ResolverOptions); constructor( diff --git a/src/resolver/types.d.ts b/src/resolver/types.d.ts index 17107345..c6973d4b 100644 --- a/src/resolver/types.d.ts +++ b/src/resolver/types.d.ts @@ -1,5 +1,3 @@ -/** @module resolver */ - import type { EmptyObject } from 'type-fest'; import type Resolver from './resolver.js'; import type { NotFoundResult } from './utils.js'; @@ -8,11 +6,6 @@ import type { NotFoundResult } from './utils.js'; * Common Types * ======================== */ -/** - * Represents any object. This is useful as a type guard in generics. - */ -export type AnyObject = Readonly>; - /** * Represents either a value or a promise of a value. * @@ -47,7 +40,7 @@ export type ActionResult = T | NotFoundResult | null | undefined | void; * * @interface */ -export type ChildrenCallback = ( +export type ChildrenCallback = ( context: RouteChildrenContext, ) => MaybePromise | ReadonlyArray> | void>; @@ -64,7 +57,7 @@ export type ChildrenCallback = ( * @typeParam C - The type of user-defined context-specific data. Defaults to an * empty object. */ -export type BasicRoutePart = Readonly<{ +export type RouteBase = Readonly<{ /** * The name of the route. */ @@ -106,12 +99,7 @@ export type BasicRoutePart = Readon * {@inheritDoc BasicRoutePart} * @interface */ -export type Route = BasicRoutePart< - T, - R, - C -> & - R; +export type Route = RouteBase & R; /** * A matched route with its associated path. @@ -124,7 +112,7 @@ export type Route = Readonly<{ +export type Match = Readonly<{ /** The path of the matched route. */ path: string; /** The route object associated with the matched path. */ @@ -142,7 +130,7 @@ export type Match = Readonly<{ * * @interface */ -export type ChainItem = { +export type ChainItem = { /** A DOM element associated with the route. */ element?: Element; /** The path of the route. */ @@ -160,7 +148,7 @@ export type ChainItem = { * * @interface */ -export type ResolveContext = Readonly<{ +export type ResolveContext = Readonly<{ /** The current location. */ pathname: string; }> & @@ -178,7 +166,7 @@ export type ResolveContext = Readonly<{ * * @interface */ -export type RouteContext = Readonly<{ +export type RouteContext = Readonly<{ /** * The {@link https://developer.mozilla.org/en-US/docs/Web/API/URL/hash | hash} * fragment of the URL. @@ -248,7 +236,7 @@ export type RouteContext = Omit< +export type RouteChildrenContext = Omit< RouteContext, 'next' >; diff --git a/src/resolver/utils.ts b/src/resolver/utils.ts index 9530278e..d8ba33aa 100644 --- a/src/resolver/utils.ts +++ b/src/resolver/utils.ts @@ -1,4 +1,4 @@ -import type { AnyObject, ChildrenCallback, Route, RouteContext } from './types.js'; +import type { ChildrenCallback, Route, RouteContext } from './types.js'; /** * {@inheritDoc "".NotFoundError} @@ -14,7 +14,7 @@ export type NotFoundResult = typeof notFoundResult; /** * An error to be thrown when a route is not found. */ -export class NotFoundError extends Error { +export class NotFoundError extends Error { /** * The HTTP status code to be used when the route is not found. */ @@ -60,7 +60,7 @@ export function log(msg: string): string { } /** @internal */ -export function getNotFoundError( +export function getNotFoundError( context: RouteContext, ): NotFoundError { return new NotFoundError(context); @@ -72,12 +72,12 @@ export function resolvePath(path?: string | readonly string[]): string { } /** @internal */ -export function getRoutePath(route: Route | undefined): string { +export function getRoutePath(route: Route | undefined): string { return resolvePath(route?.path); } /** @internal */ -export function unwrapChildren( +export function unwrapChildren( children: ChildrenCallback | ReadonlyArray> | undefined, ): ReadonlyArray> | undefined { return Array.isArray>>(children) && children.length > 0 ? children : undefined; diff --git a/src/router.ts b/src/router.ts index fa169c3a..600f24bb 100644 --- a/src/router.ts +++ b/src/router.ts @@ -9,7 +9,6 @@ import animate from './transitions/animate.js'; import { DEFAULT_TRIGGERS, setNavigationTriggers } from './triggers/navigation.js'; import type { ActionResult, - AnyObject, Commands, ChainItem, ContextExtension, @@ -99,7 +98,7 @@ const rootContext: RouteContext = { * * Only `setRoutes` has to be called manually, others are automatically invoked when creating a new instance. */ -export class Router extends Resolver< +export class Router extends Resolver< ActionValue, RouteExtension, ContextExtension diff --git a/src/types.d.ts b/src/types.d.ts index b4656241..2ce30a08 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -2,7 +2,6 @@ import type { EmptyObject, RequireAtLeastOne } from 'type-fest'; import type { ResolutionError, ResolverOptions } from './resolver/resolver.js'; import type { ActionResult as _ActionResult, - AnyObject, ChildrenCallback as _ChildrenCallback, ChainItem as _ChainItem, RouteChildrenContext as _RouteChildrenContext, @@ -16,7 +15,7 @@ import type { } from './resolver/types.js'; import type { Router } from './router.js'; -export type { AnyObject, ResolutionError, IndexedParams, Params, ParamValue, PrimitiveParamValue }; +export type { ResolutionError, IndexedParams, Params, ParamValue, PrimitiveParamValue }; /** * A custom event that is triggered when the location changes. @@ -38,7 +37,7 @@ export type VaadinRouterLocationChangedEvent = CustomEvent< * @typeParam C - The type of user-defined context-specific data. Defaults to an * empty object. */ -export type VaadinRouterErrorEvent = CustomEvent< +export type VaadinRouterErrorEvent = CustomEvent< Readonly<{ /** The error object. */ error: ResolutionError; @@ -112,7 +111,7 @@ export type ActionValue = HTMLElement | PreventResult | RedirectResult; /** * A result of the {@link RouteContext.next} function. */ -export type NextResult = _ActionResult>; +export type NextResult = _ActionResult>; /** * A result of the {@link RouteExtension.action | Route.action}. @@ -122,7 +121,7 @@ export type ActionResult = _ActionResult; /** * {@inheritDoc "".ChainItem} */ -export type ChainItem = _ChainItem< +export type ChainItem = _ChainItem< ActionValue, RouteExtension, ContextExtension @@ -138,7 +137,7 @@ export type ChainItem = _ChainItem< * * @internal */ -export type ContextExtension = Readonly<{ +export type ContextExtension = Readonly<{ resolver?: Router; chain?: Array>; }> & @@ -147,7 +146,7 @@ export type ContextExtension = Readonl /** * {@inheritDoc "".ChildrenCallback} */ -export type ChildrenCallback = _ChildrenCallback< +export type ChildrenCallback = _ChildrenCallback< ActionValue, RouteExtension, ContextExtension @@ -160,7 +159,7 @@ export type ChildrenCallback = RequireAtLeastOne<{ +export type RouteExtension = RequireAtLeastOne<{ children?: ChildrenCallback | ReadonlyArray>; component?: string; redirect?: string; @@ -189,7 +188,7 @@ export type RouteExtension = RequireAt * {@inheritDoc "".RouteContext} * @interface */ -export type RouteContext = _RouteContext< +export type RouteContext = _RouteContext< ActionValue, RouteExtension, ContextExtension @@ -200,15 +199,15 @@ export type RouteContext = _RouteChildrenContext, ContextExtension>; /** * {@inheritDoc "".Route} * @interface */ -export type Route = _Route< +export type Route = _Route< ActionValue, RouteExtension, ContextExtension @@ -218,7 +217,7 @@ export type Route".ResolverOptions} * @interface */ -export type RouterOptions = ResolverOptions< +export type RouterOptions = ResolverOptions< ActionValue, RouteExtension, ContextExtension @@ -234,7 +233,7 @@ export type RouterOptions { +export interface RouterLocation { /** * The base URL used in the router. See [the `baseUrl` property * ](#/classes/Router#property-baseUrl) in the Router. @@ -409,7 +408,7 @@ export interface RouterLocation +export interface WebComponentInterface extends HTMLElement { location?: RouterLocation; name?: string; diff --git a/src/utils.ts b/src/utils.ts index b1542ede..cf52ca27 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -4,7 +4,6 @@ import { isFunction, isObject, isString, log, toArray } from './resolver/utils.j import type { Router } from './router.js'; import type { ActionResult, - AnyObject, ChainItem, IndexedParams, RedirectResult, @@ -15,7 +14,7 @@ import type { } from './types.js'; /** @internal */ -export function ensureRoute(route?: Route): void { +export function ensureRoute(route?: Route): void { if (!route || !isString(route.path)) { throw new Error( log(`Expected route config to be an object with a "path" string property, or an array of such objects`), @@ -52,14 +51,14 @@ export function ensureRoute(route?: Ro } /** @internal */ -export function ensureRoutes( +export function ensureRoutes( routes: Route | ReadonlyArray>, ): void { toArray(routes).forEach((route) => ensureRoute(route)); } /** @internal */ -export function copyContextWithoutNext({ +export function copyContextWithoutNext({ next: _, ...context }: RouteContext): Omit, 'next'> { @@ -67,7 +66,7 @@ export function copyContextWithoutNext } /** @internal */ -export function getPathnameForRouter( +export function getPathnameForRouter( pathname: string, router: Resolver, ): string { @@ -89,15 +88,15 @@ export function getMatchedPath(pathItems: ReadonlyArray(chain: ReadonlyArray>): string { +export function getRoutePath(chain: ReadonlyArray>): string { return getMatchedPath(chain.map((chainItem) => chainItem.route)); } /** @internal */ -export type ResolverOnlyContext = Readonly<{ resolver: Router }>; +export type ResolverOnlyContext = Readonly<{ resolver: Router }>; /** @internal */ -type PartialRouteContext = Readonly<{ +type PartialRouteContext = Readonly<{ chain?: ReadonlyArray>; hash?: string; params?: IndexedParams; @@ -108,14 +107,14 @@ type PartialRouteContext = Readonly<{ }>; /** @internal */ -export function createLocation({ +export function createLocation({ resolver, }: ResolverOnlyContext): RouterLocation; -export function createLocation( +export function createLocation( context: RouteContext, route?: Route, ): RouterLocation; -export function createLocation( +export function createLocation( { chain = [], hash = '', params = {}, pathname = '', redirectFrom, resolver, search = '' }: PartialRouteContext, route?: Route, ): RouterLocation { @@ -141,7 +140,7 @@ export function createLocation( } /** @internal */ -export function createRedirect( +export function createRedirect( context: RouteContext, pathname: string, ): RedirectResult { @@ -156,7 +155,7 @@ export function createRedirect( } /** @internal */ -export function renderElement>( +export function renderElement>( context: RouteContext, element: E, ): E { @@ -187,7 +186,7 @@ export function maybeCall( export function amend< A extends readonly unknown[], N extends keyof O, - O extends AnyObject & { [key in N]: (this: O, ...args: A) => ActionResult | undefined }, + O extends object & { [key in N]: (this: O, ...args: A) => ActionResult | undefined }, >(fn: keyof O, obj: O | undefined, ...args: A): (result: ActionResult) => ActionResult | undefined { return (result: ActionResult) => { if (result && isObject(result) && ('cancel' in result || 'redirect' in result)) { @@ -199,7 +198,7 @@ export function amend< } /** @internal */ -export function processNewChildren( +export function processNewChildren( newChildren: Route | ReadonlyArray> | undefined | void, route: Route, ): void { diff --git a/test/router/dynamic-redirect.spec.ts b/test/router/dynamic-redirect.spec.ts index fe96a00a..f2e27a71 100644 --- a/test/router/dynamic-redirect.spec.ts +++ b/test/router/dynamic-redirect.spec.ts @@ -15,7 +15,6 @@ use(chaiAsPromised); describe('Vaadin.Router', () => { let outlet: HTMLElement; let router: Router; - let history: sinon.SinonStubbedInstance; before(() => { outlet = document.createElement('div'); @@ -27,8 +26,6 @@ describe('Vaadin.Router', () => { }); beforeEach(() => { - history = sinon.createStubInstance(History); - // create a new router instance router = new Router(outlet); }); diff --git a/test/router/test-utils.ts b/test/router/test-utils.ts index 40bba62b..b2905e01 100644 --- a/test/router/test-utils.ts +++ b/test/router/test-utils.ts @@ -3,7 +3,6 @@ import { expect } from '@esm-bundle/chai'; import type { Commands, RouteContext, Router, WebComponentInterface, Route } from '../../src/index.js'; -import type { AnyObject } from '../../src/resolver/types.js'; export async function waitForNavigation(): Promise { return await new Promise((resolve) => { @@ -21,7 +20,7 @@ export function verifyActiveRoutes(router: Router, expectedSegments: string[]): } function createWebComponentAction(method: T) { - return ( + return ( componentName: string, callback: WebComponentInterface[T], name: string = 'unknown', diff --git a/tsconfig.json b/tsconfig.json index 4e573d7e..de12fa93 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,6 @@ "moduleResolution": "bundler", "lib": ["es2022", "dom", "DOM.Iterable"], "allowArbitraryExtensions": true, - "skipLibCheck": true, "strict": true, "strictBindCallApply": true, "sourceMap": true, @@ -20,6 +19,6 @@ "useDefineForClassFields": true, "useUnknownInCatchVariables": true }, - "include": ["scripts/**/*", "src/**/*", "test/**/*", "./vite.config.ts", "./*.cjs"], + "include": ["scripts/**/*", "src/**/*", "test/**/*", "/vite.config.ts", "/*.cjs"], "exclude": ["node_modules", "dist/**/*", "scripts/**/*.js"] } From c0d35859ff58b625962310d5359ac68b24ef746b Mon Sep 17 00:00:00 2001 From: Vlad Rindevich Date: Mon, 14 Oct 2024 17:21:30 +0300 Subject: [PATCH 6/7] refactor: small types simplification --- __src/types/Commands.d.ts | 22 ---------------------- src/resolver/resolver.ts | 17 +++++++++-------- src/resolver/types.d.ts | 12 ++++-------- 3 files changed, 13 insertions(+), 38 deletions(-) delete mode 100644 __src/types/Commands.d.ts diff --git a/__src/types/Commands.d.ts b/__src/types/Commands.d.ts deleted file mode 100644 index f706c36d..00000000 --- a/__src/types/Commands.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { EmptyObject } from '@ausginer/router'; -import type { PreventResult, RedirectResult } from './general.js'; - -export interface Commands { - component(name: K): HTMLElementTagNameMap[K]; - component(name: string): HTMLElement; - /** - * Function that creates a special object that can be returned to abort - * the current navigation and fall back to the last one. If there is no - * existing one, an exception is thrown. - */ - prevent(): PreventResult; - redirect(path: string): RedirectResult; -} - -export type Command = { - [$command]: true; -}; - -export type EmptyCommands = EmptyObject; -export type PreventCommands = Pick; -export type PreventAndRedirectCommands = Pick; diff --git a/src/resolver/resolver.ts b/src/resolver/resolver.ts index 42f27a68..a9b73a15 100644 --- a/src/resolver/resolver.ts +++ b/src/resolver/resolver.ts @@ -9,7 +9,7 @@ import type { EmptyObject } from 'type-fest'; import matchRoute, { type MatchWithRoute } from './matchRoute.js'; import defaultResolveRoute from './resolveRoute.js'; -import type { ActionResult, RouteBase, Match, MaybePromise, ResolveContext, Route, RouteContext } from './types.js'; +import type { ActionResult, Route, Match, MaybePromise, ResolveContext, RouteContext } from './types.js'; import { getNotFoundError, getRoutePath, isString, NotFoundError, notFoundResult, toArray } from './utils.js'; function isDescendantRoute( @@ -131,7 +131,7 @@ class Resolver; readonly errorHandler?: ErrorHandlerCallback; readonly resolveRoute: ResolveRouteCallback; - readonly #root: RouteBase; + readonly #root: Route; constructor(routes: ReadonlyArray> | Route, options?: ResolverOptions); constructor( @@ -155,7 +155,7 @@ class Resolver undefined, path: '', - }; + } as Route; } else { this.#root = { ...routes, parent: undefined }; } @@ -170,7 +170,7 @@ class Resolver, + route: this.#root, search: '', chain: [], }; @@ -180,7 +180,7 @@ class Resolver { - return this.#root as Route; + return this.#root; } /** @@ -246,7 +246,7 @@ class Resolver, + this.#root, this.__normalizePathname(context.pathname) ?? context.pathname, !!this.baseUrl, ); @@ -294,7 +294,7 @@ class Resolver); + return await next(true, this.#root); } catch (error: unknown) { const _error = error instanceof NotFoundError @@ -315,8 +315,9 @@ class Resolver> | Route): void { + setRoutes(routes: ReadonlyArray> | Route): object { this.#root.__children = [...toArray(routes)]; + return {}; } /** diff --git a/src/resolver/types.d.ts b/src/resolver/types.d.ts index c6973d4b..ea375e31 100644 --- a/src/resolver/types.d.ts +++ b/src/resolver/types.d.ts @@ -56,8 +56,10 @@ export type ChildrenCallback = ( * empty object. * @typeParam C - The type of user-defined context-specific data. Defaults to an * empty object. + * + * @interface */ -export type RouteBase = Readonly<{ +export type Route = Readonly<{ /** * The name of the route. */ @@ -93,13 +95,7 @@ export type RouteBase = Readonly<{ children?: ReadonlyArray> | ChildrenCallback; parent?: Route; fullPath?: string; -}; - -/** - * {@inheritDoc BasicRoutePart} - * @interface - */ -export type Route = RouteBase & R; +} & R; /** * A matched route with its associated path. From 0b6d609c57535e39ffe68ce3cb64f3973b3bd0fc Mon Sep 17 00:00:00 2001 From: Anton Platonov Date: Tue, 15 Oct 2024 12:06:31 +0300 Subject: [PATCH 7/7] build: re-enable skipLibCheck in tsconfig.json --- tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tsconfig.json b/tsconfig.json index de12fa93..7a89a309 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,6 +9,7 @@ "moduleResolution": "bundler", "lib": ["es2022", "dom", "DOM.Iterable"], "allowArbitraryExtensions": true, + "skipLibCheck": true, "strict": true, "strictBindCallApply": true, "sourceMap": true,