diff --git a/ReadMe.md b/ReadMe.md index 0a29380..07e79ba 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -8,10 +8,14 @@ [![Demo](https://codesandbox.io/static/img/play-codesandbox.svg)][4] +## Preview + +https://idea2app.github.io/React-Router-class-tools/preview/ + ## Feature - [x] `withRouter()` function -- [ ] `withRouter()` decorator +- [x] `withRouter()` decorator - [x] `RouteComponentProps` - [ ] `this.props.history` - [x] `this.props.location` @@ -25,9 +29,14 @@ import { PureComponent } from 'react'; import { RouteComponentProps, withRouter } from 'react-router-class-tools'; -class RoutePage extends PureComponent< - RouteComponentProps<{ id: string }, {}, { extra: number }> -> { +type RoutePageProps = RouteComponentProps< + { id: string }, + {}, + { extra: number } +>; + +@withRouter +export class RoutePage extends PureComponent { render() { const { id } = this.props.match.params, { extra } = this.props.query; @@ -40,7 +49,6 @@ class RoutePage extends PureComponent< ); } } -export default withRouter(RoutePage); ``` ## Reference diff --git a/package.json b/package.json index 20a3cf3..de9408e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-router-class-tools", - "version": "0.1.4", + "version": "0.2.0", "license": "LGPL-3.0", "author": "shiy2008@gmail.com", "description": "Class Component utilities for React Router 6+", @@ -33,16 +33,21 @@ "react-router-dom": ">=6" }, "devDependencies": { + "@parcel/config-default": "~2.15.4", "@parcel/packager-ts": "~2.15.4", + "@parcel/transformer-typescript-tsc": "~2.15.4", "@parcel/transformer-typescript-types": "~2.15.4", "@types/node": "^22.18.3", "@types/react": "^19.1.13", + "@types/react-dom": "^19.1.9", "husky": "^9.1.7", "lint-staged": "^16.1.6", "parcel": "~2.15.4", "prettier": "^3.6.2", "react": "^19.1.1", + "react-dom": "^19.1.1", "react-router-dom": "^7.9.1", + "rimraf": "^6.0.1", "typedoc": "^0.28.1", "typedoc-plugin-mdn-links": "^5.0.9", "typescript": "~5.9.2" @@ -70,12 +75,17 @@ "optimize": true } }, + "@parcel/resolver-default": { + "packageExports": true + }, "scripts": { "prepare": "husky", "test": "lint-staged", + "preview": "cd test/ && rimraf ../.parcel-cache/ dist/ && parcel --open", + "pack-preview": "cd test/ && rimraf ../.parcel-cache/ dist/ && parcel build --public-url=. --dist-dir=../docs/preview/", "pack-code": "rm -rf dist/ && parcel build source/index.tsx", "pack-docs": "rm -rf docs/ && typedoc source/", - "build": "npm run pack-code && npm run pack-docs", + "build": "npm run pack-code && npm run pack-docs && npm run pack-preview", "prepublishOnly": "npm test && npm run build" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a031f74..ff1b9c4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,9 +15,15 @@ importers: specifier: ^4.6.0 version: 4.6.0(typescript@5.9.2) devDependencies: + '@parcel/config-default': + specifier: ^2.15.4 + version: 2.15.4(@parcel/core@2.15.4(@swc/helpers@0.5.17))(@swc/helpers@0.5.17) '@parcel/packager-ts': specifier: ~2.15.4 version: 2.15.4(@parcel/core@2.15.4(@swc/helpers@0.5.17)) + '@parcel/transformer-typescript-tsc': + specifier: ^2.15.4 + version: 2.15.4(@parcel/core@2.15.4(@swc/helpers@0.5.17))(typescript@5.9.2) '@parcel/transformer-typescript-types': specifier: ~2.15.4 version: 2.15.4(@parcel/core@2.15.4(@swc/helpers@0.5.17))(typescript@5.9.2) @@ -27,6 +33,9 @@ importers: '@types/react': specifier: ^19.1.13 version: 19.1.13 + '@types/react-dom': + specifier: ^19.1.9 + version: 19.1.9(@types/react@19.1.13) husky: specifier: ^9.1.7 version: 9.1.7 @@ -42,9 +51,15 @@ importers: react: specifier: ^19.1.1 version: 19.1.1 + react-dom: + specifier: ^19.1.1 + version: 19.1.1(react@19.1.1) react-router-dom: specifier: ^7.9.1 - version: 7.9.1(react@19.1.1) + version: 7.9.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + rimraf: + specifier: ^6.0.1 + version: 6.0.1 typedoc: specifier: ^0.28.1 version: 0.28.1(typescript@5.9.2) @@ -64,6 +79,18 @@ packages: '@gerrit0/mini-shiki@3.2.1': resolution: {integrity: sha512-HbzRC6MKB6U8kQhczz0APKPIzFHTrcqhaC7es2EXInq1SpjPVnpVSIsBe6hNoLWqqCx1n5VKiPXq6PfXnHZKOQ==} + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + '@lezer/common@1.2.3': resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} @@ -412,6 +439,12 @@ packages: resolution: {integrity: sha512-Q22e0VRbx62VXFlvJWIlc8ihlLaPQgtnAZz5E1/+ojiNb+k0PmIRjNJclVWPF6IdCsLO5tnGfUOaXe2OnZz28Q==} engines: {node: '>= 16.0.0', parcel: ^2.15.4} + '@parcel/transformer-typescript-tsc@2.15.4': + resolution: {integrity: sha512-HrRC5B4/Mr+hN/lmdHNhor19uslPK7wFIH5jZC39oSXCdSvR89dDMWgIRLcp8znAkZ9TIGBzFCvDjTEDqhOprw==} + engines: {node: '>= 16.0.0', parcel: ^2.15.4} + peerDependencies: + typescript: '>=3.0.0' + '@parcel/transformer-typescript-types@2.15.4': resolution: {integrity: sha512-fvc9X2NR36rSalhLAY0nhawPHODTJw7vgoWmNUt63HEUjtYluf5FSz7mASoLVVy/5CKKUg3FdXFWpCYetxOCXQ==} engines: {node: '>= 16.0.0', parcel: ^2.15.4} @@ -603,9 +636,6 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/helpers@0.5.15': - resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@swc/helpers@0.5.17': resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} @@ -618,6 +648,11 @@ packages: '@types/node@22.18.3': resolution: {integrity: sha512-gTVM8js2twdtqM+AE2PdGEe9zGQY4UvmFjan9rZcVb6FGdStfjWoWejdmy4CfWVO9rh5MiYQGZloKAGkJt8lMw==} + '@types/react-dom@19.1.9': + resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==} + peerDependencies: + '@types/react': ^19.0.0 + '@types/react@19.1.13': resolution: {integrity: sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==} @@ -628,6 +663,10 @@ packages: resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} engines: {node: '>=18'} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + ansi-regex@6.1.0: resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} engines: {node: '>=12'} @@ -714,6 +753,10 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} @@ -743,12 +786,21 @@ packages: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + electron-to-chromium@1.5.218: resolution: {integrity: sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==} emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -768,6 +820,10 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + get-east-asian-width@1.3.0: resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} engines: {node: '>=18'} @@ -776,6 +832,11 @@ packages: resolution: {integrity: sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==} engines: {node: '>=6'} + glob@11.0.3: + resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} + engines: {node: 20 || >=22} + hasBin: true + globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} @@ -796,6 +857,10 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-fullwidth-code-point@5.0.0: resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} engines: {node: '>=18'} @@ -808,6 +873,13 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + engines: {node: 20 || >=22} + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -901,6 +973,10 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} + lru-cache@11.2.1: + resolution: {integrity: sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==} + engines: {node: 20 || >=22} + lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} @@ -919,10 +995,18 @@ packages: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} + minimatch@10.0.3: + resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} + engines: {node: 20 || >=22} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -964,11 +1048,22 @@ packages: ordered-binary@1.5.3: resolution: {integrity: sha512-oGFr3T+pYdTGJ+YFEILMpS3es+GiIbs9h/XQrclBXUtd44ey7XwfsMzM31f64I1SQOawDoDr/D823kNCADI8TA==} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parcel@2.15.4: resolution: {integrity: sha512-eZHQ/omuQ7yBYB9XezyzSqhc826oy/uhloCNiej1CTZ+twAqJVtp4MRvTGMcivKhE+WE8QkYD5XkJHLLQsJQcg==} engines: {node: '>= 16.0.0'} hasBin: true + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -993,6 +1088,11 @@ packages: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} + react-dom@19.1.1: + resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} + peerDependencies: + react: ^19.1.1 + react-refresh@0.16.0: resolution: {integrity: sha512-FPvF2XxTSikpJxcr+bHut2H4gJ17+18Uy20D5/F+SKzFap62R3cM5wH6b8WN3LyGSYeQilLEcJcR1fjBSI2S1A==} engines: {node: '>=0.10.0'} @@ -1028,9 +1128,17 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rimraf@6.0.1: + resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} + engines: {node: 20 || >=22} + hasBin: true + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + semver@7.7.1: resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} engines: {node: '>=10'} @@ -1039,6 +1147,14 @@ packages: set-cookie-parser@2.7.1: resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -1051,6 +1167,14 @@ packages: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + string-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} @@ -1059,6 +1183,10 @@ packages: resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==} engines: {node: '>=20'} + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + strip-ansi@7.1.0: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} @@ -1124,6 +1252,19 @@ packages: element-internals-polyfill: '>=1' typescript: '>=4.1' + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrap-ansi@9.0.0: resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} engines: {node: '>=18'} @@ -1150,6 +1291,21 @@ snapshots: '@shikijs/types': 3.2.1 '@shikijs/vscode-textmate': 10.0.2 + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + '@lezer/common@1.2.3': {} '@lezer/lr@1.4.2': @@ -1682,7 +1838,7 @@ snapshots: '@parcel/source-map': 2.1.1 '@parcel/utils': 2.15.4 '@parcel/workers': 2.15.4(@parcel/core@2.15.4(@swc/helpers@0.5.17)) - '@swc/helpers': 0.5.15 + '@swc/helpers': 0.5.17 browserslist: 4.26.0 nullthrows: 1.1.1 regenerator-runtime: 0.14.1 @@ -1753,6 +1909,16 @@ snapshots: - '@parcel/core' - napi-wasm + '@parcel/transformer-typescript-tsc@2.15.4(@parcel/core@2.15.4(@swc/helpers@0.5.17))(typescript@5.9.2)': + dependencies: + '@parcel/plugin': 2.15.4(@parcel/core@2.15.4(@swc/helpers@0.5.17)) + '@parcel/source-map': 2.1.1 + '@parcel/ts-utils': 2.15.4(typescript@5.9.2) + typescript: 5.9.2 + transitivePeerDependencies: + - '@parcel/core' + - napi-wasm + '@parcel/transformer-typescript-types@2.15.4(@parcel/core@2.15.4(@swc/helpers@0.5.17))(typescript@5.9.2)': dependencies: '@parcel/diagnostic': 2.15.4 @@ -1932,10 +2098,6 @@ snapshots: '@swc/counter@0.1.3': {} - '@swc/helpers@0.5.15': - dependencies: - tslib: 2.8.1 - '@swc/helpers@0.5.17': dependencies: tslib: 2.8.1 @@ -1952,6 +2114,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/react-dom@19.1.9(@types/react@19.1.13)': + dependencies: + '@types/react': 19.1.13 + '@types/react@19.1.13': dependencies: csstype: 3.1.3 @@ -1962,6 +2128,8 @@ snapshots: dependencies: environment: 1.1.0 + ansi-regex@5.0.1: {} + ansi-regex@6.1.0: {} ansi-styles@4.3.0: @@ -2032,6 +2200,12 @@ snapshots: cookie@1.0.2: {} + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + csstype@3.1.3: {} debug@4.4.3: @@ -2048,10 +2222,16 @@ snapshots: dotenv@16.6.1: {} + eastasianwidth@0.2.0: {} + electron-to-chromium@1.5.218: {} emoji-regex@10.4.0: {} + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + entities@4.5.0: {} environment@1.1.0: {} @@ -2064,10 +2244,24 @@ snapshots: dependencies: to-regex-range: 5.0.1 + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + get-east-asian-width@1.3.0: {} get-port@4.2.0: {} + glob@11.0.3: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.1.1 + minimatch: 10.0.3 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 + globals@13.24.0: dependencies: type-fest: 0.20.2 @@ -2082,6 +2276,8 @@ snapshots: is-extglob@2.1.1: {} + is-fullwidth-code-point@3.0.0: {} + is-fullwidth-code-point@5.0.0: dependencies: get-east-asian-width: 1.3.0 @@ -2092,6 +2288,12 @@ snapshots: is-number@7.0.0: {} + isexe@2.0.0: {} + + jackspeak@4.1.1: + dependencies: + '@isaacs/cliui': 8.0.2 + json5@2.2.3: {} lightningcss-darwin-arm64@1.30.1: @@ -2192,6 +2394,8 @@ snapshots: strip-ansi: 7.1.0 wrap-ansi: 9.0.0 + lru-cache@11.2.1: {} + lunr@2.3.9: {} markdown-it@14.1.0: @@ -2212,10 +2416,16 @@ snapshots: mimic-function@5.0.1: {} + minimatch@10.0.3: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 + minipass@7.1.2: {} + ms@2.1.3: {} msgpackr-extract@3.0.3: @@ -2259,6 +2469,8 @@ snapshots: ordered-binary@1.5.3: {} + package-json-from-dist@1.0.1: {} + parcel@2.15.4(@swc/helpers@0.5.17): dependencies: '@parcel/config-default': 2.15.4(@parcel/core@2.15.4(@swc/helpers@0.5.17))(@swc/helpers@0.5.17) @@ -2280,6 +2492,13 @@ snapshots: - '@swc/helpers' - napi-wasm + path-key@3.1.1: {} + + path-scurry@2.0.0: + dependencies: + lru-cache: 11.2.1 + minipass: 7.1.2 + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -2292,18 +2511,26 @@ snapshots: punycode.js@2.3.1: {} + react-dom@19.1.1(react@19.1.1): + dependencies: + react: 19.1.1 + scheduler: 0.26.0 + react-refresh@0.16.0: {} - react-router-dom@7.9.1(react@19.1.1): + react-router-dom@7.9.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: react: 19.1.1 - react-router: 7.9.1(react@19.1.1) + react-dom: 19.1.1(react@19.1.1) + react-router: 7.9.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - react-router@7.9.1(react@19.1.1): + react-router@7.9.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1): dependencies: cookie: 1.0.2 react: 19.1.1 set-cookie-parser: 2.7.1 + optionalDependencies: + react-dom: 19.1.1(react@19.1.1) react@19.1.1: {} @@ -2316,12 +2543,25 @@ snapshots: rfdc@1.4.1: {} + rimraf@6.0.1: + dependencies: + glob: 11.0.3 + package-json-from-dist: 1.0.1 + safe-buffer@5.2.1: {} + scheduler@0.26.0: {} + semver@7.7.1: {} set-cookie-parser@2.7.1: {} + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + signal-exit@4.1.0: {} slice-ansi@7.1.0: @@ -2331,6 +2571,18 @@ snapshots: string-argv@0.3.2: {} + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + string-width@7.2.0: dependencies: emoji-regex: 10.4.0 @@ -2342,6 +2594,10 @@ snapshots: get-east-asian-width: 1.3.0 strip-ansi: 7.1.0 + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + strip-ansi@7.1.0: dependencies: ansi-regex: 6.1.0 @@ -2395,6 +2651,22 @@ snapshots: regenerator-runtime: 0.14.1 typescript: 5.9.2 + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + wrap-ansi@9.0.0: dependencies: ansi-styles: 6.2.1 diff --git a/source/index.tsx b/source/index.tsx index 032b496..32de127 100644 --- a/source/index.tsx +++ b/source/index.tsx @@ -1,6 +1,6 @@ import { Constructor, parseURLData } from 'web-utility'; import { Location } from 'history'; -import React, { Component, PureComponent } from 'react'; +import { Component, ComponentClass, ErrorInfo, FC } from 'react'; import { useLocation, useParams } from 'react-router-dom'; // Types come from https://cdn.jsdelivr.net/npm/@types/react-router/index.d.ts @@ -22,35 +22,116 @@ export interface RouteComponentProps< Context extends StaticContext = StaticContext, Query extends Record = {} > { - location: Location; - match: match; - query: Query; + location?: Location; + match?: match; + query?: Query; staticContext?: Context; } +const HooksWrapper: FC<{ ClassComponent: ComponentClass }> = ({ + ClassComponent +}) => { + const location = useLocation(), + params = useParams(); + + const { pathname = '/', search = '', hash = '' } = location; + + const path = pathname + search + hash; + + const match = { + url: globalThis.location.origin + path, + path, + params + }, + query = parseURLData(search); + + return ; +}; + /** * @see https://v5.reactrouter.com/web/api/withRouter + * + * @example + * ```tsx + * import { Component } from 'react'; + * import { RouteComponentProps, withRouter } from 'react-router-class-tools'; + * + * @withRouter + * export class PageWithRouterDecorator extends Component { + * render() { + * const { location, match, query } = this.props; + * + * return ( + *
{JSON.stringify({ location, match, query }, null, 4)}
+ * ); + * } + * } + * ``` + * + * @example + * ```tsx + * import { Component } from 'react'; + * import { RouteComponentProps, withRouter } from 'react-router-class-tools'; + * + * export default withRouter(class PageWithRouterFunction extends Component { + * render() { + * const { location, match, query } = this.props; + * + * return ( + *
{JSON.stringify({ location, match, query }, null, 4)}
+ * ); + * } + * } + * ``` */ -export function withRouter( - Class: Constructor< - Component | PureComponent - > -) { - return () => { - const location = useLocation(), - params = useParams(); - - const { pathname = '/', search = '', hash = '' } = location; - - const path = pathname + search + hash; - - const match = { - url: globalThis.location.origin + path, - path, - params - }, - query = parseURLData(search); - - return ; +export const withRouter = < + P extends RouteComponentProps, + C extends ComponentClass

+>( + Class: C, + context?: ClassDecoratorContext +) => + class ComponentWithRouter extends (Class as Constructor>) { + static WrappedComponent = Class; + static displayName = `withRouter(${Class.displayName || Class.name})`; + + static getDerivedStateFromProps( + nextProps: Readonly

, + prevState: Readonly<{}> + ) { + return {}; + } + + static getDerivedStateFromError(error: Error) {} + + state: Readonly<{}> = {}; + + componentDidMount() {} + + getSnapshotBeforeUpdate( + prevProps: Readonly

, + prevState: Readonly<{}> + ) {} + + shouldComponentUpdate( + nextProps: Readonly

, + nextState: Readonly<{}>, + nextContext: any + ) { + return true; + } + + componentDidUpdate( + prevProps: Readonly

, + prevState: Readonly<{}>, + snapshot?: any + ) {} + + componentDidCatch(error: Error, errorInfo: ErrorInfo) {} + + componentWillUnmount() {} + + render() { + return ; + } }; -} diff --git a/test/.parcelrc b/test/.parcelrc new file mode 100644 index 0000000..36cdf20 --- /dev/null +++ b/test/.parcelrc @@ -0,0 +1,8 @@ +{ + "extends": "@parcel/config-default", + "transformers": { + "*.{ts,tsx}": [ + "@parcel/transformer-typescript-tsc" + ] + } +} diff --git a/test/package.json b/test/package.json new file mode 100644 index 0000000..19bfefb --- /dev/null +++ b/test/package.json @@ -0,0 +1,5 @@ +{ + "name": "react-router-preview", + "private": true, + "source": "source/index.html" +} diff --git a/test/source/index.html b/test/source/index.html new file mode 100644 index 0000000..bce4fe7 --- /dev/null +++ b/test/source/index.html @@ -0,0 +1,11 @@ + + + + + + React Router class tools + + + + + diff --git a/test/source/page/WithRouterDecorator.tsx b/test/source/page/WithRouterDecorator.tsx new file mode 100644 index 0000000..7acb273 --- /dev/null +++ b/test/source/page/WithRouterDecorator.tsx @@ -0,0 +1,21 @@ +import { Component } from 'react'; + +import { RouteComponentProps, withRouter } from '../../../source'; + +@withRouter +export class PageWithRouterDecorator extends Component { + render() { + const { location, match, query } = this.props; + + return ( + <> +

@withRouter

+
    +
  • Location: {location.pathname + location.search}
  • +
  • Match: {JSON.stringify(match)}
  • +
  • Query: {JSON.stringify(query)}
  • +
+ + ); + } +} diff --git a/test/source/page/WithRouterFunction.tsx b/test/source/page/WithRouterFunction.tsx new file mode 100644 index 0000000..9441c38 --- /dev/null +++ b/test/source/page/WithRouterFunction.tsx @@ -0,0 +1,21 @@ +import { Component } from 'react'; + +import { RouteComponentProps, withRouter } from '../../../source'; + +class PageWithRouterFunction extends Component { + render() { + const { location, match, query } = this.props; + + return ( + <> +

withRouter()

+
    +
  • Location: {location.pathname + location.search}
  • +
  • Match: {JSON.stringify(match)}
  • +
  • Query: {JSON.stringify(query)}
  • +
+ + ); + } +} +export default withRouter(PageWithRouterFunction); diff --git a/test/source/page/index.tsx b/test/source/page/index.tsx new file mode 100644 index 0000000..8811ad4 --- /dev/null +++ b/test/source/page/index.tsx @@ -0,0 +1,33 @@ +import { createRoot } from 'react-dom/client'; +import { HashRouter, Link, Route, Routes } from 'react-router-dom'; +import { documentReady } from 'web-utility'; + +import PageWithRouterFunction from './WithRouterFunction'; +import { PageWithRouterDecorator } from './WithRouterDecorator'; + +documentReady.then(() => + createRoot(document.body).render( + + + + Home Page} /> + } + /> + } + /> + + + ) +); diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 0000000..8bd7533 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "ES2020", + "jsx": "react-jsx" + }, + "include": ["./**/*", "../source/*"] +} diff --git a/tsconfig.json b/tsconfig.json index 8445d1b..bb8708e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "module": "UMD", "moduleResolution": "Node", "esModuleInterop": true, - "jsx": "react", + "jsx": "react-jsx", "declaration": true, "skipLibCheck": true, "lib": ["ES2021", "DOM"],