{"_id":"@axelraag/next-type-safe-routes","_rev":"2-c9d44fd9b6773398dfdfb884e0c68c2f","name":"@axelraag/next-type-safe-routes","dist-tags":{"latest":"0.4.0"},"versions":{"0.3.1":{"name":"@axelraag/next-type-safe-routes","version":"0.3.1","description":"Never should your users experience broken links again!","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc","prepare":"yarn build","test":"yarn jest --updateSnapshot","lint":"eslint '*/**/*.{js,ts}' --quiet --fix"},"husky":{"hooks":{"pre-commit":"lint-staged"}},"lint-staged":{"*.{js,ts,tsx}":["eslint --fix"]},"dependencies":{"chokidar":"^3.5.1","mkdirp":"^1.0.4","walk-sync":"^2.2.0"},"devDependencies":{"@types/jest":"^26.0.21","@types/node":"^14.14.34","@typescript-eslint/eslint-plugin":"^5.10.1","@typescript-eslint/parser":"^5.10.1","commander":"^7.1.0","eslint":"^8.7.0","eslint-plugin-prettier":"^4.0.0","jest":"^26.6.3","prettier":"^2.5.1","ts-jest":"^26.5.4","typescript":"^4.5.5"},"repository":{"type":"git","url":"git+https://github.com/ckastbjerg/next-type-safe-routes.git"},"keywords":[],"author":{"name":"Christian Kastbjerg"},"license":"ISC","bugs":{"url":"https://github.com/ckastbjerg/next-type-safe-routes/issues"},"homepage":"https://github.com/ckastbjerg/next-type-safe-routes#readme","directories":{"example":"example"},"gitHead":"4d7b50e4c4cadbaf15ff71cef646ff870788466e","_id":"@axelraag/next-type-safe-routes@0.3.1","_nodeVersion":"18.11.0","_npmVersion":"9.1.1","dist":{"integrity":"sha512-I6C2ifFl60mdxGJ6FADk+v36F/oeCmVUk++9rWIz14lfUfFMEuIbrYM48J8DMW4pgtSmDEwhh+fwcpMVAfwg8Q==","shasum":"95e557acea74ea2851be76e12b4a9a3217f1bdf5","tarball":"https://registry.npmjs.org/@axelraag/next-type-safe-routes/-/next-type-safe-routes-0.3.1.tgz","fileCount":98,"unpackedSize":44855,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDd1M2g5OtIRYhm70iLyQqn+tAeaJIn23Xi9QrGaVsKWwIgUjwOntJMn6hPobtOANsOmYQkRqYFdnDbUXw6I/mnDzI="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjrupbACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmoMmw//WJRDFYem/yqI9Ejk4P8HhsLY0VCnETpG/TWfoxmVBeBd2y96\r\nydGUU8AMc0uFsre5BBEX6l3SE4xoOWaxQ7KItn4Q/Jhqnf4/Ma6BXHtbtr8S\r\n3Y9JSXHKpNfua9Bv/IlfkGaGZK9yHO8is244fh3b2VBOPkFRA2S9IcV0iNOk\r\nSTcuGEJceWb3IBTVcuoVIxF8JBFNw9UFPUW/B7AnL3a/4oMC6tY+I/Ho79zb\r\n8uB42l51wWD5T9AnYj/nVSNwUC0LO35J4IhILVa7Ot0hX5KJw84pMKlzOvUA\r\n9xNX/kpiDRtfdWY2bmvs15BrZoPM9EJtgtlEW/4l+WdXaR1f4pDpSSJ61i8t\r\nY2E3VFzmsLb2YVe5OlT5aZT3+/Ssv9uSAtF1TYBvH8aXFJ+aZd9Dexbzbh/i\r\nKOLY/6gfpkLeMWA835eda98Swys+CID4BCYADcbNOTgWxbS7nOdUhDmJGY+c\r\nDhrZ4JodzYen8VZEeQhqoBQcwdtXCUtf0+x6ZAtESvrR0+RtRDTKRkYC6KSI\r\nhxEZ/U5dFu8jIXjaeP4Felp4N2/7x+haIOPzgiebf+Kmc3cbQFmR/t3e0hVG\r\nd4cbgCvui/lIOVCgptE9UoJcYfChXNwK9hDMndJWuodqXYL/i11M+m35NWf5\r\nVaiqZe6fvUYvzGAHe7T7qaNkLb1xeRkFOrA=\r\n=ywUa\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"calvin-f","email":"calvin.falter@gmail.com"},"maintainers":[{"name":"calvin-f","email":"calvin.falter@gmail.com"},{"name":"jonbru","email":"jonas.brunner@axelra.com"},{"name":"warrioru","email":"nicolas.gordillo@axelra.com"},{"name":"severinwullschleger","email":"severin.wullschleger+npm@gmail.com"},{"name":"axelra","email":"devops@axelra.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-type-safe-routes_0.3.1_1672407643555_0.30348239375757613"},"_hasShrinkwrap":false},"0.3.2":{"name":"@axelraag/next-type-safe-routes","version":"0.3.2","description":"Never should your users experience broken links again!","main":"dist/index.js","types":"dist/index.d.ts","bin":{"next-type-safe-routes":"dist/postinstall-generation.js"},"scripts":{"build":"tsc","prepare":"yarn build","test":"yarn jest --updateSnapshot","lint":"eslint '*/**/*.{js,ts}' --quiet --fix"},"husky":{"hooks":{"pre-commit":"lint-staged"}},"lint-staged":{"*.{js,ts,tsx}":["eslint --fix"]},"dependencies":{"chokidar":"^3.5.1","mkdirp":"^1.0.4","walk-sync":"^2.2.0"},"devDependencies":{"@types/jest":"^29.2.5","@types/node":"^14.14.34","@typescript-eslint/eslint-plugin":"^5.10.1","@typescript-eslint/parser":"^5.10.1","commander":"^7.1.0","eslint":"^8.7.0","eslint-plugin-prettier":"^4.0.0","jest":"^29.3.1","prettier":"^2.5.1","ts-jest":"^29.0.3","typescript":"^4.5.5"},"repository":{"type":"git","url":"git+https://github.com/ckastbjerg/next-type-safe-routes.git"},"keywords":[],"author":{"name":"Christian Kastbjerg"},"license":"ISC","bugs":{"url":"https://github.com/ckastbjerg/next-type-safe-routes/issues"},"homepage":"https://github.com/ckastbjerg/next-type-safe-routes#readme","directories":{"example":"example"},"gitHead":"d1c5db21fda52496f6528948d94d75877a5d66ad","_id":"@axelraag/next-type-safe-routes@0.3.2","_nodeVersion":"16.13.0","_npmVersion":"8.1.0","dist":{"integrity":"sha512-r9+uc+n3xVNjeoHR3Rz2r01gFhCv6DPRGyiZ33lDFX5wZOASuruwCdC+aeq78gwBeQfRNXLLeXIJm8BXgeX7hA==","shasum":"68bd2fff0906bb3b61601d6907190ab3eb6658aa","tarball":"https://registry.npmjs.org/@axelraag/next-type-safe-routes/-/next-type-safe-routes-0.3.2.tgz","fileCount":101,"unpackedSize":46848,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCmhwMmsvbC2KPMacsvd2oNNIZDS+AXQ0JTz1YO2cc/UgIgX4mcWRnG3OuSuZNUxj5c7u4Qsp2TJxCVktwW0TvC3XQ="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjvX0FACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqrjA//bK5fEFXt0Vojaf0SXn5JeIqxNu3kSmC0i+/yVWsNhiYNmzEc\r\n7aCJOhlCWeyWPw0S/Gogu/zkmPxgadgo04fNBDBSu/V3bELKgXzDOS3O8ZI+\r\n1TO4SQScokH0BJLFtZOOsouoDAkF2egb/n8jSo0bh3U4blh3wHBHaoGo3HKa\r\n80MB4Si2JLTi2g96+xfWSRtpDTfGkDegbf2FxemffqHniQl6Ib4r6h/yEOrZ\r\nrLaqpgikL30rNn7QvceuZuOI01EuP5bR2uHiBHjlt4Lm4TuN0ZTgbWnGjzKM\r\nhpOWjHPovhMDc7Vz1Zw0SL2ISZjNWdbuRQnOTXu/LWOVfPpCr7ZLkJQNH2rI\r\n/CsZ6iDsrBLPY85cnYoXnKa+aGkYmsOvNAuDHxJgOI+Ejo/eS/yWjQDoqwHJ\r\n5+6QRHOPw4sLS9etEaRNK+zEUdlHR8fxI3IM4QD7FzF6TpjucCLr821lrdKW\r\nu7iXsawbbYpaASCqkW4uL/d0FAWOS8dZ6X1j04pY6G67o+gGOYYVQLqj9E+i\r\nsyLfi/PGvJ72+5+rA9KMfrhLJNo16/S2vIOLpcaq9JXh2TiRcHemokiMLySW\r\nTbh+j8aq6Wmx2kkoJ5TClkBnbA1hYZJuTdZ4gTxAbf6IHIq9uJkYROH5TKQM\r\nHtRACZAN7xnz73gWPH2wA28cZqmi0LPyuis=\r\n=7DWY\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"jonbru","email":"jonas.brunner@axelra.com"},"maintainers":[{"name":"calvin-f","email":"calvin.falter@gmail.com"},{"name":"jonbru","email":"jonas.brunner@axelra.com"},{"name":"warrioru","email":"nicolas.gordillo@axelra.com"},{"name":"severinwullschleger","email":"severin.wullschleger+npm@gmail.com"},{"name":"axelra","email":"devops@axelra.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-type-safe-routes_0.3.2_1673362693179_0.33072279382462133"},"_hasShrinkwrap":false},"0.4.0":{"name":"@axelraag/next-type-safe-routes","version":"0.4.0","description":"Never should your users experience broken links again!","main":"dist/index.js","types":"dist/index.d.ts","bin":{"next-type-safe-routes":"dist/postinstall-generation.js"},"scripts":{"build":"tsc","prepare":"yarn build","test":"yarn jest --updateSnapshot","lint":"eslint '*/**/*.{js,ts}' --quiet --fix"},"husky":{"hooks":{"pre-commit":"lint-staged"}},"lint-staged":{"*.{js,ts,tsx}":["eslint --fix"]},"dependencies":{"chokidar":"^3.5.1","mkdirp":"^1.0.4","walk-sync":"^2.2.0"},"devDependencies":{"@types/jest":"^29.2.5","@types/node":"^14.14.34","@typescript-eslint/eslint-plugin":"^5.10.1","@typescript-eslint/parser":"^5.10.1","commander":"^7.1.0","eslint":"^8.7.0","eslint-plugin-prettier":"^4.0.0","jest":"^29.3.1","prettier":"^2.5.1","ts-jest":"^29.0.3","typescript":"^4.5.5"},"repository":{"type":"git","url":"git+https://github.com/ckastbjerg/next-type-safe-routes.git"},"keywords":[],"author":{"name":"Christian Kastbjerg"},"license":"ISC","bugs":{"url":"https://github.com/ckastbjerg/next-type-safe-routes/issues"},"homepage":"https://github.com/ckastbjerg/next-type-safe-routes#readme","directories":{"example":"example"},"gitHead":"f02a8d87775545b826138ecc2ae0605b1a5f1806","_id":"@axelraag/next-type-safe-routes@0.4.0","_nodeVersion":"16.20.0","_npmVersion":"8.19.4","dist":{"integrity":"sha512-F91MVa5uz/N70rF4kmv6lbTp5H3hIuPQpj8jCZsgiCyJwhKQmYbhYx9PWum3MELbEAesfu+nxDd2iEyfviLRNw==","shasum":"1ed419a0562f446df96acccc705b384326d1b336","tarball":"https://registry.npmjs.org/@axelraag/next-type-safe-routes/-/next-type-safe-routes-0.4.0.tgz","fileCount":134,"unpackedSize":62837,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCq2nlpSneDij9Gl++Sy5X2j4PlyAxnI8eKCGkBZff3ugIhAInVILjlKV5+NrfNQpEK3vW76o6GKGCHeQ89lCmROXTJ"}]},"_npmUser":{"name":"jonbru","email":"jonas.brunner@axelra.com"},"maintainers":[{"name":"calvin-f","email":"calvin.falter@gmail.com"},{"name":"jonbru","email":"jonas.brunner@axelra.com"},{"name":"warrioru","email":"nicolas.gordillo@axelra.com"},{"name":"severinwullschleger","email":"severin.wullschleger+npm@gmail.com"},{"name":"axelra","email":"devops@axelra.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/next-type-safe-routes_0.4.0_1700553287875_0.04853074483093622"},"_hasShrinkwrap":false}},"time":{"created":"2022-12-30T13:40:43.502Z","0.3.1":"2022-12-30T13:40:43.755Z","modified":"2023-11-21T07:54:48.295Z","0.3.2":"2023-01-10T14:58:13.376Z","0.4.0":"2023-11-21T07:54:48.051Z"},"maintainers":[{"name":"calvin-f","email":"calvin.falter@gmail.com"},{"name":"jonbru","email":"jonas.brunner@axelra.com"},{"name":"warrioru","email":"nicolas.gordillo@axelra.com"},{"name":"severinwullschleger","email":"severin.wullschleger+npm@gmail.com"},{"name":"axelra","email":"devops@axelra.com"}],"description":"Never should your users experience broken links again!","homepage":"https://github.com/ckastbjerg/next-type-safe-routes#readme","keywords":[],"repository":{"type":"git","url":"git+https://github.com/ckastbjerg/next-type-safe-routes.git"},"author":{"name":"Christian Kastbjerg"},"bugs":{"url":"https://github.com/ckastbjerg/next-type-safe-routes/issues"},"license":"ISC","readme":"<p align=\"center\">\n  <img src=\"https://upload.wikimedia.org/wikipedia/commons/8/8e/Nextjs-logo.svg\" height=\"100\">\n  <h1 align=\"center\">@axelraag/next-type-safe-routes</h1>\n</p>\n\n<p align=\"center\">\n  <a aria-label=\"NPM version\" href=\"https://www.npmjs.com/package/next-type-safe-routes\">\n    <img alt=\"\" src=\"https://img.shields.io/npm/v/next-type-safe-routes.svg?style=for-the-badge&labelColor=000000\">\n  </a>\n  <a aria-label=\"License\" href=\"https://github.com/ckastbjerg/next-type-safe-routes/license.md\">\n    <img alt=\"\" src=\"https://img.shields.io/npm/l/next.svg?style=for-the-badge&labelColor=000000\">\n  </a>\n</p>\n\n`next-type-safe-routes` parses the `/pages` folder in your Next.js app and generates types for all the **pages** and **API routes** in the application. These types can then be used to ensure that you only link to pages (and only use API routes) that _actually_ exists in your application.\n\nWith the types generated, you can use the `getRoute` utility to retrieve **links that are guaranteed to exist** in your the application:\n\n<img src=\"./getRoute.gif\" />\n\n## Features\n\n- **Automatic route listing**. Avoid having to maintain a list of existing pages for your application\n- **Compile time route validation**. Avoid having to run your application to verify if links are correct, just use types\n- **Unopinionated**. Use our simple and composable utils or create your own abstraction\n\n## Installation\n\nInstall using yarn:\n\n```bash\nyarn add @axelraag/next-type-safe-routes\n```\n\nOr using npm:\n\n```bash\nnpm install @axelraag/next-type-safe-routes --save\n```\n\n> For an example setup, see the [`/example`](/example) folder\n\nThe easiest way to use `next-type-safe-routes`, is with [`next-compose-plugins`](https://github.com/cyrilwanner/next-compose-plugins). With `next-compose-plugins` installed, you can add a `next.config.js` file with the following content:\n\n```js\nconst withPlugins = require(\"next-compose-plugins\");\nconst nextTypeSafePages = require(\"next-type-safe-routes/plugin\");\n\nmodule.exports = withPlugins([nextTypeSafePages]);\n```\n\nWhen you start up your application, it will generate types for all of your pages and API routes and save them to the file `next-type-safe-routes/dist/utils.d.ts` in the package. The file will be updated whenever you add or remove pages and API routes.\n\nTo generate the types without starting the application, execute the command `next-type-safe-routes`. This may be necessary to use it in CI/CD that all test pass.\n\n```json\n{\n  \"scripts\": {\n    \"postinstall\": \"next-type-safe-routes\"\n  }\n}\n```\n\n## Usage\n\nYou can now import the `getRoute` util from `next-type-safe-routes` and use it to retrieve a route that's is guaranteed to exist in your application.\n\n```ts\nimport { getRoute } from \"next-type-safe-routes\";\n\n// for simple routes (e.g. the file `/pages/users.tsx`)\ngetRoute(\"/users\");\n// for dynamic routes (e.g. the file `/pages/users/[userId]/page.tsx`)\ngetRoute({ route: \"/users/[userId]\", params: { userId: \"1\" } });\n// for catch all routes (e.g. the file `/pages/catch-all/[[...slug]].tsx`)\ngetRoute({ route: \"/catch-all\", path: \"/a/b/c\" });\n```\n\nNow you just need to decide how you want to integrate `next-type-safe-routes` in your project. If you want inspiration, we demonstrate how to create a simple abstraction for the Next.js `Link` and `router` in [the example project](/example/src).\n\n<img src=\"./example.gif\" />\n\n## How it works\n\nSince the Next.js router is based (strictly) on the file-system, we can determine which pages and API routes exists in an application simply by parsing the `/pages` folder. And due to the strictness, we can also determine which parameters are needed for dynamic routes.\n\nAs mentioned in the usage section, we generate a module declaration specific to your project when running your project. The output looks like this:\n\n```ts\ndeclare module \"next-type-safe-routes\" {\n  export type TypeSafePage = ... // all your pages\n  export type TypeSafeApiRoute = ... // all your routes\n  export const getPathname = ... // typed based on your routes\n  export const getRoute = ... // typed based on your routes\n}\n```\n\n> See [`/example/src/@types/next-type-safe-routes/index.d.ts`](/example/src/@types/next-type-safe-routes/index.d.ts) for a real example\n\nThe trick here is, that we override the types for `next-type-safe-routes`. And we (re)define the args accepted by the `getRoute` and `getPathname` to match the types for your project.\n\nThe declaration will be written to `@types/next-type-safe-routes/index.d.ts` in the root (determined by Next.js) of your project.\n\n## API reference\n\nHow you ensure that only links to existing pages is essentially up to you, but we do expose a few _tiny_ util methods to help you do this.\n\n#### The `getRoute` method\n\nA simple method that converts a type-safe route to an \"actual\" route.\n\n**Examples:**\n\n```ts\nimport { getRoute } from \"next-type-safe-routes\";\n\n// For simple (non-dynamic) routes\nconst route = getRoute(\"/users\"); // => \"/users\"\n\n// With query params\nconst route = getRoute({\n  route: \"/users\",\n  query: { \"not-typed\": \"whatevs\" },\n}); // => \"/users?not-typed=whatevs\"\n\n// For dynamic routes\nconst route = getRoute({\n  route: \"/users/[userId]\",\n  params: { userId: 1234 },\n}); // => \"/users/1234\"\n\n// For catch all routes\nconst route = getRoute({\n  route: \"/catch-all\",\n  path: \"/can/be/anything\",\n}); // => \"/catch-all/can/be/anything\"\n```\n\n> [Optional catch all routes](https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes) are also supported.\n\n#### The `getPathname` method\n\nThe `getPathname` works similarly to the `getRoute`. It just returs a [Next.js pathname](https://nextjs.org/docs/api-reference/next/router#router-object). For instance:\n\n```ts\nimport { getPathname } from \"next-type-safe-routes\";\n\nconst path = getPathname({\n  route: \"/users/[userId]\",\n  params: { userId: 1234 },\n}); // => `/users/[userId]`\n```\n\n#### The `TypeSafePage` and `TypeSafeApiRoute` types\n\nThese can be useful for making your own abstraction. For instance, if you want to make a tiny abstraction ontop of the `next/router`:\n\n```ts\nimport { TypeSafePage, getRoute } from \"next-type-safe-routes\";\nimport { useRouter as useNextRouter } from \"next/router\";\n\nconst useRouter = () => {\n  const router = useNextRouter();\n\n  // Say you only want to allow links to pages (and not API routes)\n  const push = (typeSafeUrl: TypeSafePage) => {\n    router.push(getRoute(typeSafeUrl));\n  };\n\n  return { ...router, push };\n};\n\nexport default useRouter;\n```\n\nFor basic routes, the type can be of the type `string` or:\n\n```ts\n{\n  route: string,\n  query?: { ... } // any key value pairs (not type-safe)\n}\n```\n\nAnd for dynamic routes, the type is always:\n\n```ts\n{\n  route: string,\n  params: { ... }, // based on the file name\n  query?: { ... } // any key value pairs (not type-safe)\n}\n```\n\nAnd for [catch all routes](https://nextjs.org/docs/routing/dynamic-routes#catch-all-routes), a (non-typed) `path` will also be required (or optional for [optional catch all routes](https://nextjs.org/docs/routing/dynamic-routes#optional-catch-all-routes)):\n\n```ts\n{\n  route: string,\n  path: string,\n  params: { ... }, // based on the file name\n  query?: { ... } // any key value pairs (not type-safe)\n}\n```\n\n**Examples**:\n\n```ts\ntype Query = { [key: string]: any };\nexport type TypeSafePage =\n  | \"/users\"\n  | { route: \"/users\"; query?: Query }\n  | {\n      route: \"/users/[userId]\";\n      params: { userId: string | number };\n      query?: Query;\n    }\n  | {\n      route: \"/users/[userId]/catch-all-route\";\n      params: { userId: string | number };\n      path=\"/catch/all/path\"\n      query?: Query;\n    };\n```\n\n> Note, the `TypeSafePage` and `TypeSafeApiRoute` are kept separate even though they are essentially the same type. We do this, as you may potentially want to distinguish between them in your application.\n\n## Motivation\n\nAt my company, [Proper](https://helloproper.com/), we like pages. Like...a lot! Our platform is a fairly large Next.js application consisting of ~70 pages. And we link between pages ~200 places in the application.\n\nWe find that having pages make features easily discoverable by end-users and developers alike. And having pages (urls) for each of our features help us maintain a sane information architecture throughout our platform.\n\nThe [Next.js file-system based router](https://nextjs.org/docs/routing/introduction) help us stay consistent and organised around our pages. But we've had some incidents where our application was released with dead links.\n\nAt one point, a file in the `/pages` folder was renamed and we simply overlooked (forgot to change) some of the links to that page. Another time, a bit of \"clever\" string concatenation caused an issue. In this case, we had moved a page, and failed to update all links to the page correctly due to the concatenated links.\n\nWith the `next-type-safe-routes`, we're trying to mitigate this issue. The plugin gives us confidence when refactoring as well as a top notch developer experience.\n\n> We considered something like the [`next-routes`](https://github.com/fridays/next-routes) approach, but we don't want to manually have to maintain a list of routes in the application. We prefer conventions to be enforced when possible.\n","readmeFilename":"README.md"}